dsh-turn-navigator 0.4.0 → 0.4.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 +8 -0
- package/lib/client.js +24 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.1] - 2026-09-03
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Rail tooltip/aria now show the TRUE turn number** — the "第 N 轮" label previously used `entry.index` (the position within each source's own subset: every history page starts at 1, and window-only extras start at 1 too), so a merged long session could read "第 38 轮" followed by "第 2 轮". The label now comes from `entry.turn` (absolute, consistent with the "正在定位第 N 轮" bubble), and the merged list re-derives `index` purely as its sorted list position — `src/client/TurnNavRail.tsx`, `src/client/turns.ts`.
|
|
8
|
+
- **Rail viewport follows the active turn** — the rail is capped at 30vh with an internal scrollbar, so in a long session the current turn's capsule sat outside the visible band on open/scroll. A new effect scrolls the active capsule into the rail's viewport (centered) whenever it leaves it, with a pointer-over guard so a user browsing the rail is never yanked — `src/client/TurnNavRail.tsx`.
|
|
9
|
+
- **Compile/runtime hardening on dsh 0.1.2+** — the newer `ClientContext` type surface dropped `connection.api` (the 0.1.2+ journal channel replaced the RPC); the browser side now reads it structurally (`(connection as { api?: HistoryApi })?.api`) so both older and newer hosts compile and run — `src/client/index.ts`.
|
|
10
|
+
|
|
3
11
|
## [0.4.0] - 2026-08-29
|
|
4
12
|
|
|
5
13
|
### Added
|
package/lib/client.js
CHANGED
|
@@ -442,10 +442,10 @@ window.__ModuleLoader__.load({
|
|
|
442
442
|
function clampFeedbackY(y) {
|
|
443
443
|
return Math.max(24, Math.min(y, window.innerHeight - 24));
|
|
444
444
|
}
|
|
445
|
-
/** Tooltip body for one turn:
|
|
445
|
+
/** Tooltip body for one turn: turn number, time, full summary. */
|
|
446
446
|
function tooltipText(entry, t) {
|
|
447
447
|
const time = formatTime(entry.startTime);
|
|
448
|
-
const label = t("turnLabel", { n: String(entry.
|
|
448
|
+
const label = t("turnLabel", { n: String(entry.turn) });
|
|
449
449
|
const body = entry.fullText || entry.summary || t("noSummary");
|
|
450
450
|
const lines = [label];
|
|
451
451
|
if (time !== "") lines.push(time);
|
|
@@ -480,6 +480,7 @@ window.__ModuleLoader__.load({
|
|
|
480
480
|
const railRef = (0, react.useRef)(null);
|
|
481
481
|
const tipRef = (0, react.useRef)(null);
|
|
482
482
|
const hoverScrollRef = (0, react.useRef)(null);
|
|
483
|
+
const pointerOverRailRef = (0, react.useRef)(false);
|
|
483
484
|
(0, react.useEffect)(() => {
|
|
484
485
|
let timer = null;
|
|
485
486
|
const check = () => {
|
|
@@ -545,7 +546,10 @@ window.__ModuleLoader__.load({
|
|
|
545
546
|
if (historyTurns.length === 0) return windowTurns;
|
|
546
547
|
const historySet = new Set(historyTurns.map((entry) => entry.turn));
|
|
547
548
|
const extras = windowTurns.filter((entry) => !historySet.has(entry.turn));
|
|
548
|
-
return [...historyTurns, ...extras].sort((a, b) => a.turn - b.turn)
|
|
549
|
+
return [...historyTurns, ...extras].sort((a, b) => a.turn - b.turn).map((entry, i) => ({
|
|
550
|
+
...entry,
|
|
551
|
+
index: i + 1
|
|
552
|
+
}));
|
|
549
553
|
}, [historyTurns, windowTurns]);
|
|
550
554
|
const [activeTurn, setActiveTurn] = (0, react.useState)(null);
|
|
551
555
|
(0, react.useEffect)(() => {
|
|
@@ -582,6 +586,19 @@ window.__ModuleLoader__.load({
|
|
|
582
586
|
if (frame !== null) cancelAnimationFrame(frame);
|
|
583
587
|
};
|
|
584
588
|
}, [turns.length]);
|
|
589
|
+
(0, react.useEffect)(() => {
|
|
590
|
+
if (activeTurn === null || pointerOverRailRef.current) return;
|
|
591
|
+
const index = turns.findIndex((entry) => entry.turn === activeTurn);
|
|
592
|
+
if (index < 0) return;
|
|
593
|
+
const rail = railRef.current;
|
|
594
|
+
if (rail === null) return;
|
|
595
|
+
const btn = rail.querySelectorAll(".tn-cap-btn")[index];
|
|
596
|
+
if (btn === void 0) return;
|
|
597
|
+
const railRect = rail.getBoundingClientRect();
|
|
598
|
+
const btnRect = btn.getBoundingClientRect();
|
|
599
|
+
if (btnRect.top >= railRect.top && btnRect.bottom <= railRect.bottom) return;
|
|
600
|
+
centerCapsule(index);
|
|
601
|
+
}, [activeTurn, turns]);
|
|
585
602
|
(0, react.useEffect)(() => {
|
|
586
603
|
const rail = railRef.current;
|
|
587
604
|
if (rail === null) return;
|
|
@@ -741,7 +758,11 @@ window.__ModuleLoader__.load({
|
|
|
741
758
|
className: `tn-wrap${officialRail ? " tn-nudge" : ""}`,
|
|
742
759
|
role: "navigation",
|
|
743
760
|
"aria-label": t("rail"),
|
|
761
|
+
onMouseEnter: () => {
|
|
762
|
+
pointerOverRailRef.current = true;
|
|
763
|
+
},
|
|
744
764
|
onMouseLeave: () => {
|
|
765
|
+
pointerOverRailRef.current = false;
|
|
745
766
|
setHoverIndex(-1);
|
|
746
767
|
stopHoverScroll();
|
|
747
768
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-turn-navigator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "DSH Smoothly Turn Nav (DSH STN): full-history piano-key turn rail for DeepSeek Harness (dsh) web conversations — see every turn at a glance, hover to preview, click to jump anywhere, and replace the official (non-disableable) turn rail.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|