1.6.0 mermaid fix a lot

This commit is contained in:
Jacobinwwey 2025-12-28 10:40:00 +08:00
parent 3e2890b9bb
commit 4a095d8cde
5 changed files with 267 additions and 3 deletions

View file

@ -121,6 +121,10 @@ That's it! Explore the settings to unlock more features like web research, trans
- **Comment Integration**: Automatically merges trailing comments (starting with `%`) into the edge label (e.g., `A -- Label --> B; % Comment` becomes `A -- "Label(Comment)" --> B;`).
- **Malformed Arrows**: Fixes arrows absorbed into quotes (e.g., `A -- "Label -->" B` becomes `A -- "Label" --> B`).
- **Inline Subgraphs**: Converts inline subgraph labels to edge labels.
- **Reverse Arrow Fix**: Corrects non-standard `X <-- Y` arrows to `Y --> X`.
- **Direction Keyword Fix**: Ensures `direction` keyword is lowercase inside subgraphs (e.g., `Direction TB` -> `direction TB`).
- **Comment Conversion**: Converts `//` comments into edge labels (e.g., `A --> B; // Comment` -> `A -- "Comment" --> B;`).
- **Duplicate Label Fix**: Simplifies repeated bracketed labels (e.g., `Node["Label"]["Label"]` -> `Node["Label"]`).
- **Advanced Fix Mode**: Includes robust fixes for unquoted node labels containing spaces, special characters, or nested brackets (e.g., `Node[Label [Text]]` -> `Node["Label [Text]"]`), ensuring compatibility with complex diagrams like Stellar Evolution paths. Also corrects malformed edge labels (e.g., `--["Label["-->` to `-- "Label" -->`). Additionally converts inline comments (`Consensus --> Adaptive; # Some advanced consensus` to `Consensus -- "Some advanced consensus" --> Adaptive`) and fixes incomplete quotes at line ends (`;"` at the end replaced with `"]`).
- **Note Conversion**: Automatically converts `note right/left of` comments into standard Mermaid edge labels (e.g., `note right of A: text` becomes `A -- "text" --> B`), improving graph layout and readability. Now supports both arrow links (`-->`) and solid links (`---`).
- **Standardize Pipe Labels**: Automatically fixes and standardizes edge labels containing pipes, ensuring they are properly quoted (e.g., `-->|Text|` becomes `-->|"Text"|` and `-->|Math|^2|` becomes `-->|"Math|^2"|`).

View file

@ -119,6 +119,10 @@ Notemd 通过与各种大型语言模型 (LLM) 集成来增强您的 Obsidian
- **注释集成**: 自动将尾随注释(以 `%` 开头)合并到连接线标签中(例如,`A -- Label --> B; % Comment` 变为 `A -- "Label(Comment)" --> B;`)。
- **畸形箭头**: 修复被引号吸收的箭头(例如 `A -- "Label -->" B` 修正为 `A -- "Label" --> B`)。
- **行内子图**: 将行内子图标签转换为连接线标签。
- **反向箭头修复**: 将非标准的 `X <-- Y` 箭头修正为 `Y --> X`
- **方向关键字修复**: 确保子图内的 `direction` 关键字为小写(例如 `Direction TB` -> `direction TB`)。
- **注释转换**: 将 `//` 注释转换为连接线标签(例如 `A --> B; // 注释` -> `A -- "注释" --> B;`)。
- **重复标签修复**: 简化重复的括号标签(例如 `Node["标签"]["标签"]` -> `Node["标签"]`)。
- **高级修复模式**: 包含针对包含空格、特殊字符或嵌套括号的未加引号节点标签的稳健修复(例如,将 `Node[标签 [文本]]` 转换为 `Node["标签 [文本]"]`)。
- **注释转换**: 自动将 `note right/left of` 样式的注释转换为标准的 Mermaid 连接线标签(例如,将 `note right of A: text` 转换为 `A -- "text" --> B`),从而改善图表布局和可读性。现在支持箭头连接(`-->`)和实线连接(`---`)。
- **标准化管道标签**: 自动修复和标准化包含管道符的连接线标签,确保它们被正确引用(例如,将 `-->|文本|` 转换为 `-->|"文本"|`)。

