dsh-skin-galactic-opera 0.1.9 → 0.2.1

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 CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to this package will be documented in this file.
4
4
 
5
+ ## 0.2.1 - 2026-09-04
6
+
7
+ ### Changed
8
+
9
+ - Documents the recommended DSH compatibility range in both English and Simplified Chinese README sections.
10
+
11
+ ### Fixed
12
+
13
+ - Aligns click pulse and scanner target effects with the clicked position after Shadow DOM decor isolation.
14
+
15
+ ## 0.2.0 - 2026-09-04
16
+
17
+ ### Changed
18
+
19
+ - Pauses skin animation work while the page is hidden to reduce background CPU/GPU usage.
20
+ - Suspends telemetry scans while the page is hidden, then forces an immediate refresh plus a short follow-up when the page becomes visible again.
21
+
5
22
  ## 0.1.9 - 2026-09-03
6
23
 
7
24
  ### Changed
package/README.md CHANGED
@@ -41,6 +41,19 @@ It uses generic sci-fi visual language: deep-space backgrounds, neon HUD chrome,
41
41
  - Uses Shadow DOM for the decorative layer to reduce interference from host application styles.
42
42
  - Respects `prefers-reduced-motion: reduce` by disabling major animations.
43
43
 
44
+ ### Compatibility
45
+
46
+ Recommended DSH version: **`0.1.0-rc.7` or newer**.
47
+
48
+ Known incompatible versions:
49
+
50
+ - `0.0.1-rc.1`
51
+ - `0.0.1-rc.2`
52
+
53
+ These early versions of `@deepseek-ai/dsh-client-ui-theme` do not expose `theme.overrideTokens()`, which this skin uses for its token override layer.
54
+
55
+ Versions from `0.0.1-rc.3` onward include the required theme APIs in static inspection, but DSH releases before `0.1.0-rc.7` are not actively tested by this package. If you are using an older DSH release, upgrade DSH first before reporting skin issues.
56
+
44
57
  ### Telemetry panel
45
58
 
46
59
  When DSH Web exposes bottom status/usage data, the skin keeps the original UI layout in place and mirrors the detected global telemetry into a non-interactive bottom-right HUD panel.
@@ -193,6 +206,19 @@ DSH Web GUI 的非官方电影感太空歌剧暗色皮肤插件。
193
206
  - 装饰层使用 Shadow DOM,减少宿主应用样式对行星、扫描盘、点击脉冲等几何形态的污染。
194
207
  - 遵循 `prefers-reduced-motion: reduce`,会关闭主要动画。
195
208
 
209
+ ### 兼容性
210
+
211
+ 推荐 DSH 版本:**`0.1.0-rc.7` 或更新版本**。
212
+
213
+ 已知不兼容版本:
214
+
215
+ - `0.0.1-rc.1`
216
+ - `0.0.1-rc.2`
217
+
218
+ 这些早期版本的 `@deepseek-ai/dsh-client-ui-theme` 不提供 `theme.overrideTokens()`,而本皮肤需要它来应用主题 token 覆盖层。
219
+
220
+ 从 `0.0.1-rc.3` 开始,静态检查显示已具备本插件需要的 theme API;但 `0.1.0-rc.7` 之前的 DSH 版本本包不会主动测试。如果你正在使用更早的 DSH 版本,建议先升级 DSH,再反馈皮肤兼容问题。
221
+
196
222
  ### Telemetry 面板
197
223
 
198
224
  当 DSH Web 暴露底部状态/用量信息时,皮肤会保留原始 UI 布局,并将检测到的全局 telemetry 镜像到右下角的非交互 HUD 面板中。
