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.
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/backend/CommunityDetection.js +58 -0
- package/dist/backend/FileLoader.js +110 -0
- package/dist/backend/GraphBuilder.js +347 -0
- package/dist/backend/GraphMetrics.js +70 -0
- package/dist/backend/algorithms/CycleDetection.js +63 -0
- package/dist/backend/algorithms/HybridEngine.js +70 -0
- package/dist/backend/algorithms/StatisticalAnalyzer.js +123 -0
- package/dist/backend/algorithms/TopologicalSort.js +69 -0
- package/dist/backend/algorithms/VectorSpace.js +87 -0
- package/dist/backend/build_dag.js +164 -0
- package/dist/backend/config.js +17 -0
- package/dist/backend/graph.js +108 -0
- package/dist/backend/main.js +67 -0
- package/dist/backend/parser.js +94 -0
- package/dist/backend/test_robustness/test_hybrid.js +60 -0
- package/dist/backend/test_robustness/test_statistics.js +58 -0
- package/dist/backend/test_robustness/test_vector.js +54 -0
- package/dist/backend/test_robustness.js +113 -0
- package/dist/backend/types.js +3 -0
- package/dist/backend/utils/frontmatterParser.js +121 -0
- package/dist/backend/utils/stringUtils.js +66 -0
- package/dist/backend/workers/keywordMatchWorker.js +22 -0
- package/dist/core/Graph.js +121 -0
- package/dist/core/Graph.test.js +37 -0
- package/dist/core/types.js +2 -0
- package/dist/frontend/analysis.js +356 -0
- package/dist/frontend/app.js +1447 -0
- package/dist/frontend/data.js +8356 -0
- package/dist/frontend/graph_data.json +8356 -0
- package/dist/frontend/index.html +279 -0
- package/dist/frontend/reader.js +177 -0
- package/dist/frontend/settings.js +84 -0
- package/dist/frontend/source_manager.js +61 -0
- package/dist/frontend/styles.css +577 -0
- package/dist/frontend/styles_analysis.css +145 -0
- package/dist/index.js +121 -0
- package/dist/server.js +149 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jacob
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# NoteConnection: Hierarchical Knowledge Graph Visualization System
|
|
2
|
+
> **Unlock the Structure of Your Knowledge.**
|
|
3
|
+
|
|
4
|
+
**NoteConnection** is a high-performance, standalone visualization system engineered to transform unstructured Markdown knowledge bases into **Directed Acyclic Graphs (DAGs)**.
|
|
5
|
+
|
|
6
|
+
Unlike traditional "network" views that show a messy web of links, NoteConnection reveals the **hierarchy**, **learning paths**, and **dependency structures** hidden within your notes. It is built for scalability, capable of handling tens of thousands of nodes with ease, and operates completely independently of any specific note-taking app.
|
|
7
|
+
|
|
8
|
+
<img width="2010" height="2011" alt="image" src="https://github.com/user-attachments/assets/fa55676d-f58d-414e-943c-7a10567f88a5" />
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🚀 Key Features
|
|
13
|
+
|
|
14
|
+
### 1. Visualization & Layout
|
|
15
|
+
* **Structure Over Chaos**: Switch between **Force-Directed** (Physics) and **DAG** (Hierarchical) layouts. The DAG layout automatically identifies "Prerequisites" and "Next Steps" to arrange concepts in logical layers.
|
|
16
|
+
* **Dual Rendering Engine (v0.8.7)**: Seamlessly toggle between **SVG** (for interactivity) and **Canvas** (for high-performance rendering of 10,000+ nodes).
|
|
17
|
+
* **Interactive Focus Mode**: Click any node to isolate it and its context. Features **Freeze on Select** (v0.8.9) to prevent drift and adjustable **Vertical/Horizontal Spacing** (v0.8.8) to prevent overlap.
|
|
18
|
+
|
|
19
|
+
<img width="2010" height="2011" alt="image" src="https://github.com/user-attachments/assets/52785445-20bf-4ecc-847a-23863f291b6a" />
|
|
20
|
+
### 2. Intelligence & Inference
|
|
21
|
+
* **Hybrid Inference Engine**: Combines **Statistical Probability** ($P(A|B)$) and **Vector Similarity** (TF-IDF) to infer hidden dependencies (e.g., "Fluorescence" implies "Photon") without external AI APIs.
|
|
22
|
+
* **Scalable Clustering**: Aggregates thousands of nodes into high-level "Concept Bubbles" based on folder structure or tags for a cleaner overview.
|
|
23
|
+
|
|
24
|
+
<img width="3723" height="1992" alt="image" src="https://github.com/user-attachments/assets/9e56e567-1742-48cf-b720-cf65a47fd317" />
|
|
25
|
+
### 3. Performance & Control
|
|
26
|
+
* **High-Capacity Parallel Processing**: Utilizes Node.js `worker_threads` (up to 12 cores) to distribute computationally intensive keyword matching.
|
|
27
|
+
* **Simulation Controls (v0.9.0)**: Fine-tune the physics with a **Speed/Damping Slider** or use the **Freeze Layout** switch to stop the simulation for stable manual arrangement.
|
|
28
|
+
* **Hover Lock**: Hovering over a node temporarily locks its position, allowing for stable inspection of connections.
|
|
29
|
+
|
|
30
|
+
<img width="2012" height="2024" alt="image" src="https://github.com/user-attachments/assets/e5e4c42d-54a7-463c-bc43-0feb42469a12" />
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🏗️ System Architecture
|
|
35
|
+
|
|
36
|
+
NoteConnection is built on a modular architecture designed for performance and extensibility.
|
|
37
|
+
|
|
38
|
+
### Backend (`src/backend`)
|
|
39
|
+
* **GraphBuilder**: The core orchestrator. It manages the pipeline from file reading to graph construction.
|
|
40
|
+
* **Worker Threads**: Heavy lifting (keyword matching, text analysis) is offloaded to a pool of worker threads (`src/backend/workers`), ensuring the main thread remains responsive.
|
|
41
|
+
* **Inference Engines**:
|
|
42
|
+
* `StatisticalAnalyzer`: Calculates co-occurrence matrices.
|
|
43
|
+
* `VectorSpace`: Handles TF-IDF embedding and cosine similarity.
|
|
44
|
+
* `HybridEngine`: Combines signals to suggest directed edges.
|
|
45
|
+
|
|
46
|
+
### Frontend (`src/frontend`)
|
|
47
|
+
* **Dual-Engine Renderer**:
|
|
48
|
+
* **D3.js (SVG)**: Used for high-fidelity, interactive graphs with detailed tooltips and CSS styling.
|
|
49
|
+
* **HTML5 Canvas**: Optimized for rendering massive datasets where DOM manipulation overhead is too high.
|
|
50
|
+
* **State Management**: `SettingsManager` persists user preferences (Physics, Visuals) to `localStorage`.
|
|
51
|
+
* **Layout Logic**: Custom algorithms for Sugiyama-style layering and Force-directed physics.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 📦 Quick Start
|
|
56
|
+
|
|
57
|
+
### 1. Installation
|
|
58
|
+
```bash
|
|
59
|
+
npm install
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Run the Server
|
|
63
|
+
Launch the integrated server. This allows you to browse and build graphs from your browser.
|
|
64
|
+
```bash
|
|
65
|
+
npm start
|
|
66
|
+
```
|
|
67
|
+
* Server runs at: `http://localhost:3000`
|
|
68
|
+
|
|
69
|
+
### 3. Usage Guide
|
|
70
|
+
1. **Select Source**: Use the dropdown in the top-left to choose a folder from `Knowledge_Base`.
|
|
71
|
+
2. **Load**: Click "Load". For large datasets (>200 files), parallel processing engages automatically.
|
|
72
|
+
3. **Explore**:
|
|
73
|
+
* **Layout**: Toggle **DAG** for hierarchy or **Force** for clusters.
|
|
74
|
+
* **Renderer**: Switch to **Canvas** if the graph feels sluggish.
|
|
75
|
+
* **Focus**: Click a node to enter Focus Mode. Use the sliders to adjust spacing.
|
|
76
|
+
* **Control**: Use the **Simulation** panel to freeze the layout or adjust speed.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 📅 Changelog
|
|
81
|
+
|
|
82
|
+
### v0.9.0 - Precise Control & Stability (2025-12-23)
|
|
83
|
+
- [x] **Hover Lock**: Hovering over a node locks its position to prevent inspection drift.
|
|
84
|
+
- [x] **Simulation Controls**: Added **Freeze Layout** checkbox and **Speed/Damping** slider.
|
|
85
|
+
|
|
86
|
+
### v0.8.9 - Stability Improvements
|
|
87
|
+
- [x] **Freeze on Select**: Nodes in Focus Mode retain their position after interaction.
|
|
88
|
+
|
|
89
|
+
### v0.8.8 - Scalability Defaults
|
|
90
|
+
- [x] **Clutter Reduction**: Edges and orphans hidden by default.
|
|
91
|
+
- [x] **Horizontal Spacing**: New slider for horizontal node separation in Focus Mode.
|
|
92
|
+
|
|
93
|
+
### v0.8.7 - Rendering Engine
|
|
94
|
+
- [x] **Canvas Renderer**: Added HTML5 Canvas support for high performance.
|
|
95
|
+
- [x] **Worker Scaling**: Increased thread limit to 12.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
# NoteConnection: 层级知识图谱可视化系统
|
|
101
|
+
> **解锁你知识库的深层结构。**
|
|
102
|
+
|
|
103
|
+
**NoteConnection** 是一个高性能的独立可视化系统,旨在将非结构化的 Markdown 知识库转化为**有向无环图 (DAG)**。
|
|
104
|
+
|
|
105
|
+
与展示杂乱链接网的传统“网络”视图不同,NoteConnection 揭示了隐藏在笔记中的**层级关系**、**学习路径**和**依赖结构**。它专为可扩展性而设计,能够轻松处理数万个节点,并且完全独立于任何特定的笔记应用程序运行。
|
|
106
|
+
|
|
107
|
+
<img width="2784" height="2034" alt="image" src="https://github.com/user-attachments/assets/0ea42609-4296-42ea-978d-c6cb7d448068" />
|
|
108
|
+
<img width="3543" height="2159" alt="image" src="https://github.com/user-attachments/assets/0b2d80f5-ec8c-4ac1-9607-b925d4ab5f82" />
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🚀 核心特性
|
|
113
|
+
|
|
114
|
+
### 1. 可视化与布局
|
|
115
|
+
* **结构优于混沌**: 在 **力导向 (Force-Directed)** 和 **DAG (层级)** 布局之间切换。DAG 布局自动识别“先决条件”和“后续步骤”,将概念按逻辑分层排列。
|
|
116
|
+
* **双渲染引擎 (v0.8.7)**: 无缝切换 **SVG** (用于交互) 和 **Canvas** (用于 10,000+ 节点的高性能渲染)。
|
|
117
|
+
* **交互式专注模式**: 点击任意节点以隔离它及其上下文。包含 **选中冻结** (v0.8.9) 以防止漂移,以及可调节的 **垂直/水平间距** (v0.8.8) 以防止重叠。
|
|
118
|
+
|
|
119
|
+
<img width="3404" height="2028" alt="image" src="https://github.com/user-attachments/assets/39ea71da-be14-4fdc-9fec-9f33cab92e1b" />
|
|
120
|
+
|
|
121
|
+
### 2. 智能与推断
|
|
122
|
+
* **混合推断引擎**: 结合 **统计概率** ($P(A|B)$) 和 **向量相似度** (TF-IDF) 推断隐藏的依赖关系(例如,“荧光”隐含“光子”),无需外部 AI API。
|
|
123
|
+
* **可扩展聚类**: 基于文件夹结构或标签,将数千个节点聚合为高级“概念气泡”,提供清晰的概览。
|
|
124
|
+
|
|
125
|
+
<img width="3723" height="2007" alt="image" src="https://github.com/user-attachments/assets/10978984-3e2d-4ab6-8b44-342d4f3c3800" />
|
|
126
|
+
|
|
127
|
+
### 3. 性能与控制
|
|
128
|
+
* **高容量并行处理**: 利用 Node.js `worker_threads` (最多 12 核) 分发计算密集的关键词匹配任务。
|
|
129
|
+
* **模拟控制 (v0.9.0)**: 通过 **速度/阻尼滑块** 微调物理效果,或使用 **冻结布局** 开关停止模拟以进行稳定的手动排列。
|
|
130
|
+
* **悬停锁定**: 悬停在节点上时暂时锁定其位置,以便稳定地检查连接。
|
|
131
|
+
|
|
132
|
+
<img width="2012" height="2024" alt="image" src="https://github.com/user-attachments/assets/bf6e7508-7e42-46cb-9a3e-b92be063ad3d" />
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 🏗️ 系统架构
|
|
138
|
+
|
|
139
|
+
NoteConnection 基于模块化架构构建,旨在实现高性能和可扩展性。
|
|
140
|
+
|
|
141
|
+
### 后端 (`src/backend`)
|
|
142
|
+
* **GraphBuilder**: 核心协调器。管理从文件读取到图构建的整个流程。
|
|
143
|
+
* **Worker Threads**: 繁重的任务(关键词匹配、文本分析)被卸载到工作线程池 (`src/backend/workers`),确保主线程保持响应。
|
|
144
|
+
* **推断引擎**:
|
|
145
|
+
* `StatisticalAnalyzer`: 计算共现矩阵。
|
|
146
|
+
* `VectorSpace`: 处理 TF-IDF 嵌入和余弦相似度。
|
|
147
|
+
* `HybridEngine`: 结合信号建议有向边。
|
|
148
|
+
|
|
149
|
+
### 前端 (`src/frontend`)
|
|
150
|
+
* **双引擎渲染器**:
|
|
151
|
+
* **D3.js (SVG)**: 用于高保真、交互式图表,具有详细的工具提示和 CSS 样式。
|
|
152
|
+
* **HTML5 Canvas**: 针对海量数据集进行了优化,消除了 DOM 操作的开销。
|
|
153
|
+
* **状态管理**: `SettingsManager` 将用户偏好(物理、视觉)持久化到 `localStorage`。
|
|
154
|
+
* **布局逻辑**: 自定义的 Sugiyama 风格分层算法和力导向物理算法。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 📦 快速开始
|
|
159
|
+
|
|
160
|
+
### 1. 安装
|
|
161
|
+
```bash
|
|
162
|
+
npm install
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 2. 运行服务器
|
|
166
|
+
启动集成服务器。这允许您从浏览器浏览和构建图谱。
|
|
167
|
+
```bash
|
|
168
|
+
npm start
|
|
169
|
+
```
|
|
170
|
+
* 服务器运行于: `http://localhost:3000`
|
|
171
|
+
|
|
172
|
+
### 3. 使用指南
|
|
173
|
+
1. **选择数据源**: 使用左上角的下拉菜单从 `Knowledge_Base` 中选择文件夹。
|
|
174
|
+
2. **加载**: 点击 "Load"。对于大数据集 (>200 文件),并行处理将自动启用。
|
|
175
|
+
3. **探索**:
|
|
176
|
+
* **布局**: 切换 **DAG** 查看层级或 **Force** 查看聚类。
|
|
177
|
+
* **渲染器**: 如果图表感觉迟缓,请切换到 **Canvas**。
|
|
178
|
+
* **专注**: 点击节点进入专注模式。使用滑块调整间距。
|
|
179
|
+
* **控制**: 使用 **Simulation** 面板冻结布局或调整速度。
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 📅 更新日志 (Changelog)
|
|
184
|
+
|
|
185
|
+
### v0.9.0 - 精确控制与稳定性 (2025-12-23)
|
|
186
|
+
- [x] **悬停锁定**: 悬停节点时锁定其位置,防止检查时漂移。
|
|
187
|
+
- [x] **模拟控制**: 添加了 **冻结布局** 复选框和 **速度/阻尼** 滑块。
|
|
188
|
+
|
|
189
|
+
### v0.8.9 - 稳定性改进
|
|
190
|
+
- [x] **选中冻结**: 专注模式下的节点在交互后保留其位置。
|
|
191
|
+
|
|
192
|
+
### v0.8.8 - 可扩展性默认值
|
|
193
|
+
- [x] **减少杂乱**: 默认隐藏边和孤立节点。
|
|
194
|
+
- [x] **水平间距**: 专注模式下新增水平节点分隔滑块。
|
|
195
|
+
|
|
196
|
+
### v0.8.7 - 渲染引擎
|
|
197
|
+
- [x] **Canvas 渲染器**: 添加 HTML5 Canvas 支持以实现高性能。
|
|
198
|
+
- [x] **Worker 扩展**: 将线程限制增加到 12。
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CommunityDetection = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple Label Propagation Algorithm for Community Detection
|
|
6
|
+
*/
|
|
7
|
+
class CommunityDetection {
|
|
8
|
+
static detect(graph) {
|
|
9
|
+
const nodes = graph.toJSON().nodes;
|
|
10
|
+
const labels = new Map();
|
|
11
|
+
// Initialize: each node has its own label
|
|
12
|
+
nodes.forEach(n => labels.set(n.id, n.id));
|
|
13
|
+
const maxIter = 50;
|
|
14
|
+
let changed = true;
|
|
15
|
+
let iter = 0;
|
|
16
|
+
while (changed && iter < maxIter) {
|
|
17
|
+
changed = false;
|
|
18
|
+
iter++;
|
|
19
|
+
// Randomize order
|
|
20
|
+
const shuffledNodes = [...nodes].sort(() => Math.random() - 0.5);
|
|
21
|
+
for (const node of shuffledNodes) {
|
|
22
|
+
const neighbors = graph.getIncomingEdges(node.id).map(e => e.source)
|
|
23
|
+
.concat(graph.getOutgoingEdges(node.id).map(e => e.target));
|
|
24
|
+
if (neighbors.length === 0)
|
|
25
|
+
continue;
|
|
26
|
+
// Count neighbor labels
|
|
27
|
+
const labelCounts = new Map();
|
|
28
|
+
neighbors.forEach(neighborId => {
|
|
29
|
+
const l = labels.get(neighborId);
|
|
30
|
+
if (l)
|
|
31
|
+
labelCounts.set(l, (labelCounts.get(l) || 0) + 1);
|
|
32
|
+
});
|
|
33
|
+
// Find max
|
|
34
|
+
let maxCount = -1;
|
|
35
|
+
let bestLabels = [];
|
|
36
|
+
labelCounts.forEach((count, label) => {
|
|
37
|
+
if (count > maxCount) {
|
|
38
|
+
maxCount = count;
|
|
39
|
+
bestLabels = [label];
|
|
40
|
+
}
|
|
41
|
+
else if (count === maxCount) {
|
|
42
|
+
bestLabels.push(label);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// Pick random best
|
|
46
|
+
if (bestLabels.length > 0) {
|
|
47
|
+
const newLabel = bestLabels[Math.floor(Math.random() * bestLabels.length)];
|
|
48
|
+
if (newLabel !== labels.get(node.id)) {
|
|
49
|
+
labels.set(node.id, newLabel);
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return labels;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.CommunityDetection = CommunityDetection;
|
|
@@ -0,0 +1,110 @@
|
|
|
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.FileLoader = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Service to load files from a directory.
|
|
41
|
+
* 从目录加载文件的服务。
|
|
42
|
+
*/
|
|
43
|
+
class FileLoader {
|
|
44
|
+
/**
|
|
45
|
+
* Recursively reads files from a directory.
|
|
46
|
+
* 递归读取目录中的文件。
|
|
47
|
+
* @param dirPath Directory path | 目录路径
|
|
48
|
+
* @param extensions File extensions to include (e.g., ['.md']) | 要包含的文件扩展名
|
|
49
|
+
*/
|
|
50
|
+
static async loadFiles(dirPath, extensions = ['.md']) {
|
|
51
|
+
const filePaths = [];
|
|
52
|
+
// Check if directory exists
|
|
53
|
+
if (!fs.existsSync(dirPath)) {
|
|
54
|
+
console.warn(`Directory not found: ${dirPath}`);
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
// 1. Gather all file paths first (Sequential directory scan is safer for handles)
|
|
58
|
+
async function scanDir(currentPath) {
|
|
59
|
+
// Use withFileTypes to avoid extra stat calls
|
|
60
|
+
try {
|
|
61
|
+
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
await scanDir(fullPath);
|
|
66
|
+
}
|
|
67
|
+
else if (entry.isFile()) {
|
|
68
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
69
|
+
if (extensions.includes(ext)) {
|
|
70
|
+
filePaths.push(fullPath);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
console.warn(`Failed to read directory: ${currentPath}`, e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
await scanDir(dirPath);
|
|
80
|
+
// 2. Read files with concurrency limit
|
|
81
|
+
const results = [];
|
|
82
|
+
const CONCURRENCY_LIMIT = 100; // Conservative limit
|
|
83
|
+
// Helper to process in batches
|
|
84
|
+
for (let i = 0; i < filePaths.length; i += CONCURRENCY_LIMIT) {
|
|
85
|
+
const batch = filePaths.slice(i, i + CONCURRENCY_LIMIT);
|
|
86
|
+
const batchPromises = batch.map(async (fullPath) => {
|
|
87
|
+
try {
|
|
88
|
+
const content = await fs.promises.readFile(fullPath, 'utf-8');
|
|
89
|
+
const filename = path.basename(fullPath, path.extname(fullPath));
|
|
90
|
+
return {
|
|
91
|
+
filepath: fullPath,
|
|
92
|
+
filename: filename,
|
|
93
|
+
content: content
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
console.warn(`Failed to read file: ${fullPath}`, e);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const batchResults = await Promise.all(batchPromises);
|
|
102
|
+
batchResults.forEach(res => {
|
|
103
|
+
if (res)
|
|
104
|
+
results.push(res);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.FileLoader = FileLoader;
|