sage-mem 2.1.19 → 2.1.20
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 +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +61 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
**Cross-session memory for DeepSeek Harness — plain markdown files, no database, no worker.**
|
|
14
14
|
|
|
15
|
-
**Current release: `2.1.
|
|
15
|
+
**Current release: `2.1.20`** — the npm badge above is the dynamic source of truth. **Contract baseline: `1.0.0`** — the first stable release, whose public commitments, the capability boundaries and the list of things it explicitly cannot do are frozen in the
|
|
16
16
|
[frozen contract](https://github.com/gezi-wen/sage-mem/blob/main/docs/1.0.0-contract.md).
|
|
17
17
|
|
|
18
18
|
If it is useful to you, a Star is the most direct feedback.
|
package/README.zh.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
> File-based cross-session memory for DeepSeek Harness — every memory is a plain markdown file you can open, edit and delete. No database, no worker, no port.
|
|
16
16
|
|
|
17
|
-
**当前发布版本:`2.1.
|
|
17
|
+
**当前发布版本:`2.1.20`** —— 顶部 npm 徽章是动态权威。**契约基线:`1.0.0`** —— 第一个稳定版,公开承诺、能力边界与「明确做不到」的清单冻结在
|
|
18
18
|
[契约冻结](https://github.com/gezi-wen/sage-mem/blob/main/docs/1.0.0-contract.md)。
|
|
19
19
|
|
|
20
20
|
如果它对你有用,给个 Star 是最直接的反馈。
|
package/lib/client.js
CHANGED
|
@@ -978,6 +978,22 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
978
978
|
return makeCanvas2DRenderer(canvasEl, layout, visBox, hoverBox);
|
|
979
979
|
}
|
|
980
980
|
|
|
981
|
+
/**
|
|
982
|
+
* 星图动画的统一时钟(Q15)。
|
|
983
|
+
*
|
|
984
|
+
* rAF 回调的时间戳与 `performance.now()` 是**同一个原点**(单调、从页面加载起算),
|
|
985
|
+
* 而 `Date.now()` 是 epoch 毫秒 —— 两套混用会让相位变成大负数。旧版入场动画就是这个
|
|
986
|
+
* bug:`t0 = Date.now()` 去减 rAF 的 `ts`,`ts - t0` 恒为大负数 → 永远「入场没结束」,
|
|
987
|
+
* 但每帧画完又不接着排帧,于是入场只画了一帧、相位还是负的。
|
|
988
|
+
*
|
|
989
|
+
* 星图里所有「过了多少毫秒」都走这里(freshness 那种墙钟判断除外,那本来就该用 Date.now)。
|
|
990
|
+
*/
|
|
991
|
+
function starmapNow() {
|
|
992
|
+
return (typeof performance !== "undefined" && performance && typeof performance.now === "function")
|
|
993
|
+
? performance.now()
|
|
994
|
+
: Date.now();
|
|
995
|
+
}
|
|
996
|
+
|
|
981
997
|
// ── 星图主体(canvas + 图例 + 详情面板)──
|
|
982
998
|
function StarMap(props) {
|
|
983
999
|
const ctx = props.ctx || appCtx;
|
|
@@ -1108,16 +1124,18 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1108
1124
|
if (!data || !layout || !canvasEl) return undefined;
|
|
1109
1125
|
const renderer = createRenderer(canvasEl, layout, visBox, hoverBox);
|
|
1110
1126
|
drawBox.renderer = renderer;
|
|
1111
|
-
|
|
1127
|
+
// t0 与 rAF 的 ts 必须同一时钟(见 starmapNow)。旧版用 Date.now 当 t0,
|
|
1128
|
+
// 于是相位是 epoch 级的大负数,入场窗口永远关不上。
|
|
1129
|
+
drawBox.t0 = starmapNow();
|
|
1112
1130
|
renderer.render(0);
|
|
1113
1131
|
|
|
1114
1132
|
// ── 按需重绘(重做过一版)──
|
|
1115
1133
|
// 旧版是一个常驻 rAF 循环:只要星图页开着就永远满帧重画,后台标签页也一样,
|
|
1116
1134
|
// 而画面其实早就静止了。新的规矩只有三种情况会画:
|
|
1117
1135
|
// 1. 入场展示动画期间(约 1.3s,星点按诞生时间依次点亮)
|
|
1118
|
-
// 2. 有人调 markDirty()(hover / 数据或筛选变化 /
|
|
1136
|
+
// 2. 有人调 markDirty()(hover / 数据或筛选变化 / 尺寸变化 / 回放)
|
|
1119
1137
|
// 3. 诞生回放进行中
|
|
1120
|
-
// 其余时间一帧都不画。另外帧率封顶 30fps
|
|
1138
|
+
// 其余时间一帧都不画。另外帧率封顶 30fps,页面不可见时暂停(回来再补一帧)。
|
|
1121
1139
|
const FRAME_MS = 1000 / 30;
|
|
1122
1140
|
const ENTRANCE_MS = 1300;
|
|
1123
1141
|
let raf = 0;
|
|
@@ -1126,26 +1144,51 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1126
1144
|
function schedule() {
|
|
1127
1145
|
if (!raf) raf = window.requestAnimationFrame(tick);
|
|
1128
1146
|
}
|
|
1147
|
+
/** 还需要连续帧的两种状态:入场动画没走完 / 诞生回放激活。 */
|
|
1148
|
+
function animating(ts) {
|
|
1149
|
+
return (ts - drawBox.t0 < ENTRANCE_MS) || replayBox.active === true;
|
|
1150
|
+
}
|
|
1129
1151
|
function tick(ts) {
|
|
1130
1152
|
raf = 0;
|
|
1131
1153
|
if (!drawBox.renderer) return;
|
|
1132
|
-
if (ts
|
|
1154
|
+
if (animating(ts)) dirty = true;
|
|
1155
|
+
// 页面不可见:这一帧不画,也**不**再排下一帧(旧版直接 return,结果从后台
|
|
1156
|
+
// 切回来就再也不画了)。visibilitychange 回到可见时会补一帧。
|
|
1157
|
+
if (typeof document !== "undefined" && document && document.hidden) {
|
|
1158
|
+
dirty = true;
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1133
1161
|
if (!dirty) return;
|
|
1134
|
-
if (typeof document !== "undefined" && document.hidden) return;
|
|
1135
1162
|
if (ts - lastDraw < FRAME_MS) { schedule(); return; }
|
|
1136
1163
|
dirty = false;
|
|
1137
1164
|
lastDraw = ts;
|
|
1138
1165
|
drawBox.renderer.render((ts - drawBox.t0) / 1000);
|
|
1166
|
+
// 入场 / 回放没结束就接着排下一帧;静止之后一帧都不排(不允许常驻空转)。
|
|
1167
|
+
if (animating(ts)) schedule();
|
|
1139
1168
|
}
|
|
1140
1169
|
drawBox.markDirty = function () { dirty = true; schedule(); };
|
|
1170
|
+
// 从后台标签页回来:补一帧(隐藏期间不排帧,靠这个事件唤醒)。
|
|
1171
|
+
const onVisibility = function () {
|
|
1172
|
+
if (typeof document === "undefined" || !document) return;
|
|
1173
|
+
if (document.hidden) { dirty = true; return; }
|
|
1174
|
+
dirty = true;
|
|
1175
|
+
schedule();
|
|
1176
|
+
};
|
|
1177
|
+
if (typeof document !== "undefined" && document && typeof document.addEventListener === "function") {
|
|
1178
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
1179
|
+
}
|
|
1141
1180
|
schedule();
|
|
1142
1181
|
|
|
1143
1182
|
return () => {
|
|
1144
1183
|
window.cancelAnimationFrame(raf);
|
|
1184
|
+
if (typeof document !== "undefined" && document && typeof document.removeEventListener === "function") {
|
|
1185
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
1186
|
+
}
|
|
1145
1187
|
drawBox.renderer = null;
|
|
1146
1188
|
drawBox.markDirty = null;
|
|
1147
1189
|
replayBox.active = false;
|
|
1148
1190
|
window.cancelAnimationFrame(replayBox.raf);
|
|
1191
|
+
replayBox.raf = 0;
|
|
1149
1192
|
if (renderer.destroy) renderer.destroy();
|
|
1150
1193
|
};
|
|
1151
1194
|
}, [data, layout, canvasEl, themeKey]);
|
|
@@ -1163,7 +1206,7 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1163
1206
|
if (drawBox.renderer.setGeometry) drawBox.renderer.setGeometry();
|
|
1164
1207
|
// 交给按需重绘那一套:下一帧画(<33ms),不再这里直接 render
|
|
1165
1208
|
if (drawBox.markDirty) drawBox.markDirty();
|
|
1166
|
-
else drawBox.renderer.render((
|
|
1209
|
+
else drawBox.renderer.render((starmapNow() - drawBox.t0) / 1000);
|
|
1167
1210
|
}
|
|
1168
1211
|
}, [data, layout, kindsOn, query, showArchive]);
|
|
1169
1212
|
|
|
@@ -1298,7 +1341,9 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1298
1341
|
function stopReplay() {
|
|
1299
1342
|
replayBox.active = false;
|
|
1300
1343
|
window.cancelAnimationFrame(replayBox.raf);
|
|
1344
|
+
replayBox.raf = 0;
|
|
1301
1345
|
if (drawBox.renderer && drawBox.renderer.setReveal) drawBox.renderer.setReveal(null);
|
|
1346
|
+
// 停回放要回到「全亮」:markDirty 让绘制循环补这一帧。
|
|
1302
1347
|
if (drawBox.markDirty) drawBox.markDirty();
|
|
1303
1348
|
setReplaying(false);
|
|
1304
1349
|
setReplayDate(null);
|
|
@@ -1316,17 +1361,22 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1316
1361
|
window.cancelAnimationFrame(replayBox.raf);
|
|
1317
1362
|
const span = Math.max(mx - mn, 60000) + 2400;
|
|
1318
1363
|
const dur = 18000;
|
|
1319
|
-
|
|
1364
|
+
let t0 = null;
|
|
1320
1365
|
replayBox.active = true;
|
|
1321
1366
|
setReplaying(true);
|
|
1367
|
+
// 关键:回放期间由「按需绘制」那套逐帧重画。setReveal 只改渲染器的变量,
|
|
1368
|
+
// 不标脏 / 不唤醒绘制循环的话屏幕上什么都不动(旧版就是这样,回放是空的)。
|
|
1369
|
+
if (drawBox.markDirty) drawBox.markDirty();
|
|
1322
1370
|
let lastLabel = 0;
|
|
1323
|
-
const tick = () => {
|
|
1371
|
+
const tick = (ts) => {
|
|
1324
1372
|
if (!replayBox.active) return;
|
|
1325
|
-
|
|
1373
|
+
// 进度也用 rAF 的单调时间戳,和绘制循环同一个时钟(旧版用 Date.now 又混了一次)。
|
|
1374
|
+
if (t0 === null) t0 = ts;
|
|
1375
|
+
const p = Math.min(1, (ts - t0) / dur);
|
|
1326
1376
|
const cur = mn - 1200 + span * p;
|
|
1327
1377
|
if (drawBox.renderer && drawBox.renderer.setReveal) drawBox.renderer.setReveal(cur);
|
|
1328
|
-
if (
|
|
1329
|
-
lastLabel =
|
|
1378
|
+
if (ts - lastLabel > 250) {
|
|
1379
|
+
lastLabel = ts;
|
|
1330
1380
|
setReplayDate(Math.min(mx, cur));
|
|
1331
1381
|
}
|
|
1332
1382
|
if (p >= 1) { stopReplay(); return; }
|
package/package.json
CHANGED