dsh-any-background 0.3.8 → 0.3.9

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 CHANGED
@@ -72,14 +72,14 @@ A **DeepSeek Harness** appearance plugin: custom theme color, background wallpap
72
72
  - **Layout Modes** — Fit / Fill / Stretch / Tile / Center for both images and videos; in Fit mode the editor-committed framing stays consistent across window resizes and cross-monitor moves.
73
73
  - **Generated Dynamic Backgrounds** — Choose mesh gradient, Shader, or geometric patterns with adjustable spread, intensity, and seed locking.
74
74
  - **Per-surface Interface Opacity** — Independent sliders for the main background, sidebar, cards & panels (including the dropdowns and menus around the dialog), the input & controls (composer box, Cordis panel), the settings panel, the conversation text frame, the trajectory view, the right sidebar (or bettersidebar), produced / highlighted content, the header popovers (Agent Team panel, background-job list and the session-header dropdowns), and the header bar (the conversation's title / view-tab row).
75
- - **Per-surface Interface Blur** — Frosted-glass `backdrop-filter` blur (0–60 px) per surface, including a real backdrop on the composer, the Cordis panel and popover surfaces via stable host selectors. A slider at 0 leaves the surface's OWN host frost in place rather than clearing it: the host frosts its menus by default (`--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`), and a plugin value only takes over once the slider moves — the same "compete for nothing at 0" rule the composer capsule follows.
75
+ - **Per-surface Interface Blur** — Frosted-glass `backdrop-filter` blur (0–60 px) per surface, including a real backdrop on the composer, the Cordis panel and popover surfaces via stable host selectors. The slider IS the frost amount, and **0 means no frost** — including over the host's own default (`--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`). Through 0.3.7 a 0 meant "leave the host alone", which made the bottom of the slider the blurriest setting it could produce (0 = the host's 40 px, 1 = 1 px) and left "turn the blur off" unreachable; now 0 is 0, and because every part still DEFAULTS to 30 px a stock install keeps the frosted look unless someone drags it down.
76
76
  - **Left sidebar + the caption band above it** — On the Windows desktop shell the sidebar's opacity + blur pair drives the sidebar column AND the segment of the window caption strip that sits directly above it. The host paints that strip as one full-width box; the plugin cuts it at the sidebar's own published width (`--dsh-windows-sidebar-width`), so the left part carries the sidebar's material and frost and the rest stays the 「标题栏」 card's. Drag the sidebar wider, or collapse it to `0px`, and the cut moves with it. The split is deliberate (issue #29, item 3): the left strip IS the sidebar column's own surface, so the header-bar sliders are not meant to reach it.
77
77
  - **Produced / Highlights** — Code blocks in conversation content (with their language banner), inline `code` highlight chips and produced chips share one opacity + blur slider. The opacity is the alpha of **each surface's own background color** (no second color stacked on top of the original), and the blur frosts that same layer so the wallpaper shows through the content.
78
78
  - **Right sidebar / bettersidebar surface** — One slider pair (`panelOpacity` / `blurs.panel`), two identities: without dsh-better-sidebar it reads "右方侧边栏" (Right sidebar) and drives the official right Sidebar's surface tokens and frosted blur (works on 0.1.5-rc.2 through 0.1.7); with dsh-better-sidebar installed it reads "bettersidebar" and takes over that plugin's bottom workbench panel (the official sidebar keeps responding too). The row is always visible.
79
- - **Header popovers** — The session-header dropdowns get their own opacity + blur pair: the Agent Team panel, the background-job list, the open-in-app / session-log menus and the subagent lineage tree. On 0.1.7 the open-in-app picker moved to a portal and the session-row menu became a dynamic slot, so the plugin observes the stable `conversation.session.header*` slot anchors and tags the open popover at runtime instead of relying on class shapes.
80
- - **Header bar (the session title row)** — The row carrying the session title, its actions and the view tabs gets a slider pair of its own. The host paints nothing there (the centre column's background shows straight through), so once a wallpaper is up the title and tabs sat on the raw picture while every masked surface around them did not. It now paints as a region tint, the same recipe as the chat column's card: the material is the plugin's main-background surface at this part's own opacity, so the bar reads as one band over the column rather than a second colour stacked on it, and the frost rides the row's own `backdrop-filter`, and because that filter turns the row into a stacking context the row also carries a compensating `z-index:10` — the overlays it hosts (the background-job list through `0.2.1-alpha.1`, the schedule task menu on every release) hang out of it with `z-index:100` and would otherwise be confined under the conversation. 10 clears the in-column chrome it overlaps (seat 9, back-to-bottom 8) and stays under every page-level surface (100 / 1000 / 1100), so nothing about the part's look changes when a menu opens. No `position` is written — the row is a flex item, where `z-index` applies on its own. See v0.3.7. On the Windows desktop shell the band it drives is split with the sidebar (previous bullet), dims itself while a modal is open (the host mask deliberately stops below the caption), and its generated segment is paint-only — `pointer-events:none` and no `app-region` declaration — so it can never swallow clicks meant for the native caption menubar (issue #29). It also paints below the
79
+ - **Header popovers** — The session-header dropdowns get their own opacity + blur pair: the Agent Team panel, the background-job list, the open-in-app / session-log menus and the subagent lineage tree. On 0.1.7 the open-in-app picker moved to a portal and the session-row menu became a dynamic slot, so the plugin observes the stable `conversation.session.header*` slot anchors and tags the open popover at runtime instead of relying on class shapes. A popover counts as the header's when it is portaled to `<body>` **or** mounted inside one of those anchors — the session-log `…` menu is the second kind (`session-log-export/HeaderAction.tsx` renders its `Menu` without `portal`), and until 0.3.9 only a fragile `_denseList`-class arm reached it.
80
+ - **Header bar (the session title row)** — The row carrying the session title, its actions and the view tabs gets a slider pair of its own. The host paints nothing there (the centre column's background shows straight through), so once a wallpaper is up the title and tabs sat on the raw picture while every masked surface around them did not. It now paints as a region tint, the same recipe as the chat column's card: the material is the plugin's main-background surface at this part's own opacity, so the bar reads as one band over the column rather than a second colour stacked on it. The frost rides a `::before` underlay behind an `isolation:isolate` on the row — a stacking context that is NOT a backdrop root, which is what lets the overlays hanging off the row frost the wallpaper at all (through 0.3.8 the filter sat on the row itself, and a filtered row is a backdrop root: exactly those panels were left blurring a flat tint). Because that context still localizes the row's paint order, the row carries a compensating `z-index:10` — the overlays it hosts (the background-job list through `0.2.1-alpha.1`, the schedule task menu and the session-log `…` menu on every release) hang out of it with `z-index:100` and would otherwise be confined under the conversation. 10 clears the in-column chrome it overlaps (seat 9, back-to-bottom 8) and stays under every page-level surface (100 / 1000 / 1100), so nothing about the part's look changes when a menu opens. `z-index` needs no `position` — the row is a flex item, where it applies on its own — and the underlay's `position:relative` is written inline only while a blur exists and only when the host left the row `static`. See v0.3.7 / v0.3.9. On the Windows desktop shell the band it drives is split with the sidebar (previous bullet), dims itself while a modal is open (the host mask deliberately stops below the caption), and its generated segment is paint-only — `pointer-events:none` and no `app-region` declaration — so it can never swallow clicks meant for the native caption menubar (issue #29). It also paints below the
81
81
  controls (`z-index:-1`) and carries its own ink (`--dsh-any-caption-ink`), so the sidebar toggle and the
82
- 应用 / 编辑 menubar stay readable even when a low sidebar opacity makes the strip light. The anchor is expressed in CSS (the row is the `conversation.session.header` slot's direct parent) instead of guessing a hashed class or tagging a host element at runtime; before `0.1.7-alpha.1` the host mounts that slot bare with no such element, so there the rule matches nothing and the part is simply inert. On the Windows desktop shell the same slider pair also drives the window caption band — except for the segment that sits above the left sidebar, which belongs to that sidebar's own pair (see **Left sidebar + the caption band above it**) — and the native minimize/maximize/close block is handed `transparent` so Windows paints no background of its own, leaving the band itself showing through. A blank session marks the row `headerBlank` (a placeholder left with only its padding) and the plugin skips claiming it.
82
+ 应用 / 编辑 menubar stay readable even when a low sidebar opacity makes the strip light — and 「标题条带文字」 says who wins when the strip's brightness and the theme's direction disagree (自动 derives it from the strip, 跟随主题 leaves the host's own label ink alone, 始终白色 / 始终黑色 pins it; see issue #30). The anchor is expressed in CSS (the row is the `conversation.session.header` slot's direct parent) instead of guessing a hashed class or tagging a host element at runtime; before `0.1.7-alpha.1` the host mounts that slot bare with no such element, so there the rule matches nothing and the part is simply inert. On the Windows desktop shell the same slider pair also drives the window caption band — except for the segment that sits above the left sidebar, which belongs to that sidebar's own pair (see **Left sidebar + the caption band above it**) — and the native minimize/maximize/close block is handed `transparent` so Windows paints no background of its own, leaving the band itself showing through. A blank session marks the row `headerBlank` (a placeholder left with only its padding) and the plugin skips claiming it.
83
83
  - **Sidebar "Theme" page (dual mode)** — The same five pages (Color / Interface / Font / Background / Profiles) register into two surfaces: without dsh-better-sidebar, a "Theme" card is contributed to the **official right Sidebar's guide page** through its public extension points (`sidebarRightTabs` + the `sidebar.right.pane.tab` keyed seat); with dsh-better-sidebar installed, the page registers in that plugin's sidebar instead and the official guide card withdraws itself, so the two never duplicate. Settings panel, official sidebar and better-sidebar all share one page implementation and one state store — a change in any of them shows up everywhere. The shell adapts to the panel width, and a narrow panel tightens padding and falls back to a single column. On a host without the right Sidebar the registration silently never happens.
84
84
  - **Conversation View Cards** — The message list is wrapped in a translucent card automatically, and the trajectory page gets whole-page opacity & blur controls, letting the wallpaper shine through the content.
85
85
  - **Theme Export / Import** — One-click export to a self-contained `dsh-any-theme.json` (config + wallpaper, video embedded as a data URL) and import to restore it anywhere.
@@ -95,6 +95,16 @@ controls (`z-index:-1`) and carries its own ink (`--dsh-any-caption-ink`), so th
95
95
 
96
96
  ## Changelog (latest two releases)
97
97
 
98
+ ### v0.3.9 (Issue #30 makes the caption ink a choice, and the header sliders reach every header popover)
99
+
100
+ - **The caption strip's ink is the user's call now (issue #30).** 0.3.8 derived it from what the strip is actually painted on, which fixed the invisible white toggle — and made the native menubar (「应用 / 编辑」) black under a dark theme whose sidebar is translucent over a bright wallpaper, because there the strip really IS light. The two reports of that one situation wanted opposite winners, so the 「标题栏」 card gained a 「标题条带文字」 selector: **自动** keeps the strip-following derivation, **跟随主题** emits no override at all (the menubar and the sidebar's fixed caption controls read the host's own label tokens, exactly as they did through 0.3.7), **始终白色 / 始终黑色** pin it. The key is stored and whitelisted on the host half too — the #28 lesson is that a key only one half knows is silently dropped at write time — and `npm test` round-trips it, including an unknown value collapsing to `auto`.
101
+ - **In-place header popovers are claimed, and the claim no longer waits for a frame.** `header-tag.ts` accepted a popover only when its parent was `<body>`, so the session-log `…` menu — which `session-log-export/HeaderAction.tsx` renders WITHOUT the `portal` prop, inside `[data-slot="conversation.session.header.utilities"]` — was never tagged, and the only thing styling it at all was the header group's `[role="menu"][class*="_denseList"]:not([class*="_portal"])` arm. That arm dies the moment the host drops `dense` (or ships a header popover that is not a dense menu), and until then the panel followed the 「卡片」 slider while sitting in the title row. A candidate is now "portaled to `<body>`, **or** a descendant of one of the header's own slot anchors": a modal is still never claimed, and the composer's in-place lists stay with the card group that owns them. The observer's flush also moved off `requestAnimationFrame` onto the microtask queue, which is the other half of "still doesn't work": a queued frame does not run while a window is not painting (hidden tab, occluded or minimized desktop window, background Electron surface), so the tag simply never landed and the panel kept whatever the card group gave it — measured live in `dsh web`, an open header menu stayed untagged indefinitely on a non-painting page, and the same menu was tagged on the next microtask after this change. A microtask coalesces a mutation burst exactly as an animation frame did (the observer callback is already per-batch) with no dependency on the compositor, and it lands in the frame the popover mounts, so the panel no longer flashes the card look on open either.
102
+ - **The 「标题栏」 row's frost stopped eating its own children's blur.** `backdrop-filter` on the row made the row a BACKDROP ROOT as well as a stacking context, so an in-place header popover could only ever blur what the row itself paints — a flat tint — and the header blur slider read dead on exactly the panel that hangs off the row. The frost now rides a `::before` underlay behind an `isolation:isolate` (a stacking context that is NOT a backdrop root), which is the recipe this plugin already uses for the frame, the composer capsule and every part column; the row keeps its compensating `z-index:10`, and the underlay's `position:relative` is written inline only while a blur exists and only when the host left the row static. Measured in the real `dsh web` app (the plugin linked from this working tree, driven through the actual 「顶栏选项」 sliders on the 界面 page): the in-place `…` menu and the portaled open-in-app menu both report `rgba(243,247,252,1)` at opacity 100 / blur 4 and `rgba(243,247,252,0.08)` at opacity 8 / blur 44. Measured again in a browser harness running the plugin's real client code over the host's own built CSS: the row reports `backdrop-filter: none`, its `::before` reports the slider's blur, and every header popup shape — portaled dense `MenuSurface`, in-place dense, in-place non-dense, the role-less job-list surface, the lineage tree — takes the 顶栏 pair's fill and frost instead of the card's.
103
+ - **The interface-opacity sliders work on a fresh install now.** `readHostOpacityTokens()` sampled `getComputedStyle(:root)`, but `design-platform.css` declares this whole token set inside a `body { … }` block and a custom property is inherited downward only — so with no picked theme color every read came back empty, `applyCustomTokensNow` took its `surfaces === null` exit, and the 侧栏 / 卡片 / 输入框 / 顶栏选项 alphas were never emitted at all. The sample now comes off `body`, cached per host generation (its dark-mode marker plus the platform attribute), and the plugin's own `body{}` token sheet is switched off while reading — otherwise the second sample returns the plugin's own substituted literal and the surfaces freeze at whatever color they first had instead of following the host.
104
+ - **A 0 on a menu blur slider now means no frost.** 0.3.7 introduced a `<html>` class gate (`dab-card-frost` / `dab-header-frost`) so a slider parked at 0 would not delete the host's own `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)` — an element's own declaration always beats what it inherits, so the un-gated `var(--x, none)` really did clear it. The gate turned out to be the wrong trade: it made the slider's LOW end its HIGH end (0 → the host's 40 px, 1 → 1 px), so 「把模糊度关掉」 was unreachable and the control read as dead at exactly the position the user was testing. The frost token is now asserted at every value with the `none` fallback winning at 0, and the gate machinery is gone. Nothing loses the host look by accident: every part default is 30 px, so a stock install is frosted and only an explicit drag to 0 clears it.
105
+ - **No release-table change.** The shapes above were re-checked against the installed `0.2.0-rc.1`, `0.2.1-alpha.3` and `0.2.1-rc.2` bundles: both utilities controls, `MenuSurface`'s `data-menu-material` + `.material` painter and the menu token set are identical across the three, so the `0.2.1` adapter's facts still hold.
106
+
107
+
98
108
  ### v0.3.8 (Issues #28/#29 and the desktop sidebar: the header-bar card persists, its band stops eating clicks, the mask reaches the caption, and the sidebar is one surface again)
99
109
 
100
110
  - **The 「标题栏」 settings survive a restart now.** The host half whitelists every key before it writes (`normalizeConfig` → `theme-config.json`), and its copy of the config shape had never learned that card's three keys: `blurs.headerBar` fell back to 30 px, `strokes.headerBar` to width 0, and `headerBarOpacity` was dropped outright at write time — while the browser half restored the file with its own defaults (`clamp01(undefined, .5) → 50%`), so the card looked like it saved and then reset on every reload. The profile sanitizer had the same hole, and was also dropping `blurs.header` from saved profiles. All three keys now exist in the host's `PartBlurs`, `DEFAULT_CONFIG`, `STROKE_GROUPS`, both normalizers and `ProfileAppearance`; `npm test` boots the built host half against a throwaway `$DSH_HOME` and asserts the whole round trip (disk + reload + profile), including the "ignoring unknown config field" warning staying silent.
@@ -106,20 +116,6 @@ controls (`z-index:-1`) and carries its own ink (`--dsh-any-caption-ink`), so th
106
116
  - **The plugin-install dialog's npm-registry panel keeps the host's opacity.** `<fieldset data-install-registry>` is `createPortal`ed to `document.body` as a `position:fixed; z-index:1100` panel and paints from `--dsw-alias-bg-layer-2` (itself and its toggle) and `--dsw-alias-bg-layer-3` (the expanded toggle, the typed-address input) — tokens the 「卡片」 slider owns globally, so the panel faded with the card slider while the host's own comment on that rule says the layer is opaque *"to keep the registry form controls readable over the dialog"*. It joins the exemption set `applyExemptDefaults` already pins for confirm dialogs and the session-row menu: the current theme color at alpha 1, no translucency, nothing else re-inked or re-tinted, and the pin spelled for the subtree as well because a custom property travels downward and the options live inside it.
107
117
  - **The `0.2.1` line is one channel, and every checked build is named.** `0.2.1-alpha.3` moved the background-job list onto the shared `MenuSurface` — the surface root carries no `role`, and the fill plus the frost live on its `.material` child — so the runtime tagger now also claims body-level `[data-menu-material]` surfaces and the header selector set gained the matching arm; the older self-painting `<ul class*="_menu">` shape stays matched. Verified per tag from `alpha.3` through `rc.2`: no file the adapter speaks for moved (`design-platform.css`, `MenuSurface`, `MenuGroup`, `AppFrame`, `ConversationRoot`, `TaskMenu`, `preload-windows`, the five header slots). `SidebarRight.module.css` only gained `overflow:clip` on the panel wrapper (fixed floating children still escape), and the plugin-manager page was refactored with all three list anchors intact. The `0.2.1-alpha` and `0.2.1-rc` channels therefore collapse into one `0.2.1` channel covering `alpha.1`…`rc.2`, with a `SUPPORTED_RELEASES` row per build (each `exact`), `engines`/peers/`dsh.compatibility` naming them, and the folder renamed `v0-2-1`.
108
118
 
109
- ### v0.3.7 (Menus keep the host's frost at blur 0, the left sidebar owns the band above it, and the release table is 0.2.x only)
110
-
111
- - **The reported "the background-job list is under the conversation" was caused by the 「标题栏」 card's own frost, and the fix is a stacking order, not a state switch.** The session header row is not an empty strip: the host mounts its header chrome INTO it (`conversation.session.header.actions|.utilities`), and that chrome raises its overlays from inside the row. `ui-jobs/JobListAction` hangs `.menu{position:absolute; top:calc(100% + 5px); z-index:100}` off a `.root{position:relative}` on every release through `0.2.1-alpha.1` — the captured DOM's `style="left: 0px"` with no `top` and no `role` IS that in-place shape, and the `0.2.1-alpha.2` portal is what hid the symptom on the newest tag, not what fixed it. `ui-schedule/TaskMenu` (`role="menu"`, `position:absolute; z-index:100` whenever the primitive is not in portal mode) sits in the same row on every release in range. Putting `backdrop-filter` on the row makes the row a STACKING CONTEXT, so those `z-index:100` values stop being page-level and only order the overlays WITHIN the row; the row then paints at its own level — under the conversation body, the composer seat and `.root`'s background. The menu was never demoted by the host: it was confined by us. The row now carries a compensating `z-index:10` — above the in-column chrome the menu hangs over (`.seat` 9, back-to-bottom 8, footer 7, `.tabs` 1) and far below every page-level surface (portaled menus 100, modals 1000, stat / context panels 1100) — so the frost, the tint and the drag region never change state, and a portaled list still covers the title row exactly as the host intends. No `position` is written: the row is a flex item of `ConversationRoot .root{display:flex}`, where `z-index` applies on its own, which keeps the plugin out of deciding a host box's positioning (issue #24). The first attempt at this in the same release was a `:has()` valve that dropped the frost while a header overlay was open — it cleared the occlusion but re-tinted the whole title bar every time anyone opened a menu, which reads as the part's two sliders moving themselves. That attempt is gone.
112
-
113
- - **A menu's blur slider at 0 no longer deletes the host's frost.** The host frosts every menu by itself — `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`, declared on `body` — and both paint shapes read that inherited value: a self-painting list through its own `backdrop-filter`, a `MenuSurface` card through its `.material` layer. The plugin re-declared that token on the menu with a `none` fallback, and an element's own declaration always beats what it inherits, so at slider 0 the frost was not "left alone", it was REMOVED — a faded menu with nothing frosted behind it lets the conversation show straight through it. (A second and separate defect made the same popup get buried outright — see the first bullet of this release.) Both menu groups' frost now rides a `:where()` gate on `<html>` (`dab-card-frost` / `dab-header-frost`) that switches on only while the slider is non-zero — the idiom the composer capsule already uses. `:where()` is what keeps the gate free: this plugin's menu cascade is ordered at equal specificity (card group → header group → exemptions), and a plain class prefix would have lifted the card group over the exemption it must lose to. The exemption's own `none` stays, because `applyExemptDefaults` pins those surfaces at alpha 1 beside it — opaque by design, not broken.
114
-
115
- - **The background-job list is where the bug became visible.** `0.2.1-alpha.2` portaled it out of the header row to `document.body` as `position:fixed; z-index:100`, a bare `<ul>` with no `role` — so the missing-frost defect below finally had something to be seen on — a list facing the whole page instead of hanging inside the column. The occlusion reported alongside it had the different cause spelled out in the first bullet. `MenuSurface`'s own `data-menu-material` marker joins the card group for the same reason: `ModelSelect` renders its model pane as `role="group"`, so keyed on `[role="menu"]` alone that card faded with the slider and never frosted with it.
116
-
117
- - **The left sidebar and the caption band above it are one surface now.** `AppFrame`'s band is a single full-width box (`.frame::before`) whose left segment sits directly above the sidebar column, and the host publishes that column's width on the frame itself (`--dsh-windows-sidebar-width`, inline, Windows builds only). The plugin cuts the band at exactly that width: `::before` is clipped to everything right of it — keeping the 「标题栏」 card's material, frost and drag region — and a new `::after` paints the left segment from `--dsh-any-op-sidebar`, the very literal the sidebar column is painted with, frosted from a root-level mirror of the sidebar blur. One opacity + blur pair drives the column and the strip above it, and the cut follows a dragged or collapsed sidebar with no observer of ours (`0px` returns the whole band to the 标题栏 card).
118
-
119
- - **The custom font now wins the cascade it was silently losing on `0.2.1-alpha.2`.** That tag moved the user font list into `--dsh-font-family-text` and made the host compose `body{--dsw-font-family:var(--dsh-font-family-text,…)}` in its own `base.css` — the same token, the same `body` selector and the same specificity as the rule this plugin's custom font writes, so which one applied became a question of which stylesheet landed last. The font rule now carries `!important`, the way the token sheet already does; code typography is left alone, as always.
120
-
121
- - **The release table now speaks only for `0.2.x`.** The `0.1.5-rc`, `0.1.6-alpha`, `0.1.7-alpha` and `0.1.7-rc` folders, channels and rows are gone: nothing in the plugin still asserts a fact checked against them, and a table nobody verifies is worse than no table. `engines.dsh`, the gated peers and `dsh.compatibility` now start at `0.2.0-rc.1`, which means an older host is refused at profile composition instead of being quietly styled with the wrong adapter — the DSHA launcher's bundled `0.1.5-rc.2` among them. `0.2.1-alpha.2` … `0.2.1-rc.2` were verified by diffing each tag against the folder's facts (`design-platform.css`, `MenuSurface`, `MenuGroup`, the `--dsw-menu-backdrop-filter` consumers, `AppFrame`, `ConversationRoot`, `TaskMenu`, the header slot keys and the right-sidebar panel — none of them moved) and join that folder, renamed `v0-2-1`, so each verdict reads `exact`.
122
-
123
119
  ## Installation
124
120
 
125
121
  ### Method 1: npm install (Recommended)
package/README.zh.md CHANGED
@@ -72,12 +72,12 @@
72
72
  - **布局模式** — 适应 / 填充 / 拉伸 / 平铺 / 居中五种排布,图片与视频通用;「适应」模式下编辑器提交的构图在窗口缩放、跨屏移动后保持一致。
73
73
  - **动态生成背景** — 支持网格渐变、Shader、几何图案,可调节扩散范围、色彩强度并锁定种子。
74
74
  - **分部位界面透明度** — 主背景、侧边栏、卡片面板(含对话框周围的选项框/菜单)、输入框与控件(发送框、Cordis 插件面板)、设置面板、对话文本框、轨迹页、右方侧边栏(或 bettersidebar)、产出物/高亮内容、顶栏选项(Agent Team 面板、后台任务列表与会话头部下拉菜单),以及标题栏(会话顶部那条标题行 + 视图页签)各自独立滑块。
75
- - **分部位界面模糊度** — 每个界面部位可独立调整毛玻璃 `backdrop-filter` 模糊(0–60 px),并通过宿主的稳定选择器为发送框、Cordis 面板与弹出层提供真实背景模糊。滑块停在 0 时保留该表面**宿主自己的**磨砂,而不是把它抹掉:宿主默认就给菜单上 `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`,插件的数值只在滑块离开 0 后才接管——与发送框那条「为 0 就不抢」的规矩一致。
75
+ - **分部位界面模糊度** — 每个界面部位可独立调整毛玻璃 `backdrop-filter` 模糊(0–60 px),并通过宿主的稳定选择器为发送框、Cordis 面板与弹出层提供真实背景模糊。滑块就是该表面的磨砂量,**0 就是没有磨砂**——包括盖掉宿主默认的 `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`。0.3.7 曾把 0 解释成「保留宿主自己的磨砂」,结果滑块的下端反而是最糊的一端(0 = 宿主的 40px,1 = 1px),「把模糊关掉」根本够不着;现在 0 就是 0,而各部件的**默认值仍是 30px**,所以全新安装照旧是磨砂的,只有明确拖到 0 才会清掉。
76
76
  - **左侧栏 + 它上方的标题条带** — 桌面壳(Windows)上,「侧栏」这对滑块现在同时驱动侧栏列,以及那条压在侧栏正上方的窗口标题条带分段。宿主把整条条带画成一个全宽的盒子(`.frame::before`),而宿主自己就把侧栏列宽发在帧元素上(`--dsh-windows-sidebar-width`);插件就按这个宽度切开条带:`::before` 被裁到该宽度右侧(继续保留「标题栏」卡片的材质、模糊与拖拽区),新增的 `::after` 用 `--dsh-any-op-sidebar`——也就是侧栏列本身用的那份颜色——画出左侧那段,并按侧栏模糊的根级镜像上霜。于是一对滑块管住侧栏与它上方那一条,拖动或折叠侧栏时切口跟着移动(宽度 `0px` 时整条条带重新归「标题栏」)。这个切分是有意的(issue #29 第 3 条):左段本就是侧栏列自己的表面,标题栏滑块不该够到它。
77
77
  - **产出物 / 高亮内容** — 对话正文里的代码块(含语言顶栏)、行内 `code` 高亮芯片与产出物 chip 共用一支滑块:透明度只**调制这些表面原本的背景色**(不会在旧背景上再叠一层新色),模糊度给同一层表面加磨砂,壁纸从内容后方透出来。
78
78
  - **右方侧边栏 / bettersidebar 表面** — 同一对滑块(`panelOpacity` / `blurs.panel`)按宿主环境自动切换目标:未安装 dsh-better-sidebar 时叫「右方侧边栏」,驱动官方右侧栏的表面令牌与毛玻璃模糊(范围内每个 release 均适用);安装了 dsh-better-sidebar 时改叫「bettersidebar」,接管其底部工作台面板(官方右侧栏同样生效)。该行现在始终显示。
79
- - **顶栏选项** — 会话头部的下拉浮层获得了独立的透明度 + 模糊滑块:Agent Team 面板、后台任务列表、「用其它应用打开」与「会话日志」菜单、子代理血缘树。由于这些浮层被 portal 到 `<body>`(与头部 DOM 断开),且 0.1.7 把「用其它应用打开」改成 portal、把会话行菜单改成动态槽位,插件改为在运行时监听稳定的 `conversation.session.header*` 槽位锚点来标记当前展开的浮层,不再依赖类名形状。
80
- - **标题栏(会话标题行)** — 会话顶部那条标题 / 操作 / 视图页签行终于有了自己的一对滑块。宿主自身不给它任何底色(中心列的底色直接透上来),所以壁纸一起来,标题与页签就直接压在图上、和周围被蒙住的表面割裂。现在它按「区块淡色」作画,配方与对话文本框那张卡一致:材质取插件主背景面的颜色、按本部件自己的透明度上色,读起来是压在列上的一条带,而不是叠上去的第二层颜色;模糊落在该行自己的 `backdrop-filter` 上;而这道霜会把行变成层叠上下文,所以行同时带上补偿性的 `z-index:10`——行内挂出的浮层(直到 `0.2.1-alpha.1` 的后台任务列表、每个 release 上都可能就地的日程任务菜单)本来用 `z-index:100`,否则会被关在行里、压到对话之下。10 高于它会重叠到的列内部件(输入位 9、回到底部 8),又远低于所有页面级表面(100 / 1000 / 1100),因此打开菜单时这一条的颜色和模糊都不会变;也不写 `position`——该行本是 flex 子项,`z-index` 天然生效。详见 v0.3.7。Windows 上它驱动的条带与侧栏分段(见上一条);模态打开时条带会跟着宿主蒙版自行变暗(宿主蒙版故意停在标题区下方);生成出来的那一段只负责绘制——`pointer-events:none` 且不声明 app-region——因此不会吞掉本该给原生标题栏菜单的点击(issue #29)。它还画在控件之下(`z-index:-1`),并自带墨色(`--dsh-any-caption-ink`):即使侧栏透明度很低、条带发浅,开合按钮与「应用 / 编辑」菜单也清楚可读。这一行是**运行时认领**的:插件从 `conversation.session.header*` 槽位锚点向上找到最近的那个 `<header>` 再打上自己的类(与卡片、帧同款做法),不猜哈希类名、也不依赖宿主把槽位摆在哪个层级;`0.1.7-alpha.1` 之前宿主把槽位裸挂在会话根上、没有这一行元素,那种宿主上插件什么都不会认领,部件自然无效果。桌面壳(Windows)上这一对滑块同时驱动窗口标题条带——**但左侧栏正上方的那一段除外**,那一段归「侧栏」这对滑块管(见下条);原生最小化/最大化/关闭那块改由取色探针报 `transparent`,让 Windows 不画自己的背景,露出的正是条带本身。空会话时宿主把该行标为 `headerBlank`(一条只留 padding 的占位),插件跳过认领。
79
+ - **顶栏选项** — 会话头部的下拉浮层获得了独立的透明度 + 模糊滑块:Agent Team 面板、后台任务列表、「用其它应用打开」与「会话日志」菜单、子代理血缘树。由于这些浮层被 portal 到 `<body>`(与头部 DOM 断开),且 0.1.7 把「用其它应用打开」改成 portal、把会话行菜单改成动态槽位,插件改为在运行时监听稳定的 `conversation.session.header*` 槽位锚点来标记当前展开的浮层,不再依赖类名形状。浮层算不算头部的判据是「portal 到了 `<body>`,**或者**就在这些锚点里面」——「会话日志」的 `…` 菜单属于后一种(`session-log-export/HeaderAction.tsx` 渲染它的 `Menu` 时不带 `portal`),而在 0.3.9 之前能碰到它的只有一条按 `_denseList` 类名匹配的脆弱分支。
80
+ - **标题栏(会话标题行)** — 会话顶部那条标题 / 操作 / 视图页签行终于有了自己的一对滑块。宿主自身不给它任何底色(中心列的底色直接透上来),所以壁纸一起来,标题与页签就直接压在图上、和周围被蒙住的表面割裂。现在它按「区块淡色」作画,配方与对话文本框那张卡一致:材质取插件主背景面的颜色、按本部件自己的透明度上色,读起来是压在列上的一条带,而不是叠上去的第二层颜色;模糊落在行前的一块 `::before` 垫层上(配合 `isolation:isolate`——它是层叠上下文,但**不是** backdrop root;0.3.8 之前霜直接压在行身上,而行一旦带上 `backdrop-filter` 就成了 backdrop root,恰恰把挂在行里的那些浮层的磨砂压成一片平色);而这道霜仍然把行变成层叠上下文,所以行同时带上补偿性的 `z-index:10`——行内挂出的浮层(直到 `0.2.1-alpha.1` 的后台任务列表、每个 release 上都可能就地的日程任务菜单与「会话日志」`…` 菜单)本来用 `z-index:100`,否则会被关在行里、压到对话之下。10 高于它会重叠到的列内部件(输入位 9、回到底部 8),又远低于所有页面级表面(100 / 1000 / 1100),因此打开菜单时这一条的颜色和模糊都不会变;`z-index` 无需 `position`——该行本是 flex 子项,`z-index` 天然生效,而垫层要用的 `position:relative` 只在模糊非 0、且宿主把这一行留在 static 时才以内联方式写入。详见 v0.3.7 / v0.3.9。Windows 上它驱动的条带与侧栏分段(见上一条);模态打开时条带会跟着宿主蒙版自行变暗(宿主蒙版故意停在标题区下方);生成出来的那一段只负责绘制——`pointer-events:none` 且不声明 app-region——因此不会吞掉本该给原生标题栏菜单的点击(issue #29)。它还画在控件之下(`z-index:-1`),并自带墨色(`--dsh-any-caption-ink`):即使侧栏透明度很低、条带发浅,开合按钮与「应用 / 编辑」菜单也清楚可读;而当条带的明暗与主题方向冲突时,由「标题条带文字」裁定谁获胜——「自动」沿用条带推导,「跟随主题」完全不写覆盖(回到宿主的标签墨色,即 0.3.7 及之前),「始终白色 / 始终黑色」直接钉死(issue #30)。这一行是**运行时认领**的:插件从 `conversation.session.header*` 槽位锚点向上找到最近的那个 `<header>` 再打上自己的类(与卡片、帧同款做法),不猜哈希类名、也不依赖宿主把槽位摆在哪个层级;`0.1.7-alpha.1` 之前宿主把槽位裸挂在会话根上、没有这一行元素,那种宿主上插件什么都不会认领,部件自然无效果。桌面壳(Windows)上这一对滑块同时驱动窗口标题条带——**但左侧栏正上方的那一段除外**,那一段归「侧栏」这对滑块管(见下条);原生最小化/最大化/关闭那块改由取色探针报 `transparent`,让 Windows 不画自己的背景,露出的正是条带本身。空会话时宿主把该行标为 `headerBlank`(一条只留 padding 的占位),插件跳过认领。
81
81
  - **侧边栏「主题」页面(双模式)** — 同一套五页(色彩 / 界面 / 字体 / 背景 / 配置)注册到两处:未安装 dsh-better-sidebar 时,通过官方右侧栏的公开扩展点(`sidebarRightTabs` + `sidebar.right.pane.tab`)在**官方侧边栏**的引导页挂一张「主题」卡片,点开即五页;安装了 dsh-better-sidebar 时,改为在它的侧边栏注册同名页面,官方引导页上的卡片自动撤下,避免重复。两处(加设置面板共三处)共用同一套页面代码与同一份状态,改一处处处同步;外壳按面板宽度自适应,窄面板收紧内边距并回到单列。宿主没有右侧栏扩展点时静默不注册,不影响任何其它功能。
82
82
  - **对话视图卡片** — 消息列表自动包裹为半透明卡片,轨迹页可整页调节透明度与模糊,让壁纸从内容后方透出来。
83
83
  - **主题导出 / 导入** — 一键导出为自包含的 `dsh-any-theme.json`(配置 + 壁纸,视频以 data URL 内嵌),可随时导入还原。
@@ -93,6 +93,16 @@
93
93
 
94
94
  ## 更新日志(只保留最近两个版本)
95
95
 
96
+ ### v0.3.9(issue #30:条带墨色改由用户裁定;顶栏那对滑块重新管住所有头部浮层)
97
+
98
+ - **「标题条带文字」现在是一个选项(issue #30)。** 0.3.8 让条带墨色跟随它实际被画在什么上,这修好了「白色开合按钮在亮条带上消失」——但也让暗色主题下、侧栏半透明压在亮壁纸上时,原生菜单条(「应用 / 编辑」)变成黑色,因为那一段条带确实是亮的。同一个场景,两份反馈要的是相反的赢家,所以这个取舍交给用户:「标题栏」卡片新增「标题条带文字」——**自动**保持 0.3.8 的推导;**跟随主题**完全不发射覆盖(菜单条与侧栏 fixed 在条带上的控件读宿主自己的标签令牌,即 0.3.7 及之前的样子);**始终白色 / 始终黑色**钉死。这个键同样补进了宿主半侧的白名单(#28 的教训是:只有一侧认识的键会在写盘时被静默丢弃),`npm test` 会把「落盘 → 重读」跑一遍,并断言未知值塌回 `auto`。
99
+ - **就地的头部浮层现在会被认领,而且认领不再等一帧。** `header-tag.ts` 过去只接受父节点是 `<body>` 的浮层,而 `session-log-export/HeaderAction.tsx` 的那个 `…` 菜单**不带 `portal`**,就地挂在 `[data-slot="conversation.session.header.utilities"]` 里,于是永远拿不到标记;唯一还在给它上色的,是顶栏组的 `[role="menu"][class*="_denseList"]:not([class*="_portal"])` 分支——宿主一旦不再标 `dense`(或者新增一个非 dense 的头部浮层)这条分支就失效,而在此之前那块面板一直跟着「卡片」滑块走,尽管它就悬在标题行上。认领条件现在是「portal 到 `<body>`,**或者**是头部某个槽位锚点的后代」:模态依旧绝不认领,输入位自己的就地列表也继续归卡片组。观察者的一次性 flush 也从 `requestAnimationFrame` 挪到了微任务队列——这才是「还是没生效」的另一半原因:**排下去的那一帧在窗口不绘制时根本不会跑**(后台标签页、被遮住或最小化的桌面窗口、后台的 Electron 表面),于是标记永远落不下,面板就一直保持着卡片组给它的样子。在真实 `dsh web` 里量过:页面不绘制时,一个打开的头部菜单无限期保持未标记;改成微任务后,同一个菜单在下一个微任务就被标记上了。微任务同样能把一批变更合并成一次(`MutationObserver` 的回调本就按批投递),却完全不依赖合成器;而且它落在浮层挂载的那一帧里,打开面板时不再有一帧的「卡片样式」闪动。
100
+ - **「标题栏」这一行的霜不再吞掉子层的模糊。** `backdrop-filter` 让标题行同时成为层叠上下文和 BACKDROP ROOT,于是挂在行内的就地浮层能采到的背景只剩这一行自己画的东西——一片平色——恰恰是这条从行上垂下来的面板,模糊滑块看起来完全失效。现在霜改走 `::before` 垫层(配 `isolation:isolate`:是层叠上下文,但**不是** backdrop root),也就是本插件给帧、输入位胶囊和各个部件列一直使用的配方;行继续保留补偿性的 `z-index:10`,而垫层需要的 `position:relative` 只在模糊非 0、且宿主把行留在 static 时才以内联方式写入。在真实 `dsh web` 里(配置档直接 link 本工作树,用界面页上真正的「顶栏选项」滑块驱动):就地的 `…` 菜单与 portal 出去的「更多打开方式」菜单,在透明度 100 / 模糊 4 时都是 `rgba(243,247,252,1)`,在透明度 8 / 模糊 44 时都是 `rgba(243,247,252,0.08)` + `blur(44px)`。再用「跑真实插件代码 + 宿主安装产物 CSS」的浏览器复测:行的 `backdrop-filter: none`、其 `::before` 报告滑块的模糊值,而所有头部浮层形状(portal 的 dense `MenuSurface`、就地的 dense、就地的非 dense、无 role 的后台任务表面、子代理血缘树)都取「顶栏选项」的底色与磨砂,不再落到卡片组。
101
+ - **全新安装下界面透明度滑块不再是死的。** `readHostOpacityTokens()` 采的是 `getComputedStyle(:root)`,而 `design-platform.css` 把这一整套表面令牌声明在 `body { … }` 块里,自定义属性只会向下继承——于是没有选定主题色时每次读取都是空,`applyCustomTokensNow` 走 `surfaces === null` 那条出口,侧栏 / 卡片 / 输入框 / 顶栏选项的 alpha 从头到尾没被写出来。现在改从 `body` 采样,按宿主代次(它自己的深色标记 + 平台属性)缓存,并且读取时把插件自己那份 `body{}` 令牌表临时关掉——否则第二次采样拿回来的是插件自己刚写进去的字面量,表面颜色会冻结在第一次采到的那个值上,不再跟随宿主。
102
+ - **菜单模糊滑块拉到 0,现在就是没有磨砂。** 0.3.7 加过一个 `<html>` 类门控(`dab-card-frost` / `dab-header-frost`),让停在 0 的滑块不去抹宿主自己的 `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`(元素自身声明恒胜继承,所以未门控的 `var(--x, none)` 确实会清掉它)。事后看这个取舍是错的:它把滑块的**下端变成了最糊的一端**(0 → 宿主的 40px,1 → 1px),于是「把模糊度关掉」根本够不到,用户恰恰在那个位置上觉得控件是死的。现在这个 frost 令牌在每个取值上都被声明,0 时由 `none` 兜底胜出,门控机制整体删除。不会有人因此丢掉宿主观感:每个部件的默认值都是 30px,全新安装依旧是磨砂的,只有明确拖到 0 才会清掉。
103
+ - **版本表不动。** 上面这些形状都对着安装产物里的 `0.2.0-rc.1`、`0.2.1-alpha.3`、`0.2.1-rc.2` 三个构建复核过:两个 utilities 控件、`MenuSurface` 的 `data-menu-material` + `.material` 绘制层、菜单令牌集合三处一致,`0.2.1` 适配层的事实继续成立。
104
+
105
+
96
106
  ### v0.3.8(issue #28/#29 与桌面端侧栏:「标题栏」设置能落盘了,条带不再吞点击,模态蒙版够到标题区,侧栏重新是一个面)
97
107
 
98
108
  - **「标题栏」的三项设置现在重启后还在。** 宿主半侧在写盘前会按白名单归一化(`normalizeConfig` → `theme-config.json`),而它那份配置形状一直没学会这张卡的三把键:`blurs.headerBar` 回落到 30px、`strokes.headerBar` 回落到 0 宽、`headerBarOpacity` 在写盘时被整条丢掉——浏览器半侧读回时又用自己的默认值回填(`clamp01(undefined,.5) → 50%`),于是看起来"保存成功、刷新即还原"。保存 Profile 的归一化器有同一个洞,还额外丢了 `blurs.header`。现在三把键都补进了宿主的 `PartBlurs`、`DEFAULT_CONFIG`、`STROKE_GROUPS`、两个归一化器和 `ProfileAppearance`;`npm test` 会用一个临时 `$DSH_HOME` 启起真正的宿主半侧,把「落盘 → 重读 → Profile」整条往返跑一遍,并断言不再出现 `ignoring unknown config field` 警告。
@@ -104,20 +114,6 @@
104
114
  - **插件安装对话框里的「npm 源」面板保持宿主的不透明度。** `<fieldset data-install-registry>` 被 `createPortal` 到 `document.body`,是一个 `position:fixed; z-index:1100` 的面板,底色取自 `--dsw-alias-bg-layer-2`(面板自身与它的开关)和 `--dsw-alias-bg-layer-3`(展开态开关、手填地址的输入框)——这些都是「卡片」滑块全局接管的令牌,于是这块面板跟着卡片滑块一起变透明;而宿主写在那条规则上的注释恰恰相反:这层之所以不透明,是为了"让源选择表单控件压在对话框上仍然可读"。它因此并入 `applyExemptDefaults` 已经在维护的那组豁免(确认对话框、会话行菜单):保持当前主题色、把 alpha 钉在 1,不撤模糊也不改字色,其它一律不动;钉的值同样要覆盖其子树,因为自定义属性只会向下传播,选项就在里面。
105
115
  - **`0.2.1` 整条线合成一个渠道,并逐构建点名。** `0.2.1-alpha.3` 把后台任务列表改画到共享的 `MenuSurface` 上——表面根没有 `role`,底色与磨砂落到了它的 `.material` 子层——所以运行时标记器现在也会认领 body 级的 `[data-menu-material]` 表面,顶栏选择器组同步加了对应分支;更早那种自己上色的 `<ul class*="_menu">` 形状继续匹配。从 `alpha.3` 到 `rc.2` 逐 tag 核对:适配层发言的文件一个都没动(`design-platform.css`、`MenuSurface`、`MenuGroup`、`AppFrame`、`ConversationRoot`、`TaskMenu`、`preload-windows`、五个头部槽位)。`SidebarRight.module.css` 只给面板外层加了 `overflow:clip`(fixed 浮动子节点仍会逃逸),插件页重构过但三个列表锚点都在。于是 `0.2.1-alpha` 与 `0.2.1-rc` 合为一个 `0.2.1` 渠道,覆盖 `alpha.1`…`rc.2`,每个构建一条 `SUPPORTED_RELEASES` 行(均为 `exact`),`engines`/peer/`dsh.compatibility` 逐一点名,事实目录改名为 `v0-2-1`。
106
116
 
107
- ### v0.3.7(菜单在模糊为 0 时保留宿主磨砂;左侧栏接管其上方条带;版本表只讲 0.2.x)
108
-
109
- - **「后台任务列表被对话内容压住」的真正原因,是「标题栏」卡片自己给标题行加的霜;修法是补层级,不是切状态。** 会话标题行不是一条空带:宿主把头部控件挂进行内(`conversation.session.header.actions|.utilities`),而这些控件就从行内升起浮层。`ui-jobs/JobListAction` 在直到 `0.2.1-alpha.1` 的每个 release 上都是就地弹出——`.root{position:relative}` 下挂 `.menu{position:absolute; top:calc(100% + 5px); z-index:100}`;你抓的那段 DOM 里 `style="left: 0px"` 既没有 `top` 也没有 `role`,正是这个就地形状,而 `0.2.1-alpha.2` 的 portal 只是把症状在最新构建上藏住,并没有修好它。`ui-schedule/TaskMenu`(`role="menu"`,非 portal 模式时 `position:absolute; z-index:100` 就地)在范围内每个 release 都挂在同一行里。给这一行加 `backdrop-filter` 会把行变成 STACKING CONTEXT,于是那些 `z-index:100` 不再是页面级取值,只能在行内部相互排序;整行随后按自身层级绘制,落在对话正文、输入位和 `.root` 底色之下。菜单不是被宿主降级的,是被我们关起来的。现在行带上补偿性的 `z-index:10`——高于菜单会盖到的列内部件(`.seat` 9、回到底部 8、页脚 7、`.tabs` 1),又远低于所有页面级表面(portal 菜单 100、模态 1000、统计 / 上下文面板 1100)——于是磨砂、底色、拖拽区都不再切换状态,而 portal 出去的列表仍像宿主预期那样盖住标题行。不写 `position`:该行本来就是 `ConversationRoot .root{display:flex}` 的 flex 子项,`z-index` 天然生效,这样插件就不去替宿主的盒子决定定位(issue #24)。本 releases 里第一次的尝试是 `:has()` 让位门控——行内持有头部浮层时就把霜撤掉:遮挡确实解了,但每次有人打开头部菜单整条标题栏都会重新上色,看起来像那两个滑块自己在动。那个做法已删除。
110
-
111
- - **菜单的模糊滑块停在 0 时,不再删掉宿主自己的磨砂。** 宿主本来就给每个菜单上霜——`--dsw-menu-backdrop-filter: blur(40px) saturate(150%)` 声明在 `body` 上,两种绘制形状都读这份被继承的值:自己上色的列表靠自身的 `backdrop-filter`,`MenuSurface` 卡片靠它的 `.material` 层。插件把这个令牌在菜单元素上重写成带 `none` 兜底的声明,而元素自己的声明永远压过它继承来的值:于是滑块在 0 时磨砂不是「没被改动」,而是**被抹掉了**——一张淡到底、背后什么都没蒙的菜单,会让对话正文直接从它后面透出来。(另有一个独立缺陷会让同一个浮层真的被埋住,见本版第一条。)两个菜单组的磨砂现在都改走 `<html>` 上的一对 `:where()` 门控(`dab-card-frost` / `dab-header-frost`),只有滑块非 0 时才生效——发送框那颗胶囊早就是这个做法。用 `:where()` 是因为门控必须不花优先级:本插件的菜单级联是**按书写顺序**决胜的(卡片组 → 顶栏组 → 豁免组),换成带权重的类前缀会把卡片举到它本该让位的豁免之上。豁免组那句 `none` 保持不变:`applyExemptDefaults` 同时把那些表面钉在 alpha 1,它是设计上就该不透明,不是坏掉。
112
-
113
- - **后台任务列表正是让这个 bug 现形的地方。** `0.2.1-alpha.2` 把它从头部行里 portal 到了 `document.body`,`position:fixed; z-index:100`,而且是个不带 `role` 的裸 `<ul>`——于是「磨砂缺失」这个缺陷终于有了能被看见的载体——列表直面整个页面,而不是挂在列内。而和它一起被报告的遮挡,成因见本版第一条。`MenuSurface` 自己的 `data-menu-material` 标记也加进了卡片组,原因同类的:`ModelSelect` 的模型面板渲染成 `role="group"`,只按 `[role="menu"]` 匹配时,那张卡会跟着滑块变淡、却永远不跟着变模糊。
114
-
115
- - **左侧栏和它上方那条标题带现在是一整个表面。** `AppFrame` 的条带是一个全宽盒子(`.frame::before`),它的左段正压在侧栏列上方,而宿主把那一列的宽度发在帧元素自己身上(`--dsh-windows-sidebar-width`,仅 Windows 构建、内联样式)。插件就按这个宽度切开:`::before` 裁到其右侧,继续拿「标题栏」的材质、霜与拖拽区;新增的 `::after` 用 `--dsh-any-op-sidebar`——侧栏列被画成的那一份颜色——涂上左段,模糊取侧栏模糊的根级镜像。一对滑块同时驱动这一列与其上方那条带,切口跟着拖动或折叠走(`0px` 时整条条带交还给「标题栏」)。
116
-
117
- - **自定义字体不再在 `0.2.1-alpha.2` 上被宿主悄悄抢走。** 那个 tag 把用户字体列表搬进 `--dsh-font-family-text`,并让宿主在自己的 `base.css` 里合成 `body{--dsw-font-family:var(--dsh-font-family-text,…)}` —— 与本插件写的是同一个令牌、同一个 `body` 选择器、同一个优先级,于是谁生效变成了「谁的样式表排在后面」。字体规则现在带 `!important`,与令牌表的做法一致;代码字体照旧不碰。
118
-
119
- - **版本表现在只对 `0.2.x` 发言。** `0.1.5-rc`、`0.1.6-alpha`、`0.1.7-alpha`、`0.1.7-rc` 四个目录、渠道与行都删掉了:插件里已经没有任何一条事实是按它们核对的,而一张没人再核对的表比没有表更糟。`engines.dsh`、受门禁的 peer 与 `dsh.compatibility` 现在从 `0.2.0-rc.1` 起步,也就是说更旧的宿主会在 profile 组装时被**明确拒绝**,而不是被一份错的事实悄悄改版式——DSHA 里包的 `0.1.5-rc.2` 也一样。`0.2.1-alpha.2` … `0.2.1-rc.2` 逐个 tag 比对核实(`design-platform.css`、`MenuSurface`、`MenuGroup`、`--dsw-menu-backdrop-filter` 的消费者集合、`AppFrame`、`ConversationRoot`、`TaskMenu`、头部槽位键与右侧栏面板——一个都没动),全部并入那个仍然成立的事实目录 `v0-2-1`,于是每个构建的判定都是 `exact`。
120
-
121
117
  ## 安装
122
118
 
123
119
  ### 方式一:npm 安装(推荐)