dsh-prime-memory 0.26.32 → 0.26.34
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 +19 -0
- package/dist/client.js +497 -150
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# 更新日志(Changelog)
|
|
2
2
|
|
|
3
|
+
## [0.26.33] — 2026-11-04
|
|
4
|
+
|
|
5
|
+
- **根治 3D 记忆海「闪一下变暗」:不是掉帧,是 `resize()` 清空 canvas 位图后,空帧被合成器取样**
|
|
6
|
+
|
|
7
|
+
> 先说结论:前一轮「每帧重绘太贵 → 掉帧 → 分层缓存 + 空闲停摆」的修复方向是错的,`0.26.33-local.1..4` 的四次本地迭代都治的是另一个病。真正的机制是——给 `canvas.width` 赋值这个动作本身会清空整张位图(规范行为),而 `ResizeObserver` 回调落在渲染步骤的 **layout 之后、paint 之前**:此刻清空、本帧就 paint,擦白的那一帧必然上屏,下一 rAF 才补画回来。所以观感是「闪一下」,且只在外部尺寸事件触发时出现,与单帧耗时无关。
|
|
8
|
+
|
|
9
|
+
- **像素级取证(推翻多模态观察)**:多模态逐帧描述前后三轮互相矛盾(有说 t=7s 黑屏的、有说全片无黑屏的),改用 `ffmpeg signalstats` 拉全片 **324 帧(30fps / 10.8s)** 客观统计——整片 YAVG 只有 **两个离散值**:`49.20` 与 `40.46`;**22 张暗帧完全同参**(YAVG=40.46 / SATAVG=7.29 / SATMAX=75),说明它们是**同一张图反复出现**,不是渐进掉元素。
|
|
10
|
+
- **定位到「位图透明」而非「元素缺失」**:暗帧下画布三个区域全部塌到同一个纯色——黄簇 `28.69→22.00`、青簇 `26.13→22.00`、空白星空区 `30.95→22.89`,饱和全落到 `≈4.00`。Y=22 是 limited-range,换全范围 luma ≈ 7,而 canvas 元素 L779 的 inline background `#04070f` 实测 luma = **6.99**——对上了:看到的是 canvas 自己的背景色透出来,不是背景层或点层画了一半。
|
|
11
|
+
- **排除合成器/GPU/整窗级问题**:同帧内左侧栏 `61.94→61.81`(-0.2%)、顶部图例条 `100.52→100.52`(**像素零差异**)。窗体重排、resize 抖动、掉驱动都会被这一条否掉。
|
|
12
|
+
- **修复一(幂等 resize)**:`resize()` 改成返回布尔——尺寸与 DPR 都未变时整段跳过,不碰 `canvas.width`。RO 在布局抖动/滚动条出现时的重复触发从此是零成本,不再反复擦白。
|
|
13
|
+
- **修复二(RO 内同步出帧)**:新增 `presentNow()`,RO 里 `if (resize()) presentNow()`——真擦了就**当场画完上屏**,把「清空 → paint 空帧 → 下一 rAF 补画」压成同一渲染步骤内的原子操作。原来那句 `wake()` 只排下一 rAF,本帧照旧 paint 空位图,这是闪烁的直接成因。
|
|
14
|
+
- **修复三(帧句柄自清)**:`frame()` 入口 `raf = 0`。它现在会被 `presentNow()` 同步调用,留着旧句柄会让后续 `wake()` 以为还有链在跑而漏排帧。
|
|
15
|
+
- **附带(非闪烁,每帧税)**:`getComputedStyle` 解色从「每帧 4 次」改为脏标记缓存(`refreshColors()`,仅 resize 与本 effect 重建时置脏)。原实现每帧强制同步样式重算,是纯白交的开销;语义不变——令牌解析时机仍是「主题/尺寸变化」。
|
|
16
|
+
|
|
17
|
+
- **为什么分层缓存没能解决**:那套改动只降低单帧重绘成本、减少 rAF 次数,但闪烁发生在「可见 canvas 被清空」这条独立通路上,三层缓存(edgeCv / nodeCv / buffer)再快也救不了本帧已经 paint 出去的空位图。反过来说,分层缓存本身是有效的性能优化,保留。
|
|
18
|
+
- **未改任何对外契约**:无新配置键、无端点变化、无默认值变化;仅 `client/src/explorer/Scene3D.tsx` 内部渲染调度。
|
|
19
|
+
- **验证口径(可复现)**:重录 30s 后跑 `ffmpeg -i <rec> -vf "signalstats,metadata=print:file=signalstats.txt" -f null NUL`,**暗帧计数必须为 0**(修复前同一口径为 22/324 帧)。本机已核对:`desktop` profile 装的是 `v0.26.33-local.4`,`dist/client.js` 哈希 `A6879304…` 与源码构建一致——闪烁不是「没装上新代码」,是代码里还在。
|
|
20
|
+
- **测试**:全量 **2048 passed / 1 skipped**(200 文件);client typecheck 0、`eslint client/src/explorer/Scene3D.tsx` 0 error;`npm run build` 0。产物核对:`dist/client.js` 新哈希 `98DD1061…`,与装入 `desktop` profile 的副本逐字节一致(`v0.26.33-local.5`)。
|
|
21
|
+
|
|
3
22
|
## [0.26.31] — 2026-11-03
|
|
4
23
|
|
|
5
24
|
- **回填 0.26.30 的测试缺口 + 让 npm 产物与 main 对齐(无运行时行为变更)**
|
package/dist/client.js
CHANGED
|
@@ -42548,6 +42548,43 @@ var __create = Object.create;
|
|
|
42548
42548
|
return name;
|
|
42549
42549
|
}
|
|
42550
42550
|
var STAR_COUNT = 90;
|
|
42551
|
+
var EGO_SUBGRAPH_HOPS = 3;
|
|
42552
|
+
var EGO_SUBGRAPH_CAP = 400;
|
|
42553
|
+
function egoSubgraph2(full, rootId, hops = EGO_SUBGRAPH_HOPS, cap = EGO_SUBGRAPH_CAP) {
|
|
42554
|
+
const byId = new Map(full.nodes.map((n) => [n.id, n]));
|
|
42555
|
+
const root = byId.get(rootId);
|
|
42556
|
+
if (!root) return { nodes: [], edges: [], truncated: false };
|
|
42557
|
+
const adj = /* @__PURE__ */ new Map();
|
|
42558
|
+
for (const e of full.edges) {
|
|
42559
|
+
if (!adj.has(e.from)) adj.set(e.from, []);
|
|
42560
|
+
if (!adj.has(e.to)) adj.set(e.to, []);
|
|
42561
|
+
adj.get(e.from).push(e.to);
|
|
42562
|
+
adj.get(e.to).push(e.from);
|
|
42563
|
+
}
|
|
42564
|
+
const depth = /* @__PURE__ */ new Map([[rootId, 0]]);
|
|
42565
|
+
const order = [rootId];
|
|
42566
|
+
let cutByCap = false;
|
|
42567
|
+
for (let i = 0; i < order.length; i++) {
|
|
42568
|
+
const cur = order[i];
|
|
42569
|
+
const d = depth.get(cur);
|
|
42570
|
+
if (d >= hops) continue;
|
|
42571
|
+
for (const nb of adj.get(cur) ?? []) {
|
|
42572
|
+
if (depth.has(nb)) continue;
|
|
42573
|
+
if (order.length >= cap) {
|
|
42574
|
+
cutByCap = true;
|
|
42575
|
+
break;
|
|
42576
|
+
}
|
|
42577
|
+
depth.set(nb, d + 1);
|
|
42578
|
+
order.push(nb);
|
|
42579
|
+
}
|
|
42580
|
+
}
|
|
42581
|
+
const keep = new Set(order);
|
|
42582
|
+
return {
|
|
42583
|
+
nodes: order.map((id) => byId.get(id)),
|
|
42584
|
+
edges: full.edges.filter((e) => keep.has(e.from) && keep.has(e.to)),
|
|
42585
|
+
truncated: full.truncated || cutByCap
|
|
42586
|
+
};
|
|
42587
|
+
}
|
|
42551
42588
|
var STAR_R_MIN = 0.12;
|
|
42552
42589
|
var STAR_O_MIN = 0.12;
|
|
42553
42590
|
function radiusForDegree(deg) {
|
|
@@ -44827,6 +44864,7 @@ var __create = Object.create;
|
|
|
44827
44864
|
|
|
44828
44865
|
// client/src/explorer/projection.ts
|
|
44829
44866
|
var FOCAL = 1100;
|
|
44867
|
+
var LABEL_SKIP_DIST = 2600;
|
|
44830
44868
|
var CAM_DEFAULT = { yaw: 0.45, pitch: -0.3, dist: 1200, panX: 0, panY: 0 };
|
|
44831
44869
|
var DIST_MIN = 380;
|
|
44832
44870
|
var DIST_MAX = 3200;
|
|
@@ -44986,6 +45024,10 @@ var __create = Object.create;
|
|
|
44986
45024
|
}
|
|
44987
45025
|
return null;
|
|
44988
45026
|
}
|
|
45027
|
+
var BREATH_PERIOD_MS = 260;
|
|
45028
|
+
function phaseOf(id) {
|
|
45029
|
+
return hash322(id) % 6283 / 1e3;
|
|
45030
|
+
}
|
|
44989
45031
|
function easeInOut(p) {
|
|
44990
45032
|
const k = Math.min(1, Math.max(0, p));
|
|
44991
45033
|
return k < 0.5 ? 2 * k * k : 1 - Math.pow(-2 * k + 2, 2) / 2;
|
|
@@ -45037,6 +45079,7 @@ var __create = Object.create;
|
|
|
45037
45079
|
const [pointSize, setPointSize] = (0, import_react32.useState)(2.2);
|
|
45038
45080
|
const [driftOn, setDriftOn] = (0, import_react32.useState)(false);
|
|
45039
45081
|
const [starry, setStarry] = (0, import_react32.useState)(true);
|
|
45082
|
+
const [twinkleOn, setTwinkleOn] = (0, import_react32.useState)(true);
|
|
45040
45083
|
const camRef = (0, import_react32.useRef)({ ...CAM_DEFAULT });
|
|
45041
45084
|
const modeRef = (0, import_react32.useRef)("cluster");
|
|
45042
45085
|
const focusRef = (0, import_react32.useRef)(null);
|
|
@@ -45047,7 +45090,7 @@ var __create = Object.create;
|
|
|
45047
45090
|
const clickRef = (0, import_react32.useRef)(null);
|
|
45048
45091
|
const emphasisRef = (0, import_react32.useRef)(emphasisId);
|
|
45049
45092
|
emphasisRef.current = emphasisId;
|
|
45050
|
-
const
|
|
45093
|
+
const wakeRef = (0, import_react32.useRef)(null);
|
|
45051
45094
|
const degree = (0, import_react32.useMemo)(() => degreeMapOf(full), [full]);
|
|
45052
45095
|
const edgeList = (0, import_react32.useMemo)(
|
|
45053
45096
|
() => full.edges.map((e) => ({ from: e.from, to: e.to, relation: e.relation, causal: e.causal })),
|
|
@@ -45089,6 +45132,7 @@ var __create = Object.create;
|
|
|
45089
45132
|
camRef.current.dist = Math.min(3200, Math.max(380, EGO_HOPS_MAX2 * 300 * 1.55));
|
|
45090
45133
|
camRef.current.panX = 0;
|
|
45091
45134
|
camRef.current.panY = 0;
|
|
45135
|
+
wakeRef.current?.();
|
|
45092
45136
|
}, [emphasisId, adj, clusterLayout]);
|
|
45093
45137
|
const metasRef = (0, import_react32.useRef)(/* @__PURE__ */ new Map());
|
|
45094
45138
|
const spritesRef = (0, import_react32.useRef)(/* @__PURE__ */ new Map());
|
|
@@ -45131,14 +45175,33 @@ var __create = Object.create;
|
|
|
45131
45175
|
if (!host || !canvas) return;
|
|
45132
45176
|
const ctx = canvas.getContext("2d");
|
|
45133
45177
|
if (!ctx) return;
|
|
45134
|
-
dirtyRef.current = true;
|
|
45135
45178
|
const buffer = document.createElement("canvas");
|
|
45136
45179
|
const bctx = buffer.getContext("2d");
|
|
45180
|
+
const edgeCv = document.createElement("canvas");
|
|
45181
|
+
const ectx = edgeCv.getContext("2d");
|
|
45182
|
+
const nodeCv = document.createElement("canvas");
|
|
45183
|
+
const nctx = nodeCv.getContext("2d");
|
|
45184
|
+
let edgeDirty = true;
|
|
45185
|
+
let nodeDirty = true;
|
|
45186
|
+
let needsPresent = true;
|
|
45187
|
+
let lastActiveKey = "";
|
|
45137
45188
|
let raf = 0;
|
|
45189
|
+
let running = false;
|
|
45138
45190
|
let disposed = false;
|
|
45139
45191
|
let vw = 0;
|
|
45140
45192
|
let vh = 0;
|
|
45141
45193
|
const dprRef = { current: 1 };
|
|
45194
|
+
let colEdge = "";
|
|
45195
|
+
let colCausal = "";
|
|
45196
|
+
let colText = "";
|
|
45197
|
+
let colStale = true;
|
|
45198
|
+
const refreshColors = () => {
|
|
45199
|
+
if (!colStale) return;
|
|
45200
|
+
colEdge = resolveColor2(host, "--dsh-mem-border-strong");
|
|
45201
|
+
colCausal = resolveColor2(host, "--dsh-mem-accent");
|
|
45202
|
+
colText = resolveColor2(host, "--dsh-mem-text-2");
|
|
45203
|
+
colStale = false;
|
|
45204
|
+
};
|
|
45142
45205
|
const spriteFor = (color) => {
|
|
45143
45206
|
const hit = spritesRef.current.get(color);
|
|
45144
45207
|
if (hit) return hit;
|
|
@@ -45210,23 +45273,41 @@ var __create = Object.create;
|
|
|
45210
45273
|
}
|
|
45211
45274
|
return cv;
|
|
45212
45275
|
};
|
|
45276
|
+
let sized = false;
|
|
45213
45277
|
const resize = () => {
|
|
45214
45278
|
const dpr = Math.min(2, window.devicePixelRatio || 1);
|
|
45279
|
+
const nextW = host.clientWidth || 640;
|
|
45280
|
+
const nextH = host.clientHeight || host.offsetHeight || height;
|
|
45281
|
+
if (sized && nextW === vw && nextH === vh && dpr === dprRef.current) return false;
|
|
45282
|
+
sized = true;
|
|
45215
45283
|
dprRef.current = dpr;
|
|
45216
|
-
vw =
|
|
45217
|
-
vh =
|
|
45284
|
+
vw = nextW;
|
|
45285
|
+
vh = nextH;
|
|
45218
45286
|
canvas.width = Math.round(vw * dpr);
|
|
45219
45287
|
canvas.height = Math.round(vh * dpr);
|
|
45220
45288
|
canvas.style.width = `${vw}px`;
|
|
45221
45289
|
canvas.style.height = `${vh}px`;
|
|
45222
45290
|
buffer.width = canvas.width;
|
|
45223
45291
|
buffer.height = canvas.height;
|
|
45292
|
+
edgeCv.width = canvas.width;
|
|
45293
|
+
edgeCv.height = canvas.height;
|
|
45294
|
+
nodeCv.width = canvas.width;
|
|
45295
|
+
nodeCv.height = canvas.height;
|
|
45296
|
+
ectx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
45297
|
+
nctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
45224
45298
|
bctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
45225
45299
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
45226
45300
|
bgCache = null;
|
|
45301
|
+
edgeDirty = true;
|
|
45302
|
+
nodeDirty = true;
|
|
45303
|
+
colStale = true;
|
|
45304
|
+
needsPresent = true;
|
|
45305
|
+
return true;
|
|
45227
45306
|
};
|
|
45228
45307
|
resize();
|
|
45229
|
-
const ro = new ResizeObserver(
|
|
45308
|
+
const ro = new ResizeObserver(() => {
|
|
45309
|
+
if (resize()) presentNow();
|
|
45310
|
+
});
|
|
45230
45311
|
ro.observe(host);
|
|
45231
45312
|
const activeLayout = (now) => {
|
|
45232
45313
|
const tr = transRef.current;
|
|
@@ -45241,133 +45322,192 @@ var __create = Object.create;
|
|
|
45241
45322
|
};
|
|
45242
45323
|
let lastProjected = [];
|
|
45243
45324
|
const frame = (now) => {
|
|
45325
|
+
raf = 0;
|
|
45244
45326
|
if (disposed) return;
|
|
45327
|
+
refreshColors();
|
|
45245
45328
|
const cam = camRef.current;
|
|
45246
45329
|
const layout = activeLayout(now);
|
|
45247
45330
|
const ego = modeRef.current === "ego";
|
|
45248
45331
|
const metas = metasRef.current;
|
|
45249
45332
|
const hover = hoverRef.current;
|
|
45250
45333
|
const focus = focusRef.current;
|
|
45251
|
-
|
|
45252
|
-
if (
|
|
45253
|
-
|
|
45254
|
-
|
|
45334
|
+
const emph = emphasisRef.current;
|
|
45335
|
+
if (transRef.current !== null || driftOn && !ego) {
|
|
45336
|
+
edgeDirty = true;
|
|
45337
|
+
nodeDirty = true;
|
|
45255
45338
|
}
|
|
45256
45339
|
const projected = projectAll(layout, cam, { w: vw, h: vh }, void 0, now, driftOn && !ego);
|
|
45257
45340
|
lastProjected = projected;
|
|
45258
|
-
const
|
|
45259
|
-
|
|
45260
|
-
|
|
45261
|
-
|
|
45262
|
-
|
|
45263
|
-
|
|
45264
|
-
|
|
45265
|
-
|
|
45266
|
-
|
|
45341
|
+
const activeKey = `${hover ?? ""}|${focus ?? ""}|${emph ?? ""}`;
|
|
45342
|
+
if (activeKey !== lastActiveKey) {
|
|
45343
|
+
lastActiveKey = activeKey;
|
|
45344
|
+
nodeDirty = true;
|
|
45345
|
+
}
|
|
45346
|
+
const activeIds = /* @__PURE__ */ new Set();
|
|
45347
|
+
if (hover) activeIds.add(hover);
|
|
45348
|
+
if (focus) activeIds.add(focus);
|
|
45349
|
+
if (emph) activeIds.add(emph);
|
|
45267
45350
|
const pos = new Map(projected.map((p) => [p.id, p]));
|
|
45268
|
-
|
|
45269
|
-
|
|
45270
|
-
|
|
45271
|
-
c
|
|
45272
|
-
|
|
45273
|
-
|
|
45274
|
-
|
|
45275
|
-
|
|
45276
|
-
|
|
45277
|
-
|
|
45278
|
-
|
|
45279
|
-
|
|
45280
|
-
|
|
45281
|
-
c.
|
|
45282
|
-
|
|
45351
|
+
const edgeBase = colEdge;
|
|
45352
|
+
const causalC = colCausal;
|
|
45353
|
+
if (edgeDirty) {
|
|
45354
|
+
const c = ectx;
|
|
45355
|
+
c.clearRect(0, 0, vw, vh);
|
|
45356
|
+
if (!bgCache || bgCacheW !== vw || bgCacheH !== vh || bgCacheStarry !== starry) {
|
|
45357
|
+
bgCache = bakeBg(vw, vh, starry, nodeKeySeed);
|
|
45358
|
+
bgCacheW = vw;
|
|
45359
|
+
bgCacheH = vh;
|
|
45360
|
+
bgCacheStarry = starry;
|
|
45361
|
+
}
|
|
45362
|
+
c.drawImage(bgCache, 0, 0);
|
|
45363
|
+
if (edgesOn) {
|
|
45364
|
+
c.globalCompositeOperation = "source-over";
|
|
45365
|
+
for (const e of edgeList) {
|
|
45366
|
+
const a = pos.get(e.from);
|
|
45367
|
+
const b = pos.get(e.to);
|
|
45368
|
+
if (!a || !b) continue;
|
|
45369
|
+
const avgF = (a.f + b.f) / 2;
|
|
45370
|
+
c.strokeStyle = e.causal ? causalC : edgeBase;
|
|
45371
|
+
c.globalAlpha = Math.min(0.9, Math.max(0.05, avgF * 0.34));
|
|
45372
|
+
c.lineWidth = e.causal ? 1.2 : 0.7;
|
|
45373
|
+
c.beginPath();
|
|
45374
|
+
c.moveTo(a.x, a.y);
|
|
45375
|
+
c.lineTo(b.x, b.y);
|
|
45376
|
+
c.stroke();
|
|
45377
|
+
}
|
|
45378
|
+
c.globalAlpha = 1;
|
|
45379
|
+
}
|
|
45380
|
+
edgeDirty = false;
|
|
45381
|
+
needsPresent = true;
|
|
45382
|
+
}
|
|
45383
|
+
if (nodeDirty) {
|
|
45384
|
+
const c = nctx;
|
|
45385
|
+
c.clearRect(0, 0, vw, vh);
|
|
45386
|
+
for (const p of projected) {
|
|
45387
|
+
if (activeIds.has(p.id)) continue;
|
|
45388
|
+
const m = metas.get(p.id);
|
|
45389
|
+
if (!m) continue;
|
|
45390
|
+
const alpha = brightnessFor(p.f, agingOn ? m.aging : null, true);
|
|
45391
|
+
const r = Math.max(0.8, m.base * p.f * 1.75);
|
|
45392
|
+
if (glowOn) {
|
|
45393
|
+
c.globalCompositeOperation = "lighter";
|
|
45394
|
+
c.globalAlpha = alpha * 0.5;
|
|
45395
|
+
const sp = spriteFor(m.color);
|
|
45396
|
+
const gr = r * 3.4;
|
|
45397
|
+
c.drawImage(sp, p.x - gr, p.y - gr, gr * 2, gr * 2);
|
|
45398
|
+
}
|
|
45399
|
+
c.globalCompositeOperation = "source-over";
|
|
45400
|
+
c.globalAlpha = Math.min(1, alpha + 0.15);
|
|
45401
|
+
c.fillStyle = m.color;
|
|
45283
45402
|
c.beginPath();
|
|
45284
|
-
c.
|
|
45285
|
-
c.
|
|
45286
|
-
c.stroke();
|
|
45403
|
+
c.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
45404
|
+
c.fill();
|
|
45287
45405
|
}
|
|
45288
45406
|
c.globalAlpha = 1;
|
|
45289
|
-
|
|
45290
|
-
|
|
45291
|
-
|
|
45292
|
-
|
|
45293
|
-
|
|
45294
|
-
|
|
45295
|
-
|
|
45296
|
-
|
|
45297
|
-
|
|
45298
|
-
const r = Math.max(0.8, m.base * p.f * 1.75 * (isFocus || isHover ? 1.9 : 1));
|
|
45299
|
-
if (glowOn) {
|
|
45300
|
-
c.globalCompositeOperation = "lighter";
|
|
45301
|
-
c.globalAlpha = alpha * 0.5;
|
|
45302
|
-
const sp = spriteFor(m.color);
|
|
45303
|
-
const gr = r * 3.4;
|
|
45304
|
-
c.drawImage(sp, p.x - gr, p.y - gr, gr * 2, gr * 2);
|
|
45305
|
-
}
|
|
45306
|
-
c.globalCompositeOperation = "source-over";
|
|
45307
|
-
c.globalAlpha = Math.min(1, alpha + 0.15);
|
|
45308
|
-
c.fillStyle = m.color;
|
|
45309
|
-
c.beginPath();
|
|
45310
|
-
c.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
45311
|
-
c.fill();
|
|
45312
|
-
}
|
|
45313
|
-
c.globalAlpha = 1;
|
|
45314
|
-
if (labelsOn) {
|
|
45315
|
-
const textColor = resolveColor2(host, "--dsh-mem-text-2");
|
|
45316
|
-
const inSet = /* @__PURE__ */ new Set();
|
|
45317
|
-
const cand = projected.filter((p) => metas.has(p.id)).map((p) => {
|
|
45318
|
-
const m = metas.get(p.id);
|
|
45319
|
-
const hot = p.id === focus || p.id === hover || p.id === emphasisRef.current;
|
|
45320
|
-
if (hot) inSet.add(p.id);
|
|
45321
|
-
return {
|
|
45322
|
-
id: p.id,
|
|
45323
|
-
x: p.x,
|
|
45324
|
-
y: p.y,
|
|
45325
|
-
f: p.f,
|
|
45326
|
-
rank: (hot ? 1e6 : 0) + m.degree * 10 + (m.aging ?? 1) * 5 + hash322(p.id) % 7
|
|
45407
|
+
if (labelsOn && cam.dist <= LABEL_SKIP_DIST) {
|
|
45408
|
+
const textColor = colText;
|
|
45409
|
+
const cand = projected.filter((p) => metas.has(p.id) && !activeIds.has(p.id)).map((p) => {
|
|
45410
|
+
const m = metas.get(p.id);
|
|
45411
|
+
return { id: p.id, x: p.x, y: p.y, f: p.f, rank: m.degree * 10 + (m.aging ?? 1) * 5 + hash322(p.id) % 7 };
|
|
45412
|
+
});
|
|
45413
|
+
const widthOf = (id) => {
|
|
45414
|
+
const m = metas.get(id);
|
|
45415
|
+
return m ? clipByWidth(m.name).length * 6.4 : 0;
|
|
45327
45416
|
};
|
|
45328
|
-
|
|
45329
|
-
|
|
45417
|
+
const picked = new Set(pickLabels(cand, labelBudget(cam.dist), widthOf, vw));
|
|
45418
|
+
c.globalCompositeOperation = "source-over";
|
|
45419
|
+
c.fillStyle = textColor;
|
|
45420
|
+
c.font = '11px system-ui, "Microsoft YaHei", sans-serif';
|
|
45421
|
+
for (const p of projected) {
|
|
45422
|
+
if (!picked.has(p.id)) continue;
|
|
45423
|
+
c.globalAlpha = Math.min(1, Math.max(0.35, p.f));
|
|
45424
|
+
c.fillText(clipByWidth(metas.get(p.id).name), p.x + 7, p.y - 5);
|
|
45425
|
+
}
|
|
45426
|
+
c.globalAlpha = 1;
|
|
45427
|
+
}
|
|
45428
|
+
nodeDirty = false;
|
|
45429
|
+
needsPresent = true;
|
|
45430
|
+
}
|
|
45431
|
+
const breathing = twinkleOn && activeIds.size > 0;
|
|
45432
|
+
if (breathing) {
|
|
45433
|
+
const c = bctx;
|
|
45434
|
+
c.setTransform(1, 0, 0, 1, 0, 0);
|
|
45435
|
+
c.drawImage(edgeCv, 0, 0);
|
|
45436
|
+
c.drawImage(nodeCv, 0, 0);
|
|
45437
|
+
c.setTransform(dprRef.current, 0, 0, dprRef.current, 0, 0);
|
|
45438
|
+
for (const id of activeIds) {
|
|
45439
|
+
const p = pos.get(id);
|
|
45330
45440
|
const m = metas.get(id);
|
|
45331
|
-
|
|
45332
|
-
|
|
45333
|
-
|
|
45334
|
-
|
|
45335
|
-
|
|
45336
|
-
|
|
45337
|
-
|
|
45338
|
-
|
|
45339
|
-
|
|
45340
|
-
|
|
45341
|
-
|
|
45342
|
-
c.
|
|
45343
|
-
|
|
45344
|
-
|
|
45345
|
-
|
|
45346
|
-
|
|
45347
|
-
|
|
45348
|
-
|
|
45349
|
-
|
|
45350
|
-
|
|
45351
|
-
|
|
45352
|
-
|
|
45353
|
-
|
|
45354
|
-
|
|
45355
|
-
|
|
45356
|
-
|
|
45357
|
-
const
|
|
45358
|
-
|
|
45359
|
-
|
|
45360
|
-
|
|
45361
|
-
|
|
45362
|
-
|
|
45363
|
-
|
|
45364
|
-
|
|
45441
|
+
if (!p || !m) continue;
|
|
45442
|
+
const tw = 0.5 + 0.5 * Math.sin(now / BREATH_PERIOD_MS + phaseOf(id));
|
|
45443
|
+
const alpha = brightnessFor(p.f, agingOn ? m.aging : null, true);
|
|
45444
|
+
const r = Math.max(0.8, m.base * p.f * 1.75 * 1.9);
|
|
45445
|
+
if (glowOn) {
|
|
45446
|
+
c.globalCompositeOperation = "lighter";
|
|
45447
|
+
c.globalAlpha = Math.min(1, alpha * 0.5 + tw * 0.4);
|
|
45448
|
+
const sp = spriteFor(m.color);
|
|
45449
|
+
const gr = r * 3.4 * (1 + tw * 0.45);
|
|
45450
|
+
c.drawImage(sp, p.x - gr, p.y - gr, gr * 2, gr * 2);
|
|
45451
|
+
}
|
|
45452
|
+
c.globalCompositeOperation = "source-over";
|
|
45453
|
+
c.globalAlpha = Math.min(1, alpha + 0.15 + tw * 0.25);
|
|
45454
|
+
c.fillStyle = m.color;
|
|
45455
|
+
c.beginPath();
|
|
45456
|
+
c.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
45457
|
+
c.fill();
|
|
45458
|
+
if (labelsOn) {
|
|
45459
|
+
c.globalAlpha = 1;
|
|
45460
|
+
c.fillStyle = colText;
|
|
45461
|
+
c.font = '11px system-ui, "Microsoft YaHei", sans-serif';
|
|
45462
|
+
c.fillText(clipByWidth(m.name), p.x + 7, p.y - 5);
|
|
45463
|
+
}
|
|
45464
|
+
}
|
|
45465
|
+
if (hover && edgesOn) {
|
|
45466
|
+
c.font = '10px system-ui, "Microsoft YaHei", sans-serif';
|
|
45467
|
+
const used = /* @__PURE__ */ new Set();
|
|
45468
|
+
const related = edgeList.filter((e) => (e.from === hover || e.to === hover) && pos.has(e.from) && pos.has(e.to)).sort((a, b) => Number(b.causal) - Number(a.causal));
|
|
45469
|
+
for (const e of related.slice(0, 12)) {
|
|
45470
|
+
const a = pos.get(e.from);
|
|
45471
|
+
const b = pos.get(e.to);
|
|
45472
|
+
const mx = (a.x + b.x) / 2;
|
|
45473
|
+
const my = (a.y + b.y) / 2;
|
|
45474
|
+
const cell = Math.floor(mx / 72) * 1e3 + Math.floor(my / 13);
|
|
45475
|
+
if (used.has(cell)) continue;
|
|
45476
|
+
used.add(cell);
|
|
45477
|
+
c.fillStyle = e.causal ? causalC : edgeBase;
|
|
45478
|
+
c.globalAlpha = 0.92;
|
|
45479
|
+
c.fillText(e.relation, mx, my);
|
|
45480
|
+
}
|
|
45481
|
+
c.globalAlpha = 1;
|
|
45482
|
+
}
|
|
45483
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
45484
|
+
ctx.drawImage(buffer, 0, 0);
|
|
45485
|
+
ctx.setTransform(dprRef.current, 0, 0, dprRef.current, 0, 0);
|
|
45486
|
+
needsPresent = false;
|
|
45487
|
+
} else if (needsPresent) {
|
|
45488
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
45489
|
+
ctx.drawImage(edgeCv, 0, 0);
|
|
45490
|
+
ctx.drawImage(nodeCv, 0, 0);
|
|
45491
|
+
ctx.setTransform(dprRef.current, 0, 0, dprRef.current, 0, 0);
|
|
45492
|
+
needsPresent = false;
|
|
45493
|
+
}
|
|
45494
|
+
running = edgeDirty || nodeDirty || needsPresent || breathing || transRef.current !== null || driftOn && !ego;
|
|
45495
|
+
if (running) raf = requestAnimationFrame(frame);
|
|
45496
|
+
};
|
|
45497
|
+
const presentNow = () => {
|
|
45498
|
+
if (disposed) return;
|
|
45499
|
+
if (raf) cancelAnimationFrame(raf);
|
|
45500
|
+
raf = 0;
|
|
45501
|
+
running = false;
|
|
45502
|
+
frame(performance.now());
|
|
45503
|
+
};
|
|
45504
|
+
const wake = () => {
|
|
45505
|
+
if (!running && !disposed) {
|
|
45506
|
+
running = true;
|
|
45507
|
+
raf = requestAnimationFrame(frame);
|
|
45365
45508
|
}
|
|
45366
|
-
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
45367
|
-
ctx.drawImage(buffer, 0, 0);
|
|
45368
|
-
ctx.setTransform(dprRef.current, 0, 0, dprRef.current, 0, 0);
|
|
45369
|
-
raf = requestAnimationFrame(frame);
|
|
45370
45509
|
};
|
|
45510
|
+
wakeRef.current = wake;
|
|
45371
45511
|
frame(performance.now());
|
|
45372
45512
|
const toLocal = (ev) => {
|
|
45373
45513
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -45391,13 +45531,15 @@ var __create = Object.create;
|
|
|
45391
45531
|
}
|
|
45392
45532
|
drag.x = pt.x;
|
|
45393
45533
|
drag.y = pt.y;
|
|
45394
|
-
|
|
45534
|
+
edgeDirty = true;
|
|
45535
|
+
nodeDirty = true;
|
|
45536
|
+
wake();
|
|
45395
45537
|
return;
|
|
45396
45538
|
}
|
|
45397
45539
|
const hit = pickNearest(lastProjected, pt.x, pt.y, PICK_PX);
|
|
45398
45540
|
if (hit !== hoverRef.current) {
|
|
45399
45541
|
hoverRef.current = hit;
|
|
45400
|
-
|
|
45542
|
+
wake();
|
|
45401
45543
|
setHoverInfo(hit ? { id: hit, x: pt.x, y: pt.y } : null);
|
|
45402
45544
|
} else if (hit) {
|
|
45403
45545
|
setHoverInfo({ id: hit, x: pt.x, y: pt.y });
|
|
@@ -45450,7 +45592,9 @@ var __create = Object.create;
|
|
|
45450
45592
|
const onWheel = (ev) => {
|
|
45451
45593
|
ev.preventDefault();
|
|
45452
45594
|
camRef.current.dist = zoomBy(camRef.current.dist, ev.deltaY);
|
|
45453
|
-
|
|
45595
|
+
edgeDirty = true;
|
|
45596
|
+
nodeDirty = true;
|
|
45597
|
+
wake();
|
|
45454
45598
|
};
|
|
45455
45599
|
const onKey = (ev) => {
|
|
45456
45600
|
if (ev.key === "Enter" && hoverRef.current) onPick(hoverRef.current);
|
|
@@ -45476,6 +45620,7 @@ var __create = Object.create;
|
|
|
45476
45620
|
return () => {
|
|
45477
45621
|
disposed = true;
|
|
45478
45622
|
cancelAnimationFrame(raf);
|
|
45623
|
+
wakeRef.current = null;
|
|
45479
45624
|
ro.disconnect();
|
|
45480
45625
|
canvas.removeEventListener("mousedown", onDown);
|
|
45481
45626
|
window.removeEventListener("mousemove", onMove);
|
|
@@ -45484,7 +45629,7 @@ var __create = Object.create;
|
|
|
45484
45629
|
canvas.removeEventListener("mouseleave", onLeave);
|
|
45485
45630
|
window.removeEventListener("keydown", onKey);
|
|
45486
45631
|
};
|
|
45487
|
-
}, [full, edgeList, adj, clusterLayout, glowOn, edgesOn, labelsOn, agingOn, driftOn, starry, nodeKeySeed, height, onPick]);
|
|
45632
|
+
}, [full, edgeList, adj, clusterLayout, glowOn, edgesOn, labelsOn, agingOn, driftOn, twinkleOn, starry, nodeKeySeed, pointSize, height, onPick]);
|
|
45488
45633
|
const hoverMeta = hoverInfo ? metasRef.current.get(hoverInfo.id) : null;
|
|
45489
45634
|
const ui = starry ? {
|
|
45490
45635
|
barBg: "rgba(10, 13, 22, 0.72)",
|
|
@@ -45565,6 +45710,7 @@ var __create = Object.create;
|
|
|
45565
45710
|
toggle("边", edgesOn, setEdgesOn),
|
|
45566
45711
|
toggle("标签", labelsOn, setLabelsOn),
|
|
45567
45712
|
toggle("老化亮度", agingOn, setAgingOn),
|
|
45713
|
+
toggle("呼吸", twinkleOn, setTwinkleOn),
|
|
45568
45714
|
toggle("漂移", driftOn, setDriftOn),
|
|
45569
45715
|
toggle(starry ? "星空底" : "白底", starry, setStarry),
|
|
45570
45716
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { style: { fontSize: 11, color: ui.dim, display: "inline-flex", alignItems: "center", gap: 4 }, children: [
|
|
@@ -45602,12 +45748,19 @@ var __create = Object.create;
|
|
|
45602
45748
|
const [linksError, setLinksError] = (0, import_react33.useState)(null);
|
|
45603
45749
|
const [emphasisId, setEmphasisId] = (0, import_react33.useState)(void 0);
|
|
45604
45750
|
const [view, setView] = (0, import_react33.useState)("3d");
|
|
45751
|
+
const [egoRoot, setEgoRoot] = (0, import_react33.useState)(null);
|
|
45752
|
+
const [searchText, setSearchText] = (0, import_react33.useState)("");
|
|
45753
|
+
const [searchMiss, setSearchMiss] = (0, import_react33.useState)(false);
|
|
45754
|
+
const [searchOpen, setSearchOpen] = (0, import_react33.useState)(false);
|
|
45605
45755
|
const linksSeq = (0, import_react33.useRef)(0);
|
|
45606
45756
|
const [mode, setMode] = (0, import_react33.useState)("query");
|
|
45757
|
+
const [entryPane, setEntryPane] = (0, import_react33.useState)(false);
|
|
45607
45758
|
const [detailOpen, setDetailOpen] = (0, import_react33.useState)(false);
|
|
45608
45759
|
const [detailW, setDetailW] = (0, import_react33.useState)(DETAIL_DEFAULT);
|
|
45609
45760
|
const containerRef = (0, import_react33.useRef)(null);
|
|
45610
45761
|
const dragWRef = (0, import_react33.useRef)(null);
|
|
45762
|
+
const modeRef = (0, import_react33.useRef)(mode);
|
|
45763
|
+
modeRef.current = mode;
|
|
45611
45764
|
const startDrag = (0, import_react33.useCallback)((ev) => {
|
|
45612
45765
|
ev.preventDefault();
|
|
45613
45766
|
dragWRef.current = { startX: ev.clientX, startW: detailW };
|
|
@@ -45630,6 +45783,7 @@ var __create = Object.create;
|
|
|
45630
45783
|
const loadGraph = (0, import_react33.useCallback)(() => {
|
|
45631
45784
|
setGraphLoading(true);
|
|
45632
45785
|
setGraphError(null);
|
|
45786
|
+
setEgoRoot(null);
|
|
45633
45787
|
rpc("dsh-memory/graph-preview", { mode: "full" }).then((r) => {
|
|
45634
45788
|
if (!r.ok) throw new Error(r.error.message);
|
|
45635
45789
|
setStats(r.value.stats);
|
|
@@ -45643,13 +45797,13 @@ var __create = Object.create;
|
|
|
45643
45797
|
for (const it of items) if (it.content) labels.current.set(it.id, it.content);
|
|
45644
45798
|
}, []);
|
|
45645
45799
|
const openRecord = (0, import_react33.useCallback)(
|
|
45646
|
-
(id) => {
|
|
45800
|
+
(id, opts) => {
|
|
45647
45801
|
const token = ++linksSeq.current;
|
|
45648
45802
|
setRecordId(id);
|
|
45649
45803
|
setLinksLoading(true);
|
|
45650
45804
|
setLinksError(null);
|
|
45651
|
-
|
|
45652
|
-
setDetailOpen(true);
|
|
45805
|
+
setEntryPane(true);
|
|
45806
|
+
if (modeRef.current === "graph") setDetailOpen(true);
|
|
45653
45807
|
rpc("dsh-memory/record-links", { recordId: id }).then((r) => {
|
|
45654
45808
|
if (token !== linksSeq.current) return;
|
|
45655
45809
|
if (!r.ok) throw new Error(r.error.message);
|
|
@@ -45658,6 +45812,7 @@ var __create = Object.create;
|
|
|
45658
45812
|
remember(v.records);
|
|
45659
45813
|
setLinks(v);
|
|
45660
45814
|
setEmphasisId(v.entities[0]?.id);
|
|
45815
|
+
if (!opts?.keepEgo && v.entities[0]) setEgoRoot(v.entities[0].id);
|
|
45661
45816
|
}).catch((e) => {
|
|
45662
45817
|
if (token !== linksSeq.current) return;
|
|
45663
45818
|
setLinksError(String(e?.message ?? e));
|
|
@@ -45675,26 +45830,81 @@ var __create = Object.create;
|
|
|
45675
45830
|
const known = links?.entities.find((e) => e.id === nodeId) ?? links?.neighbors.find((n) => n.id === nodeId);
|
|
45676
45831
|
const first = known?.recordIds[0];
|
|
45677
45832
|
if (first) {
|
|
45678
|
-
openRecord(first);
|
|
45833
|
+
openRecord(first, { keepEgo: true });
|
|
45679
45834
|
return;
|
|
45680
45835
|
}
|
|
45681
45836
|
rpc("dsh-memory/graph-node-get", { id: nodeId }).then((r) => {
|
|
45682
45837
|
const node = r.ok ? r.value.node : null;
|
|
45683
45838
|
const id = node?.sourceRecordIds?.[0];
|
|
45684
|
-
if (id) openRecord(id);
|
|
45839
|
+
if (id) openRecord(id, { keepEgo: true });
|
|
45685
45840
|
}).catch(() => {
|
|
45686
45841
|
});
|
|
45687
45842
|
},
|
|
45688
45843
|
[links, openRecord, rpc]
|
|
45689
45844
|
);
|
|
45845
|
+
const onPickNode2d = (0, import_react33.useCallback)(
|
|
45846
|
+
(nodeId) => {
|
|
45847
|
+
setEgoRoot(nodeId);
|
|
45848
|
+
onPickNode(nodeId);
|
|
45849
|
+
},
|
|
45850
|
+
[onPickNode]
|
|
45851
|
+
);
|
|
45852
|
+
const graphData = (0, import_react33.useMemo)(
|
|
45853
|
+
() => full && egoRoot ? egoSubgraph2(full, egoRoot) : full,
|
|
45854
|
+
[full, egoRoot]
|
|
45855
|
+
);
|
|
45856
|
+
const locateNode = (0, import_react33.useCallback)(
|
|
45857
|
+
(nodeId) => {
|
|
45858
|
+
setEmphasisId(nodeId);
|
|
45859
|
+
if (view === "2d") setEgoRoot(nodeId);
|
|
45860
|
+
setSearchOpen(false);
|
|
45861
|
+
},
|
|
45862
|
+
[view]
|
|
45863
|
+
);
|
|
45864
|
+
const searchHits = (0, import_react33.useMemo)(() => {
|
|
45865
|
+
const q = searchText.trim().toLowerCase();
|
|
45866
|
+
if (!q || !full) return [];
|
|
45867
|
+
const hits = full.nodes.filter((n) => n.name.toLowerCase().includes(q));
|
|
45868
|
+
return hits.slice(0, 200);
|
|
45869
|
+
}, [searchText, full]);
|
|
45870
|
+
const locateFirstHit = (0, import_react33.useCallback)(() => {
|
|
45871
|
+
const q = searchText.trim().toLowerCase();
|
|
45872
|
+
if (!q || !full) return;
|
|
45873
|
+
const hit = full.nodes.find((n) => n.name.toLowerCase().includes(q));
|
|
45874
|
+
if (!hit) {
|
|
45875
|
+
setSearchMiss(true);
|
|
45876
|
+
setTimeout(() => setSearchMiss(false), 1500);
|
|
45877
|
+
return;
|
|
45878
|
+
}
|
|
45879
|
+
setSearchMiss(false);
|
|
45880
|
+
locateNode(hit.id);
|
|
45881
|
+
}, [searchText, full, locateNode]);
|
|
45882
|
+
const jumpToGraph = (0, import_react33.useCallback)(
|
|
45883
|
+
(target) => {
|
|
45884
|
+
const root = links?.entities[0]?.id;
|
|
45885
|
+
setView(target);
|
|
45886
|
+
setMode("graph");
|
|
45887
|
+
setDetailOpen(true);
|
|
45888
|
+
if (root) {
|
|
45889
|
+
setEmphasisId(root);
|
|
45890
|
+
if (target === "2d") setEgoRoot(root);
|
|
45891
|
+
}
|
|
45892
|
+
},
|
|
45893
|
+
[links]
|
|
45894
|
+
);
|
|
45895
|
+
const backToEntry = (0, import_react33.useCallback)(() => {
|
|
45896
|
+
setMode("query");
|
|
45897
|
+
setEntryPane(true);
|
|
45898
|
+
}, []);
|
|
45690
45899
|
const labelOfRecord = (0, import_react33.useCallback)((id) => labels.current.get(id) ?? id.slice(0, 10), []);
|
|
45691
45900
|
const building = stats && stats.jobsTotal > 0 && stats.pendingJobs > 0;
|
|
45692
45901
|
const summary = (0, import_react33.useMemo)(() => {
|
|
45693
45902
|
if (!stats) return "统计未到";
|
|
45694
|
-
const drawn =
|
|
45695
|
-
const
|
|
45696
|
-
|
|
45697
|
-
|
|
45903
|
+
const drawn = graphData ? graphData.nodes.length : 0;
|
|
45904
|
+
const egoNote = egoRoot ? ` · ego ≤3 跳(${drawn} 节点)` : "";
|
|
45905
|
+
const cut = graphData?.truncated ? "(已截断,仅画部分)" : "";
|
|
45906
|
+
return `节点 ${drawn}/${stats.nodes} · 边 ${graphData ? graphData.edges.length : 0}/${stats.edges} · 因果边 ${stats.causalEdges}${cut}${egoNote}`;
|
|
45907
|
+
}, [stats, graphData, egoRoot]);
|
|
45698
45908
|
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { ref: containerRef, style: { display: "flex", flexDirection: "column", gap: 8, padding: 12, height: "calc(100vh - 48px)", minHeight: 0 }, children: [
|
|
45699
45909
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
|
|
45700
45910
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h2", { style: { margin: 0, fontSize: 16 }, children: "记忆库" }),
|
|
@@ -45737,6 +45947,86 @@ var __create = Object.create;
|
|
|
45737
45947
|
)
|
|
45738
45948
|
] }),
|
|
45739
45949
|
mode === "graph" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
45950
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
45951
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
45952
|
+
NInput,
|
|
45953
|
+
{
|
|
45954
|
+
value: searchText,
|
|
45955
|
+
onChange: (e) => {
|
|
45956
|
+
setSearchText(e.target.value);
|
|
45957
|
+
setSearchOpen(true);
|
|
45958
|
+
},
|
|
45959
|
+
onFocus: () => setSearchOpen(true),
|
|
45960
|
+
onBlur: () => setTimeout(() => setSearchOpen(false), 180),
|
|
45961
|
+
onKeyDown: (e) => {
|
|
45962
|
+
if (e.key === "Enter") locateFirstHit();
|
|
45963
|
+
if (e.key === "Escape") setSearchOpen(false);
|
|
45964
|
+
},
|
|
45965
|
+
placeholder: searchMiss ? "未命中该实体" : "搜实体名…",
|
|
45966
|
+
style: { width: 160, fontSize: 12 }
|
|
45967
|
+
}
|
|
45968
|
+
),
|
|
45969
|
+
searchOpen && searchText.trim() && searchHits.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
45970
|
+
"div",
|
|
45971
|
+
{
|
|
45972
|
+
style: {
|
|
45973
|
+
position: "absolute",
|
|
45974
|
+
top: "calc(100% + 4px)",
|
|
45975
|
+
left: 0,
|
|
45976
|
+
zIndex: 20,
|
|
45977
|
+
width: 240,
|
|
45978
|
+
maxHeight: 260,
|
|
45979
|
+
overflow: "auto",
|
|
45980
|
+
background: "var(--dsh-mem-bg-card)",
|
|
45981
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
45982
|
+
borderRadius: 8,
|
|
45983
|
+
boxShadow: "0 6px 18px rgba(0,0,0,.25)"
|
|
45984
|
+
},
|
|
45985
|
+
children: searchHits.map((n) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
45986
|
+
"div",
|
|
45987
|
+
{
|
|
45988
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
45989
|
+
onClick: () => locateNode(n.id),
|
|
45990
|
+
style: { padding: "5px 10px", fontSize: 12, cursor: "pointer", display: "flex", gap: 6, alignItems: "center" },
|
|
45991
|
+
title: `${n.type}${n.aging != null ? ` · 老化 ${n.aging.toFixed(2)}` : ""}`,
|
|
45992
|
+
children: [
|
|
45993
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: n.name }),
|
|
45994
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: { fontSize: 10, color: "var(--dsh-mem-text-3)" }, children: n.type })
|
|
45995
|
+
]
|
|
45996
|
+
},
|
|
45997
|
+
n.id
|
|
45998
|
+
))
|
|
45999
|
+
}
|
|
46000
|
+
) : null,
|
|
46001
|
+
searchOpen && searchText.trim() && searchHits.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
46002
|
+
"div",
|
|
46003
|
+
{
|
|
46004
|
+
style: {
|
|
46005
|
+
position: "absolute",
|
|
46006
|
+
top: "calc(100% + 4px)",
|
|
46007
|
+
left: 0,
|
|
46008
|
+
zIndex: 20,
|
|
46009
|
+
width: 240,
|
|
46010
|
+
padding: "6px 10px",
|
|
46011
|
+
fontSize: 12,
|
|
46012
|
+
background: "var(--dsh-mem-bg-card)",
|
|
46013
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
46014
|
+
borderRadius: 8,
|
|
46015
|
+
color: "var(--dsh-mem-text-3)"
|
|
46016
|
+
},
|
|
46017
|
+
children: "图里没有匹配的实体"
|
|
46018
|
+
}
|
|
46019
|
+
) : null
|
|
46020
|
+
] }),
|
|
46021
|
+
view === "2d" && egoRoot ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
46022
|
+
NButton,
|
|
46023
|
+
{
|
|
46024
|
+
onClick: () => setEgoRoot(null),
|
|
46025
|
+
title: "退出 ego 聚焦,重新渲染全图",
|
|
46026
|
+
style: { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" },
|
|
46027
|
+
children: "回全图"
|
|
46028
|
+
}
|
|
46029
|
+
) : null,
|
|
45740
46030
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
45741
46031
|
NButton,
|
|
45742
46032
|
{
|
|
@@ -45751,26 +46041,77 @@ var __create = Object.create;
|
|
|
45751
46041
|
children: "详情"
|
|
45752
46042
|
}
|
|
45753
46043
|
),
|
|
45754
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.
|
|
45755
|
-
|
|
45756
|
-
|
|
45757
|
-
|
|
45758
|
-
|
|
45759
|
-
|
|
45760
|
-
|
|
45761
|
-
|
|
45762
|
-
|
|
46044
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { style: { display: "inline-flex", border: "1px solid var(--dsh-mem-border)", borderRadius: 6, overflow: "hidden", fontSize: 12 }, children: [
|
|
46045
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
46046
|
+
"span",
|
|
46047
|
+
{
|
|
46048
|
+
onClick: () => setView("2d"),
|
|
46049
|
+
title: "G6 力导星图",
|
|
46050
|
+
style: { padding: "3px 10px", cursor: "pointer", background: view === "2d" ? "var(--dsh-mem-accent)" : "transparent", color: view === "2d" ? "#fff" : "var(--dsh-mem-text-2)" },
|
|
46051
|
+
children: "2D"
|
|
46052
|
+
}
|
|
46053
|
+
),
|
|
46054
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
46055
|
+
"span",
|
|
46056
|
+
{
|
|
46057
|
+
onClick: () => setView("3d"),
|
|
46058
|
+
title: "Canvas 三维投影记忆海",
|
|
46059
|
+
style: { padding: "3px 10px", cursor: "pointer", background: view === "3d" ? "var(--dsh-mem-accent)" : "transparent", color: view === "3d" ? "#fff" : "var(--dsh-mem-text-2)" },
|
|
46060
|
+
children: "3D"
|
|
46061
|
+
}
|
|
46062
|
+
)
|
|
46063
|
+
] })
|
|
45763
46064
|
] }) : null,
|
|
45764
46065
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: loadGraph, title: "重新拉取图与统计", children: "刷新图" }),
|
|
45765
46066
|
onClose ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: onClose, title: "关闭", children: "关闭" }) : null
|
|
45766
46067
|
] }),
|
|
45767
|
-
mode === "query" ? /* @__PURE__ */ (0, import_jsx_runtime33.
|
|
46068
|
+
mode === "query" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { display: "flex", flex: 1, minHeight: 0, gap: 0 }, children: [
|
|
46069
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { flex: 1, minWidth: 0, minHeight: 0, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(RecordPane, { rpc, activeId: recordId, onPick: openRecord }) }),
|
|
46070
|
+
entryPane && recordId ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
46071
|
+
"div",
|
|
46072
|
+
{
|
|
46073
|
+
style: {
|
|
46074
|
+
width: 420,
|
|
46075
|
+
minWidth: DETAIL_MIN,
|
|
46076
|
+
flexShrink: 0,
|
|
46077
|
+
minHeight: 0,
|
|
46078
|
+
overflow: "auto",
|
|
46079
|
+
borderLeft: "1px solid var(--dsh-mem-border)",
|
|
46080
|
+
paddingLeft: 10,
|
|
46081
|
+
display: "flex",
|
|
46082
|
+
flexDirection: "column",
|
|
46083
|
+
gap: 8
|
|
46084
|
+
},
|
|
46085
|
+
children: [
|
|
46086
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center", flexWrap: "wrap" }, children: [
|
|
46087
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: "在图中定位本条目的实体:" }),
|
|
46088
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: () => jumpToGraph("2d"), disabled: !links || links.entities.length === 0, title: "切到 2D 力导星图并 ego 聚焦首实体", children: "2D 图" }),
|
|
46089
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: () => jumpToGraph("3d"), disabled: !links || links.entities.length === 0, title: "切到 3D 记忆海并 ego 聚焦首实体", children: "3D 图" }),
|
|
46090
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: { flex: 1 } }),
|
|
46091
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: () => setEntryPane(false), title: "收起条目面板", children: "收起" })
|
|
46092
|
+
] }),
|
|
46093
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
46094
|
+
LinkPane,
|
|
46095
|
+
{
|
|
46096
|
+
recordId,
|
|
46097
|
+
links,
|
|
46098
|
+
loading: linksLoading,
|
|
46099
|
+
error: linksError,
|
|
46100
|
+
onPickRecord: openRecord,
|
|
46101
|
+
onEmphasisNode: setEmphasisId,
|
|
46102
|
+
labelOfRecord
|
|
46103
|
+
}
|
|
46104
|
+
)
|
|
46105
|
+
]
|
|
46106
|
+
}
|
|
46107
|
+
) : null
|
|
46108
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { display: "flex", flex: 1, minHeight: 0, gap: 0 }, children: [
|
|
45768
46109
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { flex: 1, minWidth: 0, minHeight: 0, display: "flex", flexDirection: "column" }, children: graphError ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { fontSize: 12, color: "var(--dsh-mem-accent)", padding: 8 }, children: [
|
|
45769
46110
|
"图数据读取失败:",
|
|
45770
46111
|
graphError,
|
|
45771
46112
|
" ",
|
|
45772
46113
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: loadGraph, children: "重试" })
|
|
45773
|
-
] }) : graphLoading ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)", padding: 8 }, children: "图加载中…" }) : full && full.nodes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)", padding: 8 }, children: "图里还没有节点。图谱投影默认关闭——在设置页「概览 → 知识图谱」打开开关后,存量记忆会逐批投影成实体与关系。" }) : full ? view === "3d" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Scene3D, { full, onPick: onPickNode, emphasisId, height: canvasHeight }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GraphCanvas, { full, onPick:
|
|
46114
|
+
] }) : graphLoading ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)", padding: 8 }, children: "图加载中…" }) : full && full.nodes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)", padding: 8 }, children: "图里还没有节点。图谱投影默认关闭——在设置页「概览 → 知识图谱」打开开关后,存量记忆会逐批投影成实体与关系。" }) : full ? view === "3d" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Scene3D, { full, onPick: onPickNode, emphasisId, height: canvasHeight }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GraphCanvas, { full: graphData ?? full, onPick: onPickNode2d, emphasisId, fit: "view", height: "fill" }) : null }),
|
|
45774
46115
|
detailOpen ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
45775
46116
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
45776
46117
|
"div",
|
|
@@ -45780,18 +46121,24 @@ var __create = Object.create;
|
|
|
45780
46121
|
title: "拖动调整详情宽度"
|
|
45781
46122
|
}
|
|
45782
46123
|
),
|
|
45783
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.
|
|
45784
|
-
|
|
45785
|
-
|
|
45786
|
-
recordId,
|
|
45787
|
-
|
|
45788
|
-
|
|
45789
|
-
|
|
45790
|
-
|
|
45791
|
-
|
|
45792
|
-
|
|
45793
|
-
|
|
45794
|
-
|
|
46124
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { width: detailW, minWidth: DETAIL_MIN, flexShrink: 0, overflow: "auto", minHeight: 0, borderLeft: "1px solid var(--dsh-mem-border)", paddingLeft: 10, display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
46125
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center", flexWrap: "wrap" }, children: [
|
|
46126
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: "本条目:" }),
|
|
46127
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NButton, { onClick: backToEntry, disabled: !recordId, title: "切回查询视图并展开本条目(保留当前选点)", children: "按条目展示" })
|
|
46128
|
+
] }),
|
|
46129
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
46130
|
+
LinkPane,
|
|
46131
|
+
{
|
|
46132
|
+
recordId,
|
|
46133
|
+
links,
|
|
46134
|
+
loading: linksLoading,
|
|
46135
|
+
error: linksError,
|
|
46136
|
+
onPickRecord: openRecord,
|
|
46137
|
+
onEmphasisNode: setEmphasisId,
|
|
46138
|
+
labelOfRecord
|
|
46139
|
+
}
|
|
46140
|
+
)
|
|
46141
|
+
] })
|
|
45795
46142
|
] }) : null
|
|
45796
46143
|
] })
|
|
45797
46144
|
] });
|
package/dsh.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-prime-memory",
|
|
3
3
|
"name": "dsh-prime-memory",
|
|
4
|
-
"version": "0.26.
|
|
4
|
+
"version": "0.26.34",
|
|
5
5
|
"description": {
|
|
6
6
|
"en": "Layered distilled memory plugin (L0 capture → L1 atomic memory → L2 scenes → L3 persona) with automatic recall injection before each model step.",
|
|
7
7
|
"zh": "L0~L3 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
|