@zombie-mermaid/ascii-renderer 3.2.0 → 4.0.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/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1804 -1428
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/ascii-class-box-occupancy.test.ts +30 -30
- package/src/__tests__/ascii-class-cross-level-jog-corruption.test.ts +19 -19
- package/src/__tests__/ascii-class-namespace-1196.test.ts +109 -0
- package/src/__tests__/ascii-class-parent-alignment-971.test.ts +3 -3
- package/src/__tests__/ascii-cluster-entry-1283.test.ts +258 -0
- package/src/__tests__/ascii-cluster-exit-anchoring-1148.test.ts +6 -2
- package/src/__tests__/ascii-multibox-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-rl-direction-1231.test.ts +156 -0
- package/src/__tests__/ascii-subgraph-back-edge-1197.test.ts +10 -18
- package/src/__tests__/ascii-subgraph-frames-1252-1254.test.ts +135 -0
- package/src/__tests__/ascii-subgraph-title-edge-1222.test.ts +51 -42
- package/src/__tests__/ascii-subgraph-title-padding.test.ts +7 -4
- package/src/__tests__/ascii-subgraph-title-room-1278.test.ts +101 -0
- package/src/__tests__/ascii-subgraph-wall-clears-node-1288.test.ts +55 -0
- package/src/__tests__/testdata/ascii/cls_all_relationships.txt +1 -1
- package/src/__tests__/testdata/ascii/cls_annotation.txt +1 -1
- package/src/__tests__/testdata/ascii/cls_basic.txt +9 -9
- package/src/__tests__/testdata/ascii/cls_inheritance.txt +7 -7
- package/src/__tests__/testdata/ascii/cls_methods.txt +2 -2
- package/src/__tests__/testdata/ascii/graph_rl_direction.txt +8 -0
- package/src/__tests__/testdata/ascii/nested_subgraphs_with_labels.txt +35 -25
- package/src/__tests__/testdata/ascii/subgraph_direction_override.txt +21 -21
- package/src/__tests__/testdata/ascii/subgraph_mixed_nodes_td.txt +38 -38
- package/src/__tests__/testdata/unicode/cls_all_relationships.txt +1 -1
- package/src/__tests__/testdata/unicode/cls_annotation.txt +1 -1
- package/src/__tests__/testdata/unicode/cls_basic.txt +9 -9
- package/src/__tests__/testdata/unicode/cls_inheritance.txt +7 -7
- package/src/__tests__/testdata/unicode/cls_methods.txt +2 -2
- package/src/__tests__/testdata/unicode/graph_rl_direction.txt +8 -0
- package/src/canvas.ts +152 -1
- package/src/class-diagram.ts +174 -2
- package/src/cluster-boundary.ts +56 -0
- package/src/converter.ts +9 -1
- package/src/draw-arrows.ts +81 -8
- package/src/draw-subgraphs.ts +33 -22
- package/src/draw.ts +9 -0
- package/src/edge-routing.ts +75 -1
- package/src/flowchart.ts +22 -2
- package/src/frame-avoidance.ts +31 -19
- package/src/grid.ts +261 -6
- package/src/hyperlinks.ts +9 -0
- package/src/types.ts +14 -0
|
@@ -4,12 +4,12 @@ classDiagram
|
|
|
4
4
|
+eat() void
|
|
5
5
|
}
|
|
6
6
|
---
|
|
7
|
-
|
|
8
|
-
| Animal
|
|
9
|
-
|
|
10
|
-
| + name
|
|
11
|
-
|
|
12
|
-
| + eat(): void
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
+---------------+
|
|
8
|
+
| Animal |
|
|
9
|
+
+---------------+
|
|
10
|
+
| + String name |
|
|
11
|
+
+---------------+
|
|
12
|
+
| + eat(): void |
|
|
13
|
+
+---------------+
|
|
14
|
+
|
|
15
|
+
|
|
@@ -3,13 +3,13 @@ classDiagram
|
|
|
3
3
|
Animal : +String name
|
|
4
4
|
Dog : +bark() void
|
|
5
5
|
---
|
|
6
|
-
|
|
7
|
-
| Animal
|
|
8
|
-
|
|
9
|
-
| + name
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
+---------------+
|
|
7
|
+
| Animal |
|
|
8
|
+
+---------------+
|
|
9
|
+
| + String name |
|
|
10
|
+
+---------------+
|
|
11
|
+
^
|
|
12
|
+
--
|
|
13
13
|
|
|
|
14
14
|
+----------------+
|
|
15
15
|
| Dog |
|
|
@@ -10,8 +10,8 @@ classDiagram
|
|
|
10
10
|
+------------------------------+
|
|
11
11
|
| BankAccount |
|
|
12
12
|
+------------------------------+
|
|
13
|
-
| + owner
|
|
14
|
-
| - balance
|
|
13
|
+
| + String owner |
|
|
14
|
+
| - int balance |
|
|
15
15
|
+------------------------------+
|
|
16
16
|
| + deposit(int amount): void |
|
|
17
17
|
| + withdraw(int amount): bool |
|
|
@@ -9,28 +9,38 @@ B
|
|
|
9
9
|
C
|
|
10
10
|
end
|
|
11
11
|
---
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| |
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
12
|
+
+-----------------+
|
|
13
|
+
| Frontend |
|
|
14
|
+
| |
|
|
15
|
+
| |
|
|
16
|
+
| +-------------+ |
|
|
17
|
+
| | | |
|
|
18
|
+
| | Web Server | |
|
|
19
|
+
| | | |
|
|
20
|
+
| +------+------+ |
|
|
21
|
+
| | |
|
|
22
|
+
+--------|--------+
|
|
23
|
+
|
|
|
24
|
+
|
|
|
25
|
+
|
|
|
26
|
+
+--------|--------+
|
|
27
|
+
|Backend | |
|
|
28
|
+
| | |
|
|
29
|
+
| v |
|
|
30
|
+
| +-------------+ |
|
|
31
|
+
| | | |
|
|
32
|
+
| | API Gateway | |
|
|
33
|
+
| | | |
|
|
34
|
+
| +------+------+ |
|
|
35
|
+
| | |
|
|
36
|
+
| | |
|
|
37
|
+
| | |
|
|
38
|
+
| | |
|
|
39
|
+
| v |
|
|
40
|
+
| +-------------+ |
|
|
41
|
+
| | | |
|
|
42
|
+
| | Web Server | |
|
|
43
|
+
| | | |
|
|
44
|
+
| +-------------+ |
|
|
45
|
+
| |
|
|
46
|
+
+-----------------+
|
|
@@ -16,27 +16,27 @@ B --> Y
|
|
|
16
16
|
|
|
|
17
17
|
|
|
|
18
18
|
|
|
|
19
|
-
|
|
20
|
-
| LR
|
|
21
|
-
| |
|
|
22
|
-
| v
|
|
23
|
-
| +---+
|
|
24
|
-
| | |
|
|
25
|
-
| | A |
|
|
26
|
-
| | |
|
|
27
|
-
| +-+-+
|
|
28
|
-
| |
|
|
29
|
-
| |
|
|
30
|
-
| |
|
|
31
|
-
| |
|
|
32
|
-
| v
|
|
33
|
-
| +---+
|
|
34
|
-
| | |
|
|
35
|
-
| | B |
|
|
36
|
-
| | |
|
|
37
|
-
| +-+-+
|
|
38
|
-
| |
|
|
39
|
-
|
|
19
|
+
+---|----------+
|
|
20
|
+
| | LR Group |
|
|
21
|
+
| | |
|
|
22
|
+
| v |
|
|
23
|
+
| +---+ |
|
|
24
|
+
| | | |
|
|
25
|
+
| | A | |
|
|
26
|
+
| | | |
|
|
27
|
+
| +-+-+ |
|
|
28
|
+
| | |
|
|
29
|
+
| | |
|
|
30
|
+
| | |
|
|
31
|
+
| | |
|
|
32
|
+
| v |
|
|
33
|
+
| +---+ |
|
|
34
|
+
| | | |
|
|
35
|
+
| | B | |
|
|
36
|
+
| | | |
|
|
37
|
+
| +-+-+ |
|
|
38
|
+
| | |
|
|
39
|
+
+---|----------+
|
|
40
40
|
|
|
|
41
41
|
|
|
|
42
42
|
v
|
|
@@ -7,42 +7,42 @@ Y
|
|
|
7
7
|
X --> A
|
|
8
8
|
B --> Y
|
|
9
9
|
---
|
|
10
|
-
+---+
|
|
11
|
-
| |
|
|
12
|
-
| X |
|
|
13
|
-
| |
|
|
10
|
+
+---+
|
|
11
|
+
| |
|
|
12
|
+
| X |
|
|
13
|
+
| |
|
|
14
14
|
+-+-+
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
21
|
-
|one|
|
|
22
|
-
| |
|
|
23
|
-
| v
|
|
24
|
-
| +---+
|
|
25
|
-
| | |
|
|
26
|
-
| | A |
|
|
27
|
-
| | |
|
|
28
|
-
| +-+-+
|
|
29
|
-
| |
|
|
30
|
-
| |
|
|
31
|
-
| |
|
|
32
|
-
| |
|
|
33
|
-
| v
|
|
34
|
-
| +---+
|
|
35
|
-
| | |
|
|
36
|
-
| | B |
|
|
37
|
-
| | |
|
|
38
|
-
| +-+-+
|
|
39
|
-
| |
|
|
40
|
-
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
v
|
|
44
|
-
+---+
|
|
45
|
-
| |
|
|
46
|
-
| Y |
|
|
47
|
-
| |
|
|
48
|
-
+---+
|
|
15
|
+
|
|
|
16
|
+
|
|
|
17
|
+
|
|
|
18
|
+
|
|
|
19
|
+
|
|
|
20
|
+
+---|-----+
|
|
21
|
+
| | one |
|
|
22
|
+
| | |
|
|
23
|
+
| v |
|
|
24
|
+
| +---+ |
|
|
25
|
+
| | | |
|
|
26
|
+
| | A | |
|
|
27
|
+
| | | |
|
|
28
|
+
| +-+-+ |
|
|
29
|
+
| | |
|
|
30
|
+
| | |
|
|
31
|
+
| | |
|
|
32
|
+
| | |
|
|
33
|
+
| v |
|
|
34
|
+
| +---+ |
|
|
35
|
+
| | | |
|
|
36
|
+
| | B | |
|
|
37
|
+
| | | |
|
|
38
|
+
| +-+-+ |
|
|
39
|
+
| | |
|
|
40
|
+
+---|-----+
|
|
41
|
+
|
|
|
42
|
+
|
|
|
43
|
+
v
|
|
44
|
+
+---+
|
|
45
|
+
| |
|
|
46
|
+
| Y |
|
|
47
|
+
| |
|
|
48
|
+
+---+
|
|
@@ -4,12 +4,12 @@ classDiagram
|
|
|
4
4
|
+eat() void
|
|
5
5
|
}
|
|
6
6
|
---
|
|
7
|
-
|
|
8
|
-
│ Animal
|
|
9
|
-
|
|
10
|
-
│ + name
|
|
11
|
-
|
|
12
|
-
│ + eat(): void
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
┌───────────────┐
|
|
8
|
+
│ Animal │
|
|
9
|
+
├───────────────┤
|
|
10
|
+
│ + String name │
|
|
11
|
+
├───────────────┤
|
|
12
|
+
│ + eat(): void │
|
|
13
|
+
└───────────────┘
|
|
14
|
+
|
|
15
|
+
|
|
@@ -3,13 +3,13 @@ classDiagram
|
|
|
3
3
|
Animal : +String name
|
|
4
4
|
Dog : +bark() void
|
|
5
5
|
---
|
|
6
|
-
|
|
7
|
-
│ Animal
|
|
8
|
-
|
|
9
|
-
│ + name
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
┌───────────────┐
|
|
7
|
+
│ Animal │
|
|
8
|
+
├───────────────┤
|
|
9
|
+
│ + String name │
|
|
10
|
+
└───────────────┘
|
|
11
|
+
△
|
|
12
|
+
└┐
|
|
13
13
|
│
|
|
14
14
|
┌────────────────┐
|
|
15
15
|
│ Dog │
|
|
@@ -10,8 +10,8 @@ classDiagram
|
|
|
10
10
|
┌──────────────────────────────┐
|
|
11
11
|
│ BankAccount │
|
|
12
12
|
├──────────────────────────────┤
|
|
13
|
-
│ + owner
|
|
14
|
-
│ - balance
|
|
13
|
+
│ + String owner │
|
|
14
|
+
│ - int balance │
|
|
15
15
|
├──────────────────────────────┤
|
|
16
16
|
│ + deposit(int amount): void │
|
|
17
17
|
│ + withdraw(int amount): bool │
|
package/src/canvas.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
ColorMode,
|
|
17
17
|
} from './types.ts'
|
|
18
18
|
import { colorizeLine, DEFAULT_ASCII_THEME } from './ansi.ts'
|
|
19
|
-
import { toDisplayCells } from './display-width.ts'
|
|
19
|
+
import { toDisplayCells, WIDE_CHAR_PLACEHOLDER } from './display-width.ts'
|
|
20
20
|
import { joinWithLinks } from './hyperlinks.ts'
|
|
21
21
|
import type { LinkCanvas } from './hyperlinks.ts'
|
|
22
22
|
|
|
@@ -753,6 +753,157 @@ export function flipRoleCanvasVertically(roleCanvas: RoleCanvas): RoleCanvas {
|
|
|
753
753
|
return roleCanvas
|
|
754
754
|
}
|
|
755
755
|
|
|
756
|
+
// ============================================================================
|
|
757
|
+
// Canvas horizontal flip — used for RL (right-to-left) direction support.
|
|
758
|
+
//
|
|
759
|
+
// RL is laid out as LR and the finished canvas is mirrored left-to-right,
|
|
760
|
+
// remapping glyphs whose meaning depends on the X-axis. The counterpart of the
|
|
761
|
+
// vertical flip above.
|
|
762
|
+
// ============================================================================
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Characters that change meaning when the X-axis is flipped. Symmetric
|
|
766
|
+
* characters (─, │, ┬, ┴, ┼, ╵, ╷) are unchanged.
|
|
767
|
+
*/
|
|
768
|
+
const HORIZONTAL_FLIP_MAP: Record<string, string> = {
|
|
769
|
+
// Arrowheads
|
|
770
|
+
'►': '◄',
|
|
771
|
+
'◄': '►',
|
|
772
|
+
'>': '<',
|
|
773
|
+
'<': '>',
|
|
774
|
+
// Diagonal arrowheads (U+2196-U+2199)
|
|
775
|
+
'↖': '↗',
|
|
776
|
+
'↗': '↖',
|
|
777
|
+
'↙': '↘',
|
|
778
|
+
'↘': '↙',
|
|
779
|
+
// Corners
|
|
780
|
+
'┌': '┐',
|
|
781
|
+
'┐': '┌',
|
|
782
|
+
'└': '┘',
|
|
783
|
+
'┘': '└',
|
|
784
|
+
// Junctions (T-pieces flip horizontally)
|
|
785
|
+
'├': '┤',
|
|
786
|
+
'┤': '├',
|
|
787
|
+
// Rounded corners
|
|
788
|
+
'╭': '╮',
|
|
789
|
+
'╮': '╭',
|
|
790
|
+
'╰': '╯',
|
|
791
|
+
'╯': '╰',
|
|
792
|
+
// Double-line corners and junctions
|
|
793
|
+
'╔': '╗',
|
|
794
|
+
'╗': '╔',
|
|
795
|
+
'╚': '╝',
|
|
796
|
+
'╝': '╚',
|
|
797
|
+
'╟': '╢',
|
|
798
|
+
'╢': '╟',
|
|
799
|
+
// Corner brackets
|
|
800
|
+
'⌜': '⌝',
|
|
801
|
+
'⌝': '⌜',
|
|
802
|
+
'⌞': '⌟',
|
|
803
|
+
'⌟': '⌞',
|
|
804
|
+
// Slanted strokes and triangles
|
|
805
|
+
'╱': '╲',
|
|
806
|
+
'╲': '╱',
|
|
807
|
+
'/': '\\',
|
|
808
|
+
'\\': '/',
|
|
809
|
+
'◢': '◣',
|
|
810
|
+
'◣': '◢',
|
|
811
|
+
'◤': '◥',
|
|
812
|
+
'◥': '◤',
|
|
813
|
+
'◸': '◹',
|
|
814
|
+
'◹': '◸',
|
|
815
|
+
'◺': '◿',
|
|
816
|
+
'◿': '◺',
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Flip the canvas horizontally (mirror across the vertical center).
|
|
821
|
+
* Reverses column order and remaps directional characters so arrows point
|
|
822
|
+
* the other way and corners mirror correctly.
|
|
823
|
+
*
|
|
824
|
+
* Cells `roleCanvas` marks as `'text'` are never remapped: a label containing
|
|
825
|
+
* `<` or `/` is prose, not an arrowhead. Call `mirrorLabelColumns` first so
|
|
826
|
+
* labels keep reading left-to-right.
|
|
827
|
+
*
|
|
828
|
+
* Used to transform an LR-rendered canvas into RL output. Mutates the canvas
|
|
829
|
+
* in place and returns it. `roleCanvas` must still be unflipped when this runs.
|
|
830
|
+
*/
|
|
831
|
+
export function flipCanvasHorizontally(
|
|
832
|
+
canvas: Canvas,
|
|
833
|
+
roleCanvas?: RoleCanvas,
|
|
834
|
+
): Canvas {
|
|
835
|
+
for (const [x, col] of canvas.entries()) {
|
|
836
|
+
for (const [y, ch] of col.entries()) {
|
|
837
|
+
if (roleCanvas?.[x]?.[y] === 'text') continue
|
|
838
|
+
const flipped = HORIZONTAL_FLIP_MAP[ch]
|
|
839
|
+
if (flipped) col[y] = flipped
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
canvas.reverse()
|
|
843
|
+
return canvas
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Pre-compensate labels for a following horizontal flip: within each label
|
|
848
|
+
* rectangle, reflect the `'text'` cells across the rectangle's vertical
|
|
849
|
+
* center, so that after `flipCanvasHorizontally` each label lands at its
|
|
850
|
+
* mirrored position but still reads left-to-right (wide-character placeholder
|
|
851
|
+
* cells keep their order too, since the two reflections cancel). `linkCanvas`
|
|
852
|
+
* is permuted identically. Mutates in place; call before the flips.
|
|
853
|
+
*/
|
|
854
|
+
export function mirrorLabelColumns(
|
|
855
|
+
canvas: Canvas,
|
|
856
|
+
roleCanvas: RoleCanvas,
|
|
857
|
+
labelRects: LabelRect[],
|
|
858
|
+
linkCanvas?: (string | null)[][],
|
|
859
|
+
): void {
|
|
860
|
+
const moves: { cells: [number, number][]; xSum: number }[] = []
|
|
861
|
+
const seen = new Set<string>()
|
|
862
|
+
for (const { x0, y0, x1, y1 } of labelRects) {
|
|
863
|
+
if (x0 === x1) continue
|
|
864
|
+
const cells: [number, number][] = []
|
|
865
|
+
for (let x = x0; x <= x1; x++) {
|
|
866
|
+
for (let y = y0; y <= y1; y++) {
|
|
867
|
+
const key = `${x},${y}`
|
|
868
|
+
// A wide glyph's placeholder cell is '' and carries no role of its
|
|
869
|
+
// own, but it must travel with the glyph it follows.
|
|
870
|
+
const isText =
|
|
871
|
+
roleCanvas[x]?.[y] === 'text' ||
|
|
872
|
+
canvas[x]?.[y] === WIDE_CHAR_PLACEHOLDER
|
|
873
|
+
if (!isText || seen.has(key)) continue
|
|
874
|
+
seen.add(key)
|
|
875
|
+
cells.push([x, y])
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
moves.push({ cells, xSum: x0 + x1 })
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
for (const { cells, xSum } of moves) {
|
|
882
|
+
moveCellsX(canvas, cells, xSum, ' ')
|
|
883
|
+
moveCellsX(roleCanvas, cells, xSum, null)
|
|
884
|
+
if (linkCanvas) moveCellsX(linkCanvas, cells, xSum, null)
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function moveCellsX<T>(
|
|
889
|
+
layer: T[][],
|
|
890
|
+
cells: [number, number][],
|
|
891
|
+
xSum: number,
|
|
892
|
+
empty: T,
|
|
893
|
+
): void {
|
|
894
|
+
const saved = cells.map(([cx, cy]) => layer[cx]![cy]!)
|
|
895
|
+
for (const [cx, cy] of cells) layer[cx]![cy] = empty
|
|
896
|
+
for (const [i, [cx, cy]] of cells.entries()) {
|
|
897
|
+
layer[xSum - cx]![cy] = saved[i]!
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/** Flip the role canvas horizontally to match flipCanvasHorizontally. */
|
|
902
|
+
export function flipRoleCanvasHorizontally(roleCanvas: RoleCanvas): RoleCanvas {
|
|
903
|
+
roleCanvas.reverse()
|
|
904
|
+
return roleCanvas
|
|
905
|
+
}
|
|
906
|
+
|
|
756
907
|
/**
|
|
757
908
|
* Draw text string onto the canvas starting at the given coordinate.
|
|
758
909
|
* By default, preserves existing non-space characters (labels don't overwrite each other).
|