1.6.0 mermaid error update

This commit is contained in:
Jacobinwwey 2025-12-26 15:30:07 +08:00
parent 74ba2a8580
commit 9e185e2370
9 changed files with 1325 additions and 68 deletions

View file

@ -251,6 +251,9 @@ Access plugin settings via:
* **On**: Allows you to specify a custom output folder and filename suffix.
#### Batch Mermaid Fix
- **Enable Mermaid Error Detection**:
* **Off (Default)**: Error detection is skipped after processing.
* **On**: Scans processed files for remaining Mermaid syntax errors and generates a `mermaid_error_{foldername}.md` report.
- **Move files with Mermaid errors to specified folder**:
* **Off**: Files with errors remain in place.
* **On**: Moves any files that still contain Mermaid syntax errors after the fix attempt to a dedicated folder for manual review.

View file

@ -296,6 +296,9 @@ Notemd 通过与各种大型语言模型 (LLM) 集成来增强您的 Obsidian
* **开启**: 允许您指定自定义输出文件夹和文件名后缀。
#### 批量Mermaid修复
- **启用Mermaid错误检测**:
* **关闭 (默认)**: 处理后跳过错误检测。
* **开启**: 扫描处理后的文件以查找剩余的Mermaid语法错误并生成 `mermaid_error_{foldername}.md` 报告。
- **将存在Mermaid错误的文件移动到指定文件夹**:
* **关闭**: 有错误的文件保留在原位。
* **开启**: 将修复尝试后仍包含Mermaid语法错误的文件移动到专用文件夹以供手动审查。

1264
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -34,6 +34,9 @@
"obsidian": "latest",
"ts-jest": "^29.3.2",
"tslib": "2.4.0",
"typescript": "4.7.4"
"typescript": "^5.9.3"
},
"dependencies": {
"mermaid": "^11.12.2"
}
}

View file

@ -225,6 +225,7 @@ export const DEFAULT_SETTINGS: NotemdSettings = {
extractOriginalTextCustomSuffix: '_Extracted',
useCustomPromptForExtractOriginalText: false,
customPromptExtractOriginalText: '',
enableMermaidErrorDetection: false,
moveMermaidErrorFiles: false,
mermaidErrorFolderPath: 'MermaidErrors',
};

View file

