noteconnection 1.1.2 → 1.3.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/README.md +81 -3
- package/dist/src/core/Graph.js +84 -0
- package/dist/src/core/PathBridge.js +49 -0
- package/dist/src/core/PathEngine.js +196 -0
- package/dist/src/core/PathEngine.test.js +86 -0
- package/dist/src/electron/main.js +14 -0
- package/dist/src/frontend/README.md +81 -3
- package/dist/src/frontend/app.js +39 -0
- package/dist/src/frontend/index.html +128 -2
- package/dist/src/frontend/libs/path_core.js +429 -0
- package/dist/src/frontend/locales/en.json +52 -29
- package/dist/src/frontend/locales/zh.json +30 -7
- package/dist/src/frontend/path.html +100 -0
- package/dist/src/frontend/path_app.js +685 -0
- package/dist/src/frontend/path_styles.css +240 -0
- package/dist/src/frontend/path_worker.js +176 -0
- package/dist/src/frontend/styles.css +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
|
|
2
|
+
/* Auto-bundled Path Core */
|
|
3
|
+
(function() {
|
|
4
|
+
|
|
5
|
+
var exports = {};
|
|
6
|
+
var module = { exports: exports };
|
|
7
|
+
// Global scope exposure
|
|
8
|
+
// var Graph, PathEngine; // Removed to avoid syntax error with class declaration
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/* Graph.js */
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Directed Graph implementation for managing notes and dependencies.
|
|
17
|
+
* 用于管理笔记和依赖关系的有向图实现。
|
|
18
|
+
*/
|
|
19
|
+
class Graph {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.nodes = new Map();
|
|
22
|
+
this.adjacencyList = new Map();
|
|
23
|
+
this.reverseAdjacencyList = new Map();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Adds a node to the graph.
|
|
27
|
+
* 向图中添加一个节点。
|
|
28
|
+
* @param node The node to add | 要添加的节点
|
|
29
|
+
*/
|
|
30
|
+
addNode(node) {
|
|
31
|
+
if (!this.nodes.has(node.id)) {
|
|
32
|
+
this.nodes.set(node.id, { ...node, inDegree: 0, outDegree: 0 });
|
|
33
|
+
this.adjacencyList.set(node.id, []);
|
|
34
|
+
this.reverseAdjacencyList.set(node.id, []);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Retrieves a node by its ID.
|
|
39
|
+
* 通过 ID 获取节点。
|
|
40
|
+
* @param id The node ID | 节点 ID
|
|
41
|
+
* @returns The node or undefined if not found | 节点,如果未找到则返回 undefined
|
|
42
|
+
*/
|
|
43
|
+
getNode(id) {
|
|
44
|
+
return this.nodes.get(id);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Checks if a node exists in the graph.
|
|
48
|
+
* 检查图中是否存在该节点。
|
|
49
|
+
* @param id The node ID | 节点 ID
|
|
50
|
+
*/
|
|
51
|
+
hasNode(id) {
|
|
52
|
+
return this.nodes.has(id);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Adds a directed edge between two nodes.
|
|
56
|
+
* 在两个节点之间添加有向边。
|
|
57
|
+
* @param source Source node ID | 源节点 ID
|
|
58
|
+
* @param target Target node ID | 目标节点 ID
|
|
59
|
+
* @param type Relationship type | 关系类型
|
|
60
|
+
* @param weight Edge weight (confidence) | 边权重 (置信度)
|
|
61
|
+
*/
|
|
62
|
+
addEdge(source, target, type = 'dependency', weight = 1) {
|
|
63
|
+
if (!this.nodes.has(source)) {
|
|
64
|
+
this.addNode({ id: source, label: source, inDegree: 0, outDegree: 0 });
|
|
65
|
+
}
|
|
66
|
+
if (!this.nodes.has(target)) {
|
|
67
|
+
this.addNode({ id: target, label: target, inDegree: 0, outDegree: 0 });
|
|
68
|
+
}
|
|
69
|
+
const edge = { source, target, type, weight };
|
|
70
|
+
// Add to adjacency list (outgoing)
|
|
71
|
+
const outgoing = this.adjacencyList.get(source) || [];
|
|
72
|
+
// Prevent duplicate edges
|
|
73
|
+
if (!outgoing.some(e => e.target === target && e.type === type)) {
|
|
74
|
+
outgoing.push(edge);
|
|
75
|
+
this.adjacencyList.set(source, outgoing);
|
|
76
|
+
// Update out-degree
|
|
77
|
+
const sourceNode = this.nodes.get(source);
|
|
78
|
+
sourceNode.outDegree++;
|
|
79
|
+
}
|
|
80
|
+
// Add to reverse adjacency list (incoming)
|
|
81
|
+
const incoming = this.reverseAdjacencyList.get(target) || [];
|
|
82
|
+
if (!incoming.some(e => e.source === source && e.type === type)) {
|
|
83
|
+
incoming.push(edge);
|
|
84
|
+
this.reverseAdjacencyList.set(target, incoming);
|
|
85
|
+
// Update in-degree
|
|
86
|
+
const targetNode = this.nodes.get(target);
|
|
87
|
+
targetNode.inDegree++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Gets all outgoing edges from a node.
|
|
92
|
+
* 获取节点的所有出边。
|
|
93
|
+
* @param id Node ID | 节点 ID
|
|
94
|
+
*/
|
|
95
|
+
getOutgoingEdges(id) {
|
|
96
|
+
return this.adjacencyList.get(id) || [];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Gets all outgoing neighbor IDs for a node.
|
|
100
|
+
* 获取节点的所有出度邻居 ID。
|
|
101
|
+
* @param id Node ID | 节点 ID
|
|
102
|
+
*/
|
|
103
|
+
getNeighbors(id) {
|
|
104
|
+
return (this.adjacencyList.get(id) || []).map(edge => edge.target);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Gets all nodes in the graph.
|
|
108
|
+
* 获取图中的所有节点。
|
|
109
|
+
*/
|
|
110
|
+
getNodes() {
|
|
111
|
+
return Array.from(this.nodes.values());
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Gets all edges in the graph.
|
|
115
|
+
* 获取图中的所有边。
|
|
116
|
+
*/
|
|
117
|
+
getEdges() {
|
|
118
|
+
return Array.from(this.adjacencyList.values()).flat();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Gets all incoming edges to a node.
|
|
122
|
+
* 获取节点的所有入边。
|
|
123
|
+
* @param id Node ID | 节点 ID
|
|
124
|
+
*/
|
|
125
|
+
getIncomingEdges(id) {
|
|
126
|
+
return this.reverseAdjacencyList.get(id) || [];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Returns the graph data in a serializable format.
|
|
130
|
+
* 以可序列化的格式返回图数据。
|
|
131
|
+
*/
|
|
132
|
+
toJSON() {
|
|
133
|
+
return {
|
|
134
|
+
nodes: Array.from(this.nodes.values()),
|
|
135
|
+
edges: Array.from(this.adjacencyList.values()).flat()
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Gets all predecessor nodes (transitive closure of incoming edges).
|
|
140
|
+
* 获取所有前驱节点(入边的传递闭包)。
|
|
141
|
+
* @param id Target node ID
|
|
142
|
+
*/
|
|
143
|
+
getPredecessors(id) {
|
|
144
|
+
const predecessors = new Set();
|
|
145
|
+
const queue = [id];
|
|
146
|
+
const visited = new Set();
|
|
147
|
+
visited.add(id);
|
|
148
|
+
while (queue.length > 0) {
|
|
149
|
+
const current = queue.shift();
|
|
150
|
+
const incoming = this.getIncomingEdges(current);
|
|
151
|
+
for (const edge of incoming) {
|
|
152
|
+
if (!visited.has(edge.source)) {
|
|
153
|
+
visited.add(edge.source);
|
|
154
|
+
predecessors.add(edge.source);
|
|
155
|
+
queue.push(edge.source);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return predecessors;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Gets all successor nodes (transitive closure of outgoing edges).
|
|
163
|
+
* 获取所有后继节点(出边的传递闭包)。
|
|
164
|
+
* @param id Source node ID
|
|
165
|
+
*/
|
|
166
|
+
getSuccessors(id) {
|
|
167
|
+
const successors = new Set();
|
|
168
|
+
const queue = [id];
|
|
169
|
+
const visited = new Set();
|
|
170
|
+
visited.add(id);
|
|
171
|
+
while (queue.length > 0) {
|
|
172
|
+
const current = queue.shift();
|
|
173
|
+
const outgoing = this.getOutgoingEdges(current);
|
|
174
|
+
for (const edge of outgoing) {
|
|
175
|
+
if (!visited.has(edge.target)) {
|
|
176
|
+
visited.add(edge.target);
|
|
177
|
+
successors.add(edge.target);
|
|
178
|
+
queue.push(edge.target);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return successors;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Calculates the shortest path between two nodes (unweighted BFS).
|
|
186
|
+
* 计算两个节点之间的最短路径 (无权 BFS)。
|
|
187
|
+
* @param source Source node ID
|
|
188
|
+
* @param target Target node ID
|
|
189
|
+
* @returns Array of node IDs representing the path, or empty if no path found
|
|
190
|
+
*/
|
|
191
|
+
getShortestPath(source, target) {
|
|
192
|
+
if (source === target)
|
|
193
|
+
return [source];
|
|
194
|
+
const queue = [source];
|
|
195
|
+
const visited = new Set();
|
|
196
|
+
const parent = new Map();
|
|
197
|
+
visited.add(source);
|
|
198
|
+
while (queue.length > 0) {
|
|
199
|
+
const current = queue.shift();
|
|
200
|
+
if (current === target)
|
|
201
|
+
break;
|
|
202
|
+
const neighbors = this.getNeighbors(current);
|
|
203
|
+
for (const neighbor of neighbors) {
|
|
204
|
+
if (!visited.has(neighbor)) {
|
|
205
|
+
visited.add(neighbor);
|
|
206
|
+
parent.set(neighbor, current);
|
|
207
|
+
queue.push(neighbor);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (!visited.has(target))
|
|
212
|
+
return [];
|
|
213
|
+
// Reconstruct path
|
|
214
|
+
const path = [target];
|
|
215
|
+
let curr = target;
|
|
216
|
+
while (curr !== source) {
|
|
217
|
+
curr = parent.get(curr);
|
|
218
|
+
path.unshift(curr);
|
|
219
|
+
}
|
|
220
|
+
return path;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
Graph = Graph;
|
|
224
|
+
|
|
225
|
+
// Explicitly expose
|
|
226
|
+
self.Graph = Graph;
|
|
227
|
+
|
|
228
|
+
/* PathEngine.js */
|
|
229
|
+
"use strict";
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class PathEngine {
|
|
233
|
+
constructor(graph) {
|
|
234
|
+
this.graph = graph;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Domain Learning: Extracts an efficient learning path for a set of nodes (or all nodes).
|
|
238
|
+
* 领域学习:为一组节点(或所有节点)提取高效的学习路径。
|
|
239
|
+
* @param nodeIds Specific nodes to learn (optional, defaults to all)
|
|
240
|
+
* @param strategy prioritization strategy
|
|
241
|
+
*/
|
|
242
|
+
domainLearning(nodeIds, strategy) {
|
|
243
|
+
const targetNodes = nodeIds ? new Set(nodeIds) : new Set(this.graph.getNodes().map(n => n.id));
|
|
244
|
+
// For Domain Learning, we want to learn *everything* in the set.
|
|
245
|
+
// We strictly respect dependencies within the graph.
|
|
246
|
+
// If a node in the set depends on a node OUTSIDE the set, we assume the outside node is already known
|
|
247
|
+
// OR we must add it. Requirement implies "user-defined domain", so usually we strictly stay inside or include prereqs.
|
|
248
|
+
// Let's assume we need to be strictly self-contained or include necessary prerequisites.
|
|
249
|
+
// Safe bet: Extract subgraph of targetNodes + all their ancestors to ensure validity.
|
|
250
|
+
const relevantNodes = this.expandToIncludePrerequisites(targetNodes);
|
|
251
|
+
return this.generateLearningPath(relevantNodes, strategy);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Diffusion Learning: Extracts shortest learning path to a specific target node.
|
|
255
|
+
* 扩散学习:提取通往特定目标节点的最短学习路径。
|
|
256
|
+
* @param targetId Target node ID
|
|
257
|
+
* @param strategy prioritization strategy for tie-breaking
|
|
258
|
+
*/
|
|
259
|
+
diffusionLearning(targetId, strategy) {
|
|
260
|
+
if (!this.graph.hasNode(targetId)) {
|
|
261
|
+
throw new Error(`Node ${targetId} not found in graph`);
|
|
262
|
+
}
|
|
263
|
+
// 1. Identify all ancestors (prerequisites)
|
|
264
|
+
const ancestors = this.graph.getPredecessors(targetId);
|
|
265
|
+
ancestors.add(targetId);
|
|
266
|
+
// 2. Generate path for this subset
|
|
267
|
+
return this.generateLearningPath(ancestors, strategy);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Core generation logic using Priority-Queue Topological Sort.
|
|
271
|
+
*/
|
|
272
|
+
generateLearningPath(nodesOfInterest, strategy) {
|
|
273
|
+
const nodes = Array.from(nodesOfInterest).map(id => this.graph.getNode(id));
|
|
274
|
+
const nodeMap = new Map(nodes.map(n => [n.id, n]));
|
|
275
|
+
// Build local in-degrees for the subgraph
|
|
276
|
+
const localInDegree = new Map();
|
|
277
|
+
const localAdjacency = new Map();
|
|
278
|
+
nodes.forEach(node => {
|
|
279
|
+
localInDegree.set(node.id, 0);
|
|
280
|
+
localAdjacency.set(node.id, []);
|
|
281
|
+
});
|
|
282
|
+
// Populate edges restricted to the subgraph
|
|
283
|
+
const relevantEdges = [];
|
|
284
|
+
nodes.forEach(node => {
|
|
285
|
+
const outgoing = this.graph.getOutgoingEdges(node.id);
|
|
286
|
+
outgoing.forEach(edge => {
|
|
287
|
+
if (nodesOfInterest.has(edge.target)) {
|
|
288
|
+
localAdjacency.get(node.id).push(edge.target);
|
|
289
|
+
localInDegree.set(edge.target, (localInDegree.get(edge.target) || 0) + 1);
|
|
290
|
+
relevantEdges.push(edge);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
// Initialize queue with nodes having 0 in-degree (within subgraph)
|
|
295
|
+
let available = [];
|
|
296
|
+
nodes.forEach(node => {
|
|
297
|
+
if (localInDegree.get(node.id) === 0) {
|
|
298
|
+
available.push(node.id);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
const learnedPath = [];
|
|
302
|
+
const visited = new Set();
|
|
303
|
+
let step = 1;
|
|
304
|
+
// Helper to process a node and unlock neighbors
|
|
305
|
+
const processNode = (currentId) => {
|
|
306
|
+
visited.add(currentId);
|
|
307
|
+
const currentNode = nodeMap.get(currentId);
|
|
308
|
+
// Add to path
|
|
309
|
+
learnedPath.push({
|
|
310
|
+
...currentNode,
|
|
311
|
+
stepOrder: step++,
|
|
312
|
+
isCompleted: false,
|
|
313
|
+
unlocks: localAdjacency.get(currentId)
|
|
314
|
+
});
|
|
315
|
+
// "Unlock" neighbors
|
|
316
|
+
const neighbors = localAdjacency.get(currentId);
|
|
317
|
+
neighbors.forEach(neighborId => {
|
|
318
|
+
// Only decrement if neighbor not visited (avoid double counting in cycles)
|
|
319
|
+
if (!visited.has(neighborId)) {
|
|
320
|
+
const newDegree = (localInDegree.get(neighborId) || 0) - 1;
|
|
321
|
+
localInDegree.set(neighborId, newDegree);
|
|
322
|
+
if (newDegree <= 0) { // Changed to <= 0 to be robust against negative logic errors
|
|
323
|
+
// Check if already in available to prevent duplicates
|
|
324
|
+
if (!available.includes(neighborId)) {
|
|
325
|
+
available.push(neighborId);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
while (learnedPath.length < nodes.length) {
|
|
332
|
+
if (available.length > 0) {
|
|
333
|
+
// Normal Topological Sort Step
|
|
334
|
+
available.sort((a, b) => this.compareNodes(a, b, strategy));
|
|
335
|
+
const currentId = available.shift();
|
|
336
|
+
if (!visited.has(currentId)) {
|
|
337
|
+
processNode(currentId);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Cycle Detected or Disconnected Components with strict dependencies
|
|
342
|
+
// Strategy: Break cycle by picking the "best" remaining node
|
|
343
|
+
// Prioritize nodes with HIGHEST Out-Degree (unlocks the most)
|
|
344
|
+
// or lowest remaining in-degree?
|
|
345
|
+
// "Lowest In-Degree" is usually the best heuristic for Feedback Arc Set.
|
|
346
|
+
// Find remaining nodes
|
|
347
|
+
const remainingIds = [];
|
|
348
|
+
nodes.forEach(n => {
|
|
349
|
+
if (!visited.has(n.id))
|
|
350
|
+
remainingIds.push(n.id);
|
|
351
|
+
});
|
|
352
|
+
if (remainingIds.length === 0)
|
|
353
|
+
break; // Done
|
|
354
|
+
// Sort by In-Degree (Ascending) -> Strategy Score (Desc)
|
|
355
|
+
remainingIds.sort((a, b) => {
|
|
356
|
+
const degA = localInDegree.get(a) || 0;
|
|
357
|
+
const degB = localInDegree.get(b) || 0;
|
|
358
|
+
if (degA !== degB)
|
|
359
|
+
return degA - degB;
|
|
360
|
+
return this.compareNodes(a, b, strategy);
|
|
361
|
+
});
|
|
362
|
+
const forceId = remainingIds[0];
|
|
363
|
+
// Force process strict dependency validation
|
|
364
|
+
localInDegree.set(forceId, 0); // Pretend it's free
|
|
365
|
+
processNode(forceId);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
nodes: learnedPath,
|
|
370
|
+
edges: relevantEdges,
|
|
371
|
+
strategy,
|
|
372
|
+
coverage: learnedPath.length / nodes.length
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Helper to ensure valid learning set (closure of predecessors).
|
|
377
|
+
*/
|
|
378
|
+
expandToIncludePrerequisites(initialNodes) {
|
|
379
|
+
const result = new Set(initialNodes);
|
|
380
|
+
let changed = true;
|
|
381
|
+
// Iteratively add parents until stable
|
|
382
|
+
// Optimally we just merge getPredecessors for all nodes
|
|
383
|
+
// But getPredecessors returns full closure, so we only need to do it once per node.
|
|
384
|
+
for (const nodeId of initialNodes) {
|
|
385
|
+
const preds = this.graph.getPredecessors(nodeId);
|
|
386
|
+
preds.forEach(p => result.add(p));
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Strategy Comparator
|
|
392
|
+
* Returns negative if A is better than B (for sorting A before B).
|
|
393
|
+
*/
|
|
394
|
+
compareNodes(idA, idB, strategy) {
|
|
395
|
+
const nodeA = this.graph.getNode(idA);
|
|
396
|
+
const nodeB = this.graph.getNode(idB);
|
|
397
|
+
// Primary Metric: Strategy Score
|
|
398
|
+
const scoreA = this.calculateScore(nodeA, strategy);
|
|
399
|
+
const scoreB = this.calculateScore(nodeB, strategy);
|
|
400
|
+
if (scoreA !== scoreB) {
|
|
401
|
+
return scoreB - scoreA; // Higher score first
|
|
402
|
+
}
|
|
403
|
+
// Tie-breaker: ID (stable sort)
|
|
404
|
+
return idA.localeCompare(idB);
|
|
405
|
+
}
|
|
406
|
+
calculateScore(node, strategy) {
|
|
407
|
+
// Avoid division by zero
|
|
408
|
+
const safeInDegree = node.inDegree + 1;
|
|
409
|
+
if (strategy === 'foundational') {
|
|
410
|
+
// Foundational: Low In-Degree (Global), High Out-Degree (Global)
|
|
411
|
+
// "Low in-degree yet highly correlated with other required nodes (out-degree)"
|
|
412
|
+
// Score = OutDegree / InDegree
|
|
413
|
+
return (node.outDegree + 0.1) / safeInDegree;
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
// Core: High Centrality, Low In-Degree (in learning set context)
|
|
417
|
+
// Note: In-degree in context is already 0 (since they are in 'available' list).
|
|
418
|
+
// So we use Global Centrality as the main differentiator.
|
|
419
|
+
// "Highly correlated (Centrality) ... low in-degree (Global)"
|
|
420
|
+
return (node.centrality || 0) * 10 - node.inDegree;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
PathEngine = PathEngine;
|
|
425
|
+
|
|
426
|
+
// Explicitly expose
|
|
427
|
+
self.PathEngine = PathEngine;
|
|
428
|
+
|
|
429
|
+
})();
|
|
@@ -126,7 +126,9 @@
|
|
|
126
126
|
"general": "General",
|
|
127
127
|
"physics": "Physics",
|
|
128
128
|
"performance": "Performance",
|
|
129
|
-
"visuals": "Visuals"
|
|
129
|
+
"visuals": "Visuals",
|
|
130
|
+
"reading": "Reading",
|
|
131
|
+
"path_mode": "Path Mode"
|
|
130
132
|
},
|
|
131
133
|
"labels": {
|
|
132
134
|
"language": "Language",
|
|
@@ -139,7 +141,10 @@
|
|
|
139
141
|
"compactMode": "Compact Mode (Hide Edges)",
|
|
140
142
|
"staticMode": "Static Mode",
|
|
141
143
|
"gpuRendering": "GPU Layout Optimization (Beta)",
|
|
142
|
-
"deepDebug": "Deep Debug Mode"
|
|
144
|
+
"deepDebug": "Deep Debug Mode",
|
|
145
|
+
"opacity": "Edge Opacity",
|
|
146
|
+
"reading_mode": "Open Mode",
|
|
147
|
+
"retain_history": "Retain Learning History"
|
|
143
148
|
},
|
|
144
149
|
"descriptions": {
|
|
145
150
|
"workers": "Lower this if Node.js runs out of memory.",
|
|
@@ -148,7 +153,8 @@
|
|
|
148
153
|
"compactMode": "Don't load/render edges by default to improve performance for >5k nodes.",
|
|
149
154
|
"staticMode": "Stop simulation immediately after build. Graph won't move.",
|
|
150
155
|
"gpuRendering": "Offload layout calculations to GPU (AMD Radeon 7900XT or compatible, may be slower for <2k nodes).",
|
|
151
|
-
"deepDebug": "Enable verbose logging during build."
|
|
156
|
+
"deepDebug": "Enable verbose logging during build.",
|
|
157
|
+
"retain_history": "Keep learned nodes state across sessions."
|
|
152
158
|
},
|
|
153
159
|
"save": "Save",
|
|
154
160
|
"cancel": "Cancel",
|
|
@@ -196,6 +202,32 @@
|
|
|
196
202
|
"controls": "Controls"
|
|
197
203
|
}
|
|
198
204
|
},
|
|
205
|
+
"notifications": {
|
|
206
|
+
"buildSuccess": "Build Success! Reloading interface...",
|
|
207
|
+
"buildFailed": "Build failed. Please check your knowledge base path.",
|
|
208
|
+
"noData": "No data available. Please load a knowledge base first."
|
|
209
|
+
},
|
|
210
|
+
"manual": {
|
|
211
|
+
"loading": "Loading documentation...",
|
|
212
|
+
"error": "Error Loading Documentation",
|
|
213
|
+
"errorDetail": "Could not load User_Manual.md or README.md.",
|
|
214
|
+
"technicalDetail": "Technical details: {error}",
|
|
215
|
+
"close": "Close"
|
|
216
|
+
},
|
|
217
|
+
"btn_settings": "Settings",
|
|
218
|
+
"manual_title": "Quick Start Guide",
|
|
219
|
+
"manual_step1_title": "1. Load Knowledge Base",
|
|
220
|
+
"manual_step1_desc": "Select a folder from the dropdown (top-left) and click \"Load\" to visualize your notes.",
|
|
221
|
+
"manual_step2_title": "2. Navigation",
|
|
222
|
+
"manual_step2_desc": "• <strong>Pan/Zoom</strong>: Drag background to move, Scroll/Pinch to zoom.<br />• <strong>Inspect</strong>: Click (Mobile) or Hover (PC) a node to see connections.",
|
|
223
|
+
"manual_step3_title": "3. Focus Mode",
|
|
224
|
+
"manual_step3_desc": "<strong>Double Click</strong> a node to enter Focus Mode. This isolates the concept and arranges its dependencies hierarchically.",
|
|
225
|
+
"manual_step4_title": "4. Controls",
|
|
226
|
+
"manual_step4_desc": "• <strong>Freeze (❄️)</strong>: Stop movement to easily click/read nodes.<br />• <strong>Layout</strong>: Switch between \"Force\" (Cluster) and \"DAG\" (Tree) layouts in the side panel.",
|
|
227
|
+
"view_mode": "View Mode:",
|
|
228
|
+
"view_nodes": "Nodes",
|
|
229
|
+
"view_clusters": "Clusters (Overview)",
|
|
230
|
+
"auto_arrange": "Auto Arrange",
|
|
199
231
|
"nodes": "Nodes",
|
|
200
232
|
"edges": "Edges",
|
|
201
233
|
"show_all": "Show All",
|
|
@@ -252,6 +284,7 @@
|
|
|
252
284
|
"grp_performance": "Performance",
|
|
253
285
|
"grp_visuals": "Visuals",
|
|
254
286
|
"grp_reading": "Reading",
|
|
287
|
+
"grp_path_mode": "Path Mode",
|
|
255
288
|
"lbl_language": "Language",
|
|
256
289
|
"lbl_repulsion": "Repulsion Strength",
|
|
257
290
|
"lbl_distance": "Link Distance",
|
|
@@ -265,6 +298,7 @@
|
|
|
265
298
|
"lbl_deep_debug": "Deep Debug Mode",
|
|
266
299
|
"lbl_opacity": "Edge Opacity",
|
|
267
300
|
"lbl_reading_mode": "Open Mode",
|
|
301
|
+
"lbl_retain_history": "Retain Learning History",
|
|
268
302
|
"desc_workers": "Lower this if Node.js runs out of memory.",
|
|
269
303
|
"desc_gpu": "Use GPU for similarity calculation (Requires page reload).",
|
|
270
304
|
"desc_memory_saving": "Use lower precision strategies to prevent OOM on large files.",
|
|
@@ -272,36 +306,25 @@
|
|
|
272
306
|
"desc_static_mode": "Stop simulation after 2 seconds. Recommended for large graphs.",
|
|
273
307
|
"desc_gpu_rendering": "Enable AMDGPU acceleration for loading and positioning.",
|
|
274
308
|
"desc_deep_debug": "Enable detailed logging for debugging.",
|
|
309
|
+
"desc_retain_history": "Keep learned nodes state across sessions.",
|
|
275
310
|
"opt_window": "Window",
|
|
276
311
|
"opt_fullscreen": "Full Screen",
|
|
277
312
|
"btn_reset": "Reset",
|
|
278
313
|
"btn_done": "Done",
|
|
279
314
|
"btn_got_it": "Got it!",
|
|
280
315
|
"dont_show_again": "Don't show again",
|
|
281
|
-
"
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
"
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
"
|
|
294
|
-
"
|
|
295
|
-
"manual_step1_title": "1. Load Knowledge Base",
|
|
296
|
-
"manual_step1_desc": "Select a folder from the dropdown (top-left) and click \"Load\" to visualize your notes.",
|
|
297
|
-
"manual_step2_title": "2. Navigation",
|
|
298
|
-
"manual_step2_desc": "• <strong>Pan/Zoom</strong>: Drag background to move, Scroll/Pinch to zoom.<br />• <strong>Inspect</strong>: Click (Mobile) or Hover (PC) a node to see connections.",
|
|
299
|
-
"manual_step3_title": "3. Focus Mode",
|
|
300
|
-
"manual_step3_desc": "<strong>Double Click</strong> a node to enter Focus Mode. This isolates the concept and arranges its dependencies hierarchically.",
|
|
301
|
-
"manual_step4_title": "4. Controls",
|
|
302
|
-
"manual_step4_desc": "• <strong>Freeze (❄️)</strong>: Stop movement to easily click/read nodes.<br />• <strong>Layout</strong>: Switch between \"Force\" (Cluster) and \"DAG\" (Tree) layouts in the side panel.",
|
|
303
|
-
"view_mode": "View Mode:",
|
|
304
|
-
"view_nodes": "Nodes",
|
|
305
|
-
"view_clusters": "Clusters (Overview)",
|
|
306
|
-
"auto_arrange": "Auto Arrange"
|
|
316
|
+
"lbl_mode": "Mode:",
|
|
317
|
+
"opt_domain": "Domain Learning",
|
|
318
|
+
"opt_diffusion": "Diffusion Learning",
|
|
319
|
+
"lbl_strategy": "Strategy:",
|
|
320
|
+
"opt_strat_foundational": "Foundational",
|
|
321
|
+
"opt_strat_core": "Core",
|
|
322
|
+
"lbl_layout": "Layout:",
|
|
323
|
+
"opt_layout_vertical": "Vertical Tree",
|
|
324
|
+
"opt_layout_horizontal": "Horizontal Tree",
|
|
325
|
+
"opt_layout_radial": "Radial",
|
|
326
|
+
"opt_layout_orbital": "Orbital (Focus)",
|
|
327
|
+
"btn_complete": "Complete",
|
|
328
|
+
"btn_history": "History",
|
|
329
|
+
"btn_exit": "Exit"
|
|
307
330
|
}
|
|
@@ -126,7 +126,9 @@
|
|
|
126
126
|
"general": "常规",
|
|
127
127
|
"physics": "物理引擎",
|
|
128
128
|
"performance": "性能优化",
|
|
129
|
-
"visuals": "视觉效果"
|
|
129
|
+
"visuals": "视觉效果",
|
|
130
|
+
"reading": "阅读",
|
|
131
|
+
"path_mode": "路径模式"
|
|
130
132
|
},
|
|
131
133
|
"labels": {
|
|
132
134
|
"language": "语言",
|
|
@@ -139,7 +141,10 @@
|
|
|
139
141
|
"compactMode": "紧凑模式 (隐藏边)",
|
|
140
142
|
"staticMode": "静态模式",
|
|
141
143
|
"gpuRendering": "GPU 布局优化 (Beta)",
|
|
142
|
-
"deepDebug": "深度调试模式"
|
|
144
|
+
"deepDebug": "深度调试模式",
|
|
145
|
+
"opacity": "边透明度",
|
|
146
|
+
"reading_mode": "打开模式",
|
|
147
|
+
"retain_history": "保留学习记录"
|
|
143
148
|
},
|
|
144
149
|
"descriptions": {
|
|
145
150
|
"workers": "如果 Node.js 内存不足,请降低此值。",
|
|
@@ -148,7 +153,8 @@
|
|
|
148
153
|
"compactMode": "默认不加载/渲染边以提高 >5k 节点的性能。",
|
|
149
154
|
"staticMode": "构建后立即停止模拟。图谱不会移动。",
|
|
150
155
|
"gpuRendering": "将布局计算卸载到 GPU(AMD Radeon 7900XT 或兼容GPU,低于 2k 节点可能更慢)。",
|
|
151
|
-
"deepDebug": "在构建期间启用详细日志。"
|
|
156
|
+
"deepDebug": "在构建期间启用详细日志。",
|
|
157
|
+
"retain_history": "在会话之间保存已学习节点的状态。"
|
|
152
158
|
},
|
|
153
159
|
"save": "保存",
|
|
154
160
|
"cancel": "取消",
|
|
@@ -257,6 +263,7 @@
|
|
|
257
263
|
"grp_performance": "性能优化",
|
|
258
264
|
"grp_visuals": "视觉效果",
|
|
259
265
|
"grp_reading": "阅读",
|
|
266
|
+
"grp_path_mode": "路径模式",
|
|
260
267
|
"lbl_language": "语言",
|
|
261
268
|
"lbl_repulsion": "排斥力强度",
|
|
262
269
|
"lbl_distance": "链接距离",
|
|
@@ -270,13 +277,15 @@
|
|
|
270
277
|
"lbl_deep_debug": "深度调试模式",
|
|
271
278
|
"lbl_opacity": "边透明度",
|
|
272
279
|
"lbl_reading_mode": "打开模式",
|
|
280
|
+
"lbl_retain_history": "保留学习记录",
|
|
273
281
|
"desc_workers": "如果 Node.js 内存不足,请降低此值。",
|
|
274
282
|
"desc_gpu": "使用 GPU 进行相似度计算(需要重新加载页面)。",
|
|
275
283
|
"desc_memory_saving": "使用低精度策略防止大文件 OOM。",
|
|
276
284
|
"desc_compact_mode": "默认不加载/渲染边以提高 >5k 节点的性能。",
|
|
277
|
-
"desc_static_mode": "
|
|
278
|
-
"desc_gpu_rendering": "
|
|
279
|
-
"desc_deep_debug": "
|
|
285
|
+
"desc_static_mode": "构建后立即停止模拟。图谱不会移动。",
|
|
286
|
+
"desc_gpu_rendering": "将布局计算卸载到 GPU(AMD Radeon 7900XT 或兼容GPU,低于 2k 节点可能更慢)。",
|
|
287
|
+
"desc_deep_debug": "在构建期间启用详细日志。",
|
|
288
|
+
"desc_retain_history": "在会话之间保存已学习节点的状态。",
|
|
280
289
|
"opt_window": "窗口",
|
|
281
290
|
"opt_fullscreen": "全屏",
|
|
282
291
|
"btn_reset": "重置",
|
|
@@ -303,5 +312,19 @@
|
|
|
303
312
|
"view_mode": "视图模式:",
|
|
304
313
|
"view_nodes": "节点",
|
|
305
314
|
"view_clusters": "聚类 (概览)",
|
|
306
|
-
"auto_arrange": "自动排列"
|
|
315
|
+
"auto_arrange": "自动排列",
|
|
316
|
+
"lbl_mode": "模式:",
|
|
317
|
+
"opt_domain": "领域学习",
|
|
318
|
+
"opt_diffusion": "扩散学习",
|
|
319
|
+
"lbl_strategy": "策略:",
|
|
320
|
+
"opt_strat_foundational": "基础 (低入度)",
|
|
321
|
+
"opt_strat_core": "核心 (高中心性)",
|
|
322
|
+
"lbl_layout": "布局:",
|
|
323
|
+
"opt_layout_vertical": "垂直树状",
|
|
324
|
+
"opt_layout_horizontal": "水平树状",
|
|
325
|
+
"opt_layout_radial": "径向",
|
|
326
|
+
"opt_layout_orbital": "轨道 (专注)",
|
|
327
|
+
"btn_complete": "完成",
|
|
328
|
+
"btn_history": "历史",
|
|
329
|
+
"btn_exit": "退出"
|
|
307
330
|
}
|