View file

@ -313,6 +313,9 @@ export function deepDebugMermaid(content: string): string {
// 7. Fix Mermaid Comments (Move % comments to label)
processed = fixMermaidComments(processed);
// 17. Fix Double Slash Comments (// -> -- "..." -->) - Apply before general label fixes
processed = fixDoubleSlashComments(processed);
// 8. Fix Unquoted Node Labels (Quote labels with special chars)
processed = fixUnquotedNodeLabels(processed);
@ -334,6 +337,15 @@ export function deepDebugMermaid(content: string): string {
// 14. Enhanced Note Pattern and Semicolon Content Removal
processed = enhancedNoteAndSemicolonCleanup(processed);
// 15. Fix Reverse Arrows (<--)
processed = fixReverseArrows(processed);
// 16. Fix Subgraph Direction (Direction -> direction)
processed = fixSubgraphDirection(processed);
// 18. Fix Duplicate Labels (["..."]["..."] -> ["..."])
processed = fixDuplicateLabels(processed);
return processed;
}
@ -1032,3 +1044,140 @@ export function fixSmartQuotes(content: string): string {
return processed;
}
/**
* Fixes reverse arrows `A <-- B` to `B --> A`.
* Supports optional brackets/quotes on nodes.
* Example: `ExSitu["Ex-situ..."] <-- Sample;` -> `Sample --> ExSitu["Ex-situ..."];`
*/
export function fixReverseArrows(content: string): string {
const lines = content.split('\n');
const processedLines = lines.map(line => {
if (!line.includes('<--')) return line;
// Regex to capture:
// Group 1: Left Node (lazy)
// Group 2: <-- (literal)
// Group 3: Right Node (lazy)
// Group 4: Optional Semicolon
const regex = /^(.*?)\s*<--\s*(.*?)(;?)\s*$/;
const match = line.match(regex);
if (match) {
const leftNode = match[1].trim();
const rightNode = match[2].trim();
const semicolon = match[3] || '';
return `${rightNode} --> ${leftNode}${semicolon}`;
}
return line;
});
return processedLines.join('\n');
}
/**
* Fixes `Direction` keyword case in subgraphs.
* Mermaid is case-sensitive inside subgraphs: `Direction TB` -> `direction TB`.
*/
export function fixSubgraphDirection(content: string): string {
const lines = content.split('\n');
let insideSubgraph = false;
const processedLines = lines.map(line => {
if (line.trim().startsWith('subgraph')) {
insideSubgraph = true;
} else if (line.trim() === 'end') {
insideSubgraph = false;
}
if (insideSubgraph) {
// Replace `Direction` with `direction` if followed by TB, BT, LR, RL, TD
return line.replace(/^\s*Direction\s+(TB|BT|LR|RL|TD)\b/g, (match) => {
return match.replace('Direction', 'direction');
});
}
return line;
});
return processedLines.join('\n');
}
/**
* Fixes comments designated by `//` on arrow lines, converting them to edge labels.
* Example: `Thermal --> Optical; // Thermo-optic effect`
* Become: `Thermal -- "Thermo-optic effect" --> Optical;`
*/
export function fixDoubleSlashComments(content: string): string {
const lines = content.split('\n');
const processedLines = lines.map(line => {
if (!line.includes('//') || !line.includes('-->')) return line;
// Regex to find arrow relation followed by // comment
// Handle optional semicolon before //
// Group 1: Pre-arrow part
// Group 2: Arrow (-->)
// Group 3: Post-arrow part (target node)
// Group 4: Semicolon (optional)
// Group 5: Comment text
// This regex assumes standard `-->` arrow.
// We match `//` specifically.
const regex = /^(.*?)(\s*-->\s*)(.*?)(;?)\s*\/\/\s*(.*)$/;
const match = line.match(regex);
if (match) {
const preArrow = match[1]; // "Thermal"
const arrow = match[2]; // " --> "
const target = match[3].trim(); // "Optical"
const semicolon = match[4]; // ";"
const comment = match[5].trim(); // "Thermo-optic effect"
if (comment) {
// Construct: Pre -- "Comment" --> Target;
// Note: arrow variable contains " --> ". We change it to " -- "Comment" --> "
const newArrow = ` -- "${comment}" --> `;
return `${preArrow}${newArrow}${target}${semicolon}`;
}
}
return line;
});
return processedLines.join('\n');
}
/**
* Fixes duplicated bracketed labels.
* Example: `Node["Label"]["Label"]` -> `Node["Label"]`
* Retains only the last occurrence if they differ, or just collapses them.
* The prompt says: "only the final occurrence shall be retained".
*/
export function fixDuplicateLabels(content: string): string {
const lines = content.split('\n');
const processedLines = lines.map(line => {
// Regex to match consecutive quoted brackets: ["..."]["..."]...
// We want to replace the whole sequence with just the last ["..."]
// Strategy: Find a sequence of 2 or more `["..."]` blocks (allowing optional whitespace)
// Replace with the last block.
// Regex for one block: \[\"[^"]*\"\]
const blockRegex = /\["[^"]*"\]/g;
// We iterate line by line.
// If line has multiple blocks adjacent, we check them.
// Harder to do with single regex replace because of "last occurrence".
// Example: `A["1"]["2"]["3"]` -> `A["3"]`
// Regex: ((\[\"[^"]*\"\]\s*)+)
// This captures the whole chain.
return line.replace(/((?:\["[^"]*"\]\s*){2,})/g, (match) => {
// Split match into blocks
const blocks = match.match(/\["[^"]*"\]/g);
if (blocks && blocks.length > 0) {
return blocks[blocks.length - 1];
}
return match;
});
});
return processedLines.join('\n');
}

View file

@ -38,4 +38,58 @@ const input2 = `graph TD
Start[Activated Solid Sample] --> SplitSplit Sample for Multiple Tests`;
const result2 = deepDebugMermaid(input2);
console.log("\nInput 2 Result:\n", result2);
console.log("Test 2 Passed:", result2.trim().includes("Start[Activated Solid Sample] --> Split[Split Sample for Multiple Tests]"));
console.log("Test 2 Passed:", result2.trim().includes("Start[Activated Solid Sample] --> Split[Split Sample for Multiple Tests]"));
console.log("\n--- New Tests ---");
const input3 = `graph TD
subgraph "Experimental Setup: Aging at Constant Pressure"
PressureSource["Pressure Source Pump/Intensifier"] --> PressureControl["Pressure Controller Regulator/Feedback Loop"];
PressureControl --> PressureVessel["High-Pressure Vessel/Chamber"];
PressureSensor["Pressure Sensor"] --> PressureControl;
TempSource["Heating/Cooling System"] --> TempControl["Temperature Controller PID"];
TempControl --> PressureVessel;
TempSensor["Temperature Sensor"] --> TempControl;
Sample["Material Sample"] -- Placed Inside --> PressureVessel;
Sample -- Interaction --> Environment["Controlled Atmosphere/Fluid"];
subgraph "Data Acquisition & Monitoring"
InSitu["In-situ Measurements Optional, e.g., Strain Gauge, AE Sensor"] -- Attached To/Observing --> Sample;
InSitu --> DAQ["Data Acquisition System"];
PressureSensor --> DAQ;
TempSensor --> DAQ;
ExSitu["Ex-situ Characterization Post-test or Interrupted"] <-- Sample;
end
DAQ --> Analysis["Data Analysis & Modeling"];
ExSitu --> Analysis;
end
style PressureVessel fill:#f9f,stroke:#333,stroke-width:2px`;
const result3 = deepDebugMermaid(input3);
console.log("\nInput 3 Result (Reverse Arrow):\n", result3);
const expected3 = `Sample --> ExSitu["Ex-situ Characterization Post-test or Interrupted"];`;
console.log("Test 3 Passed:", result3.includes(expected3));
const input4 = `Thermal --> Optical; // Thermo-optic effect`;
const result4 = deepDebugMermaid(input4);
console.log("\nInput 4 Result (Comments):\n", result4);
const expected4 = `Thermal -- "Thermo-optic effect" --> Optical;`;
console.log("Test 4 Passed:", result4.includes(expected4));
const input5 = `subgraph test
Direction TB
end`;
const result5 = deepDebugMermaid(input5);
console.log("\nInput 5 Result (Direction):\n", result5);
const expected5 = `direction TB`;
console.log("Test 5 Passed:", result5.includes(expected5));
const input6 = `D --> E["Label"]["Label"]["Label"];`;
const result6 = deepDebugMermaid(input6);
console.log("\nInput 6 Result (Duplicate Labels):\n", result6);
const expected6 = `D --> E["Label"];`;
console.log("Test 6 Passed:", result6.includes(expected6));

View file

@ -1,7 +1,60 @@
import { deepDebugMermaid } from '../mermaidProcessor';
describe('Deep Debug Mermaid Tests', () => {
describe('deepDebugMermaid', () => {
test('User Example 1: Reverse Arrows', () => {
const input = `graph TD
subgraph "Experimental Setup: Aging at Constant Pressure"
PressureSource["Pressure Source Pump/Intensifier"] --> PressureControl["Pressure Controller Regulator/Feedback Loop"];
PressureControl --> PressureVessel["High-Pressure Vessel/Chamber"];
PressureSensor["Pressure Sensor"] --> PressureControl;
TempSource["Heating/Cooling System"] --> TempControl["Temperature Controller PID"];
TempControl --> PressureVessel;
TempSensor["Temperature Sensor"] --> TempControl;
Sample["Material Sample"] -- Placed Inside --> PressureVessel;
Sample -- Interaction --> Environment["Controlled Atmosphere/Fluid"];
subgraph "Data Acquisition & Monitoring"
InSitu["In-situ Measurements Optional, e.g., Strain Gauge, AE Sensor"] -- Attached To/Observing --> Sample;
InSitu --> DAQ["Data Acquisition System"];
PressureSensor --> DAQ;
TempSensor --> DAQ;
ExSitu["Ex-situ Characterization Post-test or Interrupted"] <-- Sample;
end
DAQ --> Analysis["Data Analysis & Modeling"];
ExSitu --> Analysis;
end
style PressureVessel fill:#f9f,stroke:#333,stroke-width:2px`;
const result = deepDebugMermaid(input);
const expectedFragment = `Sample --> ExSitu["Ex-situ Characterization Post-test or Interrupted"];`;
expect(result).toContain(expectedFragment);
});
test('User Example 2: Comments //', () => {
const input = `Thermal --> Optical; // Thermo-optic effect`;
const result = deepDebugMermaid(input);
const expected = `Thermal -- "Thermo-optic effect" --> Optical;`;
expect(result).toContain(expected);
});
test('User Example 3: Subgraph Direction', () => {
const input = `subgraph test
Direction TB
end`;
const result = deepDebugMermaid(input);
expect(result).toContain(`direction TB`);
});
test('User Example 4: Duplicate Labels', () => {
const input = `D --> E["Label"]["Label"]["Label"];`;
const result = deepDebugMermaid(input);
expect(result).toContain(`D --> E["Label"];`);
expect(result).not.toContain(`E["Label"]["Label"]`);
});
test('should add brackets to nodes missing them after arrow', () => {
const content = `\
\
@ -500,4 +553,4 @@ style Strehl_Ratio fill:#fcc,stroke:#333`;
expect(deepDebugMermaid(content).trim()).toBe(expected.trim());
});
});
});