@ -8,6 +8,7 @@ import { callDeepSeekAPI, callOpenAIApi, callAnthropicApi, callGoogleApi, callMi
import { refineMermaidBlocks, cleanupLatexDelimiters } from './mermaidProcessor'; // Assuming this will be moved or imported correctly later
import { _performResearch } from './searchUtils'; // Assuming this will be moved or imported correctly later
import { showDeletionConfirmationModal } from './ui/modals'; // Assuming this will be moved or imported correctly later
import mermaid from 'mermaid';
// --- Backlink and Note Management ---
@ -969,57 +970,50 @@ export async function batchFixMermaidSyntaxInFolder(app: App, settings: NotemdSe
progressReporter.log(` No changes needed for: ${file.name}`);
}
// Post-fix check for remaining errors
// Read content again (fixMermaidSyntaxInFile might have modified it)
const content = await app.vault.read(file);
let fileErrorCount = 0;
// Simple heuristic regex to find potentially broken nodes: Node[...] where content has space/CJK and NOT quoted
// We reuse the logic from refinedMermaidBlocks but just for detection
const lines = content.split('\n');
let inMermaid = false;
for (const line of lines) {
if (/^```\s*\(?\s*mermaid\s*\)?/.test(line)) { inMermaid = true; continue; }
if (line.trim() === '```') { inMermaid = false; continue; }
if (inMermaid) {
// Check for Node[...] that isn't quoted but should be
// We search for matches that refinedMermaidBlocks WOULD catch but maybe missed or are new?
// Actually, if refineMermaidBlocks worked, these should be fixed.
// The user says "which files still contain Mermaid errors".
// Let's assume if we find any `Node[...]` where `...` contains spaces/CJK and isn't `"... "`, it's an error.
// Regex: (\S+)\[([^"\]]+)\] where group 2 has spaces or CJK
// Exclude matches that look like `Node["..."]` (already handled by [^"])
// But `Node[Label]` is valid if Label is simple.
// Only error if Label has spaces or CJK.
// Also excluding lines with 'subgraph' as they might be handled differently or valid.
if (!line.includes('subgraph')) {
const brokenNodeRegex = /(\S+)\[([^"\]]+)\]/g;
let match;
while ((match = brokenNodeRegex.exec(line)) !== null) {
const content = match[2];
if (/[ \u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]/.test(content)) {
fileErrorCount++;
}
}
}
// Post-fix check for remaining errors (if enabled)
if (settings.enableMermaidErrorDetection) {
// Read content again (fixMermaidSyntaxInFile might have modified it)
const content = await app.vault.read(file);
let fileErrorCount = 0;
// Robust Regex to find mermaid blocks:
// 1. Matches indentation (Start of line + spaces/tabs)
// 2. Matches ``` or ~~~ fences
// 3. Matches 'mermaid' (case insensitive via flags)
// 4. Captures content inside
const mermaidBlockRegex = /^(?:[ \t]*)(?:```|~~~)\s*mermaid\b[^\n]*\n([\s\S]*?)\n(?:[ \t]*)(?:```|~~~)/gim;
let match;
while ((match = mermaidBlockRegex.exec(content)) !== null) {
const blockContent = match[1];
try {
// Use the imported mermaid library to validate syntax
// mermaid.parse throws an error if the code is invalid
await mermaid.parse(blockContent);
} catch (parseErr) {
fileErrorCount++;
// Optional: detailed logging if needed
// console.log(`Mermaid parse error in ${file.name}:`, parseErr);
}
}
}
if (fileErrorCount > 0) {
mermaidErrors.push({ filename: file.name, count: fileErrorCount });
// Move file if enabled
if (errorMoveFolder) {
const destPath = `${errorMoveFolder}/${file.name}`;
if (file.parent?.path !== errorMoveFolder) {
try {
// Check if dest exists
if (app.vault.getAbstractFileByPath(destPath)) {
progressReporter.log(`⚠️ Destination ${destPath} exists. Skipping move for ${file.name}.`);
} else {
await app.vault.rename(file, destPath);
progressReporter.log(`Moved error file to: ${destPath}`);
if (fileErrorCount > 0) {
mermaidErrors.push({ filename: file.name, count: fileErrorCount });
// Move file if enabled
if (errorMoveFolder) {
const destPath = `${errorMoveFolder}/${file.name}`;
if (file.parent?.path !== errorMoveFolder) {
try {
// Check if dest exists
if (app.vault.getAbstractFileByPath(destPath)) {
progressReporter.log(`⚠️ Destination ${destPath} exists. Skipping move for ${file.name}.`);
} else {
await app.vault.rename(file, destPath);
progressReporter.log(`Moved error file to: ${destPath}`);
}
} catch (moveErr: any) {
progressReporter.log(`Error moving file ${file.name}: ${moveErr.message}`);
}
} catch (moveErr: any) {
progressReporter.log(`Error moving file ${file.name}: ${moveErr.message}`);
}
}
}
@ -1047,18 +1041,12 @@ export async function batchFixMermaidSyntaxInFolder(app: App, settings: NotemdSe
}
// Generate Error Report
if (mermaidErrors.length > 0) {
if (settings.enableMermaidErrorDetection && mermaidErrors.length > 0) {
const folderName = folder.name === '/' ? 'Root' : folder.name;
const reportFileName = `mermaid_error_${folderName}.md`;
// Determine save path: current directory implies root or same level? User said "within the current directory".
// Usually, putting it in the root is safest/easiest to find.
const reportContent = mermaidErrors.map(e => `[[${e.filename}]]-[${e.count}]`).join(''); // User requested single line per entry?
// "structured and output as a single line per entry" -> usually means one line in the file per error entry?
// OR "output as a single line per entry" -> [[File]]-[Count]\n[[File]]-[Count]
// Example: `[[First error filename]]-[Number of faulty Mermaid diagrams][[Second error filename]]-[Number of faulty Mermaid diagrams]...`
// Wait, the example shows them ALL on one line concatenated?
// `[[First error filename]]-[Number of faulty Mermaid diagrams][[Second error filename]]-[Number of faulty Mermaid diagrams]...`
// This is unusual but I will follow the example literally: Concatenated string.
// Use triple newline to match the requested format's spacing
const reportContent = mermaidErrors.map(e => `[[${e.filename}]]-[${e.count}]`).join('\n\n\n');
try {
const reportFile = app.vault.getAbstractFileByPath(reportFileName);

View file

@ -97,6 +97,7 @@ export const mockSettings: NotemdSettings = {
extractOriginalTextUseCustomOutput: false,
extractOriginalTextCustomPath: '',
extractOriginalTextCustomSuffix: '',
enableMermaidErrorDetection: false,
moveMermaidErrorFiles: false,
mermaidErrorFolderPath: '',
};

View file

@ -134,6 +134,7 @@ export interface NotemdSettings {
extractOriginalTextCustomSuffix: string; // New
useCustomPromptForExtractOriginalText: boolean;
customPromptExtractOriginalText: string;
enableMermaidErrorDetection: boolean; // New
moveMermaidErrorFiles: boolean; // New
mermaidErrorFolderPath: string; // New
}

View file

@ -712,6 +712,17 @@ export class NotemdSettingTab extends PluginSettingTab {
// --- Batch Mermaid Fix Settings ---
new Setting(containerEl).setName('Batch Mermaid fix').setHeading();
new Setting(containerEl)
.setName('Enable Mermaid Error Detection')
.setDesc('On: Scan files for Mermaid syntax errors after fixing and generate a report. Off: Skip error detection.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enableMermaidErrorDetection)
.onChange(async (value) => {
this.plugin.settings.enableMermaidErrorDetection = value;
await this.plugin.saveSettings();
this.display(); // Refresh to show/hide related settings if needed
}));
new Setting(containerEl)
.setName('Move files with Mermaid errors to specified folder')
.setDesc('On: Move any files that still contain Mermaid errors after fixing to a designated folder.')