dsh-any-background 0.3.7 → 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 +25 -23
- package/README.zh.md +23 -23
- package/lib/client.js +457 -111
- package/lib/client.js.map +1 -1
- package/lib/index.js +43 -8
- package/package.json +16 -10
package/lib/client.js
CHANGED
|
@@ -57,6 +57,12 @@ window.__ModuleLoader__.load({
|
|
|
57
57
|
uiProduced: "产出物/高亮内容",
|
|
58
58
|
uiHeader: "顶栏选项",
|
|
59
59
|
uiHeaderBar: "标题栏",
|
|
60
|
+
captionInkLabel: "标题条带文字",
|
|
61
|
+
captionInkHint: "桌面端顶部那条标题带上的文字(原生「应用 / 编辑」菜单与侧栏开合按钮)由谁上色。条带是侧栏材质压在壁纸上的结果,两者可能一个深一个浅。",
|
|
62
|
+
captionInkAuto: "自动",
|
|
63
|
+
captionInkTheme: "跟随主题",
|
|
64
|
+
captionInkWhite: "始终白色",
|
|
65
|
+
captionInkBlack: "始终黑色",
|
|
60
66
|
fontPageTitle: "字体与描边",
|
|
61
67
|
fontTitle: "自定义字体",
|
|
62
68
|
fontHint: "支持 ttf/otf/woff/woff2 字体文件(上限 100MB),将应用到整个界面的文字;代码块的等宽字体保持不变。中文字体通常有几十 MB,首次上传与刷新加载需要片刻",
|
|
@@ -249,6 +255,12 @@ window.__ModuleLoader__.load({
|
|
|
249
255
|
uiProduced: "Produced / highlights",
|
|
250
256
|
uiHeader: "Header popovers",
|
|
251
257
|
uiHeaderBar: "Header bar",
|
|
258
|
+
captionInkLabel: "Caption strip text",
|
|
259
|
+
captionInkHint: "Who paints the text on the desktop caption strip — the native 「应用 / 编辑」 menu bar and the sidebar toggle. The strip is the sidebar material over the wallpaper, so it can end up on the opposite side of the theme.",
|
|
260
|
+
captionInkAuto: "Auto",
|
|
261
|
+
captionInkTheme: "Follow theme",
|
|
262
|
+
captionInkWhite: "Always white",
|
|
263
|
+
captionInkBlack: "Always black",
|
|
252
264
|
fontPageTitle: "Font & outline",
|
|
253
265
|
fontTitle: "Custom font",
|
|
254
266
|
fontHint: "Accepts ttf/otf/woff/woff2 files (up to 100MB) and applies them to the whole interface; code blocks keep their monospace stack. CJK fonts routinely span tens of MB, so the first upload and each reload take a moment",
|
|
@@ -518,6 +530,7 @@ window.__ModuleLoader__.load({
|
|
|
518
530
|
producedOpacity: .5,
|
|
519
531
|
headerOpacity: .5,
|
|
520
532
|
headerBarOpacity: .5,
|
|
533
|
+
captionInk: "auto",
|
|
521
534
|
profiles: [],
|
|
522
535
|
rotation: {
|
|
523
536
|
enabled: false,
|
|
@@ -539,6 +552,17 @@ window.__ModuleLoader__.load({
|
|
|
539
552
|
activeProfile: null
|
|
540
553
|
};
|
|
541
554
|
const clamp01 = (n, def) => typeof n === "number" ? Math.min(1, Math.max(0, n)) : def;
|
|
555
|
+
/** One place decides what a stored / adopted 「标题栏文字」 value means: anything the
|
|
556
|
+
* host half or an older config file did not name is `auto`, which is 0.3.8's behaviour. */
|
|
557
|
+
const CAPTION_INK_MODES = [
|
|
558
|
+
"auto",
|
|
559
|
+
"theme",
|
|
560
|
+
"white",
|
|
561
|
+
"black"
|
|
562
|
+
];
|
|
563
|
+
function adoptCaptionInk(v) {
|
|
564
|
+
return CAPTION_INK_MODES.includes(v) ? v : DEFAULT_CONFIG.captionInk;
|
|
565
|
+
}
|
|
542
566
|
let cfg = {
|
|
543
567
|
...DEFAULT_CONFIG,
|
|
544
568
|
opacities: { ...DEFAULT_CONFIG.opacities },
|
|
@@ -732,6 +756,10 @@ window.__ModuleLoader__.load({
|
|
|
732
756
|
function rHeaderBarOpacity() {
|
|
733
757
|
return clamp01(cfg.headerBarOpacity, DEFAULT_CONFIG.headerBarOpacity);
|
|
734
758
|
}
|
|
759
|
+
/** Who owns the desktop caption strip's chrome ink — see `CaptionInkMode`. */
|
|
760
|
+
function rCaptionInk() {
|
|
761
|
+
return adoptCaptionInk(cfg.captionInk);
|
|
762
|
+
}
|
|
735
763
|
function rBgState() {
|
|
736
764
|
return cfg.bgState;
|
|
737
765
|
}
|
|
@@ -968,6 +996,7 @@ window.__ModuleLoader__.load({
|
|
|
968
996
|
producedOpacity: clamp01(c.producedOpacity, DEFAULT_CONFIG.producedOpacity),
|
|
969
997
|
headerOpacity: clamp01(c.headerOpacity, DEFAULT_CONFIG.headerOpacity),
|
|
970
998
|
headerBarOpacity: clamp01(c.headerBarOpacity, DEFAULT_CONFIG.headerBarOpacity),
|
|
999
|
+
captionInk: adoptCaptionInk(c.captionInk),
|
|
971
1000
|
profiles: adoptProfiles(c.profiles),
|
|
972
1001
|
rotation: adoptRotation(c.rotation),
|
|
973
1002
|
schedule: adoptSchedule(c.schedule),
|
|
@@ -1032,12 +1061,32 @@ window.__ModuleLoader__.load({
|
|
|
1032
1061
|
release: "0.2.0-rc.2"
|
|
1033
1062
|
},
|
|
1034
1063
|
{
|
|
1035
|
-
channel: "0.2.1
|
|
1064
|
+
channel: "0.2.1",
|
|
1036
1065
|
release: "0.2.1-alpha.1"
|
|
1037
1066
|
},
|
|
1038
1067
|
{
|
|
1039
|
-
channel: "0.2.1
|
|
1068
|
+
channel: "0.2.1",
|
|
1040
1069
|
release: "0.2.1-alpha.2"
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
channel: "0.2.1",
|
|
1073
|
+
release: "0.2.1-alpha.3"
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
channel: "0.2.1",
|
|
1077
|
+
release: "0.2.1-alpha.4"
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
channel: "0.2.1",
|
|
1081
|
+
release: "0.2.1-alpha.5"
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
channel: "0.2.1",
|
|
1085
|
+
release: "0.2.1-rc.1"
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
channel: "0.2.1",
|
|
1089
|
+
release: "0.2.1-rc.2"
|
|
1041
1090
|
}
|
|
1042
1091
|
];
|
|
1043
1092
|
const UNKNOWN_HOST_INFO = {
|
|
@@ -1094,7 +1143,7 @@ window.__ModuleLoader__.load({
|
|
|
1094
1143
|
* release is outside everything this plugin has seen.
|
|
1095
1144
|
*
|
|
1096
1145
|
* A release is matched by PATCH LINE before anything else: `0.2.1-alpha.3` and a
|
|
1097
|
-
* future `0.2.1` stable both belong to the line verified at `0.2.1-alpha.1
|
|
1146
|
+
* future `0.2.1` stable both belong to the line verified at `0.2.1-alpha.1…rc.2`, and
|
|
1098
1147
|
* handing them that adapter beats giving up — the DOM probes only cover panel
|
|
1099
1148
|
* mechanics, not the surface decisions the line is known for.
|
|
1100
1149
|
*
|
|
@@ -1284,8 +1333,9 @@ window.__ModuleLoader__.load({
|
|
|
1284
1333
|
* painting element. A 15th file names the same token without consuming it:
|
|
1285
1334
|
* `ui-theme/src/styles/gradient-shadow-text.css` DECLARES it on `body` —
|
|
1286
1335
|
* `blur(40px) saturate(150%)`, no media query — which is exactly why writing the
|
|
1287
|
-
* token on a menu with a `none` fallback deletes a frost the host always had
|
|
1288
|
-
*
|
|
1336
|
+
* token on a menu with a `none` fallback deletes a frost the host always had.
|
|
1337
|
+
* That is now the intended reading of a 0 on the menu blur sliders — see the note
|
|
1338
|
+
* above `popoverBlurRule` in `wallpaper.ts`. `MenuSurface.tsx` / `.module.css` are
|
|
1289
1339
|
* byte-identical from rc.2 through `0.2.1-alpha.2`, and the consumer set moved only
|
|
1290
1340
|
* in line numbers (the same 15 files at every tag in the table, checked per tag).
|
|
1291
1341
|
*
|
|
@@ -1510,10 +1560,10 @@ window.__ModuleLoader__.load({
|
|
|
1510
1560
|
};
|
|
1511
1561
|
}
|
|
1512
1562
|
//#endregion
|
|
1513
|
-
//#region src/client/host-compat/versions/v0-2-1
|
|
1563
|
+
//#region src/client/host-compat/versions/v0-2-1/adapter.ts
|
|
1514
1564
|
/**
|
|
1515
|
-
* Adapter for the DSH `0.2.1
|
|
1516
|
-
* `
|
|
1565
|
+
* Adapter for the DSH `0.2.1` patch line, verified at `0.2.1-alpha.1`,
|
|
1566
|
+
* `alpha.2`, `alpha.3`, `alpha.4`, `alpha.5`, `rc.1` and `rc.2`.
|
|
1517
1567
|
*
|
|
1518
1568
|
* A new MINOR line opened with a prerelease, and the answer is the `0.2.0-rc.2`
|
|
1519
1569
|
* folder's — which is a statement this file has to earn, because "same as the
|
|
@@ -1537,6 +1587,21 @@ window.__ModuleLoader__.load({
|
|
|
1537
1587
|
* · `TextShimmer` still marks its root `data-shimmer`, so the stroke exemption
|
|
1538
1588
|
* written for rc.1 stands.
|
|
1539
1589
|
*
|
|
1590
|
+
* Re-checked again across the rest of the line (`alpha.3` … `rc.2`, `git diff
|
|
1591
|
+
* <tag> rc.2` on every file above): the menu token contract, both menu stylesheets
|
|
1592
|
+
* (`MenuSurface`, `MenuGroup`), `gradient-shadow-text.css` / `base.css`,
|
|
1593
|
+
* `AppFrame.*`, `ConversationRoot`, `TaskMenu`, `preload-windows` and the five
|
|
1594
|
+
* header slot keys are unchanged through `rc.2`. Two deltas, neither a fact here:
|
|
1595
|
+
* · `ui-sidebar-right/SidebarRight.module.css` gained `overflow:clip` on the
|
|
1596
|
+
* panel wrapper (hidden docked items no longer widen the frame's scrollable
|
|
1597
|
+
* overflow; fixed floating children still escape). The slide owner and the
|
|
1598
|
+
* element the frost rides are untouched, and the plugin never promotes the
|
|
1599
|
+
* wrapper on this line.
|
|
1600
|
+
* · `PluginManagerPage.tsx` was refactored and now emits `data-plugin-scope`
|
|
1601
|
+
* groups for `global`, `preset` and `more`. The anchors this plugin frames
|
|
1602
|
+
* (`[data-plugin-panel]`, `[data-plugin-scope] > ul`, `[data-plugin-loading] >
|
|
1603
|
+
* ul`) are still all there, and the rule is scope-value agnostic.
|
|
1604
|
+
*
|
|
1540
1605
|
* Re-checked at `0.2.1-alpha.2`, same list: `design-platform.css` still byte-identical
|
|
1541
1606
|
* (`--dsw-menu-surface-fill` / `--dsw-specific-menu` / `--dsw-alias-menu-group-header-fill`
|
|
1542
1607
|
* on both scheme blocks); `MenuSurface.tsx`/`.module.css` byte-identical (blob hashes
|
|
@@ -1613,8 +1678,8 @@ window.__ModuleLoader__.load({
|
|
|
1613
1678
|
}
|
|
1614
1679
|
function createAdapter$1(host) {
|
|
1615
1680
|
return {
|
|
1616
|
-
id: "0.2.1-alpha.1
|
|
1617
|
-
channel: "0.2.1
|
|
1681
|
+
id: "0.2.1-alpha.1…rc.2",
|
|
1682
|
+
channel: "0.2.1",
|
|
1618
1683
|
host,
|
|
1619
1684
|
panelFragments: panelFragments$1,
|
|
1620
1685
|
pluginPageRule: PLUGIN_PAGE_FROST_RULE,
|
|
@@ -1672,7 +1737,7 @@ window.__ModuleLoader__.load({
|
|
|
1672
1737
|
const ADAPTERS = {
|
|
1673
1738
|
"0.2.0-rc": createAdapter$3,
|
|
1674
1739
|
"0.2.0-rc.2": createAdapter$2,
|
|
1675
|
-
"0.2.1
|
|
1740
|
+
"0.2.1": createAdapter$1,
|
|
1676
1741
|
unknown: createAdapter
|
|
1677
1742
|
};
|
|
1678
1743
|
/** Pick the adapter for a resolved host. `host.channel` is already narrowed to
|
|
@@ -2126,23 +2191,37 @@ window.__ModuleLoader__.load({
|
|
|
2126
2191
|
* A header dropdown can only be open while its trigger reports
|
|
2127
2192
|
* `aria-expanded="true"`, and opening one is what mounts the popover. So a
|
|
2128
2193
|
* popover tag is maintained as: "there is at least one expanded header trigger,
|
|
2129
|
-
* AND this popover is
|
|
2194
|
+
* AND this popover is a header candidate" — see `isHeaderSurface` for the two
|
|
2195
|
+
* shapes that qualify (portaled to `<body>`, or mounted inside one of the
|
|
2196
|
+
* header's own slot anchors) and for what stays foreign. Popovers are only ever
|
|
2130
2197
|
* tagged/released on a change of that combined state, which makes the tag
|
|
2131
2198
|
* self-consistent and idempotent.
|
|
2132
2199
|
*
|
|
2133
2200
|
* The one hazard is a popover that is open for an unrelated reason (a composer
|
|
2134
|
-
* menu, a hover card) *while* a header trigger stays expanded. Such a popover
|
|
2135
|
-
*
|
|
2136
|
-
*
|
|
2137
|
-
* dropdowns are transient (opening one closes the others), so this is a
|
|
2138
|
-
* rather than the common path.
|
|
2201
|
+
* menu, a hover card) *while* a header trigger stays expanded. Such a popover is
|
|
2202
|
+
* not a candidate: it is neither portaled nor a descendant of a header anchor,
|
|
2203
|
+
* and the composer's in-place lists keep belonging to the card group. In practice
|
|
2204
|
+
* header dropdowns are transient (opening one closes the others), so this is a
|
|
2205
|
+
* guard rather than the common path.
|
|
2206
|
+
*
|
|
2207
|
+
* THE SURFACE MARKER IS PART OF THE CANDIDATE SET (`0.2.1-alpha.3` and up).
|
|
2208
|
+
* That release moved the background-job list onto the shared `MenuSurface`, whose
|
|
2209
|
+
* root carries NO role — the `<ul>` inside it is now a paint-free `.menuBody`
|
|
2210
|
+
* (`JobListAction.module.css` dropped its own `background`/`backdrop-filter`), and
|
|
2211
|
+
* the fill plus the frost live on the surface's `.material` child. Keyed on roles
|
|
2212
|
+
* alone the tagger went blind exactly when the paint moved one level up, and the
|
|
2213
|
+
* job list kept the stock look. `data-menu-material` is the surface's own semantic
|
|
2214
|
+
* marker (`MenuSurface.tsx`), so the tag rides it: the tokens land on the surface
|
|
2215
|
+
* root, which is the nearest ancestor of `.material`, and inherit down into it.
|
|
2216
|
+
* The pre-migration shape (a self-painting `<ul class*="_menu">`) stays matched by
|
|
2217
|
+
* the header group's class-shape arm, so both eras are styled by one rule set.
|
|
2139
2218
|
*
|
|
2140
2219
|
* @module
|
|
2141
2220
|
*/
|
|
2142
2221
|
/** Marker attribute the stylesheet keys on. */
|
|
2143
2222
|
const HEADER_POPOVER_ATTR = "data-dsh-any-header-popover";
|
|
2144
2223
|
/** Portaled popovers a header trigger can own. */
|
|
2145
|
-
const POPOVER_SELECTOR = "[role=\"menu\"], [role=\"tree\"], [role=\"dialog\"]";
|
|
2224
|
+
const POPOVER_SELECTOR = "[role=\"menu\"], [role=\"tree\"], [role=\"dialog\"], [data-menu-material]";
|
|
2146
2225
|
/** Which session-header slot anchors to watch — supplied by the host adapter,
|
|
2147
2226
|
* because the set is not the same on every release: 0.1.6-alpha.2 registers a
|
|
2148
2227
|
* sixth (`.leading`) that neither the line before nor the line after has.
|
|
@@ -2162,20 +2241,54 @@ window.__ModuleLoader__.load({
|
|
|
2162
2241
|
for (const anchor of document.querySelectorAll(headerSlotAnchors())) if (anchor.querySelector("[aria-expanded=\"true\"]") !== null) return true;
|
|
2163
2242
|
return false;
|
|
2164
2243
|
}
|
|
2165
|
-
/**
|
|
2166
|
-
*
|
|
2167
|
-
*
|
|
2168
|
-
* its
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2244
|
+
/** Whether this popover can belong to the header.
|
|
2245
|
+
*
|
|
2246
|
+
* Two shapes, and the second one is the reason this is not just "portaled to body":
|
|
2247
|
+
* `session-log-export/HeaderAction.tsx` renders its `Menu` WITHOUT the `portal` prop,
|
|
2248
|
+
* so the card is mounted in place, inside `[data-slot="conversation.session.header
|
|
2249
|
+
* .utilities"]` — a DOM descendant of the row, exactly where a header trigger's own
|
|
2250
|
+
* popover belongs. Keyed on `parentElement === body` alone it was never claimable, and
|
|
2251
|
+
* the only thing that styled it at all was the header group's
|
|
2252
|
+
* `[role="menu"][class*="_denseList"]:not([class*="_portal"])` arm — which dies the
|
|
2253
|
+
* moment the host drops `dense` (or adds a header popover that is not a dense menu),
|
|
2254
|
+
* and leaves the surface following the card slider while it sits in the title row.
|
|
2255
|
+
*
|
|
2256
|
+
* A popover that is neither portaled nor inside a header anchor stays foreign: that is
|
|
2257
|
+
* the composer's in-place lists (permission / command / model), which the card group
|
|
2258
|
+
* owns and this group must never reach. */
|
|
2259
|
+
function isHeaderSurface(el) {
|
|
2260
|
+
if (el.getAttribute("aria-modal") === "true") return false;
|
|
2261
|
+
return el.parentElement === document.body || el.closest(headerSlotAnchors()) !== null;
|
|
2172
2262
|
}
|
|
2173
2263
|
let observer = null;
|
|
2174
|
-
let
|
|
2264
|
+
let pending = null;
|
|
2265
|
+
/** False once the effect is torn down, so a pass already queued on the microtask queue
|
|
2266
|
+
* cannot re-mark a popover after the teardown cleared it. */
|
|
2267
|
+
let active = false;
|
|
2268
|
+
/** Coalesce the observer's batches into ONE pass, but flush it on the microtask queue
|
|
2269
|
+
* rather than a animation frame.
|
|
2270
|
+
*
|
|
2271
|
+
* The frame version was the bug, not the batching: `requestAnimationFrame` callbacks
|
|
2272
|
+
* do not run while a window is not rendering — a hidden tab, an occluded or minimized
|
|
2273
|
+
* desktop window, a background Electron surface — and a queued frame never fires, so
|
|
2274
|
+
* the tag never lands and the 「顶栏选项」 pair silently keeps whatever the card group
|
|
2275
|
+
* gave the panel. Observed live: with the page not painting, an open header menu stayed
|
|
2276
|
+
* `data-dsh-any-header-popover`-less indefinitely, and the same menu was tagged the
|
|
2277
|
+
* moment rAF was made to run. A `MutationObserver` callback is already delivered once
|
|
2278
|
+
* per batch at microtask time, so a microtask flush coalesces exactly as well and has no
|
|
2279
|
+
* dependency on the compositor at all — and it lands in the SAME frame the popover
|
|
2280
|
+
* mounts, which also removes the one-frame flash of card styling on open. */
|
|
2281
|
+
function schedule() {
|
|
2282
|
+
if (pending !== null || !active) return;
|
|
2283
|
+
pending = Promise.resolve().then(() => {
|
|
2284
|
+
pending = null;
|
|
2285
|
+
if (active) refreshHeaderPopovers();
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2175
2288
|
/** Recompute tags from the current open state. */
|
|
2176
2289
|
function refreshHeaderPopovers() {
|
|
2177
2290
|
const open = headerTriggerOpen();
|
|
2178
|
-
const candidates = [...document.querySelectorAll(POPOVER_SELECTOR)].filter(
|
|
2291
|
+
const candidates = [...document.querySelectorAll(POPOVER_SELECTOR)].filter(isHeaderSurface);
|
|
2179
2292
|
if (!open) {
|
|
2180
2293
|
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
2181
2294
|
return;
|
|
@@ -2183,16 +2296,10 @@ window.__ModuleLoader__.load({
|
|
|
2183
2296
|
for (const el of candidates) el.setAttribute(HEADER_POPOVER_ATTR, "");
|
|
2184
2297
|
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) if (!candidates.includes(el)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
2185
2298
|
}
|
|
2186
|
-
function schedule() {
|
|
2187
|
-
if (raf !== null) return;
|
|
2188
|
-
raf = window.requestAnimationFrame(() => {
|
|
2189
|
-
raf = null;
|
|
2190
|
-
refreshHeaderPopovers();
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
2299
|
/** Start tagging. Returns a teardown that stops observing and clears markings. */
|
|
2194
2300
|
function startHeaderPopoverTagging() {
|
|
2195
2301
|
if (typeof MutationObserver === "undefined" || typeof document === "undefined") return () => {};
|
|
2302
|
+
active = true;
|
|
2196
2303
|
refreshHeaderPopovers();
|
|
2197
2304
|
observer = new MutationObserver(schedule);
|
|
2198
2305
|
observer.observe(document.body, {
|
|
@@ -2202,12 +2309,9 @@ window.__ModuleLoader__.load({
|
|
|
2202
2309
|
attributeFilter: ["aria-expanded"]
|
|
2203
2310
|
});
|
|
2204
2311
|
return () => {
|
|
2312
|
+
active = false;
|
|
2205
2313
|
observer?.disconnect();
|
|
2206
2314
|
observer = null;
|
|
2207
|
-
if (raf !== null) {
|
|
2208
|
-
window.cancelAnimationFrame(raf);
|
|
2209
|
-
raf = null;
|
|
2210
|
-
}
|
|
2211
2315
|
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
2212
2316
|
};
|
|
2213
2317
|
}
|
|
@@ -3563,16 +3667,40 @@ window.__ModuleLoader__.load({
|
|
|
3563
3667
|
if (rSchemeOverride() !== "auto") return genTokens(220, .04, scheme === "dark" ? .14 : .92, scheme).tokens;
|
|
3564
3668
|
return null;
|
|
3565
3669
|
}
|
|
3566
|
-
/** Surface colors the opacity sliders fade when the plugin has NO palette of
|
|
3567
|
-
*
|
|
3568
|
-
*
|
|
3569
|
-
*
|
|
3570
|
-
*
|
|
3571
|
-
*
|
|
3572
|
-
*
|
|
3670
|
+
/** Surface colors the opacity sliders fade when the plugin has NO palette of its own
|
|
3671
|
+
* (no picked color, no forced scheme), read live from the host's own resolved tokens —
|
|
3672
|
+
* the same trick applyPanelOverrides uses for the workbench panel. The sliders only
|
|
3673
|
+
* ever supply the alpha: the host still decides the colors, so a custom host skin
|
|
3674
|
+
* survives. Returns null when the host exposes none.
|
|
3675
|
+
*
|
|
3676
|
+
* SAMPLED ON `<body>`, NOT `:root`: `design-platform.css` declares this whole token set
|
|
3677
|
+
* inside a `body { … }` block (light at :166, dark at :284), and a custom property is
|
|
3678
|
+
* inherited DOWNWARD only — read off the root element every one of them is the empty
|
|
3679
|
+
* string, this function answers null, `applyCustomTokensNow` takes its `surfaces ===
|
|
3680
|
+
* null` exit, and on an install with no picked color every interface-opacity slider does
|
|
3681
|
+
* nothing at all (the frame never gets cleared either, so the wallpaper stays hidden).
|
|
3682
|
+
* Measured against the host's own declarations: `:root` → all five tokens empty,
|
|
3683
|
+
* `body` → all five resolve.
|
|
3684
|
+
*
|
|
3685
|
+
* …AND CACHED PER HOST GENERATION, with our own sheet switched off while reading: the
|
|
3686
|
+
* plugin re-scopes these same names on `body`
|
|
3687
|
+
* (`body{--dsw-specific-menu:var(--dsh-any-op-menu)!important}`), so a second read after
|
|
3688
|
+
* the first write returns the plugin's OWN substituted literal — the surfaces would
|
|
3689
|
+
* freeze at whatever color they first sampled instead of following the host. The
|
|
3690
|
+
* generation key is what can flip the host's palette without anything in this plugin
|
|
3691
|
+
* moving: its dark-mode marker, and the platform attribute (`[data-platform='darwin']`
|
|
3692
|
+
* re-declares `--dsw-specific-menu`). */
|
|
3693
|
+
let hostSurfaceSample = null;
|
|
3573
3694
|
function readHostOpacityTokens() {
|
|
3574
|
-
if (typeof getComputedStyle === "undefined") return null;
|
|
3575
|
-
const
|
|
3695
|
+
if (typeof getComputedStyle === "undefined" || typeof document === "undefined") return null;
|
|
3696
|
+
const body = document.body;
|
|
3697
|
+
if (body === null) return null;
|
|
3698
|
+
const key = `${body.getAttribute("data-ds-dark-theme") ?? ""}|${document.documentElement.getAttribute("data-platform") ?? ""}`;
|
|
3699
|
+
if (hostSurfaceSample !== null && hostSurfaceSample.key === key) return hostSurfaceSample.map;
|
|
3700
|
+
const sheet = tokenStyleEl;
|
|
3701
|
+
const wasDisabled = sheet === null ? false : sheet.disabled;
|
|
3702
|
+
if (sheet !== null) sheet.disabled = true;
|
|
3703
|
+
const cs = getComputedStyle(body);
|
|
3576
3704
|
const out = {};
|
|
3577
3705
|
let found = false;
|
|
3578
3706
|
for (const g of OPACITY_TOKEN_GROUPS) for (const name of g.names) {
|
|
@@ -3581,7 +3709,12 @@ window.__ModuleLoader__.load({
|
|
|
3581
3709
|
out[name] = v;
|
|
3582
3710
|
found = true;
|
|
3583
3711
|
}
|
|
3584
|
-
|
|
3712
|
+
if (sheet !== null) sheet.disabled = wasDisabled;
|
|
3713
|
+
hostSurfaceSample = {
|
|
3714
|
+
key,
|
|
3715
|
+
map: found ? out : null
|
|
3716
|
+
};
|
|
3717
|
+
return hostSurfaceSample.map;
|
|
3585
3718
|
}
|
|
3586
3719
|
function applyCustomTokensNow(ops) {
|
|
3587
3720
|
const hasColor = rHasColor();
|
|
@@ -3661,34 +3794,30 @@ window.__ModuleLoader__.load({
|
|
|
3661
3794
|
function menuFrost(blurVar) {
|
|
3662
3795
|
return hostAdapter().menus.frostDecl(`var(${blurVar},none)`);
|
|
3663
3796
|
}
|
|
3664
|
-
/** The
|
|
3797
|
+
/** The frost token, asserted ALWAYS — and what that means for the slider's bottom end.
|
|
3665
3798
|
*
|
|
3666
|
-
*
|
|
3667
|
-
*
|
|
3668
|
-
*
|
|
3669
|
-
*
|
|
3670
|
-
*
|
|
3671
|
-
*
|
|
3672
|
-
* fallback does not "add no blur" at slider 0 — it DELETES the host's frost. The
|
|
3673
|
-
* result is a faded menu with nothing behind it: the page shows through it crisp and
|
|
3674
|
-
* bright, so the popup stops reading as a raised surface at all. (This is a DIFFERENT
|
|
3675
|
-
* defect from the reported "the background-job list is under the conversation" one,
|
|
3676
|
-
* which is a stacking-context injury the row takes with its own frost — see the
|
|
3677
|
-
* `z-index` note on `HEADER_BAR_RULE`. Both were live at once on `0.2.0-rc.1`.)
|
|
3678
|
-
* `none` stays correct for the exempt group: `applyExemptDefaults` pins those
|
|
3679
|
-
* surfaces at alpha 1 alongside the cleared frost, so they are opaque by design.
|
|
3799
|
+
* The host declares its own menu frost on `body` (`ui-theme/src/styles/gradient-shadow-text.css`
|
|
3800
|
+
* — `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`, no media query), and every menu
|
|
3801
|
+
* frosts from that INHERITED value, whether it filters itself (the background-job `<ul>`) or
|
|
3802
|
+
* hands the value to a `MenuSurface` `.material` child. An element's own declaration always
|
|
3803
|
+
* beats what it inherits, so writing the token with a `none` fallback does not "add no blur"
|
|
3804
|
+
* at slider 0 — it DELETES the host's frost.
|
|
3680
3805
|
*
|
|
3681
|
-
*
|
|
3682
|
-
*
|
|
3683
|
-
*
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
*
|
|
3688
|
-
*
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3806
|
+
* 0.3.7 answered that with a `<html>` class gate (`dab-card-frost` / `dab-header-frost`) that
|
|
3807
|
+
* only let the declaration through while the slider was non-zero, so 0 meant "leave the host
|
|
3808
|
+
* alone". That was the wrong trade and this is the report that says so: with the gate on, the
|
|
3809
|
+
* slider's LOW end is its HIGH end — 0 renders the host's 40 px frost and 1 renders 1 px — so
|
|
3810
|
+
* the control reads as dead in both directions, and 「把模糊关掉」 is simply not reachable
|
|
3811
|
+
* (measured in `dsh web` with 顶栏选项 at 0/0: the open-in-app panel still came out a soft,
|
|
3812
|
+
* opaque-looking card, because what it was showing was the host's blur, not the plugin's).
|
|
3813
|
+
* The token is therefore asserted at every value, `none` included: **0 means no frost**.
|
|
3814
|
+
* Nothing loses the host look by accident, because the part DEFAULTS are 30 px — a stock
|
|
3815
|
+
* install is frosted like the host's and only an explicit drag to 0 clears it. The same is
|
|
3816
|
+
* already true of every other blur slider in this plugin (0 = off), which is the interface
|
|
3817
|
+
* consistency the gate broke.
|
|
3818
|
+
*
|
|
3819
|
+
* `none` stays correct for the exempt group as well: `applyExemptDefaults` pins those surfaces
|
|
3820
|
+
* at alpha 1 alongside the cleared frost, so they are opaque by design. */
|
|
3692
3821
|
/** Declare EVERY token this release's menus can paint their fill from with the same
|
|
3693
3822
|
* value. One slider, one value, every layer reached — and a token the host does not
|
|
3694
3823
|
* read is inert, so this is safe to emit on a line that only needs one.
|
|
@@ -3702,7 +3831,7 @@ window.__ModuleLoader__.load({
|
|
|
3702
3831
|
return hostAdapter().menus.fillTokens.map((name) => `${name}:${value}`).join(";");
|
|
3703
3832
|
}
|
|
3704
3833
|
function popoverBlurRule() {
|
|
3705
|
-
return `${
|
|
3834
|
+
return `${POPOVER_MENU_SEL},${MENU_MATERIAL_SEL}{${menuFrost(CARD_BLUR_VAR)}}${POPOVER_OTHER_SEL}{${elementFrostDecl(`var(${CARD_BLUR_VAR},none)`)}}`;
|
|
3706
3835
|
}
|
|
3707
3836
|
const INPUT_FROST_CLASS = "dab-input-frost";
|
|
3708
3837
|
const TAKEOVER_FROST_CLASS = "dab-takeover-frost";
|
|
@@ -3755,6 +3884,8 @@ window.__ModuleLoader__.load({
|
|
|
3755
3884
|
}
|
|
3756
3885
|
const MOBILE_HEADER_RULE = ".dsh-mobile-app-header{background:var(--dsh-any-op-bg,transparent)!important;backdrop-filter:var(--dsh-any-part-blur-global,none);-webkit-backdrop-filter:var(--dsh-any-part-blur-global,none)}";
|
|
3757
3886
|
const HEADER_BAR_CLASS = "dab-header-bar";
|
|
3887
|
+
/** Added to the same row only while its blur is non-zero — see `HEADER_BAR_RULE`. */
|
|
3888
|
+
const HEADER_BAR_FROST_CLASS = "dab-header-bar-frost";
|
|
3758
3889
|
const HEADER_BAR_ANCHOR = "[data-slot^=\"conversation.session.header\"]";
|
|
3759
3890
|
/** The session header bar, or null where this host has no such element.
|
|
3760
3891
|
*
|
|
@@ -3772,13 +3903,35 @@ window.__ModuleLoader__.load({
|
|
|
3772
3903
|
if (bar.className.includes("headerBlank")) return null;
|
|
3773
3904
|
return bar;
|
|
3774
3905
|
}
|
|
3775
|
-
/** Exactly one owner: the row the current conversation mounted. A row that went
|
|
3776
|
-
* blank, or one the host replaced, must not keep a class from an earlier pass. */
|
|
3777
3906
|
let headerBarEl = null;
|
|
3778
|
-
|
|
3779
|
-
|
|
3907
|
+
/** Exactly one owner: the row the current conversation mounted. A row that went
|
|
3908
|
+
* blank, or one the host replaced, must not keep a class — or a `position` — from an
|
|
3909
|
+
* earlier pass. `frost` decides whether the row also carries the underlay arm, and the
|
|
3910
|
+
* underlay's containing block is written inline only while it is needed. */
|
|
3911
|
+
function releaseHeaderBar(bar) {
|
|
3912
|
+
bar.classList.remove(HEADER_BAR_CLASS, HEADER_BAR_FROST_CLASS);
|
|
3913
|
+
if (bar.getAttribute(TAKEOVER_POS_ATTR) === "1") {
|
|
3914
|
+
bar.style.removeProperty("position");
|
|
3915
|
+
bar.removeAttribute(TAKEOVER_POS_ATTR);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
function claimHeaderBar(bar, frost) {
|
|
3919
|
+
if (headerBarEl !== null && headerBarEl !== bar) releaseHeaderBar(headerBarEl);
|
|
3920
|
+
if (bar === null) {
|
|
3921
|
+
headerBarEl = null;
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3780
3924
|
headerBarEl = bar;
|
|
3781
|
-
|
|
3925
|
+
bar.classList.add(HEADER_BAR_CLASS);
|
|
3926
|
+
if (!frost) {
|
|
3927
|
+
bar.classList.remove(HEADER_BAR_FROST_CLASS);
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
if (getComputedStyle(bar).position === "static") {
|
|
3931
|
+
bar.style.position = "relative";
|
|
3932
|
+
bar.setAttribute(TAKEOVER_POS_ATTR, "1");
|
|
3933
|
+
}
|
|
3934
|
+
bar.classList.add(HEADER_BAR_FROST_CLASS);
|
|
3782
3935
|
}
|
|
3783
3936
|
/** The bar's paint. The variables are written by `applyHeaderBar`; the class is
|
|
3784
3937
|
* only ever added when they are, so a host the plugin is not theming keeps its
|
|
@@ -3818,12 +3971,27 @@ window.__ModuleLoader__.load({
|
|
|
3818
3971
|
* filter never had to be blamed for its anchoring). 10 sits in that gap: above 9, far
|
|
3819
3972
|
* below 100, so a portaled list still covers the title row exactly as the host intends.
|
|
3820
3973
|
*
|
|
3821
|
-
* `z-index` needs no `position` here and none is written: the row is
|
|
3822
|
-
* `ConversationRoot .root{display:flex;flex-direction:column}` on every
|
|
3823
|
-
* and z-index applies to flex items as they are. Writing
|
|
3824
|
-
* make the row a containing block — and deciding a host
|
|
3825
|
-
* race this plugin lost once already (issue #24).
|
|
3826
|
-
|
|
3974
|
+
* `z-index` needs no `position` here and none is written by the BASE rule: the row is
|
|
3975
|
+
* a flex ITEM of `ConversationRoot .root{display:flex;flex-direction:column}` on every
|
|
3976
|
+
* release in range, and z-index applies to flex items as they are. Writing
|
|
3977
|
+
* `position:relative` would also make the row a containing block — and deciding a host
|
|
3978
|
+
* box's positioning is the exact race this plugin lost once already (issue #24).
|
|
3979
|
+
*
|
|
3980
|
+
* THE FROST DOES NOT SIT ON THE ROW. That was the first version of this rule, and it
|
|
3981
|
+
* cost the row's own children their backdrop: `backdrop-filter` makes an element a
|
|
3982
|
+
* BACKDROP ROOT as well as a stacking context, so an in-place header popover (the
|
|
3983
|
+
* session-log `…` menu, which `HeaderAction.tsx` renders without `portal`) could only
|
|
3984
|
+
* ever blur what the row itself paints — a flat tint — and the 「标题栏」 blur slider
|
|
3985
|
+
* read as dead on exactly the panel that hangs off the row. `isolation:isolate` is a
|
|
3986
|
+
* stacking context WITHOUT being a backdrop root, so the frost now rides a `::before`
|
|
3987
|
+
* underlay like it does on every other host part this plugin filters (`PART_BLUR_RULE`,
|
|
3988
|
+
* `inputBlurRule`), and the row's children sample the wallpaper again. The underlay
|
|
3989
|
+
* needs the row to be its containing block, which is why the frost arm adds
|
|
3990
|
+
* `position:relative` — INLINE, only when the host left the row `static`, and marked
|
|
3991
|
+
* `data-dab-pos-patched` so only this writer takes it back: the same guard
|
|
3992
|
+
* `setBlur` uses, for the same reason (a stylesheet `position` would cascade against
|
|
3993
|
+
* whatever the host or another plugin decides that box's position is). */
|
|
3994
|
+
const HEADER_BAR_RULE = `.${HEADER_BAR_CLASS}{background:var(--dsh-any-op-header-bar,transparent)!important;z-index:10}.${HEADER_BAR_FROST_CLASS}{isolation:isolate}.${HEADER_BAR_FROST_CLASS}::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;-webkit-backdrop-filter:var(--dsh-any-blur-header-bar,none);backdrop-filter:var(--dsh-any-blur-header-bar,none)}`;
|
|
3827
3995
|
/** The 「标题栏」card's paint: the card's material at the card's opacity, empty
|
|
3828
3996
|
* where the plugin is not theming. The conversation header row, the Windows
|
|
3829
3997
|
* caption band, and — through the desktop preload's probe — the native
|
|
@@ -3842,7 +4010,7 @@ window.__ModuleLoader__.load({
|
|
|
3842
4010
|
const bar = discoverHeaderBar();
|
|
3843
4011
|
const paint = headerBarPaint(src);
|
|
3844
4012
|
if (paint === "") {
|
|
3845
|
-
claimHeaderBar(null);
|
|
4013
|
+
claimHeaderBar(null, false);
|
|
3846
4014
|
root.style.removeProperty("--dsh-any-op-header-bar");
|
|
3847
4015
|
root.style.removeProperty("--dsh-any-blur-header-bar");
|
|
3848
4016
|
applyNativeCaption(surfaces);
|
|
@@ -3852,12 +4020,96 @@ window.__ModuleLoader__.load({
|
|
|
3852
4020
|
const px = rBlurs().headerBar;
|
|
3853
4021
|
if (px > 0) root.style.setProperty("--dsh-any-blur-header-bar", `blur(${px}px)`);
|
|
3854
4022
|
else root.style.removeProperty("--dsh-any-blur-header-bar");
|
|
3855
|
-
claimHeaderBar(bar);
|
|
4023
|
+
claimHeaderBar(bar, px > 0);
|
|
3856
4024
|
applyNativeCaption(surfaces);
|
|
3857
4025
|
}
|
|
3858
4026
|
const CAPTION_FRAME_CLASS = "dab-frame-caption";
|
|
3859
4027
|
const FLAT_CENTER_CLASS = "dab-center-flat";
|
|
3860
4028
|
const WCO_PROBE_SEL = "body > span[style*=\"--dsw-specific-sidebar-fill\"]";
|
|
4029
|
+
/** Marks the sidebar's own caption controls — the toggle and, while collapsed,
|
|
4030
|
+
* the new-session button. Both are `position:fixed` and float over the band, so
|
|
4031
|
+
* they inherit the band's ink instead of the sidebar's. */
|
|
4032
|
+
const CAPTION_INK_ATTR = "data-dab-caption-ink";
|
|
4033
|
+
/** The strip's chrome ink, and its collapsed-state variant.
|
|
4034
|
+
*
|
|
4035
|
+
* WHY THIS IS NOT SIMPLY THE LABEL INK: the plugin flips the label tokens against the
|
|
4036
|
+
* PICKED PALETTE (white on a dark pick), but the caption band is that palette at
|
|
4037
|
+
* the sidebar/header-bar opacity OVER the wallpaper. At a low opacity the strip
|
|
4038
|
+
* is mostly wallpaper, so white ink lands on a light band and the toggle
|
|
4039
|
+
* disappears (reported on the desktop build, on a 22%-opacity sidebar over a bright
|
|
4040
|
+
* wallpaper). The strip therefore derives its own ink from what it is actually
|
|
4041
|
+
* painted on: past ~85% opacity the surface dominates and the palette direction
|
|
4042
|
+
* is the truth; below it the wallpaper's brightness verdict decides.
|
|
4043
|
+
*
|
|
4044
|
+
* WHY THAT DERIVATION IS NOT THE WHOLE ANSWER (issue #30): the same derivation makes
|
|
4045
|
+
* the native caption menubar (「应用 / 编辑」) black on a dark theme whose sidebar is
|
|
4046
|
+
* translucent over a bright wallpaper — the strip really is light there, so the strip
|
|
4047
|
+
* is right and the rest of the window is wrong, and the two reports of this same
|
|
4048
|
+
* situation asked for opposite outcomes. So the policy is the user's: 「标题栏文字」
|
|
4049
|
+
* picks the winner, and `auto` keeps the strip-following behaviour that fixed the
|
|
4050
|
+
* invisible toggle. `theme` returns null, which removes every ink declaration this
|
|
4051
|
+
* function feeds — the menubar and the sidebar's fixed controls then read the host's
|
|
4052
|
+
* own label tokens, exactly as they did before 0.3.8.
|
|
4053
|
+
*
|
|
4054
|
+
* Returned as `{ ink, collapsedInk }` because the strip's material changes with
|
|
4055
|
+
* the sidebar: expanded it is the sidebar segment, collapsed it is the 「标题栏」
|
|
4056
|
+
* card's band. `null` means "no override" — either no verdict and no palette
|
|
4057
|
+
* direction, or the user asked for the theme's own ink. */
|
|
4058
|
+
function captionInkPair() {
|
|
4059
|
+
const mode = rCaptionInk();
|
|
4060
|
+
if (mode === "theme") return null;
|
|
4061
|
+
if (mode === "white") return {
|
|
4062
|
+
ink: "#fff",
|
|
4063
|
+
collapsedInk: "#fff"
|
|
4064
|
+
};
|
|
4065
|
+
if (mode === "black") return {
|
|
4066
|
+
ink: "#000",
|
|
4067
|
+
collapsedInk: "#000"
|
|
4068
|
+
};
|
|
4069
|
+
const verdict = rBgDark();
|
|
4070
|
+
const inkIsWhite = rSchemeOverride() === "auto" ? rHasColor() ? rColorScheme() === "dark" : verdict : rScheme() === "dark";
|
|
4071
|
+
if (inkIsWhite === null || inkIsWhite === void 0) return null;
|
|
4072
|
+
const stripInk = (alpha) => {
|
|
4073
|
+
return (verdict === null || verdict === void 0 || alpha >= .85 ? inkIsWhite : verdict) ? "#fff" : "#000";
|
|
4074
|
+
};
|
|
4075
|
+
return {
|
|
4076
|
+
ink: stripInk(rOps().sidebar),
|
|
4077
|
+
collapsedInk: stripInk(rHeaderBarOpacity())
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
/** The fixed caption controls of the sidebar root, re-inked (and released) as the
|
|
4081
|
+
* strip's material changes. Throttled: this runs on every caption re-apply, and
|
|
4082
|
+
* the header-bar slider drags through that path frame by frame. */
|
|
4083
|
+
let captionInkEls = [];
|
|
4084
|
+
let captionInkSidebar = null;
|
|
4085
|
+
let captionScanAt = 0;
|
|
4086
|
+
function releaseCaptionInk() {
|
|
4087
|
+
for (const el of captionInkEls) {
|
|
4088
|
+
el.style.removeProperty("--dsw-alias-label-secondary");
|
|
4089
|
+
el.removeAttribute(CAPTION_INK_ATTR);
|
|
4090
|
+
}
|
|
4091
|
+
captionInkEls = [];
|
|
4092
|
+
}
|
|
4093
|
+
function applyCaptionInk(enabled) {
|
|
4094
|
+
if (!enabled || sidebarEl === null) {
|
|
4095
|
+
releaseCaptionInk();
|
|
4096
|
+
return;
|
|
4097
|
+
}
|
|
4098
|
+
const now = typeof performance === "undefined" ? Date.now() : performance.now();
|
|
4099
|
+
if (captionInkEls.some((el) => !el.isConnected) || sidebarEl !== captionInkSidebar || now - captionScanAt > 250) {
|
|
4100
|
+
captionScanAt = now;
|
|
4101
|
+
captionInkSidebar = sidebarEl;
|
|
4102
|
+
releaseCaptionInk();
|
|
4103
|
+
for (const btn of sidebarEl.querySelectorAll("button")) {
|
|
4104
|
+
if (getComputedStyle(btn).position !== "fixed") continue;
|
|
4105
|
+
captionInkEls.push(btn);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
for (const el of captionInkEls) {
|
|
4109
|
+
el.style.setProperty("--dsw-alias-label-secondary", "var(--dsh-any-caption-ink, #fff)");
|
|
4110
|
+
el.setAttribute(CAPTION_INK_ATTR, "");
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
3861
4113
|
let wcoStyleEl = null;
|
|
3862
4114
|
function ensureWcoStyle() {
|
|
3863
4115
|
if (wcoStyleEl?.isConnected) return wcoStyleEl;
|
|
@@ -3899,6 +4151,13 @@ window.__ModuleLoader__.load({
|
|
|
3899
4151
|
* that left segment from `--dsh-any-op-sidebar` — the same literal the sidebar
|
|
3900
4152
|
* column is painted with — and frosts from the sidebar blur's root mirror, so the
|
|
3901
4153
|
* column and the strip above it are one surface under one opacity + blur pair.
|
|
4154
|
+
* It is PAINT ONLY: `pointer-events:none`, and deliberately NO
|
|
4155
|
+
* `-webkit-app-region: drag`. Chromium collects app-region items in paint order,
|
|
4156
|
+
* and a drag annotation on a generated layer overrides the `no-drag` holes the
|
|
4157
|
+
* native caption menubar punched for itself (`preload-menu.ts` marks its host
|
|
4158
|
+
* `no-drag`), which left the top-left controls unclickable while the window still
|
|
4159
|
+
* dragged (issue #29). The host's own `.frame::before` keeps its drag region, so
|
|
4160
|
+
* the band remains movable.
|
|
3902
4161
|
* `--dsh-windows-sidebar-width` is published INLINE ON THE FRAME by the host
|
|
3903
4162
|
* (`AppFrame.tsx`, Windows builds only, `${cols.sidebar}px`) and both pseudo-elements
|
|
3904
4163
|
* inherit it, so the split tracks a dragged or collapsed sidebar with no observer of
|
|
@@ -3912,10 +4171,20 @@ window.__ModuleLoader__.load({
|
|
|
3912
4171
|
const fill = captionFill(src);
|
|
3913
4172
|
const paint = headerBarPaint(src) || fill;
|
|
3914
4173
|
const width = "var(--dsh-windows-sidebar-width,0px)";
|
|
3915
|
-
const css = fill === "" ? "" : `${WCO_PROBE_SEL}{--dsw-specific-sidebar-fill:transparent}[data-windows-titlebar] .${CAPTION_FRAME_CLASS}::before{background:var(--dsh-any-op-header-bar,${paint})!important;backdrop-filter:var(--dsh-any-blur-header-bar,none);-webkit-backdrop-filter:var(--dsh-any-blur-header-bar,none);clip-path:inset(0 0 0 ${width});-webkit-clip-path:inset(0 0 0 ${width})}[data-windows-titlebar] .${CAPTION_FRAME_CLASS}::after{content:"";position:absolute;top:0;left:0;width:${width};height:var(--dsh-windows-titlebar-height,40px);background:var(--dsh-any-op-sidebar,${fill});backdrop-filter:var(--dsh-any-blur-sidebar-band,none);-webkit-backdrop-filter:var(--dsh-any-blur-sidebar-band,none)
|
|
4174
|
+
const css = fill === "" ? "" : `${WCO_PROBE_SEL}{--dsw-specific-sidebar-fill:transparent}[data-windows-titlebar] .${CAPTION_FRAME_CLASS}::before{background:var(--dsh-any-op-header-bar,${paint})!important;backdrop-filter:var(--dsh-any-blur-header-bar,none);-webkit-backdrop-filter:var(--dsh-any-blur-header-bar,none);clip-path:inset(0 0 0 ${width});-webkit-clip-path:inset(0 0 0 ${width})}[data-windows-titlebar] .${CAPTION_FRAME_CLASS}::after{content:"";position:absolute;top:0;left:0;pointer-events:none;z-index:-1;width:${width};height:var(--dsh-windows-titlebar-height,40px);background:var(--dsh-any-op-sidebar,${fill});backdrop-filter:var(--dsh-any-blur-sidebar-band,none);-webkit-backdrop-filter:var(--dsh-any-blur-sidebar-band,none)}[data-windows-titlebar]:has([role="dialog"][aria-modal="true"]) .${CAPTION_FRAME_CLASS}::before,[data-windows-titlebar]:has([role="dialog"][aria-modal="true"]) .${CAPTION_FRAME_CLASS}::after{box-shadow:inset 0 0 0 9999px var(--dsw-alias-bg-mask-1,rgba(0,0,0,.24))}` + (rCaptionInk() === "theme" ? "" : ":root:has([data-sidebar-collapsed=\"true\"]){--dsh-any-caption-ink:var(--dsh-any-caption-ink-collapsed,#fff)!important}[data-windows-titlebar] [data-windows-menu]{--dsw-alias-label-secondary:var(--dsh-any-caption-ink,#fff);--dsw-alias-label-primary:var(--dsh-any-caption-ink,#fff)}") + `[data-windows-titlebar] .${FLAT_CENTER_CLASS}{border-top-left-radius:0!important}`;
|
|
3916
4175
|
const el = ensureWcoStyle();
|
|
3917
4176
|
if (el.textContent !== css) el.textContent = css;
|
|
4177
|
+
const ink = css === "" ? null : captionInkPair();
|
|
4178
|
+
const root = document.documentElement;
|
|
4179
|
+
if (ink === null) {
|
|
4180
|
+
root.style.removeProperty("--dsh-any-caption-ink");
|
|
4181
|
+
root.style.removeProperty("--dsh-any-caption-ink-collapsed");
|
|
4182
|
+
} else {
|
|
4183
|
+
root.style.setProperty("--dsh-any-caption-ink", ink.ink);
|
|
4184
|
+
root.style.setProperty("--dsh-any-caption-ink-collapsed", ink.collapsedInk);
|
|
4185
|
+
}
|
|
3918
4186
|
discoverParts();
|
|
4187
|
+
applyCaptionInk(ink !== null);
|
|
3919
4188
|
if (frameEl === null) return;
|
|
3920
4189
|
if (css === "") {
|
|
3921
4190
|
frameEl.classList.remove(CAPTION_FRAME_CLASS);
|
|
@@ -3960,7 +4229,11 @@ window.__ModuleLoader__.load({
|
|
|
3960
4229
|
* appears in the sheet. That is exactly how the tagged open-in-app menu kept
|
|
3961
4230
|
* taking the card blur. `[role]` is a no-op presence test that raises only the
|
|
3962
4231
|
* tagged arm to (0,2,0); the other three already qualify at (0,2,0)+. */
|
|
3963
|
-
const HEADER_SURFACES_PADDED = [
|
|
4232
|
+
const HEADER_SURFACES_PADDED = [
|
|
4233
|
+
`${HEADER_TAG_SELECTOR}[role]`,
|
|
4234
|
+
`${HEADER_TAG_SELECTOR}[data-menu-material]`,
|
|
4235
|
+
...HEADER_SURFACES.slice(1)
|
|
4236
|
+
];
|
|
3964
4237
|
/** The header blur variable, and the rule that frosts the header's dropdown set.
|
|
3965
4238
|
* The declaration comes from the same adapter answer as the card group's menu arm:
|
|
3966
4239
|
* every surface in this set paints its skin from the host's own menu-blur token
|
|
@@ -3970,7 +4243,7 @@ window.__ModuleLoader__.load({
|
|
|
3970
4243
|
* backdrop root each of those files documents avoiding. */
|
|
3971
4244
|
const HEADER_BLUR_VAR = "--dsh-any-blur-header";
|
|
3972
4245
|
function headerPopoverRule() {
|
|
3973
|
-
return `${
|
|
4246
|
+
return `${HEADER_SURFACES_PADDED.join(",")}{${menuFrost(HEADER_BLUR_VAR)}}`;
|
|
3974
4247
|
}
|
|
3975
4248
|
function exemptDefaultRule() {
|
|
3976
4249
|
return `[role="dialog"][aria-modal="true"]:not([aria-labelledby]){backdrop-filter:none;-webkit-backdrop-filter:none}[role="menu"]:has([class*="_danger"]){${hostAdapter().menus.frostDecl("none")};-webkit-text-stroke-width:0}`;
|
|
@@ -3984,13 +4257,8 @@ window.__ModuleLoader__.load({
|
|
|
3984
4257
|
function applyHeaderPopovers() {
|
|
3985
4258
|
const root = document.documentElement;
|
|
3986
4259
|
const px = rBlurs().header;
|
|
3987
|
-
if (px > 0) {
|
|
3988
|
-
|
|
3989
|
-
root.classList.add(HEADER_FROST_GATE);
|
|
3990
|
-
} else {
|
|
3991
|
-
root.style.removeProperty(HEADER_BLUR_VAR);
|
|
3992
|
-
root.classList.remove(HEADER_FROST_GATE);
|
|
3993
|
-
}
|
|
4260
|
+
if (px > 0) root.style.setProperty(HEADER_BLUR_VAR, `blur(${px}px)`);
|
|
4261
|
+
else root.style.removeProperty(HEADER_BLUR_VAR);
|
|
3994
4262
|
let opacity = rHeaderOpacity();
|
|
3995
4263
|
if (opacity < 0) opacity = 0;
|
|
3996
4264
|
if (opacity > 1) opacity = 1;
|
|
@@ -4043,8 +4311,9 @@ window.__ModuleLoader__.load({
|
|
|
4043
4311
|
return;
|
|
4044
4312
|
}
|
|
4045
4313
|
const dialog = "[role=\"dialog\"][aria-modal=\"true\"]:not([aria-labelledby])";
|
|
4314
|
+
const registry = "[data-install-registry]";
|
|
4046
4315
|
const body = decls.join(";");
|
|
4047
|
-
const css = `${dialog},${dialog} *{${body}}[role="menu"]:has([class*="_danger"]){${body}}`;
|
|
4316
|
+
const css = `${dialog},${dialog} *{${body}}[role="menu"]:has([class*="_danger"]){${body}}${registry},${registry} *{${body}}`;
|
|
4048
4317
|
if (el.textContent !== css) el.textContent = css;
|
|
4049
4318
|
}
|
|
4050
4319
|
let exemptStyleEl = null;
|
|
@@ -4431,6 +4700,43 @@ window.__ModuleLoader__.load({
|
|
|
4431
4700
|
* clear cannot be re-clobbered and there is nothing to re-apply. */
|
|
4432
4701
|
const PART_FRAME_CLASS = "dab-frame-clear";
|
|
4433
4702
|
const FRAME_CLEAR_RULE = `.${PART_FRAME_CLASS}{background:transparent!important}`;
|
|
4703
|
+
/** The sidebar column paints its fill ONCE, no matter how many nested elements
|
|
4704
|
+
* ask for it.
|
|
4705
|
+
*
|
|
4706
|
+
* WHY THIS IS NOT COSMETICS: `--dsw-specific-sidebar-fill` is a theme token the
|
|
4707
|
+
* host ships OPAQUE (`design-platform.css`: light `--dsw-static-neutral-bluish-50`,
|
|
4708
|
+
* dark `-900`), and three elements stacked in the sidebar's region each declare
|
|
4709
|
+
* `background:var(--dsw-specific-sidebar-fill)` on top of one another —
|
|
4710
|
+
* `AppFrame.module.css` `.sidebarCol` (the grid track), `ui-sidebar/SidebarRoot.module.css:16`
|
|
4711
|
+
* `.root` (the shell it fills) and `ui-workspace/WorkspaceBrowser.module.css:335`
|
|
4712
|
+
* `.fade` (the tree's 24px bottom gradient). Over an opaque color that layering is
|
|
4713
|
+
* invisible; the moment this plugin's 「侧栏」 slider makes the token translucent
|
|
4714
|
+
* every copy adds its alpha again, so the region stops reading as one surface. On
|
|
4715
|
+
* the desktop build it is outright visible: the caption strip above the sidebar is
|
|
4716
|
+
* the plugin's own `::after`, painted with the SAME variable at the SAME alpha but
|
|
4717
|
+
* exactly once (`applyNativeCaption`), so the strip and the column under it come out
|
|
4718
|
+
* at two different densities and the sidebar looks split horizontally.
|
|
4719
|
+
*
|
|
4720
|
+
* The column is the one that keeps the paint — it is the outermost box, it is the
|
|
4721
|
+
* box whose width the caption strip is cut to, and the host already treats it as the
|
|
4722
|
+
* owner on macOS, where `SidebarRoot.module.css:27` sets `.root{background:transparent}`
|
|
4723
|
+
* for exactly this reason ("the AppFrame column owns the translucent sidebar tint").
|
|
4724
|
+
* The host retires the gradient the same way for the same reason:
|
|
4725
|
+
* `WorkspaceBrowser.module.css:341` hides `.fade` on darwin because "the opaque fill
|
|
4726
|
+
* gradient would paint a solid smear instead of a fade" once the sidebar is
|
|
4727
|
+
* translucent over the window — which is the state this slider puts the sidebar in on
|
|
4728
|
+
* every platform. So the token is re-declared `transparent` for the column's
|
|
4729
|
+
* DESCENDANTS only: an element's own declaration beats what it inherits, and `body`'s
|
|
4730
|
+
* `!important` re-scope reaches these elements by inheritance only, so no `!important`
|
|
4731
|
+
* is needed here and none of the three consumers can win it back. The column itself
|
|
4732
|
+
* still resolves the faded value it inherits, which is why `*` is a descendant
|
|
4733
|
+
* combinator and not part of the column's own selector.
|
|
4734
|
+
*
|
|
4735
|
+
* Bound by the CSS-Module class shape, same as `[class*="_ioCard"]`: the built
|
|
4736
|
+
* bundle names these `pI_x6G_sidebarCol` — prefix first, local name last, and the
|
|
4737
|
+
* pair is identical across `0.2.0-rc.1`, `0.2.1-alpha.3` and `0.2.1-rc.2`. A build
|
|
4738
|
+
* that renames the track simply stops matching, which is the untouched host look. */
|
|
4739
|
+
const SIDEBAR_ONE_LAYER_RULE = "[class*=\"_sidebarCol\"] *{--dsw-specific-sidebar-fill:transparent}";
|
|
4434
4740
|
const PART_BLUR_RULE = `.${PART_BLUR_CLASS}{isolation:isolate}.${PART_BLUR_CLASS}:has([role="dialog"]){isolation:auto}.${PART_UNDERLAY_CLASS}{position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;backdrop-filter:var(--dsh-any-part-blur,none);-webkit-backdrop-filter:var(--dsh-any-part-blur,none)}`;
|
|
4435
4741
|
let partBlurStyleEl = null;
|
|
4436
4742
|
function ensurePartBlurStyle() {
|
|
@@ -4520,17 +4826,13 @@ window.__ModuleLoader__.load({
|
|
|
4520
4826
|
/** Blur of the option panels inside the settings dialog (.dab-card), owned by
|
|
4521
4827
|
* the "dialog option panel" (card) blur slider. Written as a plugin-owned
|
|
4522
4828
|
* variable consumed by SETTINGS_STYLE_RULE — deliberately NOT applied to the
|
|
4523
|
-
* homepage center column, which this slider must never touch.
|
|
4524
|
-
*
|
|
4829
|
+
* homepage center column, which this slider must never touch. At 0 the variable is
|
|
4830
|
+
* removed and the menu rule's `none` fallback wins, which is what "no frost" means
|
|
4831
|
+
* on this slider (see the note above `popoverBlurRule`). */
|
|
4525
4832
|
function applyCardPanelsBlur(px) {
|
|
4526
4833
|
const root = document.documentElement;
|
|
4527
|
-
if (px > 0) {
|
|
4528
|
-
|
|
4529
|
-
root.classList.add(CARD_FROST_GATE);
|
|
4530
|
-
} else {
|
|
4531
|
-
root.style.removeProperty(CARD_BLUR_VAR);
|
|
4532
|
-
root.classList.remove(CARD_FROST_GATE);
|
|
4533
|
-
}
|
|
4834
|
+
if (px > 0) root.style.setProperty(CARD_BLUR_VAR, `blur(${px}px)`);
|
|
4835
|
+
else root.style.removeProperty(CARD_BLUR_VAR);
|
|
4534
4836
|
}
|
|
4535
4837
|
/** Apply per-part interface blur to the AppFrame columns + settings panel. */
|
|
4536
4838
|
function applyPartBlurs(blurs) {
|
|
@@ -5261,12 +5563,15 @@ window.__ModuleLoader__.load({
|
|
|
5261
5563
|
const name = set[k];
|
|
5262
5564
|
if (name) document.documentElement.style.removeProperty(name);
|
|
5263
5565
|
}
|
|
5264
|
-
document.documentElement.classList.remove(INPUT_FROST_CLASS
|
|
5566
|
+
document.documentElement.classList.remove(INPUT_FROST_CLASS);
|
|
5265
5567
|
applyTakeoverFrost(0);
|
|
5266
5568
|
document.documentElement.style.removeProperty("--dsh-any-op-header-bar");
|
|
5267
5569
|
document.documentElement.style.removeProperty("--dsh-any-blur-header-bar");
|
|
5268
5570
|
document.documentElement.style.removeProperty("--dsh-any-blur-sidebar-band");
|
|
5269
|
-
document.
|
|
5571
|
+
document.documentElement.style.removeProperty("--dsh-any-caption-ink");
|
|
5572
|
+
document.documentElement.style.removeProperty("--dsh-any-caption-ink-collapsed");
|
|
5573
|
+
releaseCaptionInk();
|
|
5574
|
+
document.querySelectorAll(`.${HEADER_BAR_CLASS}`).forEach((el) => releaseHeaderBar(el));
|
|
5270
5575
|
headerBarEl = null;
|
|
5271
5576
|
frameEl?.classList.remove(CAPTION_FRAME_CLASS);
|
|
5272
5577
|
document.querySelectorAll(`.${FLAT_CENTER_CLASS}`).forEach((el) => el.classList.remove(FLAT_CENTER_CLASS));
|
|
@@ -5364,7 +5669,7 @@ window.__ModuleLoader__.load({
|
|
|
5364
5669
|
function buildStaticStyles() {
|
|
5365
5670
|
const adapter = rHostAdapter();
|
|
5366
5671
|
const panel = adapter.panelFragments();
|
|
5367
|
-
return DARK_GRADIENT + FRAME_CLEAR_RULE + SETTINGS_STYLE_RULE + popoverBlurRule() + TRAJECTORY_STYLE_RULE + inputBlurRule() + MOBILE_HEADER_RULE + HEADER_BAR_RULE + PANEL_TOKEN_RULE + panel.promotion + panel.blur + adapter.pluginPageRule + PRODUCED_RULE + STROKE_RULE + headerPopoverRule() + exemptDefaultRule() + PLACEHOLDER_RULE;
|
|
5672
|
+
return DARK_GRADIENT + FRAME_CLEAR_RULE + SIDEBAR_ONE_LAYER_RULE + SETTINGS_STYLE_RULE + popoverBlurRule() + TRAJECTORY_STYLE_RULE + inputBlurRule() + MOBILE_HEADER_RULE + HEADER_BAR_RULE + PANEL_TOKEN_RULE + panel.promotion + panel.blur + adapter.pluginPageRule + PRODUCED_RULE + STROKE_RULE + headerPopoverRule() + exemptDefaultRule() + PLACEHOLDER_RULE;
|
|
5368
5673
|
}
|
|
5369
5674
|
/** Append the plugin's stylesheet and keep it matched to the host.
|
|
5370
5675
|
* Returns a teardown for the plugin's effect scope. */
|
|
@@ -7291,6 +7596,47 @@ window.__ModuleLoader__.load({
|
|
|
7291
7596
|
blurs[blurKey] = v;
|
|
7292
7597
|
setBlurs(blurs);
|
|
7293
7598
|
}
|
|
7599
|
+
}),
|
|
7600
|
+
isHeaderBar && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7601
|
+
className: "dab-slider-block",
|
|
7602
|
+
title: t("captionInkHint"),
|
|
7603
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7604
|
+
className: "dab-slider-label",
|
|
7605
|
+
children: t("captionInkLabel")
|
|
7606
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7607
|
+
style: {
|
|
7608
|
+
display: "flex",
|
|
7609
|
+
alignItems: "center",
|
|
7610
|
+
gap: 10
|
|
7611
|
+
},
|
|
7612
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
7613
|
+
className: "dab-select",
|
|
7614
|
+
value: rCaptionInk(),
|
|
7615
|
+
onChange: (e) => {
|
|
7616
|
+
cfg.captionInk = e.target.value;
|
|
7617
|
+
applyHeaderBar();
|
|
7618
|
+
saveConfig();
|
|
7619
|
+
},
|
|
7620
|
+
children: [
|
|
7621
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
7622
|
+
value: "auto",
|
|
7623
|
+
children: t("captionInkAuto")
|
|
7624
|
+
}),
|
|
7625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
7626
|
+
value: "theme",
|
|
7627
|
+
children: t("captionInkTheme")
|
|
7628
|
+
}),
|
|
7629
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
7630
|
+
value: "white",
|
|
7631
|
+
children: t("captionInkWhite")
|
|
7632
|
+
}),
|
|
7633
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
7634
|
+
value: "black",
|
|
7635
|
+
children: t("captionInkBlack")
|
|
7636
|
+
})
|
|
7637
|
+
]
|
|
7638
|
+
})
|
|
7639
|
+
})]
|
|
7294
7640
|
})
|
|
7295
7641
|
]
|
|
7296
7642
|
}, blurKey);
|