From 4a095d8cde9f2a75e29baf494cbc8587bfe682ca Mon Sep 17 00:00:00 2001 From: Jacobinwwey Date: Sun, 28 Dec 2025 10:40:00 +0800 Subject: [PATCH] 1.6.0 mermaid fix a lot --- README.md | 4 + README_zh.md | 4 + src/mermaidProcessor.ts | 149 ++++++++++++++++++++++++++++++++++++ src/test.ts | 56 +++++++++++++- src/tests/deepDebug.test.ts | 57 +++++++++++++- 5 files changed, 267 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d1a69bd4..7fb6808a 100644 --- a/README.md +++ b/README.md @@ -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"|`). diff --git a/README_zh.md b/README_zh.md index a235f2cf..5379d7ab 100644 --- a/README_zh.md +++ b/README_zh.md @@ -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`),从而改善图表布局和可读性。现在支持箭头连接(`-->`)和实线连接(`---`)。 - **标准化管道标签**: 自动修复和标准化包含管道符的连接线标签,确保它们被正确引用(例如,将 `-->|文本|` 转换为 `-->|"文本"|`)。 diff --git a/src/mermaidProcessor.ts b/src/mermaidProcessor.ts index ff564280..23bd0a66 100644 --- a/src/mermaidProcessor.ts +++ b/src/mermaidProcessor.ts @@ -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'); +} diff --git a/src/test.ts b/src/test.ts index 9f052fad..652b3fc8 100644 --- a/src/test.ts +++ b/src/test.ts @@ -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]")); \ No newline at end of file +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)); diff --git a/src/tests/deepDebug.test.ts b/src/tests/deepDebug.test.ts index a6a456c6..f316e17f 100644 --- a/src/tests/deepDebug.test.ts +++ b/src/tests/deepDebug.test.ts @@ -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()); }); -}); +}); \ No newline at end of file