dsh-memory-engine 0.6.1 → 0.7.0-beta.2b
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 +73 -14
- package/lib/client.js +583 -26
- package/lib/client.src.js +397 -0
- package/lib/config.js +5 -0
- package/lib/index.js +4 -5
- package/lib/map.js +15 -10
- package/lib/onboard.js +18 -0
- package/lib/render/canvas.js +259 -0
- package/lib/render/render.mjs +15 -175
- package/lib/routes.js +56 -0
- package/package.json +2 -1
- package/scripts/build-client.mjs +22 -0
- package/scripts/lib/strip-export.mjs +7 -0
- package/lib/serve.js +0 -150
package/lib/client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// 由 scripts/build-client.mjs 生成,别直接改这个文件
|
|
1
2
|
window.__ModuleLoader__.load({
|
|
2
3
|
id: "dsh-memory-engine",
|
|
3
4
|
factory: function (require) {
|
|
@@ -12,26 +13,565 @@ window.__ModuleLoader__.load({
|
|
|
12
13
|
// 不同 DSH 版本暴露的插槽名不一样:新版本用 header.actions,
|
|
13
14
|
// 老版本没有这个槽时退回 views.actions(会出现在「更多视图」菜单里,虽然不显眼但能用)。
|
|
14
15
|
var SLOT_FALLBACKS = ["conversation.session.header.actions", "conversation.session.header.views.actions"];
|
|
15
|
-
var VIEW_URL = "http://127.0.0.1:18886/index.html";
|
|
16
16
|
|
|
17
|
-
function
|
|
17
|
+
function createRenderer(canvas, data, options = {}) {
|
|
18
|
+
const document = canvas.ownerDocument;
|
|
19
|
+
const window = document.defaultView;
|
|
20
|
+
const performance = window.performance;
|
|
21
|
+
const ctx = canvas.getContext('2d', { alpha: false });
|
|
22
|
+
if (!ctx) throw new Error('Canvas 2D 不可用');
|
|
23
|
+
const colors = { '人格':[255,215,106], '印象':[143,216,255], '知识':[126,231,178], '日记':[190,160,255], '对话':[255,160,140], '情绪':[255,90,90] };
|
|
24
|
+
const fallback = [145,205,255];
|
|
25
|
+
let nodes = [], links = [], hubs = [];
|
|
26
|
+
const byId = new Map(), adjacency = new Map(), sprites = new Map();
|
|
27
|
+
const pulses = [], firing = new Map(), selections = new Set();
|
|
28
|
+
if (typeof options.onSelect === 'function') selections.add(options.onSelect);
|
|
29
|
+
let seed = 987654321;
|
|
30
|
+
const random = () => ((seed = seed * 16807 % 2147483647) - 1) / 2147483646;
|
|
31
|
+
let width=0,height=0,dpr=1,rotX=0.3,rotY=0.15,zoom=data.nodes.length<50?1.6:1,autoSpin=true;
|
|
32
|
+
let drag=null,nextFire=0,lastTime=0,rafId=null,running=false,destroyed=false,generation=0,observer=null;
|
|
33
|
+
|
|
34
|
+
function glowSprite(rgb) {
|
|
35
|
+
const off = document.createElement('canvas');
|
|
36
|
+
off.width = off.height = 96;
|
|
37
|
+
const brush = off.getContext('2d');
|
|
38
|
+
const gradient = brush.createRadialGradient(48,48,1,48,48,48);
|
|
39
|
+
gradient.addColorStop(0,'rgba('+rgb.join(',')+',0.7)');
|
|
40
|
+
gradient.addColorStop(0.4,'rgba('+rgb.join(',')+',0.19)');
|
|
41
|
+
gradient.addColorStop(1,'rgba('+rgb.join(',')+',0)');
|
|
42
|
+
brush.fillStyle = gradient; brush.fillRect(0,0,96,96);
|
|
43
|
+
return off;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function setData(nextData) {
|
|
47
|
+
if (destroyed) return;
|
|
48
|
+
nodes = nextData.nodes.map(node => ({ ...node, pos: { ...node.pos } }));
|
|
49
|
+
links = nextData.links.map(link => [...link]);
|
|
50
|
+
byId.clear(); adjacency.clear(); sprites.clear(); pulses.length = 0; firing.clear();
|
|
51
|
+
seed = 987654321; nextFire = 0;
|
|
52
|
+
for (const node of nextData.nodes) byId.set(node.id, node);
|
|
53
|
+
for (const node of nodes) adjacency.set(node.id, []);
|
|
54
|
+
for (const [left,right] of links) {
|
|
55
|
+
if (adjacency.has(left) && adjacency.has(right)) { adjacency.get(left).push(right); adjacency.get(right).push(left); }
|
|
56
|
+
}
|
|
57
|
+
hubs = nodes.filter(star => star.degree > 0).sort((left,right) => right.degree-left.degree).slice(0,6);
|
|
58
|
+
for (const star of nodes) {
|
|
59
|
+
star._rgb = colors[star.kind] || fallback;
|
|
60
|
+
const count = Math.min(9, 4 + Math.round(Math.sqrt(star.degree) * 1.2));
|
|
61
|
+
star._dendrites = Array.from({length:count},(_,index) => ({
|
|
62
|
+
angle:index/count*Math.PI*2+random()*0.7,
|
|
63
|
+
length:0.9+random()*1.5, bend:(random()-0.5)*0.9, width:0.5+random()*0.7,
|
|
64
|
+
fork:random()<0.55 ? (random()-0.5)*1.5 : null
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
for (const star of nodes) {
|
|
68
|
+
const key = star._rgb.join(',');
|
|
69
|
+
if (!sprites.has(key)) sprites.set(key,glowSprite(star._rgb));
|
|
70
|
+
star._glow = sprites.get(key);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resize() {
|
|
76
|
+
if (destroyed) return;
|
|
77
|
+
const bounds = canvas.getBoundingClientRect();
|
|
78
|
+
width=bounds.width; height=bounds.height; dpr=Math.min(1.5,window.devicePixelRatio||1);
|
|
79
|
+
canvas.width=Math.round(width*dpr); canvas.height=Math.round(height*dpr);
|
|
80
|
+
ctx.setTransform(dpr,0,0,dpr,0,0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function project(pos) {
|
|
84
|
+
const cy=Math.cos(rotY),sy=Math.sin(rotY),cx=Math.cos(rotX),sx=Math.sin(rotX);
|
|
85
|
+
const x=pos.x*cy-pos.z*sy,z=pos.x*sy+pos.z*cy;
|
|
86
|
+
const y=pos.y*cx-z*sx,depth=pos.y*sx+z*cx+250;
|
|
87
|
+
if (depth<=12) return null;
|
|
88
|
+
const scale=760/depth*zoom;
|
|
89
|
+
return {x:width/2+x*scale,y:height/2+y*scale,scale,depth};
|
|
90
|
+
}
|
|
91
|
+
function fire(id,now) {
|
|
92
|
+
firing.set(id,1);
|
|
93
|
+
for (const other of adjacency.get(id)||[]) {
|
|
94
|
+
firing.set(other,Math.max(0.65,firing.get(other)||0));
|
|
95
|
+
pulses.push({from:id,to:other,born:now,life:900+Math.random()*700});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function select(x,y) {
|
|
100
|
+
let best=null,bestScore=Infinity;
|
|
101
|
+
for (const star of nodes) {
|
|
102
|
+
if (!star._screen) continue;
|
|
103
|
+
const value=Math.hypot(star._screen.x-x,star._screen.y-y);
|
|
104
|
+
const tolerance=Math.max(14,star._screen.r*1.15);
|
|
105
|
+
if (value>tolerance) continue;
|
|
106
|
+
const score=value-star._screen.r*0.22;
|
|
107
|
+
if (score<bestScore) {bestScore=score;best=star;}
|
|
108
|
+
}
|
|
109
|
+
if (!best) return;
|
|
110
|
+
fire(best.id,performance.now());
|
|
111
|
+
for (const callback of selections) callback(byId.get(best.id));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function onPointerDown(event) {
|
|
115
|
+
autoSpin=false; drag={x:event.clientX,y:event.clientY,moved:0,pointerId:event.pointerId};
|
|
116
|
+
canvas.setPointerCapture(event.pointerId);
|
|
117
|
+
}
|
|
118
|
+
function onPointerMove(event) {
|
|
119
|
+
if (!drag) return;
|
|
120
|
+
const dx=event.clientX-drag.x,dy=event.clientY-drag.y;
|
|
121
|
+
drag.x=event.clientX; drag.y=event.clientY; drag.moved+=Math.abs(dx)+Math.abs(dy);
|
|
122
|
+
rotY+=dx*0.006; rotX=Math.max(-1.5,Math.min(1.5,rotX+dy*0.006));
|
|
123
|
+
}
|
|
124
|
+
function releaseDrag() {
|
|
125
|
+
if (drag && canvas.hasPointerCapture(drag.pointerId)) canvas.releasePointerCapture(drag.pointerId);
|
|
126
|
+
drag=null;
|
|
127
|
+
}
|
|
128
|
+
function onPointerUp(event) {
|
|
129
|
+
const clicked = drag && drag.moved<6;
|
|
130
|
+
releaseDrag();
|
|
131
|
+
if (clicked) {
|
|
132
|
+
const bounds = canvas.getBoundingClientRect();
|
|
133
|
+
select(event.clientX-bounds.left,event.clientY-bounds.top);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function onWheel(event) {
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
zoom=Math.max(0.3,Math.min(4.5,zoom*(event.deltaY>0?0.92:1.08)));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function frame(now) {
|
|
142
|
+
const delta=Math.min(50,now-lastTime||16.7); lastTime=now;
|
|
143
|
+
if (autoSpin) rotY+=0.0011*delta/16.7;
|
|
144
|
+
if (now>nextFire) { nextFire=now+1000+Math.random()*1500; if (hubs.length) fire(hubs[Math.floor(Math.random()*hubs.length)].id,now); }
|
|
145
|
+
ctx.globalCompositeOperation='source-over'; ctx.fillStyle='#03060c'; ctx.fillRect(0,0,width,height);
|
|
146
|
+
ctx.strokeStyle='rgba(40,90,140,0.055)';ctx.lineWidth=1;
|
|
147
|
+
for (let index=0;index<20;index++) {
|
|
148
|
+
const y=index/20*height;
|
|
149
|
+
ctx.beginPath();ctx.moveTo(0,y);ctx.bezierCurveTo(width*.3,y+Math.sin(now*.0003+index)*30,width*.7,y-Math.cos(now*.00025+index)*30,width,y);ctx.stroke();
|
|
150
|
+
}
|
|
151
|
+
const points=[];
|
|
152
|
+
for (const star of nodes) {
|
|
153
|
+
const p=project(star.pos);
|
|
154
|
+
if (!p || p.x< -100 || p.x>width+100 || p.y< -100 || p.y>height+100) {star._screen=null;continue;}
|
|
155
|
+
points.push({star,p});
|
|
156
|
+
}
|
|
157
|
+
points.sort((left,right)=>right.p.depth-left.p.depth);
|
|
158
|
+
const screen=new Map(points.map(point=>[point.star.id,point]));
|
|
159
|
+
ctx.globalCompositeOperation='lighter';
|
|
160
|
+
for (const [left,right] of links) {
|
|
161
|
+
const first=screen.get(left),second=screen.get(right);
|
|
162
|
+
if (!first||!second) continue;
|
|
163
|
+
const act=Math.max(firing.get(left)||0,firing.get(right)||0);
|
|
164
|
+
const alpha=Math.min(.85,.1*Math.min(first.star.retention,second.star.retention)+.35*act);
|
|
165
|
+
if (alpha<.01) continue;
|
|
166
|
+
const mx=(first.p.x+second.p.x)/2+(first.p.y-second.p.y)*.18;
|
|
167
|
+
const my=(first.p.y+second.p.y)/2+(second.p.x-first.p.x)*.18;
|
|
168
|
+
ctx.strokeStyle='rgba(110,190,255,'+alpha+')';ctx.lineWidth=.7+act*1.6;
|
|
169
|
+
ctx.beginPath();ctx.moveTo(first.p.x,first.p.y);ctx.quadraticCurveTo(mx,my,second.p.x,second.p.y);ctx.stroke();
|
|
170
|
+
}
|
|
171
|
+
for (let index=pulses.length-1;index>=0;index--) {
|
|
172
|
+
const pulse=pulses[index],fraction=(now-pulse.born)/pulse.life;
|
|
173
|
+
if (fraction>=1) {pulses.splice(index,1);continue;}
|
|
174
|
+
const first=screen.get(pulse.from),second=screen.get(pulse.to);
|
|
175
|
+
if (!first||!second) continue;
|
|
176
|
+
const mx=(first.p.x+second.p.x)/2+(first.p.y-second.p.y)*.18,my=(first.p.y+second.p.y)/2+(second.p.x-first.p.x)*.18;
|
|
177
|
+
const inverse=1-fraction;
|
|
178
|
+
const x=inverse*inverse*first.p.x+2*inverse*fraction*mx+fraction*fraction*second.p.x;
|
|
179
|
+
const y=inverse*inverse*first.p.y+2*inverse*fraction*my+fraction*fraction*second.p.y;
|
|
180
|
+
ctx.fillStyle='rgba(205,245,255,'+(.9*inverse)+')';ctx.beginPath();ctx.arc(x,y,2.5+2*inverse,0,Math.PI*2);ctx.fill();
|
|
181
|
+
}
|
|
182
|
+
for (const {star,p} of points) {
|
|
183
|
+
const act=firing.get(star.id)||0,ret=Math.max(.05,Math.min(1.2,star.retention));
|
|
184
|
+
const intensity=Math.min(1,.22+ret*.72)*(1+act*1.1);
|
|
185
|
+
const radius=Math.max(2,star.radius*p.scale*.55)*(1+Math.sin(now*.0016+(star.pos.x+star.pos.z)*.3)*.06)*(1+act*.5);
|
|
186
|
+
const rgb=star._rgb.join(',');
|
|
187
|
+
ctx.strokeStyle='rgba('+rgb+','+Math.min(1,.3*intensity)+')';
|
|
188
|
+
if (radius>2.5) for (const branch of star._dendrites) {
|
|
189
|
+
const length=radius*2.2*branch.length,dx=Math.cos(branch.angle),dy=Math.sin(branch.angle);
|
|
190
|
+
const ex=p.x+dx*length,ey=p.y+dy*length;
|
|
191
|
+
const cx=p.x+dx*length*.55-dy*length*branch.bend*.5;
|
|
192
|
+
const cy=p.y+dy*length*.55+dx*length*branch.bend*.5;
|
|
193
|
+
ctx.lineWidth=branch.width;ctx.beginPath();ctx.moveTo(p.x,p.y);ctx.quadraticCurveTo(cx,cy,ex,ey);ctx.stroke();
|
|
194
|
+
if (branch.fork!==null) {
|
|
195
|
+
const angle=branch.angle+branch.fork;
|
|
196
|
+
ctx.lineWidth=branch.width*.65;ctx.beginPath();ctx.moveTo(cx,cy);ctx.quadraticCurveTo(cx+Math.cos(angle)*length*.2,cy+Math.sin(angle)*length*.2,cx+Math.cos(angle)*length*.34,cy+Math.sin(angle)*length*.34);ctx.stroke();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const glowRadius=radius*3.6;
|
|
200
|
+
ctx.globalAlpha=Math.min(1,.4+intensity*.7);ctx.drawImage(star._glow,p.x-glowRadius,p.y-glowRadius,glowRadius*2,glowRadius*2);ctx.globalAlpha=1;
|
|
201
|
+
ctx.fillStyle='rgba('+rgb+','+Math.min(1,.42*intensity)+')';ctx.beginPath();ctx.arc(p.x,p.y,radius,0,Math.PI*2);ctx.fill();
|
|
202
|
+
ctx.strokeStyle='rgba('+rgb+','+Math.min(1,.75*intensity)+')';ctx.lineWidth=1;ctx.stroke();
|
|
203
|
+
ctx.fillStyle='rgba(255,255,255,'+Math.min(1,.6*intensity)+')';ctx.beginPath();ctx.arc(p.x,p.y,radius*.42,0,Math.PI*2);ctx.fill();
|
|
204
|
+
if (star.retention<.25) {
|
|
205
|
+
ctx.strokeStyle='rgba(255,110,110,'+Math.max(0,.45-star.retention)+')';ctx.setLineDash([3,4]);
|
|
206
|
+
ctx.beginPath();ctx.arc(p.x,p.y,radius*3.2,0,Math.PI*2);ctx.stroke();ctx.setLineDash([]);
|
|
207
|
+
}
|
|
208
|
+
star._screen={x:p.x,y:p.y,r:Math.max(8,radius*2.6)};
|
|
209
|
+
}
|
|
210
|
+
for (const [id,value] of firing) {
|
|
211
|
+
const next=value*Math.pow(.965,delta/16.7);
|
|
212
|
+
if (next<.02) firing.delete(id); else firing.set(id,next);
|
|
213
|
+
}
|
|
214
|
+
ctx.globalCompositeOperation='source-over';
|
|
215
|
+
const vignette=ctx.createRadialGradient(width/2,height/2,Math.min(width,height)*.34,width/2,height/2,Math.max(width,height)*.8);
|
|
216
|
+
vignette.addColorStop(0,'rgba(0,0,0,0)');vignette.addColorStop(1,'rgba(0,0,0,.78)');
|
|
217
|
+
ctx.fillStyle=vignette;ctx.fillRect(0,0,width,height);
|
|
218
|
+
}
|
|
219
|
+
function start() {
|
|
220
|
+
if (running || destroyed) return;
|
|
221
|
+
running=true; lastTime=0; nextFire=0;
|
|
222
|
+
const currentGeneration = ++generation;
|
|
223
|
+
canvas.addEventListener('pointerdown',onPointerDown);
|
|
224
|
+
canvas.addEventListener('pointermove',onPointerMove);
|
|
225
|
+
canvas.addEventListener('pointerup',onPointerUp);
|
|
226
|
+
canvas.addEventListener('pointercancel',releaseDrag);
|
|
227
|
+
canvas.addEventListener('wheel',onWheel,{passive:false});
|
|
228
|
+
window.addEventListener('resize',resize);
|
|
229
|
+
if (window.ResizeObserver) {
|
|
230
|
+
observer = new window.ResizeObserver(resize);
|
|
231
|
+
observer.observe(canvas);
|
|
232
|
+
}
|
|
233
|
+
resize();
|
|
234
|
+
function tick(now) {
|
|
235
|
+
if (!running || destroyed || currentGeneration !== generation) return;
|
|
236
|
+
rafId=null;
|
|
237
|
+
frame(now);
|
|
238
|
+
if (running && !destroyed && currentGeneration === generation) rafId=window.requestAnimationFrame(tick);
|
|
239
|
+
}
|
|
240
|
+
rafId=window.requestAnimationFrame(tick);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function stop() {
|
|
244
|
+
running=false; generation++;
|
|
245
|
+
if (rafId !== null) window.cancelAnimationFrame(rafId);
|
|
246
|
+
rafId=null;
|
|
247
|
+
observer?.disconnect(); observer=null;
|
|
248
|
+
canvas.removeEventListener('pointerdown',onPointerDown);
|
|
249
|
+
canvas.removeEventListener('pointermove',onPointerMove);
|
|
250
|
+
canvas.removeEventListener('pointerup',onPointerUp);
|
|
251
|
+
canvas.removeEventListener('pointercancel',releaseDrag);
|
|
252
|
+
canvas.removeEventListener('wheel',onWheel);
|
|
253
|
+
window.removeEventListener('resize',resize);
|
|
254
|
+
releaseDrag();
|
|
255
|
+
pulses.length=0; firing.clear(); lastTime=0; nextFire=0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function destroy() {
|
|
259
|
+
if (destroyed) return;
|
|
260
|
+
stop(); destroyed=true;
|
|
261
|
+
selections.clear(); byId.clear(); adjacency.clear(); sprites.clear();
|
|
262
|
+
nodes=[]; links=[]; hubs=[];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
setData(data);
|
|
266
|
+
return {
|
|
267
|
+
start, stop, resize, setData, destroy,
|
|
268
|
+
fireNeuron(id) { if (!destroyed && byId.has(id)) fire(id,performance.now()); },
|
|
269
|
+
onSelect(callback) {
|
|
270
|
+
if (!destroyed) selections.add(callback);
|
|
271
|
+
return () => selections.delete(callback);
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
var defaultSettings = { hideButton: false, showGuideOnEmpty: true };
|
|
279
|
+
var actionStyle = { padding: "7px 12px", borderRadius: "8px", border: "1px solid #6999b8", background: "#102236", color: "#e2f4ff", font: "inherit", cursor: "pointer" };
|
|
280
|
+
|
|
281
|
+
function readApi(url, signal) {
|
|
282
|
+
return fetch(url, { cache: "no-store", signal: signal }).then(function (response) {
|
|
283
|
+
if (!response.ok) throw new Error("HTTP " + response.status);
|
|
284
|
+
return response.json();
|
|
285
|
+
}).then(function (result) {
|
|
286
|
+
if (!result || result.ok !== true || !result.data || typeof result.data !== "object") throw new Error("接口返回格式无效");
|
|
287
|
+
return result.data;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function loadSettings() {
|
|
292
|
+
var controller = new AbortController();
|
|
293
|
+
var timer = setTimeout(function () { controller.abort(); }, 5000);
|
|
294
|
+
return Promise.resolve().then(function () {
|
|
295
|
+
return readApi("/memory-engine/api/config", controller.signal);
|
|
296
|
+
}).then(function (settings) {
|
|
297
|
+
if (typeof settings.hideButton !== "boolean" || typeof settings.showGuideOnEmpty !== "boolean") throw new Error("按钮配置格式无效");
|
|
298
|
+
return settings;
|
|
299
|
+
}).finally(function () { clearTimeout(timer); });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function copyText(text) {
|
|
303
|
+
function fallback() {
|
|
304
|
+
var area = document.createElement("textarea");
|
|
305
|
+
var active = document.activeElement;
|
|
306
|
+
area.value = text;
|
|
307
|
+
area.style.cssText = "position:fixed;left:-9999px;top:0";
|
|
308
|
+
document.body.appendChild(area);
|
|
309
|
+
area.select();
|
|
310
|
+
try {
|
|
311
|
+
if (!document.execCommand("copy")) throw new Error("复制失败");
|
|
312
|
+
} finally {
|
|
313
|
+
area.remove();
|
|
314
|
+
if (active && typeof active.focus === "function") active.focus();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return Promise.resolve().then(function () {
|
|
318
|
+
if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text);
|
|
319
|
+
return fallback();
|
|
320
|
+
}).catch(function () { return fallback(); });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function GuideCard(props) {
|
|
324
|
+
var copiedState = React.useState("复制这段提示词");
|
|
325
|
+
var copyLabel = copiedState[0];
|
|
326
|
+
var setCopyLabel = copiedState[1];
|
|
327
|
+
var card = React.useRef(null);
|
|
328
|
+
React.useEffect(function () { if (card.current) card.current.focus(); }, []);
|
|
329
|
+
function trapTab(event) {
|
|
330
|
+
if (event.key !== "Tab" || !card.current) return;
|
|
331
|
+
var buttons = Array.from(card.current.querySelectorAll("button"));
|
|
332
|
+
var position = buttons.indexOf(document.activeElement);
|
|
333
|
+
if (event.shiftKey && position <= 0) { event.preventDefault(); buttons[buttons.length - 1].focus(); }
|
|
334
|
+
else if (!event.shiftKey && (position === -1 || position === buttons.length - 1)) { event.preventDefault(); buttons[0].focus(); }
|
|
335
|
+
}
|
|
336
|
+
var data = props.data;
|
|
337
|
+
return React.createElement("div", {
|
|
338
|
+
"data-memory-engine-guide-layer": "true",
|
|
339
|
+
style: { position: "absolute", inset: 0, zIndex: 2, display: "grid", placeItems: "center", padding: "16px", background: "rgba(3,6,12,.7)" },
|
|
340
|
+
}, React.createElement("section", {
|
|
341
|
+
ref: card, role: "dialog", "aria-modal": "true", "aria-label": "使用指南", tabIndex: -1,
|
|
342
|
+
"data-memory-engine-guide": "true", onKeyDown: trapTab,
|
|
343
|
+
style: { boxSizing: "border-box", width: "min(620px,100%)", maxHeight: "calc(100vh - 32px)", overflow: "auto", padding: "24px", border: "1px solid #3976a0", borderRadius: "16px", background: "#07111f", color: "#dbeaf8", font: "14px/1.65 system-ui,sans-serif", boxShadow: "0 16px 80px #0008" },
|
|
344
|
+
}, React.createElement("h2", { style: { margin: "0 0 16px", fontSize: "22px", color: "#b7e9ff" } }, "怎么用"),
|
|
345
|
+
props.error ? React.createElement("div", { role: "alert" },
|
|
346
|
+
React.createElement("p", null, "引导加载失败,请重试。"),
|
|
347
|
+
React.createElement("button", { type: "button", onClick: props.onRetry, style: actionStyle }, "重试"))
|
|
348
|
+
: !data ? React.createElement("p", { role: "status" }, "正在加载使用指南……")
|
|
349
|
+
: React.createElement(React.Fragment, null,
|
|
350
|
+
data.empty ? React.createElement("p", { style: { color: "#9fb7ce" } }, "还没有记忆,从下面任一步开始。") : null,
|
|
351
|
+
data.steps.map(function (step, index) {
|
|
352
|
+
return React.createElement("div", { key: index, style: { borderLeft: "3px solid #28577e", paddingLeft: "14px", marginBottom: "18px" } },
|
|
353
|
+
React.createElement("h3", { style: { fontSize: "15px", color: "#9fd0f5", margin: "0 0 6px" } }, "第 " + (index + 1) + " 步 " + step.title),
|
|
354
|
+
React.createElement("p", { style: { margin: "0 0 8px" } }, step.body),
|
|
355
|
+
index === 0 ? React.createElement(React.Fragment, null,
|
|
356
|
+
React.createElement("pre", { style: { whiteSpace: "pre-wrap", overflowWrap: "anywhere", maxHeight: "180px", overflow: "auto", margin: "8px 0", padding: "12px", background: "#030a13", borderRadius: "8px", font: "12px/1.6 ui-monospace,Consolas,monospace" } }, data.prompt),
|
|
357
|
+
React.createElement("button", { type: "button", "data-memory-engine-copy": "true", style: actionStyle,
|
|
358
|
+
onClick: function () { copyText(data.prompt).then(function () { setCopyLabel("已复制 ✓"); }, function () { setCopyLabel("复制失败,请手动复制"); }); },
|
|
359
|
+
}, copyLabel)) : null,
|
|
360
|
+
index === 2 ? React.createElement("button", { type: "button", onClick: props.onRefresh, style: actionStyle }, "刷新图谱") : null);
|
|
361
|
+
}),
|
|
362
|
+
React.createElement("p", { style: { fontSize: "12px", color: "#9fb7ce", overflowWrap: "anywhere" } }, "数据目录:", data.dataDir)),
|
|
363
|
+
React.createElement("div", { style: { display: "flex", justifyContent: "flex-end", marginTop: "16px" } },
|
|
364
|
+
React.createElement("button", { type: "button", onClick: props.onClose, style: actionStyle }, "知道了"))));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function MemoryDetails(props) {
|
|
368
|
+
var node = props.node;
|
|
369
|
+
var text = typeof node.text === "string" ? node.text : "";
|
|
370
|
+
var title = String(node.title || "").trim() || Array.from(text).slice(0, 20).join("");
|
|
371
|
+
var date = new Date(node.ts);
|
|
372
|
+
var day = Number.isFinite(date.getTime()) ? date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, "0") + "-" + String(date.getDate()).padStart(2, "0") : "未知";
|
|
373
|
+
var heat = { 1: "热", 2: "温", 3: "冷" }[node.heat];
|
|
374
|
+
var rows = [
|
|
375
|
+
["重要度", heat ? heat + "(" + node.heat + ")" : "未知"],
|
|
376
|
+
["留存", Number.isFinite(node.retention) ? node.retention.toFixed(2) : "—"],
|
|
377
|
+
["连接数", node.degree == null ? 0 : node.degree],
|
|
378
|
+
["时间", day],
|
|
379
|
+
];
|
|
380
|
+
var emotion = Array.isArray(node.emotion) ? node.emotion.filter(function (item) { return item && item.tag; }).map(function (item) { return item.tag; }).join(" / ") : "";
|
|
381
|
+
if (emotion) rows.push(["情绪", emotion]);
|
|
382
|
+
var copiedState = React.useState("复制正文");
|
|
383
|
+
var copyLabel = copiedState[0];
|
|
384
|
+
var setCopyLabel = copiedState[1];
|
|
385
|
+
var timer = React.useRef(null);
|
|
386
|
+
var copyVersion = React.useRef(0);
|
|
387
|
+
var body = React.useRef(null);
|
|
388
|
+
React.useEffect(function () {
|
|
389
|
+
copyVersion.current++;
|
|
390
|
+
clearTimeout(timer.current);
|
|
391
|
+
timer.current = null;
|
|
392
|
+
setCopyLabel("复制正文");
|
|
393
|
+
if (body.current) body.current.scrollTop = 0;
|
|
394
|
+
return function () { copyVersion.current++; clearTimeout(timer.current); timer.current = null; };
|
|
395
|
+
}, [node.id, text]);
|
|
396
|
+
function copyBody() {
|
|
397
|
+
var version = ++copyVersion.current;
|
|
398
|
+
clearTimeout(timer.current);
|
|
399
|
+
copyText(text).then(function () {
|
|
400
|
+
if (version !== copyVersion.current) return;
|
|
401
|
+
setCopyLabel("已复制 ✓");
|
|
402
|
+
timer.current = setTimeout(function () {
|
|
403
|
+
if (version === copyVersion.current) setCopyLabel("复制正文");
|
|
404
|
+
timer.current = null;
|
|
405
|
+
}, 1800);
|
|
406
|
+
}, function () {
|
|
407
|
+
if (version === copyVersion.current) setCopyLabel("复制失败,请重试");
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return React.createElement("section", {
|
|
411
|
+
role: "region", "aria-label": "记忆详情", "data-memory-engine-detail": "true", "data-memory-engine-node": node.id,
|
|
412
|
+
style: { position: "absolute", top: "72px", right: "18px", zIndex: 1, width: "min(350px,calc(100% - 36px))", maxHeight: "calc(100% - 96px)", boxSizing: "border-box", display: "flex", flexDirection: "column", overflow: "hidden", padding: "18px", border: "1px solid #3976a0", borderRadius: "14px", background: "#07111fee", color: "#dbeaf8", font: "14px/1.6 system-ui,sans-serif", boxShadow: "0 0 40px #1763a033", backdropFilter: "blur(8px)", animation: "dsh-memory-detail-in 180ms ease-out" },
|
|
413
|
+
}, React.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "12px", flexShrink: 0 } },
|
|
414
|
+
React.createElement("span", { "data-memory-engine-detail-kind": "true", style: { color: "#a4e6ff", background: "#15344b", borderRadius: "6px", padding: "2px 8px", overflowWrap: "anywhere" } }, node.kind || "未分类"),
|
|
415
|
+
React.createElement("button", { type: "button", "aria-label": "关闭记忆详情", onClick: props.onClose, style: { border: 0, padding: "2px 6px", background: "transparent", color: "#a4bad0", fontSize: "22px", cursor: "pointer" } }, "×")),
|
|
416
|
+
React.createElement("h2", { "data-memory-engine-detail-title": "true", style: { margin: "10px 0", fontSize: "18px", color: "#b7e9ff", overflowWrap: "anywhere", whiteSpace: "normal", maxHeight: "18vh", overflowY: "auto", flexShrink: 0 } }, title),
|
|
417
|
+
React.createElement("dl", { style: { display: "grid", gridTemplateColumns: "64px minmax(0,1fr)", gap: "5px 10px", margin: "0 0 12px", fontSize: "12px", color: "#9fb7ce", maxHeight: "25vh", overflowY: "auto", flexShrink: 0 } },
|
|
418
|
+
rows.map(function (row) { return React.createElement(React.Fragment, { key: row[0] },
|
|
419
|
+
React.createElement("dt", { style: { margin: 0 } }, row[0]),
|
|
420
|
+
React.createElement("dd", { style: { margin: 0, overflowWrap: "anywhere" } }, row[1])); })),
|
|
421
|
+
React.createElement("div", { ref: body, "data-memory-engine-detail-text": "true", style: { minHeight: 0, flex: "1 1 auto", overflowY: "auto", whiteSpace: "pre-wrap", overflowWrap: "anywhere", borderTop: "1px solid #3976a055", borderBottom: "1px solid #3976a055", padding: "12px 0" } }, text),
|
|
422
|
+
React.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px", marginTop: "12px", flexShrink: 0 } },
|
|
423
|
+
React.createElement("button", { type: "button", "data-memory-engine-highlight": "true", style: actionStyle, onClick: props.onHighlight }, "在图里高亮它"),
|
|
424
|
+
React.createElement("button", { type: "button", "data-memory-engine-copy-body": "true", style: actionStyle, onClick: copyBody }, copyLabel)));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function MemoryCanvas(props) {
|
|
428
|
+
var canvas = React.useRef(null);
|
|
429
|
+
var dataState = React.useState(null);
|
|
430
|
+
var data = dataState[0];
|
|
431
|
+
var setData = dataState[1];
|
|
432
|
+
var errorState = React.useState("");
|
|
433
|
+
var error = errorState[0];
|
|
434
|
+
var setError = errorState[1];
|
|
435
|
+
var retryState = React.useState(0);
|
|
436
|
+
var retry = retryState[0];
|
|
437
|
+
var setRetry = retryState[1];
|
|
438
|
+
React.useEffect(function () {
|
|
439
|
+
var controller = new AbortController();
|
|
440
|
+
var active = true;
|
|
441
|
+
var renderer;
|
|
442
|
+
setError("");
|
|
443
|
+
readApi("/memory-engine/api/map", controller.signal).then(function (nextData) {
|
|
444
|
+
if (!active) return;
|
|
445
|
+
renderer = createRenderer(canvas.current, nextData);
|
|
446
|
+
props.rendererRef.current = renderer;
|
|
447
|
+
renderer.onSelect(function (node) { props.selectionVersion.current++; props.onSelect(node); });
|
|
448
|
+
renderer.start();
|
|
449
|
+
setData(nextData);
|
|
450
|
+
}).catch(function (failure) {
|
|
451
|
+
if (!active) return;
|
|
452
|
+
if (renderer) renderer.destroy();
|
|
453
|
+
if (props.rendererRef.current === renderer) props.rendererRef.current = null;
|
|
454
|
+
console.error("[dsh-memory-engine] Canvas 图谱加载失败:", failure);
|
|
455
|
+
setError(failure.message);
|
|
456
|
+
});
|
|
457
|
+
return function () {
|
|
458
|
+
active = false;
|
|
459
|
+
controller.abort();
|
|
460
|
+
if (renderer) renderer.destroy();
|
|
461
|
+
if (props.rendererRef.current === renderer) props.rendererRef.current = null;
|
|
462
|
+
};
|
|
463
|
+
}, [retry, props.rendererRef, props.selectionVersion, props.onSelect]);
|
|
464
|
+
return React.createElement(React.Fragment, null,
|
|
465
|
+
React.createElement("canvas", {
|
|
466
|
+
ref: canvas, "data-memory-engine-canvas": "true", "aria-label": "记忆神经网络",
|
|
467
|
+
style: { display: "block", width: "100%", height: "100%", touchAction: "none", cursor: "grab" },
|
|
468
|
+
}),
|
|
469
|
+
React.createElement("div", {
|
|
470
|
+
"data-memory-engine-summary": "true",
|
|
471
|
+
style: { position: "absolute", top: "16px", left: "18px", pointerEvents: "none", color: "#d8e9f8", font: "13px/1.65 system-ui,sans-serif", textShadow: "0 0 12px #3388bb88" },
|
|
472
|
+
}, React.createElement("strong", { style: { color: "#a4e6ff", fontSize: "16px" } }, "记忆神经网络"),
|
|
473
|
+
React.createElement("div", null, data ? data.nodes.length + " 个神经元 · " + data.links.length + " 条突触" : "正在加载图谱……"),
|
|
474
|
+
React.createElement("div", { style: { color: "#7491a8", fontSize: "12px" } }, "大小=重要度和连接数 · 亮度=留存")),
|
|
475
|
+
data ? React.createElement("section", {
|
|
476
|
+
"aria-label": "复习清单",
|
|
477
|
+
style: { position: "absolute", bottom: "16px", left: "18px", width: "min(400px,calc(100% - 36px))", boxSizing: "border-box", maxHeight: "32%", overflow: "auto", padding: "15px", background: "#07111fee", border: "1px solid #3976a055", borderRadius: "14px", boxShadow: "0 0 40px #1763a033", backdropFilter: "blur(8px)", font: "12px/1.65 system-ui,sans-serif", color: "#b1c6d9" },
|
|
478
|
+
}, React.createElement("h2", { style: { fontSize: "14px", color: "#ffdb82", margin: "0 0 8px" } }, "复习清单 · 留存最低的记忆"),
|
|
479
|
+
data.review.map(function (item, index) {
|
|
480
|
+
return React.createElement("div", { key: item.id || index, style: { display: "flex", gap: "7px", alignItems: "center", margin: "5px 0" } },
|
|
481
|
+
React.createElement("span", { style: { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, (index + 1) + ". [" + item.kind + "] " + item.title),
|
|
482
|
+
React.createElement("span", { style: { width: "52px", height: "5px", borderRadius: "5px", background: "#335066", overflow: "hidden" } },
|
|
483
|
+
React.createElement("i", { style: { display: "block", height: "100%", background: "#6bc9fa", width: Math.min(100, Math.max(0, item.retention * 100)) + "%" } })));
|
|
484
|
+
})) : null,
|
|
485
|
+
React.createElement("div", { style: { position: "absolute", right: "18px", bottom: "18px", pointerEvents: "none", color: "#7b99ad", font: "13px system-ui,sans-serif" } }, "拖拽旋转 · 滚轮缩放 · 点击选中神经元"),
|
|
486
|
+
error ? React.createElement("div", { role: "alert", style: { position: "absolute", inset: "40% 20% auto", textAlign: "center", color: "#d8e9f8" } },
|
|
487
|
+
React.createElement("p", null, "图谱加载失败:" + error),
|
|
488
|
+
React.createElement("button", { type: "button", style: actionStyle, onClick: function () { setRetry(function (value) { return value + 1; }); } }, "重试")) : null);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function MemoryButton(props) {
|
|
492
|
+
var settings = props.settings;
|
|
18
493
|
var state = React.useState(false);
|
|
19
494
|
var open = state[0];
|
|
20
495
|
var setOpen = state[1];
|
|
21
|
-
var
|
|
496
|
+
var guideState = React.useState(false);
|
|
497
|
+
var guideOpen = guideState[0];
|
|
498
|
+
var setGuideOpen = guideState[1];
|
|
499
|
+
var dataState = React.useState(null);
|
|
500
|
+
var guideData = dataState[0];
|
|
501
|
+
var setGuideData = dataState[1];
|
|
502
|
+
var errorState = React.useState("");
|
|
503
|
+
var guideError = errorState[0];
|
|
504
|
+
var setGuideError = errorState[1];
|
|
505
|
+
var retryState = React.useState(0);
|
|
506
|
+
var retry = retryState[0];
|
|
507
|
+
var setRetry = retryState[1];
|
|
508
|
+
var selectedState = React.useState(null);
|
|
509
|
+
var selected = selectedState[0];
|
|
510
|
+
var setSelected = selectedState[1];
|
|
511
|
+
var renderer = React.useRef(null);
|
|
512
|
+
var selectionVersion = React.useRef(0);
|
|
513
|
+
var pointer = React.useRef(null);
|
|
514
|
+
var help = React.useRef(null);
|
|
515
|
+
var dismissed = React.useRef(false);
|
|
516
|
+
function closeGuide() {
|
|
517
|
+
dismissed.current = true;
|
|
518
|
+
setGuideOpen(false);
|
|
519
|
+
if (help.current) help.current.focus();
|
|
520
|
+
}
|
|
521
|
+
function openMap() {
|
|
522
|
+
dismissed.current = false;
|
|
523
|
+
setGuideOpen(false);
|
|
524
|
+
setSelected(null);
|
|
525
|
+
setOpen(Date.now());
|
|
526
|
+
}
|
|
527
|
+
function closeMap() { setSelected(null); setOpen(false); }
|
|
528
|
+
function startPointer(event) {
|
|
529
|
+
if (event.target.closest("[data-memory-engine-detail], [data-memory-engine-guide-layer], [data-memory-engine-controls]")) { pointer.current = null; return; }
|
|
530
|
+
pointer.current = { x: event.clientX, y: event.clientY, moved: 0, version: selectionVersion.current };
|
|
531
|
+
}
|
|
532
|
+
function movePointer(event) {
|
|
533
|
+
if (!pointer.current) return;
|
|
534
|
+
pointer.current.moved += Math.abs(event.clientX - pointer.current.x) + Math.abs(event.clientY - pointer.current.y);
|
|
535
|
+
pointer.current.x = event.clientX; pointer.current.y = event.clientY;
|
|
536
|
+
}
|
|
537
|
+
function dismissOutside() {
|
|
538
|
+
var gesture = pointer.current;
|
|
539
|
+
pointer.current = null;
|
|
540
|
+
if (gesture && gesture.moved < 6 && gesture.version === selectionVersion.current) setSelected(null);
|
|
541
|
+
}
|
|
22
542
|
React.useEffect(function () {
|
|
23
543
|
if (!open) return undefined;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
544
|
+
var controller = new AbortController();
|
|
545
|
+
var active = true;
|
|
546
|
+
setGuideData(null);
|
|
547
|
+
setGuideError("");
|
|
548
|
+
readApi("/memory-engine/api/onboard", controller.signal).then(function (data) {
|
|
549
|
+
if (typeof data.empty !== "boolean" || typeof data.prompt !== "string" || typeof data.dataDir !== "string"
|
|
550
|
+
|| !Array.isArray(data.steps) || data.steps.length !== 3
|
|
551
|
+
|| data.steps.some(function (step) { return !step || typeof step.title !== "string" || typeof step.body !== "string"; })) throw new Error("引导数据格式无效");
|
|
552
|
+
if (!active) return;
|
|
553
|
+
setGuideData(data);
|
|
554
|
+
if (data.empty && settings.showGuideOnEmpty && !dismissed.current) setGuideOpen(true);
|
|
555
|
+
}).catch(function (error) {
|
|
556
|
+
if (active) setGuideError(error.message);
|
|
557
|
+
});
|
|
558
|
+
return function () { active = false; controller.abort(); };
|
|
559
|
+
}, [open, retry, settings.showGuideOnEmpty]);
|
|
560
|
+
React.useEffect(function () {
|
|
561
|
+
if (!open) return undefined;
|
|
562
|
+
function onEscape() { if (guideOpen) closeGuide(); else if (selected) setSelected(null); else closeMap(); }
|
|
563
|
+
function onKey(event) {
|
|
564
|
+
if (event.key !== "Escape") return;
|
|
565
|
+
event.preventDefault();
|
|
566
|
+
event.stopPropagation();
|
|
567
|
+
if (!event.repeat) onEscape();
|
|
27
568
|
}
|
|
28
|
-
window.addEventListener("keydown", onKey);
|
|
29
|
-
window.
|
|
30
|
-
|
|
31
|
-
}, [open]);
|
|
569
|
+
window.addEventListener("keydown", onKey, true);
|
|
570
|
+
return function () { window.removeEventListener("keydown", onKey, true); };
|
|
571
|
+
}, [open, guideOpen, selected]);
|
|
32
572
|
var button = React.createElement("button", {
|
|
33
573
|
type: "button",
|
|
34
|
-
onClick:
|
|
574
|
+
onClick: openMap,
|
|
35
575
|
"data-memory-engine-map": "true",
|
|
36
576
|
"aria-label": "打开记忆图谱",
|
|
37
577
|
title: "记忆图谱",
|
|
@@ -42,24 +582,33 @@ window.__ModuleLoader__.load({
|
|
|
42
582
|
role: "dialog",
|
|
43
583
|
"aria-modal": "true",
|
|
44
584
|
"aria-label": "记忆图谱",
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
style: {
|
|
53
|
-
}
|
|
585
|
+
onPointerDownCapture: startPointer, onPointerMoveCapture: movePointer,
|
|
586
|
+
onPointerCancelCapture: function () { pointer.current = null; }, onClick: dismissOutside,
|
|
587
|
+
style: { position: "fixed", inset: 0, maxWidth: "none", maxHeight: "none", zIndex: 9999, background: "#03060c" },
|
|
588
|
+
}, React.createElement(MemoryCanvas, { key: open, rendererRef: renderer, selectionVersion: selectionVersion, onSelect: setSelected }),
|
|
589
|
+
React.createElement("style", null, "@keyframes dsh-memory-detail-in{from{opacity:0;transform:translateX(24px)}to{opacity:1;transform:translateX(0)}}@media(prefers-reduced-motion:reduce){[data-memory-engine-detail]{animation:none!important}}"),
|
|
590
|
+
React.createElement("div", {
|
|
591
|
+
"data-memory-engine-controls": "true",
|
|
592
|
+
style: { position: "absolute", top: "16px", right: "18px", zIndex: 1, display: "flex", gap: "8px" },
|
|
593
|
+
}, React.createElement("button", {
|
|
594
|
+
ref: help, type: "button", onClick: function () { setGuideOpen(true); },
|
|
595
|
+
"aria-label": "查看使用指南", title: "使用指南", style: actionStyle,
|
|
596
|
+
}, "?"), React.createElement("button", {
|
|
54
597
|
type: "button",
|
|
55
|
-
onClick:
|
|
598
|
+
onClick: closeMap,
|
|
56
599
|
"aria-label": "关闭记忆图谱",
|
|
57
|
-
style:
|
|
58
|
-
}, "关闭 ×"))
|
|
600
|
+
style: actionStyle,
|
|
601
|
+
}, "关闭 ×")), selected ? React.createElement(MemoryDetails, {
|
|
602
|
+
node: selected, onClose: function () { setSelected(null); },
|
|
603
|
+
onHighlight: function () { if (renderer.current) renderer.current.fireNeuron(selected.id); },
|
|
604
|
+
}) : null, guideOpen ? React.createElement(GuideCard, {
|
|
605
|
+
data: guideData, error: guideError, onClose: closeGuide, onRefresh: openMap,
|
|
606
|
+
onRetry: function () { setRetry(function (value) { return value + 1; }); },
|
|
607
|
+
}) : null);
|
|
59
608
|
return React.createElement(React.Fragment, null, button, ReactDOM.createPortal(overlay, document.body));
|
|
60
609
|
}
|
|
61
610
|
|
|
62
|
-
function registerOn(slots, name) {
|
|
611
|
+
function registerOn(slots, name, settings) {
|
|
63
612
|
var disposable = null;
|
|
64
613
|
slots.inject(name, function () {
|
|
65
614
|
try {
|
|
@@ -68,7 +617,7 @@ window.__ModuleLoader__.load({
|
|
|
68
617
|
id: "memory-engine-map",
|
|
69
618
|
order: 50,
|
|
70
619
|
label: function () { return "记忆图谱"; },
|
|
71
|
-
}, MemoryButton);
|
|
620
|
+
}, function ConfiguredMemoryButton() { return React.createElement(MemoryButton, { settings: settings }); });
|
|
72
621
|
} catch (error) {
|
|
73
622
|
try { console.error("[dsh-memory-engine] 注册到 " + name + " 失败:", error); } catch (ignored) {}
|
|
74
623
|
}
|
|
@@ -87,7 +636,15 @@ window.__ModuleLoader__.load({
|
|
|
87
636
|
var target = ctx.slots && typeof ctx.slots.has === "function" && !ctx.slots.has(SLOT_FALLBACKS[0])
|
|
88
637
|
? SLOT_FALLBACKS[1]
|
|
89
638
|
: SLOT_NAME;
|
|
90
|
-
|
|
639
|
+
function mount(settings) {
|
|
640
|
+
if (!settings.hideButton) registerOn(ctx.slots, target, settings);
|
|
641
|
+
}
|
|
642
|
+
function fallback(error) {
|
|
643
|
+
try { console.warn("[dsh-memory-engine] 配置读取失败,按默认显示按钮:", error); } catch (ignored) {}
|
|
644
|
+
mount(defaultSettings);
|
|
645
|
+
}
|
|
646
|
+
try { loadSettings().then(mount, fallback); }
|
|
647
|
+
catch (error) { fallback(error); }
|
|
91
648
|
} catch (error) {
|
|
92
649
|
try { console.error("[dsh-memory-engine] 插槽注册失败:", error); } catch (ignored) {}
|
|
93
650
|
}
|