Skip to content

Commit f45cc2c

Browse files
feat: add tab support, documentation, and changeset
- Tabs are now expanded to spaces instead of being rejected - Added Box-Drawing Input section to treeView documentation - Created changeset for changelog entry
1 parent 68fc0bc commit f45cc2c

4 files changed

Lines changed: 79 additions & 17 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'mermaid': minor
3+
---
4+
5+
feat: add box-drawing character input support for treeView diagrams
6+
7+
Adds an alternative input syntax for treeView-beta diagrams using box-drawing characters (├──, └──, │). The parser auto-detects box-drawing format and converts it to the standard indent-based representation before parsing. Error messages remap line numbers back to the original input. Includes 41 unit tests and 4 AST equivalence integration tests.

packages/mermaid/src/diagrams/treeView/boxDrawingPreprocessor.spec.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { describe, expect, it } from 'vitest';
22
import {
3-
isBoxDrawingFormat,
4-
preprocessBoxDrawing,
5-
remapErrorLines,
3+
isBoxDrawingFormat,
4+
preprocessBoxDrawing,
5+
remapErrorLines,
66
} from './boxDrawingPreprocessor.js';
77

88
describe('boxDrawingPreprocessor', () => {
@@ -384,12 +384,24 @@ describe('boxDrawingPreprocessor', () => {
384384
);
385385
});
386386

387-
it('should throw on tab characters', () => {
387+
it('should normalize tabs to spaces', () => {
388388
const input = ['treeView-beta', '├──\tfile.txt'].join('\n');
389+
const { text } = preprocessBoxDrawing(input);
390+
const lines = text.split('\n');
391+
expect(lines[1]).toBe(' file.txt');
392+
});
389393

390-
expect(() => preprocessBoxDrawing(input)).toThrow(
391-
'Line 2: Box-drawing format does not support tab characters'
394+
it('should handle tab-indented box-drawing lines', () => {
395+
// Tab expands to 4 spaces, so `\t├──` puts the branch at column 4 → depth 2
396+
const input = ['treeView-beta', '├── src/', '\t├── index.ts', '\t└── utils.ts'].join(
397+
'\n'
392398
);
399+
const { text } = preprocessBoxDrawing(input);
400+
const lines = text.split('\n');
401+
expect(lines[1]).toBe(' src/');
402+
// depth 2 = 8 spaces of indent
403+
expect(lines[2]).toBe(' index.ts');
404+
expect(lines[3]).toBe(' utils.ts');
393405
});
394406

395407
it('should throw on indented line without box chars in box mode', () => {

packages/mermaid/src/diagrams/treeView/boxDrawingPreprocessor.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ export function preprocessBoxDrawing(input: string): PreprocessResult {
9393
if (DECORATION_ONLY.test(line)) {
9494
continue;
9595
}
96-
contentLineTexts.push(line);
96+
// Normalize tabs early so segment-width inference uses consistent column positions
97+
contentLineTexts.push(line.replace(/\t/g, ' '));
9798
}
9899

99100
// If no box-drawing characters found → return unchanged
@@ -150,13 +151,11 @@ export function preprocessBoxDrawing(input: string): PreprocessResult {
150151
continue;
151152
}
152153

153-
// Reject tabs in box-drawing mode
154-
if (line.includes('\t')) {
155-
throw new Error(`Line ${origLineNo}: Box-drawing format does not support tab characters`);
156-
}
154+
// Normalize tabs to spaces for consistent column-position math
155+
const normalized = line.replace(/\t/g, ' ');
157156

158157
// Find branch character (├, └, ┣, ┗)
159-
const branchMatch = BRANCH_CHAR.exec(line);
158+
const branchMatch = BRANCH_CHAR.exec(normalized);
160159

161160
if (branchMatch?.index !== undefined) {
162161
// Has branch char → compute depth from column position
@@ -165,13 +164,13 @@ export function preprocessBoxDrawing(input: string): PreprocessResult {
165164

166165
// Extract content: skip branch char, then dashes, then spaces
167166
let pos = branchCol + 1;
168-
while (pos < line.length && DASH_CHAR.test(line[pos])) {
167+
while (pos < normalized.length && DASH_CHAR.test(normalized[pos])) {
169168
pos++;
170169
}
171-
while (pos < line.length && line[pos] === ' ') {
170+
while (pos < normalized.length && normalized[pos] === ' ') {
172171
pos++;
173172
}
174-
const content = line.slice(pos).trimEnd();
173+
const content = normalized.slice(pos).trimEnd();
175174

176175
if (!content) {
177176
throw new Error(
@@ -183,10 +182,10 @@ export function preprocessBoxDrawing(input: string): PreprocessResult {
183182
outputLines.push(indent + content);
184183
outLineNo++;
185184
lineMap.set(outLineNo, origLineNo);
186-
} else if (ALL_BOX_CHARS.test(line)) {
185+
} else if (ALL_BOX_CHARS.test(normalized)) {
187186
// Has box chars but no branch char — decorative, skip
188187
continue;
189-
} else if (/^\s+/.test(line)) {
188+
} else if (/^\s+/.test(normalized)) {
190189
// Leading whitespace without box chars in box mode → likely mixed format
191190
throw new Error(
192191
`Line ${origLineNo}: Unexpected indentation without box-drawing characters. ` +

packages/mermaid/src/docs/syntax/treeView.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,50 @@ treeView-beta
3030
"file.js"
3131
```
3232

33+
## Box-Drawing Input
34+
35+
As an alternative to indentation, you can use box-drawing characters to define the tree structure. The parser auto-detects the format — no extra keyword or config is needed. This is how most file tree diagrams are drawn already, so you can turn those into diagrams into Mermaid diagrams with very little effort.
36+
37+
Both standard (`├──`, `└──`, ``) and heavy (`┣━━`, `┗━━`, ``) Unicode variants are supported.
38+
39+
```mermaid-example
40+
treeView-beta
41+
├── src/
42+
│ ├── index.ts
43+
│ └── utils.ts
44+
├── package.json
45+
└── README.md
46+
```
47+
48+
All annotations work the same way — just append them after the label:
49+
50+
```mermaid-example
51+
treeView-beta
52+
├── src/
53+
│ ├── App.tsx :::highlight icon(react) ## main component
54+
│ └── index.ts ## entry point
55+
├── .env ## environment variables
56+
├── Dockerfile
57+
└── package.json
58+
```
59+
60+
Depth is inferred from the column position of the branch character, so deeper nesting works naturally:
61+
62+
```mermaid-example
63+
treeView-beta
64+
├── packages/
65+
│ ├── mermaid/
66+
│ │ ├── src/
67+
│ │ │ ├── parser.ts
68+
│ │ │ └── renderer.ts
69+
│ │ └── package.json
70+
│ └── parser/
71+
│ └── src/
72+
└── README.md
73+
```
74+
75+
> **Note:** If a parse error occurs, line numbers in the error message refer to your original input. Tab characters are automatically expanded to spaces.
76+
3377
## Annotations
3478

3579
### Highlighting with :::class

0 commit comments

Comments
 (0)