dsh-client-ui-seaglass 1.6.0 → 1.6.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/README.en.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](README.md)
4
4
 
5
- > **Compatibility** — DSH `0.1.2-rc.1` · **Updated** 2026-09-06 · Plugin v1.6.0
5
+ > **Compatibility** — DSH `0.1.2-rc.1` · **Updated** 2026-09-06 · Plugin v1.6.1
6
6
 
7
7
  Seaglass is a highly customizable glassmorphism theme: it turns many surfaces into frosted-glass panes, and you can use your own images or videos as the backdrop. Switch the theme off and you are back to the stock UI — without changing a single line of DSH source. If you like the theme, feedback and PRs are welcome.
8
8
 
@@ -34,5 +34,6 @@ Clone the repo anywhere, then hand the **local folder** straight to `dsh plugin
34
34
 
35
35
  ```sh
36
36
  git clone https://github.com/xiyunyunyun/dsh-client-ui-seaglass.git
37
+ npm install
37
38
  dsh plugin --profile web add "C:\\path\\to\\dsh-client-ui-seaglass" # your actual clone path (absolute path recommended)
38
39
  ```
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.en.md) | 中文
4
4
 
5
- > **适配版本** — DSH `0.1.2-rc.1` · **更新日期** 2026-09-06 · 插件 v1.6.0
5
+ > **适配版本** — DSH `0.1.2-rc.1` · **更新日期** 2026-09-06 · 插件 v1.6.1
6
6
 
7
7
  Seaglass 是一个高自由度的玻璃质感主题。把许多页面做成了磨砂玻璃片,你可以自定义图片和视频作为背景。关掉开关就回到原生界面,不改 DSH 任何一行源码。如果你喜欢该主题,欢迎提出建议或者拉到本地修改并提交PR。
8
8
 
@@ -34,6 +34,7 @@ dsh plugin --profile web add dsh-client-ui-seaglass
34
34
 
35
35
  ```sh
36
36
  git clone https://github.com/xiyunyunyun/dsh-client-ui-seaglass.git
37
+ npm install
37
38
  dsh plugin --profile web add "C:\path\to\dsh-client-ui-seaglass" # 用你的实际克隆路径(建议绝对路径)
