dsh-any-background 0.2.10 → 0.3.1
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 +48 -33
- package/README.zh.md +50 -34
- package/lib/client.js +1137 -63
- package/lib/client.js.map +1 -1
- package/lib/index.js +245 -10
- package/package.json +20 -10
package/lib/client.js
CHANGED
|
@@ -49,11 +49,14 @@ window.__ModuleLoader__.load({
|
|
|
49
49
|
uiOpacitySide: "侧边栏",
|
|
50
50
|
uiOpacityCard: "对话框中选项面板",
|
|
51
51
|
uiOpacityInput: "输入框与控件",
|
|
52
|
-
uiSop: "
|
|
52
|
+
uiSop: "设置界面",
|
|
53
53
|
uiChatRegion: "对话文本框",
|
|
54
54
|
uiTrajectory: "轨迹页",
|
|
55
|
-
uiPanelRegion: "
|
|
55
|
+
uiPanelRegion: "bettersidebar",
|
|
56
|
+
uiPanelNative: "右方侧边栏",
|
|
57
|
+
guideDesc: "管理主题色、壁纸与界面透明度",
|
|
56
58
|
uiProduced: "产出物/高亮内容",
|
|
59
|
+
uiHeader: "顶栏选项",
|
|
57
60
|
fontPageTitle: "字体与描边",
|
|
58
61
|
fontTitle: "自定义字体",
|
|
59
62
|
fontHint: "支持 ttf/otf/woff/woff2 字体文件(上限 100MB),将应用到整个界面的文字;代码块的等宽字体保持不变。中文字体通常有几十 MB,首次上传与刷新加载需要片刻",
|
|
@@ -238,11 +241,14 @@ window.__ModuleLoader__.load({
|
|
|
238
241
|
uiOpacitySide: "Sidebar",
|
|
239
242
|
uiOpacityCard: "Cards & panels",
|
|
240
243
|
uiOpacityInput: "Input & controls",
|
|
241
|
-
uiSop: "Settings interface
|
|
244
|
+
uiSop: "Settings interface",
|
|
242
245
|
uiChatRegion: "Conversation text frame",
|
|
243
246
|
uiTrajectory: "Trajectory view",
|
|
244
|
-
uiPanelRegion: "
|
|
247
|
+
uiPanelRegion: "bettersidebar",
|
|
248
|
+
uiPanelNative: "Right sidebar",
|
|
249
|
+
guideDesc: "Theme color, wallpaper and interface opacity in the right sidebar",
|
|
245
250
|
uiProduced: "Produced / highlights",
|
|
251
|
+
uiHeader: "Header popovers",
|
|
246
252
|
fontPageTitle: "Font & outline",
|
|
247
253
|
fontTitle: "Custom font",
|
|
248
254
|
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",
|
|
@@ -407,13 +413,13 @@ window.__ModuleLoader__.load({
|
|
|
407
413
|
const DEFAULT_CONFIG = {
|
|
408
414
|
color: null,
|
|
409
415
|
opacities: {
|
|
410
|
-
bg:
|
|
416
|
+
bg: 0,
|
|
411
417
|
sidebar: .5,
|
|
412
418
|
card: .5,
|
|
413
419
|
input: .5
|
|
414
420
|
},
|
|
415
421
|
blurs: {
|
|
416
|
-
bg:
|
|
422
|
+
bg: 0,
|
|
417
423
|
sidebar: 30,
|
|
418
424
|
card: 30,
|
|
419
425
|
settings: 30,
|
|
@@ -421,7 +427,8 @@ window.__ModuleLoader__.load({
|
|
|
421
427
|
trajectory: 30,
|
|
422
428
|
input: 30,
|
|
423
429
|
panel: 30,
|
|
424
|
-
produced: 30
|
|
430
|
+
produced: 30,
|
|
431
|
+
header: 30
|
|
425
432
|
},
|
|
426
433
|
strokes: {
|
|
427
434
|
bg: {
|
|
@@ -468,11 +475,16 @@ window.__ModuleLoader__.load({
|
|
|
468
475
|
width: 0,
|
|
469
476
|
color: "auto",
|
|
470
477
|
customColor: "#808080"
|
|
478
|
+
},
|
|
479
|
+
header: {
|
|
480
|
+
width: 0,
|
|
481
|
+
color: "auto",
|
|
482
|
+
customColor: "#808080"
|
|
471
483
|
}
|
|
472
484
|
},
|
|
473
485
|
settingsOpacity: .5,
|
|
474
486
|
wallpaperOpacity: 1,
|
|
475
|
-
blur:
|
|
487
|
+
blur: 0,
|
|
476
488
|
bgState: {
|
|
477
489
|
zoom: 1,
|
|
478
490
|
x: 0,
|
|
@@ -498,6 +510,7 @@ window.__ModuleLoader__.load({
|
|
|
498
510
|
trajectoryOpacity: .5,
|
|
499
511
|
panelOpacity: .5,
|
|
500
512
|
producedOpacity: .5,
|
|
513
|
+
headerOpacity: .5,
|
|
501
514
|
profiles: [],
|
|
502
515
|
rotation: {
|
|
503
516
|
enabled: false,
|
|
@@ -637,7 +650,8 @@ window.__ModuleLoader__.load({
|
|
|
637
650
|
"trajectory",
|
|
638
651
|
"input",
|
|
639
652
|
"panel",
|
|
640
|
-
"produced"
|
|
653
|
+
"produced",
|
|
654
|
+
"header"
|
|
641
655
|
]) {
|
|
642
656
|
const v = b[k];
|
|
643
657
|
out[k] = typeof v === "number" ? Math.min(60, Math.max(0, v)) : DEFAULT_CONFIG.blurs[k];
|
|
@@ -653,7 +667,8 @@ window.__ModuleLoader__.load({
|
|
|
653
667
|
"trajectory",
|
|
654
668
|
"input",
|
|
655
669
|
"panel",
|
|
656
|
-
"produced"
|
|
670
|
+
"produced",
|
|
671
|
+
"header"
|
|
657
672
|
];
|
|
658
673
|
const STROKE_COLOR_KEYS = [
|
|
659
674
|
"auto",
|
|
@@ -702,6 +717,9 @@ window.__ModuleLoader__.load({
|
|
|
702
717
|
function rProducedOpacity() {
|
|
703
718
|
return clamp01(cfg.producedOpacity, DEFAULT_CONFIG.producedOpacity);
|
|
704
719
|
}
|
|
720
|
+
function rHeaderOpacity() {
|
|
721
|
+
return clamp01(cfg.headerOpacity, DEFAULT_CONFIG.headerOpacity);
|
|
722
|
+
}
|
|
705
723
|
function rBgState() {
|
|
706
724
|
return cfg.bgState;
|
|
707
725
|
}
|
|
@@ -765,7 +783,8 @@ window.__ModuleLoader__.load({
|
|
|
765
783
|
chatTextOpacity: rChatTextOpacity(),
|
|
766
784
|
trajectoryOpacity: rTrajectoryOpacity(),
|
|
767
785
|
panelOpacity: rPanelOpacity(),
|
|
768
|
-
producedOpacity: rProducedOpacity()
|
|
786
|
+
producedOpacity: rProducedOpacity(),
|
|
787
|
+
headerOpacity: rHeaderOpacity()
|
|
769
788
|
};
|
|
770
789
|
}
|
|
771
790
|
/** Apply an appearance snapshot onto cfg (meta fields untouched). */
|
|
@@ -787,6 +806,7 @@ window.__ModuleLoader__.load({
|
|
|
787
806
|
cfg.trajectoryOpacity = clamp01(ap.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity);
|
|
788
807
|
cfg.panelOpacity = clamp01(ap.panelOpacity, DEFAULT_CONFIG.panelOpacity);
|
|
789
808
|
cfg.producedOpacity = clamp01(ap.producedOpacity, DEFAULT_CONFIG.producedOpacity);
|
|
809
|
+
cfg.headerOpacity = clamp01(ap.headerOpacity, DEFAULT_CONFIG.headerOpacity);
|
|
790
810
|
}
|
|
791
811
|
const num = (n, def) => typeof n === "number" ? n : def;
|
|
792
812
|
const cl = (n, lo, hi, def) => typeof n === "number" ? Math.min(hi, Math.max(lo, n)) : def;
|
|
@@ -820,7 +840,8 @@ window.__ModuleLoader__.load({
|
|
|
820
840
|
chatTextOpacity: clamp01(ac.chatTextOpacity, DEFAULT_CONFIG.chatTextOpacity),
|
|
821
841
|
trajectoryOpacity: clamp01(ac.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity),
|
|
822
842
|
panelOpacity: clamp01(ac.panelOpacity, DEFAULT_CONFIG.panelOpacity),
|
|
823
|
-
producedOpacity: clamp01(ac.producedOpacity, DEFAULT_CONFIG.producedOpacity)
|
|
843
|
+
producedOpacity: clamp01(ac.producedOpacity, DEFAULT_CONFIG.producedOpacity),
|
|
844
|
+
headerOpacity: clamp01(ac.headerOpacity, DEFAULT_CONFIG.headerOpacity)
|
|
824
845
|
}
|
|
825
846
|
});
|
|
826
847
|
}
|
|
@@ -885,7 +906,8 @@ window.__ModuleLoader__.load({
|
|
|
885
906
|
"trajectory",
|
|
886
907
|
"input",
|
|
887
908
|
"panel",
|
|
888
|
-
"produced"
|
|
909
|
+
"produced",
|
|
910
|
+
"header"
|
|
889
911
|
]) blurs[k] = num(bl[k], DEFAULT_CONFIG.blurs[k]);
|
|
890
912
|
const bgType = [
|
|
891
913
|
"video",
|
|
@@ -928,6 +950,7 @@ window.__ModuleLoader__.load({
|
|
|
928
950
|
trajectoryOpacity: clamp01(c.trajectoryOpacity, DEFAULT_CONFIG.trajectoryOpacity),
|
|
929
951
|
panelOpacity: clamp01(c.panelOpacity, DEFAULT_CONFIG.panelOpacity),
|
|
930
952
|
producedOpacity: clamp01(c.producedOpacity, DEFAULT_CONFIG.producedOpacity),
|
|
953
|
+
headerOpacity: clamp01(c.headerOpacity, DEFAULT_CONFIG.headerOpacity),
|
|
931
954
|
profiles: adoptProfiles(c.profiles),
|
|
932
955
|
rotation: adoptRotation(c.rotation),
|
|
933
956
|
schedule: adoptSchedule(c.schedule),
|
|
@@ -971,6 +994,554 @@ window.__ModuleLoader__.load({
|
|
|
971
994
|
};
|
|
972
995
|
}
|
|
973
996
|
//#endregion
|
|
997
|
+
//#region src/host-compat/channel.ts
|
|
998
|
+
/** What this plugin supports, in upgrade order. One row = one build whose facts
|
|
999
|
+
* were checked; several rows may share a `channel` when a newer build was
|
|
1000
|
+
* verified as needing no change to that folder's adapter (as `0.1.7-rc.1` was) —
|
|
1001
|
+
* a row then reports `exact` instead of `line`, and no new folder appears.
|
|
1002
|
+
* Adding an ADAPTER means adding a channel plus a folder in
|
|
1003
|
+
* `client/host-compat/versions/`. */
|
|
1004
|
+
const SUPPORTED_RELEASES = [
|
|
1005
|
+
{
|
|
1006
|
+
channel: "0.1.5-rc",
|
|
1007
|
+
release: "0.1.5-rc.2"
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
channel: "0.1.6-alpha",
|
|
1011
|
+
release: "0.1.6-alpha.2"
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
channel: "0.1.7-alpha",
|
|
1015
|
+
release: "0.1.7-alpha.2"
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
channel: "0.1.7-alpha",
|
|
1019
|
+
release: "0.1.7-rc.1"
|
|
1020
|
+
}
|
|
1021
|
+
];
|
|
1022
|
+
const UNKNOWN_HOST_INFO = {
|
|
1023
|
+
version: null,
|
|
1024
|
+
channel: "unknown"
|
|
1025
|
+
};
|
|
1026
|
+
/** True when a string looks like a release version. A bare `~/.dsh` directory
|
|
1027
|
+
* basename does not, which is why the disk probe sanity-checks before trusting
|
|
1028
|
+
* a path segment. */
|
|
1029
|
+
const VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
1030
|
+
function looksLikeVersion(value) {
|
|
1031
|
+
return VERSION_RE.test(value);
|
|
1032
|
+
}
|
|
1033
|
+
const PARSE_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
|
|
1034
|
+
function parse(version) {
|
|
1035
|
+
const m = PARSE_RE.exec(version);
|
|
1036
|
+
if (m === null) return null;
|
|
1037
|
+
const parts = m[4] === void 0 ? [] : m[4].split(".");
|
|
1038
|
+
const build = parts.length > 1 ? Number(parts[1]) : NaN;
|
|
1039
|
+
return {
|
|
1040
|
+
core: [
|
|
1041
|
+
Number(m[1]),
|
|
1042
|
+
Number(m[2]),
|
|
1043
|
+
Number(m[3])
|
|
1044
|
+
],
|
|
1045
|
+
channel: parts.length > 0 ? parts[0] : null,
|
|
1046
|
+
build: Number.isFinite(build) ? build : null
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
/** The table, parsed once: every row is a literal this file controls. */
|
|
1050
|
+
const TABLE = SUPPORTED_RELEASES.map((row) => ({
|
|
1051
|
+
...row,
|
|
1052
|
+
parsed: parse(row.release)
|
|
1053
|
+
}));
|
|
1054
|
+
function cmpCore(a, b) {
|
|
1055
|
+
for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
|
|
1056
|
+
return 0;
|
|
1057
|
+
}
|
|
1058
|
+
/** Total order over releases, following the semver rule that a prerelease sorts
|
|
1059
|
+
* BELOW the release it precedes (`0.1.6-alpha.2 < 0.1.6`). */
|
|
1060
|
+
function cmp(a, b) {
|
|
1061
|
+
const core = cmpCore(a.core, b.core);
|
|
1062
|
+
if (core !== 0) return core;
|
|
1063
|
+
if (a.channel === null || b.channel === null) {
|
|
1064
|
+
if (a.channel === b.channel) return 0;
|
|
1065
|
+
return a.channel === null ? 1 : -1;
|
|
1066
|
+
}
|
|
1067
|
+
if (a.channel !== b.channel) return a.channel < b.channel ? -1 : 1;
|
|
1068
|
+
const ab = a.build ?? 0;
|
|
1069
|
+
const bb = b.build ?? 0;
|
|
1070
|
+
return ab === bb ? 0 : ab < bb ? -1 : 1;
|
|
1071
|
+
}
|
|
1072
|
+
/** Pick the adapter for a release, clamping to the nearest verified line when the
|
|
1073
|
+
* release is outside everything this plugin has seen.
|
|
1074
|
+
*
|
|
1075
|
+
* A release is matched by PATCH LINE before anything else: `0.1.6-alpha.4` and a
|
|
1076
|
+
* future `0.1.6` stable both belong to the line verified at `0.1.6-alpha.2`, and
|
|
1077
|
+
* handing them that adapter beats giving up — the DOM probes only cover panel
|
|
1078
|
+
* mechanics, not the surface decisions the line is known for.
|
|
1079
|
+
*
|
|
1080
|
+
* Outside every verified line the answer is a guess, so it is the closest one and
|
|
1081
|
+
* it says so (`nearest`): newer than the newest line → that newest adapter, older
|
|
1082
|
+
* than the oldest → the oldest, strictly between two → the LOWER one, because an
|
|
1083
|
+
* adapter may only claim what it was verified for. A release that will not parse
|
|
1084
|
+
* at all is `unknown`, and the DOM arbitrates there. */
|
|
1085
|
+
function classifyRelease(version) {
|
|
1086
|
+
const host = version === null ? null : parse(version);
|
|
1087
|
+
if (host === null) return {
|
|
1088
|
+
channel: "unknown",
|
|
1089
|
+
match: "unresolved"
|
|
1090
|
+
};
|
|
1091
|
+
const sameLine = TABLE.filter((row) => cmpCore(row.parsed.core, host.core) === 0);
|
|
1092
|
+
if (sameLine.length > 0) {
|
|
1093
|
+
const exact = sameLine.find((row) => row.release === version);
|
|
1094
|
+
if (exact !== void 0) return {
|
|
1095
|
+
channel: exact.channel,
|
|
1096
|
+
match: "exact"
|
|
1097
|
+
};
|
|
1098
|
+
const atOrBelow = sameLine.filter((row) => cmp(row.parsed, host) <= 0);
|
|
1099
|
+
return {
|
|
1100
|
+
channel: (atOrBelow.length > 0 ? atOrBelow[atOrBelow.length - 1] : sameLine[0]).channel,
|
|
1101
|
+
match: "line"
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
const older = TABLE.filter((row) => cmp(row.parsed, host) < 0);
|
|
1105
|
+
if (older.length === 0) return {
|
|
1106
|
+
channel: TABLE[0].channel,
|
|
1107
|
+
match: "nearest"
|
|
1108
|
+
};
|
|
1109
|
+
if (TABLE.filter((row) => cmp(row.parsed, host) > 0).length === 0) return {
|
|
1110
|
+
channel: TABLE[TABLE.length - 1].channel,
|
|
1111
|
+
match: "nearest"
|
|
1112
|
+
};
|
|
1113
|
+
return {
|
|
1114
|
+
channel: older[older.length - 1].channel,
|
|
1115
|
+
match: "nearest"
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
/** The channel a release maps onto — the verdict's answer without the confidence. */
|
|
1119
|
+
function channelOf(version) {
|
|
1120
|
+
return classifyRelease(version).channel;
|
|
1121
|
+
}
|
|
1122
|
+
/** Prerelease tag of a release string (`alpha.2` in `0.1.6-alpha.2`), or null
|
|
1123
|
+
* when the version is bare/undetermined. Needed because a channel is coarser
|
|
1124
|
+
* than reality: `conversation.session.header.leading` ships in exactly one
|
|
1125
|
+
* build within the 0.1.6-alpha line. */
|
|
1126
|
+
function prereleaseOf(version) {
|
|
1127
|
+
if (version === null) return null;
|
|
1128
|
+
return /^\d+\.\d+\.\d+-([0-9A-Za-z.-]+)$/.exec(version)?.[1] ?? null;
|
|
1129
|
+
}
|
|
1130
|
+
//#endregion
|
|
1131
|
+
//#region src/client/host-compat/versions/shared.ts
|
|
1132
|
+
/**
|
|
1133
|
+
* Selector + declaration vocabulary shared by every per-version adapter.
|
|
1134
|
+
*
|
|
1135
|
+
* These are the ANCHORS, not the decisions. What varies between releases (which
|
|
1136
|
+
* element slides, whether the panel needs promoting, which header slot keys
|
|
1137
|
+
* exist) is answered in the version folders; what is stable across all of them
|
|
1138
|
+
* lives here so no adapter has to re-type a host selector and get it subtly wrong.
|
|
1139
|
+
*
|
|
1140
|
+
* Verified against the harness release tags (dsh-v0.1.5-rc.2 … dsh-v0.1.7-rc.1),
|
|
1141
|
+
* not inferred:
|
|
1142
|
+
* · `[data-sidebar-right-panel]` — emitted by `ui-sidebar-right/shell/SidebarRight.tsx`
|
|
1143
|
+
* on every release in range, with values `push` | `fullscreen`.
|
|
1144
|
+
* · `[data-dockkit-host]` / `[data-dockkit-empty]` — emitted ONLY by
|
|
1145
|
+
* `ui-dockkit/components/TabLayout.tsx`, a file that first appears at
|
|
1146
|
+
* 0.1.7-alpha.1. Dockkit itself EXISTS earlier (its `TabMenu` carries
|
|
1147
|
+
* `[data-dockkit-tab-menu]` on every release), so only these two attributes
|
|
1148
|
+
* are the 0.1.7 marker — never test "is dockkit installed".
|
|
1149
|
+
* · `[data-dsh-bottom-panel]` — the optional dsh-better-sidebar workbench, not
|
|
1150
|
+
* a host element at all. It is a single element on every release, which is
|
|
1151
|
+
* why it needs no version arm.
|
|
1152
|
+
* · `[data-plugin-panel]` / `[data-plugin-scope] > ul` — emitted by
|
|
1153
|
+
* `ui-plugin-manager/src/client/PluginManagerPage.tsx`. That package is NEW AT
|
|
1154
|
+
* 0.1.6-alpha.2: `git ls-tree` shows it absent from 0.1.5-rc.2, 0.1.5-rc.3 and
|
|
1155
|
+
* 0.1.6-alpha.1, where the plugin list lived in `ui-settings-plugin-inventory`
|
|
1156
|
+
* under `[data-plugin-scope]` alone — no `[data-plugin-panel]`, and no card
|
|
1157
|
+
* `ul` in that group either. From alpha.2 on, the group markup (`section` →
|
|
1158
|
+
* `div.groupHead` + `ul.cards`) is byte-identical through 0.1.7-rc.1, so the
|
|
1159
|
+
* ONE rule below serves all four tags that emit the page.
|
|
1160
|
+
*
|
|
1161
|
+
* @module
|
|
1162
|
+
*/
|
|
1163
|
+
/** The host's own right Sidebar wrapper. */
|
|
1164
|
+
const PANEL_WRAPPER = "[data-sidebar-right-panel]";
|
|
1165
|
+
/** The same wrapper in its fullscreen state; the host raises its own z-index to 40. */
|
|
1166
|
+
const PANEL_FULLSCREEN = "[data-sidebar-right-panel=\"fullscreen\"]";
|
|
1167
|
+
/** The docked children a 0.1.7+ panel slides — the panel itself stays put. */
|
|
1168
|
+
const DOCKKIT_SLIDERS = "[data-sidebar-right-panel] [data-dockkit-host=\"dock\"],[data-sidebar-right-panel] [data-dockkit-empty]";
|
|
1169
|
+
/** The third-party workbench panel, one element on every release. */
|
|
1170
|
+
const BETTER_SIDEBAR_PANEL = "[data-dsh-bottom-panel]";
|
|
1171
|
+
/** Every surface the panel opacity slider owns. Layout-neutral token re-scope. */
|
|
1172
|
+
const PANEL_SURFACES = `${BETTER_SIDEBAR_PANEL},${PANEL_WRAPPER}`;
|
|
1173
|
+
/** DOM-shape test for "this is NOT a 0.1.7-style frame". Used ONLY by the
|
|
1174
|
+
* unresolved adapter, where the DOM is the authority because the release is not. */
|
|
1175
|
+
const WITHOUT_DOCKKIT_FRAME = ":not(:has([data-dockkit-host],[data-dockkit-empty]))";
|
|
1176
|
+
/** The panel blur declaration pair. Both prefixes because Safari needs the
|
|
1177
|
+
* `-webkit-` one and the cascade must not let them disagree. */
|
|
1178
|
+
function panelBlurRule(selectors) {
|
|
1179
|
+
return `${selectors}{-webkit-backdrop-filter:var(--dsh-any-blur-panel,none);backdrop-filter:var(--dsh-any-blur-panel,none)}`;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* The plugin-manager page's frosted card block, for the releases that ship it.
|
|
1183
|
+
*
|
|
1184
|
+
* `设置 → 插件` lists each group's plugins in a `ul` that has NO surface of its
|
|
1185
|
+
* own — measured live on 0.1.7-alpha.1: a transparent flex column, 2px gaps, each
|
|
1186
|
+
* `li` transparent with a 12px radius. With the settings surfaces faded, the whole
|
|
1187
|
+
* table floats straight on the wallpaper. This frames each group's list the way the
|
|
1188
|
+
* composer capsule is framed: a rounded block on an `::before` underlay.
|
|
1189
|
+
*
|
|
1190
|
+
* Which releases emit the page decides who gets this string — see `pluginPageRule`
|
|
1191
|
+
* in each version folder.
|
|
1192
|
+
*
|
|
1193
|
+
* BINDING, and why it is not just "put it in the dialog": the block paints from
|
|
1194
|
+
* `--dsh-any-bg-settings-surface` and frosts from `--dsh-any-blur-settings`, i.e.
|
|
1195
|
+
* the settings-opacity / settings-blur pair. On 0.1.7 the page is a child of
|
|
1196
|
+
* `centerCol`, NOT of the settings dialog, so `SETTINGS_STYLE_RULE`'s token
|
|
1197
|
+
* re-scope never reaches it and the list would otherwise follow the homepage card
|
|
1198
|
+
* alpha; reading the plugin-owned variables off `:root` puts them under the slider
|
|
1199
|
+
* that owns this page. The `var(--dsw-alias-bg-layer-2)` fallback covers the window
|
|
1200
|
+
* before the first `applySettingsOverrides` write.
|
|
1201
|
+
*
|
|
1202
|
+
* The frost rides an underlay because backdrop-filter must never sit directly on a
|
|
1203
|
+
* host part (containing block + backdrop root), and the `isolation` is what keeps
|
|
1204
|
+
* the underlay's `z-index:-1` inside the block instead of escaping behind the frame.
|
|
1205
|
+
* The `:has([role="dialog"])` valve matters here more than anywhere else: every row
|
|
1206
|
+
* carries a `plugins.item` slot that third-party plugins fill with their own
|
|
1207
|
+
* controls, so a dialog can legitimately open inside the list.
|
|
1208
|
+
*/
|
|
1209
|
+
const PLUGIN_PAGE_FROST_RULE = "[data-plugin-panel] [data-plugin-scope]>ul{position:relative;isolation:isolate;padding:8px;border-radius:14px}[data-plugin-panel] [data-plugin-scope]>ul:has([role=\"dialog\"]){isolation:auto}[data-plugin-panel] [data-plugin-scope]>ul::before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;background:var(--dsh-any-bg-settings-surface,var(--dsw-alias-bg-layer-2));-webkit-backdrop-filter:var(--dsh-any-blur-settings,none);backdrop-filter:var(--dsh-any-blur-settings,none)}";
|
|
1210
|
+
/** Session-header slot anchors present on EVERY release in range. The slot
|
|
1211
|
+
* renderer stamps `data-slot="<key>"` unconditionally (`ui-renderer/scoped-slots.tsx`),
|
|
1212
|
+
* and all five keys are registered from 0.1.5-rc.2 through 0.1.7-rc.1 —
|
|
1213
|
+
* verified per tag. Adapters append the keys their own release adds. */
|
|
1214
|
+
const BASE_HEADER_SLOT_KEYS = [
|
|
1215
|
+
"conversation.session.header",
|
|
1216
|
+
"conversation.session.header.actions",
|
|
1217
|
+
"conversation.session.header.utilities",
|
|
1218
|
+
"conversation.session.header.corner",
|
|
1219
|
+
"conversation.session.header.lineage"
|
|
1220
|
+
];
|
|
1221
|
+
//#endregion
|
|
1222
|
+
//#region src/client/host-compat/versions/v0-1-5-rc-2-3/adapter.ts
|
|
1223
|
+
/**
|
|
1224
|
+
* Adapter for the DSH 0.1.5-rc channel (`0.1.5-rc.2`, `0.1.5-rc.3`).
|
|
1225
|
+
*
|
|
1226
|
+
* Nothing here is inferred from a neighbouring release — each fact below was
|
|
1227
|
+
* checked against the harness tags `dsh-v0.1.5-rc.2` / `dsh-v0.1.5-rc.3`.
|
|
1228
|
+
*
|
|
1229
|
+
* Host shape this line presents:
|
|
1230
|
+
* `ui-sidebar-right/shell/SidebarRight.module.css`
|
|
1231
|
+
* .panel { position:absolute; z-index:10; transform:translateX(100%); visibility:hidden }
|
|
1232
|
+
* .panel[data-sidebar-right-open] { transform:none }
|
|
1233
|
+
* .panel[data-sidebar-right-panel='fullscreen'] { position:fixed; inset:0; z-index:40 }
|
|
1234
|
+
* `ui-dockkit` ships WITHOUT `TabLayout.tsx`, so `[data-dockkit-host]` and
|
|
1235
|
+
* `[data-dockkit-empty]` are never rendered on this line.
|
|
1236
|
+
* `ui-plugin-manager` (the page behind `PLUGIN_PAGE_FROST_RULE`) is not a package
|
|
1237
|
+
* on this line at all, and neither tag emits `[data-plugin-panel]`.
|
|
1238
|
+
*
|
|
1239
|
+
* Consequence for the panel slider: the WRAPPER is what slides, so the
|
|
1240
|
+
* `backdrop-filter` belongs on the wrapper — and because the wrapper sits inside
|
|
1241
|
+
* an animated track with its own stacking context, it must be promoted out of it
|
|
1242
|
+
* or the filter has no wallpaper to sample.
|
|
1243
|
+
*
|
|
1244
|
+
* @module
|
|
1245
|
+
*/
|
|
1246
|
+
/** The wrapper carries the slide transform on this line, so it carries the blur. */
|
|
1247
|
+
function panelFragments$3() {
|
|
1248
|
+
return {
|
|
1249
|
+
promotion: `${PANEL_WRAPPER}{position:fixed!important;z-index:26!important}${PANEL_FULLSCREEN}{z-index:40!important}`,
|
|
1250
|
+
blur: panelBlurRule(PANEL_WRAPPER) + panelBlurRule(BETTER_SIDEBAR_PANEL)
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
function createAdapter$3(host) {
|
|
1254
|
+
return {
|
|
1255
|
+
id: "0.1.5-rc.2/rc.3",
|
|
1256
|
+
channel: "0.1.5-rc",
|
|
1257
|
+
host,
|
|
1258
|
+
panelFragments: panelFragments$3,
|
|
1259
|
+
pluginPageRule: "",
|
|
1260
|
+
surface: {
|
|
1261
|
+
ownsSidebarGuideSurface: false,
|
|
1262
|
+
headerSlotKeys: BASE_HEADER_SLOT_KEYS
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
//#endregion
|
|
1267
|
+
//#region src/client/host-compat/versions/v0-1-6-alpha-1-2/adapter.ts
|
|
1268
|
+
/**
|
|
1269
|
+
* Adapter for the DSH 0.1.6-alpha channel (`0.1.6-alpha.1`, `0.1.6-alpha.2`).
|
|
1270
|
+
*
|
|
1271
|
+
* Facts checked per tag against `dsh-v0.1.6-alpha.1` / `dsh-v0.1.6-alpha.2` —
|
|
1272
|
+
* deliberately not copied over from the 0.1.5 folder, even where the two lines
|
|
1273
|
+
* turned out to agree. If a future 0.1.6 patch diverges, only this file moves.
|
|
1274
|
+
*
|
|
1275
|
+
* Host shape this line presents:
|
|
1276
|
+
* `SidebarRight.module.css` is unchanged from the 0.1.5 line for the parts that
|
|
1277
|
+
* matter: `.panel` still carries `position:absolute` + `transform:translateX(100%)`,
|
|
1278
|
+
* `data-sidebar-right-open` still clears it with `transform:none`, and the panel
|
|
1279
|
+
* width is still an inline `width` with no `--dsh-sidebar-width` variable.
|
|
1280
|
+
* alpha.2 appends fullscreen window chrome (`.63` windows / `.77` darwin), which
|
|
1281
|
+
* does not move the blur target, AND the `ui-plugin-manager` package — so the
|
|
1282
|
+
* plugin-page frost below is prerelease-gated rather than channel-wide.
|
|
1283
|
+
* `ui-dockkit` still has no `TabLayout.tsx`, so no `[data-dockkit-host]` /
|
|
1284
|
+
* `[data-dockkit-empty]` on this line either.
|
|
1285
|
+
*
|
|
1286
|
+
* So the panel mechanics match 0.1.5: the wrapper slides, the wrapper takes the
|
|
1287
|
+
* blur, and the wrapper needs promoting.
|
|
1288
|
+
*
|
|
1289
|
+
* @module
|
|
1290
|
+
*/
|
|
1291
|
+
/** The extra session-header anchor this line introduces: the macOS titlebar work
|
|
1292
|
+
* added `conversation.session.header.leading`. It was deleted again at
|
|
1293
|
+
* 0.1.7-alpha.1 with no shim, so it is unique to this line — which is why the
|
|
1294
|
+
* channel alone is not enough to answer it and the prerelease is read.
|
|
1295
|
+
* `alpha.1` predates it.
|
|
1296
|
+
*
|
|
1297
|
+
* Erring toward "included": a slot key the host never emits simply matches
|
|
1298
|
+
* nothing, whereas a key we omit hides a header area from the plugin for the
|
|
1299
|
+
* whole session. */
|
|
1300
|
+
function hasLeadingHeaderSlot(version) {
|
|
1301
|
+
const pre = prereleaseOf(version);
|
|
1302
|
+
if (pre === null) return true;
|
|
1303
|
+
const m = /^alpha\.(\d+)$/.exec(pre);
|
|
1304
|
+
return m === null || Number(m[1]) >= 2;
|
|
1305
|
+
}
|
|
1306
|
+
/** The other thing this line splits on: `ui-plugin-manager` — the package that
|
|
1307
|
+
* renders `设置 → 插件` and stamps `[data-plugin-panel]` — was CREATED at
|
|
1308
|
+
* alpha.2. Until then the plugin list was a settings tab in
|
|
1309
|
+
* `ui-settings-plugin-inventory`, which carries `[data-plugin-scope]` but no
|
|
1310
|
+
* card list to frame, so alpha.1 has nothing for the frost rule to apply to.
|
|
1311
|
+
*
|
|
1312
|
+
* Same erring-toward-included convention as `hasLeadingHeaderSlot`: the rule's
|
|
1313
|
+
* own `[data-plugin-panel]` prefix means an over-broad arm cannot paint, while
|
|
1314
|
+
* an over-narrow one leaves a page bare for the whole session. */
|
|
1315
|
+
function hasPluginManagerPage(version) {
|
|
1316
|
+
const pre = prereleaseOf(version);
|
|
1317
|
+
if (pre === null) return true;
|
|
1318
|
+
const m = /^alpha\.(\d+)$/.exec(pre);
|
|
1319
|
+
return m === null || Number(m[1]) >= 2;
|
|
1320
|
+
}
|
|
1321
|
+
function panelFragments$2() {
|
|
1322
|
+
return {
|
|
1323
|
+
promotion: `${PANEL_WRAPPER}{position:fixed!important;z-index:26!important}${PANEL_FULLSCREEN}{z-index:40!important}`,
|
|
1324
|
+
blur: panelBlurRule(PANEL_WRAPPER) + panelBlurRule(BETTER_SIDEBAR_PANEL)
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
function createAdapter$2(host) {
|
|
1328
|
+
return {
|
|
1329
|
+
id: "0.1.6-alpha.1/alpha.2",
|
|
1330
|
+
channel: "0.1.6-alpha",
|
|
1331
|
+
host,
|
|
1332
|
+
panelFragments: panelFragments$2,
|
|
1333
|
+
pluginPageRule: hasPluginManagerPage(host.version) ? PLUGIN_PAGE_FROST_RULE : "",
|
|
1334
|
+
surface: {
|
|
1335
|
+
ownsSidebarGuideSurface: true,
|
|
1336
|
+
headerSlotKeys: hasLeadingHeaderSlot(host.version) ? [...BASE_HEADER_SLOT_KEYS, "conversation.session.header.leading"] : BASE_HEADER_SLOT_KEYS
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region src/client/host-compat/versions/v0-1-7-alpha-1-2-rc-1/adapter.ts
|
|
1342
|
+
/**
|
|
1343
|
+
* Adapter for the DSH 0.1.7-alpha channel (`0.1.7-alpha.1`, `0.1.7-alpha.2`),
|
|
1344
|
+
* re-verified unchanged at `0.1.7-rc.1`.
|
|
1345
|
+
*
|
|
1346
|
+
* This is the line the panel mechanics actually changed on — verified against
|
|
1347
|
+
* `dsh-v0.1.6-alpha.2` → `dsh-v0.1.7-alpha.1`, where `SidebarRight.module.css`
|
|
1348
|
+
* was rewritten (116 lines changed) and `ui-dockkit/src/components/TabLayout.tsx`
|
|
1349
|
+
* was ADDED. Both facts were checked, not assumed.
|
|
1350
|
+
*
|
|
1351
|
+
* `0.1.7-rc.1` was checked the same way, by diffing every package that emits an
|
|
1352
|
+
* anchor this plugin matches (the panel CSS, dockkit, the plugin-manager page, the
|
|
1353
|
+
* slot renderer, the theme tokens): no anchor line moved, and the two UI changes it
|
|
1354
|
+
* carries do not reach us —
|
|
1355
|
+
* · `ui-plugin-manager`: a new `InstallDialog` failure screen plus compatibility
|
|
1356
|
+
* wording. The card block (`section` → `div.groupHead` + `ul.cards`) is
|
|
1357
|
+
* untouched, and the dialog is a body-level `Modal`, so the `:has()` valve of
|
|
1358
|
+
* `PLUGIN_PAGE_FROST_RULE` never even has to fire for it.
|
|
1359
|
+
* · `ui-conversation`: `.titleRow` gains `container-type:inline-size`, which makes
|
|
1360
|
+
* it a stacking context and a containing block. Header slots sit inside that
|
|
1361
|
+
* row, but this plugin reaches them through background/blur tokens, and the
|
|
1362
|
+
* dropdowns they trigger portal to `document.body` (see `header-tag.ts`), so
|
|
1363
|
+
* nothing of ours lands inside the new containment.
|
|
1364
|
+
*
|
|
1365
|
+
* Host shape this line presents:
|
|
1366
|
+
* .panel { position:absolute; --dsh-dockkit-dock-layer:10; pointer-events:none }
|
|
1367
|
+
* — no transform, no visibility, and no z-index of its own any more.
|
|
1368
|
+
* .panel :global([data-dockkit-host='dock']),
|
|
1369
|
+
* .panel :global([data-dockkit-empty]),
|
|
1370
|
+
* .panel :global([data-dockkit-divider]) { transform:translateX(var(--dsh-sidebar-width)); visibility:hidden }
|
|
1371
|
+
* .panel[data-sidebar-right-open] :global(…) { transform:none }
|
|
1372
|
+
* — the DOCKED CHILDREN are what slide.
|
|
1373
|
+
* .panel[data-sidebar-right-panel='fullscreen'] { --dsh-dockkit-dock-layer:40 }
|
|
1374
|
+
* — fullscreen no longer flips the panel to `position:fixed`; it raises a
|
|
1375
|
+
* layer variable, and the width becomes `100vw` on the absolute panel.
|
|
1376
|
+
*
|
|
1377
|
+
* Consequences encoded below:
|
|
1378
|
+
* 1. The blur belongs on the children. On the wrapper it frosted a stationary
|
|
1379
|
+
* frame — "the blur stays put while the sidebar moves".
|
|
1380
|
+
* 2. The promotion must NOT be emitted. `position:fixed` detaches the panel from
|
|
1381
|
+
* the track the host animates, which is the whole reason 2 the blur stopped
|
|
1382
|
+
* travelling. The host's own sheet documents the content root as owning "no
|
|
1383
|
+
* stacking context or transform", so `backdrop-filter` resolves against the
|
|
1384
|
+
* page and samples the wallpaper unaided.
|
|
1385
|
+
* 3. `[data-dockkit-empty]` must be included alongside `[data-dockkit-host]`: a
|
|
1386
|
+
* pane with no tabs renders the empty host instead, and keying on `host`
|
|
1387
|
+
* alone mis-detects that (transient) shape.
|
|
1388
|
+
*
|
|
1389
|
+
* Also true of this line, recorded so the next reader does not re-derive it:
|
|
1390
|
+
* · `conversation.session.header.leading` was DELETED here with no shim, so this
|
|
1391
|
+
* folder deliberately does not list it (see the 0.1.6 folder).
|
|
1392
|
+
* · `--dsw-specific-menu` was severed from `--dsw-alias-bg-layer-3` and became a
|
|
1393
|
+
* literal translucent rgba, plus a `html[data-platform='darwin']` re-override.
|
|
1394
|
+
* The plugin already samples and writes that token directly, so the severance
|
|
1395
|
+
* needs no adapter of its own — but note the consequence: `toRgba()` REPLACES
|
|
1396
|
+
* alpha, so the header slider now overrides the host's own 0.58 rather than
|
|
1397
|
+
* fading an opaque layer. Intended (the slider means "this opacity"), worth
|
|
1398
|
+
* knowing if the 0.1.7 header ever looks too thin.
|
|
1399
|
+
* · `[data-chat-flow]` is stamped on a nested `ChatGroupSeat` content element
|
|
1400
|
+
* too. Checked against every use in this plugin: the stroke group is inherited
|
|
1401
|
+
* so a second match changes nothing, and the view-card discovery takes the
|
|
1402
|
+
* first match, which is still the real flow root. No arm needed.
|
|
1403
|
+
*
|
|
1404
|
+
* @module
|
|
1405
|
+
*/
|
|
1406
|
+
function panelFragments$1() {
|
|
1407
|
+
return {
|
|
1408
|
+
promotion: "",
|
|
1409
|
+
blur: panelBlurRule(DOCKKIT_SLIDERS) + panelBlurRule(BETTER_SIDEBAR_PANEL)
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
function createAdapter$1(host) {
|
|
1413
|
+
return {
|
|
1414
|
+
id: "0.1.7-alpha.1/alpha.2/rc.1",
|
|
1415
|
+
channel: "0.1.7-alpha",
|
|
1416
|
+
host,
|
|
1417
|
+
panelFragments: panelFragments$1,
|
|
1418
|
+
pluginPageRule: PLUGIN_PAGE_FROST_RULE,
|
|
1419
|
+
surface: {
|
|
1420
|
+
ownsSidebarGuideSurface: true,
|
|
1421
|
+
headerSlotKeys: BASE_HEADER_SLOT_KEYS
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
//#endregion
|
|
1426
|
+
//#region src/client/host-compat/versions/unknown/adapter.ts
|
|
1427
|
+
/**
|
|
1428
|
+
* Stand-in for "we could not resolve the release".
|
|
1429
|
+
*
|
|
1430
|
+
* This is a REAL case, not a theoretical one: a plain `~/.dsh` install has no
|
|
1431
|
+
* launcher layout, so the front-door probe finds neither a manifest nor a
|
|
1432
|
+
* version-shaped directory name. It must not degrade into a guessed channel —
|
|
1433
|
+
* a wrong guess silently restyles the window.
|
|
1434
|
+
*
|
|
1435
|
+
* Instead, the DOM arbitrates. Both panel arms ship, each gated on the shape that
|
|
1436
|
+
* only one release family can present, which is exactly how this plugin behaved
|
|
1437
|
+
* before release detection existed. It is why an unresolved host still gets a
|
|
1438
|
+
* working panel blur.
|
|
1439
|
+
*
|
|
1440
|
+
* The one concession this costs: an engine without `:has()` cannot parse a gated
|
|
1441
|
+
* selector and drops that block, so such a host loses the promotion. A resolved
|
|
1442
|
+
* host does not have that problem — its arm is unconditional.
|
|
1443
|
+
*
|
|
1444
|
+
* @module
|
|
1445
|
+
*/
|
|
1446
|
+
function panelFragments() {
|
|
1447
|
+
const wrapper = `${PANEL_WRAPPER}${WITHOUT_DOCKKIT_FRAME}`;
|
|
1448
|
+
return {
|
|
1449
|
+
promotion: `${wrapper}{position:fixed!important;z-index:26!important}${PANEL_FULLSCREEN}${WITHOUT_DOCKKIT_FRAME}{z-index:40!important}`,
|
|
1450
|
+
blur: panelBlurRule(DOCKKIT_SLIDERS) + panelBlurRule(wrapper) + panelBlurRule(BETTER_SIDEBAR_PANEL)
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
function createAdapter(host) {
|
|
1454
|
+
return {
|
|
1455
|
+
id: "unknown",
|
|
1456
|
+
channel: "unknown",
|
|
1457
|
+
host,
|
|
1458
|
+
panelFragments,
|
|
1459
|
+
pluginPageRule: PLUGIN_PAGE_FROST_RULE,
|
|
1460
|
+
surface: {
|
|
1461
|
+
ownsSidebarGuideSurface: false,
|
|
1462
|
+
headerSlotKeys: BASE_HEADER_SLOT_KEYS
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
//#endregion
|
|
1467
|
+
//#region src/client/host-compat/versions/registry.ts
|
|
1468
|
+
const ADAPTERS = {
|
|
1469
|
+
"0.1.5-rc": createAdapter$3,
|
|
1470
|
+
"0.1.6-alpha": createAdapter$2,
|
|
1471
|
+
"0.1.7-alpha": createAdapter$1,
|
|
1472
|
+
unknown: createAdapter
|
|
1473
|
+
};
|
|
1474
|
+
/** Pick the adapter for a resolved host. `host.channel` is already narrowed to
|
|
1475
|
+
* the union by `adoptHostInfo`, so every channel here has a row by construction —
|
|
1476
|
+
* a new channel in `HostChannel` fails to compile until it is added. */
|
|
1477
|
+
function buildAdapter(host) {
|
|
1478
|
+
return ADAPTERS[host.channel](host);
|
|
1479
|
+
}
|
|
1480
|
+
//#endregion
|
|
1481
|
+
//#region src/client/host-compat/release.ts
|
|
1482
|
+
/**
|
|
1483
|
+
* The client-side half of the front door: where the release verdict lands.
|
|
1484
|
+
*
|
|
1485
|
+
* The browser cannot detect the host release — the client context exposes no
|
|
1486
|
+
* version field, and the app manifest that states it is only readable on the node
|
|
1487
|
+
* half. So `src/host-compat/detect.ts` resolves it there and the verdict travels
|
|
1488
|
+
* in the `read` RPC payload (see `client/rpc.ts`, which calls `adoptHostInfo`).
|
|
1489
|
+
*
|
|
1490
|
+
* This module only HOLDS the verdict and tells subscribers when it changes. It
|
|
1491
|
+
* deliberately answers no feature questions — that is `capabilities.ts`, so the
|
|
1492
|
+
* version → behaviour mapping stays in one place.
|
|
1493
|
+
*
|
|
1494
|
+
* @module
|
|
1495
|
+
*/
|
|
1496
|
+
let current = UNKNOWN_HOST_INFO;
|
|
1497
|
+
/** False until the server verdict lands. Feature gates must not read "not asked
|
|
1498
|
+
* yet" as "an old host" — see `adapterResolved()`. */
|
|
1499
|
+
let resolved = false;
|
|
1500
|
+
const subs$1 = /* @__PURE__ */ new Set();
|
|
1501
|
+
/** Cached adapter, keyed by the verdict it was built from. Held because React's
|
|
1502
|
+
* `useSyncExternalStore` requires a stable snapshot between notifications. */
|
|
1503
|
+
let adapterCache = null;
|
|
1504
|
+
/** Adopt the verdict delivered by the server's `read` payload.
|
|
1505
|
+
*
|
|
1506
|
+
* The payload's `channel` is IGNORED and re-derived from `version` here: the
|
|
1507
|
+
* two halves are built together, so a disagreeing pair can only mean one of them
|
|
1508
|
+
* is stale, and re-deriving keeps `channelOf` the single definition of what a
|
|
1509
|
+
* channel means. */
|
|
1510
|
+
function adoptHostInfo(raw) {
|
|
1511
|
+
if (raw === null || typeof raw !== "object") return;
|
|
1512
|
+
const h = raw;
|
|
1513
|
+
const version = typeof h.version === "string" && looksLikeVersion(h.version.trim()) ? h.version.trim() : null;
|
|
1514
|
+
const next = {
|
|
1515
|
+
version,
|
|
1516
|
+
channel: channelOf(version)
|
|
1517
|
+
};
|
|
1518
|
+
const changed = !resolved || current.version !== next.version || current.channel !== next.channel;
|
|
1519
|
+
current = next;
|
|
1520
|
+
resolved = true;
|
|
1521
|
+
if (changed) {
|
|
1522
|
+
adapterCache = null;
|
|
1523
|
+
subs$1.forEach((cb) => cb());
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
/** Subscribe to the verdict landing (or changing). */
|
|
1527
|
+
function subscribeHostInfo(cb) {
|
|
1528
|
+
subs$1.add(cb);
|
|
1529
|
+
return () => {
|
|
1530
|
+
subs$1.delete(cb);
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
/** Whether the server verdict has landed at all. */
|
|
1534
|
+
function adapterResolved() {
|
|
1535
|
+
return resolved;
|
|
1536
|
+
}
|
|
1537
|
+
/** The adapter chosen for the current verdict — `unknown`'s stand-in until the
|
|
1538
|
+
* verdict lands, which is the safe answer: it asserts nothing and lets the DOM
|
|
1539
|
+
* arbitrate. */
|
|
1540
|
+
function rHostAdapter() {
|
|
1541
|
+
adapterCache ??= buildAdapter(current);
|
|
1542
|
+
return adapterCache;
|
|
1543
|
+
}
|
|
1544
|
+
//#endregion
|
|
974
1545
|
//#region src/client/rpc.ts
|
|
975
1546
|
const RPC_CHANNEL = "/dsh-any-background";
|
|
976
1547
|
/** Same-origin serve URL of the persisted video (enough for <video src>/fetch). */
|
|
@@ -1035,6 +1606,7 @@ window.__ModuleLoader__.load({
|
|
|
1035
1606
|
const data = await rpcCall("read", {});
|
|
1036
1607
|
if (data && typeof data === "object") {
|
|
1037
1608
|
const d = data;
|
|
1609
|
+
adoptHostInfo(d.host);
|
|
1038
1610
|
if (d.config) adoptConfig(d.config);
|
|
1039
1611
|
if (typeof d.wallpaperUrl === "string") setWpImageUrl(d.wallpaperUrl);
|
|
1040
1612
|
else if (d.wallpaperUrl === null) setWpImageUrl(null);
|
|
@@ -1293,6 +1865,149 @@ window.__ModuleLoader__.load({
|
|
|
1293
1865
|
}
|
|
1294
1866
|
}
|
|
1295
1867
|
//#endregion
|
|
1868
|
+
//#region src/client/host-compat/capabilities.ts
|
|
1869
|
+
/**
|
|
1870
|
+
* The plugin's ONE vocabulary for host-dependent decisions.
|
|
1871
|
+
*
|
|
1872
|
+
* Everything downstream asks a QUESTION ("who owns the guide surface?", "which CSS
|
|
1873
|
+
* arm applies?") and gets an answer from the current adapter. No consumer names a
|
|
1874
|
+
* release. That is what keeps the per-version folders honest: when 0.1.8 needs a
|
|
1875
|
+
* new behaviour, a consumer changes by reading a new adapter field, not by growing
|
|
1876
|
+
* another version comparison.
|
|
1877
|
+
*
|
|
1878
|
+
* @module
|
|
1879
|
+
*/
|
|
1880
|
+
function sidebarGuideOwner() {
|
|
1881
|
+
if (!adapterResolved()) return "pending";
|
|
1882
|
+
return rHostAdapter().surface.ownsSidebarGuideSurface ? "host" : "plugin";
|
|
1883
|
+
}
|
|
1884
|
+
/** The current adapter, for callers that need more than one field from it (and to
|
|
1885
|
+
* keep the snapshot identity stable across a single render pass). */
|
|
1886
|
+
function hostAdapter() {
|
|
1887
|
+
return rHostAdapter();
|
|
1888
|
+
}
|
|
1889
|
+
//#endregion
|
|
1890
|
+
//#region src/client/header-tag.ts
|
|
1891
|
+
/**
|
|
1892
|
+
* Header-surface tagging.
|
|
1893
|
+
*
|
|
1894
|
+
* WHY A RUNTIME TAG IS NEEDED
|
|
1895
|
+
* The session-header dropdowns (open-in-app, session-log, Agent Team, the job
|
|
1896
|
+
* list, the subagent lineage tree) render their popover through the Menu/tree
|
|
1897
|
+
* primitives, which PORTAL the list to `document.body`. A portaled list is not a
|
|
1898
|
+
* DOM descendant of the header — no ancestor selector can reach it — and the
|
|
1899
|
+
* primitive emits no `id`/`aria-controls` back-link to its anchor either. Pure
|
|
1900
|
+
* CSS class matching was the remaining option, but 0.1.7 broke the shapes this
|
|
1901
|
+
* plugin relied on: open-in-app moved from in-place to `portal` (so the old
|
|
1902
|
+
* `:not(_portal)` discriminator no longer selected it), and the session row menu
|
|
1903
|
+
* became a dynamic `sidebar.workspaces.session.menu.item` slot whose rows are no
|
|
1904
|
+
* longer guaranteed to include a destructive one.
|
|
1905
|
+
*
|
|
1906
|
+
* THE SIGNAL
|
|
1907
|
+
* The slot renderer stamps every registered slot with `data-slot="<key>"`
|
|
1908
|
+
* (ui-renderer `scoped-slots.tsx`). Those anchors live in the header subtree, so
|
|
1909
|
+
* the plugin can detect when a header trigger is expanded — which keys identify
|
|
1910
|
+
* the header is a per-release fact and comes from the host adapter, not from a
|
|
1911
|
+
* hardcoded list here.
|
|
1912
|
+
*
|
|
1913
|
+
* WHY NOT GEOMETRY
|
|
1914
|
+
* A tempting link is the inline position the primitive writes on a portaled list
|
|
1915
|
+
* (`style="left: …; top: …"`). It was tried and REJECTED: an `align="end"` list
|
|
1916
|
+
* is flush to the trigger's right edge while the trigger itself sits at the far
|
|
1917
|
+
* right of the header, so the two boxes often do not overlap horizontally (in
|
|
1918
|
+
* the reported DOM the list is at `left:1012` with the trigger near x=1400). Any
|
|
1919
|
+
* purely geometric proximity test therefore misses real header menus.
|
|
1920
|
+
*
|
|
1921
|
+
* THE ACTUAL RULE (structural, no geometry, no timing)
|
|
1922
|
+
* A header dropdown can only be open while its trigger reports
|
|
1923
|
+
* `aria-expanded="true"`, and opening one is what mounts the popover. So a
|
|
1924
|
+
* popover tag is maintained as: "there is at least one expanded header trigger,
|
|
1925
|
+
* AND this popover is not owned by another group". Popovers are only ever
|
|
1926
|
+
* tagged/released on a change of that combined state, which makes the tag
|
|
1927
|
+
* self-consistent and idempotent.
|
|
1928
|
+
*
|
|
1929
|
+
* The one hazard is a popover that is open for an unrelated reason (a composer
|
|
1930
|
+
* menu, a hover card) *while* a header trigger stays expanded. Such a popover
|
|
1931
|
+
* is not a candidate: it is excluded by `ownsForeignSurface`, which skips any
|
|
1932
|
+
* popover carrying markers of a different slider group. In practice header
|
|
1933
|
+
* dropdowns are transient (opening one closes the others), so this is a guard
|
|
1934
|
+
* rather than the common path.
|
|
1935
|
+
*
|
|
1936
|
+
* @module
|
|
1937
|
+
*/
|
|
1938
|
+
/** Marker attribute the stylesheet keys on. */
|
|
1939
|
+
const HEADER_POPOVER_ATTR = "data-dsh-any-header-popover";
|
|
1940
|
+
/** Portaled popovers a header trigger can own. */
|
|
1941
|
+
const POPOVER_SELECTOR = "[role=\"menu\"], [role=\"tree\"], [role=\"dialog\"]";
|
|
1942
|
+
/** Which session-header slot anchors to watch — supplied by the host adapter,
|
|
1943
|
+
* because the set is not the same on every release: 0.1.6-alpha.2 registers a
|
|
1944
|
+
* sixth (`.leading`) that neither the line before nor the line after has.
|
|
1945
|
+
* Memoised on the array identity, which the adapter keeps stable until the
|
|
1946
|
+
* release verdict lands. */
|
|
1947
|
+
let slotAnchorCache = null;
|
|
1948
|
+
function headerSlotAnchors() {
|
|
1949
|
+
const keys = hostAdapter().surface.headerSlotKeys;
|
|
1950
|
+
if (slotAnchorCache === null || slotAnchorCache.keys !== keys) slotAnchorCache = {
|
|
1951
|
+
keys,
|
|
1952
|
+
selector: keys.map((key) => `[data-slot="${key}"]`).join(",")
|
|
1953
|
+
};
|
|
1954
|
+
return slotAnchorCache.selector;
|
|
1955
|
+
}
|
|
1956
|
+
/** Whether any header slot anchor has an expanded trigger. */
|
|
1957
|
+
function headerTriggerOpen() {
|
|
1958
|
+
for (const anchor of document.querySelectorAll(headerSlotAnchors())) if (anchor.querySelector("[aria-expanded=\"true\"]") !== null) return true;
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
/** Surfaces that belong to a different slider group and must never be claimed.
|
|
1962
|
+
* `_denseList` without `_portal` is the in-place card-side dense menu, an
|
|
1963
|
+
* `_ioCard`/`_menu`-classed body-level list belongs elsewhere, and a modal is
|
|
1964
|
+
* its own surface. */
|
|
1965
|
+
function ownsForeignSurface(el) {
|
|
1966
|
+
if (el.getAttribute("aria-modal") === "true") return true;
|
|
1967
|
+
return el.parentElement !== document.body;
|
|
1968
|
+
}
|
|
1969
|
+
let observer = null;
|
|
1970
|
+
let raf = null;
|
|
1971
|
+
/** Recompute tags from the current open state. */
|
|
1972
|
+
function refreshHeaderPopovers() {
|
|
1973
|
+
const open = headerTriggerOpen();
|
|
1974
|
+
const candidates = [...document.querySelectorAll(POPOVER_SELECTOR)].filter((el) => !ownsForeignSurface(el));
|
|
1975
|
+
if (!open) {
|
|
1976
|
+
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
for (const el of candidates) el.setAttribute(HEADER_POPOVER_ATTR, "");
|
|
1980
|
+
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) if (!candidates.includes(el)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
1981
|
+
}
|
|
1982
|
+
function schedule() {
|
|
1983
|
+
if (raf !== null) return;
|
|
1984
|
+
raf = window.requestAnimationFrame(() => {
|
|
1985
|
+
raf = null;
|
|
1986
|
+
refreshHeaderPopovers();
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
/** Start tagging. Returns a teardown that stops observing and clears markings. */
|
|
1990
|
+
function startHeaderPopoverTagging() {
|
|
1991
|
+
if (typeof MutationObserver === "undefined" || typeof document === "undefined") return () => {};
|
|
1992
|
+
refreshHeaderPopovers();
|
|
1993
|
+
observer = new MutationObserver(schedule);
|
|
1994
|
+
observer.observe(document.body, {
|
|
1995
|
+
childList: true,
|
|
1996
|
+
subtree: true,
|
|
1997
|
+
attributes: true,
|
|
1998
|
+
attributeFilter: ["aria-expanded"]
|
|
1999
|
+
});
|
|
2000
|
+
return () => {
|
|
2001
|
+
observer?.disconnect();
|
|
2002
|
+
observer = null;
|
|
2003
|
+
if (raf !== null) {
|
|
2004
|
+
window.cancelAnimationFrame(raf);
|
|
2005
|
+
raf = null;
|
|
2006
|
+
}
|
|
2007
|
+
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
//#endregion
|
|
1296
2011
|
//#region src/client/utils/image.ts
|
|
1297
2012
|
/**
|
|
1298
2013
|
* Read a chosen image file as a data URL WITHOUT re-encoding: the original
|
|
@@ -2711,17 +3426,15 @@ window.__ModuleLoader__.load({
|
|
|
2711
3426
|
lastBgKey = bgKey;
|
|
2712
3427
|
applyPartOpacities(ops);
|
|
2713
3428
|
}
|
|
3429
|
+
applyExemptDefaults();
|
|
2714
3430
|
} catch {}
|
|
2715
3431
|
}
|
|
2716
3432
|
const SETTINGS_PANEL_SEL = "[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]";
|
|
2717
3433
|
const SETTINGS_STYLE_RULE = `${SETTINGS_PANEL_SEL}{background:var(--dsh-any-bg-settings-surface,var(--dsw-alias-bg-layer-2));backdrop-filter:var(--dsh-any-blur-settings,none);--dsw-alias-bg-layer-1:var(--dsh-any-bg-settings-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-bg-settings-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-bg-settings-layer-3)}${SETTINGS_PANEL_SEL} .dab-card{backdrop-filter:var(--dsh-any-blur-card-panels,none);-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,none)}`;
|
|
2718
|
-
const POPOVER_BLUR_RULE = "[role=\"menu\"]:not([data-dockkit-tab-menu]),[role=\"listbox\"],body > [role=\"tree\"],body > [role=\"dialog\"]:not([aria-modal=\"true\"]){backdrop-filter:var(--dsh-any-blur-card-panels,none);-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,none)}";
|
|
2719
|
-
const INPUT_BLUR_RULE = "[data-composer-card]{position:relative;isolation:isolate}[data-composer-card]::before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;-webkit-backdrop-filter:var(--dsh-any-input-blur,none);backdrop-filter:var(--dsh-any-input-blur,none)}[data-cordis-panel]{-webkit-backdrop-filter:var(--dsh-any-input-blur,none);backdrop-filter:var(--dsh-any-input-blur,none)}[data-cordis-panel]{--dsw-specific-menu:var(--dsh-any-op-menu-cordis)!important}";
|
|
2720
3434
|
function applyInputBlur(px) {
|
|
2721
3435
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-input-blur", `blur(${px}px)`);
|
|
2722
3436
|
else document.documentElement.style.removeProperty("--dsh-any-input-blur");
|
|
2723
3437
|
}
|
|
2724
|
-
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)}";
|
|
2725
3438
|
/** Mirror of the bg-part blur on :root. --dsh-any-part-blur is element-scoped
|
|
2726
3439
|
* to the columns, so surfaces outside their subtree (the mobile header, or
|
|
2727
3440
|
* third-party styles) can never inherit it. */
|
|
@@ -2729,17 +3442,114 @@ window.__ModuleLoader__.load({
|
|
|
2729
3442
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-part-blur-global", `blur(${px}px)`);
|
|
2730
3443
|
else document.documentElement.style.removeProperty("--dsh-any-part-blur-global");
|
|
2731
3444
|
}
|
|
2732
|
-
const PANEL_TOKEN_RULE =
|
|
2733
|
-
|
|
2734
|
-
|
|
3445
|
+
const PANEL_TOKEN_RULE = `${PANEL_SURFACES}{--dsw-alias-bg-base:var(--dsh-any-panel-bg-base);--dsw-alias-bg-layer-1:var(--dsh-any-panel-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-panel-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-panel-layer-3)}`;
|
|
3446
|
+
/** The header dropdown surfaces, spelled ONCE. Three rules consume this set (the
|
|
3447
|
+
* static blur rule, the dynamic token rule and the stroke group), and every one
|
|
3448
|
+
* of them drifting by a single attribute re-introduces the bug where a header
|
|
3449
|
+
* menu keeps the card slider's look instead of its own.
|
|
3450
|
+
*
|
|
3451
|
+
* Strategy 1 is the runtime tag (`header-tag.ts`); strategies 2 are class-shape
|
|
3452
|
+
* fallbacks for the first paint before the tag observer runs. */
|
|
3453
|
+
const HEADER_TAG_SELECTOR = `[${HEADER_POPOVER_ATTR}]`;
|
|
3454
|
+
const HEADER_SURFACES = [
|
|
3455
|
+
HEADER_TAG_SELECTOR,
|
|
3456
|
+
"ul[class*=\"_menu\"],div[role=\"dialog\"][class*=\"_panel\"]:not([aria-modal=\"true\"])",
|
|
3457
|
+
`[role="menu"][class*="_denseList"]:not([class*="_portal"])`,
|
|
3458
|
+
"[role=\"tree\"][class*=\"_menu\"]"
|
|
3459
|
+
];
|
|
3460
|
+
/** SPECIFICITY IS LOAD-BEARING HERE. The card group's rule is
|
|
3461
|
+
* `[role="menu"]:not([data-dockkit-tab-menu])` — specificity (0,2,0) — so a bare
|
|
3462
|
+
* `[data-dsh-any-header-popover]` (0,1,0) LOSES to it no matter how late it
|
|
3463
|
+
* appears in the sheet. That is exactly how the tagged open-in-app menu kept
|
|
3464
|
+
* taking the card blur. `[role]` is a no-op presence test that raises only the
|
|
3465
|
+
* tagged arm to (0,2,0); the other three already qualify at (0,2,0)+. */
|
|
3466
|
+
const HEADER_SURFACES_PADDED = [`${HEADER_TAG_SELECTOR}[role]`, ...HEADER_SURFACES.slice(1)];
|
|
3467
|
+
const HEADER_POPOVER_RULE = `${HEADER_SURFACES_PADDED.join(",")}{-webkit-backdrop-filter:var(--dsh-any-blur-header,none);backdrop-filter:var(--dsh-any-blur-header,none)}`;
|
|
3468
|
+
/** Selector the dynamic header token rule targets — the same set as the static
|
|
3469
|
+
* HEADER_POPOVER_RULE above, so the two can never drift apart. */
|
|
3470
|
+
const HEADER_TOKEN_SELECTOR = HEADER_SURFACES_PADDED.join(",");
|
|
3471
|
+
/** Header-popover surfaces: retint the menu token and (re)write the blur.
|
|
3472
|
+
* Mirrors applyProduced: the static rule above never changes, so a drag only
|
|
3473
|
+
* rewrites the blur variable and this one dynamic rule. */
|
|
3474
|
+
function applyHeaderPopovers() {
|
|
3475
|
+
const root = document.documentElement;
|
|
3476
|
+
const px = rBlurs().header;
|
|
3477
|
+
if (px > 0) root.style.setProperty("--dsh-any-blur-header", `blur(${px}px)`);
|
|
3478
|
+
else root.style.removeProperty("--dsh-any-blur-header");
|
|
3479
|
+
let opacity = rHeaderOpacity();
|
|
3480
|
+
if (opacity < 0) opacity = 0;
|
|
3481
|
+
if (opacity > 1) opacity = 1;
|
|
3482
|
+
const menu = (paletteTokens() ?? readHostOpacityTokens())?.["--dsw-specific-menu"];
|
|
3483
|
+
const el = ensureHeaderStyle();
|
|
3484
|
+
const css = menu === void 0 ? "" : `${HEADER_TOKEN_SELECTOR}{--dsw-specific-menu:${toRgba(menu, opacity)}}`;
|
|
3485
|
+
if (el.textContent !== css) el.textContent = css;
|
|
3486
|
+
}
|
|
3487
|
+
let headerStyleEl = null;
|
|
3488
|
+
function ensureHeaderStyle() {
|
|
3489
|
+
if (headerStyleEl?.isConnected) return headerStyleEl;
|
|
3490
|
+
headerStyleEl = document.createElement("style");
|
|
3491
|
+
headerStyleEl.dataset.plugin = "dsh-any-background-header";
|
|
3492
|
+
document.head.appendChild(headerStyleEl);
|
|
3493
|
+
return headerStyleEl;
|
|
3494
|
+
}
|
|
3495
|
+
/** Pin the exempt groups (confirm dialogs and the session-row menu) so no
|
|
3496
|
+
* appearance slider moves them — while KEEPING the active theme color.
|
|
3497
|
+
*
|
|
3498
|
+
* "Default" here means "not faded, not blurred", NOT "host-palette original":
|
|
3499
|
+
* an earlier version restored the :root host colors, which also stripped the
|
|
3500
|
+
* picked theme color and made the menu look un-themed. The correct source is
|
|
3501
|
+
* the same one every other slider uses (the plugin palette when there is one,
|
|
3502
|
+
* else the host's resolved tokens) with alpha pinned to 1 — so the surface
|
|
3503
|
+
* keeps whatever color the theme currently dictates and only loses the slider's
|
|
3504
|
+
* translucency and blur.
|
|
3505
|
+
*
|
|
3506
|
+
* Emitted as REAL literals into a dedicated stylesheet, never as
|
|
3507
|
+
* `var(--x, var(--x))`: a self-referencing fallback is a CSS cycle that
|
|
3508
|
+
* computes to the guaranteed-invalid value and turns every consuming
|
|
3509
|
+
* `background:var(--dsw-alias-bg-layer-2)` fully transparent. Literals also
|
|
3510
|
+
* mean that when no color can be resolved we emit NO declaration at all, so
|
|
3511
|
+
* the surface keeps the re-scoped value rather than going transparent. */
|
|
3512
|
+
function applyExemptDefaults() {
|
|
3513
|
+
if (typeof document === "undefined") return;
|
|
3514
|
+
const source = paletteTokens() ?? readHostOpacityTokens();
|
|
3515
|
+
const el = ensureExemptStyle();
|
|
3516
|
+
const decls = [];
|
|
3517
|
+
const add = (token, value) => {
|
|
3518
|
+
if (value === void 0 || value === "") return;
|
|
3519
|
+
decls.push(`${token}:${toRgba(value, 1)}`);
|
|
3520
|
+
};
|
|
3521
|
+
add("--dsw-alias-bg-layer-1", source?.["--dsw-alias-bg-layer-1"]);
|
|
3522
|
+
add("--dsw-alias-bg-layer-2", source?.["--dsw-alias-bg-layer-2"]);
|
|
3523
|
+
add("--dsw-alias-bg-layer-3", source?.["--dsw-alias-bg-layer-3"]);
|
|
3524
|
+
add("--dsw-specific-menu", source?.["--dsw-specific-menu"]);
|
|
3525
|
+
if (decls.length === 0) {
|
|
3526
|
+
if (el.textContent !== "") el.textContent = "";
|
|
3527
|
+
return;
|
|
3528
|
+
}
|
|
3529
|
+
const dialog = "[role=\"dialog\"][aria-modal=\"true\"]:not([aria-labelledby])";
|
|
3530
|
+
const body = decls.join(";");
|
|
3531
|
+
const css = `${dialog},${dialog} *{${body}}[role="menu"]:has([class*="_danger"]){${body}}`;
|
|
3532
|
+
if (el.textContent !== css) el.textContent = css;
|
|
3533
|
+
}
|
|
3534
|
+
let exemptStyleEl = null;
|
|
3535
|
+
function ensureExemptStyle() {
|
|
3536
|
+
if (exemptStyleEl?.isConnected) return exemptStyleEl;
|
|
3537
|
+
exemptStyleEl = document.createElement("style");
|
|
3538
|
+
exemptStyleEl.dataset.plugin = "dsh-any-background-exempt";
|
|
3539
|
+
document.head.appendChild(exemptStyleEl);
|
|
3540
|
+
return exemptStyleEl;
|
|
3541
|
+
}
|
|
3542
|
+
const PRODUCED_RULE = "[data-code-block-content] pre,[data-code-block-banner],[data-composer-chip],[data-changed-files]>button:first-child,[data-terminal],[data-read],[data-context-injection-body],[data-search],[data-web],[class*=\"_ioCard\"]{-webkit-backdrop-filter:var(--dsh-any-blur-prod,none);backdrop-filter:var(--dsh-any-blur-prod,none)}@supports (background:color-mix(in srgb,red 50%,transparent)){.md-code-block{background:transparent!important}.md-code-block>div{background-color:transparent!important}.md-code-block [data-code-block-content] pre{background-color:color-mix(in srgb,var(--dsl-code-block-background,var(--dsw-alias-markdown-code-block,transparent)) var(--dsh-any-prod-pct,100%),transparent)!important}.md-code-block [data-code-block-banner]{background-color:color-mix(in srgb,var(--dsl-code-block-banner-background-color,var(--dsw-alias-markdown-code-block-banner,transparent)) var(--dsh-any-prod-pct,100%),transparent)!important}[data-code-block-content]{--shiki-background:color-mix(in srgb,var(--dsw-alias-markdown-code-block,var(--dsl-code-block-background,transparent)) var(--dsh-any-prod-pct,100%),transparent)}:not(pre)>code{background-color:color-mix(in srgb,var(--dsw-alias-markdown-inline-code,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-composer-chip]>*{background-color:color-mix(in srgb,var(--dsw-alias-interactive-bg-hover,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-terminal]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-terminal] button{background-color:transparent!important}[data-read]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-read]>div:first-child{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block-banner,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-context-injection-body]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-search]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-web]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[class*=\"_ioCard\"]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}}";
|
|
2735
3543
|
const STROKE_RULE = [
|
|
2736
3544
|
`${SETTINGS_PANEL_SEL}{-webkit-text-stroke:var(--dsh-any-stroke-settings-w,0px) var(--dsh-any-stroke-settings-c,transparent);paint-order:stroke fill}`,
|
|
2737
3545
|
"[role=\"menu\"]:not([data-dockkit-tab-menu]),[role=\"listbox\"],body>[role=\"tree\"],body>[role=\"dialog\"]:not([aria-modal=\"true\"]){-webkit-text-stroke:var(--dsh-any-stroke-card-w,0px) var(--dsh-any-stroke-card-c,transparent);paint-order:stroke fill}",
|
|
2738
3546
|
"[data-composer-card],[data-cordis-panel],[data-composer-card] textarea,[data-composer-card] input,[data-composer-card] [contenteditable],[data-cordis-panel] input,[data-cordis-panel] textarea{-webkit-text-stroke:var(--dsh-any-stroke-input-w,0px) var(--dsh-any-stroke-input-c,transparent);paint-order:stroke fill}",
|
|
2739
3547
|
`[data-chat-flow]{-webkit-text-stroke:var(--dsh-any-stroke-chat-w,0px) var(--dsh-any-stroke-chat-c,transparent);paint-order:stroke fill}`,
|
|
2740
3548
|
`[data-conversation-composer-overlay]{-webkit-text-stroke:var(--dsh-any-stroke-trajectory-w,0px) var(--dsh-any-stroke-trajectory-c,transparent);paint-order:stroke fill}`,
|
|
2741
|
-
"[data-code-block-content] pre,[data-code-block-content],[data-code-block-banner],[data-composer-chip],:not(pre)>code{-webkit-text-stroke:var(--dsh-any-stroke-produced-w,0px) var(--dsh-any-stroke-produced-c,transparent);paint-order:stroke fill}",
|
|
2742
|
-
|
|
3549
|
+
"[data-code-block-content] pre,[data-code-block-content],[data-code-block-banner],[data-composer-chip],:not(pre)>code,[data-changed-files],[data-terminal],[data-read],[data-context-injection-body],[data-search],[data-web],[class*=\"_ioCard\"]{-webkit-text-stroke:var(--dsh-any-stroke-produced-w,0px) var(--dsh-any-stroke-produced-c,transparent);paint-order:stroke fill}",
|
|
3550
|
+
`${PANEL_SURFACES}{-webkit-text-stroke:var(--dsh-any-stroke-panel-w,0px) var(--dsh-any-stroke-panel-c,transparent);paint-order:stroke fill}`,
|
|
3551
|
+
`${HEADER_SURFACES.join(",")}{-webkit-text-stroke:var(--dsh-any-stroke-header-w,0px) var(--dsh-any-stroke-header-c,transparent);paint-order:stroke fill}`,
|
|
3552
|
+
"[data-chat-flow] [role=\"status\"],[data-chat-flow] [data-turn-process],[data-chat-flow] [data-text-shimmer],[data-chat-flow] [style*=\"--dsh-text-shimmer-spread\"]{-webkit-text-stroke-width:0!important;paint-order:normal!important}",
|
|
2743
3553
|
`svg{-webkit-text-stroke-width:0!important;paint-order:normal!important}`,
|
|
2744
3554
|
`::placeholder{-webkit-text-stroke-width:0!important}`
|
|
2745
3555
|
].join("");
|
|
@@ -2753,7 +3563,8 @@ window.__ModuleLoader__.load({
|
|
|
2753
3563
|
"trajectory",
|
|
2754
3564
|
"input",
|
|
2755
3565
|
"panel",
|
|
2756
|
-
"produced"
|
|
3566
|
+
"produced",
|
|
3567
|
+
"header"
|
|
2757
3568
|
];
|
|
2758
3569
|
/** Resolve one group's stroke color key into a concrete CSS color.
|
|
2759
3570
|
* 'auto' contrasts the FONT direction (white fonts → black stroke and vice
|
|
@@ -2955,7 +3766,6 @@ window.__ModuleLoader__.load({
|
|
|
2955
3766
|
function applySettingsOverrides(op) {
|
|
2956
3767
|
applyLayerOverrides("--dsh-any-bg-settings", op, true);
|
|
2957
3768
|
}
|
|
2958
|
-
const TRAJECTORY_STYLE_RULE = "[data-conversation-composer-overlay]{--dsw-alias-bg-layer-1:var(--dsh-any-traj-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-traj-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-traj-layer-3)}";
|
|
2959
3769
|
function applyTrajectoryOverrides(op) {
|
|
2960
3770
|
applyLayerOverrides("--dsh-any-traj", op, false);
|
|
2961
3771
|
}
|
|
@@ -3061,7 +3871,21 @@ window.__ModuleLoader__.load({
|
|
|
3061
3871
|
let rightEl = null;
|
|
3062
3872
|
const PART_BLUR_CLASS = "dab-part-blur";
|
|
3063
3873
|
const PART_UNDERLAY_CLASS = "dab-part-underlay";
|
|
3064
|
-
|
|
3874
|
+
/** The AppFrame's main-bg clear-out, as a class rather than an inline write.
|
|
3875
|
+
*
|
|
3876
|
+
* WHY NOT INLINE (measured on 0.1.7): the frame's own translucent
|
|
3877
|
+
* `--dsw-alias-bg-base` is what hides the wallpaper, so `applyPartOpacities` has
|
|
3878
|
+
* to clear it. Written as `frameEl.style.background = 'transparent'` the clear
|
|
3879
|
+
* survived a settings change but not a sidebar open/close — the host owns that
|
|
3880
|
+
* element's `style` too (it animates `grid-template-columns`), and while it
|
|
3881
|
+
* re-asserts its own background the frame snaps back to an opaque
|
|
3882
|
+
* `rgb(200,207,218)`: wallpaper gone, and every frost on top of it flattened,
|
|
3883
|
+
* because a backdrop-filter with nothing behind it has nothing to blur. A class
|
|
3884
|
+
* rule with `!important` sits outside the host's `style` writes entirely, so the
|
|
3885
|
+
* clear cannot be re-clobbered and there is nothing to re-apply. */
|
|
3886
|
+
const PART_FRAME_CLASS = "dab-frame-clear";
|
|
3887
|
+
const FRAME_CLEAR_RULE = `.${PART_FRAME_CLASS}{background:transparent!important}`;
|
|
3888
|
+
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)}`;
|
|
3065
3889
|
let partBlurStyleEl = null;
|
|
3066
3890
|
function ensurePartBlurStyle() {
|
|
3067
3891
|
if (partBlurStyleEl?.isConnected) return;
|
|
@@ -3142,7 +3966,8 @@ window.__ModuleLoader__.load({
|
|
|
3142
3966
|
discoverParts();
|
|
3143
3967
|
if (frameEl === null) return;
|
|
3144
3968
|
const base = tokens["--dsw-alias-bg-base"];
|
|
3145
|
-
frameEl.
|
|
3969
|
+
frameEl.classList.add(PART_FRAME_CLASS);
|
|
3970
|
+
if (frameEl.style.background !== "") frameEl.style.removeProperty("background");
|
|
3146
3971
|
if (centerEl !== null) centerEl.style.background = base !== void 0 ? toRgba(base, ops.bg) : "transparent";
|
|
3147
3972
|
if (rightEl !== null) rightEl.style.background = "";
|
|
3148
3973
|
}
|
|
@@ -3167,6 +3992,7 @@ window.__ModuleLoader__.load({
|
|
|
3167
3992
|
applyInputBlur(blurs.input);
|
|
3168
3993
|
applyPanelBlur(blurs.panel);
|
|
3169
3994
|
applyProduced();
|
|
3995
|
+
applyHeaderPopovers();
|
|
3170
3996
|
applyViewCards();
|
|
3171
3997
|
}
|
|
3172
3998
|
/** Produced/artifact surfaces (conversation code blocks + their banner +
|
|
@@ -3207,6 +4033,10 @@ window.__ModuleLoader__.load({
|
|
|
3207
4033
|
applyProduced();
|
|
3208
4034
|
return;
|
|
3209
4035
|
}
|
|
4036
|
+
if (part === "header") {
|
|
4037
|
+
applyHeaderPopovers();
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
3210
4040
|
if (part === "chat" || part === "trajectory") {
|
|
3211
4041
|
applyViewCards();
|
|
3212
4042
|
return;
|
|
@@ -3316,6 +4146,7 @@ window.__ModuleLoader__.load({
|
|
|
3316
4146
|
if (cached !== null && centerEl.contains(cached)) return cached;
|
|
3317
4147
|
viewTargets[idx] = null;
|
|
3318
4148
|
if (spec.fallback !== true) return null;
|
|
4149
|
+
if (centerEl.querySelector("[data-chat-flow],[data-conversation-scroll],[data-composer-seat],[data-conversation-composer-overlay]") === null) return null;
|
|
3319
4150
|
if (centerEl.querySelector("[data-conversation-scroll]") !== null) return null;
|
|
3320
4151
|
if (spec.opacity() <= 0 && spec.blur() <= 0) return null;
|
|
3321
4152
|
let best = null;
|
|
@@ -3334,6 +4165,7 @@ window.__ModuleLoader__.load({
|
|
|
3334
4165
|
if (el.clientHeight < centerEl.clientHeight * .5) continue;
|
|
3335
4166
|
if (el.clientHeight > (best?.clientHeight ?? 0)) best = el;
|
|
3336
4167
|
}
|
|
4168
|
+
if (best !== null && best.closest(SETTINGS_PANEL_SEL) !== null) best = null;
|
|
3337
4169
|
const refined = best !== null ? refineMessageColumn(best) : null;
|
|
3338
4170
|
viewTargets[idx] = refined;
|
|
3339
4171
|
return refined;
|
|
@@ -3808,6 +4640,9 @@ window.__ModuleLoader__.load({
|
|
|
3808
4640
|
applyTrajectoryOverrides(rTrajectoryOpacity());
|
|
3809
4641
|
}
|
|
3810
4642
|
applyPanelOverrides(rPanelOpacity());
|
|
4643
|
+
applyProduced();
|
|
4644
|
+
applyHeaderPopovers();
|
|
4645
|
+
applyExemptDefaults();
|
|
3811
4646
|
applyPartBlurs(rBlurs());
|
|
3812
4647
|
applyStrokes();
|
|
3813
4648
|
}
|
|
@@ -3844,6 +4679,11 @@ window.__ModuleLoader__.load({
|
|
|
3844
4679
|
document.documentElement.style.removeProperty("--dsh-any-blur-panel");
|
|
3845
4680
|
document.documentElement.style.removeProperty("--dsh-any-blur-prod");
|
|
3846
4681
|
document.documentElement.style.removeProperty("--dsh-any-prod-pct");
|
|
4682
|
+
document.documentElement.style.removeProperty("--dsh-any-blur-header");
|
|
4683
|
+
headerStyleEl?.remove();
|
|
4684
|
+
headerStyleEl = null;
|
|
4685
|
+
exemptStyleEl?.remove();
|
|
4686
|
+
exemptStyleEl = null;
|
|
3847
4687
|
for (const v of Object.values(OPACITY_VARS)) document.documentElement.style.removeProperty(v);
|
|
3848
4688
|
baseTokenKey = "";
|
|
3849
4689
|
lastBgKey = "";
|
|
@@ -3866,7 +4706,7 @@ window.__ModuleLoader__.load({
|
|
|
3866
4706
|
setBlur(sidebarEl, 0);
|
|
3867
4707
|
setBlur(centerEl, 0);
|
|
3868
4708
|
setBlur(rightEl, 0);
|
|
3869
|
-
if (frameEl !== null) frameEl.
|
|
4709
|
+
if (frameEl !== null) frameEl.classList.remove(PART_FRAME_CLASS);
|
|
3870
4710
|
if (centerEl !== null) centerEl.style.removeProperty("background");
|
|
3871
4711
|
if (rightEl !== null) rightEl.style.removeProperty("background");
|
|
3872
4712
|
stopWatchingParts();
|
|
@@ -3880,6 +4720,70 @@ window.__ModuleLoader__.load({
|
|
|
3880
4720
|
if (wpEl) wpEl.style.filter = v > 0 ? `blur(${v}px)` : "none";
|
|
3881
4721
|
}
|
|
3882
4722
|
//#endregion
|
|
4723
|
+
//#region src/client/host-compat/styles.ts
|
|
4724
|
+
/**
|
|
4725
|
+
* Assembly of the plugin's one static stylesheet.
|
|
4726
|
+
*
|
|
4727
|
+
* Until now the sheet was a single template literal in the client entry with
|
|
4728
|
+
* thirteen constants bolted together in whatever order they were added. That
|
|
4729
|
+
* order is load-bearing and was documented in three separate comment blocks in
|
|
4730
|
+
* `wallpaper.ts` — so it is encoded here instead, in one list, with the reason
|
|
4731
|
+
* next to each entry.
|
|
4732
|
+
*
|
|
4733
|
+
* What the release can change lives in one contiguous slot: the panel group
|
|
4734
|
+
* (which element carries the frost) and the plugin-manager page (whether this
|
|
4735
|
+
* release ships that page at all). Everything outside the slot is host-agnostic by
|
|
4736
|
+
* construction — it targets attributes every release in range emits.
|
|
4737
|
+
*
|
|
4738
|
+
* The sheet is rebuilt when the release verdict lands. It has to be: `apply` runs
|
|
4739
|
+
* synchronously while the verdict is a Node round-trip, so the FIRST sheet is
|
|
4740
|
+
* always the unresolved, DOM-arbitrated one. That is the correct interim state,
|
|
4741
|
+
* and swapping in the resolved arm a moment later is what removes the `:has()`
|
|
4742
|
+
* dependency from hosts we do know the answer for.
|
|
4743
|
+
*
|
|
4744
|
+
* @module
|
|
4745
|
+
*/
|
|
4746
|
+
/** The plugin's own dark-mode value on the body attribute the host sets. Scoping
|
|
4747
|
+
* the gradient to it avoids matching the host's own theme attribute. */
|
|
4748
|
+
const DARK_GRADIENT = "body[data-ds-dark-theme=\"dsh-any-background\"]::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(255,255,255,0.03) 0%,transparent 60%)}";
|
|
4749
|
+
/**
|
|
4750
|
+
* The order contract. Later entries override earlier ones at equal specificity,
|
|
4751
|
+
* which several groups rely on:
|
|
4752
|
+
* · HEADER_POPOVER_RULE after POPOVER_BLUR_RULE — a surface moving from the card
|
|
4753
|
+
* group to the header group must win, not merely be listed twice.
|
|
4754
|
+
* · PRODUCED_RULE / STROKE_RULE after the blur groups — they shield their
|
|
4755
|
+
* surfaces from an inherited slider.
|
|
4756
|
+
* · EXEMPT_DEFAULT_RULE last of the token groups — an exemption has to land
|
|
4757
|
+
* after whatever it exempts.
|
|
4758
|
+
* · PLACEHOLDER_RULE genuinely last — `::placeholder` resets must survive every
|
|
4759
|
+
* group above.
|
|
4760
|
+
*/
|
|
4761
|
+
function buildStaticStyles() {
|
|
4762
|
+
const adapter = rHostAdapter();
|
|
4763
|
+
const panel = adapter.panelFragments();
|
|
4764
|
+
return DARK_GRADIENT + FRAME_CLEAR_RULE + SETTINGS_STYLE_RULE + "[role=\"menu\"]:not([data-dockkit-tab-menu]),[role=\"listbox\"],body > [role=\"tree\"],body > [role=\"dialog\"]:not([aria-modal=\"true\"]){backdrop-filter:var(--dsh-any-blur-card-panels,none);-webkit-backdrop-filter:var(--dsh-any-blur-card-panels,none)}[data-conversation-composer-overlay]{--dsw-alias-bg-layer-1:var(--dsh-any-traj-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-traj-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-traj-layer-3)}[data-composer-card]{position:relative;isolation:isolate}[data-composer-card]::before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;-webkit-backdrop-filter:var(--dsh-any-input-blur,none);backdrop-filter:var(--dsh-any-input-blur,none)}[data-cordis-panel]{-webkit-backdrop-filter:var(--dsh-any-input-blur,none);backdrop-filter:var(--dsh-any-input-blur,none)}[data-cordis-panel]{--dsw-specific-menu:var(--dsh-any-op-menu-cordis)!important}.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)}" + PANEL_TOKEN_RULE + panel.promotion + panel.blur + adapter.pluginPageRule + PRODUCED_RULE + STROKE_RULE + HEADER_POPOVER_RULE + "[role=\"dialog\"][aria-modal=\"true\"]:not([aria-labelledby]){backdrop-filter:none;-webkit-backdrop-filter:none}[role=\"menu\"]:has([class*=\"_danger\"]){backdrop-filter:none;-webkit-backdrop-filter:none;-webkit-text-stroke-width:0}[data-composer-card] textarea::placeholder,[data-composer-card] input::placeholder,[data-composer-card] [contenteditable]::placeholder,[data-cordis-panel] input::placeholder,[data-cordis-panel] textarea::placeholder,.dab-input::placeholder,.dab-input textarea::placeholder,.dab-input input::placeholder{color:var(--dsh-any-placeholder,var(--dsw-alias-label-caption,#8a8f98))!important;font-style:italic;opacity:.85}";
|
|
4765
|
+
}
|
|
4766
|
+
/** Append the plugin's stylesheet and keep it matched to the host.
|
|
4767
|
+
* Returns a teardown for the plugin's effect scope. */
|
|
4768
|
+
function mountStaticStyles() {
|
|
4769
|
+
const el = document.createElement("style");
|
|
4770
|
+
el.dataset.plugin = "dsh-any-background";
|
|
4771
|
+
el.textContent = buildStaticStyles();
|
|
4772
|
+
document.head.appendChild(el);
|
|
4773
|
+
let last = el.textContent;
|
|
4774
|
+
const unsubscribe = subscribeHostInfo(() => {
|
|
4775
|
+
const next = buildStaticStyles();
|
|
4776
|
+
if (next !== last) {
|
|
4777
|
+
last = next;
|
|
4778
|
+
el.textContent = next;
|
|
4779
|
+
}
|
|
4780
|
+
});
|
|
4781
|
+
return () => {
|
|
4782
|
+
unsubscribe();
|
|
4783
|
+
el.parentNode?.removeChild(el);
|
|
4784
|
+
};
|
|
4785
|
+
}
|
|
4786
|
+
//#endregion
|
|
3883
4787
|
//#region src/client/utils/video.ts
|
|
3884
4788
|
/**
|
|
3885
4789
|
* Video-background helpers: capturing a single frame as a JPEG snapshot.
|
|
@@ -3947,7 +4851,11 @@ window.__ModuleLoader__.load({
|
|
|
3947
4851
|
* column: the host provides the modal chrome (backdrop, centering, closing).
|
|
3948
4852
|
* These classes style only the embedded shell; transient fixed layers (toast,
|
|
3949
4853
|
* color picker, background editor) escape through Portals on <html>. */
|
|
3950
|
-
.dab-root{position:relative;color:var(--dsw-alias-label-primary);animation:dab-fade-in .35s ease both;container-type:inline-size;display:flex;flex-direction:column;align-items:center;width:100%;min-width:0;--dab-mono:ui-monospace,"Cascadia Mono","SF Mono",Consolas,"Courier New",monospace}
|
|
4854
|
+
.dab-root{position:relative;box-sizing:border-box;color:var(--dsw-alias-label-primary);animation:dab-fade-in .35s ease both;container-type:inline-size;display:flex;flex-direction:column;align-items:center;width:100%;min-width:0;--dab-mono:ui-monospace,"Cascadia Mono","SF Mono",Consolas,"Courier New",monospace}
|
|
4855
|
+
/* The root needs its OWN border-box, not just the descendants' below: the
|
|
4856
|
+
* sidebar surface puts 12px of padding on it, and with the default content-box
|
|
4857
|
+
* that padding landed OUTSIDE the width:100%, so the page came out 24px wider
|
|
4858
|
+
* than the pane and the host clipped both edges off. */
|
|
3951
4859
|
.dab-root *,.dab-root *::before,.dab-root *::after{box-sizing:border-box}
|
|
3952
4860
|
.dab-root button{font-family:inherit}
|
|
3953
4861
|
|
|
@@ -5589,12 +6497,16 @@ window.__ModuleLoader__.load({
|
|
|
5589
6497
|
* signal and the body childList observer fires exactly when it appears. The
|
|
5590
6498
|
* panel surfaces we actually style are listed only as reinforcement: they are
|
|
5591
6499
|
* conditionally rendered (collapsed panels drop them), so they must never be
|
|
5592
|
-
* the sole signal.
|
|
6500
|
+
* the sole signal.
|
|
6501
|
+
*
|
|
6502
|
+
* `[data-sidebar-right-panel]` is deliberately ABSENT: on every host generation
|
|
6503
|
+
* it marks the host's own right Sidebar (always present once a session opens),
|
|
6504
|
+
* so counting it would make the better-sidebar verdict permanently true and the
|
|
6505
|
+
* panel slider could never drop back to its native "右方侧边栏" identity. */
|
|
5593
6506
|
const BETTER_SIDEBAR_MARKERS = [
|
|
5594
6507
|
"[data-dsh-better-sidebar]",
|
|
5595
6508
|
"[data-dsh-panel-host]",
|
|
5596
|
-
"[data-dsh-bottom-panel]"
|
|
5597
|
-
"[data-sidebar-right-panel]"
|
|
6509
|
+
"[data-dsh-bottom-panel]"
|
|
5598
6510
|
];
|
|
5599
6511
|
let betterSidebar = false;
|
|
5600
6512
|
const subs = /* @__PURE__ */ new Set();
|
|
@@ -5630,6 +6542,15 @@ window.__ModuleLoader__.load({
|
|
|
5630
6542
|
observer.observe(document.body, { childList: true });
|
|
5631
6543
|
return () => observer.disconnect();
|
|
5632
6544
|
}
|
|
6545
|
+
let nativeTabs = false;
|
|
6546
|
+
const nativeSubs = /* @__PURE__ */ new Set();
|
|
6547
|
+
/** Mark the native right-Sidebar service as confirmed. Sticky — the service
|
|
6548
|
+
* never goes away within a session. */
|
|
6549
|
+
function markNativeSidebarTabs() {
|
|
6550
|
+
if (nativeTabs) return;
|
|
6551
|
+
nativeTabs = true;
|
|
6552
|
+
nativeSubs.forEach((cb) => cb());
|
|
6553
|
+
}
|
|
5633
6554
|
//#endregion
|
|
5634
6555
|
//#region src/client/components/pages/InterfacePage.tsx
|
|
5635
6556
|
const PARTS$1 = [
|
|
@@ -5673,6 +6594,11 @@ window.__ModuleLoader__.load({
|
|
|
5673
6594
|
labelKey: "uiProduced",
|
|
5674
6595
|
Icon: TextIcon
|
|
5675
6596
|
},
|
|
6597
|
+
{
|
|
6598
|
+
isHeader: true,
|
|
6599
|
+
labelKey: "uiHeader",
|
|
6600
|
+
Icon: PanelIcon
|
|
6601
|
+
},
|
|
5676
6602
|
{
|
|
5677
6603
|
isPanel: true,
|
|
5678
6604
|
labelKey: "uiPanelRegion",
|
|
@@ -5683,7 +6609,6 @@ window.__ModuleLoader__.load({
|
|
|
5683
6609
|
const { t, setOps, setBlurs, setSop, setPanelOp, useStore } = p;
|
|
5684
6610
|
useStore((s) => s.metaRev);
|
|
5685
6611
|
const hasBetterSidebar = useBetterSidebar();
|
|
5686
|
-
const parts = PARTS$1.filter((part) => !(part.isPanel && !hasBetterSidebar));
|
|
5687
6612
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
5688
6613
|
className: "dab-head dab-rise",
|
|
5689
6614
|
style: { "--d": 0 },
|
|
@@ -5703,11 +6628,12 @@ window.__ModuleLoader__.load({
|
|
|
5703
6628
|
]
|
|
5704
6629
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5705
6630
|
className: "dab-grid-parts",
|
|
5706
|
-
children:
|
|
5707
|
-
const { labelKey, Icon, isSettings, isChat, isTrajectory, isPanel, isProduced } = part;
|
|
6631
|
+
children: PARTS$1.map((part, i) => {
|
|
6632
|
+
const { labelKey, Icon, isSettings, isChat, isTrajectory, isPanel, isProduced, isHeader } = part;
|
|
5708
6633
|
const opKey = part.opKey;
|
|
5709
|
-
const
|
|
5710
|
-
const
|
|
6634
|
+
const label = isPanel ? t(hasBetterSidebar ? "uiPanelRegion" : "uiPanelNative") : t(labelKey);
|
|
6635
|
+
const blurKey = isChat ? "chat" : isTrajectory ? "trajectory" : isSettings ? "settings" : isPanel ? "panel" : isProduced ? "produced" : isHeader ? "header" : opKey;
|
|
6636
|
+
const opacity = isChat ? rChatTextOpacity() : isTrajectory ? rTrajectoryOpacity() : isSettings ? rSop() : isPanel ? rPanelOpacity() : isProduced ? rProducedOpacity() : isHeader ? rHeaderOpacity() : rOps()[opKey];
|
|
5711
6637
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5712
6638
|
className: "dab-card dab-card-hover dab-rise",
|
|
5713
6639
|
style: { "--d": i + 1 },
|
|
@@ -5721,7 +6647,7 @@ window.__ModuleLoader__.load({
|
|
|
5721
6647
|
}),
|
|
5722
6648
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5723
6649
|
className: "dab-part-name",
|
|
5724
|
-
children:
|
|
6650
|
+
children: label
|
|
5725
6651
|
}),
|
|
5726
6652
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5727
6653
|
className: "dab-part-badge",
|
|
@@ -5753,6 +6679,9 @@ window.__ModuleLoader__.load({
|
|
|
5753
6679
|
} else if (isProduced) {
|
|
5754
6680
|
cfg.producedOpacity = op;
|
|
5755
6681
|
applyProduced();
|
|
6682
|
+
} else if (isHeader) {
|
|
6683
|
+
cfg.headerOpacity = op;
|
|
6684
|
+
applyHeaderPopovers();
|
|
5756
6685
|
} else {
|
|
5757
6686
|
const ops = { ...rOps() };
|
|
5758
6687
|
ops[opKey] = op;
|
|
@@ -5777,6 +6706,10 @@ window.__ModuleLoader__.load({
|
|
|
5777
6706
|
cfg.producedOpacity = op;
|
|
5778
6707
|
applyProduced();
|
|
5779
6708
|
saveConfig();
|
|
6709
|
+
} else if (isHeader) {
|
|
6710
|
+
cfg.headerOpacity = op;
|
|
6711
|
+
applyHeaderPopovers();
|
|
6712
|
+
saveConfig();
|
|
5780
6713
|
} else {
|
|
5781
6714
|
const ops = { ...rOps() };
|
|
5782
6715
|
ops[opKey] = op;
|
|
@@ -5852,6 +6785,11 @@ window.__ModuleLoader__.load({
|
|
|
5852
6785
|
labelKey: "uiProduced",
|
|
5853
6786
|
Icon: TextIcon
|
|
5854
6787
|
},
|
|
6788
|
+
{
|
|
6789
|
+
key: "header",
|
|
6790
|
+
labelKey: "uiHeader",
|
|
6791
|
+
Icon: PanelIcon
|
|
6792
|
+
},
|
|
5855
6793
|
{
|
|
5856
6794
|
key: "panel",
|
|
5857
6795
|
labelKey: "uiPanelRegion",
|
|
@@ -7224,7 +8162,8 @@ window.__ModuleLoader__.load({
|
|
|
7224
8162
|
trajectory: 0,
|
|
7225
8163
|
input: 0,
|
|
7226
8164
|
panel: 0,
|
|
7227
|
-
produced: 0
|
|
8165
|
+
produced: 0,
|
|
8166
|
+
header: 0
|
|
7228
8167
|
};
|
|
7229
8168
|
/** Presets ship with strokes off — text outlines are an opt-in look. Fresh
|
|
7230
8169
|
* objects per call so a profile restore can never alias another preset's
|
|
@@ -7244,7 +8183,8 @@ window.__ModuleLoader__.load({
|
|
|
7244
8183
|
trajectory: off(),
|
|
7245
8184
|
input: off(),
|
|
7246
8185
|
panel: off(),
|
|
7247
|
-
produced: off()
|
|
8186
|
+
produced: off(),
|
|
8187
|
+
header: off()
|
|
7248
8188
|
};
|
|
7249
8189
|
};
|
|
7250
8190
|
const BUILTIN_PRESETS = [
|
|
@@ -7266,7 +8206,8 @@ window.__ModuleLoader__.load({
|
|
|
7266
8206
|
chatTextOpacity: 0,
|
|
7267
8207
|
trajectoryOpacity: 1,
|
|
7268
8208
|
panelOpacity: 1,
|
|
7269
|
-
producedOpacity: 1
|
|
8209
|
+
producedOpacity: 1,
|
|
8210
|
+
headerOpacity: 1
|
|
7270
8211
|
}
|
|
7271
8212
|
},
|
|
7272
8213
|
{
|
|
@@ -7300,7 +8241,8 @@ window.__ModuleLoader__.load({
|
|
|
7300
8241
|
chatTextOpacity: 0,
|
|
7301
8242
|
trajectoryOpacity: .85,
|
|
7302
8243
|
panelOpacity: .85,
|
|
7303
|
-
producedOpacity: 1
|
|
8244
|
+
producedOpacity: 1,
|
|
8245
|
+
headerOpacity: 1
|
|
7304
8246
|
}
|
|
7305
8247
|
},
|
|
7306
8248
|
{
|
|
@@ -7321,7 +8263,8 @@ window.__ModuleLoader__.load({
|
|
|
7321
8263
|
chatTextOpacity: 0,
|
|
7322
8264
|
trajectoryOpacity: 1,
|
|
7323
8265
|
panelOpacity: 1,
|
|
7324
|
-
producedOpacity: 1
|
|
8266
|
+
producedOpacity: 1,
|
|
8267
|
+
headerOpacity: 1
|
|
7325
8268
|
}
|
|
7326
8269
|
},
|
|
7327
8270
|
{
|
|
@@ -7355,7 +8298,8 @@ window.__ModuleLoader__.load({
|
|
|
7355
8298
|
chatTextOpacity: 0,
|
|
7356
8299
|
trajectoryOpacity: .8,
|
|
7357
8300
|
panelOpacity: .8,
|
|
7358
|
-
producedOpacity: 1
|
|
8301
|
+
producedOpacity: 1,
|
|
8302
|
+
headerOpacity: 1
|
|
7359
8303
|
}
|
|
7360
8304
|
},
|
|
7361
8305
|
{
|
|
@@ -7389,7 +8333,8 @@ window.__ModuleLoader__.load({
|
|
|
7389
8333
|
chatTextOpacity: 0,
|
|
7390
8334
|
trajectoryOpacity: .78,
|
|
7391
8335
|
panelOpacity: .75,
|
|
7392
|
-
producedOpacity: 1
|
|
8336
|
+
producedOpacity: 1,
|
|
8337
|
+
headerOpacity: 1
|
|
7393
8338
|
}
|
|
7394
8339
|
},
|
|
7395
8340
|
{
|
|
@@ -7414,7 +8359,8 @@ window.__ModuleLoader__.load({
|
|
|
7414
8359
|
chatTextOpacity: 0,
|
|
7415
8360
|
trajectoryOpacity: 1,
|
|
7416
8361
|
panelOpacity: 1,
|
|
7417
|
-
producedOpacity: 1
|
|
8362
|
+
producedOpacity: 1,
|
|
8363
|
+
headerOpacity: 1
|
|
7418
8364
|
}
|
|
7419
8365
|
}
|
|
7420
8366
|
];
|
|
@@ -8027,6 +8973,9 @@ window.__ModuleLoader__.load({
|
|
|
8027
8973
|
const read = (0, react.useCallback)(() => locale.getSnapshot().active, [locale]);
|
|
8028
8974
|
return (0, react.useSyncExternalStore)(subscribe, read, read);
|
|
8029
8975
|
}
|
|
8976
|
+
/** Rendered inside a sidebar tab: the five pages, `surface: 'sidebar'` shell.
|
|
8977
|
+
* Shared by the better-sidebar page and the native right-Sidebar tab — both
|
|
8978
|
+
* hand a full-height column that owns its own scroll. */
|
|
8030
8979
|
function ThemeTab({ face, useStore, locale }) {
|
|
8031
8980
|
const active = useActiveLocale(locale);
|
|
8032
8981
|
const props = (0, react.useMemo)(() => ({
|
|
@@ -8067,19 +9016,145 @@ window.__ModuleLoader__.load({
|
|
|
8067
9016
|
ctx.inject?.(["betterSidebar"], (scope) => {
|
|
8068
9017
|
const service = scope.betterSidebar;
|
|
8069
9018
|
if (service === void 0) return;
|
|
9019
|
+
const owner = sidebarGuideOwner();
|
|
9020
|
+
if (owner === "host") return;
|
|
9021
|
+
let disposeTab = null;
|
|
9022
|
+
const unsubscribeHost = owner === "pending" ? subscribeHostInfo(() => {
|
|
9023
|
+
if (sidebarGuideOwner() === "host") {
|
|
9024
|
+
disposeTab?.();
|
|
9025
|
+
disposeTab = null;
|
|
9026
|
+
unsubscribeHost();
|
|
9027
|
+
}
|
|
9028
|
+
}) : () => {};
|
|
8070
9029
|
const face = opts.face();
|
|
8071
|
-
scope.effect(() =>
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
9030
|
+
scope.effect(() => {
|
|
9031
|
+
disposeTab = service.registerTab({
|
|
9032
|
+
id: "dsh-any-background:theme",
|
|
9033
|
+
title: () => t("nav"),
|
|
9034
|
+
icon: (size) => (0, react.createElement)(SunIcon, { size }),
|
|
9035
|
+
order: ORDER,
|
|
9036
|
+
single: true,
|
|
9037
|
+
component: () => (0, react.createElement)(ThemeTab, {
|
|
9038
|
+
face,
|
|
9039
|
+
useStore,
|
|
9040
|
+
locale
|
|
9041
|
+
})
|
|
9042
|
+
}) ?? null;
|
|
9043
|
+
return () => {
|
|
9044
|
+
unsubscribeHost();
|
|
9045
|
+
disposeTab?.();
|
|
9046
|
+
disposeTab = null;
|
|
9047
|
+
};
|
|
9048
|
+
});
|
|
9049
|
+
});
|
|
9050
|
+
}
|
|
9051
|
+
//#endregion
|
|
9052
|
+
//#region src/client/sidebar/native-tab.tsx
|
|
9053
|
+
/**
|
|
9054
|
+
* dsh-any-background — native right-Sidebar integration.
|
|
9055
|
+
*
|
|
9056
|
+
* DSH ships an official right Sidebar (`dsh-client-ui-sidebar-right`) with a
|
|
9057
|
+
* public two-stage extension path: a static tab type into
|
|
9058
|
+
* `ctx.sidebarRightTabs` — whose `guide` entry draws a card on the Sidebar's
|
|
9059
|
+
* guide page — and a body under the keyed `sidebar.right.pane.tab` seat. This
|
|
9060
|
+
* module registers the plugin's appearance pages through that path, so the
|
|
9061
|
+
* official Sidebar gains a "主题" card that opens the very same five pages the
|
|
9062
|
+
* settings panel shows (one implementation, one state store; editing in either
|
|
9063
|
+
* place shows up in the other).
|
|
9064
|
+
*
|
|
9065
|
+
* NOTE: that registry is NOT a new-host feature. It is provided on 0.1.5-rc.2
|
|
9066
|
+
* already (verified against the release tag), so nothing here may treat "the
|
|
9067
|
+
* service answered" as "this is a recent DSH" — whether this tab or the
|
|
9068
|
+
* better-sidebar page owns the guide surface is the host adapter's call.
|
|
9069
|
+
*
|
|
9070
|
+
* The dependency is optional at every level, mirroring `sidebar/tab.tsx`:
|
|
9071
|
+
* · the registry is waited on with `ctx.inject`, so a host without the
|
|
9072
|
+
* right Sidebar (or an older DSH) silently registers nothing, and a
|
|
9073
|
+
* Sidebar that initializes after this plugin still gets the card;
|
|
9074
|
+
* · the service shape is restated as a minimal local contract instead of
|
|
9075
|
+
* imported — a value import would be rejected by the host's client-bundle
|
|
9076
|
+
* purity gate, and even a type-only import would make the package a
|
|
9077
|
+
* build-time dependency for everyone who does not run it.
|
|
9078
|
+
*
|
|
9079
|
+
* When dsh-better-sidebar is installed it brings its own "主题" page (registered
|
|
9080
|
+
* by `sidebar/tab.tsx`), which would duplicate this card on the same guide
|
|
9081
|
+
* page. The native card therefore yields: it is not registered while
|
|
9082
|
+
* better-sidebar is detected, and it withdraws itself if better-sidebar mounts
|
|
9083
|
+
* later (the presence verdict is sticky-true, so no re-registration path is
|
|
9084
|
+
* needed). The Interface page's panel slider follows the same split:
|
|
9085
|
+
* "右方侧边栏" natively, "bettersidebar" when that plugin is present.
|
|
9086
|
+
*/
|
|
9087
|
+
/** The tab implementation's identity, and the key the body registers under. */
|
|
9088
|
+
const NATIVE_TAB_ID = "dsh-any-background:theme";
|
|
9089
|
+
/** The page kind: what `openTab` names and the guide entry opens. */
|
|
9090
|
+
const NATIVE_TAB_KIND = "danybg-theme";
|
|
9091
|
+
/**
|
|
9092
|
+
* Register the appearance pages with the native right Sidebar, if one exists.
|
|
9093
|
+
*
|
|
9094
|
+
* Call from the client `apply`. Both registrations sit inside the injected
|
|
9095
|
+
* scope's effects, so unloading the plugin (or HMR) withdraws them — without
|
|
9096
|
+
* that, the next activation would throw on the duplicate id.
|
|
9097
|
+
* @param ctx - client context.
|
|
9098
|
+
* @param opts.face - lazy accessor for the shared business face.
|
|
9099
|
+
* @param opts.store - the plugin's store instance, or null on a host where the
|
|
9100
|
+
* store module never resolved (in that case there is nothing to bind and no
|
|
9101
|
+
* page is registered).
|
|
9102
|
+
*/
|
|
9103
|
+
function registerNativeSidebarTab(ctx, opts) {
|
|
9104
|
+
const { store } = opts;
|
|
9105
|
+
if (store === null || typeof store.getSnapshot !== "function" || typeof store.subscribe !== "function") {
|
|
9106
|
+
console.warn("dsh-any-background: no observable store instance on this host; the native sidebar page stays unregistered");
|
|
9107
|
+
return;
|
|
9108
|
+
}
|
|
9109
|
+
const locale = ctx.locale;
|
|
9110
|
+
const t = locale.bind(NS);
|
|
9111
|
+
const useStore = createStoreHook(store);
|
|
9112
|
+
ctx.inject?.(["sidebarRightTabs"], (scope) => {
|
|
9113
|
+
const tabs = scope.sidebarRightTabs;
|
|
9114
|
+
if (tabs === void 0 || typeof tabs.register !== "function") return;
|
|
9115
|
+
markNativeSidebarTabs();
|
|
9116
|
+
const face = opts.face();
|
|
9117
|
+
const ThemeBody = () => (0, react.createElement)(ThemeTab, {
|
|
9118
|
+
face,
|
|
9119
|
+
useStore,
|
|
9120
|
+
locale
|
|
9121
|
+
});
|
|
9122
|
+
scope.effect(() => {
|
|
9123
|
+
const yieldTo = () => sidebarGuideOwner() === "plugin" && rBetterSidebar();
|
|
9124
|
+
if (yieldTo()) return;
|
|
9125
|
+
let disposeTab = null;
|
|
9126
|
+
const withdraw = () => {
|
|
9127
|
+
disposeTab?.();
|
|
9128
|
+
disposeTab = null;
|
|
9129
|
+
};
|
|
9130
|
+
const unsubscribeBetter = subscribe(() => {
|
|
9131
|
+
if (yieldTo()) withdraw();
|
|
9132
|
+
});
|
|
9133
|
+
const unsubscribeHost = subscribeHostInfo(() => {
|
|
9134
|
+
if (yieldTo()) withdraw();
|
|
9135
|
+
});
|
|
9136
|
+
disposeTab = tabs.register({
|
|
9137
|
+
id: NATIVE_TAB_ID,
|
|
9138
|
+
kind: NATIVE_TAB_KIND,
|
|
9139
|
+
title: () => t("nav"),
|
|
9140
|
+
guide: [{
|
|
9141
|
+
id: "theme",
|
|
9142
|
+
order: 55,
|
|
9143
|
+
title: () => t("nav"),
|
|
9144
|
+
description: () => t("guideDesc"),
|
|
9145
|
+
icon: SunIcon
|
|
9146
|
+
}]
|
|
9147
|
+
});
|
|
9148
|
+
return () => {
|
|
9149
|
+
unsubscribeBetter();
|
|
9150
|
+
unsubscribeHost();
|
|
9151
|
+
withdraw();
|
|
9152
|
+
};
|
|
9153
|
+
}, "dsh-any-background: native sidebar theme tab");
|
|
9154
|
+
scope.effect(() => ctx.slots.inject("sidebar.right.pane.tab", () => ctx.slots.register({
|
|
9155
|
+
name: "sidebar.right.pane.tab",
|
|
9156
|
+
key: NATIVE_TAB_ID
|
|
9157
|
+
}, ThemeBody)), "dsh-any-background: native sidebar theme body");
|
|
8083
9158
|
});
|
|
8084
9159
|
}
|
|
8085
9160
|
//#endregion
|
|
@@ -8181,13 +9256,7 @@ window.__ModuleLoader__.load({
|
|
|
8181
9256
|
customDispose?.();
|
|
8182
9257
|
if (colorTimerRef.current !== null) window.clearTimeout(colorTimerRef.current);
|
|
8183
9258
|
}, "dsh-any-background: skin dispose");
|
|
8184
|
-
|
|
8185
|
-
styleEl.dataset.plugin = "dsh-any-background";
|
|
8186
|
-
styleEl.textContent = `body[data-ds-dark-theme="dsh-any-background"]::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(255,255,255,0.03) 0%,transparent 60%)}${SETTINGS_STYLE_RULE}${POPOVER_BLUR_RULE}${TRAJECTORY_STYLE_RULE}${INPUT_BLUR_RULE}${MOBILE_HEADER_RULE}${PANEL_TOKEN_RULE}${PANEL_BLUR_RULE}${PRODUCED_RULE}${STROKE_RULE}[data-composer-card] textarea::placeholder,[data-composer-card] input::placeholder,[data-composer-card] [contenteditable]::placeholder,[data-cordis-panel] input::placeholder,[data-cordis-panel] textarea::placeholder,.dab-input::placeholder,.dab-input textarea::placeholder,.dab-input input::placeholder{color:var(--dsh-any-placeholder,var(--dsw-alias-label-caption,#8a8f98))!important;font-style:italic;opacity:.85}`;
|
|
8187
|
-
document.head.appendChild(styleEl);
|
|
8188
|
-
ctx.effect(() => () => {
|
|
8189
|
-
styleEl?.parentNode?.removeChild(styleEl);
|
|
8190
|
-
}, "dsh-any-background: gradient");
|
|
9259
|
+
ctx.effect(() => mountStaticStyles(), "dsh-any-background: stylesheet");
|
|
8191
9260
|
const disposeDragQuality = watchWallpaperDragQuality();
|
|
8192
9261
|
ctx.effect(() => () => disposeDragQuality(), "dsh-any-background: drag quality");
|
|
8193
9262
|
let rev = 0;
|
|
@@ -8461,6 +9530,7 @@ window.__ModuleLoader__.load({
|
|
|
8461
9530
|
teardownWp();
|
|
8462
9531
|
}, "dsh-any-background: wp cleanup");
|
|
8463
9532
|
ctx.effect(() => startBetterSidebarWatch(), "dsh-any-background: better-sidebar watch");
|
|
9533
|
+
ctx.effect(() => startHeaderPopoverTagging(), "dsh-any-background: header popover tagging");
|
|
8464
9534
|
ctx.effect(() => ctx.on("theme/change", () => {
|
|
8465
9535
|
if (rHasColor() || rBgDark() !== null || rSchemeOverride() !== "auto") {
|
|
8466
9536
|
const snapshot = ctx.theme.getTheme();
|
|
@@ -8953,6 +10023,10 @@ window.__ModuleLoader__.load({
|
|
|
8953
10023
|
face: buildFace,
|
|
8954
10024
|
store: storeInstance
|
|
8955
10025
|
});
|
|
10026
|
+
registerNativeSidebarTab(ctx, {
|
|
10027
|
+
face: buildFace,
|
|
10028
|
+
store: storeInstance
|
|
10029
|
+
});
|
|
8956
10030
|
const navLabel = () => ctx.locale.bind(NS)("nav");
|
|
8957
10031
|
const applyNavIcon = () => {
|
|
8958
10032
|
const nav = document.querySelector("[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]")?.querySelector("nav");
|