dsh-any-background 0.3.0 → 0.3.2
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 +32 -19
- package/README.zh.md +33 -20
- package/lib/client.js +965 -344
- package/lib/client.js.map +1 -1
- package/lib/index.js +229 -56
- package/package.json +14 -11
package/lib/client.js
CHANGED
|
@@ -49,10 +49,9 @@ window.__ModuleLoader__.load({
|
|
|
49
49
|
uiOpacitySide: "侧边栏",
|
|
50
50
|
uiOpacityCard: "对话框中选项面板",
|
|
51
51
|
uiOpacityInput: "输入框与控件",
|
|
52
|
-
uiSop: "
|
|
52
|
+
uiSop: "设置界面",
|
|
53
53
|
uiChatRegion: "对话文本框",
|
|
54
54
|
uiTrajectory: "轨迹页",
|
|
55
|
-
uiPanelRegion: "bettersidebar",
|
|
56
55
|
uiPanelNative: "右方侧边栏",
|
|
57
56
|
guideDesc: "管理主题色、壁纸与界面透明度",
|
|
58
57
|
uiProduced: "产出物/高亮内容",
|
|
@@ -241,10 +240,9 @@ window.__ModuleLoader__.load({
|
|
|
241
240
|
uiOpacitySide: "Sidebar",
|
|
242
241
|
uiOpacityCard: "Cards & panels",
|
|
243
242
|
uiOpacityInput: "Input & controls",
|
|
244
|
-
uiSop: "Settings interface
|
|
243
|
+
uiSop: "Settings interface",
|
|
245
244
|
uiChatRegion: "Conversation text frame",
|
|
246
245
|
uiTrajectory: "Trajectory view",
|
|
247
|
-
uiPanelRegion: "bettersidebar",
|
|
248
246
|
uiPanelNative: "Right sidebar",
|
|
249
247
|
guideDesc: "Theme color, wallpaper and interface opacity in the right sidebar",
|
|
250
248
|
uiProduced: "Produced / highlights",
|
|
@@ -413,13 +411,13 @@ window.__ModuleLoader__.load({
|
|
|
413
411
|
const DEFAULT_CONFIG = {
|
|
414
412
|
color: null,
|
|
415
413
|
opacities: {
|
|
416
|
-
bg:
|
|
414
|
+
bg: 0,
|
|
417
415
|
sidebar: .5,
|
|
418
416
|
card: .5,
|
|
419
417
|
input: .5
|
|
420
418
|
},
|
|
421
419
|
blurs: {
|
|
422
|
-
bg:
|
|
420
|
+
bg: 0,
|
|
423
421
|
sidebar: 30,
|
|
424
422
|
card: 30,
|
|
425
423
|
settings: 30,
|
|
@@ -484,7 +482,7 @@ window.__ModuleLoader__.load({
|
|
|
484
482
|
},
|
|
485
483
|
settingsOpacity: .5,
|
|
486
484
|
wallpaperOpacity: 1,
|
|
487
|
-
blur:
|
|
485
|
+
blur: 0,
|
|
488
486
|
bgState: {
|
|
489
487
|
zoom: 1,
|
|
490
488
|
x: 0,
|
|
@@ -994,93 +992,556 @@ window.__ModuleLoader__.load({
|
|
|
994
992
|
};
|
|
995
993
|
}
|
|
996
994
|
//#endregion
|
|
997
|
-
//#region src/
|
|
995
|
+
//#region src/host-compat/channel.ts
|
|
996
|
+
/** What this plugin supports, in upgrade order. One row = one build whose facts
|
|
997
|
+
* were checked; several rows may share a `channel` when a newer build was
|
|
998
|
+
* verified as needing no change to that folder's adapter (as `0.1.7-rc.1` was) —
|
|
999
|
+
* a row then reports `exact` instead of `line`, and no new folder appears.
|
|
1000
|
+
* Adding an ADAPTER means adding a channel plus a folder in
|
|
1001
|
+
* `client/host-compat/versions/`. */
|
|
1002
|
+
const SUPPORTED_RELEASES = [
|
|
1003
|
+
{
|
|
1004
|
+
channel: "0.1.5-rc",
|
|
1005
|
+
release: "0.1.5-rc.2"
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
channel: "0.1.6-alpha",
|
|
1009
|
+
release: "0.1.6-alpha.2"
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
channel: "0.1.7-alpha",
|
|
1013
|
+
release: "0.1.7-alpha.2"
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
channel: "0.1.7-alpha",
|
|
1017
|
+
release: "0.1.7-rc.1"
|
|
1018
|
+
}
|
|
1019
|
+
];
|
|
1020
|
+
const UNKNOWN_HOST_INFO = {
|
|
1021
|
+
version: null,
|
|
1022
|
+
channel: "unknown"
|
|
1023
|
+
};
|
|
1024
|
+
/** True when a string looks like a release version. A bare `~/.dsh` directory
|
|
1025
|
+
* basename does not, which is why the disk probe sanity-checks before trusting
|
|
1026
|
+
* a path segment. */
|
|
1027
|
+
const VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
1028
|
+
function looksLikeVersion(value) {
|
|
1029
|
+
return VERSION_RE.test(value);
|
|
1030
|
+
}
|
|
1031
|
+
const PARSE_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
|
|
1032
|
+
function parse(version) {
|
|
1033
|
+
const m = PARSE_RE.exec(version);
|
|
1034
|
+
if (m === null) return null;
|
|
1035
|
+
const parts = m[4] === void 0 ? [] : m[4].split(".");
|
|
1036
|
+
const build = parts.length > 1 ? Number(parts[1]) : NaN;
|
|
1037
|
+
return {
|
|
1038
|
+
core: [
|
|
1039
|
+
Number(m[1]),
|
|
1040
|
+
Number(m[2]),
|
|
1041
|
+
Number(m[3])
|
|
1042
|
+
],
|
|
1043
|
+
channel: parts.length > 0 ? parts[0] : null,
|
|
1044
|
+
build: Number.isFinite(build) ? build : null
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
/** The table, parsed once: every row is a literal this file controls. */
|
|
1048
|
+
const TABLE = SUPPORTED_RELEASES.map((row) => ({
|
|
1049
|
+
...row,
|
|
1050
|
+
parsed: parse(row.release)
|
|
1051
|
+
}));
|
|
1052
|
+
function cmpCore(a, b) {
|
|
1053
|
+
for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
|
|
1054
|
+
return 0;
|
|
1055
|
+
}
|
|
1056
|
+
/** Total order over releases, following the semver rule that a prerelease sorts
|
|
1057
|
+
* BELOW the release it precedes (`0.1.6-alpha.2 < 0.1.6`). */
|
|
1058
|
+
function cmp(a, b) {
|
|
1059
|
+
const core = cmpCore(a.core, b.core);
|
|
1060
|
+
if (core !== 0) return core;
|
|
1061
|
+
if (a.channel === null || b.channel === null) {
|
|
1062
|
+
if (a.channel === b.channel) return 0;
|
|
1063
|
+
return a.channel === null ? 1 : -1;
|
|
1064
|
+
}
|
|
1065
|
+
if (a.channel !== b.channel) return a.channel < b.channel ? -1 : 1;
|
|
1066
|
+
const ab = a.build ?? 0;
|
|
1067
|
+
const bb = b.build ?? 0;
|
|
1068
|
+
return ab === bb ? 0 : ab < bb ? -1 : 1;
|
|
1069
|
+
}
|
|
1070
|
+
/** Pick the adapter for a release, clamping to the nearest verified line when the
|
|
1071
|
+
* release is outside everything this plugin has seen.
|
|
1072
|
+
*
|
|
1073
|
+
* A release is matched by PATCH LINE before anything else: `0.1.6-alpha.4` and a
|
|
1074
|
+
* future `0.1.6` stable both belong to the line verified at `0.1.6-alpha.2`, and
|
|
1075
|
+
* handing them that adapter beats giving up — the DOM probes only cover panel
|
|
1076
|
+
* mechanics, not the surface decisions the line is known for.
|
|
1077
|
+
*
|
|
1078
|
+
* Outside every verified line the answer is a guess, so it is the closest one and
|
|
1079
|
+
* it says so (`nearest`): newer than the newest line → that newest adapter, older
|
|
1080
|
+
* than the oldest → the oldest, strictly between two → the LOWER one, because an
|
|
1081
|
+
* adapter may only claim what it was verified for. A release that will not parse
|
|
1082
|
+
* at all is `unknown`, and the DOM arbitrates there. */
|
|
1083
|
+
function classifyRelease(version) {
|
|
1084
|
+
const host = version === null ? null : parse(version);
|
|
1085
|
+
if (host === null) return {
|
|
1086
|
+
channel: "unknown",
|
|
1087
|
+
match: "unresolved"
|
|
1088
|
+
};
|
|
1089
|
+
const sameLine = TABLE.filter((row) => cmpCore(row.parsed.core, host.core) === 0);
|
|
1090
|
+
if (sameLine.length > 0) {
|
|
1091
|
+
const exact = sameLine.find((row) => row.release === version);
|
|
1092
|
+
if (exact !== void 0) return {
|
|
1093
|
+
channel: exact.channel,
|
|
1094
|
+
match: "exact"
|
|
1095
|
+
};
|
|
1096
|
+
const atOrBelow = sameLine.filter((row) => cmp(row.parsed, host) <= 0);
|
|
1097
|
+
return {
|
|
1098
|
+
channel: (atOrBelow.length > 0 ? atOrBelow[atOrBelow.length - 1] : sameLine[0]).channel,
|
|
1099
|
+
match: "line"
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
const older = TABLE.filter((row) => cmp(row.parsed, host) < 0);
|
|
1103
|
+
if (older.length === 0) return {
|
|
1104
|
+
channel: TABLE[0].channel,
|
|
1105
|
+
match: "nearest"
|
|
1106
|
+
};
|
|
1107
|
+
if (TABLE.filter((row) => cmp(row.parsed, host) > 0).length === 0) return {
|
|
1108
|
+
channel: TABLE[TABLE.length - 1].channel,
|
|
1109
|
+
match: "nearest"
|
|
1110
|
+
};
|
|
1111
|
+
return {
|
|
1112
|
+
channel: older[older.length - 1].channel,
|
|
1113
|
+
match: "nearest"
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
/** The channel a release maps onto — the verdict's answer without the confidence. */
|
|
1117
|
+
function channelOf(version) {
|
|
1118
|
+
return classifyRelease(version).channel;
|
|
1119
|
+
}
|
|
1120
|
+
/** Prerelease tag of a release string (`alpha.2` in `0.1.6-alpha.2`), or null
|
|
1121
|
+
* when the version is bare/undetermined. Needed because a channel is coarser
|
|
1122
|
+
* than reality: `conversation.session.header.leading` ships in exactly one
|
|
1123
|
+
* build within the 0.1.6-alpha line. */
|
|
1124
|
+
function prereleaseOf(version) {
|
|
1125
|
+
if (version === null) return null;
|
|
1126
|
+
return /^\d+\.\d+\.\d+-([0-9A-Za-z.-]+)$/.exec(version)?.[1] ?? null;
|
|
1127
|
+
}
|
|
1128
|
+
//#endregion
|
|
1129
|
+
//#region src/client/host-compat/versions/shared.ts
|
|
998
1130
|
/**
|
|
999
|
-
*
|
|
1131
|
+
* Selector + declaration vocabulary shared by every per-version adapter.
|
|
1000
1132
|
*
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1005
|
-
* is also not sufficient, because the obvious probes do not separate the
|
|
1006
|
-
* generations — `ctx.sidebarRightTabs` and the native `[data-sidebar-right-panel]`
|
|
1007
|
-
* marker both exist on 0.1.5-rc.2 already, so using either as a "0.1.6+" test
|
|
1008
|
-
* mis-classifies 0.1.5 as newer.
|
|
1133
|
+
* These are the ANCHORS, not the decisions. What varies between releases (which
|
|
1134
|
+
* element slides, whether the panel needs promoting, which header slot keys
|
|
1135
|
+
* exist) is answered in the version folders; what is stable across all of them
|
|
1136
|
+
* lives here so no adapter has to re-type a host selector and get it subtly wrong.
|
|
1009
1137
|
*
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1138
|
+
* Verified against the harness release tags (dsh-v0.1.5-rc.2 … dsh-v0.1.7-rc.1),
|
|
1139
|
+
* not inferred:
|
|
1140
|
+
* · `[data-sidebar-right-panel]` — emitted by `ui-sidebar-right/shell/SidebarRight.tsx`
|
|
1141
|
+
* on every release in range, with values `push` | `fullscreen`.
|
|
1142
|
+
* · `[data-dockkit-host]` / `[data-dockkit-empty]` — emitted ONLY by
|
|
1143
|
+
* `ui-dockkit/components/TabLayout.tsx`, a file that first appears at
|
|
1144
|
+
* 0.1.7-alpha.1. Dockkit itself EXISTS earlier (its `TabMenu` carries
|
|
1145
|
+
* `[data-dockkit-tab-menu]` on every release), so only these two attributes
|
|
1146
|
+
* are the 0.1.7 marker — never test "is dockkit installed".
|
|
1147
|
+
* · `[data-dsh-bottom-panel]` — the optional dsh-better-sidebar workbench, not
|
|
1148
|
+
* a host element at all. It is a single element on every release, which is
|
|
1149
|
+
* why it needs no version arm.
|
|
1150
|
+
* · `[data-plugin-panel]` / `[data-plugin-scope] > ul` — emitted by
|
|
1151
|
+
* `ui-plugin-manager/src/client/PluginManagerPage.tsx`. That package is NEW AT
|
|
1152
|
+
* 0.1.6-alpha.2: `git ls-tree` shows it absent from 0.1.5-rc.2, 0.1.5-rc.3 and
|
|
1153
|
+
* 0.1.6-alpha.1, where the plugin list lived in `ui-settings-plugin-inventory`
|
|
1154
|
+
* under `[data-plugin-scope]` alone — no `[data-plugin-panel]`, and no card
|
|
1155
|
+
* `ul` in that group either. From alpha.2 on, the group markup (`section` →
|
|
1156
|
+
* `div.groupHead` + `ul.cards`) is byte-identical through 0.1.7-rc.1, so the
|
|
1157
|
+
* ONE rule below serves all four tags that emit the page.
|
|
1015
1158
|
*
|
|
1016
1159
|
* @module
|
|
1017
1160
|
*/
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1161
|
+
/** The host's own right Sidebar wrapper. */
|
|
1162
|
+
const PANEL_WRAPPER = "[data-sidebar-right-panel]";
|
|
1163
|
+
/** The same wrapper in its fullscreen state; the host raises its own z-index to 40. */
|
|
1164
|
+
const PANEL_FULLSCREEN = "[data-sidebar-right-panel=\"fullscreen\"]";
|
|
1165
|
+
/** The docked children a 0.1.7+ panel slides — the panel itself stays put. */
|
|
1166
|
+
const DOCKKIT_SLIDERS = "[data-sidebar-right-panel] [data-dockkit-host=\"dock\"],[data-sidebar-right-panel] [data-dockkit-empty]";
|
|
1167
|
+
/** The third-party workbench panel, one element on every release. Owned by the
|
|
1168
|
+
* PRODUCED slider (0.3.2): a conversation artifact that happens to open in a
|
|
1169
|
+
* panel, not a page column — so it fades and frosts with the produced surfaces. */
|
|
1170
|
+
const BETTER_SIDEBAR_PANEL = "[data-dsh-bottom-panel]";
|
|
1171
|
+
/** Every surface the panel opacity slider owns — the host's own right Sidebar.
|
|
1172
|
+
* Layout-neutral token re-scope. (The dsh-better-sidebar workbench used to be
|
|
1173
|
+
* listed here; it moved to the produced group, see `PRODUCED_RULE`.) */
|
|
1174
|
+
const PANEL_SURFACES = PANEL_WRAPPER;
|
|
1175
|
+
/** DOM-shape test for "this is NOT a 0.1.7-style frame". Used ONLY by the
|
|
1176
|
+
* unresolved adapter, where the DOM is the authority because the release is not. */
|
|
1177
|
+
const WITHOUT_DOCKKIT_FRAME = ":not(:has([data-dockkit-host],[data-dockkit-empty]))";
|
|
1178
|
+
/** The panel blur declaration pair. Both prefixes because Safari needs the
|
|
1179
|
+
* `-webkit-` one and the cascade must not let them disagree. */
|
|
1180
|
+
function panelBlurRule(selectors) {
|
|
1181
|
+
return `${selectors}{-webkit-backdrop-filter:var(--dsh-any-blur-panel,none);backdrop-filter:var(--dsh-any-blur-panel,none)}`;
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* The plugin-manager page's frosted card block, for the releases that ship it.
|
|
1185
|
+
*
|
|
1186
|
+
* `设置 → 插件` lists each group's plugins in a `ul` that has NO surface of its
|
|
1187
|
+
* own — measured live on 0.1.7-alpha.1: a transparent flex column, 2px gaps, each
|
|
1188
|
+
* `li` transparent with a 12px radius. With the settings surfaces faded, the whole
|
|
1189
|
+
* table floats straight on the wallpaper. This frames each group's list the way the
|
|
1190
|
+
* composer capsule is framed: a rounded block on an `::before` underlay.
|
|
1191
|
+
*
|
|
1192
|
+
* Which releases emit the page decides who gets this string — see `pluginPageRule`
|
|
1193
|
+
* in each version folder.
|
|
1194
|
+
*
|
|
1195
|
+
* BINDING, and why it is not just "put it in the dialog": the block paints from
|
|
1196
|
+
* `--dsh-any-bg-settings-surface` and frosts from `--dsh-any-blur-settings`, i.e.
|
|
1197
|
+
* the settings-opacity / settings-blur pair. On 0.1.7 the page is a child of
|
|
1198
|
+
* `centerCol`, NOT of the settings dialog, so `SETTINGS_STYLE_RULE`'s token
|
|
1199
|
+
* re-scope never reaches it and the list would otherwise follow the homepage card
|
|
1200
|
+
* alpha; reading the plugin-owned variables off `:root` puts them under the slider
|
|
1201
|
+
* that owns this page. The `var(--dsw-alias-bg-layer-2)` fallback covers the window
|
|
1202
|
+
* before the first `applySettingsOverrides` write.
|
|
1203
|
+
*
|
|
1204
|
+
* The frost rides an underlay because backdrop-filter must never sit directly on a
|
|
1205
|
+
* host part (containing block + backdrop root), and the `isolation` is what keeps
|
|
1206
|
+
* the underlay's `z-index:-1` inside the block instead of escaping behind the frame.
|
|
1207
|
+
* The `:has([role="dialog"])` valve matters here more than anywhere else: every row
|
|
1208
|
+
* carries a `plugins.item` slot that third-party plugins fill with their own
|
|
1209
|
+
* controls, so a dialog can legitimately open inside the list.
|
|
1210
|
+
*/
|
|
1211
|
+
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)}";
|
|
1212
|
+
/** Session-header slot anchors present on EVERY release in range. The slot
|
|
1213
|
+
* renderer stamps `data-slot="<key>"` unconditionally (`ui-renderer/scoped-slots.tsx`),
|
|
1214
|
+
* and all five keys are registered from 0.1.5-rc.2 through 0.1.7-rc.1 —
|
|
1215
|
+
* verified per tag. Adapters append the keys their own release adds. */
|
|
1216
|
+
const BASE_HEADER_SLOT_KEYS = [
|
|
1217
|
+
"conversation.session.header",
|
|
1218
|
+
"conversation.session.header.actions",
|
|
1219
|
+
"conversation.session.header.utilities",
|
|
1220
|
+
"conversation.session.header.corner",
|
|
1221
|
+
"conversation.session.header.lineage"
|
|
1222
|
+
];
|
|
1223
|
+
//#endregion
|
|
1224
|
+
//#region src/client/host-compat/versions/v0-1-5-rc-2-3/adapter.ts
|
|
1225
|
+
/**
|
|
1226
|
+
* Adapter for the DSH 0.1.5-rc channel (`0.1.5-rc.2`, `0.1.5-rc.3`).
|
|
1227
|
+
*
|
|
1228
|
+
* Nothing here is inferred from a neighbouring release — each fact below was
|
|
1229
|
+
* checked against the harness tags `dsh-v0.1.5-rc.2` / `dsh-v0.1.5-rc.3`.
|
|
1230
|
+
*
|
|
1231
|
+
* Host shape this line presents:
|
|
1232
|
+
* `ui-sidebar-right/shell/SidebarRight.module.css`
|
|
1233
|
+
* .panel { position:absolute; z-index:10; transform:translateX(100%); visibility:hidden }
|
|
1234
|
+
* .panel[data-sidebar-right-open] { transform:none }
|
|
1235
|
+
* .panel[data-sidebar-right-panel='fullscreen'] { position:fixed; inset:0; z-index:40 }
|
|
1236
|
+
* `ui-dockkit` ships WITHOUT `TabLayout.tsx`, so `[data-dockkit-host]` and
|
|
1237
|
+
* `[data-dockkit-empty]` are never rendered on this line.
|
|
1238
|
+
* `ui-plugin-manager` (the page behind `PLUGIN_PAGE_FROST_RULE`) is not a package
|
|
1239
|
+
* on this line at all, and neither tag emits `[data-plugin-panel]`.
|
|
1240
|
+
*
|
|
1241
|
+
* Consequence for the panel slider: the WRAPPER is what slides, so the
|
|
1242
|
+
* `backdrop-filter` belongs on the wrapper — and because the wrapper sits inside
|
|
1243
|
+
* an animated track with its own stacking context, it must be promoted out of it
|
|
1244
|
+
* or the filter has no wallpaper to sample.
|
|
1245
|
+
*
|
|
1246
|
+
* @module
|
|
1247
|
+
*/
|
|
1248
|
+
/** The wrapper carries the slide transform on this line, so it carries the blur. */
|
|
1249
|
+
function panelFragments$3() {
|
|
1250
|
+
return {
|
|
1251
|
+
promotion: `${PANEL_WRAPPER}{position:fixed!important;z-index:26!important}${PANEL_FULLSCREEN}{z-index:40!important}`,
|
|
1252
|
+
blur: panelBlurRule(PANEL_WRAPPER)
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
function createAdapter$3(host) {
|
|
1256
|
+
return {
|
|
1257
|
+
id: "0.1.5-rc.2/rc.3",
|
|
1258
|
+
channel: "0.1.5-rc",
|
|
1259
|
+
host,
|
|
1260
|
+
panelFragments: panelFragments$3,
|
|
1261
|
+
pluginPageRule: "",
|
|
1262
|
+
surface: {
|
|
1263
|
+
ownsSidebarGuideSurface: false,
|
|
1264
|
+
headerSlotKeys: BASE_HEADER_SLOT_KEYS
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
//#endregion
|
|
1269
|
+
//#region src/client/host-compat/versions/v0-1-6-alpha-1-2/adapter.ts
|
|
1270
|
+
/**
|
|
1271
|
+
* Adapter for the DSH 0.1.6-alpha channel (`0.1.6-alpha.1`, `0.1.6-alpha.2`).
|
|
1272
|
+
*
|
|
1273
|
+
* Facts checked per tag against `dsh-v0.1.6-alpha.1` / `dsh-v0.1.6-alpha.2` —
|
|
1274
|
+
* deliberately not copied over from the 0.1.5 folder, even where the two lines
|
|
1275
|
+
* turned out to agree. If a future 0.1.6 patch diverges, only this file moves.
|
|
1276
|
+
*
|
|
1277
|
+
* Host shape this line presents:
|
|
1278
|
+
* `SidebarRight.module.css` is unchanged from the 0.1.5 line for the parts that
|
|
1279
|
+
* matter: `.panel` still carries `position:absolute` + `transform:translateX(100%)`,
|
|
1280
|
+
* `data-sidebar-right-open` still clears it with `transform:none`, and the panel
|
|
1281
|
+
* width is still an inline `width` with no `--dsh-sidebar-width` variable.
|
|
1282
|
+
* alpha.2 appends fullscreen window chrome (`.63` windows / `.77` darwin), which
|
|
1283
|
+
* does not move the blur target, AND the `ui-plugin-manager` package — so the
|
|
1284
|
+
* plugin-page frost below is prerelease-gated rather than channel-wide.
|
|
1285
|
+
* `ui-dockkit` still has no `TabLayout.tsx`, so no `[data-dockkit-host]` /
|
|
1286
|
+
* `[data-dockkit-empty]` on this line either.
|
|
1287
|
+
*
|
|
1288
|
+
* So the panel mechanics match 0.1.5: the wrapper slides, the wrapper takes the
|
|
1289
|
+
* blur, and the wrapper needs promoting.
|
|
1290
|
+
*
|
|
1291
|
+
* @module
|
|
1292
|
+
*/
|
|
1293
|
+
/** The extra session-header anchor this line introduces: the macOS titlebar work
|
|
1294
|
+
* added `conversation.session.header.leading`. It was deleted again at
|
|
1295
|
+
* 0.1.7-alpha.1 with no shim, so it is unique to this line — which is why the
|
|
1296
|
+
* channel alone is not enough to answer it and the prerelease is read.
|
|
1297
|
+
* `alpha.1` predates it.
|
|
1298
|
+
*
|
|
1299
|
+
* Erring toward "included": a slot key the host never emits simply matches
|
|
1300
|
+
* nothing, whereas a key we omit hides a header area from the plugin for the
|
|
1301
|
+
* whole session. */
|
|
1302
|
+
function hasLeadingHeaderSlot(version) {
|
|
1303
|
+
const pre = prereleaseOf(version);
|
|
1304
|
+
if (pre === null) return true;
|
|
1305
|
+
const m = /^alpha\.(\d+)$/.exec(pre);
|
|
1306
|
+
return m === null || Number(m[1]) >= 2;
|
|
1307
|
+
}
|
|
1308
|
+
/** The other thing this line splits on: `ui-plugin-manager` — the package that
|
|
1309
|
+
* renders `设置 → 插件` and stamps `[data-plugin-panel]` — was CREATED at
|
|
1310
|
+
* alpha.2. Until then the plugin list was a settings tab in
|
|
1311
|
+
* `ui-settings-plugin-inventory`, which carries `[data-plugin-scope]` but no
|
|
1312
|
+
* card list to frame, so alpha.1 has nothing for the frost rule to apply to.
|
|
1313
|
+
*
|
|
1314
|
+
* Same erring-toward-included convention as `hasLeadingHeaderSlot`: the rule's
|
|
1315
|
+
* own `[data-plugin-panel]` prefix means an over-broad arm cannot paint, while
|
|
1316
|
+
* an over-narrow one leaves a page bare for the whole session. */
|
|
1317
|
+
function hasPluginManagerPage(version) {
|
|
1318
|
+
const pre = prereleaseOf(version);
|
|
1319
|
+
if (pre === null) return true;
|
|
1320
|
+
const m = /^alpha\.(\d+)$/.exec(pre);
|
|
1321
|
+
return m === null || Number(m[1]) >= 2;
|
|
1322
|
+
}
|
|
1323
|
+
function panelFragments$2() {
|
|
1324
|
+
return {
|
|
1325
|
+
promotion: `${PANEL_WRAPPER}{position:fixed!important;z-index:26!important}${PANEL_FULLSCREEN}{z-index:40!important}`,
|
|
1326
|
+
blur: panelBlurRule(PANEL_WRAPPER)
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function createAdapter$2(host) {
|
|
1330
|
+
return {
|
|
1331
|
+
id: "0.1.6-alpha.1/alpha.2",
|
|
1332
|
+
channel: "0.1.6-alpha",
|
|
1333
|
+
host,
|
|
1334
|
+
panelFragments: panelFragments$2,
|
|
1335
|
+
pluginPageRule: hasPluginManagerPage(host.version) ? PLUGIN_PAGE_FROST_RULE : "",
|
|
1336
|
+
surface: {
|
|
1337
|
+
ownsSidebarGuideSurface: true,
|
|
1338
|
+
headerSlotKeys: hasLeadingHeaderSlot(host.version) ? [...BASE_HEADER_SLOT_KEYS, "conversation.session.header.leading"] : BASE_HEADER_SLOT_KEYS
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
//#endregion
|
|
1343
|
+
//#region src/client/host-compat/versions/v0-1-7-alpha-1-2-rc-1/adapter.ts
|
|
1344
|
+
/**
|
|
1345
|
+
* Adapter for the DSH 0.1.7-alpha channel (`0.1.7-alpha.1`, `0.1.7-alpha.2`),
|
|
1346
|
+
* re-verified unchanged at `0.1.7-rc.1`.
|
|
1347
|
+
*
|
|
1348
|
+
* This is the line the panel mechanics actually changed on — verified against
|
|
1349
|
+
* `dsh-v0.1.6-alpha.2` → `dsh-v0.1.7-alpha.1`, where `SidebarRight.module.css`
|
|
1350
|
+
* was rewritten (116 lines changed) and `ui-dockkit/src/components/TabLayout.tsx`
|
|
1351
|
+
* was ADDED. Both facts were checked, not assumed.
|
|
1352
|
+
*
|
|
1353
|
+
* `0.1.7-rc.1` was checked the same way, by diffing every package that emits an
|
|
1354
|
+
* anchor this plugin matches (the panel CSS, dockkit, the plugin-manager page, the
|
|
1355
|
+
* slot renderer, the theme tokens): no anchor line moved, and the two UI changes it
|
|
1356
|
+
* carries do not reach us —
|
|
1357
|
+
* · `ui-plugin-manager`: a new `InstallDialog` failure screen plus compatibility
|
|
1358
|
+
* wording. The card block (`section` → `div.groupHead` + `ul.cards`) is
|
|
1359
|
+
* untouched, and the dialog is a body-level `Modal`, so the `:has()` valve of
|
|
1360
|
+
* `PLUGIN_PAGE_FROST_RULE` never even has to fire for it.
|
|
1361
|
+
* · `ui-conversation`: `.titleRow` gains `container-type:inline-size`, which makes
|
|
1362
|
+
* it a stacking context and a containing block. Header slots sit inside that
|
|
1363
|
+
* row, but this plugin reaches them through background/blur tokens, and the
|
|
1364
|
+
* dropdowns they trigger portal to `document.body` (see `header-tag.ts`), so
|
|
1365
|
+
* nothing of ours lands inside the new containment.
|
|
1366
|
+
*
|
|
1367
|
+
* Host shape this line presents:
|
|
1368
|
+
* .panel { position:absolute; --dsh-dockkit-dock-layer:10; pointer-events:none }
|
|
1369
|
+
* — no transform, no visibility, and no z-index of its own any more.
|
|
1370
|
+
* .panel :global([data-dockkit-host='dock']),
|
|
1371
|
+
* .panel :global([data-dockkit-empty]),
|
|
1372
|
+
* .panel :global([data-dockkit-divider]) { transform:translateX(var(--dsh-sidebar-width)); visibility:hidden }
|
|
1373
|
+
* .panel[data-sidebar-right-open] :global(…) { transform:none }
|
|
1374
|
+
* — the DOCKED CHILDREN are what slide.
|
|
1375
|
+
* .panel[data-sidebar-right-panel='fullscreen'] { --dsh-dockkit-dock-layer:40 }
|
|
1376
|
+
* — fullscreen no longer flips the panel to `position:fixed`; it raises a
|
|
1377
|
+
* layer variable, and the width becomes `100vw` on the absolute panel.
|
|
1378
|
+
*
|
|
1379
|
+
* Consequences encoded below:
|
|
1380
|
+
* 1. The blur belongs on the children. On the wrapper it frosted a stationary
|
|
1381
|
+
* frame — "the blur stays put while the sidebar moves".
|
|
1382
|
+
* 2. The promotion must NOT be emitted. `position:fixed` detaches the panel from
|
|
1383
|
+
* the track the host animates, which is the whole reason 2 the blur stopped
|
|
1384
|
+
* travelling. The host's own sheet documents the content root as owning "no
|
|
1385
|
+
* stacking context or transform", so `backdrop-filter` resolves against the
|
|
1386
|
+
* page and samples the wallpaper unaided.
|
|
1387
|
+
* 3. `[data-dockkit-empty]` must be included alongside `[data-dockkit-host]`: a
|
|
1388
|
+
* pane with no tabs renders the empty host instead, and keying on `host`
|
|
1389
|
+
* alone mis-detects that (transient) shape.
|
|
1390
|
+
*
|
|
1391
|
+
* Also true of this line, recorded so the next reader does not re-derive it:
|
|
1392
|
+
* · `conversation.session.header.leading` was DELETED here with no shim, so this
|
|
1393
|
+
* folder deliberately does not list it (see the 0.1.6 folder).
|
|
1394
|
+
* · `--dsw-specific-menu` was severed from `--dsw-alias-bg-layer-3` and became a
|
|
1395
|
+
* literal translucent rgba, plus a `html[data-platform='darwin']` re-override.
|
|
1396
|
+
* The plugin already samples and writes that token directly, so the severance
|
|
1397
|
+
* needs no adapter of its own — but note the consequence: `toRgba()` REPLACES
|
|
1398
|
+
* alpha, so the header slider now overrides the host's own 0.58 rather than
|
|
1399
|
+
* fading an opaque layer. Intended (the slider means "this opacity"), worth
|
|
1400
|
+
* knowing if the 0.1.7 header ever looks too thin.
|
|
1401
|
+
* · `[data-chat-flow]` is stamped on a nested `ChatGroupSeat` content element
|
|
1402
|
+
* too. Checked against every use in this plugin: the stroke group is inherited
|
|
1403
|
+
* so a second match changes nothing, and the view-card discovery takes the
|
|
1404
|
+
* first match, which is still the real flow root. No arm needed.
|
|
1405
|
+
*
|
|
1406
|
+
* @module
|
|
1407
|
+
*/
|
|
1408
|
+
function panelFragments$1() {
|
|
1409
|
+
return {
|
|
1410
|
+
promotion: "",
|
|
1411
|
+
blur: panelBlurRule(DOCKKIT_SLIDERS)
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
function createAdapter$1(host) {
|
|
1415
|
+
return {
|
|
1416
|
+
id: "0.1.7-alpha.1/alpha.2/rc.1",
|
|
1417
|
+
channel: "0.1.7-alpha",
|
|
1418
|
+
host,
|
|
1419
|
+
panelFragments: panelFragments$1,
|
|
1420
|
+
pluginPageRule: PLUGIN_PAGE_FROST_RULE,
|
|
1421
|
+
surface: {
|
|
1422
|
+
ownsSidebarGuideSurface: true,
|
|
1423
|
+
headerSlotKeys: BASE_HEADER_SLOT_KEYS
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
//#endregion
|
|
1428
|
+
//#region src/client/host-compat/versions/unknown/adapter.ts
|
|
1429
|
+
/**
|
|
1430
|
+
* Stand-in for "we could not resolve the release".
|
|
1431
|
+
*
|
|
1432
|
+
* This is a REAL case, not a theoretical one: a plain `~/.dsh` install has no
|
|
1433
|
+
* launcher layout, so the front-door probe finds neither a manifest nor a
|
|
1434
|
+
* version-shaped directory name. It must not degrade into a guessed channel —
|
|
1435
|
+
* a wrong guess silently restyles the window.
|
|
1436
|
+
*
|
|
1437
|
+
* Instead, the DOM arbitrates. Both panel arms ship, each gated on the shape that
|
|
1438
|
+
* only one release family can present, which is exactly how this plugin behaved
|
|
1439
|
+
* before release detection existed. It is why an unresolved host still gets a
|
|
1440
|
+
* working panel blur.
|
|
1441
|
+
*
|
|
1442
|
+
* The one concession this costs: an engine without `:has()` cannot parse a gated
|
|
1443
|
+
* selector and drops that block, so such a host loses the promotion. A resolved
|
|
1444
|
+
* host does not have that problem — its arm is unconditional.
|
|
1445
|
+
*
|
|
1446
|
+
* @module
|
|
1447
|
+
*/
|
|
1448
|
+
function panelFragments() {
|
|
1449
|
+
const wrapper = `${PANEL_WRAPPER}${WITHOUT_DOCKKIT_FRAME}`;
|
|
1450
|
+
return {
|
|
1451
|
+
promotion: `${wrapper}{position:fixed!important;z-index:26!important}${PANEL_FULLSCREEN}${WITHOUT_DOCKKIT_FRAME}{z-index:40!important}`,
|
|
1452
|
+
blur: panelBlurRule(DOCKKIT_SLIDERS) + panelBlurRule(wrapper)
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function createAdapter(host) {
|
|
1456
|
+
return {
|
|
1457
|
+
id: "unknown",
|
|
1458
|
+
channel: "unknown",
|
|
1459
|
+
host,
|
|
1460
|
+
panelFragments,
|
|
1461
|
+
pluginPageRule: PLUGIN_PAGE_FROST_RULE,
|
|
1462
|
+
surface: {
|
|
1463
|
+
ownsSidebarGuideSurface: false,
|
|
1464
|
+
headerSlotKeys: BASE_HEADER_SLOT_KEYS
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
//#endregion
|
|
1469
|
+
//#region src/client/host-compat/versions/registry.ts
|
|
1470
|
+
const ADAPTERS = {
|
|
1471
|
+
"0.1.5-rc": createAdapter$3,
|
|
1472
|
+
"0.1.6-alpha": createAdapter$2,
|
|
1473
|
+
"0.1.7-alpha": createAdapter$1,
|
|
1474
|
+
unknown: createAdapter
|
|
1021
1475
|
};
|
|
1022
|
-
/**
|
|
1023
|
-
*
|
|
1476
|
+
/** Pick the adapter for a resolved host. `host.channel` is already narrowed to
|
|
1477
|
+
* the union by `adoptHostInfo`, so every channel here has a row by construction —
|
|
1478
|
+
* a new channel in `HostChannel` fails to compile until it is added. */
|
|
1479
|
+
function buildAdapter(host) {
|
|
1480
|
+
return ADAPTERS[host.channel](host);
|
|
1481
|
+
}
|
|
1482
|
+
//#endregion
|
|
1483
|
+
//#region src/client/host-compat/release.ts
|
|
1484
|
+
/**
|
|
1485
|
+
* The client-side half of the front door: where the release verdict lands.
|
|
1486
|
+
*
|
|
1487
|
+
* The browser cannot detect the host release — the client context exposes no
|
|
1488
|
+
* version field, and the app manifest that states it is only readable on the node
|
|
1489
|
+
* half. So `src/host-compat/detect.ts` resolves it there and the verdict travels
|
|
1490
|
+
* in the `read` RPC payload (see `client/rpc.ts`, which calls `adoptHostInfo`).
|
|
1491
|
+
*
|
|
1492
|
+
* This module only HOLDS the verdict and tells subscribers when it changes. It
|
|
1493
|
+
* deliberately answers no feature questions — that is `capabilities.ts`, so the
|
|
1494
|
+
* version → behaviour mapping stays in one place.
|
|
1495
|
+
*
|
|
1496
|
+
* @module
|
|
1497
|
+
*/
|
|
1498
|
+
let current = UNKNOWN_HOST_INFO;
|
|
1499
|
+
/** False until the server verdict lands. Feature gates must not read "not asked
|
|
1500
|
+
* yet" as "an old host" — see `adapterResolved()`. */
|
|
1024
1501
|
let resolved = false;
|
|
1025
1502
|
const subs$1 = /* @__PURE__ */ new Set();
|
|
1026
|
-
/**
|
|
1503
|
+
/** Cached adapter, keyed by the verdict it was built from. Held because React's
|
|
1504
|
+
* `useSyncExternalStore` requires a stable snapshot between notifications. */
|
|
1505
|
+
let adapterCache = null;
|
|
1506
|
+
/** Adopt the verdict delivered by the server's `read` payload.
|
|
1507
|
+
*
|
|
1508
|
+
* The payload's `channel` is IGNORED and re-derived from `version` here: the
|
|
1509
|
+
* two halves are built together, so a disagreeing pair can only mean one of them
|
|
1510
|
+
* is stale, and re-deriving keeps `channelOf` the single definition of what a
|
|
1511
|
+
* channel means. */
|
|
1027
1512
|
function adoptHostInfo(raw) {
|
|
1028
1513
|
if (raw === null || typeof raw !== "object") return;
|
|
1029
1514
|
const h = raw;
|
|
1030
|
-
const version = typeof h.version === "string" && h.version.trim()
|
|
1031
|
-
const
|
|
1032
|
-
const changed = !resolved || current.version !== version || current.generation !== generation;
|
|
1033
|
-
current = {
|
|
1515
|
+
const version = typeof h.version === "string" && looksLikeVersion(h.version.trim()) ? h.version.trim() : null;
|
|
1516
|
+
const next = {
|
|
1034
1517
|
version,
|
|
1035
|
-
|
|
1518
|
+
channel: channelOf(version)
|
|
1036
1519
|
};
|
|
1520
|
+
const changed = !resolved || current.version !== next.version || current.channel !== next.channel;
|
|
1521
|
+
current = next;
|
|
1037
1522
|
resolved = true;
|
|
1038
|
-
if (changed)
|
|
1523
|
+
if (changed) {
|
|
1524
|
+
adapterCache = null;
|
|
1525
|
+
subs$1.forEach((cb) => cb());
|
|
1526
|
+
}
|
|
1039
1527
|
}
|
|
1040
|
-
/** Subscribe to
|
|
1528
|
+
/** Subscribe to the verdict landing (or changing). */
|
|
1041
1529
|
function subscribeHostInfo(cb) {
|
|
1042
1530
|
subs$1.add(cb);
|
|
1043
1531
|
return () => {
|
|
1044
1532
|
subs$1.delete(cb);
|
|
1045
1533
|
};
|
|
1046
1534
|
}
|
|
1047
|
-
/**
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
"0.1.5": 5,
|
|
1051
|
-
"0.1.6": 6,
|
|
1052
|
-
"0.1.7": 7
|
|
1053
|
-
};
|
|
1054
|
-
/**
|
|
1055
|
-
* Whether the host is at least `gen`. An unknown generation answers `false`:
|
|
1056
|
-
* callers treat that as "do not assume the newer behaviour" and use whatever
|
|
1057
|
-
* capability probe they own, which is the safe direction for every gate here.
|
|
1058
|
-
*/
|
|
1059
|
-
function hostAtLeast(gen) {
|
|
1060
|
-
const rank = RANK[current.generation];
|
|
1061
|
-
return rank !== void 0 && rank >= RANK[gen];
|
|
1535
|
+
/** Whether the server verdict has landed at all. */
|
|
1536
|
+
function adapterResolved() {
|
|
1537
|
+
return resolved;
|
|
1062
1538
|
}
|
|
1063
|
-
/**
|
|
1064
|
-
*
|
|
1065
|
-
*
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
* `false` — 0.1.5 or an undetermined-but-answered generation: keep it.
|
|
1070
|
-
* `null` — the verdict has not landed yet (the probe is a Node round-trip).
|
|
1071
|
-
*
|
|
1072
|
-
* Callers must treat `null` as "not yet known": register nothing eagerly on a
|
|
1073
|
-
* `true`, and arm a withdrawal for a later `true` rather than blocking on it.
|
|
1074
|
-
*
|
|
1075
|
-
* NOTE: 0.1.5-rc.2 ALSO ships the native right Sidebar (verified: the
|
|
1076
|
-
* `ui-sidebar-right` entry is in that release's `dsh-web-app/cordis.patch.yml`),
|
|
1077
|
-
* so this gate deliberately keys off the release rather than off the presence of
|
|
1078
|
-
* the native sidebar service — the two are not the same fact.
|
|
1079
|
-
*/
|
|
1080
|
-
function suppressesBetterSidebar() {
|
|
1081
|
-
if (!resolved) return null;
|
|
1082
|
-
if (current.generation === "unknown") return false;
|
|
1083
|
-
return hostAtLeast("0.1.6");
|
|
1539
|
+
/** The adapter chosen for the current verdict — `unknown`'s stand-in until the
|
|
1540
|
+
* verdict lands, which is the safe answer: it asserts nothing and lets the DOM
|
|
1541
|
+
* arbitrate. */
|
|
1542
|
+
function rHostAdapter() {
|
|
1543
|
+
adapterCache ??= buildAdapter(current);
|
|
1544
|
+
return adapterCache;
|
|
1084
1545
|
}
|
|
1085
1546
|
//#endregion
|
|
1086
1547
|
//#region src/client/rpc.ts
|
|
@@ -1406,6 +1867,149 @@ window.__ModuleLoader__.load({
|
|
|
1406
1867
|
}
|
|
1407
1868
|
}
|
|
1408
1869
|
//#endregion
|
|
1870
|
+
//#region src/client/host-compat/capabilities.ts
|
|
1871
|
+
/**
|
|
1872
|
+
* The plugin's ONE vocabulary for host-dependent decisions.
|
|
1873
|
+
*
|
|
1874
|
+
* Everything downstream asks a QUESTION ("who owns the guide surface?", "which CSS
|
|
1875
|
+
* arm applies?") and gets an answer from the current adapter. No consumer names a
|
|
1876
|
+
* release. That is what keeps the per-version folders honest: when 0.1.8 needs a
|
|
1877
|
+
* new behaviour, a consumer changes by reading a new adapter field, not by growing
|
|
1878
|
+
* another version comparison.
|
|
1879
|
+
*
|
|
1880
|
+
* @module
|
|
1881
|
+
*/
|
|
1882
|
+
function sidebarGuideOwner() {
|
|
1883
|
+
if (!adapterResolved()) return "pending";
|
|
1884
|
+
return rHostAdapter().surface.ownsSidebarGuideSurface ? "host" : "plugin";
|
|
1885
|
+
}
|
|
1886
|
+
/** The current adapter, for callers that need more than one field from it (and to
|
|
1887
|
+
* keep the snapshot identity stable across a single render pass). */
|
|
1888
|
+
function hostAdapter() {
|
|
1889
|
+
return rHostAdapter();
|
|
1890
|
+
}
|
|
1891
|
+
//#endregion
|
|
1892
|
+
//#region src/client/header-tag.ts
|
|
1893
|
+
/**
|
|
1894
|
+
* Header-surface tagging.
|
|
1895
|
+
*
|
|
1896
|
+
* WHY A RUNTIME TAG IS NEEDED
|
|
1897
|
+
* The session-header dropdowns (open-in-app, session-log, Agent Team, the job
|
|
1898
|
+
* list, the subagent lineage tree) render their popover through the Menu/tree
|
|
1899
|
+
* primitives, which PORTAL the list to `document.body`. A portaled list is not a
|
|
1900
|
+
* DOM descendant of the header — no ancestor selector can reach it — and the
|
|
1901
|
+
* primitive emits no `id`/`aria-controls` back-link to its anchor either. Pure
|
|
1902
|
+
* CSS class matching was the remaining option, but 0.1.7 broke the shapes this
|
|
1903
|
+
* plugin relied on: open-in-app moved from in-place to `portal` (so the old
|
|
1904
|
+
* `:not(_portal)` discriminator no longer selected it), and the session row menu
|
|
1905
|
+
* became a dynamic `sidebar.workspaces.session.menu.item` slot whose rows are no
|
|
1906
|
+
* longer guaranteed to include a destructive one.
|
|
1907
|
+
*
|
|
1908
|
+
* THE SIGNAL
|
|
1909
|
+
* The slot renderer stamps every registered slot with `data-slot="<key>"`
|
|
1910
|
+
* (ui-renderer `scoped-slots.tsx`). Those anchors live in the header subtree, so
|
|
1911
|
+
* the plugin can detect when a header trigger is expanded — which keys identify
|
|
1912
|
+
* the header is a per-release fact and comes from the host adapter, not from a
|
|
1913
|
+
* hardcoded list here.
|
|
1914
|
+
*
|
|
1915
|
+
* WHY NOT GEOMETRY
|
|
1916
|
+
* A tempting link is the inline position the primitive writes on a portaled list
|
|
1917
|
+
* (`style="left: …; top: …"`). It was tried and REJECTED: an `align="end"` list
|
|
1918
|
+
* is flush to the trigger's right edge while the trigger itself sits at the far
|
|
1919
|
+
* right of the header, so the two boxes often do not overlap horizontally (in
|
|
1920
|
+
* the reported DOM the list is at `left:1012` with the trigger near x=1400). Any
|
|
1921
|
+
* purely geometric proximity test therefore misses real header menus.
|
|
1922
|
+
*
|
|
1923
|
+
* THE ACTUAL RULE (structural, no geometry, no timing)
|
|
1924
|
+
* A header dropdown can only be open while its trigger reports
|
|
1925
|
+
* `aria-expanded="true"`, and opening one is what mounts the popover. So a
|
|
1926
|
+
* popover tag is maintained as: "there is at least one expanded header trigger,
|
|
1927
|
+
* AND this popover is not owned by another group". Popovers are only ever
|
|
1928
|
+
* tagged/released on a change of that combined state, which makes the tag
|
|
1929
|
+
* self-consistent and idempotent.
|
|
1930
|
+
*
|
|
1931
|
+
* The one hazard is a popover that is open for an unrelated reason (a composer
|
|
1932
|
+
* menu, a hover card) *while* a header trigger stays expanded. Such a popover
|
|
1933
|
+
* is not a candidate: it is excluded by `ownsForeignSurface`, which skips any
|
|
1934
|
+
* popover carrying markers of a different slider group. In practice header
|
|
1935
|
+
* dropdowns are transient (opening one closes the others), so this is a guard
|
|
1936
|
+
* rather than the common path.
|
|
1937
|
+
*
|
|
1938
|
+
* @module
|
|
1939
|
+
*/
|
|
1940
|
+
/** Marker attribute the stylesheet keys on. */
|
|
1941
|
+
const HEADER_POPOVER_ATTR = "data-dsh-any-header-popover";
|
|
1942
|
+
/** Portaled popovers a header trigger can own. */
|
|
1943
|
+
const POPOVER_SELECTOR = "[role=\"menu\"], [role=\"tree\"], [role=\"dialog\"]";
|
|
1944
|
+
/** Which session-header slot anchors to watch — supplied by the host adapter,
|
|
1945
|
+
* because the set is not the same on every release: 0.1.6-alpha.2 registers a
|
|
1946
|
+
* sixth (`.leading`) that neither the line before nor the line after has.
|
|
1947
|
+
* Memoised on the array identity, which the adapter keeps stable until the
|
|
1948
|
+
* release verdict lands. */
|
|
1949
|
+
let slotAnchorCache = null;
|
|
1950
|
+
function headerSlotAnchors() {
|
|
1951
|
+
const keys = hostAdapter().surface.headerSlotKeys;
|
|
1952
|
+
if (slotAnchorCache === null || slotAnchorCache.keys !== keys) slotAnchorCache = {
|
|
1953
|
+
keys,
|
|
1954
|
+
selector: keys.map((key) => `[data-slot="${key}"]`).join(",")
|
|
1955
|
+
};
|
|
1956
|
+
return slotAnchorCache.selector;
|
|
1957
|
+
}
|
|
1958
|
+
/** Whether any header slot anchor has an expanded trigger. */
|
|
1959
|
+
function headerTriggerOpen() {
|
|
1960
|
+
for (const anchor of document.querySelectorAll(headerSlotAnchors())) if (anchor.querySelector("[aria-expanded=\"true\"]") !== null) return true;
|
|
1961
|
+
return false;
|
|
1962
|
+
}
|
|
1963
|
+
/** Surfaces that belong to a different slider group and must never be claimed.
|
|
1964
|
+
* `_denseList` without `_portal` is the in-place card-side dense menu, an
|
|
1965
|
+
* `_ioCard`/`_menu`-classed body-level list belongs elsewhere, and a modal is
|
|
1966
|
+
* its own surface. */
|
|
1967
|
+
function ownsForeignSurface(el) {
|
|
1968
|
+
if (el.getAttribute("aria-modal") === "true") return true;
|
|
1969
|
+
return el.parentElement !== document.body;
|
|
1970
|
+
}
|
|
1971
|
+
let observer = null;
|
|
1972
|
+
let raf = null;
|
|
1973
|
+
/** Recompute tags from the current open state. */
|
|
1974
|
+
function refreshHeaderPopovers() {
|
|
1975
|
+
const open = headerTriggerOpen();
|
|
1976
|
+
const candidates = [...document.querySelectorAll(POPOVER_SELECTOR)].filter((el) => !ownsForeignSurface(el));
|
|
1977
|
+
if (!open) {
|
|
1978
|
+
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
for (const el of candidates) el.setAttribute(HEADER_POPOVER_ATTR, "");
|
|
1982
|
+
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) if (!candidates.includes(el)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
1983
|
+
}
|
|
1984
|
+
function schedule() {
|
|
1985
|
+
if (raf !== null) return;
|
|
1986
|
+
raf = window.requestAnimationFrame(() => {
|
|
1987
|
+
raf = null;
|
|
1988
|
+
refreshHeaderPopovers();
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
/** Start tagging. Returns a teardown that stops observing and clears markings. */
|
|
1992
|
+
function startHeaderPopoverTagging() {
|
|
1993
|
+
if (typeof MutationObserver === "undefined" || typeof document === "undefined") return () => {};
|
|
1994
|
+
refreshHeaderPopovers();
|
|
1995
|
+
observer = new MutationObserver(schedule);
|
|
1996
|
+
observer.observe(document.body, {
|
|
1997
|
+
childList: true,
|
|
1998
|
+
subtree: true,
|
|
1999
|
+
attributes: true,
|
|
2000
|
+
attributeFilter: ["aria-expanded"]
|
|
2001
|
+
});
|
|
2002
|
+
return () => {
|
|
2003
|
+
observer?.disconnect();
|
|
2004
|
+
observer = null;
|
|
2005
|
+
if (raf !== null) {
|
|
2006
|
+
window.cancelAnimationFrame(raf);
|
|
2007
|
+
raf = null;
|
|
2008
|
+
}
|
|
2009
|
+
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
//#endregion
|
|
1409
2013
|
//#region src/client/utils/image.ts
|
|
1410
2014
|
/**
|
|
1411
2015
|
* Read a chosen image file as a data URL WITHOUT re-encoding: the original
|
|
@@ -2827,13 +3431,16 @@ window.__ModuleLoader__.load({
|
|
|
2827
3431
|
applyExemptDefaults();
|
|
2828
3432
|
} catch {}
|
|
2829
3433
|
}
|
|
2830
|
-
const SETTINGS_PANEL_SEL = "[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]";
|
|
3434
|
+
const SETTINGS_PANEL_SEL = "[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby][class*=\"_panel\"]";
|
|
2831
3435
|
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)}`;
|
|
2832
|
-
const
|
|
2833
|
-
const INPUT_BLUR_RULE =
|
|
3436
|
+
const INPUT_FROST_CLASS = "dab-input-frost";
|
|
3437
|
+
const INPUT_BLUR_RULE = `.${INPUT_FROST_CLASS} [data-composer-card]{position:relative;isolation:isolate}.${INPUT_FROST_CLASS} [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}`;
|
|
2834
3438
|
function applyInputBlur(px) {
|
|
2835
|
-
|
|
2836
|
-
|
|
3439
|
+
const root = document.documentElement;
|
|
3440
|
+
if (px > 0) root.classList.add(INPUT_FROST_CLASS);
|
|
3441
|
+
else root.classList.remove(INPUT_FROST_CLASS);
|
|
3442
|
+
if (px > 0) root.style.setProperty("--dsh-any-input-blur", `blur(${px}px)`);
|
|
3443
|
+
else root.style.removeProperty("--dsh-any-input-blur");
|
|
2837
3444
|
}
|
|
2838
3445
|
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)}";
|
|
2839
3446
|
/** Mirror of the bg-part blur on :root. --dsh-any-part-blur is element-scoped
|
|
@@ -2843,16 +3450,32 @@ window.__ModuleLoader__.load({
|
|
|
2843
3450
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-part-blur-global", `blur(${px}px)`);
|
|
2844
3451
|
else document.documentElement.style.removeProperty("--dsh-any-part-blur-global");
|
|
2845
3452
|
}
|
|
2846
|
-
const PANEL_TOKEN_RULE =
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
*
|
|
2854
|
-
|
|
2855
|
-
const
|
|
3453
|
+
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)}`;
|
|
3454
|
+
/** The header dropdown surfaces, spelled ONCE. Three rules consume this set (the
|
|
3455
|
+
* static blur rule, the dynamic token rule and the stroke group), and every one
|
|
3456
|
+
* of them drifting by a single attribute re-introduces the bug where a header
|
|
3457
|
+
* menu keeps the card slider's look instead of its own.
|
|
3458
|
+
*
|
|
3459
|
+
* Strategy 1 is the runtime tag (`header-tag.ts`); strategies 2 are class-shape
|
|
3460
|
+
* fallbacks for the first paint before the tag observer runs. */
|
|
3461
|
+
const HEADER_TAG_SELECTOR = `[${HEADER_POPOVER_ATTR}]`;
|
|
3462
|
+
const HEADER_SURFACES = [
|
|
3463
|
+
HEADER_TAG_SELECTOR,
|
|
3464
|
+
"ul[class*=\"_menu\"],div[role=\"dialog\"][class*=\"_panel\"]:not([aria-modal=\"true\"])",
|
|
3465
|
+
`[role="menu"][class*="_denseList"]:not([class*="_portal"])`,
|
|
3466
|
+
"[role=\"tree\"][class*=\"_menu\"]"
|
|
3467
|
+
];
|
|
3468
|
+
/** SPECIFICITY IS LOAD-BEARING HERE. The card group's rule is
|
|
3469
|
+
* `[role="menu"]:not([data-dockkit-tab-menu])` — specificity (0,2,0) — so a bare
|
|
3470
|
+
* `[data-dsh-any-header-popover]` (0,1,0) LOSES to it no matter how late it
|
|
3471
|
+
* appears in the sheet. That is exactly how the tagged open-in-app menu kept
|
|
3472
|
+
* taking the card blur. `[role]` is a no-op presence test that raises only the
|
|
3473
|
+
* tagged arm to (0,2,0); the other three already qualify at (0,2,0)+. */
|
|
3474
|
+
const HEADER_SURFACES_PADDED = [`${HEADER_TAG_SELECTOR}[role]`, ...HEADER_SURFACES.slice(1)];
|
|
3475
|
+
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)}`;
|
|
3476
|
+
/** Selector the dynamic header token rule targets — the same set as the static
|
|
3477
|
+
* HEADER_POPOVER_RULE above, so the two can never drift apart. */
|
|
3478
|
+
const HEADER_TOKEN_SELECTOR = HEADER_SURFACES_PADDED.join(",");
|
|
2856
3479
|
/** Header-popover surfaces: retint the menu token and (re)write the blur.
|
|
2857
3480
|
* Mirrors applyProduced: the static rule above never changes, so a drag only
|
|
2858
3481
|
* rewrites the blur variable and this one dynamic rule. */
|
|
@@ -2924,16 +3547,16 @@ window.__ModuleLoader__.load({
|
|
|
2924
3547
|
document.head.appendChild(exemptStyleEl);
|
|
2925
3548
|
return exemptStyleEl;
|
|
2926
3549
|
}
|
|
2927
|
-
const PRODUCED_RULE =
|
|
3550
|
+
const PRODUCED_RULE = `[data-code-block-content] pre,[data-code-block-banner],[data-composer-chip],[data-changed-files],[data-terminal],[data-read],[data-diff],[data-context-injection-body],[data-search],[data-web],[class*="_ioCard"],[data-inspect],[data-turn-trigger],[style*="--dsh-hover-preview-fade"],:not(pre)>code{-webkit-backdrop-filter:var(--dsh-any-blur-prod,none);backdrop-filter:var(--dsh-any-blur-prod,none)}[data-markdown-variant="compact"] :not(pre)>code{-webkit-backdrop-filter:none;backdrop-filter:none}${BETTER_SIDEBAR_PANEL}{-webkit-backdrop-filter:var(--dsh-any-blur-prod,none);backdrop-filter:var(--dsh-any-blur-prod,none)}${BETTER_SIDEBAR_PANEL}{--dsw-alias-bg-base:var(--dsh-any-prod-panel-bg-base);--dsw-alias-bg-layer-1:var(--dsh-any-prod-panel-layer-1);--dsw-alias-bg-layer-2:var(--dsh-any-prod-panel-layer-2);--dsw-alias-bg-layer-3:var(--dsh-any-prod-panel-layer-3)}@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-banner]{background-color:color-mix(in srgb,var(--dsl-code-block-banner-background-color,var(--dsl-code-block-background,var(--dsw-alias-markdown-code-block,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],[data-diff]{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:not([data-code-block-banner]),[data-diff]>div:first-child:not([data-code-block-banner]){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-inspect],[data-turn-trigger]{background-color:color-mix(in srgb,var(--dsw-alias-markdown-code-block,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-turn-trigger]:hover{background-color:color-mix(in srgb,var(--dsw-alias-interactive-bg-hover,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-changed-files]{background-color:var(--dsh-any-prod-layer-1,var(--dsw-alias-bg-layer-1,transparent))!important}[data-changed-files]>button:first-child{background-color:color-mix(in srgb,var(--changes-fill,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[data-changed-files]>button:first-child:hover{background-color:color-mix(in srgb,var(--changes-hover,var(--changes-fill,transparent)) var(--dsh-any-prod-pct,100%),transparent)!important}[data-changed-files]>button:first-child>span:first-child{background-color:color-mix(in srgb,var(--changes-fill,transparent) var(--dsh-any-prod-pct,100%),transparent)!important}[style*="--dsh-hover-preview-fade"]{background-color:color-mix(in srgb,var(--dsw-hovercard-bg,#2c2c2e) var(--dsh-any-prod-pct,100%),transparent)!important}[style*="--dsh-hover-preview-fade"][class*="_preview"]{background-color:var(--dsh-any-prod-layer-1,var(--dsw-alias-bg-layer-1,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}}`;
|
|
2928
3551
|
const STROKE_RULE = [
|
|
2929
3552
|
`${SETTINGS_PANEL_SEL}{-webkit-text-stroke:var(--dsh-any-stroke-settings-w,0px) var(--dsh-any-stroke-settings-c,transparent);paint-order:stroke fill}`,
|
|
2930
3553
|
"[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}",
|
|
2931
3554
|
"[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}",
|
|
2932
3555
|
`[data-chat-flow]{-webkit-text-stroke:var(--dsh-any-stroke-chat-w,0px) var(--dsh-any-stroke-chat-c,transparent);paint-order:stroke fill}`,
|
|
2933
3556
|
`[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}`,
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
3557
|
+
`[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-diff],[data-inspect],[data-turn-trigger],[data-context-injection-body],[data-search],[data-web],[class*="_ioCard"],[style*="--dsh-hover-preview-fade"],${BETTER_SIDEBAR_PANEL}{-webkit-text-stroke:var(--dsh-any-stroke-produced-w,0px) var(--dsh-any-stroke-produced-c,transparent);paint-order:stroke fill}`,
|
|
3558
|
+
`${PANEL_SURFACES}{-webkit-text-stroke:var(--dsh-any-stroke-panel-w,0px) var(--dsh-any-stroke-panel-c,transparent);paint-order:stroke fill}`,
|
|
3559
|
+
`${HEADER_SURFACES.join(",")}{-webkit-text-stroke:var(--dsh-any-stroke-header-w,0px) var(--dsh-any-stroke-header-c,transparent);paint-order:stroke fill}`,
|
|
2937
3560
|
"[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}",
|
|
2938
3561
|
`svg{-webkit-text-stroke-width:0!important;paint-order:normal!important}`,
|
|
2939
3562
|
`::placeholder{-webkit-text-stroke-width:0!important}`
|
|
@@ -3071,12 +3694,7 @@ window.__ModuleLoader__.load({
|
|
|
3071
3694
|
fontStyleEl?.remove();
|
|
3072
3695
|
fontStyleEl = null;
|
|
3073
3696
|
}
|
|
3074
|
-
/** The host's own
|
|
3075
|
-
* plugin has no palette (no picked color, no wallpaper verdict, no forced
|
|
3076
|
-
* scheme). Without these the panel's re-scope would resolve to invalid vars
|
|
3077
|
-
* and the panel would have no background — reading the host's resolved value
|
|
3078
|
-
* keeps the panel opaque by default and lets the slider retint it like every
|
|
3079
|
-
* other homepage part. Reads from the live `:root` so a custom host skin or
|
|
3697
|
+
/** The host's own defaults, read from the live `:root` so a custom host skin or
|
|
3080
3698
|
* theme override wins. */
|
|
3081
3699
|
function readHostLayerTokens() {
|
|
3082
3700
|
if (typeof getComputedStyle === "undefined") return null;
|
|
@@ -3094,32 +3712,63 @@ window.__ModuleLoader__.load({
|
|
|
3094
3712
|
layer3
|
|
3095
3713
|
};
|
|
3096
3714
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3715
|
+
/** The host's own right Sidebar (`PANEL_TOKEN_RULE`) reads this set. */
|
|
3716
|
+
const PANEL_LAYER_VARS = {
|
|
3717
|
+
base: "--dsh-any-panel-bg-base",
|
|
3718
|
+
layer1: "--dsh-any-panel-layer-1",
|
|
3719
|
+
layer2: "--dsh-any-panel-layer-2",
|
|
3720
|
+
layer3: "--dsh-any-panel-layer-3"
|
|
3721
|
+
};
|
|
3722
|
+
/** The dsh-better-sidebar workbench (see `PRODUCED_RULE`) reads this set, so the
|
|
3723
|
+
* two panels can follow two different sliders. */
|
|
3724
|
+
const PROD_PANEL_LAYER_VARS = {
|
|
3725
|
+
base: "--dsh-any-prod-panel-bg-base",
|
|
3726
|
+
layer1: "--dsh-any-prod-panel-layer-1",
|
|
3727
|
+
layer2: "--dsh-any-prod-panel-layer-2",
|
|
3728
|
+
layer3: "--dsh-any-prod-panel-layer-3"
|
|
3729
|
+
};
|
|
3730
|
+
/** The changed-files card root needs one token only — the RAW `--dsw-alias-bg-layer-1`
|
|
3731
|
+
* at the produced alpha. It cannot mix on the token itself the way the other
|
|
3732
|
+
* produced surfaces do, because that name is what the interface card slider
|
|
3733
|
+
* re-emits globally on `body` (OPACITY_TOKEN_GROUPS), so mixing on it would bind
|
|
3734
|
+
* one artifact card to two sliders. */
|
|
3735
|
+
const PROD_LAYER_VARS = { layer1: "--dsh-any-prod-layer-1" };
|
|
3736
|
+
const LAYER_KEYS = [
|
|
3737
|
+
"base",
|
|
3738
|
+
"layer1",
|
|
3739
|
+
"layer2",
|
|
3740
|
+
"layer3"
|
|
3741
|
+
];
|
|
3742
|
+
/** Re-emit the four layer colors at `op` alpha into `names`. The source is the
|
|
3743
|
+
* plugin palette when a picked color / forced scheme owns the theme, otherwise
|
|
3744
|
+
* the host's own resolved tokens — a slider only ever supplies the alpha, so the
|
|
3745
|
+
* colors keep following whichever theme or host skin is active.
|
|
3746
|
+
*
|
|
3747
|
+
* Every pass writes SOMETHING: the re-scope rules live in the always-emitted
|
|
3748
|
+
* stylesheet, and a `var()` naming an unwritten variable makes the surface
|
|
3749
|
+
* guaranteed-invalid, which drops its background entirely rather than leaving it
|
|
3750
|
+
* on the host default. */
|
|
3751
|
+
function writeLayerVars(names, op) {
|
|
3752
|
+
const palette = paletteTokens();
|
|
3753
|
+
const src = palette !== null ? {
|
|
3754
|
+
base: palette["--dsw-alias-bg-base"],
|
|
3755
|
+
layer1: palette["--dsw-alias-bg-layer-1"],
|
|
3756
|
+
layer2: palette["--dsw-alias-bg-layer-2"],
|
|
3757
|
+
layer3: palette["--dsw-alias-bg-layer-3"]
|
|
3758
|
+
} : readHostLayerTokens() ?? {};
|
|
3099
3759
|
const root = document.documentElement;
|
|
3100
|
-
if (
|
|
3101
|
-
const
|
|
3102
|
-
const layer1 = tokens["--dsw-alias-bg-layer-1"];
|
|
3103
|
-
const layer2 = tokens["--dsw-alias-bg-layer-2"];
|
|
3104
|
-
const layer3 = tokens["--dsw-alias-bg-layer-3"];
|
|
3105
|
-
if (base !== void 0) root.style.setProperty("--dsh-any-panel-bg-base", toRgba(base, op));
|
|
3106
|
-
if (layer1 !== void 0) root.style.setProperty("--dsh-any-panel-layer-1", toRgba(layer1, op));
|
|
3107
|
-
if (layer2 !== void 0) root.style.setProperty("--dsh-any-panel-layer-2", toRgba(layer2, op));
|
|
3108
|
-
if (layer3 !== void 0) root.style.setProperty("--dsh-any-panel-layer-3", toRgba(layer3, op));
|
|
3760
|
+
if (LAYER_KEYS.every((k) => !src[k])) {
|
|
3761
|
+
for (const k of LAYER_KEYS) if (names[k]) root.style.setProperty(names[k], "transparent");
|
|
3109
3762
|
return;
|
|
3110
3763
|
}
|
|
3111
|
-
const
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
root.style.setProperty(
|
|
3115
|
-
root.style.setProperty("--dsh-any-panel-layer-2", "transparent");
|
|
3116
|
-
root.style.setProperty("--dsh-any-panel-layer-3", "transparent");
|
|
3117
|
-
return;
|
|
3764
|
+
for (const k of LAYER_KEYS) {
|
|
3765
|
+
const name = names[k];
|
|
3766
|
+
const v = src[k];
|
|
3767
|
+
if (name && v) root.style.setProperty(name, toRgba(v, op));
|
|
3118
3768
|
}
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
if (host.layer3) root.style.setProperty("--dsh-any-panel-layer-3", toRgba(host.layer3, op));
|
|
3769
|
+
}
|
|
3770
|
+
function applyPanelOverrides(op) {
|
|
3771
|
+
writeLayerVars(PANEL_LAYER_VARS, op);
|
|
3123
3772
|
}
|
|
3124
3773
|
function applyPanelBlur(px) {
|
|
3125
3774
|
if (px > 0) document.documentElement.style.setProperty("--dsh-any-blur-panel", `blur(${px}px)`);
|
|
@@ -3151,7 +3800,6 @@ window.__ModuleLoader__.load({
|
|
|
3151
3800
|
function applySettingsOverrides(op) {
|
|
3152
3801
|
applyLayerOverrides("--dsh-any-bg-settings", op, true);
|
|
3153
3802
|
}
|
|
3154
|
-
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)}";
|
|
3155
3803
|
function applyTrajectoryOverrides(op) {
|
|
3156
3804
|
applyLayerOverrides("--dsh-any-traj", op, false);
|
|
3157
3805
|
}
|
|
@@ -3257,7 +3905,21 @@ window.__ModuleLoader__.load({
|
|
|
3257
3905
|
let rightEl = null;
|
|
3258
3906
|
const PART_BLUR_CLASS = "dab-part-blur";
|
|
3259
3907
|
const PART_UNDERLAY_CLASS = "dab-part-underlay";
|
|
3260
|
-
|
|
3908
|
+
/** The AppFrame's main-bg clear-out, as a class rather than an inline write.
|
|
3909
|
+
*
|
|
3910
|
+
* WHY NOT INLINE (measured on 0.1.7): the frame's own translucent
|
|
3911
|
+
* `--dsw-alias-bg-base` is what hides the wallpaper, so `applyPartOpacities` has
|
|
3912
|
+
* to clear it. Written as `frameEl.style.background = 'transparent'` the clear
|
|
3913
|
+
* survived a settings change but not a sidebar open/close — the host owns that
|
|
3914
|
+
* element's `style` too (it animates `grid-template-columns`), and while it
|
|
3915
|
+
* re-asserts its own background the frame snaps back to an opaque
|
|
3916
|
+
* `rgb(200,207,218)`: wallpaper gone, and every frost on top of it flattened,
|
|
3917
|
+
* because a backdrop-filter with nothing behind it has nothing to blur. A class
|
|
3918
|
+
* rule with `!important` sits outside the host's `style` writes entirely, so the
|
|
3919
|
+
* clear cannot be re-clobbered and there is nothing to re-apply. */
|
|
3920
|
+
const PART_FRAME_CLASS = "dab-frame-clear";
|
|
3921
|
+
const FRAME_CLEAR_RULE = `.${PART_FRAME_CLASS}{background:transparent!important}`;
|
|
3922
|
+
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)}`;
|
|
3261
3923
|
let partBlurStyleEl = null;
|
|
3262
3924
|
function ensurePartBlurStyle() {
|
|
3263
3925
|
if (partBlurStyleEl?.isConnected) return;
|
|
@@ -3338,7 +4000,8 @@ window.__ModuleLoader__.load({
|
|
|
3338
4000
|
discoverParts();
|
|
3339
4001
|
if (frameEl === null) return;
|
|
3340
4002
|
const base = tokens["--dsw-alias-bg-base"];
|
|
3341
|
-
frameEl.
|
|
4003
|
+
frameEl.classList.add(PART_FRAME_CLASS);
|
|
4004
|
+
if (frameEl.style.background !== "") frameEl.style.removeProperty("background");
|
|
3342
4005
|
if (centerEl !== null) centerEl.style.background = base !== void 0 ? toRgba(base, ops.bg) : "transparent";
|
|
3343
4006
|
if (rightEl !== null) rightEl.style.background = "";
|
|
3344
4007
|
}
|
|
@@ -3367,11 +4030,15 @@ window.__ModuleLoader__.load({
|
|
|
3367
4030
|
applyViewCards();
|
|
3368
4031
|
}
|
|
3369
4032
|
/** Produced/artifact surfaces (conversation code blocks + their banner +
|
|
3370
|
-
*
|
|
3371
|
-
*
|
|
3372
|
-
*
|
|
3373
|
-
*
|
|
3374
|
-
*
|
|
4033
|
+
* inline code chips + composer chips + the dsh-better-sidebar workbench):
|
|
4034
|
+
* re-write the root blur and the alpha percentage consumed by PRODUCED_RULE. The
|
|
4035
|
+
* percentage is an alpha for the surface's OWN host color — 1 (100%) reproduces
|
|
4036
|
+
* the untouched host look and lowering it fades exactly that color out — so no
|
|
4037
|
+
* palette sampling is involved and the surfaces keep following the active
|
|
4038
|
+
* theme/wallpaper color on their own. Two surfaces are the exception: the
|
|
4039
|
+
* workbench reads a whole set of layer tokens rather than one color, and the
|
|
4040
|
+
* changed-files card reads a token the card slider owns globally — both take their
|
|
4041
|
+
* alpha from `writeLayerVars` instead of mixing on the live token. */
|
|
3375
4042
|
function applyProduced() {
|
|
3376
4043
|
const px = rBlurs().produced;
|
|
3377
4044
|
const root = document.documentElement;
|
|
@@ -3381,6 +4048,8 @@ window.__ModuleLoader__.load({
|
|
|
3381
4048
|
if (opacity < 0) opacity = 0;
|
|
3382
4049
|
if (opacity > 1) opacity = 1;
|
|
3383
4050
|
root.style.setProperty("--dsh-any-prod-pct", `${Math.round(opacity * 100)}%`);
|
|
4051
|
+
writeLayerVars(PROD_PANEL_LAYER_VARS, opacity);
|
|
4052
|
+
writeLayerVars(PROD_LAYER_VARS, opacity);
|
|
3384
4053
|
}
|
|
3385
4054
|
/** Live per-part blur update during slider drag (no full re-apply). */
|
|
3386
4055
|
function setPartBlur(part, v) {
|
|
@@ -3517,6 +4186,7 @@ window.__ModuleLoader__.load({
|
|
|
3517
4186
|
if (cached !== null && centerEl.contains(cached)) return cached;
|
|
3518
4187
|
viewTargets[idx] = null;
|
|
3519
4188
|
if (spec.fallback !== true) return null;
|
|
4189
|
+
if (centerEl.querySelector("[data-chat-flow],[data-conversation-scroll],[data-composer-seat],[data-conversation-composer-overlay]") === null) return null;
|
|
3520
4190
|
if (centerEl.querySelector("[data-conversation-scroll]") !== null) return null;
|
|
3521
4191
|
if (spec.opacity() <= 0 && spec.blur() <= 0) return null;
|
|
3522
4192
|
let best = null;
|
|
@@ -3535,6 +4205,7 @@ window.__ModuleLoader__.load({
|
|
|
3535
4205
|
if (el.clientHeight < centerEl.clientHeight * .5) continue;
|
|
3536
4206
|
if (el.clientHeight > (best?.clientHeight ?? 0)) best = el;
|
|
3537
4207
|
}
|
|
4208
|
+
if (best !== null && best.closest("[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby][class*=\"_panel\"]") !== null) best = null;
|
|
3538
4209
|
const refined = best !== null ? refineMessageColumn(best) : null;
|
|
3539
4210
|
viewTargets[idx] = refined;
|
|
3540
4211
|
return refined;
|
|
@@ -4041,10 +4712,15 @@ window.__ModuleLoader__.load({
|
|
|
4041
4712
|
document.documentElement.style.removeProperty("--dsh-any-blur-card-panels");
|
|
4042
4713
|
document.documentElement.style.removeProperty("--dsh-any-input-blur");
|
|
4043
4714
|
document.documentElement.style.removeProperty("--dsh-any-part-blur-global");
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4715
|
+
for (const set of [
|
|
4716
|
+
PANEL_LAYER_VARS,
|
|
4717
|
+
PROD_PANEL_LAYER_VARS,
|
|
4718
|
+
PROD_LAYER_VARS
|
|
4719
|
+
]) for (const k of LAYER_KEYS) {
|
|
4720
|
+
const name = set[k];
|
|
4721
|
+
if (name) document.documentElement.style.removeProperty(name);
|
|
4722
|
+
}
|
|
4723
|
+
document.documentElement.classList.remove(INPUT_FROST_CLASS);
|
|
4048
4724
|
document.documentElement.style.removeProperty("--dsh-any-blur-panel");
|
|
4049
4725
|
document.documentElement.style.removeProperty("--dsh-any-blur-prod");
|
|
4050
4726
|
document.documentElement.style.removeProperty("--dsh-any-prod-pct");
|
|
@@ -4075,7 +4751,7 @@ window.__ModuleLoader__.load({
|
|
|
4075
4751
|
setBlur(sidebarEl, 0);
|
|
4076
4752
|
setBlur(centerEl, 0);
|
|
4077
4753
|
setBlur(rightEl, 0);
|
|
4078
|
-
if (frameEl !== null) frameEl.
|
|
4754
|
+
if (frameEl !== null) frameEl.classList.remove(PART_FRAME_CLASS);
|
|
4079
4755
|
if (centerEl !== null) centerEl.style.removeProperty("background");
|
|
4080
4756
|
if (rightEl !== null) rightEl.style.removeProperty("background");
|
|
4081
4757
|
stopWatchingParts();
|
|
@@ -4089,6 +4765,70 @@ window.__ModuleLoader__.load({
|
|
|
4089
4765
|
if (wpEl) wpEl.style.filter = v > 0 ? `blur(${v}px)` : "none";
|
|
4090
4766
|
}
|
|
4091
4767
|
//#endregion
|
|
4768
|
+
//#region src/client/host-compat/styles.ts
|
|
4769
|
+
/**
|
|
4770
|
+
* Assembly of the plugin's one static stylesheet.
|
|
4771
|
+
*
|
|
4772
|
+
* Until now the sheet was a single template literal in the client entry with
|
|
4773
|
+
* thirteen constants bolted together in whatever order they were added. That
|
|
4774
|
+
* order is load-bearing and was documented in three separate comment blocks in
|
|
4775
|
+
* `wallpaper.ts` — so it is encoded here instead, in one list, with the reason
|
|
4776
|
+
* next to each entry.
|
|
4777
|
+
*
|
|
4778
|
+
* What the release can change lives in one contiguous slot: the panel group
|
|
4779
|
+
* (which element carries the frost) and the plugin-manager page (whether this
|
|
4780
|
+
* release ships that page at all). Everything outside the slot is host-agnostic by
|
|
4781
|
+
* construction — it targets attributes every release in range emits.
|
|
4782
|
+
*
|
|
4783
|
+
* The sheet is rebuilt when the release verdict lands. It has to be: `apply` runs
|
|
4784
|
+
* synchronously while the verdict is a Node round-trip, so the FIRST sheet is
|
|
4785
|
+
* always the unresolved, DOM-arbitrated one. That is the correct interim state,
|
|
4786
|
+
* and swapping in the resolved arm a moment later is what removes the `:has()`
|
|
4787
|
+
* dependency from hosts we do know the answer for.
|
|
4788
|
+
*
|
|
4789
|
+
* @module
|
|
4790
|
+
*/
|
|
4791
|
+
/** The plugin's own dark-mode value on the body attribute the host sets. Scoping
|
|
4792
|
+
* the gradient to it avoids matching the host's own theme attribute. */
|
|
4793
|
+
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%)}";
|
|
4794
|
+
/**
|
|
4795
|
+
* The order contract. Later entries override earlier ones at equal specificity,
|
|
4796
|
+
* which several groups rely on:
|
|
4797
|
+
* · HEADER_POPOVER_RULE after POPOVER_BLUR_RULE — a surface moving from the card
|
|
4798
|
+
* group to the header group must win, not merely be listed twice.
|
|
4799
|
+
* · PRODUCED_RULE / STROKE_RULE after the blur groups — they shield their
|
|
4800
|
+
* surfaces from an inherited slider.
|
|
4801
|
+
* · EXEMPT_DEFAULT_RULE last of the token groups — an exemption has to land
|
|
4802
|
+
* after whatever it exempts.
|
|
4803
|
+
* · PLACEHOLDER_RULE genuinely last — `::placeholder` resets must survive every
|
|
4804
|
+
* group above.
|
|
4805
|
+
*/
|
|
4806
|
+
function buildStaticStyles() {
|
|
4807
|
+
const adapter = rHostAdapter();
|
|
4808
|
+
const panel = adapter.panelFragments();
|
|
4809
|
+
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)}" + INPUT_BLUR_RULE + MOBILE_HEADER_RULE + 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}";
|
|
4810
|
+
}
|
|
4811
|
+
/** Append the plugin's stylesheet and keep it matched to the host.
|
|
4812
|
+
* Returns a teardown for the plugin's effect scope. */
|
|
4813
|
+
function mountStaticStyles() {
|
|
4814
|
+
const el = document.createElement("style");
|
|
4815
|
+
el.dataset.plugin = "dsh-any-background";
|
|
4816
|
+
el.textContent = buildStaticStyles();
|
|
4817
|
+
document.head.appendChild(el);
|
|
4818
|
+
let last = el.textContent;
|
|
4819
|
+
const unsubscribe = subscribeHostInfo(() => {
|
|
4820
|
+
const next = buildStaticStyles();
|
|
4821
|
+
if (next !== last) {
|
|
4822
|
+
last = next;
|
|
4823
|
+
el.textContent = next;
|
|
4824
|
+
}
|
|
4825
|
+
});
|
|
4826
|
+
return () => {
|
|
4827
|
+
unsubscribe();
|
|
4828
|
+
el.parentNode?.removeChild(el);
|
|
4829
|
+
};
|
|
4830
|
+
}
|
|
4831
|
+
//#endregion
|
|
4092
4832
|
//#region src/client/utils/video.ts
|
|
4093
4833
|
/**
|
|
4094
4834
|
* Video-background helpers: capturing a single frame as a JPEG snapshot.
|
|
@@ -4156,7 +4896,11 @@ window.__ModuleLoader__.load({
|
|
|
4156
4896
|
* column: the host provides the modal chrome (backdrop, centering, closing).
|
|
4157
4897
|
* These classes style only the embedded shell; transient fixed layers (toast,
|
|
4158
4898
|
* color picker, background editor) escape through Portals on <html>. */
|
|
4159
|
-
.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}
|
|
4899
|
+
.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}
|
|
4900
|
+
/* The root needs its OWN border-box, not just the descendants' below: the
|
|
4901
|
+
* sidebar surface puts 12px of padding on it, and with the default content-box
|
|
4902
|
+
* that padding landed OUTSIDE the width:100%, so the page came out 24px wider
|
|
4903
|
+
* than the pane and the host clipped both edges off. */
|
|
4160
4904
|
.dab-root *,.dab-root *::before,.dab-root *::after{box-sizing:border-box}
|
|
4161
4905
|
.dab-root button{font-family:inherit}
|
|
4162
4906
|
|
|
@@ -5781,78 +6525,6 @@ window.__ModuleLoader__.load({
|
|
|
5781
6525
|
});
|
|
5782
6526
|
}
|
|
5783
6527
|
//#endregion
|
|
5784
|
-
//#region src/client/env.ts
|
|
5785
|
-
/**
|
|
5786
|
-
* Host capability detection — which third-party plugins are actually present.
|
|
5787
|
-
*
|
|
5788
|
-
* The host exposes no plugin registry on the client context, so presence is
|
|
5789
|
-
* inferred from the stable DOM markers those plugins mount. Detection runs
|
|
5790
|
-
* once (plugin boot) and is re-armed by a cheap body-level MutationObserver so
|
|
5791
|
-
* a plugin that mounts after this one still flips the cached verdict. The
|
|
5792
|
-
* verdict is sticky-true for the session: whether a detected plugin's surface
|
|
5793
|
-
* is currently collapsed must not hide the settings that govern it.
|
|
5794
|
-
*/
|
|
5795
|
-
/** Persistent container + surfaces dsh-better-sidebar mounts when loaded.
|
|
5796
|
-
* `[data-dsh-better-sidebar]` is the plugin's host wrapper — always appended
|
|
5797
|
-
* to document.body on mount — so it is the authoritative "plugin is loaded"
|
|
5798
|
-
* signal and the body childList observer fires exactly when it appears. The
|
|
5799
|
-
* panel surfaces we actually style are listed only as reinforcement: they are
|
|
5800
|
-
* conditionally rendered (collapsed panels drop them), so they must never be
|
|
5801
|
-
* the sole signal.
|
|
5802
|
-
*
|
|
5803
|
-
* `[data-sidebar-right-panel]` is deliberately ABSENT: on every host generation
|
|
5804
|
-
* it marks the host's own right Sidebar (always present once a session opens),
|
|
5805
|
-
* so counting it would make the better-sidebar verdict permanently true and the
|
|
5806
|
-
* panel slider could never drop back to its native "右方侧边栏" identity. */
|
|
5807
|
-
const BETTER_SIDEBAR_MARKERS = [
|
|
5808
|
-
"[data-dsh-better-sidebar]",
|
|
5809
|
-
"[data-dsh-panel-host]",
|
|
5810
|
-
"[data-dsh-bottom-panel]"
|
|
5811
|
-
];
|
|
5812
|
-
let betterSidebar = false;
|
|
5813
|
-
const subs = /* @__PURE__ */ new Set();
|
|
5814
|
-
/** Read snapshot of whether the better-sidebar plugin is present. */
|
|
5815
|
-
function rBetterSidebar() {
|
|
5816
|
-
return betterSidebar;
|
|
5817
|
-
}
|
|
5818
|
-
function emit() {
|
|
5819
|
-
subs.forEach((cb) => cb());
|
|
5820
|
-
}
|
|
5821
|
-
function sync() {
|
|
5822
|
-
if (document.querySelector(BETTER_SIDEBAR_MARKERS.join(",")) !== null && !betterSidebar) {
|
|
5823
|
-
betterSidebar = true;
|
|
5824
|
-
emit();
|
|
5825
|
-
}
|
|
5826
|
-
}
|
|
5827
|
-
/** React hook: re-renders the caller when better-sidebar presence changes. */
|
|
5828
|
-
function useBetterSidebar() {
|
|
5829
|
-
return (0, react.useSyncExternalStore)(subscribe, rBetterSidebar);
|
|
5830
|
-
}
|
|
5831
|
-
/** Subscribe to verdict changes. */
|
|
5832
|
-
function subscribe(cb) {
|
|
5833
|
-
subs.add(cb);
|
|
5834
|
-
return () => {
|
|
5835
|
-
subs.delete(cb);
|
|
5836
|
-
};
|
|
5837
|
-
}
|
|
5838
|
-
/** Start detection (probe once, then watch body mounts). Returns an
|
|
5839
|
-
* unsubscribe for plugin teardown. */
|
|
5840
|
-
function startBetterSidebarWatch() {
|
|
5841
|
-
sync();
|
|
5842
|
-
const observer = new MutationObserver(sync);
|
|
5843
|
-
observer.observe(document.body, { childList: true });
|
|
5844
|
-
return () => observer.disconnect();
|
|
5845
|
-
}
|
|
5846
|
-
let nativeTabs = false;
|
|
5847
|
-
const nativeSubs = /* @__PURE__ */ new Set();
|
|
5848
|
-
/** Mark the native right-Sidebar service as confirmed. Sticky — the service
|
|
5849
|
-
* never goes away within a session. */
|
|
5850
|
-
function markNativeSidebarTabs() {
|
|
5851
|
-
if (nativeTabs) return;
|
|
5852
|
-
nativeTabs = true;
|
|
5853
|
-
nativeSubs.forEach((cb) => cb());
|
|
5854
|
-
}
|
|
5855
|
-
//#endregion
|
|
5856
6528
|
//#region src/client/components/pages/InterfacePage.tsx
|
|
5857
6529
|
const PARTS$1 = [
|
|
5858
6530
|
{
|
|
@@ -5902,14 +6574,13 @@ window.__ModuleLoader__.load({
|
|
|
5902
6574
|
},
|
|
5903
6575
|
{
|
|
5904
6576
|
isPanel: true,
|
|
5905
|
-
labelKey: "
|
|
6577
|
+
labelKey: "uiPanelNative",
|
|
5906
6578
|
Icon: PanelIcon
|
|
5907
6579
|
}
|
|
5908
6580
|
];
|
|
5909
6581
|
function InterfacePage({ p }) {
|
|
5910
6582
|
const { t, setOps, setBlurs, setSop, setPanelOp, useStore } = p;
|
|
5911
6583
|
useStore((s) => s.metaRev);
|
|
5912
|
-
const hasBetterSidebar = useBetterSidebar();
|
|
5913
6584
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
5914
6585
|
className: "dab-head dab-rise",
|
|
5915
6586
|
style: { "--d": 0 },
|
|
@@ -5932,7 +6603,7 @@ window.__ModuleLoader__.load({
|
|
|
5932
6603
|
children: PARTS$1.map((part, i) => {
|
|
5933
6604
|
const { labelKey, Icon, isSettings, isChat, isTrajectory, isPanel, isProduced, isHeader } = part;
|
|
5934
6605
|
const opKey = part.opKey;
|
|
5935
|
-
const label =
|
|
6606
|
+
const label = t(labelKey);
|
|
5936
6607
|
const blurKey = isChat ? "chat" : isTrajectory ? "trajectory" : isSettings ? "settings" : isPanel ? "panel" : isProduced ? "produced" : isHeader ? "header" : opKey;
|
|
5937
6608
|
const opacity = isChat ? rChatTextOpacity() : isTrajectory ? rTrajectoryOpacity() : isSettings ? rSop() : isPanel ? rPanelOpacity() : isProduced ? rProducedOpacity() : isHeader ? rHeaderOpacity() : rOps()[opKey];
|
|
5938
6609
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
@@ -6093,9 +6764,8 @@ window.__ModuleLoader__.load({
|
|
|
6093
6764
|
},
|
|
6094
6765
|
{
|
|
6095
6766
|
key: "panel",
|
|
6096
|
-
labelKey: "
|
|
6097
|
-
Icon: PanelIcon
|
|
6098
|
-
needsSidebar: true
|
|
6767
|
+
labelKey: "uiPanelNative",
|
|
6768
|
+
Icon: PanelIcon
|
|
6099
6769
|
}
|
|
6100
6770
|
];
|
|
6101
6771
|
const COLOR_KEYS = [
|
|
@@ -6128,7 +6798,6 @@ window.__ModuleLoader__.load({
|
|
|
6128
6798
|
}
|
|
6129
6799
|
function FontPage({ p, notify }) {
|
|
6130
6800
|
const { t, setFont, removeFont, setFontEnabled, setStrokes, useStore } = p;
|
|
6131
|
-
const hasBetterSidebar = useBetterSidebar();
|
|
6132
6801
|
const fileRef = (0, react.useRef)(null);
|
|
6133
6802
|
const [strokes, setStrokesState] = (0, react.useState)(() => rStrokes());
|
|
6134
6803
|
const [fontInfo, setFontInfo] = (0, react.useState)(() => ({
|
|
@@ -6303,7 +6972,7 @@ window.__ModuleLoader__.load({
|
|
|
6303
6972
|
}),
|
|
6304
6973
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6305
6974
|
className: "dab-grid-parts",
|
|
6306
|
-
children: PARTS.
|
|
6975
|
+
children: PARTS.map((part, i) => {
|
|
6307
6976
|
const s = strokes[part.key];
|
|
6308
6977
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
6309
6978
|
className: "dab-card dab-card-hover dab-rise",
|
|
@@ -8317,11 +8986,11 @@ window.__ModuleLoader__.load({
|
|
|
8317
8986
|
ctx.inject?.(["betterSidebar"], (scope) => {
|
|
8318
8987
|
const service = scope.betterSidebar;
|
|
8319
8988
|
if (service === void 0) return;
|
|
8320
|
-
const
|
|
8321
|
-
if (
|
|
8989
|
+
const owner = sidebarGuideOwner();
|
|
8990
|
+
if (owner === "host") return;
|
|
8322
8991
|
let disposeTab = null;
|
|
8323
|
-
const unsubscribeHost =
|
|
8324
|
-
if (
|
|
8992
|
+
const unsubscribeHost = owner === "pending" ? subscribeHostInfo(() => {
|
|
8993
|
+
if (sidebarGuideOwner() === "host") {
|
|
8325
8994
|
disposeTab?.();
|
|
8326
8995
|
disposeTab = null;
|
|
8327
8996
|
unsubscribeHost();
|
|
@@ -8350,12 +9019,80 @@ window.__ModuleLoader__.load({
|
|
|
8350
9019
|
});
|
|
8351
9020
|
}
|
|
8352
9021
|
//#endregion
|
|
9022
|
+
//#region src/client/env.ts
|
|
9023
|
+
/**
|
|
9024
|
+
* Host capability detection — which third-party plugins are actually present.
|
|
9025
|
+
*
|
|
9026
|
+
* The host exposes no plugin registry on the client context, so presence is
|
|
9027
|
+
* inferred from the stable DOM markers those plugins mount. Detection runs
|
|
9028
|
+
* once (plugin boot) and is re-armed by a cheap body-level MutationObserver so
|
|
9029
|
+
* a plugin that mounts after this one still flips the cached verdict. The
|
|
9030
|
+
* verdict is sticky-true for the session: whether a detected plugin's surface
|
|
9031
|
+
* is currently collapsed must not hide the settings that govern it.
|
|
9032
|
+
*/
|
|
9033
|
+
/** Persistent container + surfaces dsh-better-sidebar mounts when loaded.
|
|
9034
|
+
* `[data-dsh-better-sidebar]` is the plugin's host wrapper — always appended
|
|
9035
|
+
* to document.body on mount — so it is the authoritative "plugin is loaded"
|
|
9036
|
+
* signal and the body childList observer fires exactly when it appears. The
|
|
9037
|
+
* panel surfaces are listed only as reinforcement: they are conditionally
|
|
9038
|
+
* rendered (collapsed panels drop them), so they must never be the sole
|
|
9039
|
+
* signal.
|
|
9040
|
+
*
|
|
9041
|
+
* `[data-sidebar-right-panel]` is deliberately ABSENT: on every host generation
|
|
9042
|
+
* it marks the host's own right Sidebar (always present once a session opens),
|
|
9043
|
+
* so counting it would make the better-sidebar verdict permanently true on any
|
|
9044
|
+
* host and mis-drive everything gated on it. */
|
|
9045
|
+
const BETTER_SIDEBAR_MARKERS = [
|
|
9046
|
+
"[data-dsh-better-sidebar]",
|
|
9047
|
+
"[data-dsh-panel-host]",
|
|
9048
|
+
"[data-dsh-bottom-panel]"
|
|
9049
|
+
];
|
|
9050
|
+
let betterSidebar = false;
|
|
9051
|
+
const subs = /* @__PURE__ */ new Set();
|
|
9052
|
+
/** Read snapshot of whether the better-sidebar plugin is present. */
|
|
9053
|
+
function rBetterSidebar() {
|
|
9054
|
+
return betterSidebar;
|
|
9055
|
+
}
|
|
9056
|
+
function emit() {
|
|
9057
|
+
subs.forEach((cb) => cb());
|
|
9058
|
+
}
|
|
9059
|
+
function sync() {
|
|
9060
|
+
if (document.querySelector(BETTER_SIDEBAR_MARKERS.join(",")) !== null && !betterSidebar) {
|
|
9061
|
+
betterSidebar = true;
|
|
9062
|
+
emit();
|
|
9063
|
+
}
|
|
9064
|
+
}
|
|
9065
|
+
/** Subscribe to verdict changes. */
|
|
9066
|
+
function subscribe(cb) {
|
|
9067
|
+
subs.add(cb);
|
|
9068
|
+
return () => {
|
|
9069
|
+
subs.delete(cb);
|
|
9070
|
+
};
|
|
9071
|
+
}
|
|
9072
|
+
/** Start detection (probe once, then watch body mounts). Returns an
|
|
9073
|
+
* unsubscribe for plugin teardown. */
|
|
9074
|
+
function startBetterSidebarWatch() {
|
|
9075
|
+
sync();
|
|
9076
|
+
const observer = new MutationObserver(sync);
|
|
9077
|
+
observer.observe(document.body, { childList: true });
|
|
9078
|
+
return () => observer.disconnect();
|
|
9079
|
+
}
|
|
9080
|
+
let nativeTabs = false;
|
|
9081
|
+
const nativeSubs = /* @__PURE__ */ new Set();
|
|
9082
|
+
/** Mark the native right-Sidebar service as confirmed. Sticky — the service
|
|
9083
|
+
* never goes away within a session. */
|
|
9084
|
+
function markNativeSidebarTabs() {
|
|
9085
|
+
if (nativeTabs) return;
|
|
9086
|
+
nativeTabs = true;
|
|
9087
|
+
nativeSubs.forEach((cb) => cb());
|
|
9088
|
+
}
|
|
9089
|
+
//#endregion
|
|
8353
9090
|
//#region src/client/sidebar/native-tab.tsx
|
|
8354
9091
|
/**
|
|
8355
|
-
* dsh-any-background — native right-Sidebar integration
|
|
9092
|
+
* dsh-any-background — native right-Sidebar integration.
|
|
8356
9093
|
*
|
|
8357
|
-
* DSH
|
|
8358
|
-
*
|
|
9094
|
+
* DSH ships an official right Sidebar (`dsh-client-ui-sidebar-right`) with a
|
|
9095
|
+
* public two-stage extension path: a static tab type into
|
|
8359
9096
|
* `ctx.sidebarRightTabs` — whose `guide` entry draws a card on the Sidebar's
|
|
8360
9097
|
* guide page — and a body under the keyed `sidebar.right.pane.tab` seat. This
|
|
8361
9098
|
* module registers the plugin's appearance pages through that path, so the
|
|
@@ -8363,6 +9100,11 @@ window.__ModuleLoader__.load({
|
|
|
8363
9100
|
* settings panel shows (one implementation, one state store; editing in either
|
|
8364
9101
|
* place shows up in the other).
|
|
8365
9102
|
*
|
|
9103
|
+
* NOTE: that registry is NOT a new-host feature. It is provided on 0.1.5-rc.2
|
|
9104
|
+
* already (verified against the release tag), so nothing here may treat "the
|
|
9105
|
+
* service answered" as "this is a recent DSH" — whether this tab or the
|
|
9106
|
+
* better-sidebar page owns the guide surface is the host adapter's call.
|
|
9107
|
+
*
|
|
8366
9108
|
* The dependency is optional at every level, mirroring `sidebar/tab.tsx`:
|
|
8367
9109
|
* · the registry is waited on with `ctx.inject`, so a host without the
|
|
8368
9110
|
* right Sidebar (or an older DSH) silently registers nothing, and a
|
|
@@ -8416,7 +9158,7 @@ window.__ModuleLoader__.load({
|
|
|
8416
9158
|
locale
|
|
8417
9159
|
});
|
|
8418
9160
|
scope.effect(() => {
|
|
8419
|
-
const yieldTo = () =>
|
|
9161
|
+
const yieldTo = () => sidebarGuideOwner() === "plugin" && rBetterSidebar();
|
|
8420
9162
|
if (yieldTo()) return;
|
|
8421
9163
|
let disposeTab = null;
|
|
8422
9164
|
const withdraw = () => {
|
|
@@ -8454,121 +9196,6 @@ window.__ModuleLoader__.load({
|
|
|
8454
9196
|
});
|
|
8455
9197
|
}
|
|
8456
9198
|
//#endregion
|
|
8457
|
-
//#region src/client/header-tag.ts
|
|
8458
|
-
/**
|
|
8459
|
-
* Header-surface tagging.
|
|
8460
|
-
*
|
|
8461
|
-
* WHY A RUNTIME TAG IS NEEDED
|
|
8462
|
-
* The session-header dropdowns (open-in-app, session-log, Agent Team, the job
|
|
8463
|
-
* list, the subagent lineage tree) render their popover through the Menu/tree
|
|
8464
|
-
* primitives, which PORTAL the list to `document.body`. A portaled list is not a
|
|
8465
|
-
* DOM descendant of the header — no ancestor selector can reach it — and the
|
|
8466
|
-
* primitive emits no `id`/`aria-controls` back-link to its anchor either. Pure
|
|
8467
|
-
* CSS class matching was the remaining option, but 0.1.7 broke the shapes this
|
|
8468
|
-
* plugin relied on: open-in-app moved from in-place to `portal` (so the old
|
|
8469
|
-
* `:not(_portal)` discriminator no longer selected it), and the session row menu
|
|
8470
|
-
* became a dynamic `sidebar.workspaces.session.menu.item` slot whose rows are no
|
|
8471
|
-
* longer guaranteed to include a destructive one.
|
|
8472
|
-
*
|
|
8473
|
-
* THE SIGNAL
|
|
8474
|
-
* The slot renderer stamps every registered slot with `data-slot="<key>"`
|
|
8475
|
-
* (ui-renderer `scoped-slots.tsx`), and the session header's own slots are
|
|
8476
|
-
* `conversation.session.header{,.actions,.utilities,.corner,.lineage}` — present
|
|
8477
|
-
* on 0.1.5 through 0.1.7 alike. Those anchors live in the header subtree, so the
|
|
8478
|
-
* plugin can detect when a header trigger is expanded.
|
|
8479
|
-
*
|
|
8480
|
-
* WHY NOT GEOMETRY
|
|
8481
|
-
* A tempting link is the inline position the primitive writes on a portaled list
|
|
8482
|
-
* (`style="left: …; top: …"`). It was tried and REJECTED: an `align="end"` list
|
|
8483
|
-
* is flush to the trigger's right edge while the trigger itself sits at the far
|
|
8484
|
-
* right of the header, so the two boxes often do not overlap horizontally (in
|
|
8485
|
-
* the reported DOM the list is at `left:1012` with the trigger near x=1400). Any
|
|
8486
|
-
* purely geometric proximity test therefore misses real header menus.
|
|
8487
|
-
*
|
|
8488
|
-
* THE ACTUAL RULE (structural, no geometry, no timing)
|
|
8489
|
-
* A header dropdown can only be open while its trigger reports
|
|
8490
|
-
* `aria-expanded="true"`, and opening one is what mounts the popover. So a
|
|
8491
|
-
* popover tag is maintained as: "there is at least one expanded header trigger,
|
|
8492
|
-
* AND this popover is not owned by another group". Popovers are only ever
|
|
8493
|
-
* tagged/released on a change of that combined state, which makes the tag
|
|
8494
|
-
* self-consistent and idempotent.
|
|
8495
|
-
*
|
|
8496
|
-
* The one hazard is a popover that is open for an unrelated reason (a composer
|
|
8497
|
-
* menu, a hover card) *while* a header trigger stays expanded. Such a popover
|
|
8498
|
-
* is not a candidate: it is excluded by `ownsForeignSurface`, which skips any
|
|
8499
|
-
* popover carrying markers of a different slider group. In practice header
|
|
8500
|
-
* dropdowns are transient (opening one closes the others), so this is a guard
|
|
8501
|
-
* rather than the common path.
|
|
8502
|
-
*
|
|
8503
|
-
* @module
|
|
8504
|
-
*/
|
|
8505
|
-
/** Marker attribute the stylesheet keys on. */
|
|
8506
|
-
const HEADER_POPOVER_ATTR = "data-dsh-any-header-popover";
|
|
8507
|
-
/** Slot anchors that identify the session header's own surfaces. */
|
|
8508
|
-
const HEADER_SLOT_SELECTOR = [
|
|
8509
|
-
"[data-slot=\"conversation.session.header\"]",
|
|
8510
|
-
"[data-slot=\"conversation.session.header.actions\"]",
|
|
8511
|
-
"[data-slot=\"conversation.session.header.utilities\"]",
|
|
8512
|
-
"[data-slot=\"conversation.session.header.corner\"]",
|
|
8513
|
-
"[data-slot=\"conversation.session.header.lineage\"]"
|
|
8514
|
-
].join(",");
|
|
8515
|
-
/** Portaled popovers a header trigger can own. */
|
|
8516
|
-
const POPOVER_SELECTOR = "[role=\"menu\"], [role=\"tree\"], [role=\"dialog\"]";
|
|
8517
|
-
/** Whether any header slot anchor has an expanded trigger. */
|
|
8518
|
-
function headerTriggerOpen() {
|
|
8519
|
-
for (const anchor of document.querySelectorAll(HEADER_SLOT_SELECTOR)) if (anchor.querySelector("[aria-expanded=\"true\"]") !== null) return true;
|
|
8520
|
-
return false;
|
|
8521
|
-
}
|
|
8522
|
-
/** Surfaces that belong to a different slider group and must never be claimed.
|
|
8523
|
-
* `_denseList` without `_portal` is the in-place card-side dense menu, an
|
|
8524
|
-
* `_ioCard`/`_menu`-classed body-level list belongs elsewhere, and a modal is
|
|
8525
|
-
* its own surface. */
|
|
8526
|
-
function ownsForeignSurface(el) {
|
|
8527
|
-
if (el.getAttribute("aria-modal") === "true") return true;
|
|
8528
|
-
return el.parentElement !== document.body;
|
|
8529
|
-
}
|
|
8530
|
-
let observer = null;
|
|
8531
|
-
let raf = null;
|
|
8532
|
-
/** Recompute tags from the current open state. */
|
|
8533
|
-
function refreshHeaderPopovers() {
|
|
8534
|
-
const open = headerTriggerOpen();
|
|
8535
|
-
const candidates = [...document.querySelectorAll(POPOVER_SELECTOR)].filter((el) => !ownsForeignSurface(el));
|
|
8536
|
-
if (!open) {
|
|
8537
|
-
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
8538
|
-
return;
|
|
8539
|
-
}
|
|
8540
|
-
for (const el of candidates) el.setAttribute(HEADER_POPOVER_ATTR, "");
|
|
8541
|
-
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) if (!candidates.includes(el)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
8542
|
-
}
|
|
8543
|
-
function schedule() {
|
|
8544
|
-
if (raf !== null) return;
|
|
8545
|
-
raf = window.requestAnimationFrame(() => {
|
|
8546
|
-
raf = null;
|
|
8547
|
-
refreshHeaderPopovers();
|
|
8548
|
-
});
|
|
8549
|
-
}
|
|
8550
|
-
/** Start tagging. Returns a teardown that stops observing and clears markings. */
|
|
8551
|
-
function startHeaderPopoverTagging() {
|
|
8552
|
-
if (typeof MutationObserver === "undefined" || typeof document === "undefined") return () => {};
|
|
8553
|
-
refreshHeaderPopovers();
|
|
8554
|
-
observer = new MutationObserver(schedule);
|
|
8555
|
-
observer.observe(document.body, {
|
|
8556
|
-
childList: true,
|
|
8557
|
-
subtree: true,
|
|
8558
|
-
attributes: true,
|
|
8559
|
-
attributeFilter: ["aria-expanded"]
|
|
8560
|
-
});
|
|
8561
|
-
return () => {
|
|
8562
|
-
observer?.disconnect();
|
|
8563
|
-
observer = null;
|
|
8564
|
-
if (raf !== null) {
|
|
8565
|
-
window.cancelAnimationFrame(raf);
|
|
8566
|
-
raf = null;
|
|
8567
|
-
}
|
|
8568
|
-
for (const el of document.querySelectorAll(`[${HEADER_POPOVER_ATTR}]`)) el.removeAttribute(HEADER_POPOVER_ATTR);
|
|
8569
|
-
};
|
|
8570
|
-
}
|
|
8571
|
-
//#endregion
|
|
8572
9199
|
//#region src/client/index.tsx
|
|
8573
9200
|
/**
|
|
8574
9201
|
* dsh-any-background — browser half entry.
|
|
@@ -8667,13 +9294,7 @@ window.__ModuleLoader__.load({
|
|
|
8667
9294
|
customDispose?.();
|
|
8668
9295
|
if (colorTimerRef.current !== null) window.clearTimeout(colorTimerRef.current);
|
|
8669
9296
|
}, "dsh-any-background: skin dispose");
|
|
8670
|
-
|
|
8671
|
-
styleEl.dataset.plugin = "dsh-any-background";
|
|
8672
|
-
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_PROMOTION_RULE}${PANEL_BLUR_RULE}${PRODUCED_RULE}${STROKE_RULE}${HEADER_POPOVER_RULE}${EXEMPT_DEFAULT_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}`;
|
|
8673
|
-
document.head.appendChild(styleEl);
|
|
8674
|
-
ctx.effect(() => () => {
|
|
8675
|
-
styleEl?.parentNode?.removeChild(styleEl);
|
|
8676
|
-
}, "dsh-any-background: gradient");
|
|
9297
|
+
ctx.effect(() => mountStaticStyles(), "dsh-any-background: stylesheet");
|
|
8677
9298
|
const disposeDragQuality = watchWallpaperDragQuality();
|
|
8678
9299
|
ctx.effect(() => () => disposeDragQuality(), "dsh-any-background: drag quality");
|
|
8679
9300
|
let rev = 0;
|
|
@@ -9446,7 +10067,7 @@ window.__ModuleLoader__.load({
|
|
|
9446
10067
|
});
|
|
9447
10068
|
const navLabel = () => ctx.locale.bind(NS)("nav");
|
|
9448
10069
|
const applyNavIcon = () => {
|
|
9449
|
-
const nav = document.querySelector(
|
|
10070
|
+
const nav = document.querySelector(SETTINGS_PANEL_SEL)?.querySelector("nav");
|
|
9450
10071
|
if (!nav) return;
|
|
9451
10072
|
const target = navLabel();
|
|
9452
10073
|
for (const cell of Array.from(nav.querySelectorAll("button"))) {
|
|
@@ -9476,7 +10097,7 @@ window.__ModuleLoader__.load({
|
|
|
9476
10097
|
for (const n of r.addedNodes) {
|
|
9477
10098
|
if (n.nodeType !== 1) continue;
|
|
9478
10099
|
const el = n;
|
|
9479
|
-
if (el.matches?.("[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]") || el.querySelector?.("[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]")) return true;
|
|
10100
|
+
if (el.matches?.("[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby][class*=\"_panel\"]") || el.querySelector?.("[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby][class*=\"_panel\"]")) return true;
|
|
9480
10101
|
}
|
|
9481
10102
|
return false;
|
|
9482
10103
|
})) applyNavIcon();
|