dsh-mpkg-wallpaper 3.15.6 → 3.15.8
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/lib/client.js +187 -83
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1479,6 +1479,17 @@ function sectionSigNow() {
|
|
|
1479
1479
|
} catch (e) {}
|
|
1480
1480
|
return "";
|
|
1481
1481
|
}
|
|
1482
|
+
/* ②(2026-10-05 真机残留复核) `sceneKey` 是**粘性源字段**之一:换档/自愈之后它可能还挂着
|
|
1483
|
+
上一个场景的值(真机现场:`converted="mp4"` 的 mpkg 档里仍留着 `scene|probe|…/scene.pkg`)。
|
|
1484
|
+
今天它无害(挂载只在 `converted === "scene"` 时看它),但它是"哪天被别的分支读到就翻盘"的
|
|
1485
|
+
典型地雷 ⇒ 自愈写回时**显式删掉**(`null` 是唯一会被 sticky 护栏尊重的删除语义)。 */
|
|
1486
|
+
function mpwStripForeignSceneKey(sec) {
|
|
1487
|
+
try {
|
|
1488
|
+
const out = Object.assign({}, sec || {});
|
|
1489
|
+
if (String(out.converted || "") !== "scene" && out.sceneKey) out.sceneKey = null;
|
|
1490
|
+
return out;
|
|
1491
|
+
} catch (e) { return sec }
|
|
1492
|
+
}
|
|
1482
1493
|
/** 档里是不是"只剩 image token、身份/类型全空"(真机那条半残档的形状)。 */
|
|
1483
1494
|
function mpwIdentityMissing(s) {
|
|
1484
1495
|
try {
|
|
@@ -2553,7 +2564,7 @@ function mpwThumbFail(el, why) {
|
|
|
2553
2564
|
if (MPW_HEAL.last && MPW_HEAL.last.ok && MPW_HEAL.last.key === key && mpwVal(cur, "image") !== void 0) return false;
|
|
2554
2565
|
const bak = mpwBackupSrc();
|
|
2555
2566
|
if (bak && MPW_SRC_KEYS.some((sk) => typeof bak[sk] === "string" && bak[sk])) {
|
|
2556
|
-
const next = mpwMergeSection(cur, bak);
|
|
2567
|
+
const next = mpwStripForeignSceneKey(mpwMergeSection(cur, bak));
|
|
2557
2568
|
writeSection(next, true);
|
|
2558
2569
|
changed = true;
|
|
2559
2570
|
MPW_HEAL.attempted++;
|
|
@@ -2573,7 +2584,7 @@ function mpwThumbFail(el, why) {
|
|
|
2573
2584
|
try { nowKey = String(readSection().mpkgKey || "") } catch {}
|
|
2574
2585
|
if (nowKey !== key) { try { console.warn("[dsh-mpkg-wallpaper] 自愈完成时壁纸已切换,丢弃迟到结果:", key); } catch {} return }
|
|
2575
2586
|
if (found) {
|
|
2576
|
-
writeSection(mpwMergeSection(readSection(), found), true);
|
|
2587
|
+
writeSection(mpwStripForeignSceneKey(mpwMergeSection(readSection(), found)), true);
|
|
2577
2588
|
MPW_HEAL.healed++;
|
|
2578
2589
|
MPW_HEAL.last = { key, ok: true, from: "derive", at: Date.now() };
|
|
2579
2590
|
mpwTrace("settings:heal", { key, from: "derive" });
|
|
@@ -11811,6 +11822,7 @@ function mpwThumbFail(el, why) {
|
|
|
11811
11822
|
keyFromImageToken: (img) => mpwKeyFromImageToken(img),
|
|
11812
11823
|
healState: () => { try { return Object.assign({}, MPW_HEAL) } catch (e) { return {} } },
|
|
11813
11824
|
identityMissing: (s) => mpwIdentityMissing(s || readSection()),
|
|
11825
|
+
stripForeignSceneKey: (s) => mpwStripForeignSceneKey(s || readSection()),
|
|
11814
11826
|
deriveFromKey: (key) => mpwDeriveSourceFromKey(key || readSection().mpkgKey),
|
|
11815
11827
|
heal: () => { try { return mpwHealHalfArchive() } catch (e) { return String(e && e.message || e) } },
|
|
11816
11828
|
healState: () => Object.assign({}, MPW_HEAL, { last: MPW_HEAL.last ? Object.assign({}, MPW_HEAL.last) : null, sig: mpwHealSig }),
|
|
@@ -17722,6 +17734,12 @@ html body [data-sidebar-right-panel], html body [data-dockkit-pane], html body [
|
|
|
17722
17734
|
z-index: 12 !important;
|
|
17723
17735
|
}
|
|
17724
17736
|
html body [class*="sidebarCol"]:not([data-sidebar-right-panel]) { z-index: 1 !important; }
|
|
17737
|
+
/* ②(2026-10-05 真机:"设置 → Agent 预设 → 查看,弹出的框被压在设置底下")
|
|
17738
|
+
根因:宿主把设置弹层渲染在**左栏子树里**(真机读数 VOzbGW_overlay[z=2000] 是 pI_x6G_sidebarCol
|
|
17739
|
+
的后代),而上面那条把左栏钉成 z-index:1 ⇒ 连同里面的弹窗一起被困在 z=1 的层叠上下文里,
|
|
17740
|
+
被别的 z=2000 层盖住。⇒ 左栏子树里出现**可见的**宿主弹层/对话框时给左栏打抬升标记;
|
|
17741
|
+
弹层关掉即摘(收纳态"左栏盖右栏"的老问题不受影响)。 */
|
|
17742
|
+
html body [data-mpw-side-lift] { z-index: 2500 !important; }
|
|
17725
17743
|
[role="menu"], [role="listbox"], [role="dialog"], [role="tooltip"],
|
|
17726
17744
|
[class*="_popover"], [class*="_dropdown"], [class*="_selectMenu"], [class*="_menu_"],
|
|
17727
17745
|
[data-radix-popper-content-wrapper], [data-dsh-popover] {
|
|
@@ -18102,6 +18120,10 @@ ${popSurfaceSel} {
|
|
|
18102
18120
|
有宿主玻璃的弹层(标题栏子代理展开框 / VS Code 菜单 / 模型选择…)一个像素都不动。
|
|
18103
18121
|
独立 token:--mpw-pop-surface(-dark)(不再共用面板表面 token --mpw-surface-pop)。 */
|
|
18104
18122
|
html body [data-mpw-pop-bg] { background-color: var(--mpw-pop-surface) !important; }
|
|
18123
|
+
/* ②(2026-10-05) 弹层处在一个"自己有底的玻璃祖先"里面(backdrop root 被截断 ⇒ 我们糊不到页面背后):
|
|
18124
|
+
与其"半透明却没模糊"(真机被读成"只有白色滤镜"),不如**做实在**——表面不透明度抬到 88%,
|
|
18125
|
+
并把 backdrop-filter 撤掉(白算一遍)。注意:只动我们自己的标记属性,宿主元素一个都不碰。 */
|
|
18126
|
+
html body [data-mpw-pop-trunc] { background-color: color-mix(in srgb, var(--dsw-specific-sidebar-fill) 88%, transparent) !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
|
|
18105
18127
|
html body[data-ds-dark-theme] [data-mpw-pop-bg] { background-color: var(--mpw-pop-surface-dark) !important; }
|
|
18106
18128
|
[role="menu"] [class*="card"],
|
|
18107
18129
|
[role="tooltip"] [class*="card"] {
|
|
@@ -24666,11 +24688,37 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
24666
24688
|
getAnimations 只读动画表、不触发布局,闸门本身廉价。 */
|
|
24667
24689
|
let mpwHeavyRecheckTimer = 0;
|
|
24668
24690
|
const mpwHeavyPending = new Map();
|
|
24691
|
+
/* ②(2026-10-05 真机回归修复) 闸门的判据必须是"**真的在跑、而且会跑完**的动画":
|
|
24692
|
+
旧写法 `document.getAnimations().length > 0` 把**任何**留在动画表里的条目都算成"在动画",
|
|
24693
|
+
包括 `playState === 'finished'`(CSS 动画带 fill: forwards 会一直留着)与
|
|
24694
|
+
`iterations === Infinity`(光标闪烁/转圈这类**永不结束**的动画)⇒ 真机上只要页面里有
|
|
24695
|
+
一个循环动画,闸门就**永久**登记、永不放行 ⇒ 弹层兜底底标记/右栏最外层标记/侧栏抑制
|
|
24696
|
+
这些活全被饿死(真机读数:四个弹层都不再带 data-mpw-pop-bg,只剩宿主自己的不透明白底
|
|
24697
|
+
+ 我们的 blur = "白色滤镜、没有玻璃")。
|
|
24698
|
+
现在:① 只认 `running`;② 跳过无限循环动画(它们不会结束,不能拿它们当"稍后再跑"的理由);
|
|
24699
|
+
③ 兜底——连续推迟超过 MPW_HEAVY_MAX_DEFER 次就**无条件放行一次**(宁可多跑一次重活,
|
|
24700
|
+
也不能让功能被永久饿死)。 */
|
|
24701
|
+
const MPW_HEAVY_MAX_DEFER = 3;
|
|
24702
|
+
let mpwHeavyDeferStreak = 0;
|
|
24669
24703
|
function mpwHeavyAnimRunning() {
|
|
24670
|
-
try {
|
|
24704
|
+
try {
|
|
24705
|
+
if (!document.getAnimations) return false;
|
|
24706
|
+
const list = document.getAnimations();
|
|
24707
|
+
for (const a of list) {
|
|
24708
|
+
try {
|
|
24709
|
+
if (!a || a.playState !== "running") continue; // finished/paused/idle 一律不算
|
|
24710
|
+
const t = a.effect && a.effect.getTiming ? a.effect.getTiming() : null;
|
|
24711
|
+
if (t && t.iterations === Infinity) continue; // 永不结束的循环动画:不能当等待理由
|
|
24712
|
+
return true;
|
|
24713
|
+
} catch (e) { /* 单个动画读不到就跳过它 */ }
|
|
24714
|
+
}
|
|
24715
|
+
return false;
|
|
24716
|
+
} catch (e) { return false }
|
|
24671
24717
|
}
|
|
24672
24718
|
function mpwHeavyGate(name, fn) {
|
|
24673
|
-
if (!mpwHeavyAnimRunning()) return true;
|
|
24719
|
+
if (!mpwHeavyAnimRunning()) { mpwHeavyDeferStreak = 0; return true; }
|
|
24720
|
+
if (mpwHeavyDeferStreak >= MPW_HEAVY_MAX_DEFER) { mpwHeavyDeferStreak = 0; return true; }
|
|
24721
|
+
mpwHeavyDeferStreak++;
|
|
24674
24722
|
mpwHeavyPending.set(name, fn);
|
|
24675
24723
|
if (!mpwHeavyRecheckTimer) {
|
|
24676
24724
|
mpwHeavyRecheckTimer = setTimeout(() => {
|
|
@@ -24803,31 +24851,6 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
24803
24851
|
return r.width * r.height <= 0.4 * vw * vh && r.height <= vh * 0.9;
|
|
24804
24852
|
} catch (e) { return false; }
|
|
24805
24853
|
}
|
|
24806
|
-
function mpwTagPopoverBg() {
|
|
24807
|
-
let list = null;
|
|
24808
|
-
try { list = document.querySelectorAll(MPW_POP_TAG_SEL); } catch (e) { return; }
|
|
24809
|
-
if (!list) return;
|
|
24810
|
-
for (let i = 0; i < list.length; i++) {
|
|
24811
|
-
const el = list[i];
|
|
24812
|
-
let need = false;
|
|
24813
|
-
try {
|
|
24814
|
-
const cls = (typeof el.className === "string") ? el.className : "";
|
|
24815
|
-
if (!MPW_POP_TAG_NOT.test(cls)) {
|
|
24816
|
-
const cs = getComputedStyle(el);
|
|
24817
|
-
if (cs && cs.display !== "none" && cs.visibility !== "hidden"
|
|
24818
|
-
&& /blur\(/.test(String(cs.backdropFilter || cs.webkitBackdropFilter || ""))) {
|
|
24819
|
-
const a = mpwColorAlpha(cs.backgroundColor);
|
|
24820
|
-
if (a < 0.05) need = !mpwPopHasPaintedAncestor(el);
|
|
24821
|
-
else if (a >= 0.999) need = mpwPopIsFloating(el);
|
|
24822
|
-
}
|
|
24823
|
-
}
|
|
24824
|
-
} catch (e) { need = false; }
|
|
24825
|
-
try {
|
|
24826
|
-
if (need) { if (!el.hasAttribute("data-mpw-pop-bg")) el.setAttribute("data-mpw-pop-bg", ""); }
|
|
24827
|
-
else if (el.hasAttribute("data-mpw-pop-bg")) el.removeAttribute("data-mpw-pop-bg");
|
|
24828
|
-
} catch (e) {}
|
|
24829
|
-
}
|
|
24830
|
-
}
|
|
24831
24854
|
/* ②(2026-10-04 真机:"右侧边栏比左栏/标题栏更白")**嵌套表面只画最外层**:
|
|
24832
24855
|
右栏/dock 家族是互相嵌套的(真机读数:两个命中元素的 rect 只差 1px),把同一枚半透明表面色
|
|
24833
24856
|
刷在两层上 ⇒ 合成不透明度 1-(1-a)²(a=0.55 时 0.80)≈ 一块近白板;左栏只有一个匹配元素,
|
|
@@ -24867,62 +24890,85 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
24867
24890
|
} catch (e) { return 0 }
|
|
24868
24891
|
}
|
|
24869
24892
|
|
|
24870
|
-
|
|
24871
|
-
|
|
24872
|
-
|
|
24873
|
-
|
|
24874
|
-
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
|
|
24878
|
-
const cls = (typeof el.className === "string") ? el.className : "";
|
|
24879
|
-
if (!MPW_POP_TAG_NOT.test(cls)) {
|
|
24880
|
-
const cs = getComputedStyle(el);
|
|
24881
|
-
if (cs && cs.display !== "none" && cs.visibility !== "hidden"
|
|
24882
|
-
&& /blur\(/.test(String(cs.backdropFilter || cs.webkitBackdropFilter || ""))) {
|
|
24883
|
-
const a = mpwColorAlpha(cs.backgroundColor);
|
|
24884
|
-
if (a < 0.05) need = !mpwPopHasPaintedAncestor(el);
|
|
24885
|
-
else if (a >= 0.999) need = mpwPopIsFloating(el);
|
|
24886
|
-
}
|
|
24887
|
-
}
|
|
24888
|
-
} catch (e) { need = false; }
|
|
24889
|
-
try {
|
|
24890
|
-
if (need) { if (!el.hasAttribute("data-mpw-pop-bg")) el.setAttribute("data-mpw-pop-bg", ""); }
|
|
24891
|
-
else if (el.hasAttribute("data-mpw-pop-bg")) el.removeAttribute("data-mpw-pop-bg");
|
|
24892
|
-
} catch (e) {}
|
|
24893
|
-
}
|
|
24894
|
-
}
|
|
24895
|
-
/* ②(2026-10-04 真机:"右侧边栏比左栏/标题栏更白")**嵌套表面只画最外层**:
|
|
24896
|
-
右栏/dock 家族是互相嵌套的(真机读数:两个命中元素的 rect 只差 1px),把同一枚半透明表面色
|
|
24897
|
-
刷在两层上 ⇒ 合成不透明度 1-(1-a)²(a=0.55 时 0.80)≈ 一块近白板;左栏只有一个匹配元素,
|
|
24898
|
-
于是"左栏灰、右栏白"。这里按**结构**(有没有同样命中的祖先)给最外层打
|
|
24899
|
-
`data-mpw-rs-surface`,CSS 只认这个标记(与 `data-mpw-holds-layer` 同一手法)。
|
|
24900
|
-
幂等、无布局读取(只用 matches/parentElement),可随突变跑。 */
|
|
24901
|
-
function mpwTagSurfaceOuter() {
|
|
24893
|
+
/* ②(2026-10-05 真机:"刚展开是白色的不透明背景,过一会儿才切换")——**同步快路**:
|
|
24894
|
+
弹层插进 DOM 的那一刻就判定并打标(在同一个 mutation 回调里,早于下一帧绘制),不再等
|
|
24895
|
+
rAF 合并 + 闸门(那条路最坏 ~400ms,用户看到的就是"先白、过一会儿才变")。
|
|
24896
|
+
只处理本次**新增子树**里的候选(与弹层数量同阶),不做全文档扫描。 */
|
|
24897
|
+
/* ②(2026-10-05) 左栏里出现"可见的宿主弹层/对话框" ⇒ 给左栏打抬升标记(见上面 CSS 注释)。
|
|
24898
|
+
只读结构 + 尺寸,弹层关掉同一趟里摘掉;与弹层标记共用同一批触发点。 */
|
|
24899
|
+
const MPW_OVERLAY_SEL = '[class*="_overlay"], [class*="_modal"], [role="dialog"], [role="alertdialog"]';
|
|
24900
|
+
function mpwSideLiftSync() {
|
|
24902
24901
|
try {
|
|
24903
|
-
const
|
|
24904
|
-
if (!
|
|
24905
|
-
const set = new Set(all);
|
|
24906
|
-
/* 真机读数:DSH 右栏面板与 dockkit 面板/标签条**互相重叠**(rect 只差 1~2px)却不在同一条
|
|
24907
|
-
祖先链上 ⇒ 只按"有没有命中祖先"判不出重叠,两层 55% 会合成 ~80%(用户看到的"更白")。
|
|
24908
|
-
判据:DSH 自己的右栏面板在场时,dock 子件一律不刷(它们显示的就是那块面板的表面);
|
|
24909
|
-
只有"没有右栏面板、只有 dock"的布局(better-sidebar 底部工作台等)才由 dock 自己承担。 */
|
|
24910
|
-
const panel = document.querySelector('[data-sidebar-right-panel]');
|
|
24911
|
-
const isDock = (el) => { try { return el.matches('[data-dockkit-pane], [data-dockkit-strip], [data-dockkit-surface], [data-dockkit-float]') } catch (e) { return false } };
|
|
24902
|
+
const sides = Array.from(document.querySelectorAll('[class*="sidebarCol"]:not([data-sidebar-right-panel])'));
|
|
24903
|
+
if (!sides.length) return 0;
|
|
24912
24904
|
let changed = 0;
|
|
24913
|
-
for (const
|
|
24914
|
-
let
|
|
24915
|
-
for (
|
|
24916
|
-
|
|
24917
|
-
|
|
24918
|
-
|
|
24919
|
-
|
|
24920
|
-
|
|
24905
|
+
for (const side of sides) {
|
|
24906
|
+
let want = false;
|
|
24907
|
+
for (const dlg of Array.from(side.querySelectorAll(MPW_OVERLAY_SEL))) {
|
|
24908
|
+
try {
|
|
24909
|
+
const cs = getComputedStyle(dlg);
|
|
24910
|
+
if (cs.position !== "fixed" || cs.display === "none" || cs.visibility === "hidden") continue;
|
|
24911
|
+
if (Number(cs.opacity) === 0) continue;
|
|
24912
|
+
const r = dlg.getBoundingClientRect();
|
|
24913
|
+
if (r.width <= 0 || r.height <= 0) continue;
|
|
24914
|
+
want = true; break;
|
|
24915
|
+
} catch (e) { /* 单个读不到就跳过 */ }
|
|
24916
|
+
}
|
|
24917
|
+
const has = side.hasAttribute("data-mpw-side-lift");
|
|
24918
|
+
if (want && !has) { try { side.setAttribute("data-mpw-side-lift", ""); changed++ } catch (e) {} }
|
|
24919
|
+
else if (!want && has) { try { side.removeAttribute("data-mpw-side-lift"); changed++ } catch (e) {} }
|
|
24921
24920
|
}
|
|
24922
24921
|
return changed;
|
|
24923
24922
|
} catch (e) { return 0 }
|
|
24924
24923
|
}
|
|
24925
24924
|
|
|
24925
|
+
function mpwTagPopoverFast(records) {
|
|
24926
|
+
try {
|
|
24927
|
+
let n = 0;
|
|
24928
|
+
for (const rec of (records || [])) {
|
|
24929
|
+
for (const node of (rec.addedNodes || [])) {
|
|
24930
|
+
if (!node || node.nodeType !== 1) continue;
|
|
24931
|
+
const cands = [];
|
|
24932
|
+
try {
|
|
24933
|
+
if (node.matches && node.matches(MPW_POP_TAG_SEL)) cands.push(node);
|
|
24934
|
+
if (node.querySelectorAll) { const subs = node.querySelectorAll(MPW_POP_TAG_SEL); for (let i = 0; i < subs.length && i < 40; i++) cands.push(subs[i]); }
|
|
24935
|
+
} catch (e) {}
|
|
24936
|
+
for (const el of cands) { if (mpwTagOnePopover(el)) n++; }
|
|
24937
|
+
}
|
|
24938
|
+
}
|
|
24939
|
+
try { mpwSideLiftSync(); } catch (e) {}
|
|
24940
|
+
if (n) { try { mpwPopUntruncSync(); } catch (e) {} }
|
|
24941
|
+
return n;
|
|
24942
|
+
} catch (e) { return 0 }
|
|
24943
|
+
}
|
|
24944
|
+
/** 单个元素:该不该有我们的兜底底;返回是否发生了变化。 */
|
|
24945
|
+
function mpwTagOnePopover(el) {
|
|
24946
|
+
let need = false;
|
|
24947
|
+
try {
|
|
24948
|
+
const cls = (typeof el.className === "string") ? el.className : "";
|
|
24949
|
+
if (!MPW_POP_TAG_NOT.test(cls)) {
|
|
24950
|
+
const cs = getComputedStyle(el);
|
|
24951
|
+
if (cs && cs.display !== "none" && cs.visibility !== "hidden"
|
|
24952
|
+
&& /blur\(/.test(String(cs.backdropFilter || cs.webkitBackdropFilter || ""))) {
|
|
24953
|
+
const a = mpwColorAlpha(cs.backgroundColor);
|
|
24954
|
+
if (a < 0.05) need = !mpwPopHasPaintedAncestor(el);
|
|
24955
|
+
else if (a >= 0.999) need = mpwPopIsFloating(el);
|
|
24956
|
+
}
|
|
24957
|
+
}
|
|
24958
|
+
} catch (e) { need = false; }
|
|
24959
|
+
try {
|
|
24960
|
+
if (need) { if (!el.hasAttribute("data-mpw-pop-bg")) { el.setAttribute("data-mpw-pop-bg", ""); return true } }
|
|
24961
|
+
else if (el.hasAttribute("data-mpw-pop-bg")) { el.removeAttribute("data-mpw-pop-bg"); return true }
|
|
24962
|
+
} catch (e) {}
|
|
24963
|
+
return false;
|
|
24964
|
+
}
|
|
24965
|
+
function mpwTagPopoverBg() {
|
|
24966
|
+
let list = null;
|
|
24967
|
+
try { list = document.querySelectorAll(MPW_POP_TAG_SEL); } catch (e) { return; }
|
|
24968
|
+
if (!list) return;
|
|
24969
|
+
for (let i = 0; i < list.length; i++) mpwTagOnePopover(list[i]);
|
|
24970
|
+
}
|
|
24971
|
+
|
|
24926
24972
|
/* ①(P3 修复 2026-10-03) 弹层玻璃的 backdrop root 被截断 ⇒ 有半透明、没模糊。
|
|
24927
24973
|
读数与机制(docs/POPOVER-BLUR.md):挂在宿主输入框卡片(uV2eYG_card,backdrop-filter:
|
|
24928
24974
|
blur(14px))子树里的弹层,其 backdrop root 被卡片截断 ⇒ 弹层自己的 blur(11px) 只能采样
|
|
@@ -24949,24 +24995,60 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
24949
24995
|
try {
|
|
24950
24996
|
const surfaces = document.querySelectorAll("[data-mpw-pop-bg]");
|
|
24951
24997
|
const targets = new Set();
|
|
24998
|
+
const truncated = new Set();
|
|
24999
|
+
const markedNow = Array.from(document.querySelectorAll("[data-mpw-pop-untrunc]"));
|
|
24952
25000
|
for (let i = 0; i < surfaces.length; i++) {
|
|
24953
25001
|
const el = surfaces[i];
|
|
24954
25002
|
let cs = null;
|
|
24955
25003
|
try { cs = getComputedStyle(el) } catch (e) {}
|
|
24956
25004
|
/* 硬前提(与 mpwTagPopoverBg 同口径):我们真的给了模糊才值得摘祖先的截断 */
|
|
24957
25005
|
if (!cs || !cs.backdropFilter || String(cs.backdropFilter).indexOf("blur") < 0) continue;
|
|
25006
|
+
/* ②(2026-10-05 抖动修复) **已经摘过、且仍在这条祖先链上**的祖先,弹层没关就一直保持:
|
|
25007
|
+
摘掉之后它自己就没有 blur 了,若每轮都重新"找第一个带 blur 的祖先",下一轮就找不到它
|
|
25008
|
+
⇒ 属性被当陈旧标记摘掉 ⇒ blur 又回来 ⇒ 摘/还原来回抖(真机现象:同一个菜单时好时坏)。 */
|
|
25009
|
+
let kept = false;
|
|
25010
|
+
for (const mEl of markedNow) {
|
|
25011
|
+
if (mEl === el) continue;
|
|
25012
|
+
let onChain = false;
|
|
25013
|
+
/* 用 parentElement 走链(不用 contains:桩 DOM/老引擎上不一定有) */
|
|
25014
|
+
for (let q = el.parentElement, k = 0; q && k < 40; q = q.parentElement, k++) { if (q === mEl) { onChain = true; break } }
|
|
25015
|
+
if (onChain) { targets.add(mEl); kept = true; break }
|
|
25016
|
+
}
|
|
25017
|
+
if (kept) continue;
|
|
25018
|
+
let blockedByGlass = false;
|
|
24958
25019
|
for (let p = el.parentElement, n = 0; p && p !== document.body && p !== document.documentElement && n < 12; p = p.parentElement, n++) {
|
|
24959
25020
|
let acs = null;
|
|
24960
25021
|
try { acs = getComputedStyle(p) } catch (e) { break }
|
|
24961
|
-
|
|
25022
|
+
const ablur = acs && acs.backdropFilter && acs.backdropFilter !== "none" && String(acs.backdropFilter).indexOf("blur") >= 0;
|
|
25023
|
+
if (!ablur) continue;
|
|
25024
|
+
/* ②(2026-10-05 真机回归) **祖先自己是"看得见的玻璃板"时绝不动它**:
|
|
25025
|
+
它是用户能看到的那层模糊(真机现场:子代理展开框/输入框卡片被我们摘掉 blur 之后
|
|
25026
|
+
"模糊背景没有了"——正是之前夸过的那处效果)。判据 = 它有可见底色(α ≥ 0.05)。
|
|
25027
|
+
这种情况下改为把**弹层自己的表面做实在**(不能糊到页面背后,就别假装是玻璃)。 */
|
|
25028
|
+
/* ②(2026-10-05 真机复核) 判据收紧到"**它自己也是我们管的弹层表面**":
|
|
25029
|
+
子代理展开框/后台任务面板这类"用户正在看的面板"本身就在我们的弹层表面集合里
|
|
25030
|
+
⇒ 摘它的 blur = 把用户看得见的那层玻璃删掉(真机回归)。
|
|
25031
|
+
而输入框卡片 `uV2eYG_card`(有底、有 blur,但**不是**弹层表面)只是"碰巧挡在
|
|
25032
|
+
弹层前面的包装" ⇒ 摘掉它的 blur 换来弹层真模糊,代价是"菜单打开期间它自己不糊"。
|
|
25033
|
+
真机读数支持这条:四个弹层里只有 body 下的模型选择器有模糊,另三个全被卡片截断。 */
|
|
25034
|
+
let ancestorIsSurface = false;
|
|
25035
|
+
try { ancestorIsSurface = p.matches(MPW_POP_TAG_SEL) } catch (e) {}
|
|
25036
|
+
if (mpwColorAlpha(acs.backgroundColor) >= 0.05 && ancestorIsSurface) { blockedByGlass = true; break }
|
|
25037
|
+
targets.add(p);
|
|
25038
|
+
break;
|
|
24962
25039
|
}
|
|
25040
|
+
if (blockedByGlass) truncated.add(el);
|
|
24963
25041
|
}
|
|
24964
|
-
const stale =
|
|
25042
|
+
const stale = markedNow;
|
|
24965
25043
|
for (let i = 0; i < stale.length; i++) { const el = stale[i]; if (!targets.has(el)) { try { el.removeAttribute("data-mpw-pop-untrunc") } catch (e) {} } }
|
|
24966
25044
|
for (const el of targets) { if (!el.hasAttribute("data-mpw-pop-untrunc")) { try { el.setAttribute("data-mpw-pop-untrunc", "") } catch (e) {} } }
|
|
25045
|
+
/* 被"有底的玻璃祖先"挡住的那批:标记在**弹层自己**身上(绝不碰宿主元素),由 CSS 给实底 */
|
|
25046
|
+
const staleT = document.querySelectorAll("[data-mpw-pop-trunc]");
|
|
25047
|
+
for (let i = 0; i < staleT.length; i++) { const el = staleT[i]; if (!truncated.has(el)) { try { el.removeAttribute("data-mpw-pop-trunc") } catch (e) {} } }
|
|
25048
|
+
for (const el of truncated) { if (!el.hasAttribute("data-mpw-pop-trunc")) { try { el.setAttribute("data-mpw-pop-trunc", "") } catch (e) {} } }
|
|
24967
25049
|
if (targets.size) mpwPopUntruncStyle();
|
|
24968
25050
|
else if (popUntruncStyleEl && popUntruncStyleEl.parentElement) { try { popUntruncStyleEl.remove() } catch (e) {} popUntruncStyleEl = null; }
|
|
24969
|
-
return targets.size;
|
|
25051
|
+
return targets.size + truncated.size * 1000; // 千位段 = 被"有底玻璃祖先"挡住、改为实底的弹层数
|
|
24970
25052
|
} catch (e) { return 0 }
|
|
24971
25053
|
}
|
|
24972
25054
|
/* ①(P3) 判据出口(tools/popover-untrunc-test.mjs 直接调 sync 断言 打标/还原/硬前提/首截断者)。 */
|
|
@@ -24977,6 +25059,24 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
24977
25059
|
};
|
|
24978
25060
|
} catch (e) {}
|
|
24979
25061
|
|
|
25062
|
+
/* ②(2026-10-05) 探针/判据出口:弹层兜底底标记与"摘截断"两件事可被**手动触发并读回**——
|
|
25063
|
+
真机排障要能区分"根本没跑"(调度被饿死、observer 没接上)与"跑了但条件不满足"(宿主
|
|
25064
|
+
自己画了不透明白底/面积超阈值…)。只读+幂等,不改变任何语义。 */
|
|
25065
|
+
try {
|
|
25066
|
+
globalThis.__mpwPopTagTest = {
|
|
25067
|
+
run: () => { try { return mpwTagPopoverBg() } catch (e) { return -1 } },
|
|
25068
|
+
untrunc: () => { try { return mpwPopUntruncSync() } catch (e) { return -1 } },
|
|
25069
|
+
state: (sel) => {
|
|
25070
|
+
try {
|
|
25071
|
+
const el = document.querySelector(sel || "[data-mpw-pop-bg]");
|
|
25072
|
+
if (!el) return null;
|
|
25073
|
+
const cs = getComputedStyle(el);
|
|
25074
|
+
return { cls: String(el.className).slice(0, 60), tag: el.hasAttribute("data-mpw-pop-bg"), bg: cs.backgroundColor, bf: cs.backdropFilter, display: cs.display, vis: cs.visibility };
|
|
25075
|
+
} catch (e) { return null }
|
|
25076
|
+
},
|
|
25077
|
+
};
|
|
25078
|
+
} catch (e) {}
|
|
25079
|
+
|
|
24980
25080
|
function setupPopTagWatch() {
|
|
24981
25081
|
if (popTagWatch !== null) return;
|
|
24982
25082
|
let praf = 0;
|
|
@@ -24994,12 +25094,15 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
24994
25094
|
之后面板出现就变成两层(真机读数:dockPane/dockStrip 仍是 0.55)。
|
|
24995
25095
|
这里与弹层标记共用同一个 rAF 合并的入口(要素很少,代价可忽略)。 */
|
|
24996
25096
|
try { mpwTagSurfaceOuter(); } catch (e) {}
|
|
25097
|
+
try { mpwSideLiftSync(); } catch (e) {}
|
|
24997
25098
|
/* ①(P3) 弹层开着 ⇒ 摘截断祖先的 blur;关了 ⇒ 还原(同一入口,随突变重算) */
|
|
24998
25099
|
try { mpwPopUntruncSync(); } catch (e) {}
|
|
24999
25100
|
} catch (e) {}
|
|
25000
25101
|
};
|
|
25001
25102
|
try {
|
|
25002
|
-
popTagWatch = new MutationObserver(() => {
|
|
25103
|
+
popTagWatch = new MutationObserver((records) => {
|
|
25104
|
+
/* 同步快路:新增的弹层当场判定打标(消灭"先白后变"的过渡态) */
|
|
25105
|
+
try { mpwTagPopoverFast(records); } catch (e) {}
|
|
25003
25106
|
if (praf) return;
|
|
25004
25107
|
try { praf = (typeof requestAnimationFrame === "function") ? requestAnimationFrame(prun) : setTimeout(prun, 16); }
|
|
25005
25108
|
catch (e) { prun(); }
|
|
@@ -25069,7 +25172,8 @@ body[data-mpw-modal] [role="dialog"] {
|
|
|
25069
25172
|
动画停 ⇒ 补跑执行、账目清空)。 */
|
|
25070
25173
|
try {
|
|
25071
25174
|
globalThis.__mpwHeavyTest = {
|
|
25072
|
-
state: () => ({ pending: Array.from(mpwHeavyPending.keys()), timer: !!mpwHeavyRecheckTimer }),
|
|
25175
|
+
state: () => ({ pending: Array.from(mpwHeavyPending.keys()), timer: !!mpwHeavyRecheckTimer, streak: mpwHeavyDeferStreak }),
|
|
25176
|
+
animRunning: () => mpwHeavyAnimRunning(),
|
|
25073
25177
|
gate: (name, fn) => mpwHeavyGate(name, fn),
|
|
25074
25178
|
flushNow: () => {
|
|
25075
25179
|
if (mpwHeavyRecheckTimer) { try { clearTimeout(mpwHeavyRecheckTimer) } catch (e) {} mpwHeavyRecheckTimer = 0; }
|
package/package.json
CHANGED