@powerhousedao/knowledge-note 1.0.2 → 1.0.3
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/dist/editors/knowledge-vault/components/GraphView.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/components/GraphView.js +210 -32
- package/dist/editors/knowledge-vault/components/NoteList.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/components/NoteList.js +13 -16
- package/dist/editors/knowledge-vault/hooks/use-drive-init.d.ts.map +1 -1
- package/dist/editors/knowledge-vault/hooks/use-drive-init.js +0 -2
- package/dist/package.json +2 -2
- package/dist/processors/factory.d.ts.map +1 -1
- package/dist/processors/factory.js +0 -3
- package/dist/processors/graph-indexer/index.d.ts.map +1 -1
- package/dist/processors/graph-indexer/index.js +5 -8
- package/dist/style.css +67 -0
- package/dist/subgraphs/index.d.ts +0 -1
- package/dist/subgraphs/index.d.ts.map +1 -1
- package/dist/subgraphs/index.js +0 -1
- package/dist/subgraphs/knowledge-graph/subgraph.d.ts +28 -13
- package/dist/subgraphs/knowledge-graph/subgraph.d.ts.map +1 -1
- package/dist/subgraphs/knowledge-graph/subgraph.js +99 -7
- package/package.json +3 -3
- package/dist/processors/methodology-indexer/factory.d.ts +0 -4
- package/dist/processors/methodology-indexer/factory.d.ts.map +0 -1
- package/dist/processors/methodology-indexer/factory.js +0 -23
- package/dist/processors/methodology-indexer/index.d.ts +0 -11
- package/dist/processors/methodology-indexer/index.d.ts.map +0 -1
- package/dist/processors/methodology-indexer/index.js +0 -116
- package/dist/processors/methodology-indexer/migrations.d.ts +0 -4
- package/dist/processors/methodology-indexer/migrations.d.ts.map +0 -1
- package/dist/processors/methodology-indexer/migrations.js +0 -39
- package/dist/processors/methodology-indexer/query.d.ts +0 -35
- package/dist/processors/methodology-indexer/query.d.ts.map +0 -1
- package/dist/processors/methodology-indexer/query.js +0 -114
- package/dist/processors/methodology-indexer/schema.d.ts +0 -22
- package/dist/processors/methodology-indexer/schema.d.ts.map +0 -1
- package/dist/processors/methodology-indexer/schema.js +0 -1
- package/dist/subgraphs/methodology/index.d.ts +0 -2
- package/dist/subgraphs/methodology/index.d.ts.map +0 -1
- package/dist/subgraphs/methodology/index.js +0 -1
- package/dist/subgraphs/methodology/subgraph.d.ts +0 -47
- package/dist/subgraphs/methodology/subgraph.d.ts.map +0 -1
- package/dist/subgraphs/methodology/subgraph.js +0 -100
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/GraphView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAUzE,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,EAAE,CAAC;IACJ,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,EAAE,CAAC;IACJ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,IAAI,CAAC;AAET,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzD,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;
|
|
1
|
+
{"version":3,"file":"GraphView.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/GraphView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAUzE,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,EAAE,CAAC;IACJ,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,gBAAgB,EAAE,MAAM,CAAC;QACzB,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,EAAE,CAAC;IACJ,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,IAAI,CAAC;AAET,KAAK,OAAO,GAAG;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACzD,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;AA6fF,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE,cAAc,2CAmtBhB"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useState, useEffect, useCallback, useMemo } from "react";
|
|
3
3
|
import cytoscape from "cytoscape";
|
|
4
|
-
// @ts-expect-error - no types available for cytoscape-
|
|
5
|
-
import
|
|
4
|
+
// @ts-expect-error - no types available for cytoscape-fcose
|
|
5
|
+
import fcose from "cytoscape-fcose";
|
|
6
6
|
import { setSelectedNode } from "@powerhousedao/reactor-browser";
|
|
7
|
-
// Register the
|
|
8
|
-
|
|
7
|
+
// Register the fcose layout once
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
9
|
+
cytoscape.use(fcose);
|
|
9
10
|
/* ------------------------------------------------------------------ */
|
|
10
11
|
/* Constants */
|
|
11
12
|
/* ------------------------------------------------------------------ */
|
|
@@ -29,13 +30,79 @@ const TENSION_EDGE_COLOR = "#ef4444";
|
|
|
29
30
|
const DEFAULT_NODE_COLOR = "#6b7280";
|
|
30
31
|
const DEFAULT_EDGE_COLOR = "#64748b";
|
|
31
32
|
/* ------------------------------------------------------------------ */
|
|
33
|
+
/* Position persistence */
|
|
34
|
+
/* ------------------------------------------------------------------ */
|
|
35
|
+
const POSITIONS_STORAGE_KEY = "bai-graph-positions";
|
|
36
|
+
function loadPositions() {
|
|
37
|
+
try {
|
|
38
|
+
const raw = localStorage.getItem(POSITIONS_STORAGE_KEY);
|
|
39
|
+
return raw ? JSON.parse(raw) : null;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function savePositions(cy) {
|
|
46
|
+
try {
|
|
47
|
+
const positions = {};
|
|
48
|
+
cy.nodes().forEach((node) => {
|
|
49
|
+
const pos = node.position();
|
|
50
|
+
positions[node.id()] = { x: pos.x, y: pos.y };
|
|
51
|
+
});
|
|
52
|
+
localStorage.setItem(POSITIONS_STORAGE_KEY, JSON.stringify(positions));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// localStorage unavailable or full — silently fail
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function clearStoredPositions() {
|
|
59
|
+
try {
|
|
60
|
+
localStorage.removeItem(POSITIONS_STORAGE_KEY);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// ignore
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/* ------------------------------------------------------------------ */
|
|
32
67
|
/* Build cytoscape elements from data */
|
|
33
68
|
/* ------------------------------------------------------------------ */
|
|
34
69
|
function buildElements(notes, graphState, mocs, tensions) {
|
|
35
70
|
const elements = [];
|
|
36
71
|
const noteMap = new Map(notes.map((n) => [n.id, n]));
|
|
72
|
+
// Build MOC membership: note → primary MOC id
|
|
73
|
+
const noteToMoc = new Map();
|
|
74
|
+
if (mocs?.length) {
|
|
75
|
+
const refCounts = new Map();
|
|
76
|
+
for (const moc of mocs) {
|
|
77
|
+
for (const idea of moc.coreIdeas) {
|
|
78
|
+
if (!refCounts.has(idea.noteRef))
|
|
79
|
+
refCounts.set(idea.noteRef, new Map());
|
|
80
|
+
const counts = refCounts.get(idea.noteRef);
|
|
81
|
+
counts.set(moc.id, (counts.get(moc.id) ?? 0) + 1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const [noteId, counts] of refCounts) {
|
|
85
|
+
let bestMoc = "";
|
|
86
|
+
let bestCount = 0;
|
|
87
|
+
for (const [mocId, count] of counts) {
|
|
88
|
+
if (count > bestCount) {
|
|
89
|
+
bestMoc = mocId;
|
|
90
|
+
bestCount = count;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (bestMoc)
|
|
94
|
+
noteToMoc.set(noteId, bestMoc);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Helper: check if an edge crosses MOC cluster boundaries
|
|
98
|
+
const isCrossCluster = (sourceId, targetId) => {
|
|
99
|
+
const sMoc = noteToMoc.get(sourceId);
|
|
100
|
+
const tMoc = noteToMoc.get(targetId);
|
|
101
|
+
if (!sMoc && !tMoc)
|
|
102
|
+
return false; // both orphans — not cross-cluster
|
|
103
|
+
return sMoc !== tMoc;
|
|
104
|
+
};
|
|
37
105
|
if (graphState?.nodes.length) {
|
|
38
|
-
// Use persisted graph state
|
|
39
106
|
const linkCounts = new Map();
|
|
40
107
|
for (const edge of graphState.edges) {
|
|
41
108
|
linkCounts.set(edge.sourceDocumentId, (linkCounts.get(edge.sourceDocumentId) ?? 0) + 1);
|
|
@@ -63,13 +130,13 @@ function buildElements(notes, graphState, mocs, tensions) {
|
|
|
63
130
|
source: edge.sourceDocumentId,
|
|
64
131
|
target: edge.targetDocumentId,
|
|
65
132
|
linkType: edge.linkType ?? null,
|
|
133
|
+
crossCluster: isCrossCluster(edge.sourceDocumentId, edge.targetDocumentId),
|
|
66
134
|
color: LINK_TYPE_COLORS[edge.linkType ?? ""] ?? DEFAULT_EDGE_COLOR,
|
|
67
135
|
},
|
|
68
136
|
});
|
|
69
137
|
}
|
|
70
138
|
}
|
|
71
139
|
else {
|
|
72
|
-
// Compute from notes
|
|
73
140
|
const nodeIds = new Set(notes.map((n) => n.id));
|
|
74
141
|
const edgeList = [];
|
|
75
142
|
for (const note of notes) {
|
|
@@ -109,6 +176,7 @@ function buildElements(notes, graphState, mocs, tensions) {
|
|
|
109
176
|
source: edge.source,
|
|
110
177
|
target: edge.target,
|
|
111
178
|
linkType: edge.linkType ?? null,
|
|
179
|
+
crossCluster: isCrossCluster(edge.source, edge.target),
|
|
112
180
|
color: LINK_TYPE_COLORS[edge.linkType ?? ""] ?? DEFAULT_EDGE_COLOR,
|
|
113
181
|
},
|
|
114
182
|
});
|
|
@@ -116,7 +184,7 @@ function buildElements(notes, graphState, mocs, tensions) {
|
|
|
116
184
|
}
|
|
117
185
|
// Track existing node IDs for MOC + tension edge targets
|
|
118
186
|
const existingNodeIds = new Set(elements.filter((e) => !e.data.source).map((e) => e.data.id));
|
|
119
|
-
// Add MOC nodes
|
|
187
|
+
// Add MOC nodes as compound parents + edges for non-parented refs
|
|
120
188
|
if (mocs?.length) {
|
|
121
189
|
for (const moc of mocs) {
|
|
122
190
|
elements.push({
|
|
@@ -220,18 +288,19 @@ const cyStylesheet = [
|
|
|
220
288
|
"transition-duration": 150,
|
|
221
289
|
},
|
|
222
290
|
},
|
|
223
|
-
// MOC nodes — diamond
|
|
291
|
+
// MOC nodes — diamond, prominent cluster anchors
|
|
224
292
|
{
|
|
225
293
|
selector: "node[?isMoc]",
|
|
226
294
|
style: {
|
|
227
295
|
shape: "diamond",
|
|
228
|
-
width:
|
|
229
|
-
height:
|
|
230
|
-
"font-size": "
|
|
296
|
+
width: 45,
|
|
297
|
+
height: 45,
|
|
298
|
+
"font-size": "12px",
|
|
231
299
|
"font-weight": "bold",
|
|
232
|
-
"border-width": 2,
|
|
300
|
+
"border-width": 2.5,
|
|
233
301
|
"border-color": "#cba6f7",
|
|
234
|
-
"border-opacity": 0.
|
|
302
|
+
"border-opacity": 0.7,
|
|
303
|
+
"text-max-width": "140px",
|
|
235
304
|
},
|
|
236
305
|
},
|
|
237
306
|
// Tension nodes — triangle shape, red
|
|
@@ -248,6 +317,16 @@ const cyStylesheet = [
|
|
|
248
317
|
"border-opacity": 0.5,
|
|
249
318
|
},
|
|
250
319
|
},
|
|
320
|
+
// Cross-cluster edges — very faint, don't distract from clusters
|
|
321
|
+
{
|
|
322
|
+
selector: "edge[?crossCluster]",
|
|
323
|
+
style: {
|
|
324
|
+
opacity: 0.15,
|
|
325
|
+
width: 0.8,
|
|
326
|
+
"line-style": "dotted",
|
|
327
|
+
"line-dash-pattern": [2, 4],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
251
330
|
// MOC edges — dashed
|
|
252
331
|
{
|
|
253
332
|
selector: "edge[linkType = 'CORE_IDEA']",
|
|
@@ -310,19 +389,66 @@ const cyStylesheet = [
|
|
|
310
389
|
/* ------------------------------------------------------------------ */
|
|
311
390
|
/* Layout options */
|
|
312
391
|
/* ------------------------------------------------------------------ */
|
|
313
|
-
function getLayoutOptions() {
|
|
392
|
+
function getLayoutOptions(opts) {
|
|
393
|
+
const saved = opts?.savedPositions;
|
|
394
|
+
const hasPositions = saved && Object.keys(saved).length > 0;
|
|
395
|
+
// All nodes have saved positions — instant preset layout
|
|
396
|
+
if (hasPositions && !opts?.newNodeIds?.size) {
|
|
397
|
+
return {
|
|
398
|
+
name: "preset",
|
|
399
|
+
positions: (node) => {
|
|
400
|
+
const id = node.id();
|
|
401
|
+
return saved[id] ?? { x: 0, y: 0 };
|
|
402
|
+
},
|
|
403
|
+
fit: false,
|
|
404
|
+
padding: 60,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
// Some or all nodes need computation.
|
|
408
|
+
// Per-edge idealEdgeLength pulls MOC clusters tight and keeps
|
|
409
|
+
// derivation chains compact while general notes spread out.
|
|
314
410
|
return {
|
|
315
|
-
name: "
|
|
411
|
+
name: "fcose",
|
|
316
412
|
animate: false,
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
413
|
+
quality: "default",
|
|
414
|
+
randomize: !hasPositions,
|
|
415
|
+
nodeRepulsion: (node) => node.data("isMoc") ? 30000 : 8000,
|
|
416
|
+
idealEdgeLength: (edge) => {
|
|
417
|
+
const lt = edge.data("linkType");
|
|
418
|
+
const cross = edge.data("crossCluster");
|
|
419
|
+
if (lt === "CORE_IDEA")
|
|
420
|
+
return 70;
|
|
421
|
+
if (cross)
|
|
422
|
+
return 350;
|
|
423
|
+
if (lt === "BUILDS_ON" || lt === "DERIVED_FROM")
|
|
424
|
+
return 90;
|
|
425
|
+
return 150;
|
|
426
|
+
},
|
|
427
|
+
edgeElasticity: (edge) => {
|
|
428
|
+
const lt = edge.data("linkType");
|
|
429
|
+
const cross = edge.data("crossCluster");
|
|
430
|
+
if (lt === "CORE_IDEA")
|
|
431
|
+
return 0.45;
|
|
432
|
+
if (cross)
|
|
433
|
+
return 0.01;
|
|
434
|
+
if (lt === "BUILDS_ON" || lt === "DERIVED_FROM")
|
|
435
|
+
return 0.3;
|
|
436
|
+
return 0.1;
|
|
437
|
+
},
|
|
320
438
|
nestingFactor: 0.1,
|
|
321
|
-
gravity: 0.
|
|
322
|
-
numIter:
|
|
439
|
+
gravity: 0.08,
|
|
440
|
+
numIter: 500,
|
|
323
441
|
tile: true,
|
|
324
|
-
fit:
|
|
325
|
-
padding:
|
|
442
|
+
fit: false,
|
|
443
|
+
padding: 60,
|
|
444
|
+
// Pin existing nodes, only layout new ones
|
|
445
|
+
...(hasPositions
|
|
446
|
+
? {
|
|
447
|
+
fixedNodeConstraint: Object.entries(saved)
|
|
448
|
+
.filter(([id]) => !opts?.newNodeIds?.has(id))
|
|
449
|
+
.map(([id, pos]) => ({ nodeId: id, position: pos })),
|
|
450
|
+
}
|
|
451
|
+
: {}),
|
|
326
452
|
};
|
|
327
453
|
}
|
|
328
454
|
/* ------------------------------------------------------------------ */
|
|
@@ -335,7 +461,7 @@ export function GraphView({ notes, graphState, mocs, tensions, }) {
|
|
|
335
461
|
const [zoomLevel, setZoomLevel] = useState(1);
|
|
336
462
|
const [hoverInfo, setHoverInfo] = useState(null);
|
|
337
463
|
// Build elements from data
|
|
338
|
-
const elements = useMemo(() => buildElements(notes, graphState ?? null, mocs, tensions), [notes, graphState]);
|
|
464
|
+
const elements = useMemo(() => buildElements(notes, graphState ?? null, mocs, tensions), [notes, graphState, mocs, tensions]);
|
|
339
465
|
// Gather neighbor info for the detail panel
|
|
340
466
|
const getNodeDetail = useCallback((nodeId) => {
|
|
341
467
|
const cy = cyRef.current;
|
|
@@ -394,11 +520,21 @@ export function GraphView({ notes, graphState, mocs, tensions, }) {
|
|
|
394
520
|
useEffect(() => {
|
|
395
521
|
if (!containerRef.current)
|
|
396
522
|
return;
|
|
523
|
+
// Detect which nodes are new vs have saved positions
|
|
524
|
+
const savedPositions = loadPositions();
|
|
525
|
+
const savedNodeIds = savedPositions
|
|
526
|
+
? new Set(Object.keys(savedPositions))
|
|
527
|
+
: new Set();
|
|
528
|
+
const currentNodeIds = new Set(elements.filter((e) => !e.data.source).map((e) => e.data.id));
|
|
529
|
+
const newNodeIds = new Set([...currentNodeIds].filter((id) => !savedNodeIds.has(id)));
|
|
397
530
|
const cy = cytoscape({
|
|
398
531
|
container: containerRef.current,
|
|
399
532
|
elements,
|
|
400
533
|
style: cyStylesheet,
|
|
401
|
-
layout: getLayoutOptions(
|
|
534
|
+
layout: getLayoutOptions({
|
|
535
|
+
savedPositions,
|
|
536
|
+
newNodeIds: newNodeIds.size > 0 ? newNodeIds : undefined,
|
|
537
|
+
}),
|
|
402
538
|
zoom: 1,
|
|
403
539
|
minZoom: 0.1,
|
|
404
540
|
maxZoom: 5,
|
|
@@ -406,14 +542,51 @@ export function GraphView({ notes, graphState, mocs, tensions, }) {
|
|
|
406
542
|
boxSelectionEnabled: false,
|
|
407
543
|
});
|
|
408
544
|
cyRef.current = cy;
|
|
409
|
-
// After layout settles,
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const modelCenter = { x: (bb.x1 + bb.x2) / 2, y: (bb.y1 + bb.y2) / 2 };
|
|
415
|
-
cy.zoom({ level: 1, position: modelCenter });
|
|
545
|
+
// After layout settles, save positions and center view.
|
|
546
|
+
const centerGraph = () => {
|
|
547
|
+
cy.fit(cy.elements(), 60);
|
|
548
|
+
if (cy.zoom() > 0.8)
|
|
549
|
+
cy.zoom(0.8);
|
|
416
550
|
cy.center();
|
|
551
|
+
};
|
|
552
|
+
cy.one("layoutstop", () => {
|
|
553
|
+
savePositions(cy);
|
|
554
|
+
centerGraph();
|
|
555
|
+
});
|
|
556
|
+
// For preset layout, also center immediately since layoutstop
|
|
557
|
+
// fires synchronously before the viewport may have adjusted.
|
|
558
|
+
requestAnimationFrame(() => centerGraph());
|
|
559
|
+
// Drag MOC nodes with their cluster children
|
|
560
|
+
const mocDragState = new Map();
|
|
561
|
+
cy.on("grab", "node[?isMoc]", (evt) => {
|
|
562
|
+
const node = evt.target;
|
|
563
|
+
const pos = node.position();
|
|
564
|
+
mocDragState.set(node.id(), { x: pos.x, y: pos.y });
|
|
565
|
+
});
|
|
566
|
+
cy.on("drag", "node[?isMoc]", (evt) => {
|
|
567
|
+
const moc = evt.target;
|
|
568
|
+
const prev = mocDragState.get(moc.id());
|
|
569
|
+
if (!prev)
|
|
570
|
+
return;
|
|
571
|
+
const curr = moc.position();
|
|
572
|
+
const dx = curr.x - prev.x;
|
|
573
|
+
const dy = curr.y - prev.y;
|
|
574
|
+
mocDragState.set(moc.id(), { x: curr.x, y: curr.y });
|
|
575
|
+
// Move all CORE_IDEA-connected notes along with the MOC
|
|
576
|
+
moc.connectedEdges().forEach((edge) => {
|
|
577
|
+
if (edge.data("linkType") !== "CORE_IDEA")
|
|
578
|
+
return;
|
|
579
|
+
const child = edge.source().id() === moc.id()
|
|
580
|
+
? edge.target()
|
|
581
|
+
: edge.source();
|
|
582
|
+
if (child.grabbed())
|
|
583
|
+
return; // don't fight if user is dragging it
|
|
584
|
+
child.shift({ x: dx, y: dy });
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
// Persist position after manual drag
|
|
588
|
+
cy.on("dragfree", "node", () => {
|
|
589
|
+
savePositions(cy);
|
|
417
590
|
});
|
|
418
591
|
// Track zoom level for UI
|
|
419
592
|
cy.on("zoom", () => {
|
|
@@ -510,7 +683,12 @@ export function GraphView({ notes, graphState, mocs, tensions, }) {
|
|
|
510
683
|
const cy = cyRef.current;
|
|
511
684
|
if (!cy)
|
|
512
685
|
return;
|
|
513
|
-
|
|
686
|
+
clearStoredPositions();
|
|
687
|
+
const layout = cy.layout(getLayoutOptions());
|
|
688
|
+
layout.run();
|
|
689
|
+
cy.one("layoutstop", () => {
|
|
690
|
+
savePositions(cy);
|
|
691
|
+
});
|
|
514
692
|
}, []);
|
|
515
693
|
if (notes.length === 0) {
|
|
516
694
|
return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsx("p", { style: { color: "var(--bai-text-muted)" }, children: "No notes to display in graph" }) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoteList.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/NoteList.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,KAAK,aAAa,GAAG;IACnB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B,CAAC;
|
|
1
|
+
{"version":3,"file":"NoteList.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/components/NoteList.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,KAAK,aAAa,GAAG;IACnB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B,CAAC;AA2BF,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,aAAa,2CAgDhD"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { setSelectedNode } from "@powerhousedao/reactor-browser";
|
|
3
|
+
import { memo, useState } from "react";
|
|
4
|
+
const PAGE_SIZE = 30;
|
|
3
5
|
const STATUS_BADGE_STYLES = {
|
|
4
6
|
DRAFT: {
|
|
5
7
|
background: "rgba(245, 158, 11, 0.2)",
|
|
@@ -23,31 +25,26 @@ const STATUS_BADGE_STYLES = {
|
|
|
23
25
|
},
|
|
24
26
|
};
|
|
25
27
|
export function NoteList({ notes }) {
|
|
28
|
+
const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
|
|
26
29
|
if (notes.length === 0) {
|
|
27
30
|
return (_jsx("div", { className: "flex h-full items-center justify-center", children: _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-lg", style: { color: "var(--bai-text-muted)" }, children: "No knowledge notes yet" }), _jsx("p", { className: "mt-1 text-sm", style: { color: "var(--bai-text-faint)" }, children: "Create your first note to get started" })] }) }));
|
|
28
31
|
}
|
|
29
|
-
|
|
32
|
+
const hasMore = visibleCount < notes.length;
|
|
33
|
+
return (_jsxs("div", { className: "p-4", children: [_jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: notes.slice(0, visibleCount).map((note) => (_jsx(NoteCard, { note: note }, note.id))) }), hasMore && (_jsx("div", { className: "mt-4 flex justify-center", children: _jsxs("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE), className: "rounded-lg border px-4 py-2 text-sm transition-colors", style: {
|
|
34
|
+
borderColor: "var(--bai-border)",
|
|
35
|
+
color: "var(--bai-text-muted)",
|
|
36
|
+
background: "var(--bai-surface)",
|
|
37
|
+
}, children: ["Show more (", notes.length - visibleCount, " remaining)"] }) }))] }));
|
|
30
38
|
}
|
|
31
|
-
function NoteCard({ note }) {
|
|
39
|
+
const NoteCard = memo(function NoteCard({ note }) {
|
|
32
40
|
const title = note.title ?? note.name;
|
|
33
41
|
const status = note.status ?? "DRAFT";
|
|
34
42
|
const badgeStyle = STATUS_BADGE_STYLES[status] ?? STATUS_BADGE_STYLES.DRAFT;
|
|
35
|
-
return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(note.id), className: "
|
|
36
|
-
background: "var(--bai-surface)",
|
|
37
|
-
borderColor: "var(--bai-border)",
|
|
38
|
-
}, children: [_jsxs("div", { className: "mb-2 flex items-start justify-between gap-2", children: [_jsx("h3", { className: "note-card-title flex-1 truncate text-sm font-semibold", style: { color: "var(--bai-text)" }, children: title }), _jsx("span", { className: "shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium", style: badgeStyle, children: status.replace("_", " ") })] }), note.description && (_jsx("p", { className: "mb-3 line-clamp-2 text-xs leading-relaxed", style: { color: "var(--bai-text-muted)" }, children: note.description })), _jsxs("div", { className: "mt-auto flex flex-wrap items-center gap-1.5 pt-2", children: [note.noteType && (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[10px] font-medium", style: {
|
|
43
|
+
return (_jsxs("button", { type: "button", onClick: () => setSelectedNode(note.id), className: "group flex flex-col rounded-xl border border-[var(--bai-border)] bg-[var(--bai-surface)] p-4 text-left transition-all hover:border-[var(--bai-accent)] hover:bg-[var(--bai-hover)]", children: [_jsxs("div", { className: "mb-2 flex items-start justify-between gap-2", children: [_jsx("h3", { className: "flex-1 truncate text-sm font-semibold text-[var(--bai-text)] group-hover:text-[var(--bai-accent)]", children: title }), _jsx("span", { className: "shrink-0 rounded-full border px-2 py-0.5 text-[10px] font-medium", style: badgeStyle, children: status.replace("_", " ") })] }), note.description && (_jsx("p", { className: "mb-3 line-clamp-2 text-xs leading-relaxed", style: { color: "var(--bai-text-muted)" }, children: note.description })), _jsxs("div", { className: "mt-auto flex flex-wrap items-center gap-1.5 pt-2", children: [note.noteType && (_jsx("span", { className: "rounded px-1.5 py-0.5 text-[10px] font-medium", style: {
|
|
39
44
|
background: "var(--bai-hover)",
|
|
40
45
|
color: "var(--bai-text-muted)",
|
|
41
46
|
}, children: note.noteType })), note.topics.slice(0, 3).map((t) => (_jsxs("span", { className: "text-[10px]", style: { color: "var(--bai-accent)", opacity: 0.6 }, children: ["#", t.name] }, t.id))), note.links.length > 0 && (_jsxs("span", { className: "ml-auto text-[10px]", style: { color: "var(--bai-text-faint)" }, children: [note.links.length, " link", note.links.length !== 1 ? "s" : ""] }))] }), note.provenance?.author && (_jsxs("div", { className: "mt-2 border-t pt-2 text-[10px]", style: {
|
|
42
47
|
borderColor: "var(--bai-border)",
|
|
43
48
|
color: "var(--bai-text-faint)",
|
|
44
|
-
}, children: ["by ", note.provenance.author, note.provenance.updatedAt && (_jsxs("span", { children: [" \u00b7 ", new Date(note.provenance.updatedAt).toLocaleDateString()] }))] }))
|
|
45
|
-
|
|
46
|
-
background: var(--bai-hover) !important;
|
|
47
|
-
border-color: var(--bai-accent) !important;
|
|
48
|
-
}
|
|
49
|
-
.note-card:hover .note-card-title {
|
|
50
|
-
color: var(--bai-accent) !important;
|
|
51
|
-
}
|
|
52
|
-
` })] }));
|
|
53
|
-
}
|
|
49
|
+
}, children: ["by ", note.provenance.author, note.provenance.updatedAt && (_jsxs("span", { children: [" \u00b7 ", new Date(note.provenance.updatedAt).toLocaleDateString()] }))] }))] }));
|
|
50
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-drive-init.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-drive-init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-drive-init.d.ts","sourceRoot":"","sources":["../../../../editors/knowledge-vault/hooks/use-drive-init.ts"],"names":[],"mappings":"AA6DA,wBAAgB,YAAY,SAsB3B;AAsGD;;;GAGG;AACH,wBAAgB,YAAY,wBAI3B"}
|
|
@@ -14,7 +14,6 @@ import { useSelectedDrive, useNodesInSelectedDrive, addDocument, addFolder, } fr
|
|
|
14
14
|
* /ops/queue/ <- pipeline queue singleton
|
|
15
15
|
* /self/ <- system identity & config
|
|
16
16
|
* /self/methodology/ <- methodology notes
|
|
17
|
-
* /research/ <- bundled research claims
|
|
18
17
|
*
|
|
19
18
|
* Pattern: follows contributor-billing's proven approach —
|
|
20
19
|
* module-level tracking Set per drive prevents duplicates,
|
|
@@ -35,7 +34,6 @@ const FOLDERS = [
|
|
|
35
34
|
{ name: "queue", parentPath: "ops" },
|
|
36
35
|
{ name: "self" },
|
|
37
36
|
{ name: "methodology", parentPath: "self" },
|
|
38
|
-
{ name: "research" },
|
|
39
37
|
];
|
|
40
38
|
const SINGLETONS = [
|
|
41
39
|
{
|
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/knowledge-note",
|
|
3
3
|
"description": "Knowledge Note document model package for Powerhouse",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"@powerhousedao/document-engineering": "1.40.1",
|
|
114
114
|
"@powerhousedao/vetra": "6.0.0-dev.105",
|
|
115
115
|
"cytoscape": "^3.33.1",
|
|
116
|
-
"cytoscape-
|
|
116
|
+
"cytoscape-fcose": "^2.2.0",
|
|
117
117
|
"document-model": "6.0.0-dev.105",
|
|
118
118
|
"graphql": "16.12.0",
|
|
119
119
|
"graphql-tag": "^2.12.6",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../processors/factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EAErB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../processors/factory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EAErB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,eAAO,MAAM,gBAAgB,GAAI,QAAQ,oBAAoB,MAa7C,aAAa,gBAAgB,KAAG,OAAO,CAAC,eAAe,EAAE,CAUxE,CAAC"}
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
* This file aggregates all processor factories
|
|
3
3
|
*/
|
|
4
4
|
import { graphIndexerProcessorFactory } from "./graph-indexer/factory.js";
|
|
5
|
-
import { methodologyIndexerProcessorFactory } from "./methodology-indexer/factory.js";
|
|
6
5
|
export const processorFactory = (module) => {
|
|
7
6
|
console.log(`[processorFactory] Initializing with processorApp: ${module.processorApp}`);
|
|
8
7
|
const factories = [];
|
|
9
8
|
// Register graph indexer (watches bai/knowledge-note)
|
|
10
9
|
factories.push(graphIndexerProcessorFactory(module));
|
|
11
|
-
// Register methodology indexer (watches bai/research-claim)
|
|
12
|
-
factories.push(methodologyIndexerProcessorFactory(module));
|
|
13
10
|
console.log(`[processorFactory] Loaded ${factories.length} factories`);
|
|
14
11
|
// Return the inner function that will be called for each drive
|
|
15
12
|
return async (driveHeader) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAqGV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../processors/graph-indexer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,qBAAa,qBAAsB,SAAQ,qBAAqB,CAAC,EAAE,CAAC;WAClD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,YAAY,CACzB,UAAU,EAAE,oBAAoB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC;IAqGV,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,UAAU;CAoBzB"}
|
|
@@ -89,14 +89,11 @@ export class GraphIndexerProcessor extends RelationalDbProcessor {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
async onDisconnect() {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
catch (err) {
|
|
98
|
-
console.error(`[GraphIndexer] Error cleaning up:`, err);
|
|
99
|
-
}
|
|
92
|
+
// Intentionally no-op: preserve indexed data across restarts.
|
|
93
|
+
// The reactor does not replay historical operations on reconnect,
|
|
94
|
+
// so wiping tables here would leave the index permanently empty
|
|
95
|
+
// until new operations arrive. Use knowledgeGraphReindex mutation
|
|
96
|
+
// to rebuild if needed.
|
|
100
97
|
}
|
|
101
98
|
async deleteNode(documentId) {
|
|
102
99
|
try {
|