@linxin666/dsh-client-ui-skin-center 0.3.3 → 0.3.4

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.
@@ -45,7 +45,7 @@
45
45
  | `data-dsh-wallpaper-active` | html + body(body/html 级,另行管理) | WE 壁纸挂载期间置 `true`,卸载 / 禁用清除;供皮肤 CSS 与壁纸中和规则锚定(#734) |
46
46
  | `data-dsh-wallpaper-surface` | 官方 shell 全视口背景元素 + 侧栏工作区淡化条(元素级) | `WallpaperController.markWallpaperSurfaces()` 在 WE 壁纸挂载期间打标(全视口 bg-base 背景 + `data-slot="sidebar.workspaces"` 内渐变淡化条),命中 `html[data-dsh-wallpaper-active] [data-dsh-wallpaper-surface]` 中和;卸载清除,不含哈希类依赖(#734) |
47
47
 
48
- ## part 组(31 行,含各 owner 行)
48
+ ## part 组(38 行,含各 owner 行)
49
49
 
50
50
  shell 区域(owner: shell):
51
51
 
@@ -59,6 +59,7 @@ shell 区域(owner: shell):
59
59
  | `queue-dock` | 排队条;`[data-queue-dock]` |
60
60
  | `turn-tail` | turn 尾行;`[data-turn-tail]` |
61
61
  | `resize-handle` | 列宽手柄;`[data-side]` |
62
+ | `new-session` | 侧栏新会话按钮;官方稳定属性落地前由兼容适配器从 `button[class*="newSession"]` 补打,皮肤不得依赖本地化文案 |
62
63
 
63
64
  family / 插件区域:
64
65
 
@@ -101,8 +102,14 @@ family / 插件区域:
101
102
  | `row` | session-id | 会话列表行;面板内行容器(`[data-dsh-part="row"]`) |
102
103
  | `copy` | session-id | 每行复制按钮;`button[data-dsh-part="copy"]` |
103
104
  | `search` | session-id | 面板搜索输入框;`input[type="search"][data-dsh-part="search"]` |
105
+ | `sprite` | miku-pet | 宠物帧舞台;`[data-dsh-plugin="miku-pet"] [data-dsh-part="sprite"]` |
106
+ | `menu` | miku-pet | 悬停菜单(两级);`[data-dsh-part="menu"]` |
107
+ | `stats` | miku-pet | 左侧属性彩条;`[data-dsh-part="stats"]` |
108
+ | `shop` | miku-pet | 商店居中窗口;`[data-dsh-part="shop"]` |
109
+ | `bubble` | miku-pet | 对话气泡;`[data-dsh-part="bubble"]` |
110
+ | `float` | miku-pet | 互动飘字;`[data-dsh-part="float"]` |
104
111
 
105
- ## plugin 组(12 个,含停更 aionui-panel)
112
+ ## plugin 组(13 个,含停更 aionui-panel)
106
113
 
107
114
  | data-dsh-plugin | owner | 锚定方式 |
108
115
  | --- | --- | --- |
@@ -118,6 +125,7 @@ family / 插件区域:
118
125
  | `dsh-web-ui-market` | dsh-market | 创意工坊商店一级页(settings.section id `dsh-web-ui-market`),商店卡与目录条目容器 |
119
126
  | `skin-center` | skins/skin-center | 一级设置分区 settings.section id `skin-center`(列已安装皮肤,属内置源时显式标记) |
120
127
  | `session-id` | dsh-session-id | footer action slot entry id `session-id`;`[data-dsh-plugin="session-id"]`(面板 overlay 根 + 入口触发器) |
128
+ | `miku-pet` | dsh-miku-pet | 宠物浮层根 `[data-dsh-plugin="miku-pet"]`;host 路由前缀 `/miku-pet/*`;设置页 settings.section id `miku-pet-config` |
121
129
 
122
130
  ## 已知脆弱点(上游主题缝 PR 诉求)
123
131
 
package/lib/client.js CHANGED
@@ -746,6 +746,11 @@ window.__ModuleLoader__.load({
746
746
  this.publish();
747
747
  this.scope.set("weLibraryDirs", this.dirsValue);
748
748
  }
749
+ failedIds = /* @__PURE__ */ new Set();
750
+ isDisplaying = () => {
751
+ const id = this.activeId();
752
+ return this.enabledValue && id !== null && !this.failedIds.has(id);
753
+ };
749
754
  activeId = () => {
750
755
  const current = this.previewing ?? this.applied;
751
756
  return this.mediaLayer !== null && current !== null ? current.id : null;
@@ -811,6 +816,7 @@ window.__ModuleLoader__.load({
811
816
  this.scope.set("volume", this.volumeValue);
812
817
  }
813
818
  applySelection(descriptor) {
819
+ this.failedIds.delete(descriptor.id);
814
820
  this.applied = descriptor;
815
821
  this.previewing = null;
816
822
  this.selectionValue = descriptor.id;
@@ -842,6 +848,7 @@ window.__ModuleLoader__.load({
842
848
  if (descriptor !== null) this.probeSceneCapabilitiesIfNeeded(descriptor);
843
849
  }
844
850
  tryOn(descriptor) {
851
+ this.failedIds.delete(descriptor.id);
845
852
  this.previewing = descriptor;
846
853
  this.render();
847
854
  this.publish();
@@ -1111,6 +1118,22 @@ window.__ModuleLoader__.load({
1111
1118
  const nextFallback = frameUrl !== null ? previewUrl : null;
1112
1119
  const img = this.buildImage(nextUrl, nextFallback);
1113
1120
  if (img && video.parentElement) video.parentElement.replaceChild(img, video);
1121
+ else {
1122
+ const currentId = this.previewing?.id ?? this.applied?.id;
1123
+ if (currentId) {
1124
+ this.failedIds.add(currentId);
1125
+ this.render();
1126
+ this.publish();
1127
+ }
1128
+ }
1129
+ }, { once: true });
1130
+ else video.addEventListener("error", () => {
1131
+ const currentId = this.previewing?.id ?? this.applied?.id;
1132
+ if (currentId) {
1133
+ this.failedIds.add(currentId);
1134
+ this.render();
1135
+ this.publish();
1136
+ }
1114
1137
  }, { once: true });
1115
1138
  video.play()?.catch(() => {});
1116
1139
  return video;
@@ -1161,9 +1184,18 @@ window.__ModuleLoader__.load({
1161
1184
  const image = this.doc.createElement("img");
1162
1185
  image.src = url;
1163
1186
  image.alt = "";
1164
- if (fallbackUrl !== null && fallbackUrl !== url) image.addEventListener("error", () => {
1165
- if (image.src !== fallbackUrl) image.src = fallbackUrl;
1166
- }, { once: true });
1187
+ image.addEventListener("error", () => {
1188
+ if (fallbackUrl !== null && fallbackUrl !== url && image.src !== fallbackUrl) {
1189
+ image.src = fallbackUrl;
1190
+ return;
1191
+ }
1192
+ const currentId = this.previewing?.id ?? this.applied?.id;
1193
+ if (currentId) {
1194
+ this.failedIds.add(currentId);
1195
+ this.render();
1196
+ this.publish();
1197
+ }
1198
+ });
1167
1199
  styleCover(image, this.fitValue);
1168
1200
  return image;
1169
1201
  }
@@ -2130,7 +2162,7 @@ window.__ModuleLoader__.load({
2130
2162
  * @returns the plugin card.
2131
2163
  */
2132
2164
  function SkinCenter({ t, runtime, theme, background, wallpaper, preview, customTheme }) {
2133
- const snapshot = (0, react.useSyncExternalStore)((listener) => theme.subscribe(listener), () => theme.getTheme());
2165
+ (0, react.useSyncExternalStore)((listener) => theme.subscribe(listener), () => theme.getTheme());
2134
2166
  const enabled = (0, react.useSyncExternalStore)(background.subscribe, background.enabled);
2135
2167
  const opacity = (0, react.useSyncExternalStore)(background.subscribe, background.opacity);
2136
2168
  const blurEmpty = (0, react.useSyncExternalStore)(background.subscribe, background.blurEmpty);
@@ -2253,7 +2285,7 @@ window.__ModuleLoader__.load({
2253
2285
  return active;
2254
2286
  }));
2255
2287
  };
2256
- const dark = snapshot.active.colorScheme === "dark";
2288
+ const dark = document.body.hasAttribute("data-ds-dark-theme");
2257
2289
  /** One row: try-on control + apply button. Shared by the official card and every skin card. */
2258
2290
  const actionButtons = (opts) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2259
2291
  className: skin_center_module_css_default.actions,
@@ -3055,20 +3087,30 @@ window.__ModuleLoader__.load({
3055
3087
  }
3056
3088
  return Object.keys(patch).length === 0 ? null : patch;
3057
3089
  }
3090
+ /** Deterministic serialization of the user layer for content-based dedup. */
3091
+ function serializeSkinBackgroundUserLayer(user) {
3092
+ const extracted = extractSkinBackgroundUserLayer(user);
3093
+ return extracted === null ? "" : JSON.stringify(extracted);
3094
+ }
3058
3095
  /**
3059
- * Accept a scope publication only when its namespace revision is new. The
3060
- * caller records the returned revision even when there is no user patch, so a
3061
- * later mirror publication with the same revision stays inert.
3096
+ * Accept a scope publication only when its namespace revision is new AND the
3097
+ * user layer has actually changed. A revision bump with identical user-layer
3098
+ * content is a replay (settings-mirror resync, WS reconnect, or another
3099
+ * plugin writing to the global settings document) and must not overwrite the
3100
+ * authoritative v2 state.
3062
3101
  */
3063
- function reconcileSkinBackgroundScope(current, snapshot, lastRevision) {
3064
- if (snapshot.revision === void 0 || snapshot.revision === lastRevision) return {
3102
+ function reconcileSkinBackgroundScope(current, snapshot, lastRevision, lastUserJson) {
3103
+ const currentUserJson = serializeSkinBackgroundUserLayer(snapshot.user);
3104
+ if (snapshot.revision === void 0 || snapshot.revision === lastRevision || currentUserJson === lastUserJson) return {
3065
3105
  accepted: false,
3066
- revision: lastRevision,
3106
+ revision: snapshot.revision ?? lastRevision,
3107
+ lastUserJson: currentUserJson,
3067
3108
  patch: null
3068
3109
  };
3069
3110
  return {
3070
3111
  accepted: true,
3071
3112
  revision: snapshot.revision,
3113
+ lastUserJson: currentUserJson,
3072
3114
  patch: skinBackgroundUserPatch(current, snapshot.user)
3073
3115
  };
3074
3116
  }
@@ -3431,6 +3473,11 @@ window.__ModuleLoader__.load({
3431
3473
  attrs: [["data-dsh-part", "resize-handle"]],
3432
3474
  note: "column resize handle"
3433
3475
  },
3476
+ {
3477
+ selector: "button[class*=\"newSession\"]",
3478
+ attrs: [["data-dsh-part", "new-session"]],
3479
+ note: "sidebar new-session action (compat seam shields skins from localized labels)"
3480
+ },
3434
3481
  {
3435
3482
  selector: "[data-dsh-taskboard-view], [data-dsh-taskboard-board], [data-dsh-taskboard-entry]",
3436
3483
  attrs: [["data-dsh-plugin", "task-board"]],
@@ -3610,10 +3657,31 @@ window.__ModuleLoader__.load({
3610
3657
  ${scoped("[data-dsh-part=\"scrollport\"]")} {
3611
3658
  /* The composer is the scrollport's final in-flow child. Reserving physical
3612
3659
  padding after it lifts the active dock by one composer height and also
3613
- shifts the hero above center. Scroll padding keeps scrollIntoView()
3614
- clearance without changing either layout's geometry. */
3660
+ shifts the hero above center. */
3615
3661
  padding-bottom: 0 !important;
3616
- scroll-padding-bottom: var(--dsh-composer-height, 100px) !important;
3662
+ }
3663
+ /* #978 / #1133: Line-level scroll-margin retains scrollIntoView() clearance
3664
+ above the sticky composer without placing a scrollport-level scroll-padding
3665
+ that breaks browser caret-reveal geometry (which caused micro-scrolling on
3666
+ every keystroke while reading history). */
3667
+ ${scoped("[data-conversation-scroll] [data-chat-anchor-key]")},
3668
+ ${scoped("[data-conversation-scroll] [data-chat-flow-kind]")},
3669
+ ${scoped("[data-conversation-scroll] [data-dsh-part=\"message-row\"]")},
3670
+ ${scoped("[data-conversation-scroll] [data-turn-tail]")},
3671
+ ${scoped("[data-conversation-scroll] [class*=\"_userRow\"]")},
3672
+ ${scoped("[data-conversation-scroll] [class*=\"_compactionRow\"]")},
3673
+ ${scoped("[data-conversation-scroll] [class*=\"_contextRow\"]")},
3674
+ ${scoped("[data-conversation-scroll] [class*=\"_turnErrorRow\"]")} {
3675
+ scroll-margin-bottom: var(--dsh-composer-height, 100px) !important;
3676
+ }
3677
+ /* #1117: The upstream recommended badge pairs two background-fill tokens
3678
+ as bg + text — in dark mode, skins like Blue Fantasy collapse them to
3679
+ near-identical dark navy values (contrast ~1:1). Override the text
3680
+ color to a readable foreground and tweak the background for contrast. */
3681
+ body[data-ds-dark-theme] ${scoped("[data-question-key] [class*=\"_badge\"]")},
3682
+ body[data-ds-dark-theme] ${scoped("[data-question-scroll] [class*=\"_badge\"]")} {
3683
+ color: var(--dsw-alias-label-primary, #ffffff) !important;
3684
+ background: var(--dsw-alias-interactive-bg-active, color-mix(in srgb, var(--dsw-alias-button-info-fill, #4a5fa8) 50%, transparent)) !important;
3617
3685
  }
3618
3686
  `;
3619
3687
  }
@@ -4159,9 +4227,11 @@ window.__ModuleLoader__.load({
4159
4227
  active = payload.ok && typeof payload.active === "string" ? payload.active : null;
4160
4228
  }
4161
4229
  if (active === null) return;
4162
- const entry = store.find(active);
4230
+ let entry = store.find(active);
4163
4231
  if (entry === null) {
4164
- await controller.switchTo(null, null);
4232
+ const defaultEntry = store.find("blue-fantasy");
4233
+ if (defaultEntry !== null) await controller.switchTo("blue-fantasy", defaultEntry);
4234
+ else await controller.switchTo(null, null);
4165
4235
  return;
4166
4236
  }
4167
4237
  await controller.switchTo(active, entry);
@@ -4169,6 +4239,18 @@ window.__ModuleLoader__.load({
4169
4239
  await controller.switchTo(null, null).catch(() => {});
4170
4240
  }
4171
4241
  })();
4242
+ if (typeof doc.defaultView !== "undefined" && doc.defaultView !== null) {
4243
+ const win = doc.defaultView;
4244
+ const onSkinApplied = (e) => {
4245
+ const detail = e.detail;
4246
+ if (detail && typeof detail.id === "string") refreshCatalog().then(async () => {
4247
+ const entry = store.find(detail.id);
4248
+ if (entry !== null) await controller.switchTo(detail.id, entry);
4249
+ });
4250
+ };
4251
+ win.addEventListener("dsh-skin-applied", onSkinApplied);
4252
+ listeners.add(() => win.removeEventListener("dsh-skin-applied", onSkinApplied));
4253
+ }
4172
4254
  return store;
4173
4255
  }
4174
4256
  //#endregion
@@ -4552,7 +4634,7 @@ window.__ModuleLoader__.load({
4552
4634
  /** The building package's version, when the bundle carries it. */
4553
4635
  function bakedVersion() {
4554
4636
  try {
4555
- return "0.3.3";
4637
+ return "0.3.4";
4556
4638
  } catch {
4557
4639
  return;
4558
4640
  }
@@ -4707,6 +4789,7 @@ window.__ModuleLoader__.load({
4707
4789
  };
4708
4790
  let v2Loaded = false;
4709
4791
  let lastScopeRevision = backgroundScope.getSnapshot().revision;
4792
+ let lastUserJson = serializeSkinBackgroundUserLayer(backgroundScope.getSnapshot().user);
4710
4793
  const background = new BackgroundController(scopeConfig(), persistBackground);
4711
4794
  const reconcileScope = () => {
4712
4795
  if (!v2Loaded) return;
@@ -4714,10 +4797,10 @@ window.__ModuleLoader__.load({
4714
4797
  const result = reconcileSkinBackgroundScope(background.snapshot(), {
4715
4798
  revision: snapshot.revision,
4716
4799
  user: snapshot.user
4717
- }, lastScopeRevision);
4718
- if (!result.accepted) return;
4800
+ }, lastScopeRevision, lastUserJson);
4719
4801
  lastScopeRevision = result.revision;
4720
- if (result.patch === null) return;
4802
+ lastUserJson = result.lastUserJson;
4803
+ if (!result.accepted || result.patch === null) return;
4721
4804
  const current = background.snapshot();
4722
4805
  background.init({
4723
4806
  ...current,
@@ -4743,7 +4826,7 @@ window.__ModuleLoader__.load({
4743
4826
  const wallpaper = new WallpaperController(binder.bind({ namespace: SKIN_WALLPAPER_NS }));
4744
4827
  ctx.effect(() => () => wallpaper.dispose(), "ui-skin-center: wallpaper dispose");
4745
4828
  installBootRestore(wallpaper);
4746
- const runtime = bootSkinRuntime({ suppressBackgroundMedia: () => wallpaper.enabled() && wallpaper.activeId() !== null && wallpaper.activeId() !== "" });
4829
+ const runtime = bootSkinRuntime({ suppressBackgroundMedia: () => wallpaper.enabled() && wallpaper.isDisplaying() });
4747
4830
  ctx.effect(() => () => runtime.shutdown(), "ui-skin-center: runtime shutdown");
4748
4831
  ctx.effect(() => wallpaper.subscribe(() => {
4749
4832
  runtime.controller.refresh();