zombie-mermaid 2.2.1 → 2.2.2
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/LICENSE +1 -0
- package/README.md +7 -2
- package/dist/ascii.cjs +9 -9
- package/dist/ascii.cjs.map +1 -1
- package/dist/ascii.js +4069 -3969
- package/dist/ascii.js.map +1 -1
- package/dist/cli.js +7605 -7381
- package/dist/index.cjs +52 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7613 -7459
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +14142 -13717
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +14142 -13717
- package/dist/mcp.js.map +1 -1
- package/package.json +27 -5
- package/packages/ascii-renderer/LICENSE +22 -0
- package/{src/ascii → packages/ascii-renderer/src}/class-diagram.ts +277 -57
- package/{src/ascii → packages/ascii-renderer/src}/er-diagram.ts +2 -2
- package/packages/ascii-renderer/src/flowchart.ts +94 -0
- package/{src/ascii → packages/ascii-renderer/src}/grid.ts +23 -0
- package/{src/ascii → packages/ascii-renderer/src}/index.ts +13 -84
- package/{src/ascii → packages/ascii-renderer/src}/registry.ts +23 -12
- package/{src/ascii → packages/ascii-renderer/src}/sequence.ts +55 -8
- package/{src/ascii → packages/ascii-renderer/src}/xychart.ts +6 -3
- package/packages/core/LICENSE +22 -0
- package/packages/core/src/click-directive.ts +106 -0
- package/packages/core/src/color-utils.ts +205 -0
- package/packages/core/src/diagram-type.ts +35 -0
- package/packages/core/src/direction-override.ts +46 -0
- package/packages/core/src/direction.ts +56 -0
- package/packages/core/src/index.ts +34 -0
- package/packages/core/src/init-directive.ts +248 -0
- package/packages/core/src/multiline-utils.ts +275 -0
- package/packages/core/src/statements.ts +197 -0
- package/packages/core/src/style-directives.ts +214 -0
- package/packages/core/src/text-metrics.ts +498 -0
- package/packages/core/src/theme.ts +696 -0
- package/packages/core/src/types.ts +670 -0
- package/packages/mcp/LICENSE +22 -0
- package/{src/mcp → packages/mcp/src}/server.ts +34 -2
- package/{src/mcp → packages/mcp/src}/tools/check-sequence-activations.ts +8 -5
- package/packages/mcp/src/tools/fix-sequence-activations.ts +77 -0
- package/{src/mcp → packages/mcp/src}/tools/render-ascii.ts +2 -2
- package/{src/mcp → packages/mcp/src}/tools/render-svg.ts +1 -1
- package/packages/mermaid-parser/LICENSE +22 -0
- package/{src → packages/mermaid-parser/src}/class/parser.ts +86 -8
- package/{src → packages/mermaid-parser/src}/er/parser.ts +59 -26
- package/packages/mermaid-parser/src/index.ts +48 -0
- package/{src → packages/mermaid-parser/src}/sequence/activation-check.ts +4 -3
- package/packages/mermaid-parser/src/sequence/activation-fix.ts +112 -0
- package/{src → packages/mermaid-parser/src}/sequence/parser.ts +74 -16
- package/{src → packages/mermaid-parser/src}/xychart/parser.ts +84 -6
- package/packages/svg-renderer/LICENSE +22 -0
- package/{src → packages/svg-renderer/src}/class/layout.ts +8 -4
- package/{src → packages/svg-renderer/src}/class/renderer.ts +7 -6
- package/packages/svg-renderer/src/edge-curves.ts +204 -0
- package/packages/svg-renderer/src/elk-instance.ts +292 -0
- package/{src → packages/svg-renderer/src}/er/layout.ts +6 -4
- package/{src → packages/svg-renderer/src}/er/renderer.ts +4 -4
- package/packages/svg-renderer/src/index.ts +58 -0
- package/packages/svg-renderer/src/layout-engine/constants.ts +19 -0
- package/packages/svg-renderer/src/layout-engine/edge-bundling.ts +379 -0
- package/packages/svg-renderer/src/layout-engine/elk-adapter-utils.ts +81 -0
- package/packages/svg-renderer/src/layout-engine/elk-graph-builder.ts +240 -0
- package/packages/svg-renderer/src/layout-engine/from-elk.ts +685 -0
- package/packages/svg-renderer/src/layout-engine/layer-alignment.ts +174 -0
- package/packages/svg-renderer/src/layout-engine/to-elk.ts +695 -0
- package/packages/svg-renderer/src/layout-engine.ts +74 -0
- package/packages/svg-renderer/src/layout.ts +8 -0
- package/packages/svg-renderer/src/renderer.ts +1485 -0
- package/packages/svg-renderer/src/resolve-colors.ts +339 -0
- package/{src → packages/svg-renderer/src}/sequence/layout.ts +2 -6
- package/{src → packages/svg-renderer/src}/sequence/renderer.ts +4 -4
- package/packages/svg-renderer/src/shape-clipping.ts +197 -0
- package/packages/svg-renderer/src/styles.ts +118 -0
- package/{src → packages/svg-renderer/src}/xychart/layout.ts +2 -2
- package/{src → packages/svg-renderer/src}/xychart/renderer.ts +6 -6
- package/src/__tests__/ascii-ansi.test.ts +6 -2
- package/src/__tests__/ascii-canvas-write.test.ts +2 -2
- package/src/__tests__/ascii-charset-border-junctions.test.ts +1 -1
- package/src/__tests__/ascii-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-class-box-occupancy.test.ts +147 -45
- package/src/__tests__/ascii-class-column-width-488-489.test.ts +1 -1
- package/src/__tests__/ascii-class-cross-level-jog-corruption.test.ts +187 -0
- package/src/__tests__/ascii-class-detour-label-routing-487.test.ts +1 -1
- package/src/__tests__/ascii-class-diagram-compartments.test.ts +1 -1
- package/src/__tests__/ascii-class-label-box-collision.test.ts +1 -1
- package/src/__tests__/ascii-class-label-row-collision-531.test.ts +1 -1
- package/src/__tests__/ascii-class-label-territory-row-awareness.test.ts +1 -1
- package/src/__tests__/ascii-class-padding.test.ts +1 -1
- package/src/__tests__/ascii-class-reciprocal-relationships-448.test.ts +86 -1
- package/src/__tests__/ascii-combining-mark-width.test.ts +2 -2
- package/src/__tests__/ascii-coords-overlay.test.ts +2 -2
- package/src/__tests__/ascii-decision-lr-box-start.test.ts +2 -2
- package/src/__tests__/ascii-display-width-unit.test.ts +1 -1
- package/src/__tests__/ascii-draw-arrows-coverage.test.ts +1 -1
- package/src/__tests__/ascii-draw-arrows-single-point-path.test.ts +12 -5
- package/src/__tests__/ascii-draw-bundles.test.ts +5 -5
- package/src/__tests__/ascii-edge-bundling-rank-violation-454.test.ts +1 -1
- package/src/__tests__/ascii-edge-bundling-routing.test.ts +9 -5
- package/src/__tests__/ascii-edge-cross-style-overlap.test.ts +10 -6
- package/src/__tests__/ascii-edge-ending-glyphs.test.ts +1 -1
- package/src/__tests__/ascii-edge-label-diagonal-fallback-418.test.ts +2 -2
- package/src/__tests__/ascii-edge-routing-fixes.test.ts +1 -1
- package/src/__tests__/ascii-edge-routing-single-point-path.test.ts +8 -4
- package/src/__tests__/ascii-edge-styles.test.ts +1 -1
- package/src/__tests__/ascii-emoji-cluster-width.test.ts +2 -2
- package/src/__tests__/ascii-er-box-occupancy.test.ts +1 -1
- package/src/__tests__/ascii-er-cardinality.test.ts +1 -1
- package/src/__tests__/ascii-er-corner-glyphs.test.ts +1 -1
- package/src/__tests__/ascii-er-jog-stray-line.test.ts +1 -1
- package/src/__tests__/ascii-er-label-padding.test.ts +1 -1
- package/src/__tests__/ascii-er-padding.test.ts +1 -1
- package/src/__tests__/ascii-er-relationship-label-corruption-350.test.ts +1 -1
- package/src/__tests__/ascii-er-relationship-overwrite.test.ts +1 -1
- package/src/__tests__/ascii-er-stray-connectors.test.ts +7 -4
- package/src/__tests__/ascii-er-unrelated-stem-separation-411.test.ts +1 -1
- package/src/__tests__/ascii-er-vertical-one-marker.test.ts +1 -1
- package/src/__tests__/ascii-grid-deep-chain.test.ts +4 -4
- package/src/__tests__/ascii-hyperlinks.test.ts +9 -6
- package/src/__tests__/ascii-label-line-terminal-fallback.test.ts +8 -4
- package/src/__tests__/ascii-lane-search.test.ts +1 -1
- package/src/__tests__/ascii-multibox-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-multiline.test.ts +6 -3
- package/src/__tests__/ascii-nested-subgraph-sibling-order-444.test.ts +8 -5
- package/src/__tests__/ascii-package-boundary.test.ts +47 -37
- package/src/__tests__/ascii-padding-edge-cases.test.ts +1 -1
- package/src/__tests__/ascii-parallel-edges-329.test.ts +8 -5
- package/src/__tests__/ascii-pathfinder-route-edge.test.ts +14 -4
- package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +9 -5
- package/src/__tests__/ascii-sequence-alt-else-label.test.ts +1 -1
- package/src/__tests__/ascii-sequence-block-wall-clearance.test.ts +1 -1
- package/src/__tests__/ascii-sequence-box-group.test.ts +1 -1
- package/src/__tests__/ascii-sequence-cjk-width.test.ts +1 -1
- package/src/__tests__/ascii-sequence-create-destroy.test.ts +1 -1
- package/src/__tests__/ascii-sequence-form-invariants.test.ts +48 -50
- package/src/__tests__/ascii-sequence-mermaid-parity.test.ts +1 -1
- package/src/__tests__/ascii-sequence-notes.test.ts +1 -1
- package/src/__tests__/ascii-sequence-padding.test.ts +1 -1
- package/src/__tests__/ascii-sequence-self-arrow.test.ts +1 -1
- package/src/__tests__/ascii-shape-diamond.test.ts +3 -3
- package/src/__tests__/ascii-shape-rectangle.test.ts +5 -5
- package/src/__tests__/ascii-shape-rounded.test.ts +3 -3
- package/src/__tests__/ascii-shapes-circle.test.ts +3 -3
- package/src/__tests__/ascii-shapes-hexagon.test.ts +3 -3
- package/src/__tests__/ascii-shapes-special.test.ts +10 -5
- package/src/__tests__/ascii-shapes-stadium.test.ts +3 -3
- package/src/__tests__/ascii-shapes-state.test.ts +15 -5
- package/src/__tests__/ascii-state-bidirectional-label-swap-530.test.ts +1 -1
- package/src/__tests__/ascii-subgraph-bbox.test.ts +8 -4
- package/src/__tests__/ascii-subgraph-direction-honored-445.test.ts +1 -1
- package/src/__tests__/ascii-subgraph-label-border-clip.test.ts +88 -25
- package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +8 -4
- package/src/__tests__/ascii-subgraph-title-padding.test.ts +1 -1
- package/src/__tests__/ascii-territory-unit.test.ts +2 -2
- package/src/__tests__/ascii-validate.test.ts +1 -1
- package/src/__tests__/ascii.test.ts +5 -2
- package/src/__tests__/box-color.test.ts +1 -1
- package/src/__tests__/class-arrow-directions.test.ts +1 -1
- package/src/__tests__/class-click-interactions.test.ts +3 -6
- package/src/__tests__/class-integration.test.ts +1 -1
- package/src/__tests__/class-notes.test.ts +3 -3
- package/src/__tests__/class-parser-error-messages.test.ts +162 -0
- package/src/__tests__/class-parser.test.ts +6 -6
- package/src/__tests__/class-styling.test.ts +2 -2
- package/src/__tests__/cli-render.test.ts +16 -1
- package/src/__tests__/direction-override.test.ts +1 -1
- package/src/__tests__/draw-lines.test.ts +2 -2
- package/src/__tests__/edge-cell-styles.test.ts +5 -2
- package/src/__tests__/elk-layout-cache.test.ts +6 -4
- package/src/__tests__/er-click-unsupported.test.ts +5 -8
- package/src/__tests__/er-parser-error-messages.test.ts +82 -0
- package/src/__tests__/er-parser.test.ts +3 -6
- package/src/__tests__/expanded-shapes.test.ts +1 -1
- package/src/__tests__/grid-occupancy.test.ts +1 -1
- package/src/__tests__/mcp-check-sequence-activations-tool.test.ts +1 -1
- package/src/__tests__/mcp-fix-sequence-activations-tool.test.ts +134 -0
- package/src/__tests__/mcp-render-ascii-tool.test.ts +5 -4
- package/src/__tests__/mcp-render-svg-theme-guard.test.ts +3 -3
- package/src/__tests__/mcp-render-svg-tool.test.ts +1 -1
- package/src/__tests__/mcp-server.test.ts +30 -1
- package/src/__tests__/multiline-labels.test.ts +29 -18
- package/src/__tests__/parser.test.ts +117 -5
- package/src/__tests__/pathfinder.test.ts +11 -4
- package/src/__tests__/render-options-overrides.test.ts +28 -21
- package/src/__tests__/render-options-per-type.test.ts +13 -9
- package/src/__tests__/resolve-colors.test.ts +1 -1
- package/src/__tests__/self-loop-edges.test.ts +112 -0
- package/src/__tests__/sequence-activation-check.test.ts +7 -8
- package/src/__tests__/sequence-activation-fix.test.ts +91 -0
- package/src/__tests__/sequence-layout.test.ts +4 -7
- package/src/__tests__/sequence-parser-syntax-errors.test.ts +179 -0
- package/src/__tests__/sequence-parser.test.ts +3 -6
- package/src/__tests__/statements.test.ts +53 -35
- package/src/__tests__/testdata/ascii/subgraph_direction_override.txt +21 -21
- package/src/__tests__/theme-palette-contrast.test.ts +84 -0
- package/src/__tests__/workspace-package-boundaries.test.ts +199 -25
- package/src/__tests__/xychart-ascii.test.ts +1 -1
- package/src/__tests__/xychart-colors.test.ts +3 -2
- package/src/__tests__/xychart-layout.test.ts +1 -1
- package/src/__tests__/xychart-parser-errors.test.ts +95 -0
- package/src/browser.ts +8 -2
- package/src/cli/mcp.ts +1 -1
- package/src/cli/render.ts +59 -25
- package/src/cli/web.ts +1 -1
- package/src/diagram-registry.ts +190 -25
- package/src/index.ts +34 -131
- package/src/parser.ts +102 -40
- /package/{src/ascii → packages/ascii-renderer/src}/ansi.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/canvas.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/converter.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/coords.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/display-width.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-arrows.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-boxes.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-bundles.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-lines.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw-subgraphs.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/draw.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/edge-bundling.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/edge-cell-styles.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/edge-routing.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/grid-occupancy.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/hyperlinks.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/lane-search.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/multiline-utils.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/pathfinder.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/circle.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/corners.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/diamond.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/hexagon.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/index.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/rectangle.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/rounded.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/special.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/stadium.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/state.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/shapes/types.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/territory.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/types.ts +0 -0
- /package/{src/ascii → packages/ascii-renderer/src}/validate.ts +0 -0
- /package/{src/mcp → packages/mcp/src}/index.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/class/format.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/class/types.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/er/types.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/expanded-shapes.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/sequence/box-color.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/sequence/types.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/xychart/colors.ts +0 -0
- /package/{src → packages/mermaid-parser/src}/xychart/types.ts +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Sequence diagram — activation/deactivation auto-fix
|
|
3
|
+
//
|
|
4
|
+
// Extends activation-check.ts (issue #539) with the "optionally proposes a
|
|
5
|
+
// fix" half of that issue's scope. Deliberately narrow: only
|
|
6
|
+
// `DANGLING_ACTIVATION` (an `activate X` never closed) is auto-fixable —
|
|
7
|
+
// the mechanical, unambiguous repair is to append a matching `deactivate X`
|
|
8
|
+
// after the diagram's last statement, which is always syntactically valid
|
|
9
|
+
// regardless of block nesting (Mermaid's `deactivate` isn't block-scoped).
|
|
10
|
+
//
|
|
11
|
+
// `UNMATCHED_DEACTIVATION` (a `deactivate X` with nothing open) is
|
|
12
|
+
// deliberately NOT auto-fixed: fixing it means either deleting that
|
|
13
|
+
// specific statement or inserting a preceding `activate X`, and this
|
|
14
|
+
// package doesn't track source line/position per activation statement (see
|
|
15
|
+
// SequenceDiagram.activations — only a message-index `afterIndex`, no raw
|
|
16
|
+
// line offset), so a text-level edit could not target the right occurrence
|
|
17
|
+
// with confidence when the same statement text repeats. Rather than guess
|
|
18
|
+
// and risk mangling unrelated source, this case is reported back as a
|
|
19
|
+
// remaining (unfixable) issue for a human — or a fuzzier LLM-based layer,
|
|
20
|
+
// per the issue's own caveat about unvalidated frontier-model behavior — to
|
|
21
|
+
// resolve.
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
import { detectDiagramType, splitStatements } from '@zombie-mermaid/core'
|
|
25
|
+
import { parseSequenceDiagram } from './parser.ts'
|
|
26
|
+
import {
|
|
27
|
+
checkActivationBalance,
|
|
28
|
+
type ActivationIssue,
|
|
29
|
+
} from './activation-check.ts'
|
|
30
|
+
|
|
31
|
+
export interface ActivationFixResult {
|
|
32
|
+
/** true when the returned diagram has no remaining activation issues. */
|
|
33
|
+
ok: boolean
|
|
34
|
+
/** Original diagram, unchanged if there was nothing fixable. */
|
|
35
|
+
originalDiagram: string
|
|
36
|
+
/**
|
|
37
|
+
* Diagram with a "deactivate X" statement appended for every
|
|
38
|
+
* DANGLING_ACTIVATION issue found. Identical to `originalDiagram` when
|
|
39
|
+
* there was nothing to fix.
|
|
40
|
+
*/
|
|
41
|
+
fixedDiagram: string
|
|
42
|
+
/** Human-readable description of each fix actually applied. */
|
|
43
|
+
fixesApplied: string[]
|
|
44
|
+
/**
|
|
45
|
+
* Issues that could not be auto-fixed (currently always
|
|
46
|
+
* UNMATCHED_DEACTIVATION — see module header). Empty when `ok` is true.
|
|
47
|
+
*/
|
|
48
|
+
remainingIssues: ActivationIssue[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check a Mermaid sequence diagram for activation/deactivation imbalance
|
|
53
|
+
* and, where mechanically safe, return a corrected version.
|
|
54
|
+
*
|
|
55
|
+
* Throws the same way `parseSequenceDiagram`/`detectDiagramType` do on
|
|
56
|
+
* invalid or non-sequence input — callers (e.g. the MCP tool handler) are
|
|
57
|
+
* expected to catch and translate, matching `check-sequence-activations.ts`'s
|
|
58
|
+
* own error handling.
|
|
59
|
+
*/
|
|
60
|
+
export function fixActivationBalance(
|
|
61
|
+
sourceDiagram: string,
|
|
62
|
+
): ActivationFixResult {
|
|
63
|
+
const diagramType = detectDiagramType(sourceDiagram)
|
|
64
|
+
if (diagramType !== 'sequence') {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'fixActivationBalance only supports sequence diagrams (source must ' +
|
|
67
|
+
`start with "sequenceDiagram"); detected diagram type: "${diagramType}".`,
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const lines = splitStatements(sourceDiagram)
|
|
72
|
+
const diagram = parseSequenceDiagram(lines)
|
|
73
|
+
const result = checkActivationBalance(diagram)
|
|
74
|
+
|
|
75
|
+
if (result.ok) {
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
originalDiagram: sourceDiagram,
|
|
79
|
+
fixedDiagram: sourceDiagram,
|
|
80
|
+
fixesApplied: [],
|
|
81
|
+
remainingIssues: [],
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const dangling = result.issues.filter(
|
|
86
|
+
(issue) => issue.code === 'DANGLING_ACTIVATION',
|
|
87
|
+
)
|
|
88
|
+
const remainingIssues = result.issues.filter(
|
|
89
|
+
(issue) => issue.code !== 'DANGLING_ACTIVATION',
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const appended = dangling
|
|
93
|
+
.map((issue) => ` deactivate ${issue.actorId}`)
|
|
94
|
+
.join('\n')
|
|
95
|
+
const fixedDiagram =
|
|
96
|
+
dangling.length === 0
|
|
97
|
+
? sourceDiagram
|
|
98
|
+
: `${sourceDiagram.replace(/\s+$/, '')}\n${appended}`
|
|
99
|
+
|
|
100
|
+
const fixesApplied = dangling.map(
|
|
101
|
+
(issue) =>
|
|
102
|
+
`Appended "deactivate ${issue.actorId}" to close: ${issue.message}`,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
ok: remainingIssues.length === 0,
|
|
107
|
+
originalDiagram: sourceDiagram,
|
|
108
|
+
fixedDiagram,
|
|
109
|
+
fixesApplied,
|
|
110
|
+
remainingIssues,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SequenceDiagram, Message, Block, Actor } from './types.ts'
|
|
2
|
-
import { normalizeBrTags } from '@zombie-mermaid/core'
|
|
2
|
+
import { normalizeBrTags, type Statement } from '@zombie-mermaid/core'
|
|
3
3
|
import { parseBoxHeader } from './box-color.ts'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -120,7 +120,7 @@ const MESSAGE_ANY_ARROW_RE =
|
|
|
120
120
|
// any of these guarantees, but removing the `!` would only replace a
|
|
121
121
|
// proven-safe assertion with an unreachable guard. Left as-is; no
|
|
122
122
|
// behavior change.
|
|
123
|
-
export function parseSequenceDiagram(lines:
|
|
123
|
+
export function parseSequenceDiagram(lines: Statement[]): SequenceDiagram {
|
|
124
124
|
const diagram: SequenceDiagram = {
|
|
125
125
|
actors: [],
|
|
126
126
|
messages: [],
|
|
@@ -136,6 +136,9 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
136
136
|
// only mean the box — but this parser is lenient about other statements,
|
|
137
137
|
// and a `loop` opened inside a box must still own the next `end`.
|
|
138
138
|
let boxOpenedAtDepth = 0
|
|
139
|
+
// Line the currently-open box started on, for the unclosed-box error at
|
|
140
|
+
// EOF below (issue #762) — `boxCtx.open` alone doesn't carry position.
|
|
141
|
+
let boxOpenLine: number | undefined
|
|
139
142
|
|
|
140
143
|
// Track actor IDs to auto-create actors referenced in messages
|
|
141
144
|
const actorIds = new Set<string>()
|
|
@@ -145,6 +148,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
145
148
|
label: string
|
|
146
149
|
startIndex: number
|
|
147
150
|
dividers: Block['dividers']
|
|
151
|
+
line: number
|
|
148
152
|
}> = []
|
|
149
153
|
|
|
150
154
|
// `autonumber` state — a bare `autonumber` turns numbering on starting at 1
|
|
@@ -163,7 +167,8 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
163
167
|
let pendingDestroy: string | undefined
|
|
164
168
|
|
|
165
169
|
for (let i = 1; i < lines.length; i++) {
|
|
166
|
-
const
|
|
170
|
+
const stmt = lines[i]!
|
|
171
|
+
const line = stmt.text
|
|
167
172
|
|
|
168
173
|
// --- box <color?> <label?> ---
|
|
169
174
|
// Opens a participant group; closed by `end`. Boxes cannot nest
|
|
@@ -173,7 +178,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
173
178
|
if (boxMatch) {
|
|
174
179
|
if (boxCtx.open !== undefined) {
|
|
175
180
|
throw new Error(
|
|
176
|
-
|
|
181
|
+
`Line ${stmt.line}: Sequence diagram: a box cannot be nested inside another box — close the open box with "end" first`,
|
|
177
182
|
)
|
|
178
183
|
}
|
|
179
184
|
const { color, label } = parseBoxHeader(boxMatch[1] ?? '')
|
|
@@ -185,6 +190,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
185
190
|
diagram.boxes.push(box)
|
|
186
191
|
boxCtx.open = diagram.boxes.length - 1
|
|
187
192
|
boxOpenedAtDepth = blockStack.length
|
|
193
|
+
boxOpenLine = stmt.line
|
|
188
194
|
continue
|
|
189
195
|
}
|
|
190
196
|
|
|
@@ -202,7 +208,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
202
208
|
if (actorIds.has(id)) {
|
|
203
209
|
// Mermaid's own wording (sequenceDb `createParticipant`).
|
|
204
210
|
throw new Error(
|
|
205
|
-
|
|
211
|
+
`Line ${stmt.line}: It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior`,
|
|
206
212
|
)
|
|
207
213
|
}
|
|
208
214
|
actorIds.add(id)
|
|
@@ -211,7 +217,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
211
217
|
label: normalizeBrTags(createMatch[3]?.trim() ?? id),
|
|
212
218
|
type,
|
|
213
219
|
})
|
|
214
|
-
joinOpenBox(diagram, boxCtx, id)
|
|
220
|
+
joinOpenBox(diagram, boxCtx, id, stmt.line)
|
|
215
221
|
pendingCreate = id
|
|
216
222
|
continue
|
|
217
223
|
}
|
|
@@ -221,7 +227,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
221
227
|
const destroyMatch = line.match(/^destroy\s+(.+)$/)
|
|
222
228
|
if (destroyMatch) {
|
|
223
229
|
const id = destroyMatch[1]!.trim()
|
|
224
|
-
ensureActor(diagram, actorIds, boxCtx, id)
|
|
230
|
+
ensureActor(diagram, actorIds, boxCtx, id, stmt.line)
|
|
225
231
|
pendingDestroy = id
|
|
226
232
|
continue
|
|
227
233
|
}
|
|
@@ -262,7 +268,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
262
268
|
}
|
|
263
269
|
// A re-declaration inside a box joins it (or errors if it already
|
|
264
270
|
// belongs to a different one) — Mermaid's `addActor` rule.
|
|
265
|
-
joinOpenBox(diagram, boxCtx, id)
|
|
271
|
+
joinOpenBox(diagram, boxCtx, id, stmt.line)
|
|
266
272
|
continue
|
|
267
273
|
}
|
|
268
274
|
|
|
@@ -279,7 +285,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
279
285
|
|
|
280
286
|
// Ensure actors exist
|
|
281
287
|
for (const aid of noteActorIds) {
|
|
282
|
-
ensureActor(diagram, actorIds, boxCtx, aid)
|
|
288
|
+
ensureActor(diagram, actorIds, boxCtx, aid, stmt.line)
|
|
283
289
|
}
|
|
284
290
|
|
|
285
291
|
let position: 'left' | 'right' | 'over' = 'over'
|
|
@@ -308,6 +314,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
308
314
|
label,
|
|
309
315
|
startIndex: diagram.messages.length,
|
|
310
316
|
dividers: [],
|
|
317
|
+
line: stmt.line,
|
|
311
318
|
})
|
|
312
319
|
continue
|
|
313
320
|
}
|
|
@@ -333,6 +340,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
333
340
|
blockStack.length === boxOpenedAtDepth
|
|
334
341
|
) {
|
|
335
342
|
boxCtx.open = undefined
|
|
343
|
+
boxOpenLine = undefined
|
|
336
344
|
continue
|
|
337
345
|
}
|
|
338
346
|
|
|
@@ -349,6 +357,17 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
349
357
|
continue
|
|
350
358
|
}
|
|
351
359
|
|
|
360
|
+
// --- Unmatched end ---
|
|
361
|
+
// Neither branch above claimed this "end" — no box and no block is
|
|
362
|
+
// currently open, so there's nothing for it to close. Previously
|
|
363
|
+
// silently ignored (#762); mirrors the "box cannot be nested" throw
|
|
364
|
+
// above in surfacing a structural mistake instead of dropping it.
|
|
365
|
+
if (line === 'end') {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`Line ${stmt.line}: Sequence diagram: "end" does not match any open block ("loop"/"alt"/"opt"/"par"/"critical"/"break"/"rect") or "box" — nothing is currently open to close.`,
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
|
|
352
371
|
// --- Standalone activate / deactivate ---
|
|
353
372
|
// "activate A" / "deactivate A". Mermaid's own grammar expands the `+`/`-`
|
|
354
373
|
// arrow shorthand into exactly this (message, then an activeStart for the
|
|
@@ -361,7 +380,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
361
380
|
const activationMatch = line.match(/^(activate|deactivate)\s+(.+)$/)
|
|
362
381
|
if (activationMatch) {
|
|
363
382
|
const actorId = activationMatch[2]!.trim()
|
|
364
|
-
ensureActor(diagram, actorIds, boxCtx, actorId)
|
|
383
|
+
ensureActor(diagram, actorIds, boxCtx, actorId, stmt.line)
|
|
365
384
|
diagram.activations.push({
|
|
366
385
|
actorId,
|
|
367
386
|
kind: activationMatch[1] === 'activate' ? 'start' : 'end',
|
|
@@ -403,6 +422,22 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
403
422
|
const msgMatch =
|
|
404
423
|
line.match(MESSAGE_LONG_ARROW_RE) ?? line.match(MESSAGE_ANY_ARROW_RE)
|
|
405
424
|
if (msgMatch) {
|
|
425
|
+
// Mermaid's own unquoted actor-name grammar excludes `>` and `)` from
|
|
426
|
+
// ever starting an identifier (see the FROM/TO comment above) — so a
|
|
427
|
+
// TO capture starting with either one means the real arrow in the
|
|
428
|
+
// source is longer than what the alternation above actually matched
|
|
429
|
+
// (e.g. "->>>" only matches as "->>", leaving a stray ">" to be
|
|
430
|
+
// absorbed into TO as part of a garbled actor name) rather than a
|
|
431
|
+
// genuinely valid, if unusual, message. Reject it with an actionable
|
|
432
|
+
// error instead of silently minting that garbled actor — see the
|
|
433
|
+
// `Alice->>>Bob: Hello` example in issue #762.
|
|
434
|
+
const arrowToken = msgMatch[2]!
|
|
435
|
+
const toFirstChar = msgMatch[4]![0]
|
|
436
|
+
if (toFirstChar === '>' || toFirstChar === ')') {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`Line ${stmt.line}: Malformed sequence-diagram arrow in "${line}" — "${arrowToken}${toFirstChar}" is not a recognized arrow. Expected one of: ->, -->, ->>, -->>, -x, --x, -), --), <<->>, <<-->>.`,
|
|
439
|
+
)
|
|
440
|
+
}
|
|
406
441
|
pushMessage(
|
|
407
442
|
diagram,
|
|
408
443
|
actorIds,
|
|
@@ -413,13 +448,14 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
413
448
|
msgMatch[3],
|
|
414
449
|
msgMatch[4]!,
|
|
415
450
|
msgMatch[5]!,
|
|
451
|
+
stmt.line,
|
|
416
452
|
)
|
|
417
453
|
const msgIndex = diagram.messages.length - 1
|
|
418
454
|
const msg = diagram.messages[msgIndex]!
|
|
419
455
|
if (pendingCreate !== undefined) {
|
|
420
456
|
if (msg.to !== pendingCreate) {
|
|
421
457
|
throw new Error(
|
|
422
|
-
`The created participant ${pendingCreate} does not have an associated creating message after its declaration. Please check the sequence diagram.`,
|
|
458
|
+
`Line ${stmt.line}: The created participant ${pendingCreate} does not have an associated creating message after its declaration. Please check the sequence diagram.`,
|
|
423
459
|
)
|
|
424
460
|
}
|
|
425
461
|
findActor(diagram, pendingCreate).createdAt = msgIndex
|
|
@@ -428,7 +464,7 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
428
464
|
if (pendingDestroy !== undefined) {
|
|
429
465
|
if (msg.from !== pendingDestroy && msg.to !== pendingDestroy) {
|
|
430
466
|
throw new Error(
|
|
431
|
-
`The destroyed participant ${pendingDestroy} does not have an associated destroying message after its declaration. Please check the sequence diagram.`,
|
|
467
|
+
`Line ${stmt.line}: The destroyed participant ${pendingDestroy} does not have an associated destroying message after its declaration. Please check the sequence diagram.`,
|
|
432
468
|
)
|
|
433
469
|
}
|
|
434
470
|
findActor(diagram, pendingDestroy).destroyedAt = msgIndex
|
|
@@ -438,6 +474,25 @@ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
|
|
|
438
474
|
}
|
|
439
475
|
}
|
|
440
476
|
|
|
477
|
+
// A block/box left open at EOF is a structural mistake, not something to
|
|
478
|
+
// silently accept as "closed by end of input" — mirrors the class-diagram
|
|
479
|
+
// parser's unclosed-body check (#761) for the same reason: previously
|
|
480
|
+
// this was silently ignored (#762). Checked innermost-first (the block
|
|
481
|
+
// stack) since an unclosed block is the more specific, more actionable
|
|
482
|
+
// thing to report when both are open.
|
|
483
|
+
if (blockStack.length > 0) {
|
|
484
|
+
const unclosed = blockStack[blockStack.length - 1]!
|
|
485
|
+
throw new Error(
|
|
486
|
+
`Line ${unclosed.line}: Sequence diagram: unclosed "${unclosed.type}" block — expected a matching "end" before the diagram ends.`,
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
if (boxCtx.open !== undefined) {
|
|
490
|
+
const openBox = diagram.boxes[boxCtx.open]!
|
|
491
|
+
throw new Error(
|
|
492
|
+
`Line ${boxOpenLine}: Sequence diagram: unclosed "box${openBox.label ? ` ${openBox.label}` : ''}" — expected a matching "end" before the diagram ends.`,
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
|
|
441
496
|
return diagram
|
|
442
497
|
}
|
|
443
498
|
|
|
@@ -461,12 +516,13 @@ function ensureActor(
|
|
|
461
516
|
actorIds: Set<string>,
|
|
462
517
|
boxCtx: BoxContext,
|
|
463
518
|
id: string,
|
|
519
|
+
lineNumber: number,
|
|
464
520
|
): void {
|
|
465
521
|
if (!actorIds.has(id)) {
|
|
466
522
|
actorIds.add(id)
|
|
467
523
|
diagram.actors.push({ id, label: id, type: 'participant' })
|
|
468
524
|
}
|
|
469
|
-
joinOpenBox(diagram, boxCtx, id)
|
|
525
|
+
joinOpenBox(diagram, boxCtx, id, lineNumber)
|
|
470
526
|
}
|
|
471
527
|
|
|
472
528
|
/**
|
|
@@ -478,6 +534,7 @@ function joinOpenBox(
|
|
|
478
534
|
diagram: SequenceDiagram,
|
|
479
535
|
boxCtx: BoxContext,
|
|
480
536
|
id: string,
|
|
537
|
+
lineNumber: number,
|
|
481
538
|
): void {
|
|
482
539
|
const open = boxCtx.open
|
|
483
540
|
if (open === undefined) return
|
|
@@ -487,7 +544,7 @@ function joinOpenBox(
|
|
|
487
544
|
const from = diagram.boxes[existing]!.label
|
|
488
545
|
const to = diagram.boxes[open]!.label
|
|
489
546
|
throw new Error(
|
|
490
|
-
`A same participant should only be defined in one Box: ${id} can't be in '${from}' and in '${to}' at the same time.`,
|
|
547
|
+
`Line ${lineNumber}: A same participant should only be defined in one Box: ${id} can't be in '${from}' and in '${to}' at the same time.`,
|
|
491
548
|
)
|
|
492
549
|
}
|
|
493
550
|
boxCtx.membership.set(id, open)
|
|
@@ -510,9 +567,10 @@ function pushMessage(
|
|
|
510
567
|
activationMark: string | undefined,
|
|
511
568
|
to: string,
|
|
512
569
|
rawLabel: string,
|
|
570
|
+
lineNumber: number,
|
|
513
571
|
): void {
|
|
514
|
-
ensureActor(diagram, actorIds, boxCtx, from)
|
|
515
|
-
ensureActor(diagram, actorIds, boxCtx, to)
|
|
572
|
+
ensureActor(diagram, actorIds, boxCtx, from, lineNumber)
|
|
573
|
+
ensureActor(diagram, actorIds, boxCtx, to, lineNumber)
|
|
516
574
|
|
|
517
575
|
const bidirectional = arrow === '<<->>' || arrow === '<<-->>'
|
|
518
576
|
const lineStyle = bidirectional
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { XYChart, XYAxis, XYChartSeries } from './types.ts'
|
|
2
|
+
import type { Statement } from '@zombie-mermaid/core'
|
|
2
3
|
|
|
3
4
|
// ============================================================================
|
|
4
5
|
// XY Chart parser
|
|
@@ -21,14 +22,15 @@ import type { XYChart, XYAxis, XYChartSeries } from './types.ts'
|
|
|
21
22
|
* Parse a Mermaid xychart-beta diagram from preprocessed lines.
|
|
22
23
|
* Lines should already be trimmed and comment-stripped.
|
|
23
24
|
*/
|
|
24
|
-
export function parseXYChart(lines:
|
|
25
|
+
export function parseXYChart(lines: Statement[]): XYChart {
|
|
25
26
|
const xAxis: XYAxis = {}
|
|
26
27
|
const yAxis: XYAxis = {}
|
|
27
28
|
const series: XYChartSeries[] = []
|
|
28
29
|
let title: string | undefined
|
|
29
30
|
let horizontal = false
|
|
30
31
|
|
|
31
|
-
for (const
|
|
32
|
+
for (const stmt of lines) {
|
|
33
|
+
const line = stmt.text
|
|
32
34
|
// Header line — detect horizontal
|
|
33
35
|
if (/^xychart(-beta)?\b/i.test(line)) {
|
|
34
36
|
if (/\bhorizontal\b/i.test(line)) horizontal = true
|
|
@@ -86,16 +88,42 @@ export function parseXYChart(lines: string[]): XYChart {
|
|
|
86
88
|
// bar [...]
|
|
87
89
|
const barMatch = line.match(/^bar\s+\[([^\]]+)\]/)
|
|
88
90
|
if (barMatch) {
|
|
89
|
-
series.push({
|
|
91
|
+
series.push({
|
|
92
|
+
type: 'bar',
|
|
93
|
+
data: parseNumericArray(barMatch[1]!, 'bar', line, stmt.line),
|
|
94
|
+
})
|
|
90
95
|
continue
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
// line [...]
|
|
94
99
|
const lineMatch = line.match(/^line\s+\[([^\]]+)\]/)
|
|
95
100
|
if (lineMatch) {
|
|
96
|
-
series.push({
|
|
101
|
+
series.push({
|
|
102
|
+
type: 'line',
|
|
103
|
+
data: parseNumericArray(lineMatch[1]!, 'line', line, stmt.line),
|
|
104
|
+
})
|
|
97
105
|
continue
|
|
98
106
|
}
|
|
107
|
+
|
|
108
|
+
// None of the recognized directive shapes matched. A line that doesn't
|
|
109
|
+
// start with any known xychart-beta keyword at all falls through
|
|
110
|
+
// silently below, same as before — but a line that *does* start with
|
|
111
|
+
// one of the five keyworded directives (x-axis/y-axis/bar/line/title)
|
|
112
|
+
// and still failed every pattern above is almost certainly an attempt
|
|
113
|
+
// at that directive with broken syntax (an unclosed bracket, a missing
|
|
114
|
+
// quote, a malformed range). Previously this was silently dropped —
|
|
115
|
+
// the diagram would render with that axis/series just missing and no
|
|
116
|
+
// indication why. Surface an actionable error instead. See issue #541
|
|
117
|
+
// (parser error-message quality audit — this parser had zero throw
|
|
118
|
+
// sites before this pass).
|
|
119
|
+
const keywordMatch = line.match(/^(x-axis|y-axis|bar|line|title)\b/i)
|
|
120
|
+
if (keywordMatch) {
|
|
121
|
+
throw malformedDirectiveError(
|
|
122
|
+
keywordMatch[1]!.toLowerCase(),
|
|
123
|
+
line,
|
|
124
|
+
stmt.line,
|
|
125
|
+
)
|
|
126
|
+
}
|
|
99
127
|
}
|
|
100
128
|
|
|
101
129
|
// Auto-derive y-axis range from data if not specified
|
|
@@ -120,6 +148,56 @@ export function parseXYChart(lines: string[]): XYChart {
|
|
|
120
148
|
return { title, horizontal, xAxis, yAxis, series }
|
|
121
149
|
}
|
|
122
150
|
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Parse a `bar`/`line` series' comma-separated bracket contents into
|
|
153
|
+
* numbers, throwing on any element that isn't a valid number instead of
|
|
154
|
+
* silently coercing it to `NaN` (which serializes as `null` and can
|
|
155
|
+
* propagate into layout math with no indication anything was wrong — see
|
|
156
|
+
* issue #541's audit finding on this exact parser).
|
|
157
|
+
*/
|
|
158
|
+
function parseNumericArray(
|
|
159
|
+
str: string,
|
|
160
|
+
seriesType: 'bar' | 'line',
|
|
161
|
+
line: string,
|
|
162
|
+
lineNumber: number,
|
|
163
|
+
): number[] {
|
|
164
|
+
return str.split(',').map((raw, index) => {
|
|
165
|
+
const trimmed = raw.trim()
|
|
166
|
+
const value = parseFloat(trimmed)
|
|
167
|
+
if (trimmed.length === 0 || Number.isNaN(value)) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Line ${lineNumber}: Invalid numeric value ${JSON.stringify(trimmed)} at position ${
|
|
170
|
+
index + 1
|
|
171
|
+
} in "${line}". Every value in a ${seriesType} [...] list must be a number.`,
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
return value
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Expected-syntax hint per xychart-beta keyword, for `malformedDirectiveError`. */
|
|
179
|
+
const XYCHART_DIRECTIVE_HELP: Record<string, string> = {
|
|
180
|
+
'x-axis':
|
|
181
|
+
'x-axis [A, B, C] (categories) or x-axis 0 --> 100 (numeric range), either optionally preceded by a quoted title',
|
|
182
|
+
'y-axis':
|
|
183
|
+
'y-axis 0 --> 100 (numeric range) or y-axis "Title" (title only), optionally preceded by a quoted title before a range',
|
|
184
|
+
bar: 'bar [10, 20, 30] — a comma-separated numeric array in square brackets',
|
|
185
|
+
line: 'line [10, 20, 30] — a comma-separated numeric array in square brackets',
|
|
186
|
+
title: 'title "Chart Title" — a double-quoted string',
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Build the error for a line that starts with a recognized xychart-beta
|
|
191
|
+
* keyword (x-axis/y-axis/bar/line/title) but doesn't match that keyword's
|
|
192
|
+
* expected syntax in any of the forms this parser supports.
|
|
193
|
+
*/
|
|
194
|
+
function malformedDirectiveError(
|
|
195
|
+
keyword: string,
|
|
196
|
+
line: string,
|
|
197
|
+
lineNumber: number,
|
|
198
|
+
): Error {
|
|
199
|
+
const help = XYCHART_DIRECTIVE_HELP[keyword] ?? keyword
|
|
200
|
+
return new Error(
|
|
201
|
+
`Line ${lineNumber}: Malformed xychart-beta "${keyword}" directive: "${line}". Expected: ${help}.`,
|
|
202
|
+
)
|
|
125
203
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Craft Docs
|
|
4
|
+
Copyright (c) 2026 Dustin Fadler
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -16,23 +16,27 @@ import type {
|
|
|
16
16
|
PositionedClassNode,
|
|
17
17
|
PositionedClassNote,
|
|
18
18
|
PositionedClassRelationship,
|
|
19
|
-
} from '
|
|
20
|
-
import { formatClassMember } from '
|
|
19
|
+
} from '@zombie-mermaid/mermaid-parser'
|
|
20
|
+
import { formatClassMember } from '@zombie-mermaid/mermaid-parser'
|
|
21
21
|
import type { ClassRenderOptions } from '@zombie-mermaid/core'
|
|
22
22
|
import {
|
|
23
23
|
estimateTextWidth,
|
|
24
24
|
estimateMonoTextWidth,
|
|
25
25
|
FONT_WEIGHTS,
|
|
26
26
|
resolveFontSizes,
|
|
27
|
-
|
|
27
|
+
} from '../styles.ts'
|
|
28
|
+
import { elkLayoutSync } from '../elk-instance.ts'
|
|
29
|
+
import {
|
|
28
30
|
extractEdgePoints,
|
|
29
31
|
extractEdgeLabelPosition,
|
|
32
|
+
} from '../layout-engine/elk-adapter-utils.ts'
|
|
33
|
+
import {
|
|
30
34
|
ELK_DIRECTION_FALLBACK,
|
|
31
35
|
baseElkLayoutOptions,
|
|
32
36
|
buildElkEdge,
|
|
33
37
|
buildElkLeafNode,
|
|
34
38
|
directionToElk,
|
|
35
|
-
} from '
|
|
39
|
+
} from '../layout-engine/elk-graph-builder.ts'
|
|
36
40
|
import { measureMultilineText, resolveNodeStyle } from '@zombie-mermaid/core'
|
|
37
41
|
|
|
38
42
|
/** Layout constants for class diagrams */
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
PositionedClassRelationship,
|
|
6
6
|
ClassMember,
|
|
7
7
|
RelationshipType,
|
|
8
|
-
} from '
|
|
8
|
+
} from '@zombie-mermaid/mermaid-parser'
|
|
9
9
|
import type { DiagramColors, SvgEmitOptions } from '@zombie-mermaid/core'
|
|
10
10
|
import {
|
|
11
11
|
svgOpenTag,
|
|
@@ -17,14 +17,14 @@ import {
|
|
|
17
17
|
escapeAttr,
|
|
18
18
|
safeHref,
|
|
19
19
|
} from '@zombie-mermaid/core'
|
|
20
|
+
import { withDataSrc } from '../renderer.ts'
|
|
20
21
|
import {
|
|
21
|
-
withDataSrc,
|
|
22
22
|
FONT_SIZES,
|
|
23
23
|
FONT_WEIGHTS,
|
|
24
24
|
STROKE_WIDTHS,
|
|
25
25
|
TEXT_BASELINE_SHIFT,
|
|
26
|
-
} from '
|
|
27
|
-
import type { FontSizes } from '
|
|
26
|
+
} from '../styles.ts'
|
|
27
|
+
import type { FontSizes } from '../styles.ts'
|
|
28
28
|
import { CLS } from './layout.ts'
|
|
29
29
|
|
|
30
30
|
// ============================================================================
|
|
@@ -61,7 +61,8 @@ const CLS_FONT = {
|
|
|
61
61
|
* @param decorative - Marks the SVG decorative (from `options.decorative`).
|
|
62
62
|
* @param linksEnabled - Whether `click`-based `<a href>` links and `<title>`
|
|
63
63
|
* tooltips render (from `options.interactivity !==
|
|
64
|
-
* 'none'`, see `resolveLinksEnabled` in
|
|
64
|
+
* 'none'`, see `resolveLinksEnabled` in
|
|
65
|
+
* src/diagram-registry.ts).
|
|
65
66
|
* Default true — matches the flowchart/state renderer.
|
|
66
67
|
* @param emit - Strict-CSP controls (from `options.nonce` /
|
|
67
68
|
* `options.styleAttribute`, see #216). Default: no nonce,
|
|
@@ -399,7 +400,7 @@ const NOTE_FOLD = 6
|
|
|
399
400
|
/**
|
|
400
401
|
* Render a note as a dog-eared box: a polygon with its top-right corner
|
|
401
402
|
* clipped plus a small fold triangle — the same shape the sequence renderer
|
|
402
|
-
* draws for its notes (
|
|
403
|
+
* draws for its notes (../sequence/renderer.ts renderNote), so notes look
|
|
403
404
|
* alike across diagram types. Wrapped in <g class="class-note"> with
|
|
404
405
|
* `data-for` naming the class it's attached to, if any.
|
|
405
406
|
*/
|