dsh-milestone 0.7.1 → 0.7.2
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.md +8 -1
- package/lib/client.js +64 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -140,7 +140,14 @@ shell.overlay (root scope)
|
|
|
140
140
|
## 更新日志
|
|
141
141
|
|
|
142
142
|
<details>
|
|
143
|
-
<summary>v0.7.1 / v0.7.0 / v0.6.6 / v0.6.5 / v0.6.4(点击展开)</summary>
|
|
143
|
+
<summary>v0.7.2 / v0.7.1 / v0.7.0 / v0.6.6 / v0.6.5 / v0.6.4(点击展开)</summary>
|
|
144
|
+
|
|
145
|
+
**v0.7.2** · 点击旧圆点自动翻页定位(深跳鲁棒性)· 459 项测试
|
|
146
|
+
|
|
147
|
+
- **点旧圆点不再"没反应"**:里程碑条覆盖整个会话,但 DOM 只渲染已加载窗口——此前点一个很早的圆点会静默无反应。现在会自动按页载入更早历史直到定位(有界 20 页),定位期间该圆点脉冲提示,失败则安静结束。
|
|
148
|
+
- 站内搜索的"下一个匹配"跳转同样受益。
|
|
149
|
+
|
|
150
|
+
> [GitHub Release v0.7.2](https://github.com/SnowCrescenter-tech/dsh-milestone/releases/tag/v0.7.2)
|
|
144
151
|
|
|
145
152
|
**v0.7.1** · 升级不再被卡(可选 peer + 兼容边界)· 恢复模型与错误徽标 · 跟随官方 0.1.5-rc.2 · 457 项测试
|
|
146
153
|
|
package/lib/client.js
CHANGED
|
@@ -2865,7 +2865,7 @@ window.__ModuleLoader__.load({
|
|
|
2865
2865
|
* Installed plugin version. Injected at build time as
|
|
2866
2866
|
* `__DSH_MILESTONE_VERSION__`; falls back to `0.0.0-dev` when unbuilt.
|
|
2867
2867
|
*/
|
|
2868
|
-
const PLUGIN_VERSION = "0.7.
|
|
2868
|
+
const PLUGIN_VERSION = "0.7.2";
|
|
2869
2869
|
//#endregion
|
|
2870
2870
|
//#region src/client/MilestoneRail.tsx
|
|
2871
2871
|
/**
|
|
@@ -3137,6 +3137,18 @@ window.__ModuleLoader__.load({
|
|
|
3137
3137
|
const DEEP_LINK_MAX_POLLS = 5;
|
|
3138
3138
|
/** P3: bounded polls after `loadOlder`, then the deep link gives up silently. */
|
|
3139
3139
|
const DEEP_LINK_MAX_RETRY_POLLS = 5;
|
|
3140
|
+
/**
|
|
3141
|
+
* Jump paging: the rail's dots cover the WHOLE log while the DOM renders only
|
|
3142
|
+
* the loaded window, so activating an older dot must page history in first.
|
|
3143
|
+
* Bounded so an unreachable target gives up instead of paging forever.
|
|
3144
|
+
*/
|
|
3145
|
+
const LOCATE_MAX_PAGES = 20;
|
|
3146
|
+
/** Delay between a page fetch and the DOM-row recheck (lets React commit). */
|
|
3147
|
+
const LOCATE_POLL_DELAY = 150;
|
|
3148
|
+
/** Pulse applied to the dot whose jump is still paging history in. */
|
|
3149
|
+
const LOCATING_CSS = `@keyframes ms-locating { 0%, 100% { opacity: 1 } 50% { opacity: 0.35 } }
|
|
3150
|
+
[data-locating="true"] { animation: ms-locating 800ms ease-in-out infinite; }
|
|
3151
|
+
@media (prefers-reduced-motion: reduce) { [data-locating="true"] { animation: none } }`;
|
|
3140
3152
|
/** B4 update-check: mount-time silent check delay (ms) — give the harness
|
|
3141
3153
|
* time to settle before hitting the registry. */
|
|
3142
3154
|
const UPDATE_CHECK_MOUNT_DELAY = 1500;
|
|
@@ -3238,6 +3250,13 @@ window.__ModuleLoader__.load({
|
|
|
3238
3250
|
const listRef = (0, react.useRef)(null);
|
|
3239
3251
|
const currentKey = useCurrentAnchor(marks.map((m) => m.key));
|
|
3240
3252
|
/**
|
|
3253
|
+
* Mark key whose jump is still paging older history in; its dot pulses so a
|
|
3254
|
+
* slow locate never looks like a dead click.
|
|
3255
|
+
*/
|
|
3256
|
+
const [locatingKey, setLocatingKey] = (0, react.useState)(null);
|
|
3257
|
+
/** Guards against overlapping locate loops (a second activation is ignored). */
|
|
3258
|
+
const locatingRef = (0, react.useRef)(false);
|
|
3259
|
+
/**
|
|
3241
3260
|
* P3: jump to the chat row with the given node key — smooth-scroll it into
|
|
3242
3261
|
* view and write the position back into the URL hash (`#msg=<key>`) so
|
|
3243
3262
|
* refresh and share preserve it. `history.replaceState` (not a
|
|
@@ -3255,6 +3274,45 @@ window.__ModuleLoader__.load({
|
|
|
3255
3274
|
});
|
|
3256
3275
|
history.replaceState(null, "", buildMessageHash(key));
|
|
3257
3276
|
};
|
|
3277
|
+
/**
|
|
3278
|
+
* Jump to a mark, paging older history in first when its row is not rendered
|
|
3279
|
+
* yet. The dots cover the whole log but the DOM holds only the loaded window,
|
|
3280
|
+
* so without this an older dot is a silent no-op (the official rail pages
|
|
3281
|
+
* unloaded turns in the same way). Bounded to LOCATE_MAX_PAGES fetches; the
|
|
3282
|
+
* dot pulses meanwhile.
|
|
3283
|
+
* @param key - the mark key (deep-link payload + exact-match anchor).
|
|
3284
|
+
* @param messageId - the user/message id (real-harness anchor suffix).
|
|
3285
|
+
*/
|
|
3286
|
+
const locateAndJump = (key, messageId) => {
|
|
3287
|
+
if (findRow(key, messageId) !== null) {
|
|
3288
|
+
jump(key, messageId);
|
|
3289
|
+
return;
|
|
3290
|
+
}
|
|
3291
|
+
if (!hasMore || locatingRef.current) return;
|
|
3292
|
+
locatingRef.current = true;
|
|
3293
|
+
setLocatingKey(key);
|
|
3294
|
+
(async () => {
|
|
3295
|
+
try {
|
|
3296
|
+
for (let page = 0; page < LOCATE_MAX_PAGES; page += 1) {
|
|
3297
|
+
try {
|
|
3298
|
+
await loadOlder();
|
|
3299
|
+
} catch {
|
|
3300
|
+
return;
|
|
3301
|
+
}
|
|
3302
|
+
await new Promise((resolve) => {
|
|
3303
|
+
window.setTimeout(resolve, LOCATE_POLL_DELAY);
|
|
3304
|
+
});
|
|
3305
|
+
if (findRow(key, messageId) !== null) {
|
|
3306
|
+
jump(key, messageId);
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
} finally {
|
|
3311
|
+
locatingRef.current = false;
|
|
3312
|
+
setLocatingKey(null);
|
|
3313
|
+
}
|
|
3314
|
+
})();
|
|
3315
|
+
};
|
|
3258
3316
|
const displayMarks = (0, react.useMemo)(() => {
|
|
3259
3317
|
if (!bookmarksOnly) return marks;
|
|
3260
3318
|
return filterByBookmarks(marks, bookmarkedKeys).visible.map((i) => marks[i]);
|
|
@@ -3624,7 +3682,7 @@ window.__ModuleLoader__.load({
|
|
|
3624
3682
|
activePos: next
|
|
3625
3683
|
}));
|
|
3626
3684
|
const mark = displayMarks[matches[next]];
|
|
3627
|
-
|
|
3685
|
+
locateAndJump(mark.key, mark.messageId);
|
|
3628
3686
|
};
|
|
3629
3687
|
const onSearchKeyDown = (e) => {
|
|
3630
3688
|
if (e.key === "Enter") advanceMatch();
|
|
@@ -4211,6 +4269,7 @@ window.__ModuleLoader__.load({
|
|
|
4211
4269
|
"data-inset": String(inset),
|
|
4212
4270
|
children: [
|
|
4213
4271
|
pulseCss !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: pulseCss }),
|
|
4272
|
+
locatingKey !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: LOCATING_CSS }),
|
|
4214
4273
|
focusActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: buildFocusCss(prefs.focus) }),
|
|
4215
4274
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: MODAL_CSS }),
|
|
4216
4275
|
showLoadOlder && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -5315,7 +5374,7 @@ window.__ModuleLoader__.load({
|
|
|
5315
5374
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5316
5375
|
style: { color: "#8b96ab" },
|
|
5317
5376
|
children: [t("update.current"), ": "]
|
|
5318
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.7.
|
|
5377
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "0.7.2" })] }),
|
|
5319
5378
|
updateCheck.phase === "ok" && updateCheck.latest !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5320
5379
|
"data-update-latest": true,
|
|
5321
5380
|
children: [
|
|
@@ -5493,8 +5552,9 @@ window.__ModuleLoader__.load({
|
|
|
5493
5552
|
top: rect.top + rect.height / 2
|
|
5494
5553
|
});
|
|
5495
5554
|
},
|
|
5496
|
-
onClick: () =>
|
|
5555
|
+
onClick: () => locateAndJump(mark.key, mark.messageId),
|
|
5497
5556
|
"data-rail-dot": true,
|
|
5557
|
+
"data-locating": locatingKey === mark.key ? "true" : void 0,
|
|
5498
5558
|
"data-turn-gap": showGroupGap ? "true" : void 0,
|
|
5499
5559
|
"data-turn": showGroupGap && mark.turn !== void 0 ? mark.turn : void 0,
|
|
5500
5560
|
"data-collapsed-summary": summaryCount !== void 0 ? "true" : void 0,
|
package/package.json
CHANGED