package/lib/client.js CHANGED
@@ -11,6 +11,7 @@ window.__ModuleLoader__.load({
11
11
  const STYLE_ID = "dsh-skin-galactic-opera-style";
12
12
  const DECOR_ID = "dsh-skin-galactic-opera-decor";
13
13
  const DECOR_HOST_CLASS = "dsh-skin-galactic-opera-decor-host";
14
+ const PAGE_HIDDEN_CLASS = "dsh-skin-galactic-opera-page-hidden";
14
15
  const CSS_TAG_ID = `${PLUGIN_ID}/global.css`;
15
16
 
16
17
  const TOKENS = Object.freeze({
@@ -170,6 +171,16 @@ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-input-active :is(.dsh-skin-
170
171
  opacity: 0.16 !important;
171
172
  }
172
173
 
174
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden::before,
175
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden::after,
176
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden #root::before,
177
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden .dsh-skin-galactic-opera-decor::before,
178
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden .dsh-skin-galactic-opera-decor *,
179
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden .dsh-skin-galactic-opera-decor *::before,
180
+ body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden .dsh-skin-galactic-opera-decor *::after {
181
+ animation-play-state: paused !important;
182
+ }
183
+
173
184
  body.dsh-skin-galactic-opera #root > *,
174
185
  body.dsh-skin-galactic-opera main,
175
186
  body.dsh-skin-galactic-opera aside,
@@ -1149,6 +1160,7 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1149
1160
  return `${CSS
1150
1161
  .replace(/body\.dsh-skin-galactic-opera\.dsh-skin-galactic-opera-input-active\s+/g, ":host-context(body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-input-active) ")
1151
1162
  .replace(/body\.dsh-skin-galactic-opera\.dsh-skin-galactic-opera-hyperspace\s+/g, ":host-context(body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-hyperspace) ")
1163
+ .replace(/body\.dsh-skin-galactic-opera\.dsh-skin-galactic-opera-page-hidden\s+/g, ":host-context(body.dsh-skin-galactic-opera.dsh-skin-galactic-opera-page-hidden) ")
1152
1164
  .replace(/body\.dsh-skin-galactic-opera\s+/g, "")}
1153
1165
 
1154
1166
  .dsh-skin-galactic-opera-decor {
@@ -1158,6 +1170,11 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1158
1170
  height: auto !important;
1159
1171
  z-index: auto !important;
1160
1172
  }
1173
+
1174
+ .dsh-skin-pulse,
1175
+ .dsh-skin-scanner-target {
1176
+ position: absolute !important;
1177
+ }
1161
1178
  `;
1162
1179
  }
1163
1180
 
@@ -1249,6 +1266,20 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1249
1266
  }
1250
1267
 
1251
1268
  const EDITABLE_CONTENT_SELECTOR = `[contenteditable]:not([contenteditable="false"])`;
1269
+
1270
+ function installVisibilityState() {
1271
+ if (typeof document === "undefined" || typeof window === "undefined") return () => {};
1272
+ const syncVisibilityClass = () => {
1273
+ document.body.classList.toggle(PAGE_HIDDEN_CLASS, document.hidden);
1274
+ };
1275
+ syncVisibilityClass();
1276
+ document.addEventListener("visibilitychange", syncVisibilityClass);
1277
+ return () => {
1278
+ document.removeEventListener("visibilitychange", syncVisibilityClass);
1279
+ document.body.classList.remove(PAGE_HIDDEN_CLASS);
1280
+ };
1281
+ }
1282
+
1252
1283
  let reducedMotionQuery;
1253
1284
  let reducedMotionMatches = false;
1254
1285
  let reducedMotionListener;
@@ -1313,6 +1344,12 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1313
1344
  const decorLayer = getDecorLayer(decor);
1314
1345
  const scanner = decorRoot?.querySelector?.(".dsh-skin-scanner");
1315
1346
  const scannerTarget = decorRoot?.querySelector?.(".dsh-skin-scanner-target");
1347
+ const useDecorLocalCoordinates = decor.shadowRoot !== null && decorLayer instanceof HTMLElement;
1348
+ const toDecorPoint = (clientX, clientY) => {
1349
+ if (!useDecorLocalCoordinates) return { x: clientX, y: clientY };
1350
+ const rect = decorLayer.getBoundingClientRect();
1351
+ return { x: clientX - rect.left, y: clientY - rect.top };
1352
+ };
1316
1353
  const pulseColors = ["rgba(125,227,255,0.94)", "rgba(255,207,92,0.92)", "rgba(255,71,120,0.88)"];
1317
1354
  const updatePointer = () => {
1318
1355
  frame = 0;
@@ -1345,8 +1382,9 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1345
1382
  const scannerRadius = Math.max(10, Math.min(rect.width, rect.height) * 0.38);
1346
1383
  const projectedRadius = Math.min(1, distance / maxDistance) * scannerRadius;
1347
1384
  const angle = Math.atan2(dy, dx);
1348
- document.body.style.setProperty("--dsh-skin-scanner-dot-x", `${Math.round(scannerCenterX + Math.cos(angle) * projectedRadius)}px`);
1349
- document.body.style.setProperty("--dsh-skin-scanner-dot-y", `${Math.round(scannerCenterY + Math.sin(angle) * projectedRadius)}px`);
1385
+ const scannerDot = toDecorPoint(scannerCenterX + Math.cos(angle) * projectedRadius, scannerCenterY + Math.sin(angle) * projectedRadius);
1386
+ document.body.style.setProperty("--dsh-skin-scanner-dot-x", `${Math.round(scannerDot.x)}px`);
1387
+ document.body.style.setProperty("--dsh-skin-scanner-dot-y", `${Math.round(scannerDot.y)}px`);
1350
1388
  scannerTarget.removeAttribute("data-ping");
1351
1389
  // Force the double-flash animation to restart on repeated clicks at the same point.
1352
1390
  void scannerTarget.offsetWidth;
@@ -1393,8 +1431,9 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1393
1431
  pulse.className = "dsh-skin-pulse";
1394
1432
  setImportantStyle(pulse, "border-radius", "50%");
1395
1433
  setImportantStyle(pulse, "box-sizing", "border-box");
1396
- pulse.style.setProperty("--dsh-skin-pulse-x", `${event.clientX}px`);
1397
- pulse.style.setProperty("--dsh-skin-pulse-y", `${event.clientY}px`);
1434
+ const pulsePoint = toDecorPoint(event.clientX, event.clientY);
1435
+ pulse.style.setProperty("--dsh-skin-pulse-x", `${Math.round(pulsePoint.x)}px`);
1436
+ pulse.style.setProperty("--dsh-skin-pulse-y", `${Math.round(pulsePoint.y)}px`);
1398
1437
  pulse.style.setProperty("--dsh-skin-pulse-color", pulseColors[pulseIndex % pulseColors.length]);
1399
1438
  pulseIndex += 1;
1400
1439
  if (decorLayer instanceof Element) decorLayer.appendChild(pulse);
@@ -1676,7 +1715,7 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1676
1715
  const update = ({ ignorePause = false } = {}) => {
1677
1716
  frame = 0;
1678
1717
  frameIgnoresTelemetryPause = false;
1679
- if (!ignorePause && isTelemetryPaused()) return;
1718
+ if (document.hidden || (!ignorePause && isTelemetryPaused())) return;
1680
1719
  let nextSources = currentSources;
1681
1720
  let text = getSourcesText(nextSources);
1682
1721
  const needsFullScan = forceTelemetryRescan || telemetryRefreshesSinceFullScan >= TELEMETRY_FULL_RESCAN_UPDATES || !areCachedSourcesUsable();
@@ -1696,7 +1735,7 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1696
1735
  renderPanel(text);
1697
1736
  };
1698
1737
  const schedule = ({ delay = TELEMETRY_UPDATE_DELAY, force = false, ignorePause = false } = {}) => {
1699
- if (!ignorePause && isTelemetryPaused()) return;
1738
+ if (document.hidden || (!ignorePause && isTelemetryPaused())) return;
1700
1739
  if (force) forceTelemetryRescan = true;
1701
1740
  if (frame !== 0) {
1702
1741
  if (!force && delay >= TELEMETRY_UPDATE_DELAY) return;
@@ -1778,6 +1817,20 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1778
1817
  forceTelemetryRescan = true;
1779
1818
  schedule();
1780
1819
  };
1820
+ const onTelemetryVisibilityChange = () => {
1821
+ if (document.hidden) {
1822
+ if (frame !== 0) {
1823
+ window.clearTimeout(frame);
1824
+ frame = 0;
1825
+ }
1826
+ frameIgnoresTelemetryPause = false;
1827
+ for (const timer of telemetryNavigationTimers) window.clearTimeout(timer);
1828
+ telemetryNavigationTimers.clear();
1829
+ return;
1830
+ }
1831
+ schedule({ delay: 0, force: true, ignorePause: true });
1832
+ setTelemetryNavigationTimer(() => schedule({ delay: 0, force: true, ignorePause: true }), TELEMETRY_NAV_FOLLOWUP_DELAY);
1833
+ };
1781
1834
  const observer = typeof MutationObserver === "undefined" ? undefined : new MutationObserver((mutations) => {
1782
1835
  if (mutations.every(isSkinOwnedMutation)) return;
1783
1836
  if (isTelemetryPaused() && mutations.some(isPotentialTelemetryNavigationMutation)) {
@@ -1790,6 +1843,7 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1790
1843
  schedule();
1791
1844
  observer?.observe(document.body, { childList: true, subtree: true, characterData: true });
1792
1845
  window.addEventListener("resize", onTelemetryResize, { passive: true });
1846
+ document.addEventListener("visibilitychange", onTelemetryVisibilityChange);
1793
1847
  window.addEventListener("popstate", checkTelemetryLocation, { passive: true });
1794
1848
  window.addEventListener("hashchange", checkTelemetryLocation, { passive: true });
1795
1849
  document.addEventListener("click", onTelemetryNavigationClick, true);
@@ -1800,6 +1854,7 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1800
1854
  if (telemetryPushState !== undefined && window.history.pushState === telemetryPushState) window.history.pushState = originalPushState;
1801
1855
  if (telemetryReplaceState !== undefined && window.history.replaceState === telemetryReplaceState) window.history.replaceState = originalReplaceState;
1802
1856
  window.removeEventListener("resize", onTelemetryResize);
1857
+ document.removeEventListener("visibilitychange", onTelemetryVisibilityChange);
1803
1858
  window.removeEventListener("popstate", checkTelemetryLocation);
1804
1859
  window.removeEventListener("hashchange", checkTelemetryLocation);
1805
1860
  document.removeEventListener("click", onTelemetryNavigationClick, true);
@@ -1821,12 +1876,14 @@ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
1821
1876
  ctx.effect(() => {
1822
1877
  const style = ensureStyle();
1823
1878
  if (typeof document !== "undefined") document.body.classList.add("dsh-skin-galactic-opera");
1879
+ const disposeVisibilityState = installVisibilityState();
1824
1880
  const decor = ensureDecor();
1825
1881
  const disposeInteractions = installInteractions(decor);
1826
1882
  const disposeTelemetryPanel = installTelemetryPanel();
1827
1883
  return () => {
1828
1884
  disposeTelemetryPanel();
1829
1885
  disposeInteractions();
1886
+ disposeVisibilityState();
1830
1887
  disposeReducedMotionCache();
1831
1888
  if (typeof document !== "undefined") document.body.classList.remove("dsh-skin-galactic-opera");
1832
1889
  decor?.remove();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-skin-galactic-opera",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
4
4
  "description": "Unofficial cinematic space-opera dark skin bundle for the DSH Web GUI.",
5
5
  "repository": {
6
6
  "type": "git",