noteconnection 0.9.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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/backend/CommunityDetection.js +58 -0
  4. package/dist/backend/FileLoader.js +110 -0
  5. package/dist/backend/GraphBuilder.js +347 -0
  6. package/dist/backend/GraphMetrics.js +70 -0
  7. package/dist/backend/algorithms/CycleDetection.js +63 -0
  8. package/dist/backend/algorithms/HybridEngine.js +70 -0
  9. package/dist/backend/algorithms/StatisticalAnalyzer.js +123 -0
  10. package/dist/backend/algorithms/TopologicalSort.js +69 -0
  11. package/dist/backend/algorithms/VectorSpace.js +87 -0
  12. package/dist/backend/build_dag.js +164 -0
  13. package/dist/backend/config.js +17 -0
  14. package/dist/backend/graph.js +108 -0
  15. package/dist/backend/main.js +67 -0
  16. package/dist/backend/parser.js +94 -0
  17. package/dist/backend/test_robustness/test_hybrid.js +60 -0
  18. package/dist/backend/test_robustness/test_statistics.js +58 -0
  19. package/dist/backend/test_robustness/test_vector.js +54 -0
  20. package/dist/backend/test_robustness.js +113 -0
  21. package/dist/backend/types.js +3 -0
  22. package/dist/backend/utils/frontmatterParser.js +121 -0
  23. package/dist/backend/utils/stringUtils.js +66 -0
  24. package/dist/backend/workers/keywordMatchWorker.js +22 -0
  25. package/dist/core/Graph.js +121 -0
  26. package/dist/core/Graph.test.js +37 -0
  27. package/dist/core/types.js +2 -0
  28. package/dist/frontend/analysis.js +356 -0
  29. package/dist/frontend/app.js +1447 -0
  30. package/dist/frontend/data.js +8356 -0
  31. package/dist/frontend/graph_data.json +8356 -0
  32. package/dist/frontend/index.html +279 -0
  33. package/dist/frontend/reader.js +177 -0
  34. package/dist/frontend/settings.js +84 -0
  35. package/dist/frontend/source_manager.js +61 -0
  36. package/dist/frontend/styles.css +577 -0
  37. package/dist/frontend/styles_analysis.css +145 -0
  38. package/dist/index.js +121 -0
  39. package/dist/server.js +149 -0
  40. package/package.json +39 -0
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TopologicalSort = void 0;
4
+ /**
5
+ * Service to perform Topological Sort and Rank assignment.
6
+ * 执行拓扑排序和等级分配的服务。
7
+ */
8
+ class TopologicalSort {
9
+ /**
10
+ * Assigns a topological rank (level) to each node.
11
+ * 为每个节点分配拓扑等级(层级)。
12
+ * Rank 0 = Roots (No dependencies).
13
+ * Rank N = Dependencies have max rank N-1.
14
+ *
15
+ * @param graph The graph to process.
16
+ * @returns Map of NodeId -> Rank.
17
+ */
18
+ static assignRanks(graph) {
19
+ const ranks = new Map();
20
+ const inDegrees = new Map();
21
+ const nodes = graph.getNodes();
22
+ // 1. Initialize In-Degrees
23
+ nodes.forEach(node => {
24
+ inDegrees.set(node.id, node.inDegree);
25
+ ranks.set(node.id, 0); // Default rank
26
+ });
27
+ // 2. Queue for nodes with in-degree 0
28
+ const queue = [];
29
+ nodes.forEach(node => {
30
+ if (node.inDegree === 0) {
31
+ queue.push(node.id);
32
+ }
33
+ });
34
+ // 3. Process Queue (Kahn's Algorithm variant for Longest Path)
35
+ // We want rank[v] = max(rank[v], rank[u] + 1)
36
+ // Standard Kahn's processes nodes when all dependencies are met.
37
+ // This implicitly ensures we processed all 'u' before 'v'.
38
+ let processedCount = 0;
39
+ while (queue.length > 0) {
40
+ const uId = queue.shift();
41
+ processedCount++;
42
+ const uRank = ranks.get(uId);
43
+ const neighbors = graph.getNeighbors(uId);
44
+ for (const vId of neighbors) {
45
+ // Update rank of v
46
+ const currentVRank = ranks.get(vId) || 0;
47
+ if (uRank + 1 > currentVRank) {
48
+ ranks.set(vId, uRank + 1);
49
+ }
50
+ // Decrement in-degree
51
+ const d = inDegrees.get(vId) - 1;
52
+ inDegrees.set(vId, d);
53
+ if (d === 0) {
54
+ queue.push(vId);
55
+ }
56
+ }
57
+ }
58
+ // 4. Handle Cycles
59
+ if (processedCount < nodes.length) {
60
+ console.warn(`Graph contains cycles! Processed ${processedCount}/${nodes.length} nodes.`);
61
+ // Nodes involved in cycles (or reachable from them) were not processed.
62
+ // Their ranks might be incorrect (default 0 or partial updates).
63
+ // We could identify them and push them to the bottom, or just leave as is.
64
+ // For now, we leave them. The CycleDetector should be used to resolve this separately.
65
+ }
66
+ return ranks;
67
+ }
68
+ }
69
+ exports.TopologicalSort = TopologicalSort;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VectorSpace = void 0;
4
+ class VectorSpace {
5
+ constructor(files) {
6
+ this.vocab = new Map();
7
+ this.idf = new Map();
8
+ this.vectors = new Map();
9
+ this.build(files);
10
+ }
11
+ build(files) {
12
+ const docCount = files.length;
13
+ const docFreq = new Map();
14
+ const tokenizedDocs = new Map();
15
+ // 1. Tokenize & Build Vocabulary
16
+ files.forEach(file => {
17
+ const tokens = this.tokenize(file.content);
18
+ tokenizedDocs.set(file.filename, tokens);
19
+ const uniqueTokens = new Set(tokens);
20
+ uniqueTokens.forEach(token => {
21
+ docFreq.set(token, (docFreq.get(token) || 0) + 1);
22
+ });
23
+ });
24
+ // 2. Calculate IDF & Build Vocab Index
25
+ let index = 0;
26
+ docFreq.forEach((count, term) => {
27
+ if (count > 1) { // Ignore rare terms (min_doc_freq = 2)
28
+ this.vocab.set(term, index++);
29
+ this.idf.set(term, Math.log(docCount / (1 + count)));
30
+ }
31
+ });
32
+ // 3. Compute TF-IDF Vectors
33
+ tokenizedDocs.forEach((tokens, fileId) => {
34
+ const vector = new Array(this.vocab.size).fill(0);
35
+ const termCounts = new Map();
36
+ tokens.forEach(t => termCounts.set(t, (termCounts.get(t) || 0) + 1));
37
+ termCounts.forEach((count, term) => {
38
+ const idx = this.vocab.get(term);
39
+ if (idx !== undefined) {
40
+ const tf = count / tokens.length;
41
+ vector[idx] = tf * this.idf.get(term);
42
+ }
43
+ });
44
+ // L2 Normalize
45
+ const norm = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
46
+ if (norm > 0) {
47
+ for (let i = 0; i < vector.length; i++)
48
+ vector[i] /= norm;
49
+ }
50
+ this.vectors.set(fileId, vector);
51
+ });
52
+ }
53
+ tokenize(text) {
54
+ // Bilingual tokenizer: English words or Chinese characters
55
+ // 双语分词器:匹配英文单词或单个中文字符
56
+ // Matches sequence of Alphanumeric OR Single CJK character
57
+ const regex = /[a-zA-Z0-9]+|[\u4e00-\u9fa5]/g;
58
+ return (text.match(regex) || []).map(t => t.toLowerCase());
59
+ }
60
+ getVector(fileId) {
61
+ return this.vectors.get(fileId);
62
+ }
63
+ getSimilar(fileId, topK = 5) {
64
+ const sourceVec = this.vectors.get(fileId);
65
+ if (!sourceVec)
66
+ return [];
67
+ const results = [];
68
+ this.vectors.forEach((targetVec, targetId) => {
69
+ if (fileId !== targetId) {
70
+ const score = this.cosineSimilarity(sourceVec, targetVec);
71
+ if (score > 0) {
72
+ results.push({ id: targetId, score });
73
+ }
74
+ }
75
+ });
76
+ return results.sort((a, b) => b.score - a.score).slice(0, topK);
77
+ }
78
+ cosineSimilarity(vecA, vecB) {
79
+ let dot = 0;
80
+ // Since vectors are L2 normalized, cosine sim is just dot product
81
+ for (let i = 0; i < vecA.length; i++) {
82
+ dot += vecA[i] * vecB[i];
83
+ }
84
+ return dot;
85
+ }
86
+ }
87
+ exports.VectorSpace = VectorSpace;
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const fs = __importStar(require("fs"));
37
+ const path = __importStar(require("path"));
38
+ // --- Implementations ---
39
+ class LocalFileLoader {
40
+ /**
41
+ * Loads all .md files from a directory.
42
+ * 从目录加载所有 .md 文件。
43
+ */
44
+ static load(dirPath) {
45
+ if (!fs.existsSync(dirPath)) {
46
+ console.error(`Directory not found: ${dirPath}`);
47
+ return [];
48
+ }
49
+ const files = fs.readdirSync(dirPath);
50
+ const notes = [];
51
+ for (const file of files) {
52
+ if (path.extname(file).toLowerCase() === '.md') {
53
+ const fullPath = path.join(dirPath, file);
54
+ const content = fs.readFileSync(fullPath, 'utf-8');
55
+ const id = path.parse(file).name; // Filename as ID
56
+ notes.push({ id, content });
57
+ }
58
+ }
59
+ console.log(`Loaded ${notes.length} notes.`);
60
+ return notes;
61
+ }
62
+ }
63
+ class KeywordMatcher {
64
+ /**
65
+ * Escapes RegExp special characters.
66
+ * 转义正则特殊字符。
67
+ */
68
+ static escapeRegExp(string) {
69
+ return string.replace(/[.*+?^${}()|[\\]/g, '\\$&');
70
+ }
71
+ /**
72
+ * Finds edges where one note mentions another's title.
73
+ * 查找一个笔记提及另一个笔记标题的边。
74
+ */
75
+ static findMatches(notes) {
76
+ const edges = [];
77
+ const ids = notes.map(n => n.id);
78
+ // Pre-compile regex for each ID to improve performance
79
+ // 为每个 ID 预编译正则以提高性能
80
+ const idRegexes = ids.map(id => ({
81
+ id: id,
82
+ regex: new RegExp(`\\b${this.escapeRegExp(id)}\\b`, 'i') // Case insensitive, whole word
83
+ }));
84
+ for (const currentNote of notes) {
85
+ for (const other of idRegexes) {
86
+ if (currentNote.id === other.id)
87
+ continue; // No self-loops
88
+ // If current note contains other note's ID
89
+ // 如果当前笔记包含其他笔记的 ID
90
+ if (other.regex.test(currentNote.content)) {
91
+ // Logic: Mentioned (other) -> Mentioner (current)
92
+ // Basic -> Advanced
93
+ edges.push({
94
+ source: other.id,
95
+ target: currentNote.id,
96
+ weight: 1 // Could count matches for higher weight
97
+ });
98
+ }
99
+ }
100
+ }
101
+ console.log(`Discovered ${edges.length} edges.`);
102
+ return edges;
103
+ }
104
+ }
105
+ class StructureGenerator {
106
+ /**
107
+ * Converts to D3 JSON format with Degree calculation.
108
+ * 转换为 D3 JSON 格式并计算度数。
109
+ */
110
+ static generateJSON(notes, edges) {
111
+ // Calculate degrees
112
+ const inDegreeMap = new Map();
113
+ const outDegreeMap = new Map();
114
+ // Initialize maps
115
+ notes.forEach(n => {
116
+ inDegreeMap.set(n.id, 0);
117
+ outDegreeMap.set(n.id, 0);
118
+ });
119
+ // Sum degrees
120
+ edges.forEach(e => {
121
+ const currentIn = inDegreeMap.get(e.target) || 0;
122
+ inDegreeMap.set(e.target, currentIn + 1);
123
+ const currentOut = outDegreeMap.get(e.source) || 0;
124
+ outDegreeMap.set(e.source, currentOut + 1);
125
+ });
126
+ const d3Nodes = notes.map(n => ({
127
+ id: n.id,
128
+ group: 1,
129
+ inDegree: inDegreeMap.get(n.id) || 0,
130
+ outDegree: outDegreeMap.get(n.id) || 0
131
+ }));
132
+ const d3Links = edges.map(e => ({
133
+ source: e.source,
134
+ target: e.target,
135
+ value: e.weight
136
+ }));
137
+ return {
138
+ nodes: d3Nodes,
139
+ links: d3Links
140
+ };
141
+ }
142
+ }
143
+ // --- Main Execution ---
144
+ const CONFIG = {
145
+ inputDir: path.join(__dirname, '../../testconcept'),
146
+ outputFile: path.join(__dirname, '../../src/frontend/graph_data.json')
147
+ };
148
+ function main() {
149
+ console.log("Starting DAG Build Process...");
150
+ // 1. Load Files
151
+ const notes = LocalFileLoader.load(CONFIG.inputDir);
152
+ // 2. Find Edges
153
+ const edges = KeywordMatcher.findMatches(notes);
154
+ // 3. Generate Graph Data
155
+ const graphData = StructureGenerator.generateJSON(notes, edges);
156
+ // 4. Write Output
157
+ const outputDir = path.dirname(CONFIG.outputFile);
158
+ if (!fs.existsSync(outputDir)) {
159
+ fs.mkdirSync(outputDir, { recursive: true });
160
+ }
161
+ fs.writeFileSync(CONFIG.outputFile, JSON.stringify(graphData, null, 2));
162
+ console.log(`Graph data written to: ${CONFIG.outputFile}`);
163
+ }
164
+ main();
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ exports.config = {
5
+ matchingStrategy: 'exact-phrase',
6
+ clusteringStrategy: 'label-propagation', // Default to current behavior
7
+ fuzzyThreshold: 2,
8
+ enableTags: true,
9
+ enableStatisticalInference: true, // Default on
10
+ enableVectorSimilarity: true, // Default on
11
+ enableHybridInference: true, // Default on
12
+ exclusionList: [
13
+ // Add common words or concepts here that cause too much noise
14
+ // e.g., "Introduction", "Summary", etc.
15
+ // For now, we leave it empty as a template
16
+ ]
17
+ };
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Exporter = exports.GraphBuilder = void 0;
37
+ const fs = __importStar(require("fs/promises"));
38
+ class GraphBuilder {
39
+ static buildGraph(concepts) {
40
+ const nodes = concepts.map(c => ({
41
+ id: c.id,
42
+ label: c.title
43
+ }));
44
+ const edges = [];
45
+ const titleMap = new Map(); // lowercase title -> real id
46
+ // Pre-process titles for case-insensitive lookup
47
+ // 预处理标题以进行不区分大小写的查找
48
+ concepts.forEach(c => {
49
+ titleMap.set(c.title.toLowerCase(), c.id);
50
+ });
51
+ // 1. Explicit Dependencies (Placeholder for now)
52
+ // 1. 显式依赖 (目前占位)
53
+ // 2. Keyword Matching Strategy
54
+ // 2. 关键词匹配策略
55
+ concepts.forEach(sourceConcept => {
56
+ const contentLower = sourceConcept.content.toLowerCase();
57
+ concepts.forEach(targetConcept => {
58
+ if (sourceConcept.id === targetConcept.id)
59
+ return; // Self-loop check
60
+ const targetTitle = targetConcept.title.toLowerCase();
61
+ // Filter out very short words to reduce noise (e.g. "Ice", "mW")
62
+ // 过滤掉非常短的单词以减少噪声 (例如 "Ice", "mW")
63
+ // Heuristic: Length >= 3 or specific whitelist
64
+ // 启发式:长度 >= 3 或特定白名单
65
+ if (targetTitle.length < 3)
66
+ return;
67
+ // Check strict inclusion (maybe utilize regex boundary \b in future)
68
+ // 检查严格包含 (未来可能利用正则边界 \b)
69
+ if (contentLower.includes(targetTitle)) {
70
+ // Heuristic: If A mentions B, B -> A (B is concept used in A)
71
+ // 启发式:如果 A 提到 B,则 B -> A (B 是 A 中使用的概念)
72
+ edges.push({
73
+ source: targetConcept.id,
74
+ target: sourceConcept.id,
75
+ type: 'keyword',
76
+ weight: 1.0
77
+ });
78
+ }
79
+ });
80
+ });
81
+ // Deduplicate edges
82
+ // 去重边
83
+ const uniqueEdges = this.deduplicateEdges(edges);
84
+ return {
85
+ nodes,
86
+ edges: uniqueEdges
87
+ };
88
+ }
89
+ static deduplicateEdges(edges) {
90
+ const seen = new Set();
91
+ return edges.filter(e => {
92
+ const key = `${e.source}->${e.target}`;
93
+ if (seen.has(key))
94
+ return false;
95
+ seen.add(key);
96
+ return true;
97
+ });
98
+ }
99
+ }
100
+ exports.GraphBuilder = GraphBuilder;
101
+ class Exporter {
102
+ static async exportToJSON(graph, outputPath) {
103
+ const json = JSON.stringify(graph, null, 2);
104
+ await fs.writeFile(outputPath, json, 'utf-8');
105
+ console.log(`Graph exported to ${outputPath}`);
106
+ }
107
+ }
108
+ exports.Exporter = Exporter;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ // src/backend/main.ts
37
+ const path = __importStar(require("path"));
38
+ const parser_1 = require("./parser");
39
+ const graph_1 = require("./graph");
40
+ async function main() {
41
+ const inputDir = path.resolve(__dirname, '../../testconcept');
42
+ const outputDir = path.resolve(__dirname, '../frontend');
43
+ const outputFile = path.join(outputDir, 'graph_data.json');
44
+ console.log(`Starting Build Process...`);
45
+ console.log(`Input Directory: ${inputDir}`);
46
+ // 1. Load Files
47
+ // 1. 加载文件
48
+ const rawFiles = await parser_1.FileLoader.loadFiles(inputDir);
49
+ console.log(`Loaded ${rawFiles.length} files.`);
50
+ if (rawFiles.length === 0) {
51
+ console.warn('No markdown files found. Exiting.');
52
+ return;
53
+ }
54
+ // 2. Parse Concepts
55
+ // 2. 解析概念
56
+ const concepts = parser_1.NoteParser.parse(rawFiles);
57
+ console.log(`Parsed ${concepts.length} concepts.`);
58
+ // 3. Build Graph
59
+ // 3. 构建图
60
+ const graph = graph_1.GraphBuilder.buildGraph(concepts);
61
+ console.log(`Graph Built: ${graph.nodes.length} Nodes, ${graph.edges.length} Edges.`);
62
+ // 4. Export
63
+ // 4. 导出
64
+ await graph_1.Exporter.exportToJSON(graph, outputFile);
65
+ console.log('Build Complete.');
66
+ }
67
+ main().catch(err => console.error(err));
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NoteParser = exports.FileLoader = void 0;
37
+ // src/backend/parser.ts
38
+ const fs_1 = require("fs");
39
+ const path = __importStar(require("path"));
40
+ // File Loader Implementation
41
+ // 文件加载器实现
42
+ class FileLoader {
43
+ static async loadFiles(directory) {
44
+ try {
45
+ const filenames = await fs_1.promises.readdir(directory);
46
+ const rawFiles = [];
47
+ for (const file of filenames) {
48
+ if (path.extname(file).toLowerCase() !== '.md')
49
+ continue;
50
+ const fullPath = path.join(directory, file);
51
+ const stat = await fs_1.promises.stat(fullPath);
52
+ const content = await fs_1.promises.readFile(fullPath, 'utf-8');
53
+ rawFiles.push({
54
+ filepath: fullPath,
55
+ filename: file,
56
+ content: content,
57
+ modifiedTime: stat.mtime
58
+ });
59
+ }
60
+ return rawFiles;
61
+ }
62
+ catch (error) {
63
+ console.error(`Error loading files from ${directory}:`, error);
64
+ return [];
65
+ }
66
+ }
67
+ }
68
+ exports.FileLoader = FileLoader;
69
+ // Note Parser Implementation
70
+ // 笔记解析器实现
71
+ class NoteParser {
72
+ static parse(files) {
73
+ return files.map(file => {
74
+ const id = path.parse(file.filename).name;
75
+ // Default title is ID, but can be extracted from first H1 if needed
76
+ // 默认标题为ID,但如果需要可以从第一个H1提取
77
+ const title = id;
78
+ // Simple parsing for now (Future: Extract YAML)
79
+ // 目前为简单解析(未来:提取 YAML)
80
+ // Removing special characters for cleaner keyword matching later
81
+ // 移除特殊字符以便后续更干净的关键词匹配
82
+ return {
83
+ id: id,
84
+ title: title,
85
+ content: file.content,
86
+ metadata: {
87
+ tags: [],
88
+ prerequisites: [] // Placeholder for v0.2.0 explicit parsing
89
+ }
90
+ };
91
+ });
92
+ }
93
+ }
94
+ exports.NoteParser = NoteParser;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const path = __importStar(require("path"));
37
+ const FileLoader_1 = require("../FileLoader");
38
+ const StatisticalAnalyzer_1 = require("../algorithms/StatisticalAnalyzer");
39
+ const VectorSpace_1 = require("../algorithms/VectorSpace");
40
+ const HybridEngine_1 = require("../algorithms/HybridEngine");
41
+ async function run() {
42
+ const root = path.resolve(__dirname, '../../../testconcept');
43
+ console.log(`Loading files from: ${root}`);
44
+ const files = await FileLoader_1.FileLoader.loadFiles(root);
45
+ console.log(`Loaded ${files.length} files.`);
46
+ const terms = files.map(f => f.filename);
47
+ console.log('Building Statistical Matrix...');
48
+ const matrix = StatisticalAnalyzer_1.StatisticalAnalyzer.analyze(files, terms);
49
+ console.log('Building Vector Space...');
50
+ const vectorSpace = new VectorSpace_1.VectorSpace(files);
51
+ console.log('Running Hybrid Inference...');
52
+ const edges = HybridEngine_1.HybridEngine.infer(matrix, vectorSpace, 0.2, 0.1);
53
+ console.log(`Inferred ${edges.length} hybrid edges.`);
54
+ console.log('Top 20 Suggestions:');
55
+ edges.slice(0, 20).forEach(d => {
56
+ console.log(`[${d.source}] -> [${d.target}] (Conf: ${d.confidence.toFixed(2)}, Weight: ${d.weight.toFixed(2)})`);
57
+ console.log(` Reason: ${d.reason}`);
58
+ });
59
+ }
60
+ run().catch(e => console.error(e));