38
39
  ```
39
40
 
package/assets/11.png ADDED
Binary file
package/assets/12.png ADDED
Binary file
package/assets/13.png ADDED
Binary file
package/assets/14.png ADDED
Binary file
package/lib/client.js CHANGED
@@ -1991,7 +1991,7 @@ function closestSpot(target) {
1991
1991
  let spotCache = null;
1992
1992
  /** Drop the shared spot cache (the seam stamper calls this after touching
1993
1993
  * any spot attribute; a full re-query happens on the next spotElements). */
1994
- function invalidateSpotCache$1() {
1994
+ function invalidateSpotCache() {
1995
1995
  spotCache = null;
1996
1996
  }
1997
1997
  /** Every stamped pane in document order. */
@@ -2550,12 +2550,42 @@ function stampPopoverShell(el, cs) {
2550
2550
  * passes skip the memo: a root REUSED by React for a different view (same
2551
2551
  * div, swapped children) gets re-judged within one catch-all period. */
2552
2552
  const chatViewRoots = new WeakSet();
2553
+ /** Grace period before a non-chat verdict is TRUSTED. When a session opens,
2554
+ * the view root mounts FIRST and the chat marker set lands ~1.3s later
2555
+ * (measured: the conversation payload is loaded asynchronously) — a root
2556
+ * judged on its first frames reads as a plugin view, gets stamped
2557
+ * data-dsh-view + a spot on the ROOT ITSELF, and the whole message flow
2558
+ * tilts under the cursor until the next pass strips it (~1.7s of visibly
2559
+ * skewed text). So a fresh root enters a grace window instead: chat
2560
+ * markers appearing within it flip the verdict for free (every mutation
2561
+ * pass re-judges), and only a root that is STILL non-chat after the window
2562
+ * is stamped. The cost is a one-off ~1.5s glass delay for genuine plugin
2563
+ * views. */
2564
+ const PLUGIN_VIEW_GRACE_MS = 1500;
2565
+ const pluginViewFirstSeen = new WeakMap();
2566
+ let graceRecheckTimer = 0;
2567
+ /** Whether the stamper's feeds are live. The grace recheck timer can outlive
2568
+ * startSeamStamper (its closure owns no shared state with this module-level
2569
+ * helper), so the timer checks this flag instead of a closure variable. */
2570
+ let seamStamperActive = false;
2571
+ /** One delayed full re-check pass so a root whose grace window expires
2572
+ * between mutations still gets stamped (mutation passes alone may not fire
2573
+ * once the view settles). Idempotent — a pending timer is never doubled. */
2574
+ function scheduleGraceRecheck() {
2575
+ if (graceRecheckTimer !== 0) return;
2576
+ graceRecheckTimer = window.setTimeout(() => {
2577
+ graceRecheckTimer = 0;
2578
+ if (!seamStamperActive) return;
2579
+ stampAll(null);
2580
+ }, PLUGIN_VIEW_GRACE_MS + 50);
2581
+ }
2553
2582
  function stampPluginViews(full) {
2554
2583
  let touched = false;
2555
2584
  for (const root of document.querySelectorAll("[data-slot=\"conversation.view\"] > *")) {
2556
2585
  const isChat = !full && chatViewRoots.has(root) || root.querySelector("[data-slot^='conversation.chat'], [data-slot^='tool.call'], [data-composer-card], [data-dsh-inputbar]") !== null;
2557
- if (isChat) chatViewRoots.add(root);
2558
2586
  if (isChat) {
2587
+ chatViewRoots.add(root);
2588
+ pluginViewFirstSeen.delete(root);
2559
2589
  if (root.hasAttribute("data-dsh-view")) {
2560
2590
  root.removeAttribute("data-dsh-view");
2561
2591
  touched = true;
@@ -2566,6 +2596,16 @@ function stampPluginViews(full) {
2566
2596
  }
2567
2597
  continue;
2568
2598
  }
2599
+ const seen = pluginViewFirstSeen.get(root);
2600
+ if (seen === undefined) {
2601
+ pluginViewFirstSeen.set(root, performance.now());
2602
+ scheduleGraceRecheck();
2603
+ continue;
2604
+ }
2605
+ if (performance.now() - seen < PLUGIN_VIEW_GRACE_MS) {
2606
+ scheduleGraceRecheck();
2607
+ continue;
2608
+ }
2569
2609
  if (!root.hasAttribute("data-dsh-view")) {
2570
2610
  root.setAttribute("data-dsh-view", "");
2571
2611
  touched = true;
@@ -2593,6 +2633,7 @@ function stampPluginViews(full) {
2593
2633
  * @returns a disposer that disconnects the observer.
2594
2634
  */
2595
2635
  function startSeamStamper() {
2636
+ seamStamperActive = true;
2596
2637
  stampAll(null);
2597
2638
  let scheduled = 0;
2598
2639
  let disposed = false;
@@ -2624,9 +2665,12 @@ function startSeamStamper() {
2624
2665
  }, 800);
2625
2666
  return () => {
2626
2667
  disposed = true;
2668
+ seamStamperActive = false;
2627
2669
  if (scheduled !== 0) cancelAnimationFrame(scheduled);
2628
2670
  scheduled = 0;
2629
2671
  window.clearInterval(catchAll);
2672
+ if (graceRecheckTimer !== 0) clearTimeout(graceRecheckTimer);
2673
+ graceRecheckTimer = 0;
2630
2674
  observer.disconnect();
2631
2675
  };
2632
2676
  }
@@ -3360,6 +3404,7 @@ function startSpotlight() {
3360
3404
  if (current === spot) {
3361
3405
  current = null;
3362
3406
  session = null;
3407
+ lastPointer = null;
3363
3408
  }
3364
3409
  const glow = spot.querySelector(`:scope > [${GLOW_ATTR}]`);
3365
3410
  if (glow !== null) glow.style.removeProperty("background-image");
@@ -3368,6 +3413,23 @@ function startSpotlight() {
3368
3413
  }
3369
3414
  easeBack(spot);
3370
3415
  };
3416
+ /** The pointer position whose radial was painted last (viewport space).
3417
+ * Geometry refreshes (the keeper's measure) must repaint the radial
3418
+ * against it — see the keeper callback below. */
3419
+ let lastPointer = null;
3420
+ /** Write (or clear) the glow radial for a pointer position. The gradient's
3421
+ * at coordinates are relative to the glow overlay's box (the visible
3422
+ * glass union), so whenever that box moves the radial must be rewritten
3423
+ * in the same breath — a stale at against a moved box misplaces the glow
3424
+ * by exactly the box shift. */
3425
+ const writeGlow = (s, clientX, clientY) => {
3426
+ if (s.glow === null) return;
3427
+ if (glowGated()) {
3428
+ s.glow.style.backgroundImage = `radial-gradient(${GLOW_RADIUS}px at ${clientX - s.visual.left}px ${clientY - s.visual.top}px, var(--dsh-aqua-spot-color, ${GLOW_FALLBACK}), transparent 70%)`;
3429
+ } else {
3430
+ s.glow.style.removeProperty("background-image");
3431
+ }
3432
+ };
3371
3433
  /** Capture (or refresh) the hover geometry; sets the glow overlay box. */
3372
3434
  const measure = (spot) => {
3373
3435
  const visual = visualRect(spot);
@@ -3391,6 +3453,7 @@ function startSpotlight() {
3391
3453
  if (raf !== 0) return;
3392
3454
  raf = requestAnimationFrame(() => {
3393
3455
  raf = 0;
3456
+ if (session !== s) return;
3394
3457
  const { spot, visual, local } = s;
3395
3458
  if (!inside(visual, clientX, clientY)) {
3396
3459
  clearSpot(spot);
@@ -3402,11 +3465,11 @@ function startSpotlight() {
3402
3465
  glow = s.glow;
3403
3466
  }
3404
3467
  if (glow !== null) {
3405
- if (glowGated()) {
3406
- glow.style.backgroundImage = `radial-gradient(${GLOW_RADIUS}px at ${clientX - visual.left}px ${clientY - visual.top}px, var(--dsh-aqua-spot-color, ${GLOW_FALLBACK}), transparent 70%)`;
3407
- } else {
3408
- glow.style.removeProperty("background-image");
3409
- }
3468
+ lastPointer = {
3469
+ x: clientX,
3470
+ y: clientY
3471
+ };
3472
+ writeGlow(s, clientX, clientY);
3410
3473
  }
3411
3474
  if (tiltGated() && tiltable(spot)) {
3412
3475
  const dx = Math.min(.5, Math.max(-.5, (clientX - visual.left) / visual.width - .5));
@@ -3524,7 +3587,9 @@ function startSpotlight() {
3524
3587
  if (session === null || refreshRaf !== 0) return;
3525
3588
  refreshRaf = requestAnimationFrame(() => {
3526
3589
  refreshRaf = 0;
3527
- if (session !== null) session = measure(session.spot);
3590
+ if (session === null) return;
3591
+ session = measure(session.spot);
3592
+ if (lastPointer !== null) writeGlow(session, lastPointer.x, lastPointer.y);
3528
3593
  });
3529
3594
  });
3530
3595
  document.addEventListener("pointermove", onMove, { passive: true });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-client-ui-seaglass",
3
3
  "description": "Seaglass: a highly customizable glassmorphism theme for the Web surface — adjustable blur, frost, fluid or wallpaper backdrop, unified corners, and motion",
4
- "version": "1.6.0",
4
+ "version": "1.6.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -88,7 +88,9 @@
88
88
  "lib/invariant.js",
89
89
  "lib/client.js",
90
90
  "lib/types/**/*.d.ts",
91
- "cordis.patch.yml"
91
+ "cordis.patch.yml",
92
+ "screenshots.json",
93
+ "assets/*.png"
92
94
  ],
93
95
  "dependencies": {
94
96
  "@deepseek-ai/schemastery": "3.18.2"
@@ -0,0 +1,6 @@
1
+ [
2
+ "assets/11.png",
3
+ "assets/12.png",
4
+ "assets/13.png",
5
+ "assets/14.png"
6
+ ]