dsh-folia 3.7.1 → 3.9.0

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 CHANGED
@@ -16314,7 +16314,7 @@ window.__ModuleLoader__.load({
16314
16314
  setNote({ kind: "ok", text: weT("已应用:「") + label + weT("」—— 皮肤中心已收敛(无需刷新)") });
16315
16315
  return;
16316
16316
  }
16317
- if (tries >= 5) {
16317
+ if (tries >= 9) { // 约 11 秒:host 会先写 null 再写目标 id(900ms),引擎每 2s 轮询一次
16318
16318
  clearInterval(pollRef.current); pollRef.current = null;
16319
16319
  setNote({
16320
16320
  kind: "err",
@@ -25729,6 +25729,27 @@ window.__ModuleLoader__.load({
25729
25729
  try { return skinYielded === true; } catch { return false; }
25730
25730
  }
25731
25731
 
25732
+ /**
25733
+ * 让路期间把**整张插件样式表**关掉。
25734
+ *
25735
+ * 为什么必须关整张:皮肤是一套完整的外观(自己的底色、圆角、字体色、边框)。我们的
25736
+ * `styles-base.js` 给应用外壳铺透明/玻璃/自定义配色,选择器落在同一批元素上,于是皮肤
25737
+ * 的规则被我们盖掉 —— 表现就是"皮肤显示不全,关掉插件就完整"。让路态此前只关了玻璃
25738
+ * 效果族(`data-we-*` 属性门控),**样式表本体一直在**,所以这个洞一直存在。
25739
+ *
25740
+ * 用 `sheet.disabled` 而不是摘 `<style>`:摘了再插会触发整页重排;disabled 是零成本开关,
25741
+ * 退出让路时立刻原样恢复。
25742
+ */
25743
+ function syncPluginCssForSkin() {
25744
+ try {
25745
+ const tag = document.querySelector('style[data-plugin-css="dsh-folia/styles-v3"]');
25746
+ if (tag === null) return;
25747
+ const off = skinYieldActive() === true;
25748
+ if (tag.sheet) tag.sheet.disabled = off;
25749
+ else tag.disabled = off;
25750
+ } catch { /* 拿不到 sheet(还在解析 / 被 CSP 拦)就跳过,不影响其它门控 */ }
25751
+ }
25752
+
25732
25753
  /** 皮肤上台 ⇒ 退场(幂等)。 */
25733
25754
  function enterSkinYield(reason) {
25734
25755
  if (skinYielded) return;
@@ -25743,6 +25764,7 @@ window.__ModuleLoader__.load({
25743
25764
  applySelection(""); // 清层:url=null ⇒ syncLayers 摘层 / 遮罩 / data-we-wallpaper
25744
25765
  persistSelection();
25745
25766
  applyEffects(); // 让路态里玻璃门控整族摘除
25767
+ syncPluginCssForSkin(); // 连同整张样式表一起让位 —— 否则皮肤被我们的外框盖住(显示不全)
25746
25768
  emit();
25747
25769
  reportClientDiag("skin-yield", "enter · " + reason + " · restore=" + (skinYieldMemory.id || "-"));
25748
25770
  }
@@ -25765,6 +25787,7 @@ window.__ModuleLoader__.load({
25765
25787
  setSetting("skinYieldRestoreRotation", false);
25766
25788
  persistSelection();
25767
25789
  applyEffects();
25790
+ syncPluginCssForSkin(); // 皮肤退场:把我们的样式表原样开回来
25768
25791
  emit();
25769
25792
  reportClientDiag("skin-yield", "exit · " + reason);
25770
25793
  }
@@ -25884,6 +25907,7 @@ window.__ModuleLoader__.load({
25884
25907
  rotationEnabled: rid ? selection.skinYieldRestoreRotation === true : selection.rotationEnabled === true,
25885
25908
  };
25886
25909
  applyEffects(); // 认领即刻摘玻璃门控(首次 applyEffects 可能已经跑过)
25910
+ syncPluginCssForSkin(); // 启动即认领时也要让出样式表:否则一开机皮肤就被外框盖着
25887
25911
  reportClientDiag("skin-yield", "adopt · install/dom");
25888
25912
  } else if (!document.documentElement.hasAttribute("data-dsh-skin") && !selection.id
25889
25913
  && String(selection.skinYieldRestoreId || "")) {
@@ -25905,6 +25929,7 @@ window.__ModuleLoader__.load({
25905
25929
  try { unsubAdopt(); } catch { /* ignore */ }
25906
25930
  skinYielded = false;
25907
25931
  skinYieldMemory = null;
25932
+ syncPluginCssForSkin(); // 清理后让路态清零 ⇒ 样式表恢复
25908
25933
  };
25909
25934
  }
25910
25935
 
@@ -552,12 +552,31 @@ export function registerSkinRoutes(webServer, c) {
552
552
  doc = JSON.parse(readFileSync(file, 'utf8')) || {};
553
553
  } catch { /* 文件不存在 / 坏掉 ⇒ 用最小骨架重建 */ }
554
554
  // 只改 `active`:background 那一段是皮肤中心自己的设置,一个字都不能丢。
555
+ const previous = typeof doc.active === 'string' ? doc.active : null;
555
556
  doc.active = id;
556
557
  if (typeof doc.initialized !== 'boolean') doc.initialized = true;
557
558
  try {
559
+ // ⚠️ 这里必须先写一次 `null`,再写目标 id —— 不是画蛇添足。
560
+ //
561
+ // 皮肤中心客户端的收敛逻辑(lib/client.js,watchPersistedSelection):
562
+ //
563
+ // applied = persisted === undefined ? undefined
564
+ // : (stamp === null || stamp === persisted ? persisted : undefined);
565
+ // // tick: next === applied ⇒ return ← 相同就永远不动
566
+ //
567
+ // 页面启动时若 `html[data-dsh-skin]` 为空,它会把**持久化的值直接当成已应用**。
568
+ // 于是我们写一个"和它记录里一样"的 id 时,轮询认为没有变化,皮肤永远不上。
569
+ // 先归零(写 null)能让它收敛到"无皮肤",紧接着写目标 id 就必然是一次变化,
570
+ // 轮询一定会应用 —— 这是绕开它这个初始化的唯一办法,且完全走它自己的路径。
571
+ if (previous === id) {
572
+ doc.active = null;
573
+ writeFileSync(file, JSON.stringify(doc, null, 2) + '\n');
574
+ await new Promise((r) => setTimeout(r, 900));
575
+ doc.active = id;
576
+ }
558
577
  writeFileSync(file, JSON.stringify(doc, null, 2) + '\n');
559
- note('[skins] activated ' + id);
560
- sendJson(res, 200, { ok: true, id, active: id, restartRequired: true });
578
+ note('[skins] activated ' + id + (previous === id ? ' (after clearing the same id)' : ''));
579
+ sendJson(res, 200, { ok: true, id, active: id, restartRequired: false, nudged: previous === id });
561
580
  } catch (err) {
562
581
  sendJson(res, 500, { ok: false, error: 'write-failed', detail: String(err && err.message || err) });
563
582
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-folia",
3
- "version": "3.7.1",
3
+ "version": "3.9.0",
4
4
  "description": "Folia 壁纸引擎:把本机 Wallpaper Engine 壁纸搬进 DSH 界面(场景实时渲染 / 视频 / 网页),内置液态玻璃与 Folia 吉祥物,并可从自建站点拉取壁纸。单包形态(组件已合并)。",
5
5
  "type": "module",
6
6
  "engines": {