agentic-mermaid 0.1.1 → 0.2.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/AGENT_NATIVE.md +155 -91
- package/CHANGELOG.md +163 -4
- package/Instructions_for_agents.md +10 -10
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +31 -38
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/hero.png +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +73 -1868
- package/dist/agent.js +336 -37325
- package/dist/agentic-mermaid-mcp.js +7 -38399
- package/dist/am.js +33626 -40446
- package/dist/chunk-2P5FKUQC.js +3485 -0
- package/dist/chunk-2S3VSS6I.js +149 -0
- package/dist/chunk-2SILJDW2.js +37 -0
- package/dist/chunk-BJYMBT4I.js +61963 -0
- package/dist/chunk-FDM43XU7.js +615 -0
- package/dist/chunk-MYPDVZCO.js +4702 -0
- package/dist/index-DWPa2MJs.d.ts +6780 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -33014
- package/dist/mcp-cli-5J47WEY3.js +17 -0
- package/docs/README.md +39 -31
- package/docs/agent-api-cookbook.md +44 -35
- package/docs/agent-mutation-policy.md +1 -1
- package/docs/agent-workflow-examples.md +2 -2
- package/docs/api.md +284 -77
- package/docs/ascii.md +26 -16
- package/docs/code-mode-impact.md +214 -0
- package/docs/comparison.md +23 -11
- package/docs/computational-aesthetics-prototype-plan.md +252 -0
- package/docs/config.md +26 -10
- package/docs/custom-fonts.md +265 -0
- package/docs/custom-style-cookbook.md +130 -9
- package/docs/diagram-families.md +48 -23
- package/docs/features.md +59 -52
- package/docs/fork-differences.md +6 -3
- package/docs/getting-started.md +23 -16
- package/docs/issue-derived-test-cases.md +29 -22
- package/docs/mcp-code-mode-rationale.md +13 -6
- package/docs/mcp-http-transport.md +20 -13
- package/docs/mermaid-layout-complaints.md +66 -52
- package/docs/mutation-testing.md +145 -66
- package/docs/quality.md +23 -14
- package/docs/react.md +2 -2
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/docs/style-authoring.md +241 -38
- package/docs/svg-semantic-contract.md +49 -0
- package/docs/testing-strategy.md +166 -58
- package/docs/theming.md +19 -32
- package/examples/styles/catalog.json +54 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
- package/examples/styles/cupertino-prototype.mmd +10 -0
- package/examples/styles/cupertino-prototype.style.json +63 -0
- package/examples/styles/mid-century-report.style.json +2 -2
- package/examples/styles/star-chart-atlas.style.json +2 -1
- package/examples/styles/transit-route-map.style.json +2 -2
- package/examples/styles/vercel-inspired-prototype.mmd +6 -0
- package/examples/styles/vercel-inspired-prototype.style.json +87 -0
- package/package.json +85 -45
- package/server.json +2 -2
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
- package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
- package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
- package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
- package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
- package/TODO.md +0 -684
- package/dist/index-wjRjbsP-.d.ts +0 -1102
- package/docs/assets/improvements/box-start-connector.png +0 -0
- package/docs/assets/improvements/comment-dropped-lint.png +0 -0
- package/docs/assets/improvements/er-content-loss.png +0 -0
- package/docs/assets/improvements/fan-in-trunks.png +0 -0
- package/docs/assets/improvements/gantt-family.png +0 -0
- package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
- package/docs/assets/improvements/gantt-verify-seam.png +0 -0
- package/docs/assets/improvements/subgraph-container-edges.png +0 -0
- package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
- package/docs/assets/improvements/wrapper-init-directive.png +0 -0
- package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
- package/docs/assets/style-cookbook/mid-century-report.png +0 -0
- package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
- package/docs/assets/style-cookbook/transit-route-map.png +0 -0
- package/docs/contributing/adding-diagram-types.md +0 -144
- package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
- package/docs/contributing/diagram-family-citizenship.md +0 -82
- package/docs/contributing/harvesting-upstream-tests.md +0 -126
- package/docs/contributing/lessons-learned.md +0 -276
- package/docs/contributing/releasing.md +0 -82
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/design/families/README.md +0 -26
- package/docs/design/families/architecture-beta.md +0 -48
- package/docs/design/families/flowchart-parser-conformance.md +0 -22
- package/docs/design/families/gantt-research.md +0 -137
- package/docs/design/families/gantt.md +0 -329
- package/docs/design/families/journey-agentic-cards-before.png +0 -0
- package/docs/design/families/journey-experience-curve-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
- package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
- package/docs/design/families/journey-migration-parity.md +0 -439
- package/docs/design/families/journey-section-overlap-after.png +0 -0
- package/docs/design/families/journey-section-overlap-before.png +0 -0
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -153
- package/docs/design/families/xychart.md +0 -85
- package/docs/design/system/README.md +0 -45
- package/docs/design/system/abstraction-audit.md +0 -224
- package/docs/design/system/abstraction-recommendations.md +0 -655
- package/docs/design/system/architecture.mmd +0 -29
- package/docs/design/system/architecture.svg +0 -137
- package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
- package/docs/design/system/layout-rubric.md +0 -138
- package/docs/design/system/route-contracts.md +0 -848
- package/docs/design/system/source-preservation-ladder.md +0 -52
- package/docs/design/system/styles-rollout.md +0 -216
- package/docs/design/system/ugly-layouts.md +0 -119
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -498
- package/docs/layout-characterization/contact-sheet.md +0 -638
- package/docs/layout-characterization/properties.md +0 -281
- package/docs/layout-characterization/visual-quality.md +0 -160
- package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
- package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
- package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
- package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
- package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
- package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
- package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
- package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
- package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
- package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
- package/docs/project/divergences.md +0 -98
- package/docs/project/dogfooding-docs-strategy.md +0 -66
- package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
- package/docs/project/lessons-learned.md +0 -1019
- package/docs/project/remove-role-styling-plan.md +0 -176
- package/examples/agent-improve-auth-flow.ts +0 -206
- package/examples/agent-loop.ts +0 -53
- package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
- package/llms.txt +0 -104
- package/skill-evals/README.md +0 -77
- package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
- package/skill-evals/fixtures/class-payment/input.mmd +0 -4
- package/skill-evals/fixtures/er-orders/input.mmd +0 -10
- package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
- package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
- package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
- package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
- package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
- package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
- package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
- package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
- package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
- package/skill-evals/shared-benchmark.json +0 -510
- package/src/agent/analyze.ts +0 -157
- package/src/agent/apply.ts +0 -146
- package/src/agent/architecture-body.ts +0 -458
- package/src/agent/certificates.ts +0 -110
- package/src/agent/class-body.ts +0 -319
- package/src/agent/core.ts +0 -207
- package/src/agent/create.ts +0 -106
- package/src/agent/describe.ts +0 -368
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -360
- package/src/agent/families-builtin.ts +0 -920
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -832
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/font-coverage.ts +0 -288
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -374
- package/src/agent/label-metrics.ts +0 -28
- package/src/agent/layout-to-rendered.ts +0 -63
- package/src/agent/mutate.ts +0 -97
- package/src/agent/mutation-ops.ts +0 -51
- package/src/agent/op-schema.ts +0 -443
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -171
- package/src/agent/quadrant-body.ts +0 -336
- package/src/agent/quality.ts +0 -363
- package/src/agent/readability-audit.ts +0 -92
- package/src/agent/rendered-route-audit.ts +0 -61
- package/src/agent/sequence-body.ts +0 -345
- package/src/agent/serialize.ts +0 -214
- package/src/agent/state-body.ts +0 -589
- package/src/agent/structural-count.ts +0 -150
- package/src/agent/timeline-body.ts +0 -280
- package/src/agent/trace-log.ts +0 -43
- package/src/agent/types.ts +0 -995
- package/src/agent/verify.ts +0 -603
- package/src/agent/xychart-body.ts +0 -472
- package/src/architecture/config.ts +0 -282
- package/src/architecture/layout.ts +0 -496
- package/src/architecture/parser.ts +0 -375
- package/src/architecture/renderer.ts +0 -611
- package/src/architecture/types.ts +0 -113
- package/src/ascii/ansi.ts +0 -423
- package/src/ascii/architecture.ts +0 -210
- package/src/ascii/canvas.ts +0 -443
- package/src/ascii/class-diagram.ts +0 -697
- package/src/ascii/converter.ts +0 -475
- package/src/ascii/draw.ts +0 -1693
- package/src/ascii/edge-bundling.ts +0 -328
- package/src/ascii/edge-routing.ts +0 -330
- package/src/ascii/er-diagram.ts +0 -439
- package/src/ascii/gantt.ts +0 -276
- package/src/ascii/grid.ts +0 -747
- package/src/ascii/index.ts +0 -196
- package/src/ascii/journey.ts +0 -169
- package/src/ascii/meta.ts +0 -244
- package/src/ascii/multiline-utils.ts +0 -80
- package/src/ascii/pathfinder.ts +0 -301
- package/src/ascii/pie.ts +0 -67
- package/src/ascii/quadrant.ts +0 -158
- package/src/ascii/reverse.ts +0 -216
- package/src/ascii/sequence.ts +0 -499
- package/src/ascii/shapes/circle.ts +0 -27
- package/src/ascii/shapes/corners.ts +0 -143
- package/src/ascii/shapes/diamond.ts +0 -27
- package/src/ascii/shapes/hexagon.ts +0 -27
- package/src/ascii/shapes/index.ts +0 -106
- package/src/ascii/shapes/rectangle.ts +0 -168
- package/src/ascii/shapes/rounded.ts +0 -27
- package/src/ascii/shapes/special.ts +0 -332
- package/src/ascii/shapes/stadium.ts +0 -110
- package/src/ascii/shapes/state.ts +0 -192
- package/src/ascii/shapes/types.ts +0 -73
- package/src/ascii/timeline.ts +0 -141
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/wrap.ts +0 -131
- package/src/ascii/xychart.ts +0 -919
- package/src/browser.ts +0 -29
- package/src/class/layout.ts +0 -240
- package/src/class/parser.ts +0 -332
- package/src/class/renderer.ts +0 -638
- package/src/class/types.ts +0 -129
- package/src/cli/agent-instructions.ts +0 -1
- package/src/cli/am-bin.ts +0 -6
- package/src/cli/exit-codes.ts +0 -18
- package/src/cli/index.ts +0 -1132
- package/src/cli/init-agent.ts +0 -147
- package/src/cli/run-entrypoint.ts +0 -10
- package/src/color-resolver.ts +0 -152
- package/src/elk-instance.ts +0 -114
- package/src/er/layout.ts +0 -187
- package/src/er/parser.ts +0 -226
- package/src/er/renderer.ts +0 -792
- package/src/er/types.ts +0 -99
- package/src/family-rubric.ts +0 -425
- package/src/font-manifest.ts +0 -31
- package/src/gantt/layout.ts +0 -365
- package/src/gantt/parser.ts +0 -282
- package/src/gantt/pipeline.ts +0 -27
- package/src/gantt/renderer.ts +0 -414
- package/src/gantt/schedule.ts +0 -513
- package/src/gantt/types.ts +0 -255
- package/src/index.ts +0 -368
- package/src/journey/layout.ts +0 -554
- package/src/journey/parse-core.ts +0 -251
- package/src/journey/parser.ts +0 -101
- package/src/journey/renderer.ts +0 -917
- package/src/journey/types.ts +0 -144
- package/src/layout/geometry.ts +0 -182
- package/src/layout/pass.ts +0 -142
- package/src/layout/passes/index.ts +0 -3916
- package/src/layout-engine.ts +0 -1565
- package/src/layout-rubric.ts +0 -659
- package/src/mcp/artifacts.ts +0 -137
- package/src/mcp/dynamic-harness.ts +0 -31
- package/src/mcp/dynamic-modules.d.ts +0 -7
- package/src/mcp/facade.ts +0 -608
- package/src/mcp/harness-runtime.ts +0 -203
- package/src/mcp/hosted-server.ts +0 -468
- package/src/mcp/mcp-bin.ts +0 -3
- package/src/mcp/mcp-cli.ts +0 -82
- package/src/mcp/protocol.ts +0 -13
- package/src/mcp/sandbox.ts +0 -195
- package/src/mcp/sdk-decl.ts +0 -391
- package/src/mcp/server.ts +0 -362
- package/src/mcp/tool-surface.ts +0 -146
- package/src/mermaid-source.ts +0 -655
- package/src/multiline-utils.ts +0 -228
- package/src/parser.ts +0 -1031
- package/src/pie/layout.ts +0 -168
- package/src/pie/parser.ts +0 -136
- package/src/pie/renderer.ts +0 -234
- package/src/pie/types.ts +0 -82
- package/src/quadrant/layout.ts +0 -191
- package/src/quadrant/parser.ts +0 -183
- package/src/quadrant/renderer.ts +0 -322
- package/src/quadrant/types.ts +0 -113
- package/src/render-family-hooks.ts +0 -244
- package/src/renderer.ts +0 -1099
- package/src/route-contracts.ts +0 -2723
- package/src/scene/backend.ts +0 -76
- package/src/scene/fidelity.ts +0 -183
- package/src/scene/hybrid-backend.ts +0 -174
- package/src/scene/ir.ts +0 -181
- package/src/scene/marks.ts +0 -127
- package/src/scene/rough-backend.ts +0 -378
- package/src/scene/seed.ts +0 -46
- package/src/scene/style-registry.ts +0 -556
- package/src/sequence/layout.ts +0 -456
- package/src/sequence/parser.ts +0 -255
- package/src/sequence/renderer.ts +0 -604
- package/src/sequence/types.ts +0 -154
- package/src/shape-clipping.ts +0 -351
- package/src/shared/accessibility-directives.ts +0 -22
- package/src/shared/batched.ts +0 -61
- package/src/shared/color-math.ts +0 -93
- package/src/shared/svg-a11y.ts +0 -29
- package/src/shared/syntax-error.ts +0 -17
- package/src/shared/unicode-ranges.ts +0 -135
- package/src/styles.ts +0 -318
- package/src/svg-paths.ts +0 -28
- package/src/text-metrics.ts +0 -248
- package/src/theme.ts +0 -710
- package/src/timeline/layout.ts +0 -381
- package/src/timeline/parser.ts +0 -215
- package/src/timeline/renderer.ts +0 -538
- package/src/timeline/types.ts +0 -105
- package/src/types.ts +0 -462
- package/src/website-worker-modules.d.ts +0 -18
- package/src/xychart/axis-utils.ts +0 -109
- package/src/xychart/colors.ts +0 -124
- package/src/xychart/config.ts +0 -88
- package/src/xychart/layout.ts +0 -656
- package/src/xychart/parser.ts +0 -504
- package/src/xychart/renderer.ts +0 -640
- package/src/xychart/types.ts +0 -282
package/src/mermaid-source.ts
DELETED
|
@@ -1,655 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Mermaid source normalization + config extraction
|
|
3
|
-
//
|
|
4
|
-
// Handles:
|
|
5
|
-
// - UTF-8 BOM stripping
|
|
6
|
-
// - leading YAML frontmatter
|
|
7
|
-
// - Mermaid directives / init blocks (%%{init: ...}%%)
|
|
8
|
-
// - Mermaid comment stripping for downstream line-based parsers
|
|
9
|
-
// - normalized, trimmed diagram lines for downstream parsers
|
|
10
|
-
// ============================================================================
|
|
11
|
-
|
|
12
|
-
import YAML from 'yaml'
|
|
13
|
-
|
|
14
|
-
export type MermaidConfigScalar = string | number | boolean | null
|
|
15
|
-
export type MermaidConfigValue = MermaidConfigScalar | MermaidConfigValue[] | MermaidConfigMap
|
|
16
|
-
|
|
17
|
-
export interface MermaidConfigMap {
|
|
18
|
-
[key: string]: MermaidConfigValue | undefined
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type MermaidFrontmatterScalar = MermaidConfigScalar
|
|
22
|
-
export type MermaidFrontmatterValue = MermaidConfigValue
|
|
23
|
-
export type MermaidFrontmatterList = MermaidFrontmatterValue[]
|
|
24
|
-
|
|
25
|
-
export interface MermaidFrontmatterMap extends MermaidConfigMap {}
|
|
26
|
-
|
|
27
|
-
export interface MermaidThemeVariables extends MermaidConfigMap {
|
|
28
|
-
fontFamily?: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface TimelineRuntimeConfig extends MermaidConfigMap {
|
|
32
|
-
disableMulticolor?: boolean
|
|
33
|
-
sectionFills?: string[]
|
|
34
|
-
sectionColours?: string[]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface JourneyRuntimeConfig extends MermaidConfigMap {
|
|
38
|
-
diagramMarginX?: number
|
|
39
|
-
diagramMarginY?: number
|
|
40
|
-
leftMargin?: number
|
|
41
|
-
maxLabelWidth?: number
|
|
42
|
-
width?: number
|
|
43
|
-
height?: number
|
|
44
|
-
boxMargin?: number
|
|
45
|
-
boxTextMargin?: number
|
|
46
|
-
noteMargin?: number
|
|
47
|
-
messageMargin?: number
|
|
48
|
-
messageAlign?: 'left' | 'center' | 'right'
|
|
49
|
-
bottomMarginAdj?: number
|
|
50
|
-
useMaxWidth?: boolean
|
|
51
|
-
rightAngles?: boolean
|
|
52
|
-
taskFontSize?: string | number
|
|
53
|
-
taskFontFamily?: string
|
|
54
|
-
taskMargin?: number
|
|
55
|
-
activationWidth?: number
|
|
56
|
-
textPlacement?: string
|
|
57
|
-
actorColours?: string[]
|
|
58
|
-
sectionFills?: string[]
|
|
59
|
-
sectionColours?: string[]
|
|
60
|
-
titleColor?: string
|
|
61
|
-
titleFontFamily?: string
|
|
62
|
-
titleFontSize?: string | number
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface GanttRuntimeConfig extends MermaidConfigMap {
|
|
66
|
-
displayMode?: string
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface MermaidRuntimeConfig extends MermaidConfigMap {
|
|
70
|
-
theme?: string
|
|
71
|
-
fontFamily?: string
|
|
72
|
-
themeVariables?: MermaidThemeVariables
|
|
73
|
-
timeline?: TimelineRuntimeConfig
|
|
74
|
-
journey?: JourneyRuntimeConfig
|
|
75
|
-
xyChart?: MermaidConfigMap
|
|
76
|
-
gantt?: GanttRuntimeConfig
|
|
77
|
-
useMaxWidth?: boolean
|
|
78
|
-
useWidth?: number
|
|
79
|
-
themeCSS?: string
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface ProcessedMermaidSource {
|
|
83
|
-
body: string
|
|
84
|
-
lines: string[]
|
|
85
|
-
frontmatter: MermaidFrontmatterMap
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface NormalizedMermaidSource {
|
|
89
|
-
text: string
|
|
90
|
-
body: string
|
|
91
|
-
lines: string[]
|
|
92
|
-
firstLine: string
|
|
93
|
-
config: MermaidRuntimeConfig
|
|
94
|
-
frontmatter: MermaidFrontmatterMap
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const FRONTMATTER_REGEX = /^\uFEFF?\s*---\s*\r?\n([\s\S]*?)\r?\n\s*---\s*(?:\r?\n|$)/
|
|
98
|
-
const INIT_DIRECTIVE_REGEX = /^\s*%%\{\s*(?:init|initialize)\s*:\s*([\s\S]*?)\}\s*%%\s*(?:\r?\n|$)?/gm
|
|
99
|
-
|
|
100
|
-
export function normalizeMermaidSource(
|
|
101
|
-
text: string,
|
|
102
|
-
baseConfig: MermaidRuntimeConfig = {},
|
|
103
|
-
): NormalizedMermaidSource {
|
|
104
|
-
const processed = preprocessMermaidSource(text, runtimeConfigToFrontmatterMap(baseConfig))
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
text: processed.lines.join('\n'),
|
|
108
|
-
body: processed.body,
|
|
109
|
-
lines: processed.lines,
|
|
110
|
-
firstLine: processed.lines[0]?.toLowerCase() ?? '',
|
|
111
|
-
config: normalizeMermaidRuntimeConfig(processed.frontmatter),
|
|
112
|
-
frontmatter: processed.frontmatter,
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function preprocessMermaidSource(
|
|
117
|
-
text: string,
|
|
118
|
-
baseFrontmatter: MermaidFrontmatterMap = {},
|
|
119
|
-
): ProcessedMermaidSource {
|
|
120
|
-
const frontmatterMatch = text.match(FRONTMATTER_REGEX)
|
|
121
|
-
const yamlFrontmatter = frontmatterMatch ? canonicalizeFrontmatterMap(parseYamlDocument(frontmatterMatch[1]!)) : {}
|
|
122
|
-
const rawBody = frontmatterMatch ? text.slice(frontmatterMatch[0].length) : text
|
|
123
|
-
const { body, frontmatter: directiveFrontmatter } = extractInitDirectives(rawBody)
|
|
124
|
-
const frontmatter = mergeFrontmatterMaps(
|
|
125
|
-
mergeFrontmatterMaps(canonicalizeFrontmatterMap(baseFrontmatter), yamlFrontmatter),
|
|
126
|
-
canonicalizeFrontmatterMap(directiveFrontmatter),
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
body,
|
|
131
|
-
lines: toMermaidLines(body),
|
|
132
|
-
frontmatter,
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function toMermaidLines(text: string): string[] {
|
|
137
|
-
return text
|
|
138
|
-
.replace(/^\uFEFF/, '')
|
|
139
|
-
.split(/\r?\n/)
|
|
140
|
-
.map(line => line.trim())
|
|
141
|
-
.filter(line => line.length > 0 && !line.startsWith('%%'))
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function mergeMermaidConfigs(...configs: MermaidRuntimeConfig[]): MermaidRuntimeConfig {
|
|
145
|
-
const merged: MermaidFrontmatterMap = {}
|
|
146
|
-
|
|
147
|
-
for (const config of configs) {
|
|
148
|
-
mergeInto(merged, runtimeConfigToFrontmatterMap(config))
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return normalizeMermaidRuntimeConfig(merged)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function mergeFrontmatterMaps(
|
|
155
|
-
base: MermaidFrontmatterMap,
|
|
156
|
-
override: MermaidFrontmatterMap,
|
|
157
|
-
): MermaidFrontmatterMap {
|
|
158
|
-
const merged = cloneFrontmatterMap(base)
|
|
159
|
-
|
|
160
|
-
for (const [key, value] of Object.entries(override)) {
|
|
161
|
-
if (value === undefined) continue
|
|
162
|
-
|
|
163
|
-
const existing = merged[key]
|
|
164
|
-
if (isFrontmatterMap(existing) && isFrontmatterMap(value)) {
|
|
165
|
-
merged[key] = mergeFrontmatterMaps(existing, value)
|
|
166
|
-
continue
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
merged[key] = cloneFrontmatterValue(value)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return merged
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function getFrontmatterMap(
|
|
176
|
-
root: MermaidFrontmatterMap,
|
|
177
|
-
path: readonly string[],
|
|
178
|
-
): MermaidFrontmatterMap | undefined {
|
|
179
|
-
let current: MermaidFrontmatterValue | undefined = root
|
|
180
|
-
for (const segment of path) {
|
|
181
|
-
if (!isFrontmatterMap(current)) return undefined
|
|
182
|
-
current = current[segment]
|
|
183
|
-
}
|
|
184
|
-
return isFrontmatterMap(current) ? current : undefined
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function getFrontmatterScalar<T extends MermaidFrontmatterScalar>(
|
|
188
|
-
root: MermaidFrontmatterMap,
|
|
189
|
-
path: readonly string[],
|
|
190
|
-
): T | undefined {
|
|
191
|
-
let current: MermaidFrontmatterValue | undefined = root
|
|
192
|
-
for (const segment of path) {
|
|
193
|
-
if (!isFrontmatterMap(current)) return undefined
|
|
194
|
-
current = current[segment]
|
|
195
|
-
}
|
|
196
|
-
return current !== undefined && !Array.isArray(current) && (typeof current !== 'object' || current === null)
|
|
197
|
-
? current as T
|
|
198
|
-
: undefined
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function getFrontmatterList<T extends MermaidFrontmatterValue = MermaidFrontmatterValue>(
|
|
202
|
-
root: MermaidFrontmatterMap,
|
|
203
|
-
path: readonly string[],
|
|
204
|
-
): T[] | undefined {
|
|
205
|
-
let current: MermaidFrontmatterValue | undefined = root
|
|
206
|
-
for (const segment of path) {
|
|
207
|
-
if (!isFrontmatterMap(current)) return undefined
|
|
208
|
-
current = current[segment]
|
|
209
|
-
}
|
|
210
|
-
return Array.isArray(current) ? current as T[] : undefined
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function runtimeConfigToFrontmatterMap(config: MermaidRuntimeConfig): MermaidFrontmatterMap {
|
|
214
|
-
return canonicalizeFrontmatterMap(toFrontmatterMap(config) ?? {})
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function normalizeMermaidRuntimeConfig(raw: MermaidFrontmatterMap): MermaidRuntimeConfig {
|
|
218
|
-
const config = cloneFrontmatterMap(raw) as MermaidRuntimeConfig
|
|
219
|
-
|
|
220
|
-
if (isFrontmatterMap(config.themeVariables)) {
|
|
221
|
-
config.themeVariables = cloneFrontmatterMap(config.themeVariables) as MermaidThemeVariables
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (isFrontmatterMap(config.timeline)) {
|
|
225
|
-
config.timeline = normalizeTimelineRuntimeConfig(config.timeline)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (isFrontmatterMap(config.journey)) {
|
|
229
|
-
config.journey = normalizeJourneyRuntimeConfig(config.journey)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return config
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function normalizeTimelineRuntimeConfig(raw: MermaidFrontmatterMap): TimelineRuntimeConfig {
|
|
236
|
-
const config = cloneFrontmatterMap(raw) as TimelineRuntimeConfig
|
|
237
|
-
|
|
238
|
-
if (typeof config.disableMulticolor !== 'boolean') {
|
|
239
|
-
delete config.disableMulticolor
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const sectionFills = normalizeStringArray(config.sectionFills)
|
|
243
|
-
if (sectionFills.length > 0) {
|
|
244
|
-
config.sectionFills = sectionFills
|
|
245
|
-
} else {
|
|
246
|
-
delete config.sectionFills
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const sectionColours = normalizeStringArray(config.sectionColours)
|
|
250
|
-
const sectionColors = normalizeStringArray((config as MermaidFrontmatterMap).sectionColors)
|
|
251
|
-
if (sectionColours.length > 0) {
|
|
252
|
-
config.sectionColours = sectionColours
|
|
253
|
-
} else if (sectionColors.length > 0) {
|
|
254
|
-
config.sectionColours = sectionColors
|
|
255
|
-
} else {
|
|
256
|
-
delete config.sectionColours
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
delete (config as MermaidFrontmatterMap).sectionColors
|
|
260
|
-
return config
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function normalizeJourneyRuntimeConfig(raw: MermaidFrontmatterMap): JourneyRuntimeConfig {
|
|
264
|
-
const config = cloneFrontmatterMap(raw) as JourneyRuntimeConfig
|
|
265
|
-
|
|
266
|
-
for (const key of [
|
|
267
|
-
'diagramMarginX', 'diagramMarginY', 'leftMargin', 'maxLabelWidth',
|
|
268
|
-
'width', 'height', 'boxMargin', 'boxTextMargin', 'noteMargin',
|
|
269
|
-
'messageMargin', 'bottomMarginAdj', 'taskMargin', 'activationWidth',
|
|
270
|
-
] as const) {
|
|
271
|
-
const value = config[key]
|
|
272
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
273
|
-
delete config[key]
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (typeof config.useMaxWidth !== 'boolean') delete config.useMaxWidth
|
|
278
|
-
if (typeof config.rightAngles !== 'boolean') delete config.rightAngles
|
|
279
|
-
|
|
280
|
-
if (config.messageAlign !== 'left' && config.messageAlign !== 'center' && config.messageAlign !== 'right') {
|
|
281
|
-
delete config.messageAlign
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const taskFontSize = normalizeCssFontSize(config.taskFontSize)
|
|
285
|
-
if (taskFontSize !== undefined) config.taskFontSize = taskFontSize
|
|
286
|
-
else delete config.taskFontSize
|
|
287
|
-
|
|
288
|
-
for (const key of ['taskFontFamily', 'textPlacement', 'titleColor', 'titleFontFamily'] as const) {
|
|
289
|
-
if (typeof config[key] !== 'string' || config[key]!.trim().length === 0) delete config[key]
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const titleFontSize = normalizeCssFontSize(config.titleFontSize)
|
|
293
|
-
if (titleFontSize !== undefined) config.titleFontSize = titleFontSize
|
|
294
|
-
else delete config.titleFontSize
|
|
295
|
-
|
|
296
|
-
for (const key of ['actorColours', 'sectionFills', 'sectionColours'] as const) {
|
|
297
|
-
const colors = normalizeStringArray(config[key])
|
|
298
|
-
if (colors.length > 0) config[key] = colors
|
|
299
|
-
else delete config[key]
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return config
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function normalizeCssFontSize(value: MermaidConfigValue | undefined): string | number | undefined {
|
|
306
|
-
if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value
|
|
307
|
-
if (typeof value === 'string' && value.trim().length > 0) return value.trim()
|
|
308
|
-
return undefined
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function normalizeStringArray(value: MermaidConfigValue | undefined): string[] {
|
|
312
|
-
return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : []
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
function mergeInto(target: MermaidFrontmatterMap, source: MermaidFrontmatterMap | undefined): void {
|
|
316
|
-
if (!source) return
|
|
317
|
-
|
|
318
|
-
for (const [key, value] of Object.entries(source)) {
|
|
319
|
-
if (value === undefined) continue
|
|
320
|
-
|
|
321
|
-
if (Array.isArray(value)) {
|
|
322
|
-
target[key] = value.map(entry => cloneFrontmatterValue(entry)!)
|
|
323
|
-
continue
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (isFrontmatterMap(value)) {
|
|
327
|
-
const existing = isFrontmatterMap(target[key]) ? target[key] as MermaidFrontmatterMap : {}
|
|
328
|
-
target[key] = existing
|
|
329
|
-
mergeInto(existing, value)
|
|
330
|
-
continue
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
target[key] = value
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function canonicalizeFrontmatterMap(raw: MermaidFrontmatterMap): MermaidFrontmatterMap {
|
|
338
|
-
const topLevel = cloneFrontmatterMap(raw)
|
|
339
|
-
const configRoot = isFrontmatterMap(topLevel.config) ? topLevel.config : undefined
|
|
340
|
-
delete topLevel.config
|
|
341
|
-
|
|
342
|
-
return configRoot ? mergeFrontmatterMaps(configRoot, topLevel) : topLevel
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function parseYamlDocument(text: string): MermaidFrontmatterMap {
|
|
346
|
-
try {
|
|
347
|
-
return toFrontmatterMap(YAML.parse(text)) ?? {}
|
|
348
|
-
} catch {
|
|
349
|
-
return {}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function extractInitDirectives(text: string): { body: string; frontmatter: MermaidFrontmatterMap } {
|
|
354
|
-
let merged: MermaidFrontmatterMap = {}
|
|
355
|
-
|
|
356
|
-
const body = text.replace(INIT_DIRECTIVE_REGEX, (_match, payload: string) => {
|
|
357
|
-
const parsed = parseDirectiveMap(payload)
|
|
358
|
-
if (parsed) merged = mergeFrontmatterMaps(merged, canonicalizeFrontmatterMap(parsed))
|
|
359
|
-
return ''
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
return { body, frontmatter: merged }
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function parseDirectiveMap(text: string): MermaidFrontmatterMap | undefined {
|
|
366
|
-
try {
|
|
367
|
-
return toFrontmatterMap(YAML.parse(text))
|
|
368
|
-
} catch {
|
|
369
|
-
return parseLooseObjectLiteral(text)
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function toFrontmatterMap(value: unknown): MermaidFrontmatterMap | undefined {
|
|
374
|
-
if (!isPlainObject(value)) return undefined
|
|
375
|
-
|
|
376
|
-
const map: MermaidFrontmatterMap = {}
|
|
377
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
378
|
-
const parsed = toFrontmatterValue(entry)
|
|
379
|
-
if (parsed !== undefined) map[key] = parsed
|
|
380
|
-
}
|
|
381
|
-
return map
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function toFrontmatterValue(value: unknown): MermaidFrontmatterValue | undefined {
|
|
385
|
-
if (value === null) return null
|
|
386
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value
|
|
387
|
-
|
|
388
|
-
if (Array.isArray(value)) {
|
|
389
|
-
const items: MermaidFrontmatterList = []
|
|
390
|
-
for (const entry of value) {
|
|
391
|
-
const parsed = toFrontmatterValue(entry)
|
|
392
|
-
if (parsed === undefined) return undefined
|
|
393
|
-
items.push(parsed)
|
|
394
|
-
}
|
|
395
|
-
return items
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return toFrontmatterMap(value)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
function cloneFrontmatterMap(value: MermaidFrontmatterMap): MermaidFrontmatterMap {
|
|
402
|
-
const clone: MermaidFrontmatterMap = {}
|
|
403
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
404
|
-
clone[key] = cloneFrontmatterValue(entry)
|
|
405
|
-
}
|
|
406
|
-
return clone
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function cloneFrontmatterValue(value: MermaidFrontmatterValue | undefined): MermaidFrontmatterValue | undefined {
|
|
410
|
-
if (value === undefined || value === null) return value
|
|
411
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value
|
|
412
|
-
if (Array.isArray(value)) return value.map(entry => cloneFrontmatterValue(entry)!)
|
|
413
|
-
return cloneFrontmatterMap(value)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function isFrontmatterMap(value: MermaidFrontmatterValue | undefined): value is MermaidFrontmatterMap {
|
|
417
|
-
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
421
|
-
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function parseScalar(valueText: string): MermaidFrontmatterScalar {
|
|
425
|
-
if ((valueText.startsWith('"') && valueText.endsWith('"')) || (valueText.startsWith("'") && valueText.endsWith("'"))) {
|
|
426
|
-
return unescapeQuotedString(valueText)
|
|
427
|
-
}
|
|
428
|
-
if (valueText === 'true') return true
|
|
429
|
-
if (valueText === 'false') return false
|
|
430
|
-
if (valueText === 'null') return null
|
|
431
|
-
if (/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(valueText)) return Number(valueText)
|
|
432
|
-
return valueText
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function parseLooseObjectLiteral(text: string): MermaidFrontmatterMap | undefined {
|
|
436
|
-
const parsed = parseFlowValue(text.trim())
|
|
437
|
-
return isFrontmatterMap(parsed) ? parsed : undefined
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function parseFlowValue(text: string): MermaidFrontmatterValue | undefined {
|
|
441
|
-
const trimmed = text.trim()
|
|
442
|
-
if (trimmed.length === 0) return undefined
|
|
443
|
-
|
|
444
|
-
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
445
|
-
return parseFlowMap(trimmed.slice(1, -1))
|
|
446
|
-
}
|
|
447
|
-
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
448
|
-
return parseFlowList(trimmed.slice(1, -1))
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return parseScalar(trimmed)
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function parseFlowMap(text: string): MermaidFrontmatterMap | undefined {
|
|
455
|
-
const map: MermaidFrontmatterMap = {}
|
|
456
|
-
for (const entry of splitFlowEntries(text)) {
|
|
457
|
-
const colonIdx = findSeparatorIndex(entry, ':')
|
|
458
|
-
if (colonIdx === -1) return undefined
|
|
459
|
-
|
|
460
|
-
const rawKey = entry.slice(0, colonIdx).trim()
|
|
461
|
-
const rawValue = entry.slice(colonIdx + 1).trim()
|
|
462
|
-
const key = parseFlowKey(rawKey)
|
|
463
|
-
if (!key) return undefined
|
|
464
|
-
|
|
465
|
-
const value = parseFlowValue(rawValue)
|
|
466
|
-
if (value === undefined) return undefined
|
|
467
|
-
map[key] = value
|
|
468
|
-
}
|
|
469
|
-
return map
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function parseFlowList(text: string): MermaidFrontmatterList | undefined {
|
|
473
|
-
const values: MermaidFrontmatterList = []
|
|
474
|
-
for (const entry of splitFlowEntries(text)) {
|
|
475
|
-
const value = parseFlowValue(entry)
|
|
476
|
-
if (value === undefined) return undefined
|
|
477
|
-
values.push(value)
|
|
478
|
-
}
|
|
479
|
-
return values
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function parseFlowKey(text: string): string | undefined {
|
|
483
|
-
const trimmed = text.trim()
|
|
484
|
-
if (!trimmed) return undefined
|
|
485
|
-
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
486
|
-
return unescapeQuotedString(trimmed)
|
|
487
|
-
}
|
|
488
|
-
return trimmed
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
function splitFlowEntries(text: string): string[] {
|
|
492
|
-
const entries: string[] = []
|
|
493
|
-
let current = ''
|
|
494
|
-
let quote: '"' | "'" | null = null
|
|
495
|
-
let braceDepth = 0
|
|
496
|
-
let bracketDepth = 0
|
|
497
|
-
|
|
498
|
-
for (let i = 0; i < text.length; i++) {
|
|
499
|
-
const char = text[i]!
|
|
500
|
-
if (quote) {
|
|
501
|
-
current += char
|
|
502
|
-
if (char === quote && text[i - 1] !== '\\') quote = null
|
|
503
|
-
continue
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (char === '"' || char === "'") {
|
|
507
|
-
quote = char
|
|
508
|
-
current += char
|
|
509
|
-
continue
|
|
510
|
-
}
|
|
511
|
-
if (char === '{') {
|
|
512
|
-
braceDepth++
|
|
513
|
-
current += char
|
|
514
|
-
continue
|
|
515
|
-
}
|
|
516
|
-
if (char === '}') {
|
|
517
|
-
braceDepth--
|
|
518
|
-
current += char
|
|
519
|
-
continue
|
|
520
|
-
}
|
|
521
|
-
if (char === '[') {
|
|
522
|
-
bracketDepth++
|
|
523
|
-
current += char
|
|
524
|
-
continue
|
|
525
|
-
}
|
|
526
|
-
if (char === ']') {
|
|
527
|
-
bracketDepth--
|
|
528
|
-
current += char
|
|
529
|
-
continue
|
|
530
|
-
}
|
|
531
|
-
if (char === ',' && braceDepth === 0 && bracketDepth === 0) {
|
|
532
|
-
const value = current.trim()
|
|
533
|
-
if (value) entries.push(value)
|
|
534
|
-
current = ''
|
|
535
|
-
continue
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
current += char
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
const trailing = current.trim()
|
|
542
|
-
if (trailing) entries.push(trailing)
|
|
543
|
-
return entries
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function findSeparatorIndex(text: string, separator: ':' | ','): number {
|
|
547
|
-
let quote: '"' | "'" | null = null
|
|
548
|
-
let braceDepth = 0
|
|
549
|
-
let bracketDepth = 0
|
|
550
|
-
|
|
551
|
-
for (let i = 0; i < text.length; i++) {
|
|
552
|
-
const char = text[i]!
|
|
553
|
-
if (quote) {
|
|
554
|
-
if (char === quote && text[i - 1] !== '\\') quote = null
|
|
555
|
-
continue
|
|
556
|
-
}
|
|
557
|
-
if (char === '"' || char === "'") {
|
|
558
|
-
quote = char
|
|
559
|
-
continue
|
|
560
|
-
}
|
|
561
|
-
if (char === '{') {
|
|
562
|
-
braceDepth++
|
|
563
|
-
continue
|
|
564
|
-
}
|
|
565
|
-
if (char === '}') {
|
|
566
|
-
braceDepth--
|
|
567
|
-
continue
|
|
568
|
-
}
|
|
569
|
-
if (char === '[') {
|
|
570
|
-
bracketDepth++
|
|
571
|
-
continue
|
|
572
|
-
}
|
|
573
|
-
if (char === ']') {
|
|
574
|
-
bracketDepth--
|
|
575
|
-
continue
|
|
576
|
-
}
|
|
577
|
-
if (char === separator && braceDepth === 0 && bracketDepth === 0) return i
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
return -1
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function unescapeQuotedString(valueText: string): string {
|
|
584
|
-
try {
|
|
585
|
-
if (valueText.startsWith("'")) {
|
|
586
|
-
return valueText
|
|
587
|
-
.slice(1, -1)
|
|
588
|
-
.replace(/\\\\/g, '\\')
|
|
589
|
-
.replace(/\\'/g, "'")
|
|
590
|
-
}
|
|
591
|
-
return JSON.parse(valueText)
|
|
592
|
-
} catch {
|
|
593
|
-
return valueText.slice(1, -1)
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
export type RoutedDiagramType = 'flowchart' | 'architecture' | 'sequence' | 'class' | 'er' | 'timeline' | 'journey' | 'xychart' | 'pie' | 'quadrant' | 'gantt'
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Return the logical Mermaid lines after frontmatter/init/comment normalization.
|
|
601
|
-
* This is a thin wrapper around the richer source preprocessing used by the
|
|
602
|
-
* public SVG and ASCII entry points.
|
|
603
|
-
*/
|
|
604
|
-
export function preprocessMermaidLines(text: string): string[] {
|
|
605
|
-
return preprocessMermaidSource(text).lines
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* Detect the routed Mermaid diagram family from a normalized first logical line.
|
|
610
|
-
* Returns null for headers that are known not to be routed by this renderer.
|
|
611
|
-
*/
|
|
612
|
-
export function detectDiagramTypeFromFirstLine(firstLine: string): RoutedDiagramType | null {
|
|
613
|
-
const line = firstLine.split(';')[0]?.trim().toLowerCase() ?? ''
|
|
614
|
-
if (/^architecture-beta\s*$/.test(line)) return 'architecture'
|
|
615
|
-
if (/^xychart(-beta)?\b/.test(line)) return 'xychart'
|
|
616
|
-
if (/^pie\b/.test(line)) return 'pie'
|
|
617
|
-
if (/^quadrantchart\s*$/.test(line)) return 'quadrant'
|
|
618
|
-
// Upstream accepts (and ignores) a direction token after the header.
|
|
619
|
-
if (/^timeline(?:\s+(?:td|tb|lr|bt|rl))?\s*$/.test(line)) return 'timeline'
|
|
620
|
-
if (/^gantt\s*$/.test(line)) return 'gantt'
|
|
621
|
-
if (/^journey\s*$/.test(line)) return 'journey'
|
|
622
|
-
if (/^sequencediagram\s*$/.test(line)) return 'sequence'
|
|
623
|
-
if (/^classdiagram\s*$/.test(line)) return 'class'
|
|
624
|
-
if (/^erdiagram\s*$/.test(line)) return 'er'
|
|
625
|
-
if (/^(?:flowchart|graph|swimlane)\b/.test(line) || /^statediagram(?:-v2)?\s*$/.test(line)) return 'flowchart'
|
|
626
|
-
return null
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/**
|
|
630
|
-
* Looser family recognition for agent parsing: malformed known-family headers
|
|
631
|
-
* should become opaque round-trip bodies instead of UNKNOWN_HEADER errors.
|
|
632
|
-
*/
|
|
633
|
-
export function detectLooseDiagramTypeFromFirstLine(firstLine: string): RoutedDiagramType | null {
|
|
634
|
-
const line = firstLine.split(';')[0]?.trim().toLowerCase() ?? ''
|
|
635
|
-
if (/^architecture-beta\b/.test(line)) return 'architecture'
|
|
636
|
-
if (/^xychart(-beta)?\b/.test(line)) return 'xychart'
|
|
637
|
-
if (/^pie\b/.test(line)) return 'pie'
|
|
638
|
-
if (/^quadrantchart\b/.test(line)) return 'quadrant'
|
|
639
|
-
if (/^timeline\b/.test(line)) return 'timeline'
|
|
640
|
-
if (/^gantt\b/.test(line)) return 'gantt'
|
|
641
|
-
if (/^journey\b/.test(line)) return 'journey'
|
|
642
|
-
if (/^sequencediagram\b/.test(line)) return 'sequence'
|
|
643
|
-
if (/^classdiagram\b/.test(line)) return 'class'
|
|
644
|
-
if (/^erdiagram\b/.test(line)) return 'er'
|
|
645
|
-
if (/^(?:flowchart|graph|swimlane)\b/.test(line) || /^statediagram(?:-v2)?\b/.test(line)) return 'flowchart'
|
|
646
|
-
return null
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
/**
|
|
650
|
-
* Detect the routed Mermaid diagram family from source text. Unknown headers
|
|
651
|
-
* intentionally route to the legacy flowchart path for backwards compatibility.
|
|
652
|
-
*/
|
|
653
|
-
export function detectDiagramType(text: string): RoutedDiagramType {
|
|
654
|
-
return detectDiagramTypeFromFirstLine(preprocessMermaidLines(text)[0] ?? '') ?? 'flowchart'
|
|
655
|
-
}
|