dsh-background-by-model 0.7.4 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/CHANGELOG.zh.md +11 -1
- package/README.md +20 -23
- package/README.zh.md +20 -23
- package/lib/client.js +201 -40
- package/lib/client.js.map +1 -1
- package/lib/index.js +46 -14
- package/package.json +4 -2
package/lib/client.js
CHANGED
|
@@ -33,6 +33,7 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
uiOpacityBg: "主背景",
|
|
34
34
|
uiOpacitySide: "左侧栏",
|
|
35
35
|
uiOpacityCard: "卡片与菜单面板",
|
|
36
|
+
uiOpacityCode: "代码块",
|
|
36
37
|
uiOpacityInput: "输入框与控件",
|
|
37
38
|
uiSop: "设置界面透明度",
|
|
38
39
|
uiChatRegion: "对话文本框",
|
|
@@ -83,6 +84,7 @@ window.__ModuleLoader__.load({
|
|
|
83
84
|
ruleColorTargetImage: "作用于选中的图片:",
|
|
84
85
|
ruleColorTargetRule: "作用于本规则(它还没有图片)",
|
|
85
86
|
ruleLayout: "布局模式",
|
|
87
|
+
ruleLayoutNoImage: "添加图片后可分别设置每张图的布局模式",
|
|
86
88
|
ruleFramingEdit: "编辑位置",
|
|
87
89
|
ruleOpacity: "背景透明度",
|
|
88
90
|
ruleBlur: "背景模糊",
|
|
@@ -141,6 +143,7 @@ window.__ModuleLoader__.load({
|
|
|
141
143
|
importDone: "已导入配置",
|
|
142
144
|
importFail: "导入失败,文件格式不正确",
|
|
143
145
|
footerTag: "按模型换背景插件",
|
|
146
|
+
repoLink: "在 GitHub 上打开仓库",
|
|
144
147
|
holidayTitle: "节日特殊背景",
|
|
145
148
|
holidayEnable: "启用节日特殊背景(中秋当天、国庆 10 月 1–7 日)",
|
|
146
149
|
trTitle: "切换效果",
|
|
@@ -174,6 +177,7 @@ window.__ModuleLoader__.load({
|
|
|
174
177
|
uiOpacityBg: "Main background",
|
|
175
178
|
uiOpacitySide: "Left panel",
|
|
176
179
|
uiOpacityCard: "Cards & menus",
|
|
180
|
+
uiOpacityCode: "Code blocks",
|
|
177
181
|
uiOpacityInput: "Input & controls",
|
|
178
182
|
uiSop: "Settings interface opacity",
|
|
179
183
|
uiChatRegion: "Conversation text frame",
|
|
@@ -224,6 +228,7 @@ window.__ModuleLoader__.load({
|
|
|
224
228
|
ruleColorTargetRule: "Applies to this rule (it has no image yet)",
|
|
225
229
|
ruleColorExtract: "Extract from image",
|
|
226
230
|
ruleLayout: "Layout mode",
|
|
231
|
+
ruleLayoutNoImage: "Add an image to give each one its own layout mode",
|
|
227
232
|
ruleFramingEdit: "Edit position",
|
|
228
233
|
ruleOpacity: "Background opacity",
|
|
229
234
|
ruleBlur: "Background blur",
|
|
@@ -282,6 +287,7 @@ window.__ModuleLoader__.load({
|
|
|
282
287
|
importDone: "Profile imported",
|
|
283
288
|
importFail: "Import failed — invalid file",
|
|
284
289
|
footerTag: "Per-model wallpaper plugin",
|
|
290
|
+
repoLink: "Open the repository on GitHub",
|
|
285
291
|
holidayTitle: "Holiday backgrounds",
|
|
286
292
|
holidayEnable: "Enable holiday backgrounds (Mid-Autumn day, Oct 1–7)",
|
|
287
293
|
trTitle: "Switch effect",
|
|
@@ -578,18 +584,22 @@ window.__ModuleLoader__.load({
|
|
|
578
584
|
* `sidebar` sidebar (`--dsw-specific-sidebar-fill`)
|
|
579
585
|
* `card` cards/panels (`--dsw-alias-bg-layer-1/2/3`, `--dsw-specific-menu`
|
|
580
586
|
* and its 0.1.7 alias `--dsw-menu-surface-fill`)
|
|
587
|
+
* `code` code blocks, banner included (`--dsw-alias-markdown-code-block`,
|
|
588
|
+
* `--dsw-alias-markdown-code-block-banner`)
|
|
581
589
|
* `input` input/control surfaces (`--dsw-specific-input-major`)
|
|
582
590
|
*/
|
|
583
591
|
const PART_OPACITY_KEYS = [
|
|
584
592
|
"bg",
|
|
585
593
|
"sidebar",
|
|
586
594
|
"card",
|
|
595
|
+
"code",
|
|
587
596
|
"input"
|
|
588
597
|
];
|
|
589
598
|
const DEFAULT_PART_OPACITIES = {
|
|
590
599
|
bg: .85,
|
|
591
600
|
sidebar: .93,
|
|
592
601
|
card: 1,
|
|
602
|
+
code: 1,
|
|
593
603
|
input: 1
|
|
594
604
|
};
|
|
595
605
|
/**
|
|
@@ -722,7 +732,9 @@ window.__ModuleLoader__.load({
|
|
|
722
732
|
*
|
|
723
733
|
* Holidays default to `fill` rather than a rule's `fit`: these are full-bleed
|
|
724
734
|
* festival wallpapers, and `fit` would letterbox them on every aspect ratio the
|
|
725
|
-
* art was not cut for.
|
|
735
|
+
* art was not cut for. The mode now lives on the IMAGE (see `BgImage.bgMode`), so
|
|
736
|
+
* that default is written there — and `fill` is what a holiday's read-back
|
|
737
|
+
* sanitizer refuses to lose (see `normalizeHolidayRule`).
|
|
726
738
|
*/
|
|
727
739
|
function defaultHolidayRule(def) {
|
|
728
740
|
const color = hexToHsl(def.color);
|
|
@@ -730,13 +742,13 @@ window.__ModuleLoader__.load({
|
|
|
730
742
|
id: def.id,
|
|
731
743
|
images: [{
|
|
732
744
|
slot: def.slot,
|
|
745
|
+
bgMode: "fill",
|
|
733
746
|
bgState: { ...DEFAULT_BG_STATE },
|
|
734
747
|
color
|
|
735
748
|
}],
|
|
736
749
|
match: "",
|
|
737
750
|
enabled: true,
|
|
738
751
|
color,
|
|
739
|
-
bgMode: "fill",
|
|
740
752
|
wallpaperOpacity: 1,
|
|
741
753
|
blur: 0,
|
|
742
754
|
rotate: defaultRotation()
|
|
@@ -867,12 +879,21 @@ window.__ModuleLoader__.load({
|
|
|
867
879
|
* same as absent: an entry with `color: null` is an image the user explicitly set
|
|
868
880
|
* to "follow the system theme", and re-inheriting the rule's color over that
|
|
869
881
|
* would undo a deliberate choice on every load.
|
|
882
|
+
*
|
|
883
|
+
* `inheritMode` is the same one-shot lift for the layout mode, and it is the
|
|
884
|
+
* caller's to resolve because the two callers answer differently: a user rule
|
|
885
|
+
* passes the mode its config carried at the RULE level (a schema-5 config, where
|
|
886
|
+
* 适应/填充 was one choice for the whole rotation), a holiday passes `fill` (its
|
|
887
|
+
* own default, which is not up for the lift to change). An entry that HAS a
|
|
888
|
+
* `bgMode` key keeps it — including one that has just been set to a value equal
|
|
889
|
+
* to the default, which is a decision and not an absence.
|
|
870
890
|
*/
|
|
871
|
-
function normalizeImage(raw, inherit = null) {
|
|
891
|
+
function normalizeImage(raw, inherit = null, inheritMode = "fit") {
|
|
872
892
|
const i = raw ?? {};
|
|
873
893
|
if (typeof i.slot !== "string" || !SLOT_RE.test(i.slot)) return null;
|
|
874
894
|
return {
|
|
875
895
|
slot: i.slot,
|
|
896
|
+
bgMode: BG_MODES$1.includes(i.bgMode) ? i.bgMode : inheritMode,
|
|
876
897
|
bgState: normalizeBgState(i.bgState),
|
|
877
898
|
color: hasOwn(i, "color") ? normalizeHsl(i.color) : inherit
|
|
878
899
|
};
|
|
@@ -892,6 +913,14 @@ window.__ModuleLoader__.load({
|
|
|
892
913
|
* it as well: it is not a default for its images at paint time any more, but it
|
|
893
914
|
* is still what the rule paints once its last picture is gone.
|
|
894
915
|
*
|
|
916
|
+
* The layout mode makes the same trip in the other direction, and this is the
|
|
917
|
+
* last release that has to: a config written before 0.8 keeps `bgMode` at the RULE
|
|
918
|
+
* level, where one value covered every picture of a rotation, so it is read here
|
|
919
|
+
* once and handed to each image as its inherited mode. What the user chose
|
|
920
|
+
* therefore survives the move the same way it survived the color's — the mode is
|
|
921
|
+
* not reset to the default on upgrade, and the field it used to live on is simply
|
|
922
|
+
* not written back.
|
|
923
|
+
*
|
|
895
924
|
* An EMPTY list is a legitimate rule, not a broken one: removing a rule's last
|
|
896
925
|
* picture has to be expressible, and forcing an entry back in is what produced a
|
|
897
926
|
* phantom blank image that took slot 1 and could never be deleted again (the next
|
|
@@ -906,11 +935,12 @@ window.__ModuleLoader__.load({
|
|
|
906
935
|
const id = typeof r.id === "string" && r.id !== "" ? r.id : null;
|
|
907
936
|
if (id === null) return null;
|
|
908
937
|
const color = normalizeHsl(r.color);
|
|
938
|
+
const mode = BG_MODES$1.includes(r.bgMode) ? r.bgMode : "fit";
|
|
909
939
|
const images = [];
|
|
910
940
|
const seen = /* @__PURE__ */ new Set();
|
|
911
941
|
const hadList = Array.isArray(r.images);
|
|
912
942
|
if (hadList) for (const entry of r.images) {
|
|
913
|
-
const image = normalizeImage(entry, color);
|
|
943
|
+
const image = normalizeImage(entry, color, mode);
|
|
914
944
|
if (image === null || seen.has(image.slot)) continue;
|
|
915
945
|
seen.add(image.slot);
|
|
916
946
|
images.push(image);
|
|
@@ -918,21 +948,22 @@ window.__ModuleLoader__.load({
|
|
|
918
948
|
const legacySlot = typeof r.slot === "string" && SLOT_RE.test(r.slot) ? r.slot : null;
|
|
919
949
|
if (images.length === 0 && legacySlot !== null) images.push({
|
|
920
950
|
slot: legacySlot,
|
|
951
|
+
bgMode: mode,
|
|
921
952
|
bgState: normalizeBgState(r.bgState),
|
|
922
953
|
color
|
|
923
954
|
});
|
|
924
955
|
if (images.length === 0 && !hadList) return null;
|
|
925
|
-
const
|
|
956
|
+
const rotate = normalizeRotation(r.rotate);
|
|
957
|
+
if (images.length < 2) rotate.enabled = false;
|
|
926
958
|
return {
|
|
927
959
|
id,
|
|
928
960
|
images,
|
|
929
961
|
match: typeof r.match === "string" ? r.match : "",
|
|
930
962
|
enabled: r.enabled !== false,
|
|
931
963
|
color,
|
|
932
|
-
bgMode: mode,
|
|
933
964
|
wallpaperOpacity: clamp01(r.wallpaperOpacity, 1),
|
|
934
965
|
blur: clamp$1(r.blur, 0, 60, 0),
|
|
935
|
-
rotate
|
|
966
|
+
rotate
|
|
936
967
|
};
|
|
937
968
|
}
|
|
938
969
|
/**
|
|
@@ -943,9 +974,12 @@ window.__ModuleLoader__.load({
|
|
|
943
974
|
* its bytes, and its theme color is a fixed part of what that holiday looks like —
|
|
944
975
|
* so a stale or hand-edited value would either orphan the wallpaper, point the
|
|
945
976
|
* holiday at a rule's image, or paint a festival in a color it does not have.
|
|
946
|
-
* Everything else is sanitized exactly like a rule's field — except
|
|
947
|
-
*
|
|
948
|
-
*
|
|
977
|
+
* Everything else is sanitized exactly like a rule's field — except three things
|
|
978
|
+
* that stay the definition's own: the image list and the rotation (so a festival
|
|
979
|
+
* can never be swapped or cycled), and the layout mode, which is the definition's
|
|
980
|
+
* `fill` for the same reason the color is its own — full-bleed art letterboxed by
|
|
981
|
+
* a stale `fit` is the one thing this feature must not do, and the mode is not
|
|
982
|
+
* editable for a holiday anywhere in the panel.
|
|
949
983
|
*/
|
|
950
984
|
function normalizeHolidayRule(def, raw) {
|
|
951
985
|
const r = raw ?? {};
|
|
@@ -953,11 +987,11 @@ window.__ModuleLoader__.load({
|
|
|
953
987
|
return {
|
|
954
988
|
...base,
|
|
955
989
|
enabled: r.enabled !== false,
|
|
956
|
-
bgMode: BG_MODES$1.includes(r.bgMode) ? r.bgMode : base.bgMode,
|
|
957
990
|
wallpaperOpacity: clamp01(r.wallpaperOpacity, base.wallpaperOpacity),
|
|
958
991
|
blur: clamp$1(r.blur, 0, 60, base.blur),
|
|
959
992
|
images: base.images.map((image) => ({
|
|
960
993
|
slot: image.slot,
|
|
994
|
+
bgMode: image.bgMode,
|
|
961
995
|
bgState: normalizeBgState(r.images?.[0]?.bgState ?? r.bgState),
|
|
962
996
|
color: image.color
|
|
963
997
|
}))
|
|
@@ -1194,7 +1228,6 @@ window.__ModuleLoader__.load({
|
|
|
1194
1228
|
match: "",
|
|
1195
1229
|
enabled: true,
|
|
1196
1230
|
color: null,
|
|
1197
|
-
bgMode: "fit",
|
|
1198
1231
|
wallpaperOpacity: 1,
|
|
1199
1232
|
blur: 0,
|
|
1200
1233
|
rotate: defaultRotation()
|
|
@@ -1255,8 +1288,17 @@ window.__ModuleLoader__.load({
|
|
|
1255
1288
|
.25
|
|
1256
1289
|
];
|
|
1257
1290
|
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Layout mode of the CURRENT image (per image, see BgImage).
|
|
1293
|
+
*
|
|
1294
|
+
* Read from the image and never from the rule, which no longer has a mode at all:
|
|
1295
|
+
* a rotation through a photo and a screenshot has to be able to letterbox one and
|
|
1296
|
+
* fill the other, and a rule-level answer would be the wrong one for half the
|
|
1297
|
+
* list. A rule with no image has nothing to place, so it answers with the
|
|
1298
|
+
* default — the renderer's null case, painted as "no wallpaper".
|
|
1299
|
+
*/
|
|
1258
1300
|
function rBgMode() {
|
|
1259
|
-
return
|
|
1301
|
+
return activeImage()?.bgMode ?? "fit";
|
|
1260
1302
|
}
|
|
1261
1303
|
function rWop() {
|
|
1262
1304
|
return clamp01(activeRule()?.wallpaperOpacity, 1);
|
|
@@ -1510,7 +1552,7 @@ window.__ModuleLoader__.load({
|
|
|
1510
1552
|
const d = data;
|
|
1511
1553
|
const slots = Array.isArray(d.slots) ? d.slots.filter((s) => typeof s === "string") : [];
|
|
1512
1554
|
const schema = typeof d.schema === "number" && isFinite(d.schema) ? d.schema : null;
|
|
1513
|
-
hostShape = schema === null ? "legacy" : schema >=
|
|
1555
|
+
hostShape = schema === null ? "legacy" : schema >= 6 ? "current" : "legacy";
|
|
1514
1556
|
if (hostShape === "legacy") holdWrite();
|
|
1515
1557
|
return {
|
|
1516
1558
|
config: d.config,
|
|
@@ -1603,10 +1645,11 @@ window.__ModuleLoader__.load({
|
|
|
1603
1645
|
if (names.length === 0) return "";
|
|
1604
1646
|
const css = registrations(names, input.initial);
|
|
1605
1647
|
if (!(input.durationMs > 0)) return css;
|
|
1648
|
+
const written = new Set(input.written);
|
|
1606
1649
|
const timing = (n) => `${n} ${input.durationMs}ms ${input.easing}`;
|
|
1607
1650
|
return `${css}${input.scopes.map((s) => ({
|
|
1608
1651
|
selector: s.selector,
|
|
1609
|
-
transitions: [...s.tokens.filter((n) => names.includes(n)), ...s.properties ?? []]
|
|
1652
|
+
transitions: [...s.tokens.filter((n) => names.includes(n) && written.has(n)), ...s.properties ?? []]
|
|
1610
1653
|
})).filter((s) => s.transitions.length > 0).map((s) => `${s.selector}{transition:${s.transitions.map(timing).join(",")}}`).join("")}`;
|
|
1611
1654
|
}
|
|
1612
1655
|
/**
|
|
@@ -1777,6 +1820,7 @@ window.__ModuleLoader__.load({
|
|
|
1777
1820
|
"--dsw-alias-interactive-bg-hover": rgba(h(0), s(0), l(-.3), .08),
|
|
1778
1821
|
"--dsw-alias-interactive-bg-active": rgba(h(0), s(0), l(-.3), .14),
|
|
1779
1822
|
"--dsw-alias-markdown-code-block": hsl(h(0), s(-.1), l(-.03)),
|
|
1823
|
+
"--dsw-alias-markdown-code-block-banner": hsl(h(0), s(-.1), l(-.06)),
|
|
1780
1824
|
"--dsw-alias-markdown-inline-code": hsl(h(0), s(-.08), l(.04)),
|
|
1781
1825
|
"--dsw-specific-sidebar-fill": hsl(h(0), s(-.1), l(-.03)),
|
|
1782
1826
|
"--dsw-specific-sidebar-nav-item-active": hsl(h(0), s(-.08), l(.05)),
|
|
@@ -2002,7 +2046,11 @@ window.__ModuleLoader__.load({
|
|
|
2002
2046
|
/* ── shell: nav rail + page body ─────────────────────────────────────────── */
|
|
2003
2047
|
.dab-shell{display:grid;grid-template-columns:158px minmax(0,1fr);gap:26px;align-items:start;padding-bottom:8px;width:100%;max-width:980px;margin:0 auto}
|
|
2004
2048
|
.dab-nav{position:sticky;top:0;display:flex;flex-direction:column;gap:18px}
|
|
2005
|
-
|
|
2049
|
+
/* The brand is an <a> (it opens the repository): an anchor brings its own colour
|
|
2050
|
+
and underline, so both are reset here rather than left to the host's link
|
|
2051
|
+
styles, which would repaint the tile's label. */
|
|
2052
|
+
.dab-brand{display:flex;align-items:center;gap:10px;padding:2px 6px;color:inherit;text-decoration:none}
|
|
2053
|
+
.dab-brand:hover .dab-brand-name{color:var(--dsw-alias-brand-primary)}
|
|
2006
2054
|
.dab-brand-tile{width:30px;height:30px;flex:none;border-radius:9px;display:grid;place-items:center;color:var(--dsw-alias-label-primary-inverted);background:var(--dsw-alias-brand-primary);box-shadow:0 4px 14px -4px var(--dsw-alias-brand-primary)}
|
|
2007
2055
|
.dab-brand-name{font-size:13px;font-weight:650;letter-spacing:.01em;line-height:1.25}
|
|
2008
2056
|
.dab-brand-tag{font-size:9px;letter-spacing:.16em;font-weight:600;color:var(--dsw-alias-label-quaternary,var(--dsw-alias-label-tertiary));text-transform:uppercase}
|
|
@@ -2093,6 +2141,11 @@ window.__ModuleLoader__.load({
|
|
|
2093
2141
|
.dab-chip-row{display:flex;flex-wrap:wrap;gap:8px}
|
|
2094
2142
|
.dab-chip{height:30px;padding:0 14px;border-radius:99px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary));font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all .22s ease}
|
|
2095
2143
|
.dab-chip:hover{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary)}
|
|
2144
|
+
/* A mode chip on a rule with no picture: there is nothing to lay out. It reads
|
|
2145
|
+
as unavailable rather than as an active choice, and the hover that would
|
|
2146
|
+
promise otherwise is dropped with it. */
|
|
2147
|
+
.dab-chip:disabled{opacity:.45;cursor:not-allowed}
|
|
2148
|
+
.dab-chip:disabled:hover{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
2096
2149
|
.dab-chip.is-active{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary-inverted);border-color:transparent}
|
|
2097
2150
|
|
|
2098
2151
|
/* ── sliders ─────────────────────────────────────────────────────────────── */
|
|
@@ -2151,6 +2204,10 @@ window.__ModuleLoader__.load({
|
|
|
2151
2204
|
.dab-profile-desc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.55;margin:5px 0 15px}
|
|
2152
2205
|
.dab-footer{margin-top:6px;padding:14px 4px 0;border-top:1px solid var(--dsw-alias-border-l2);display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
2153
2206
|
.dab-footer-mono{font-family:var(--dab-mono);letter-spacing:.02em}
|
|
2207
|
+
/* Same reset as the brand: the package name links to the repository, and the
|
|
2208
|
+
underline appears on hover where it reads as an affordance instead of chrome. */
|
|
2209
|
+
.dab-footer a{color:inherit;text-decoration:none}
|
|
2210
|
+
.dab-footer a:hover{color:var(--dsw-alias-brand-primary);text-decoration:underline}
|
|
2154
2211
|
|
|
2155
2212
|
/* ── toast ───────────────────────────────────────────────────────────────── */
|
|
2156
2213
|
.dab-toast{position:fixed;left:50%;bottom:30px;transform:translateX(-50%);display:flex;align-items:center;gap:8px;height:38px;padding:0 16px;border-radius:99px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 10px 30px -8px rgba(0,0,0,.38);font-size:12.5px;z-index:10001;animation:dab-toast-in .32s cubic-bezier(.22,1,.36,1) both}
|
|
@@ -2425,6 +2482,10 @@ window.__ModuleLoader__.load({
|
|
|
2425
2482
|
"--dsw-menu-surface-fill"
|
|
2426
2483
|
]
|
|
2427
2484
|
},
|
|
2485
|
+
{
|
|
2486
|
+
part: "code",
|
|
2487
|
+
names: ["--dsw-alias-markdown-code-block", "--dsw-alias-markdown-code-block-banner"]
|
|
2488
|
+
},
|
|
2428
2489
|
{
|
|
2429
2490
|
part: "input",
|
|
2430
2491
|
names: ["--dsw-specific-input-major"]
|
|
@@ -2438,7 +2499,9 @@ window.__ModuleLoader__.load({
|
|
|
2438
2499
|
"--dsw-alias-bg-layer-3": "--dsh-any-op-card-3",
|
|
2439
2500
|
"--dsw-specific-input-major": "--dsh-any-op-input",
|
|
2440
2501
|
"--dsw-specific-menu": "--dsh-any-op-menu",
|
|
2441
|
-
"--dsw-menu-surface-fill": "--dsh-any-op-menu"
|
|
2502
|
+
"--dsw-menu-surface-fill": "--dsh-any-op-menu",
|
|
2503
|
+
"--dsw-alias-markdown-code-block": "--dsh-any-op-code",
|
|
2504
|
+
"--dsw-alias-markdown-code-block-banner": "--dsh-any-op-code-banner"
|
|
2442
2505
|
};
|
|
2443
2506
|
let baseTokenKey = "";
|
|
2444
2507
|
const HOST_SURFACE_TOKENS = Object.keys(OPACITY_VARS);
|
|
@@ -2663,7 +2726,7 @@ window.__ModuleLoader__.load({
|
|
|
2663
2726
|
const scheme = own ? forceDark ? "dark" : "light" : resolveHostDark() ? "dark" : "light";
|
|
2664
2727
|
const fadeable = paletteChanged && paletteFadeAllowed(paletteScheme, scheme);
|
|
2665
2728
|
lastFadeable = fadeable;
|
|
2666
|
-
applyPaletteFade(fadeable);
|
|
2729
|
+
applyPaletteFade(fadeable, Object.keys(tokens));
|
|
2667
2730
|
if (paletteChanged) {
|
|
2668
2731
|
paletteScheme = scheme;
|
|
2669
2732
|
if (own) {
|
|
@@ -2675,7 +2738,7 @@ window.__ModuleLoader__.load({
|
|
|
2675
2738
|
const opVar = OPACITY_VARS[name];
|
|
2676
2739
|
decls.push(`${name}:${opVar !== void 0 ? `var(${opVar})` : value}!important`);
|
|
2677
2740
|
}
|
|
2678
|
-
ensureTokenStyle().textContent =
|
|
2741
|
+
ensureTokenStyle().textContent = `:root,body{${decls.join(";")}}`;
|
|
2679
2742
|
if (own) {
|
|
2680
2743
|
for (const name of appliedTokenNames) document.body.style.removeProperty(name);
|
|
2681
2744
|
appliedTokenNames = Object.keys(tokens);
|
|
@@ -2745,14 +2808,26 @@ window.__ModuleLoader__.load({
|
|
|
2745
2808
|
"--dsw-alias-bg-layer-2",
|
|
2746
2809
|
"--dsw-alias-bg-layer-3"
|
|
2747
2810
|
];
|
|
2748
|
-
/** The token names
|
|
2749
|
-
*
|
|
2750
|
-
*
|
|
2811
|
+
/** The token names a palette CAN carry — deliberately the widest branch.
|
|
2812
|
+
*
|
|
2813
|
+
* `genTokens(220, 0.55, 0.25)` resolves to the DARK branch (l = 0.25), and that
|
|
2814
|
+
* branch is the superset: the light one emits roughly thirty fewer aliases (no
|
|
2815
|
+
* bubble, selector, tip, tooltip/toast, toolbar and markdown state tokens). This
|
|
2816
|
+
* list is therefore what the REGISTRATIONS are emitted for — a later light→dark
|
|
2817
|
+
* change has to find the dark names already registered — and NOT the transition
|
|
2818
|
+
* list, which is narrowed per apply to the palette in force (`written` in
|
|
2819
|
+
* ./palette-fade). Assuming the two branches carry the same set is exactly the
|
|
2820
|
+
* mistake that let the fade animate host-owned tokens: a name only the dark
|
|
2821
|
+
* branch declares is, under a light palette, the HOST's value.
|
|
2822
|
+
*
|
|
2823
|
+
* An unknown name is only registered when the host can answer for it (see
|
|
2824
|
+
* `fadeInitials`). */
|
|
2751
2825
|
function fadeTokenNames() {
|
|
2752
2826
|
return Object.keys(genTokens(220, .55, .25).tokens);
|
|
2753
2827
|
}
|
|
2754
2828
|
/** One `transition` list per element that DECLARES palette tokens, each carrying
|
|
2755
|
-
* only the tokens that element actually declares
|
|
2829
|
+
* only the tokens that element actually declares — and, at emission, only those
|
|
2830
|
+
* the palette in force writes (see `written`).
|
|
2756
2831
|
*
|
|
2757
2832
|
* Two of the entries carry a STANDARD property instead, because those surfaces
|
|
2758
2833
|
* are not painted from a registered token: the settings dialog's plate and the
|
|
@@ -2764,6 +2839,10 @@ window.__ModuleLoader__.load({
|
|
|
2764
2839
|
* re-declares IS one, naming the token is what reaches it. */
|
|
2765
2840
|
function fadeScopes() {
|
|
2766
2841
|
return [
|
|
2842
|
+
{
|
|
2843
|
+
selector: ":root",
|
|
2844
|
+
tokens: fadeTokenNames()
|
|
2845
|
+
},
|
|
2767
2846
|
{
|
|
2768
2847
|
selector: "body",
|
|
2769
2848
|
tokens: fadeTokenNames()
|
|
@@ -2851,12 +2930,21 @@ window.__ModuleLoader__.load({
|
|
|
2851
2930
|
*
|
|
2852
2931
|
* `animate` is false on every pass that only moves an alpha (a slider drag): the
|
|
2853
2932
|
* tokens are rewritten there too, and a live transition would make the surface
|
|
2854
|
-
* lag behind the pointer.
|
|
2855
|
-
|
|
2933
|
+
* lag behind the pointer.
|
|
2934
|
+
*
|
|
2935
|
+
* `written` is the token set of the palette IN FORCE — `Object.keys(tokens)` of
|
|
2936
|
+
* the very block this pass is about to write — and only those may carry a
|
|
2937
|
+
* transition: a name the palette does not write still belongs to the host, and a
|
|
2938
|
+
* transition on it animates the host's own scheme change at this plugin's
|
|
2939
|
+
* duration (see ./palette-fade for the report that shape produced). The
|
|
2940
|
+
* registrations stay on the full set, so a later palette of the other branch
|
|
2941
|
+
* still has them in place. */
|
|
2942
|
+
function applyPaletteFade(animate, written) {
|
|
2856
2943
|
const initials = fadeInitialsOf(fadeTokenNames());
|
|
2857
2944
|
const css = paletteFadeCss({
|
|
2858
2945
|
scopes: fadeScopes(),
|
|
2859
2946
|
initial: initials,
|
|
2947
|
+
written,
|
|
2860
2948
|
durationMs: animate ? paletteFadeMs() : 0,
|
|
2861
2949
|
easing: rTransition().easing
|
|
2862
2950
|
});
|
|
@@ -4069,6 +4157,12 @@ window.__ModuleLoader__.load({
|
|
|
4069
4157
|
rx: "2"
|
|
4070
4158
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 6.6v2M7 8h2" })]
|
|
4071
4159
|
});
|
|
4160
|
+
/** Angle brackets: the code-block surface on the Interface page. */
|
|
4161
|
+
const CodeIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
4162
|
+
size,
|
|
4163
|
+
className,
|
|
4164
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.7 4.3 2.4 8l3.3 3.7M10.3 4.3 13.6 8l-3.3 3.7" })
|
|
4165
|
+
});
|
|
4072
4166
|
//#endregion
|
|
4073
4167
|
//#region src/client/components/ErrorBoundary.tsx
|
|
4074
4168
|
/**
|
|
@@ -4139,6 +4233,19 @@ window.__ModuleLoader__.load({
|
|
|
4139
4233
|
return target ? (0, react_dom.createPortal)(children, target) : null;
|
|
4140
4234
|
}
|
|
4141
4235
|
//#endregion
|
|
4236
|
+
//#region src/client/repo.ts
|
|
4237
|
+
/**
|
|
4238
|
+
* The project's public repository — the ONE address the shell links to.
|
|
4239
|
+
*
|
|
4240
|
+
* Both links the interface ships (the brand in the header, the package name in
|
|
4241
|
+
* the profile footer) read this constant: two literals would drift the day the
|
|
4242
|
+
* repository moves, and a link that points at the wrong place is indistinguishable
|
|
4243
|
+
* from a link that works until someone clicks it. `scripts/ui-strings-check.ts`
|
|
4244
|
+
* holds it against `package.json`'s `repository`, which is what npm and the
|
|
4245
|
+
* plugin market read.
|
|
4246
|
+
*/
|
|
4247
|
+
const REPO_URL = "https://github.com/HarmlessFunny/dsh-background-by-model";
|
|
4248
|
+
//#endregion
|
|
4142
4249
|
//#region src/client/components/LiveSlider.tsx
|
|
4143
4250
|
/**
|
|
4144
4251
|
* Zero-lag slider: the thumb and value label update through DOM refs while
|
|
@@ -4235,6 +4342,12 @@ window.__ModuleLoader__.load({
|
|
|
4235
4342
|
labelKey: "uiOpacityCard",
|
|
4236
4343
|
Icon: ChatIcon
|
|
4237
4344
|
},
|
|
4345
|
+
{
|
|
4346
|
+
opKey: "code",
|
|
4347
|
+
labelKey: "uiOpacityCode",
|
|
4348
|
+
Icon: CodeIcon,
|
|
4349
|
+
noBlur: true
|
|
4350
|
+
},
|
|
4238
4351
|
{
|
|
4239
4352
|
opKey: "input",
|
|
4240
4353
|
labelKey: "uiOpacityInput",
|
|
@@ -4278,9 +4391,9 @@ window.__ModuleLoader__.load({
|
|
|
4278
4391
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4279
4392
|
className: "dab-grid-parts",
|
|
4280
4393
|
children: PARTS.map((part, i) => {
|
|
4281
|
-
const { labelKey, Icon, isSettings, isChat, isTrajectory, isRightbar } = part;
|
|
4394
|
+
const { labelKey, Icon, isSettings, isChat, isTrajectory, isRightbar, noBlur } = part;
|
|
4282
4395
|
const opKey = part.opKey;
|
|
4283
|
-
const blurKey = isChat ? "chat" : isTrajectory ? "trajectory" : isRightbar ? "rightbar" : isSettings ? "settings" : opKey;
|
|
4396
|
+
const blurKey = isChat ? "chat" : isTrajectory ? "trajectory" : isRightbar ? "rightbar" : isSettings ? "settings" : noBlur ? null : opKey;
|
|
4284
4397
|
const opacity = isChat ? rChatTextOpacity() : isTrajectory ? rTrajectoryOpacity() : isRightbar ? rRightbarOpacity() : isSettings ? rSop() : rOps()[opKey];
|
|
4285
4398
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4286
4399
|
className: "dab-card dab-card-hover dab-rise",
|
|
@@ -4351,7 +4464,7 @@ window.__ModuleLoader__.load({
|
|
|
4351
4464
|
}
|
|
4352
4465
|
}
|
|
4353
4466
|
}),
|
|
4354
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
4467
|
+
blurKey !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
4355
4468
|
label: t("uiBlur"),
|
|
4356
4469
|
min: 0,
|
|
4357
4470
|
max: 60,
|
|
@@ -4372,7 +4485,7 @@ window.__ModuleLoader__.load({
|
|
|
4372
4485
|
}
|
|
4373
4486
|
})
|
|
4374
4487
|
]
|
|
4375
|
-
}, blurKey);
|
|
4488
|
+
}, blurKey ?? opKey);
|
|
4376
4489
|
})
|
|
4377
4490
|
})] });
|
|
4378
4491
|
}
|
|
@@ -5450,6 +5563,7 @@ window.__ModuleLoader__.load({
|
|
|
5450
5563
|
const rotatable = rule.images.length >= 2;
|
|
5451
5564
|
const preset = ROTATE_PRESETS.find((x) => x.ms === rule.rotate.intervalMs);
|
|
5452
5565
|
const selSlot = image?.slot ?? null;
|
|
5566
|
+
const bgMode = image?.bgMode ?? "fit";
|
|
5453
5567
|
/** How far the pointer must travel before a click turns into a drag. */
|
|
5454
5568
|
const DRAG_SLOP = 5;
|
|
5455
5569
|
const tileDown = (i, slot) => (e) => {
|
|
@@ -5932,12 +6046,17 @@ window.__ModuleLoader__.load({
|
|
|
5932
6046
|
children: t("ruleLayout")
|
|
5933
6047
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5934
6048
|
className: "dab-chip-row",
|
|
6049
|
+
style: { marginTop: 8 },
|
|
5935
6050
|
children: [BG_MODES.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5936
6051
|
type: "button",
|
|
5937
|
-
className: `dab-chip${
|
|
5938
|
-
|
|
6052
|
+
className: `dab-chip${bgMode === m.mode ? " is-active" : ""}`,
|
|
6053
|
+
disabled: image === void 0,
|
|
6054
|
+
title: image === void 0 ? t("ruleLayoutNoImage") : void 0,
|
|
6055
|
+
onClick: () => {
|
|
6056
|
+
if (image !== void 0) p.setImageMode(rule.id, image.slot, m.mode);
|
|
6057
|
+
},
|
|
5939
6058
|
children: t(m.key)
|
|
5940
|
-
}, m.mode)), url !== null &&
|
|
6059
|
+
}, m.mode)), url !== null && bgMode === "fit" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5941
6060
|
type: "button",
|
|
5942
6061
|
className: "dab-btn",
|
|
5943
6062
|
onClick: () => setEditorOpen(true),
|
|
@@ -6270,12 +6389,7 @@ window.__ModuleLoader__.load({
|
|
|
6270
6389
|
}, easing))
|
|
6271
6390
|
})] }),
|
|
6272
6391
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6273
|
-
className: "dab-swatch-title",
|
|
6274
6392
|
style: { marginTop: 12 },
|
|
6275
|
-
children: t("trDuration")
|
|
6276
|
-
}),
|
|
6277
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6278
|
-
style: { marginTop: 2 },
|
|
6279
6393
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LiveSlider, {
|
|
6280
6394
|
label: t("trDuration"),
|
|
6281
6395
|
min: 0,
|
|
@@ -6393,8 +6507,12 @@ window.__ModuleLoader__.load({
|
|
|
6393
6507
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
6394
6508
|
className: "dab-footer dab-rise",
|
|
6395
6509
|
style: { "--d": 4 },
|
|
6396
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
6510
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
6397
6511
|
className: "dab-footer-mono",
|
|
6512
|
+
href: REPO_URL,
|
|
6513
|
+
target: "_blank",
|
|
6514
|
+
rel: "noreferrer noopener",
|
|
6515
|
+
title: t("repoLink"),
|
|
6398
6516
|
children: "dsh-background-by-model"
|
|
6399
6517
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("footerTag") })]
|
|
6400
6518
|
})
|
|
@@ -6458,8 +6576,12 @@ window.__ModuleLoader__.load({
|
|
|
6458
6576
|
className: "dab-shell",
|
|
6459
6577
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
|
|
6460
6578
|
className: "dab-nav",
|
|
6461
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
6579
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
|
|
6462
6580
|
className: "dab-brand",
|
|
6581
|
+
href: REPO_URL,
|
|
6582
|
+
target: "_blank",
|
|
6583
|
+
rel: "noreferrer noopener",
|
|
6584
|
+
title: t("repoLink"),
|
|
6463
6585
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6464
6586
|
className: "dab-brand-tile",
|
|
6465
6587
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SunIcon, { size: 15 })
|
|
@@ -6519,10 +6641,21 @@ window.__ModuleLoader__.load({
|
|
|
6519
6641
|
initRpc((endpoint, payload) => ctx.connection.rpc.call(RPC_CHANNEL, endpoint, payload).then((res) => res));
|
|
6520
6642
|
let customDispose = null;
|
|
6521
6643
|
let skinTimer = null;
|
|
6644
|
+
/** Colour scheme of the skin this plugin has registered, or null when none is. */
|
|
6645
|
+
let skinScheme = null;
|
|
6646
|
+
/** Scheme the host currently holds for our theme id; null when it is not there. */
|
|
6647
|
+
const registeredScheme = () => {
|
|
6648
|
+
const theme = ctx.theme.getTheme().themes.find((t) => t.id === CUSTOM_ID);
|
|
6649
|
+
return theme === void 0 ? null : theme.colorScheme;
|
|
6650
|
+
};
|
|
6522
6651
|
const registerCustom = (h, s, l) => {
|
|
6652
|
+
const { colorScheme, tokens } = genTokens(h, s, l);
|
|
6653
|
+
if (skinScheme === colorScheme && registeredScheme() !== null) {
|
|
6654
|
+
if (ctx.theme.getTheme().preference !== CUSTOM_ID) ctx.theme.setTheme(CUSTOM_ID);
|
|
6655
|
+
return;
|
|
6656
|
+
}
|
|
6523
6657
|
customDispose?.();
|
|
6524
6658
|
try {
|
|
6525
|
-
const { colorScheme, tokens } = genTokens(h, s, l);
|
|
6526
6659
|
customDispose = ctx.theme.register({
|
|
6527
6660
|
id: CUSTOM_ID,
|
|
6528
6661
|
colorScheme,
|
|
@@ -6531,12 +6664,14 @@ window.__ModuleLoader__.load({
|
|
|
6531
6664
|
} catch {
|
|
6532
6665
|
customDispose = null;
|
|
6533
6666
|
}
|
|
6667
|
+
skinScheme = registeredScheme() ?? colorScheme;
|
|
6534
6668
|
if (ctx.theme.getTheme().themes.some((t) => t.id === CUSTOM_ID)) ctx.theme.setTheme(CUSTOM_ID);
|
|
6535
6669
|
};
|
|
6536
6670
|
/** A rule without a saved color means "follow the system theme". */
|
|
6537
6671
|
const dropCustom = () => {
|
|
6538
6672
|
customDispose?.();
|
|
6539
6673
|
customDispose = null;
|
|
6674
|
+
skinScheme = null;
|
|
6540
6675
|
try {
|
|
6541
6676
|
if (ctx.theme.getTheme().preference === CUSTOM_ID) ctx.theme.setTheme("system");
|
|
6542
6677
|
} catch {}
|
|
@@ -7091,6 +7226,7 @@ window.__ModuleLoader__.load({
|
|
|
7091
7226
|
const slot = nextSlot();
|
|
7092
7227
|
const image = {
|
|
7093
7228
|
slot,
|
|
7229
|
+
bgMode: "fit",
|
|
7094
7230
|
bgState: { ...DEFAULT_BG_STATE },
|
|
7095
7231
|
color: null
|
|
7096
7232
|
};
|
|
@@ -7111,6 +7247,7 @@ window.__ModuleLoader__.load({
|
|
|
7111
7247
|
if (idx < 0) return;
|
|
7112
7248
|
const winner = winnerId();
|
|
7113
7249
|
rule.images.splice(idx, 1);
|
|
7250
|
+
if (rule.images.length < 2 && rule.rotate.enabled) patchRotation(rule, { enabled: false });
|
|
7114
7251
|
if (id === activeRuleId) setRotIndex(0);
|
|
7115
7252
|
rulesRev++;
|
|
7116
7253
|
persistConfig();
|
|
@@ -7151,6 +7288,29 @@ window.__ModuleLoader__.load({
|
|
|
7151
7288
|
saveConfig();
|
|
7152
7289
|
repaintIfMoved(id, winner);
|
|
7153
7290
|
},
|
|
7291
|
+
/**
|
|
7292
|
+
* Store ONE image's layout mode. Addressed by slot like `setImageFraming`,
|
|
7293
|
+
* for the same reason: how a picture meets the viewport is a property of the
|
|
7294
|
+
* picture, and 适应 on a tall screenshot says nothing about the landscape
|
|
7295
|
+
* photo beside it in the rotation.
|
|
7296
|
+
*
|
|
7297
|
+
* A mode is a placement, not a paint: it changes nothing about WHICH image is
|
|
7298
|
+
* on screen, only how it is laid out. So the patch is written, sanitized, and
|
|
7299
|
+
* the layer repainted through the same `repaintIfMoved` the framing commit
|
|
7300
|
+
* uses — that call repaints the current image whether or not the winner
|
|
7301
|
+
* moved, which is what makes the chips feel live.
|
|
7302
|
+
*/
|
|
7303
|
+
setImageMode: (id, slot, mode) => {
|
|
7304
|
+
const rule = ruleById(id);
|
|
7305
|
+
const image = rule?.images.find((i) => i.slot === slot);
|
|
7306
|
+
if (rule === null || image === void 0) return;
|
|
7307
|
+
const winner = winnerId();
|
|
7308
|
+
image.bgMode = mode;
|
|
7309
|
+
normalizeRuleInPlace(rule);
|
|
7310
|
+
rulesRev++;
|
|
7311
|
+
saveConfig();
|
|
7312
|
+
repaintIfMoved(id, winner);
|
|
7313
|
+
},
|
|
7154
7314
|
setRuleRotation: (id, patch) => {
|
|
7155
7315
|
const rule = ruleById(id);
|
|
7156
7316
|
if (rule === null) return;
|
|
@@ -7205,6 +7365,7 @@ window.__ModuleLoader__.load({
|
|
|
7205
7365
|
const winner = winnerId();
|
|
7206
7366
|
rule.images.push({
|
|
7207
7367
|
slot,
|
|
7368
|
+
bgMode: "fit",
|
|
7208
7369
|
bgState: { ...DEFAULT_BG_STATE },
|
|
7209
7370
|
color: null
|
|
7210
7371
|
});
|