pi-okf-memory 0.1.0
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/LICENSE +21 -0
- package/README.en.md +204 -0
- package/README.md +265 -0
- package/cordis.patch.yml +6 -0
- package/docs/graph-demo.png +0 -0
- package/lib/capture.js +53 -0
- package/lib/client.js +417 -0
- package/lib/client.js.map +1 -0
- package/lib/concept.js +264 -0
- package/lib/dedupe.js +149 -0
- package/lib/graph.js +95 -0
- package/lib/index.js +381 -0
- package/lib/learning.js +185 -0
- package/lib/memory.js +135 -0
- package/lib/recall.js +47 -0
- package/lib/store.js +217 -0
- package/package.json +89 -0
- package/src/client/index.tsx +232 -0
- package/src/pi/graph-html.ts +300 -0
- package/src/pi/index.ts +383 -0
- package/src/server/capture.ts +52 -0
- package/src/server/concept.ts +302 -0
- package/src/server/dedupe.ts +164 -0
- package/src/server/dsh-tools.d.ts +11 -0
- package/src/server/graph.ts +135 -0
- package/src/server/index.ts +376 -0
- package/src/server/learning.ts +219 -0
- package/src/server/memory.ts +157 -0
- package/src/server/recall.ts +58 -0
- package/src/server/store.ts +255 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "pi-okf-memory",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
16
|
+
key = keys[i];
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
+
get: ((k) => from[k]).bind(null, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
//#endregion
|
|
29
|
+
let react = require("react");
|
|
30
|
+
react = __toESM(react, 1);
|
|
31
|
+
//#region src/client/index.tsx
|
|
32
|
+
/**
|
|
33
|
+
* okf-memory client(浏览器半,dsh 专用) — 记忆图谱会话标签页。
|
|
34
|
+
*
|
|
35
|
+
* 契约:
|
|
36
|
+
* - package.json 声明 dsh.client { platform:'web', inject:[client服务] }
|
|
37
|
+
* - ctx.slots.inject("conversation.view", ...) 注册对话视图标签
|
|
38
|
+
* - fetch 后端 /okf-graph → 力导向渲染
|
|
39
|
+
* - 交互:滚轮缩放 / 拖拽平移 / 拖节点 / 悬停显示详情 / 搜索命中+神经传导
|
|
40
|
+
*
|
|
41
|
+
* 只保留「记忆图谱」一个视图。
|
|
42
|
+
*/
|
|
43
|
+
const inject = ["slots", "locale"];
|
|
44
|
+
const NS = "okf-memory";
|
|
45
|
+
const TYPE_COLORS = {
|
|
46
|
+
Fact: "#2f7bff",
|
|
47
|
+
Preference: "#ffd400",
|
|
48
|
+
Decision: "#ff2d55",
|
|
49
|
+
Method: "#00e66e",
|
|
50
|
+
Insight: "#c04dff",
|
|
51
|
+
Idea: "#ff7a00",
|
|
52
|
+
Lesson: "#c8d1dd",
|
|
53
|
+
TechChoice: "#00e5ff",
|
|
54
|
+
Other: "#9aa7b8"
|
|
55
|
+
};
|
|
56
|
+
function MemoryGraphView() {
|
|
57
|
+
const [graph, setGraph] = react.default.useState(null);
|
|
58
|
+
const [error, setError] = react.default.useState(null);
|
|
59
|
+
const [query, setQuery] = react.default.useState("");
|
|
60
|
+
const canvasRef = react.default.useRef(null);
|
|
61
|
+
const layoutRef = react.default.useRef([]);
|
|
62
|
+
const viewRef = react.default.useRef({
|
|
63
|
+
zoom: 1,
|
|
64
|
+
panX: 0,
|
|
65
|
+
panY: 0
|
|
66
|
+
});
|
|
67
|
+
const dragRef = react.default.useRef({
|
|
68
|
+
id: null,
|
|
69
|
+
offX: 0,
|
|
70
|
+
offY: 0,
|
|
71
|
+
panning: false,
|
|
72
|
+
lastX: 0,
|
|
73
|
+
lastY: 0
|
|
74
|
+
});
|
|
75
|
+
const hoverRef = react.default.useRef(null);
|
|
76
|
+
const [, forceRender] = react.default.useState(0);
|
|
77
|
+
const queryRef = react.default.useRef("");
|
|
78
|
+
queryRef.current = query;
|
|
79
|
+
react.default.useEffect(() => {
|
|
80
|
+
fetch("/okf-graph").then((r) => r.json()).then((g) => {
|
|
81
|
+
if (g.error) setError(g.error);
|
|
82
|
+
else setGraph(g);
|
|
83
|
+
}).catch((e) => setError(String(e.message || e)));
|
|
84
|
+
}, []);
|
|
85
|
+
react.default.useEffect(() => {
|
|
86
|
+
if (!graph || layoutRef.current.length) return;
|
|
87
|
+
const maxW = Math.max(...graph.nodes.map((n) => n.weight), 1);
|
|
88
|
+
const n = graph.nodes.length;
|
|
89
|
+
const R = Math.max(120, Math.min(300, 60 + n * 30));
|
|
90
|
+
layoutRef.current = graph.nodes.map((node, i) => {
|
|
91
|
+
const h = hash(node.id);
|
|
92
|
+
const ang = i / n * Math.PI * 2;
|
|
93
|
+
const r = R * (.6 + h % 100 / 100 * .5);
|
|
94
|
+
const x = Math.cos(ang) * r;
|
|
95
|
+
const y = Math.sin(ang) * r;
|
|
96
|
+
return {
|
|
97
|
+
...node,
|
|
98
|
+
x,
|
|
99
|
+
y,
|
|
100
|
+
vx: 0,
|
|
101
|
+
vy: 0,
|
|
102
|
+
r: 8 + Math.sqrt(node.weight / maxW) * 20
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}, [graph]);
|
|
106
|
+
react.default.useEffect(() => {
|
|
107
|
+
if (!canvasRef.current) return;
|
|
108
|
+
const fit = () => {
|
|
109
|
+
viewRef.current = {
|
|
110
|
+
zoom: 1,
|
|
111
|
+
panX: 0,
|
|
112
|
+
panY: 0
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
fit();
|
|
116
|
+
const onResize = () => fit();
|
|
117
|
+
window.addEventListener("resize", onResize);
|
|
118
|
+
return () => window.removeEventListener("resize", onResize);
|
|
119
|
+
}, [graph]);
|
|
120
|
+
react.default.useEffect(() => {
|
|
121
|
+
if (!graph || !canvasRef.current) return;
|
|
122
|
+
const canvas = canvasRef.current;
|
|
123
|
+
let raf = 0;
|
|
124
|
+
const render = () => {
|
|
125
|
+
drawGraph(canvas, graph, queryRef.current, layoutRef, viewRef, hoverRef);
|
|
126
|
+
raf = requestAnimationFrame(render);
|
|
127
|
+
};
|
|
128
|
+
raf = requestAnimationFrame(render);
|
|
129
|
+
return () => cancelAnimationFrame(raf);
|
|
130
|
+
}, [graph]);
|
|
131
|
+
const onWheel = (e) => {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
const v = viewRef.current;
|
|
134
|
+
const factor = e.deltaY < 0 ? 1.1 : .9;
|
|
135
|
+
v.zoom = Math.min(4, Math.max(.3, v.zoom * factor));
|
|
136
|
+
forceRender((x) => x + 1);
|
|
137
|
+
};
|
|
138
|
+
const onMouseDown = (e) => {
|
|
139
|
+
const canvas = canvasRef.current;
|
|
140
|
+
const rect = canvas.getBoundingClientRect();
|
|
141
|
+
const px = e.clientX - rect.left, py = e.clientY - rect.top;
|
|
142
|
+
const cw = canvas.clientWidth || rect.width, ch = canvas.clientHeight || rect.height;
|
|
143
|
+
const hit = hitTest(px, py, layoutRef.current, viewRef.current, cw, ch);
|
|
144
|
+
if (hit) dragRef.current = {
|
|
145
|
+
id: hit.id,
|
|
146
|
+
offX: hit.x - px,
|
|
147
|
+
offY: hit.y - py,
|
|
148
|
+
panning: false,
|
|
149
|
+
lastX: px,
|
|
150
|
+
lastY: py
|
|
151
|
+
};
|
|
152
|
+
else dragRef.current = {
|
|
153
|
+
id: null,
|
|
154
|
+
offX: 0,
|
|
155
|
+
offY: 0,
|
|
156
|
+
panning: true,
|
|
157
|
+
lastX: px,
|
|
158
|
+
lastY: py
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
const onMouseMove = (e) => {
|
|
162
|
+
const canvas = canvasRef.current;
|
|
163
|
+
const rect = canvas.getBoundingClientRect();
|
|
164
|
+
const px = e.clientX - rect.left, py = e.clientY - rect.top;
|
|
165
|
+
const cw = canvas.clientWidth || rect.width, ch = canvas.clientHeight || rect.height;
|
|
166
|
+
const d = dragRef.current;
|
|
167
|
+
if (d.id) {
|
|
168
|
+
const n = layoutRef.current.find((x) => x.id === d.id);
|
|
169
|
+
if (n) {
|
|
170
|
+
const wx = (px - cw / 2 - viewRef.current.panX) / viewRef.current.zoom;
|
|
171
|
+
const wy = (py - ch / 2 - viewRef.current.panY) / viewRef.current.zoom;
|
|
172
|
+
n.x = wx + d.offX;
|
|
173
|
+
n.y = wy + d.offY;
|
|
174
|
+
n.vx = 0;
|
|
175
|
+
n.vy = 0;
|
|
176
|
+
}
|
|
177
|
+
} else if (d.panning) {
|
|
178
|
+
viewRef.current.panX += px - d.lastX;
|
|
179
|
+
viewRef.current.panY += py - d.lastY;
|
|
180
|
+
d.lastX = px;
|
|
181
|
+
d.lastY = py;
|
|
182
|
+
}
|
|
183
|
+
const hover = hitTest(px, py, layoutRef.current, viewRef.current, cw, ch)?.id ?? null;
|
|
184
|
+
if (hover !== hoverRef.current) {
|
|
185
|
+
hoverRef.current = hover;
|
|
186
|
+
forceRender((x) => x + 1);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const onMouseUp = () => {
|
|
190
|
+
dragRef.current = {
|
|
191
|
+
id: null,
|
|
192
|
+
offX: 0,
|
|
193
|
+
offY: 0,
|
|
194
|
+
panning: false,
|
|
195
|
+
lastX: 0,
|
|
196
|
+
lastY: 0
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
if (error) return react.default.createElement("div", { style: p }, "记忆图谱加载失败: " + error);
|
|
200
|
+
if (!graph) return react.default.createElement("div", { style: p }, "加载记忆图谱…");
|
|
201
|
+
const hover = layoutRef.current.find((n) => n.id === hoverRef.current);
|
|
202
|
+
return react.default.createElement("div", { style: {
|
|
203
|
+
padding: "12px",
|
|
204
|
+
position: "relative"
|
|
205
|
+
} }, react.default.createElement("input", {
|
|
206
|
+
value: query,
|
|
207
|
+
onChange: (e) => setQuery(e.target.value),
|
|
208
|
+
placeholder: "🔍 命中记忆(搜标题/类型/标签)…",
|
|
209
|
+
style: {
|
|
210
|
+
width: "100%",
|
|
211
|
+
boxSizing: "border-box",
|
|
212
|
+
padding: "9px 12px",
|
|
213
|
+
marginBottom: "10px",
|
|
214
|
+
borderRadius: "8px",
|
|
215
|
+
border: "1px solid rgba(120,160,200,.3)",
|
|
216
|
+
background: "rgba(20,32,46,.8)",
|
|
217
|
+
color: "#dbe7f3",
|
|
218
|
+
fontSize: "13px",
|
|
219
|
+
outline: "none"
|
|
220
|
+
}
|
|
221
|
+
}), react.default.createElement("div", { style: {
|
|
222
|
+
color: "#8aa4bd",
|
|
223
|
+
fontSize: "12px",
|
|
224
|
+
marginBottom: "8px"
|
|
225
|
+
} }, `${graph.nodes.length} 节点 · ${graph.edges.length} 边 · 滚轮缩放 · 拖拽平移 · 悬停查看详情 · 搜索命中→神经传导`), react.default.createElement("canvas", {
|
|
226
|
+
ref: canvasRef,
|
|
227
|
+
onWheel,
|
|
228
|
+
onMouseDown,
|
|
229
|
+
onMouseMove,
|
|
230
|
+
onMouseUp,
|
|
231
|
+
onMouseLeave: () => {
|
|
232
|
+
hoverRef.current = null;
|
|
233
|
+
forceRender((x) => x + 1);
|
|
234
|
+
},
|
|
235
|
+
style: {
|
|
236
|
+
width: "100%",
|
|
237
|
+
height: "calc(100vh - 200px)",
|
|
238
|
+
display: "block",
|
|
239
|
+
cursor: "crosshair"
|
|
240
|
+
}
|
|
241
|
+
}), hover ? react.default.createElement("div", { style: {
|
|
242
|
+
position: "absolute",
|
|
243
|
+
top: "54px",
|
|
244
|
+
right: "12px",
|
|
245
|
+
background: "rgba(10,18,28,.97)",
|
|
246
|
+
border: "1px solid " + (TYPE_COLORS[hover.type] || "#3b5a77"),
|
|
247
|
+
borderRadius: "8px",
|
|
248
|
+
padding: "9px 12px",
|
|
249
|
+
fontSize: "12px",
|
|
250
|
+
color: "#eaf3fb",
|
|
251
|
+
maxWidth: "320px",
|
|
252
|
+
boxShadow: "0 6px 20px rgba(0,0,0,.45)",
|
|
253
|
+
zIndex: 10
|
|
254
|
+
} }, react.default.createElement("b", null, hover.title), react.default.createElement("span", { style: {
|
|
255
|
+
fontSize: "10px",
|
|
256
|
+
padding: "1px 6px",
|
|
257
|
+
borderRadius: "8px",
|
|
258
|
+
background: (TYPE_COLORS[hover.type] || "#66") + "66",
|
|
259
|
+
color: "#fff",
|
|
260
|
+
marginLeft: "6px"
|
|
261
|
+
} }, hover.type), react.default.createElement("div", { style: {
|
|
262
|
+
color: "#9db8cf",
|
|
263
|
+
marginTop: "4px"
|
|
264
|
+
} }, "权重 " + hover.weight + " · " + hover.state), hover.description ? react.default.createElement("div", { style: {
|
|
265
|
+
color: "#8aa4bd",
|
|
266
|
+
marginTop: "3px"
|
|
267
|
+
} }, hover.description) : null, hover.tags && hover.tags.length ? react.default.createElement("div", { style: {
|
|
268
|
+
color: "#6f8ba5",
|
|
269
|
+
marginTop: "3px",
|
|
270
|
+
fontSize: "11px"
|
|
271
|
+
} }, hover.tags.map((x) => "#" + x).join(" ")) : null) : null);
|
|
272
|
+
}
|
|
273
|
+
const p = {
|
|
274
|
+
padding: "24px",
|
|
275
|
+
color: "#8aa4bd",
|
|
276
|
+
fontFamily: "sans-serif"
|
|
277
|
+
};
|
|
278
|
+
function matches(n, q) {
|
|
279
|
+
const t = q.trim().toLowerCase();
|
|
280
|
+
if (!t) return false;
|
|
281
|
+
return (n.title + " " + n.type + " " + (n.tags || []).join(" ")).toLowerCase().includes(t);
|
|
282
|
+
}
|
|
283
|
+
function activateHits(nodes, edges, hits) {
|
|
284
|
+
const act = new Set(hits);
|
|
285
|
+
const adj = /* @__PURE__ */ new Map();
|
|
286
|
+
edges.forEach((e) => {
|
|
287
|
+
if (!adj.has(e.source)) adj.set(e.source, []);
|
|
288
|
+
if (!adj.has(e.target)) adj.set(e.target, []);
|
|
289
|
+
adj.get(e.source).push(e.target);
|
|
290
|
+
adj.get(e.target).push(e.source);
|
|
291
|
+
});
|
|
292
|
+
const q = [...hits];
|
|
293
|
+
while (q.length) {
|
|
294
|
+
const cur = q.shift();
|
|
295
|
+
(adj.get(cur) || []).forEach((nb) => {
|
|
296
|
+
if (!act.has(nb)) {
|
|
297
|
+
act.add(nb);
|
|
298
|
+
q.push(nb);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return act;
|
|
303
|
+
}
|
|
304
|
+
function hitTest(px, py, nodes, view, canvasW, canvasH) {
|
|
305
|
+
const wx = (px - canvasW / 2 - view.panX) / view.zoom, wy = (py - canvasH / 2 - view.panY) / view.zoom;
|
|
306
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
307
|
+
const n = nodes[i];
|
|
308
|
+
const dx = n.x - wx, dy = n.y - wy;
|
|
309
|
+
if (dx * dx + dy * dy < (n.r + 6) * (n.r + 6)) return n;
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
function drawGraph(canvas, graph, query, layoutRef, viewRef, hoverRef) {
|
|
314
|
+
const dpr = window.devicePixelRatio || 1;
|
|
315
|
+
const W = canvas.clientWidth || 800, H = canvas.clientHeight || 500;
|
|
316
|
+
canvas.width = W * dpr;
|
|
317
|
+
canvas.height = H * dpr;
|
|
318
|
+
const ctx = canvas.getContext("2d");
|
|
319
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
320
|
+
const view = viewRef.current;
|
|
321
|
+
Math.max(...graph.nodes.map((n) => n.weight), 1);
|
|
322
|
+
const nodes = layoutRef.current;
|
|
323
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
324
|
+
const edges = graph.edges.filter((e) => byId.has(e.source) && byId.has(e.target));
|
|
325
|
+
const hits = /* @__PURE__ */ new Set();
|
|
326
|
+
if (query.trim()) graph.nodes.forEach((n) => {
|
|
327
|
+
if (matches(n, query)) hits.add(n.id);
|
|
328
|
+
});
|
|
329
|
+
const active = activateHits(graph.nodes, graph.edges, hits);
|
|
330
|
+
const tx = (x) => W / 2 + x * view.zoom + view.panX;
|
|
331
|
+
const ty = (y) => H / 2 + y * view.zoom + view.panY;
|
|
332
|
+
ctx.fillStyle = "#13233a";
|
|
333
|
+
ctx.fillRect(0, 0, W, H);
|
|
334
|
+
edges.forEach((e) => {
|
|
335
|
+
const a = byId.get(e.source), b = byId.get(e.target);
|
|
336
|
+
const isActive = active.has(e.source) || active.has(e.target);
|
|
337
|
+
ctx.beginPath();
|
|
338
|
+
ctx.moveTo(tx(a.x), ty(a.y));
|
|
339
|
+
ctx.lineTo(tx(b.x), ty(b.y));
|
|
340
|
+
ctx.strokeStyle = isActive ? "rgba(126,195,255,.5)" : "rgba(120,160,200,.18)";
|
|
341
|
+
ctx.lineWidth = 1 / view.zoom;
|
|
342
|
+
ctx.stroke();
|
|
343
|
+
});
|
|
344
|
+
const t = performance.now() / 700;
|
|
345
|
+
nodes.forEach((n) => {
|
|
346
|
+
const c = TYPE_COLORS[n.type] || TYPE_COLORS.Other;
|
|
347
|
+
const isHit = hits.has(n.id), isActive = active.has(n.id);
|
|
348
|
+
const rr = n.r * view.zoom;
|
|
349
|
+
const x = tx(n.x), y = ty(n.y);
|
|
350
|
+
if (isHit) {
|
|
351
|
+
const tp = (t + hash(n.id) % 10) % 1;
|
|
352
|
+
const pr = rr * (1.5 + tp * 1.8);
|
|
353
|
+
ctx.beginPath();
|
|
354
|
+
ctx.arc(x, y, pr, 0, Math.PI * 2);
|
|
355
|
+
ctx.strokeStyle = hexToRgba(c, .8 * (1 - tp));
|
|
356
|
+
ctx.lineWidth = 2.5 / view.zoom;
|
|
357
|
+
ctx.stroke();
|
|
358
|
+
}
|
|
359
|
+
if (isHit || isActive) {
|
|
360
|
+
const rg = ctx.createRadialGradient(x, y, rr * .2, x, y, rr * (isHit ? 2.9 : 2.1));
|
|
361
|
+
rg.addColorStop(0, hexToRgba(c, isHit ? .8 : .45));
|
|
362
|
+
rg.addColorStop(1, "transparent");
|
|
363
|
+
ctx.beginPath();
|
|
364
|
+
ctx.arc(x, y, rr * (isHit ? 2.9 : 2.1), 0, Math.PI * 2);
|
|
365
|
+
ctx.fillStyle = rg;
|
|
366
|
+
ctx.fill();
|
|
367
|
+
}
|
|
368
|
+
ctx.beginPath();
|
|
369
|
+
ctx.arc(x, y, rr, 0, Math.PI * 2);
|
|
370
|
+
ctx.fillStyle = isActive ? isHit ? "#fff" : c : "#13233a";
|
|
371
|
+
ctx.globalAlpha = isActive ? .95 : 1;
|
|
372
|
+
ctx.fill();
|
|
373
|
+
ctx.globalAlpha = 1;
|
|
374
|
+
ctx.beginPath();
|
|
375
|
+
ctx.arc(x, y, rr, 0, Math.PI * 2);
|
|
376
|
+
ctx.strokeStyle = isHit ? "#fff" : isActive ? c : "#33506a";
|
|
377
|
+
ctx.lineWidth = (isHit ? 3.2 : 1.2) / view.zoom;
|
|
378
|
+
ctx.stroke();
|
|
379
|
+
ctx.globalAlpha = 1;
|
|
380
|
+
ctx.fillStyle = isActive ? "#fff" : "#5f7d97";
|
|
381
|
+
ctx.font = `${11 * Math.min(1.4, Math.max(.8, view.zoom))}px sans-serif`;
|
|
382
|
+
ctx.textAlign = "center";
|
|
383
|
+
ctx.fillText(n.title.slice(0, 10), x, y + rr + 12);
|
|
384
|
+
if (isHit) {
|
|
385
|
+
ctx.fillStyle = "#fff";
|
|
386
|
+
ctx.font = `700 ${11 * Math.max(.8, view.zoom)}px sans-serif`;
|
|
387
|
+
ctx.fillText("⚡命中", x, y - rr - 9);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
function hash(s) {
|
|
392
|
+
let h = 0;
|
|
393
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) >>> 0;
|
|
394
|
+
return h;
|
|
395
|
+
}
|
|
396
|
+
function hexToRgba(hex, a) {
|
|
397
|
+
const c = hex.replace("#", "");
|
|
398
|
+
return `rgba(${parseInt(c.slice(0, 2), 16)},${parseInt(c.slice(2, 4), 16)},${parseInt(c.slice(4, 6), 16)},${a})`;
|
|
399
|
+
}
|
|
400
|
+
function apply(ctx) {
|
|
401
|
+
ctx.locale.bind(NS);
|
|
402
|
+
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
403
|
+
name: "conversation.view",
|
|
404
|
+
id: "okf-memory",
|
|
405
|
+
order: 30,
|
|
406
|
+
locale: NS,
|
|
407
|
+
label: () => "记忆图谱"
|
|
408
|
+
}, MemoryGraphView));
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
exports.apply = apply;
|
|
412
|
+
exports.inject = inject;
|
|
413
|
+
return module.exports;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["React"],"sources":["../src/client/index.tsx"],"sourcesContent":["/**\n * okf-memory client(浏览器半,dsh 专用) — 记忆图谱会话标签页。\n *\n * 契约:\n * - package.json 声明 dsh.client { platform:'web', inject:[client服务] }\n * - ctx.slots.inject(\"conversation.view\", ...) 注册对话视图标签\n * - fetch 后端 /okf-graph → 力导向渲染\n * - 交互:滚轮缩放 / 拖拽平移 / 拖节点 / 悬停显示详情 / 搜索命中+神经传导\n *\n * 只保留「记忆图谱」一个视图。\n */\nimport React from \"react\";\n\nexport const inject = [\"slots\", \"locale\"];\nconst NS = \"okf-memory\";\n\nconst TYPE_COLORS: Record<string, string> = {\n Fact: \"#2f7bff\", Preference: \"#ffd400\", Decision: \"#ff2d55\", Method: \"#00e66e\",\n Insight: \"#c04dff\", Idea: \"#ff7a00\", Lesson: \"#c8d1dd\", TechChoice: \"#00e5ff\", Other: \"#9aa7b8\",\n};\ntype GraphNode = { id: string; title: string; type: string; weight: number; state: string; description?: string; tags?: string[] };\ntype GraphEdge = { source: string; target: string };\n\ntype LayoutNode = GraphNode & { x: number; y: number; vx: number; vy: number; r: number };\n\nfunction MemoryGraphView() {\n const [graph, setGraph] = React.useState<{ nodes: GraphNode[]; edges: GraphEdge[] } | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const [query, setQuery] = React.useState(\"\");\n const canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n const layoutRef = React.useRef<LayoutNode[]>([]);\n const viewRef = React.useRef({ zoom: 1, panX: 0, panY: 0 });\n const dragRef = React.useRef<{ id: string | null; offX: number; offY: number; panning: boolean; lastX: number; lastY: number }>({ id: null, offX: 0, offY: 0, panning: false, lastX: 0, lastY: 0 });\n const hoverRef = React.useRef<string | null>(null);\n const [, forceRender] = React.useState(0); // 触发 tooltip 更新\n\n const queryRef = React.useRef(\"\");\n queryRef.current = query;\n\n React.useEffect(() => {\n fetch(\"/okf-graph\")\n .then((r) => r.json())\n .then((g) => { if (g.error) setError(g.error); else setGraph(g); })\n .catch((e) => setError(String(e.message || e)));\n }, []);\n\n // 初始化布局(一次,稳定):世界坐标以(0,0)为中心,节点分布半径随数量自适应\n React.useEffect(() => {\n if (!graph || layoutRef.current.length) return;\n const maxW = Math.max(...graph.nodes.map((n) => n.weight), 1);\n const n = graph.nodes.length;\n const R = Math.max(120, Math.min(300, 60 + n * 30));\n layoutRef.current = graph.nodes.map((node, i) => {\n const h = hash(node.id);\n const ang = (i / n) * Math.PI * 2; // 环形均布 + 轻微抖动\n const r = R * (0.6 + ((h % 100) / 100) * 0.5);\n const x = Math.cos(ang) * r;\n const y = Math.sin(ang) * r;\n return { ...node, x, y, vx: 0, vy: 0, r: 8 + Math.sqrt(node.weight / maxW) * 20 };\n });\n }, [graph]);\n\n // 缩放画布尺寸变化时重置 zoom\n React.useEffect(() => {\n const canvas = canvasRef.current; if (!canvas) return;\n const fit = () => { viewRef.current = { zoom: 1, panX: 0, panY: 0 }; };\n fit();\n const onResize = () => fit();\n window.addEventListener(\"resize\", onResize);\n return () => window.removeEventListener(\"resize\", onResize);\n }, [graph]);\n\n // rAF 动画循环\n React.useEffect(() => {\n if (!graph || !canvasRef.current) return;\n const canvas = canvasRef.current;\n let raf = 0;\n const render = () => { drawGraph(canvas, graph, queryRef.current, layoutRef, viewRef, hoverRef); raf = requestAnimationFrame(render); };\n raf = requestAnimationFrame(render);\n return () => cancelAnimationFrame(raf);\n }, [graph]);\n\n const onWheel = (e: React.WheelEvent) => {\n e.preventDefault();\n const v = viewRef.current;\n const factor = e.deltaY < 0 ? 1.1 : 0.9;\n v.zoom = Math.min(4, Math.max(0.3, v.zoom * factor));\n forceRender((x) => x + 1);\n };\n const onMouseDown = (e: React.MouseEvent) => {\n const canvas = canvasRef.current!; const rect = canvas.getBoundingClientRect();\n const px = (e.clientX - rect.left), py = (e.clientY - rect.top);\n const cw = canvas.clientWidth || rect.width, ch = canvas.clientHeight || rect.height;\n const hit = hitTest(px, py, layoutRef.current, viewRef.current, cw, ch);\n if (hit) { dragRef.current = { id: hit.id, offX: hit.x - px, offY: hit.y - py, panning: false, lastX: px, lastY: py }; }\n else { dragRef.current = { id: null, offX: 0, offY: 0, panning: true, lastX: px, lastY: py }; }\n };\n const onMouseMove = (e: React.MouseEvent) => {\n const canvas = canvasRef.current!; const rect = canvas.getBoundingClientRect();\n const px = (e.clientX - rect.left), py = (e.clientY - rect.top);\n const cw = canvas.clientWidth || rect.width, ch = canvas.clientHeight || rect.height;\n const d = dragRef.current;\n if (d.id) {\n const n = layoutRef.current.find((x) => x.id === d.id);\n if (n) {\n // 拖节点:用世界坐标(逆变换),offX 为世界坐标内偏移\n const wx = (px - cw / 2 - viewRef.current.panX) / viewRef.current.zoom;\n const wy = (py - ch / 2 - viewRef.current.panY) / viewRef.current.zoom;\n n.x = wx + d.offX; n.y = wy + d.offY; n.vx = 0; n.vy = 0;\n }\n } else if (d.panning) {\n viewRef.current.panX += px - d.lastX; viewRef.current.panY += py - d.lastY;\n d.lastX = px; d.lastY = py;\n }\n const hover = hitTest(px, py, layoutRef.current, viewRef.current, cw, ch)?.id ?? null;\n if (hover !== hoverRef.current) { hoverRef.current = hover; forceRender((x) => x + 1); }\n };\n const onMouseUp = () => { dragRef.current = { id: null, offX: 0, offY: 0, panning: false, lastX: 0, lastY: 0 }; };\n\n if (error) return React.createElement(\"div\", { style: p }, \"记忆图谱加载失败: \" + error);\n if (!graph) return React.createElement(\"div\", { style: p }, \"加载记忆图谱…\");\n\n const hover = layoutRef.current.find((n) => n.id === hoverRef.current);\n return React.createElement(\"div\", { style: { padding: \"12px\", position: \"relative\" } },\n React.createElement(\"input\", {\n value: query, onChange: (e) => setQuery(e.target.value),\n placeholder: \"🔍 命中记忆(搜标题/类型/标签)…\",\n style: { width: \"100%\", boxSizing: \"border-box\", padding: \"9px 12px\", marginBottom: \"10px\", borderRadius: \"8px\", border: \"1px solid rgba(120,160,200,.3)\", background: \"rgba(20,32,46,.8)\", color: \"#dbe7f3\", fontSize: \"13px\", outline: \"none\" },\n }),\n React.createElement(\"div\", { style: { color: \"#8aa4bd\", fontSize: \"12px\", marginBottom: \"8px\" } },\n `${graph.nodes.length} 节点 · ${graph.edges.length} 边 · 滚轮缩放 · 拖拽平移 · 悬停查看详情 · 搜索命中→神经传导`),\n React.createElement(\"canvas\", { ref: canvasRef, onWheel, onMouseDown, onMouseMove, onMouseUp, onMouseLeave: () => { hoverRef.current = null; forceRender((x) => x + 1); }, style: { width: \"100%\", height: \"calc(100vh - 200px)\", display: \"block\", cursor: \"crosshair\" } }),\n hover\n ? React.createElement(\"div\", { style: { position: \"absolute\", top: \"54px\", right: \"12px\", background: \"rgba(10,18,28,.97)\", border: \"1px solid \" + (TYPE_COLORS[hover.type] || \"#3b5a77\"), borderRadius: \"8px\", padding: \"9px 12px\", fontSize: \"12px\", color: \"#eaf3fb\", maxWidth: \"320px\", boxShadow: \"0 6px 20px rgba(0,0,0,.45)\", zIndex: 10 } },\n React.createElement(\"b\", null, hover.title), React.createElement(\"span\", { style: { fontSize: \"10px\", padding: \"1px 6px\", borderRadius: \"8px\", background: (TYPE_COLORS[hover.type] || \"#66\") + \"66\", color: \"#fff\", marginLeft: \"6px\" } }, hover.type),\n React.createElement(\"div\", { style: { color: \"#9db8cf\", marginTop: \"4px\" } }, \"权重 \" + hover.weight + \" · \" + hover.state),\n hover.description ? React.createElement(\"div\", { style: { color: \"#8aa4bd\", marginTop: \"3px\" } }, hover.description) : null,\n (hover.tags && hover.tags.length) ? React.createElement(\"div\", { style: { color: \"#6f8ba5\", marginTop: \"3px\", fontSize: \"11px\" } }, hover.tags.map((x) => \"#\" + x).join(\" \")) : null,\n )\n : null,\n );\n}\nconst p: React.CSSProperties = { padding: \"24px\", color: \"#8aa4bd\", fontFamily: \"sans-serif\" };\n\nfunction matches(n: GraphNode, q: string): boolean {\n const t = q.trim().toLowerCase(); if (!t) return false;\n return (n.title + \" \" + n.type + \" \" + (n.tags || []).join(\" \")).toLowerCase().includes(t);\n}\nfunction activateHits(nodes: GraphNode[], edges: GraphEdge[], hits: Set<string>): Set<string> {\n const act = new Set<string>(hits);\n const adj = new Map<string, string[]>();\n edges.forEach((e) => { if (!adj.has(e.source)) adj.set(e.source, []); if (!adj.has(e.target)) adj.set(e.target, []); adj.get(e.source)!.push(e.target); adj.get(e.target)!.push(e.source); });\n const q = [...hits];\n while (q.length) { const cur = q.shift()!; (adj.get(cur) || []).forEach((nb) => { if (!act.has(nb)) { act.add(nb); q.push(nb); } }); }\n return act;\n}\n\nfunction hitTest(px: number, py: number, nodes: LayoutNode[], view: { zoom: number; panX: number; panY: number }, canvasW: number, canvasH: number) {\n // 屏幕坐标 → 世界坐标(世界原点在画布中心 W/2,H/2)\n const wx = (px - canvasW / 2 - view.panX) / view.zoom, wy = (py - canvasH / 2 - view.panY) / view.zoom;\n for (let i = nodes.length - 1; i >= 0; i--) { const n = nodes[i]; const dx = n.x - wx, dy = n.y - wy; if (dx * dx + dy * dy < (n.r + 6) * (n.r + 6)) return n; }\n return null;\n}\n\nfunction drawGraph(canvas: HTMLCanvasElement, graph: { nodes: GraphNode[]; edges: GraphEdge[] }, query: string, layoutRef: React.MutableRefObject<LayoutNode[]>, viewRef: React.MutableRefObject<{ zoom: number; panX: number; panY: number }>, hoverRef: React.MutableRefObject<string | null>) {\n const dpr = window.devicePixelRatio || 1;\n const W = canvas.clientWidth || 800, H = canvas.clientHeight || 500;\n canvas.width = W * dpr; canvas.height = H * dpr;\n const ctx = canvas.getContext(\"2d\")!; ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n const view = viewRef.current;\n\n const maxW = Math.max(...graph.nodes.map((n) => n.weight), 1);\n const nodes = layoutRef.current;\n const byId = new Map(nodes.map((n) => [n.id, n]));\n const edges = graph.edges.filter((e) => byId.has(e.source) && byId.has(e.target));\n\n const hits = new Set<string>();\n if (query.trim()) graph.nodes.forEach((n) => { if (matches(n, query)) hits.add(n.id); });\n const active = activateHits(graph.nodes, graph.edges, hits);\n\n // 世界原点在画布中心:tx(x)=W/2 + x*zoom + panX\n const tx = (x: number) => W / 2 + x * view.zoom + view.panX;\n const ty = (y: number) => H / 2 + y * view.zoom + view.panY;\n\n ctx.fillStyle = \"#13233a\"; ctx.fillRect(0, 0, W, H);\n\n // 边\n edges.forEach((e) => {\n const a = byId.get(e.source)!, b = byId.get(e.target)!;\n const isActive = active.has(e.source) || active.has(e.target);\n ctx.beginPath(); ctx.moveTo(tx(a.x), ty(a.y)); ctx.lineTo(tx(b.x), ty(b.y));\n ctx.strokeStyle = isActive ? \"rgba(126,195,255,.5)\" : \"rgba(120,160,200,.18)\"; ctx.lineWidth = 1 / view.zoom; ctx.stroke();\n });\n\n const t = performance.now() / 700;\n nodes.forEach((n) => {\n const c = TYPE_COLORS[n.type] || TYPE_COLORS.Other;\n const isHit = hits.has(n.id), isActive = active.has(n.id);\n const rr = n.r * view.zoom;\n const x = tx(n.x), y = ty(n.y);\n if (isHit) {\n const tp = (t + hash(n.id) % 10) % 1;\n const pr = rr * (1.5 + tp * 1.8);\n ctx.beginPath(); ctx.arc(x, y, pr, 0, Math.PI * 2); ctx.strokeStyle = hexToRgba(c, 0.8 * (1 - tp)); ctx.lineWidth = 2.5 / view.zoom; ctx.stroke();\n }\n if (isHit || isActive) {\n const rg = ctx.createRadialGradient(x, y, rr * 0.2, x, y, rr * (isHit ? 2.9 : 2.1));\n rg.addColorStop(0, hexToRgba(c, isHit ? 0.8 : 0.45)); rg.addColorStop(1, \"transparent\");\n ctx.beginPath(); ctx.arc(x, y, rr * (isHit ? 2.9 : 2.1), 0, Math.PI * 2); ctx.fillStyle = rg; ctx.fill();\n }\n ctx.beginPath(); ctx.arc(x, y, rr, 0, Math.PI * 2); ctx.fillStyle = isActive ? (isHit ? \"#fff\" : c) : \"#13233a\"; ctx.globalAlpha = isActive ? 0.95 : 1; ctx.fill(); ctx.globalAlpha = 1;\n ctx.beginPath(); ctx.arc(x, y, rr, 0, Math.PI * 2); ctx.strokeStyle = isHit ? \"#fff\" : (isActive ? c : \"#33506a\"); ctx.lineWidth = (isHit ? 3.2 : 1.2) / view.zoom; ctx.stroke();\n ctx.globalAlpha = 1;\n // 标签(缩放时字号随 zoom 微调,避免太小)\n ctx.fillStyle = isActive ? \"#fff\" : \"#5f7d97\"; ctx.font = `${11 * Math.min(1.4, Math.max(0.8, view.zoom))}px sans-serif`; ctx.textAlign = \"center\";\n ctx.fillText(n.title.slice(0, 10), x, y + rr + 12);\n if (isHit) { ctx.fillStyle = \"#fff\"; ctx.font = `700 ${11 * Math.max(0.8, view.zoom)}px sans-serif`; ctx.fillText(\"⚡命中\", x, y - rr - 9); }\n });\n}\n\nfunction hash(s: string): number { let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return h; }\nfunction hexToRgba(hex: string, a: number): string { const c = hex.replace(\"#\", \"\"); const r = parseInt(c.slice(0, 2), 16), g = parseInt(c.slice(2, 4), 16), b = parseInt(c.slice(4, 6), 16); return `rgba(${r},${g},${b},${a})`; }\n\nexport function apply(ctx: { slots: { inject: (name: string, factory: () => unknown) => void; register: (spec: Record<string, unknown>, component: unknown) => unknown }; locale: { bind: (ns: string) => (key: string) => string } }): void {\n ctx.locale.bind(NS);\n ctx.slots.inject(\"conversation.view\", () =>\n ctx.slots.register(\n { name: \"conversation.view\", id: \"okf-memory\", order: 30, locale: NS, label: () => \"记忆图谱\" },\n MemoryGraphView,\n ),\n );\n}\n"],"mappings":"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"}
|