agentic-mermaid 0.1.0 → 0.1.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/CHANGELOG.md +149 -4
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +25 -32
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/fonts/FONT-LICENSES.md +9 -5
- package/assets/fonts/Inter-Bold.ttf +0 -0
- package/assets/fonts/Inter-Medium.ttf +0 -0
- package/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +81 -1822
- package/dist/agent.js +336 -36839
- package/dist/agentic-mermaid-mcp.js +7 -37908
- package/dist/am.js +33638 -36261
- package/dist/chunk-4QECACIA.js +4702 -0
- package/dist/chunk-CDPKJ2BQ.js +37 -0
- package/dist/chunk-QKDLMOJJ.js +3487 -0
- package/dist/chunk-WI35L7IR.js +615 -0
- package/dist/chunk-WN3YDOBG.js +149 -0
- package/dist/chunk-ZYMMKQ3I.js +62637 -0
- package/dist/index-D54dqSdC.d.ts +6777 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -32836
- package/dist/mcp-cli-SBQSTJFC.js +17 -0
- package/docs/README.md +14 -6
- package/docs/schemas/style-spec.schema.json +1221 -30
- 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 +70 -43
- package/server.json +41 -0
- 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/AGENT_NATIVE.md +0 -602
- package/Instructions_for_agents.md +0 -83
- package/TODO.md +0 -662
- package/assets/hero.png +0 -0
- package/dist/index-x2Cj1dVv.d.ts +0 -1089
- package/docs/agent-api-cookbook.md +0 -308
- package/docs/agent-mutation-policy.md +0 -104
- package/docs/agent-workflow-examples.md +0 -46
- package/docs/api.md +0 -203
- package/docs/ascii.md +0 -103
- 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/comparison.md +0 -94
- package/docs/config.md +0 -105
- 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 -71
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/custom-style-cookbook.md +0 -154
- 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-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 -433
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -125
- 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/diagram-families.md +0 -193
- package/docs/features.md +0 -147
- package/docs/fork-differences.md +0 -130
- package/docs/getting-started.md +0 -175
- package/docs/issue-derived-test-cases.md +0 -156
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -496
- 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 -118
- 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/mcp-code-mode-rationale.md +0 -101
- package/docs/mcp-http-transport.md +0 -223
- package/docs/mermaid-layout-complaints.md +0 -837
- package/docs/mutation-testing.md +0 -206
- 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/docs/quality.md +0 -212
- package/docs/react.md +0 -97
- package/docs/style-authoring.md +0 -133
- package/docs/testing-strategy.md +0 -335
- package/docs/theming.md +0 -162
- 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 -364
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -337
- package/src/agent/families-builtin.ts +0 -934
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -826
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -414
- 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 -439
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -117
- 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 -979
- package/src/agent/verify.ts +0 -571
- 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 -189
- 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 -128
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- 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 -1117
- package/src/cli/init-agent.ts +0 -147
- 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/font-manifest.ts +0 -27
- 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 -520
- package/src/journey/parser.ts +0 -218
- package/src/journey/renderer.ts +0 -811
- 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 -387
- 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 -72
- 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 -639
- 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 -454
- 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/ascii/index.ts
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Agentic Mermaid — ASCII renderer public API (published as agentic-mermaid)
|
|
3
|
-
//
|
|
4
|
-
// Renders Mermaid diagrams to ASCII or Unicode box-drawing art.
|
|
5
|
-
// No external dependencies — pure TypeScript.
|
|
6
|
-
//
|
|
7
|
-
// Supported diagram types:
|
|
8
|
-
// - Flowcharts (graph TD / flowchart LR) — grid-based layout with A* pathfinding
|
|
9
|
-
// - State diagrams (stateDiagram-v2) — same pipeline as flowcharts
|
|
10
|
-
// - Architecture diagrams (architecture-beta) — dedicated entrypoint built on graph layout
|
|
11
|
-
// - Sequence diagrams (sequenceDiagram) — column-based timeline layout
|
|
12
|
-
// - Class diagrams (classDiagram) — level-based UML layout
|
|
13
|
-
// - ER diagrams (erDiagram) — grid layout with crow's foot notation
|
|
14
|
-
// - Timeline diagrams (timeline) — chronological outline with grouped milestones
|
|
15
|
-
// - User Journey diagrams (journey) — scored task lists with actor annotations
|
|
16
|
-
// - XY charts (xychart / xychart-beta)
|
|
17
|
-
// - Pie charts (pie) — proportional slice list
|
|
18
|
-
// - Quadrant charts (quadrantChart) — coordinate plot with legend
|
|
19
|
-
// - Gantt charts (gantt) — sectioned schedule bars and milestones
|
|
20
|
-
//
|
|
21
|
-
// Usage:
|
|
22
|
-
// import { renderMermaidASCII } from 'agentic-mermaid'
|
|
23
|
-
// const ascii = renderMermaidASCII('graph LR\n A --> B')
|
|
24
|
-
// ============================================================================
|
|
25
|
-
|
|
26
|
-
import { detectColorMode, DEFAULT_ASCII_THEME, diagramColorsToAsciiTheme } from './ansi.ts'
|
|
27
|
-
import type { AsciiConfig, AsciiTheme, ColorMode } from './types.ts'
|
|
28
|
-
import { normalizeMermaidSource, detectDiagramTypeFromFirstLine } from '../mermaid-source.ts'
|
|
29
|
-
import type { MermaidRuntimeConfig } from '../mermaid-source.ts'
|
|
30
|
-
import { getFamily } from '../render-family-hooks.ts'
|
|
31
|
-
import type { DiagramKind } from '../agent/types.ts'
|
|
32
|
-
|
|
33
|
-
// Re-export types for external use
|
|
34
|
-
export type { AsciiTheme, ColorMode }
|
|
35
|
-
export { DEFAULT_ASCII_THEME, detectColorMode, diagramColorsToAsciiTheme }
|
|
36
|
-
|
|
37
|
-
export interface AsciiRenderOptions {
|
|
38
|
-
/** true = ASCII chars (+,-,|,>), false = Unicode box-drawing (┌,─,│,►). Default: false */
|
|
39
|
-
useAscii?: boolean
|
|
40
|
-
/** Horizontal spacing between nodes. Default: 5 */
|
|
41
|
-
paddingX?: number
|
|
42
|
-
/** Vertical spacing between nodes. Default: 5 */
|
|
43
|
-
paddingY?: number
|
|
44
|
-
/** Padding inside node boxes. Default: 1 */
|
|
45
|
-
boxBorderPadding?: number
|
|
46
|
-
/**
|
|
47
|
-
* Color mode for output.
|
|
48
|
-
* - 'none': No colors (plain text)
|
|
49
|
-
* - 'auto': Auto-detect (terminal ANSI capabilities, or HTML in browsers)
|
|
50
|
-
* - 'ansi16': 16-color ANSI
|
|
51
|
-
* - 'ansi256': 256-color xterm
|
|
52
|
-
* - 'truecolor': 24-bit RGB
|
|
53
|
-
* - 'html': HTML <span> tags with inline color styles (for browser rendering)
|
|
54
|
-
* Default: 'auto'
|
|
55
|
-
*/
|
|
56
|
-
colorMode?: ColorMode | 'auto'
|
|
57
|
-
/** Theme colors for ASCII output. Uses default theme if not provided. */
|
|
58
|
-
theme?: Partial<AsciiTheme>
|
|
59
|
-
/** Optional Mermaid-style runtime config (analogous to initialize/frontmatter config). */
|
|
60
|
-
mermaidConfig?: MermaidRuntimeConfig
|
|
61
|
-
/**
|
|
62
|
-
* Loop 9 M13: cap output width in characters. When set, node labels wrap
|
|
63
|
-
* at word boundaries to fit within `maxWidth / 3` per node. Single words
|
|
64
|
-
* longer than `maxWidth / 2` render anyway (with a console.warn). The
|
|
65
|
-
* overall canvas may still exceed `maxWidth` if the diagram has many
|
|
66
|
-
* parallel columns; this is best-effort wrapping, not hard truncation.
|
|
67
|
-
*/
|
|
68
|
-
maxWidth?: number
|
|
69
|
-
/**
|
|
70
|
-
* Explicit "today" for the Gantt todayMarker (date in the diagram's
|
|
71
|
-
* dateFormat or ISO YYYY-MM-DD). Gantt never reads the wall clock; without
|
|
72
|
-
* this the marker is not drawn.
|
|
73
|
-
*/
|
|
74
|
-
ganttToday?: string
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Render Mermaid diagram text to an ASCII/Unicode string.
|
|
79
|
-
*
|
|
80
|
-
* Synchronous — no async layout engine needed (unlike the SVG renderer).
|
|
81
|
-
* Auto-detects diagram type from the header line and dispatches to
|
|
82
|
-
* the appropriate renderer.
|
|
83
|
-
*
|
|
84
|
-
* @param text - Mermaid source text (any supported diagram type)
|
|
85
|
-
* @param options - Rendering options
|
|
86
|
-
* @returns Multi-line ASCII/Unicode string
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```ts
|
|
90
|
-
* const result = renderMermaidAscii(`
|
|
91
|
-
* graph LR
|
|
92
|
-
* A --> B --> C
|
|
93
|
-
* `, { useAscii: true })
|
|
94
|
-
*
|
|
95
|
-
* // Output:
|
|
96
|
-
* // +---+ +---+ +---+
|
|
97
|
-
* // | | | | | |
|
|
98
|
-
* // | A |---->| B |---->| C |
|
|
99
|
-
* // | | | | | |
|
|
100
|
-
* // +---+ +---+ +---+
|
|
101
|
-
* ```
|
|
102
|
-
*/
|
|
103
|
-
export function renderMermaidASCII(
|
|
104
|
-
text: string,
|
|
105
|
-
options: AsciiRenderOptions = {},
|
|
106
|
-
): string {
|
|
107
|
-
const config: AsciiConfig = {
|
|
108
|
-
useAscii: options.useAscii ?? false,
|
|
109
|
-
paddingX: options.paddingX ?? 5,
|
|
110
|
-
paddingY: options.paddingY ?? 5,
|
|
111
|
-
boxBorderPadding: options.boxBorderPadding ?? 1,
|
|
112
|
-
graphDirection: 'TD', // default, overridden for flowcharts below
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Resolve color mode ('auto' or unset → detect environment, otherwise use specified mode)
|
|
116
|
-
const colorMode: ColorMode = options.colorMode === 'auto' || options.colorMode === undefined
|
|
117
|
-
? detectColorMode()
|
|
118
|
-
: options.colorMode
|
|
119
|
-
|
|
120
|
-
// Merge user theme with defaults
|
|
121
|
-
const theme: AsciiTheme = { ...DEFAULT_ASCII_THEME, ...options.theme }
|
|
122
|
-
// Loop 9 M13: apply pre-render label wrapping if maxWidth is set. Walks
|
|
123
|
-
// the source and rewrites bracket-quoted labels to insert <br/> at word
|
|
124
|
-
// boundaries when the label exceeds (maxWidth / 3). Family-agnostic
|
|
125
|
-
// because every renderer respects <br/> as a hard line break.
|
|
126
|
-
const sourceText = options.maxWidth ? wrapLabelsInSource(text, options.maxWidth) : text
|
|
127
|
-
const normalizedSource = normalizeMermaidSource(sourceText, options.mermaidConfig ?? {})
|
|
128
|
-
|
|
129
|
-
const diagramType = detectDiagramTypeFromFirstLine(normalizedSource.firstLine) ?? 'flowchart'
|
|
130
|
-
|
|
131
|
-
const family = getFamily(diagramType as DiagramKind)
|
|
132
|
-
if (!family?.renderAscii) {
|
|
133
|
-
throw new Error(`No ASCII renderer registered for Mermaid family ${diagramType}`)
|
|
134
|
-
}
|
|
135
|
-
return family.renderAscii({
|
|
136
|
-
source: normalizedSource,
|
|
137
|
-
config,
|
|
138
|
-
colorMode,
|
|
139
|
-
theme,
|
|
140
|
-
options: {
|
|
141
|
-
maxWidth: options.maxWidth,
|
|
142
|
-
ganttToday: options.ganttToday,
|
|
143
|
-
},
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** @deprecated Use `renderMermaidASCII` */
|
|
148
|
-
export const renderMermaidAscii = renderMermaidASCII
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Loop 9 M13: wrap a single label string at word boundaries to fit a column
|
|
152
|
-
* width. Words longer than `maxLineWidth` emit a warn and render as-is.
|
|
153
|
-
* Returns the label with `<br/>` separators between wrapped lines.
|
|
154
|
-
*/
|
|
155
|
-
export function wrapLabel(text: string, maxLineWidth: number): string {
|
|
156
|
-
if (text.length <= maxLineWidth) return text
|
|
157
|
-
const words = text.split(/\s+/).filter(Boolean)
|
|
158
|
-
const lines: string[] = []
|
|
159
|
-
let current = ''
|
|
160
|
-
for (const word of words) {
|
|
161
|
-
if (word.length > maxLineWidth) {
|
|
162
|
-
// Word can't fit on its own line — emit warning, render anyway.
|
|
163
|
-
// eslint-disable-next-line no-console
|
|
164
|
-
console.warn(`wrapLabel: word "${word.slice(0, 20)}..." exceeds maxLineWidth=${maxLineWidth}`)
|
|
165
|
-
if (current) lines.push(current)
|
|
166
|
-
lines.push(word)
|
|
167
|
-
current = ''
|
|
168
|
-
continue
|
|
169
|
-
}
|
|
170
|
-
if (current.length === 0) current = word
|
|
171
|
-
else if (current.length + 1 + word.length <= maxLineWidth) current += ' ' + word
|
|
172
|
-
else { lines.push(current); current = word }
|
|
173
|
-
}
|
|
174
|
-
if (current) lines.push(current)
|
|
175
|
-
return lines.join('<br/>')
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Walk Mermaid source and wrap bracket-quoted labels (`["..."]`, `[...]`,
|
|
180
|
-
* `(...)`, `{...}`, `((...))`) whose contents exceed `maxWidth / 3` columns.
|
|
181
|
-
* Family-agnostic — works on flowchart node labels, sequence message text,
|
|
182
|
-
* class members, etc.
|
|
183
|
-
*/
|
|
184
|
-
function wrapLabelsInSource(source: string, maxWidth: number): string {
|
|
185
|
-
const perLabel = Math.max(8, Math.floor(maxWidth / 3))
|
|
186
|
-
// Match bracket-quoted labels: ["text"], [text], (text), {text}, ((text))
|
|
187
|
-
// Skip already-wrapped labels (those containing <br/>) and identifier-only labels.
|
|
188
|
-
return source.replace(/(\[\[|\(\(|\[|\(|\{)("?)([^\[\]\(\)\{\}\n]+)\2(\]\]|\)\)|\]|\)|\})/g,
|
|
189
|
-
(full, open: string, quote: string, inner: string, close: string) => {
|
|
190
|
-
if (inner.length <= perLabel || inner.includes('<br/>')) return full
|
|
191
|
-
// Don't wrap identifier-like content (no spaces, looks like a variable)
|
|
192
|
-
if (!inner.includes(' ')) return full
|
|
193
|
-
const wrapped = wrapLabel(inner, perLabel)
|
|
194
|
-
return open + quote + wrapped + quote + close
|
|
195
|
-
})
|
|
196
|
-
}
|
package/src/ascii/journey.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// ASCII renderer — journey diagrams
|
|
3
|
-
//
|
|
4
|
-
// Renders Mermaid user journeys as compact scored task lists with optional
|
|
5
|
-
// section headings and actor annotations.
|
|
6
|
-
// ============================================================================
|
|
7
|
-
|
|
8
|
-
import { parseJourneyDiagram } from '../journey/parser.ts'
|
|
9
|
-
import { preprocessMermaidLines } from '../mermaid-source.ts'
|
|
10
|
-
import { colorizeLine, DEFAULT_ASCII_THEME } from './ansi.ts'
|
|
11
|
-
import type { AsciiConfig, AsciiTheme, CharRole, ColorMode } from './types.ts'
|
|
12
|
-
import { visualWidth } from './width.ts'
|
|
13
|
-
|
|
14
|
-
interface StyledSegment {
|
|
15
|
-
text: string
|
|
16
|
-
role: CharRole | null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function renderStyledLine(
|
|
20
|
-
segments: StyledSegment[],
|
|
21
|
-
colorMode: ColorMode,
|
|
22
|
-
theme: AsciiTheme,
|
|
23
|
-
): string {
|
|
24
|
-
const chars: string[] = []
|
|
25
|
-
const roles: (CharRole | null)[] = []
|
|
26
|
-
|
|
27
|
-
for (const segment of segments) {
|
|
28
|
-
for (const char of segment.text) {
|
|
29
|
-
chars.push(char)
|
|
30
|
-
roles.push(segment.role)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return colorizeLine(chars, roles, theme, colorMode)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function renderScoreSegments(score: number, useAscii: boolean): StyledSegment[] {
|
|
38
|
-
const filled = useAscii ? '#' : '●'
|
|
39
|
-
const empty = useAscii ? '.' : '○'
|
|
40
|
-
|
|
41
|
-
const segments: StyledSegment[] = [
|
|
42
|
-
{ text: filled.repeat(score), role: 'arrow' },
|
|
43
|
-
{ text: empty.repeat(5 - score), role: 'border' },
|
|
44
|
-
]
|
|
45
|
-
return segments.filter(segment => segment.text.length > 0)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Render a Mermaid journey diagram to ASCII/Unicode text.
|
|
50
|
-
*/
|
|
51
|
-
export function renderJourneyAscii(
|
|
52
|
-
text: string,
|
|
53
|
-
config: AsciiConfig,
|
|
54
|
-
colorMode: ColorMode = 'none',
|
|
55
|
-
theme: AsciiTheme = DEFAULT_ASCII_THEME,
|
|
56
|
-
maxWidth?: number,
|
|
57
|
-
): string {
|
|
58
|
-
const lines = preprocessMermaidLines(text)
|
|
59
|
-
const diagram = parseJourneyDiagram(lines)
|
|
60
|
-
const useAscii = config.useAscii
|
|
61
|
-
const out: string[] = []
|
|
62
|
-
const pushLine = (segments: StyledSegment[] = []): void => {
|
|
63
|
-
out.push(segments.length === 0 ? '' : renderStyledLine(segments, colorMode, theme))
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (diagram.title) {
|
|
67
|
-
for (const line of wrapText(diagram.title, maxWidth)) {
|
|
68
|
-
pushLine([{ text: line, role: 'text' }])
|
|
69
|
-
}
|
|
70
|
-
pushLine()
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
for (let sectionIndex = 0; sectionIndex < diagram.sections.length; sectionIndex++) {
|
|
74
|
-
const section = diagram.sections[sectionIndex]!
|
|
75
|
-
|
|
76
|
-
if (section.label) {
|
|
77
|
-
for (const line of wrapText(section.label.replace(/\n/g, ' / '), maxWidth ? Math.max(1, maxWidth - 2) : undefined)) {
|
|
78
|
-
pushLine([
|
|
79
|
-
{ text: '[', role: 'border' },
|
|
80
|
-
{ text: line, role: 'text' },
|
|
81
|
-
{ text: ']', role: 'border' },
|
|
82
|
-
])
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (let taskIndex = 0; taskIndex < section.tasks.length; taskIndex++) {
|
|
87
|
-
const task = section.tasks[taskIndex]!
|
|
88
|
-
const scoreSegments = renderScoreSegments(task.score, useAscii)
|
|
89
|
-
const scoreWidth = 5
|
|
90
|
-
const taskPrefixWidth = scoreWidth + 1
|
|
91
|
-
const taskLines = wrapText(task.text, maxWidth ? Math.max(1, maxWidth - taskPrefixWidth) : undefined)
|
|
92
|
-
|
|
93
|
-
pushLine([
|
|
94
|
-
...scoreSegments,
|
|
95
|
-
{ text: ' ', role: null },
|
|
96
|
-
{ text: taskLines[0] ?? '', role: 'text' },
|
|
97
|
-
])
|
|
98
|
-
for (const line of taskLines.slice(1)) {
|
|
99
|
-
pushLine([
|
|
100
|
-
{ text: ' '.repeat(taskPrefixWidth), role: null },
|
|
101
|
-
{ text: line, role: 'text' },
|
|
102
|
-
])
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (task.actors.length > 0) {
|
|
106
|
-
const actorPrefix = ' by '
|
|
107
|
-
const actorLines = wrapText(task.actors.join(', '), maxWidth ? Math.max(1, maxWidth - visualWidth(actorPrefix)) : undefined)
|
|
108
|
-
actorLines.forEach((line, index) => {
|
|
109
|
-
pushLine([
|
|
110
|
-
{ text: index === 0 ? ' ' : ' '.repeat(visualWidth(actorPrefix)), role: null },
|
|
111
|
-
...(index === 0 ? [
|
|
112
|
-
{ text: 'by', role: 'border' as const },
|
|
113
|
-
{ text: ' ', role: null },
|
|
114
|
-
] : []),
|
|
115
|
-
{ text: line, role: 'text' },
|
|
116
|
-
])
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const moreTasks = taskIndex < section.tasks.length - 1
|
|
121
|
-
const moreSections = sectionIndex < diagram.sections.length - 1
|
|
122
|
-
if (moreTasks || moreSections) pushLine()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return out.join('\n').trimEnd()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function wrapText(text: string, maxWidth: number | undefined): string[] {
|
|
130
|
-
if (!maxWidth || !Number.isFinite(maxWidth) || maxWidth <= 0) return text.split('\n')
|
|
131
|
-
const limit = Math.max(1, Math.floor(maxWidth))
|
|
132
|
-
const lines: string[] = []
|
|
133
|
-
for (const paragraph of text.split('\n')) {
|
|
134
|
-
lines.push(...wrapParagraph(paragraph, limit))
|
|
135
|
-
}
|
|
136
|
-
return lines.length > 0 ? lines : ['']
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function wrapParagraph(text: string, maxWidth: number): string[] {
|
|
140
|
-
if (visualWidth(text) <= maxWidth) return [text]
|
|
141
|
-
const words = text.split(/\s+/).filter(Boolean)
|
|
142
|
-
if (words.length === 0) return ['']
|
|
143
|
-
|
|
144
|
-
const lines: string[] = []
|
|
145
|
-
let current = ''
|
|
146
|
-
for (const word of words) {
|
|
147
|
-
if (!current) {
|
|
148
|
-
const chunks = breakWord(word, maxWidth)
|
|
149
|
-
lines.push(...chunks.slice(0, -1))
|
|
150
|
-
current = chunks[chunks.length - 1] ?? ''
|
|
151
|
-
continue
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const candidate = `${current} ${word}`
|
|
155
|
-
if (visualWidth(candidate) <= maxWidth) {
|
|
156
|
-
current = candidate
|
|
157
|
-
continue
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
lines.push(current)
|
|
161
|
-
const chunks = breakWord(word, maxWidth)
|
|
162
|
-
lines.push(...chunks.slice(0, -1))
|
|
163
|
-
current = chunks[chunks.length - 1] ?? ''
|
|
164
|
-
}
|
|
165
|
-
if (current) lines.push(current)
|
|
166
|
-
return lines
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function breakWord(word: string, maxWidth: number): string[] {
|
|
170
|
-
if (visualWidth(word) <= maxWidth) return [word]
|
|
171
|
-
const lines: string[] = []
|
|
172
|
-
let current = ''
|
|
173
|
-
let width = 0
|
|
174
|
-
|
|
175
|
-
for (const char of word) {
|
|
176
|
-
const charWidth = visualWidth(char)
|
|
177
|
-
if (current && width + charWidth > maxWidth) {
|
|
178
|
-
lines.push(current)
|
|
179
|
-
current = char
|
|
180
|
-
width = charWidth
|
|
181
|
-
} else {
|
|
182
|
-
current += char
|
|
183
|
-
width += charWidth
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (current) lines.push(current)
|
|
188
|
-
return lines
|
|
189
|
-
}
|
package/src/ascii/meta.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// renderMermaidASCIIWithMeta — render ASCII art and return per-region metadata.
|
|
3
|
-
//
|
|
4
|
-
// Loop 9 M11. The intended use case is TUI integration: callers (e.g. a TUI
|
|
5
|
-
// debugger) need to know which characters in the rendered grid correspond to
|
|
6
|
-
// which node id so they can highlight, click-target, or scroll-anchor a
|
|
7
|
-
// region.
|
|
8
|
-
//
|
|
9
|
-
// Implementation choice: rather than instrument every renderer to capture
|
|
10
|
-
// per-character roles (which would touch grid.ts, canvas.ts, draw.ts, and
|
|
11
|
-
// every family-specific renderer), this module derives regions by scanning
|
|
12
|
-
// the rendered string for known node labels. This is honest about what it
|
|
13
|
-
// can prove:
|
|
14
|
-
// - Flowchart / state: regions for each node by label match. Flowchart
|
|
15
|
-
// subgraphs are exposed as best-effort label regions using the same stable
|
|
16
|
-
// ids as SVG/layout JSON; edge spans remain deferred until renderer
|
|
17
|
-
// instrumentation lands.
|
|
18
|
-
// - Sequence / class / ER / timeline / gantt / journey / xychart /
|
|
19
|
-
// architecture: regions for each participant / class / entity / section /
|
|
20
|
-
// task, derived by label scan. Best-effort; some renderers wrap labels
|
|
21
|
-
// and the scan misses them.
|
|
22
|
-
//
|
|
23
|
-
// Determinism: identical input → identical regions (no randomness, no
|
|
24
|
-
// timestamps). Region order follows the scan order (top-down, left-to-right).
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
|
-
import { renderMermaidASCII, type AsciiRenderOptions } from './index.ts'
|
|
28
|
-
import { parseMermaid } from '../agent/parse.ts'
|
|
29
|
-
|
|
30
|
-
export type RegionKind = 'node' | 'edge' | 'label' | 'subgraph'
|
|
31
|
-
|
|
32
|
-
export type AsciiWarningCode = 'ASCII_RENDER_FAILED' | 'ASCII_EDGE_REGION_UNMAPPED'
|
|
33
|
-
export interface AsciiWarning { code: AsciiWarningCode; message: string; severity: 'degraded' }
|
|
34
|
-
|
|
35
|
-
export const ASCII_ROUTE_PARITY_CONTRACT = {
|
|
36
|
-
version: 1,
|
|
37
|
-
routeIntent: 'shared-route-classes',
|
|
38
|
-
svgSource: 'route-contract classifyRoutes() + certificates',
|
|
39
|
-
asciiSource: 'grid router seeded with classifyRoutes() routeClass metadata, longest-path layering, and direct-lane-first routing',
|
|
40
|
-
degradationWarnings: ['ASCII_RENDER_FAILED', 'ASCII_EDGE_REGION_UNMAPPED'],
|
|
41
|
-
} as const
|
|
42
|
-
|
|
43
|
-
export interface AsciiRegion {
|
|
44
|
-
kind: RegionKind
|
|
45
|
-
/** Stable identifier for the diagram element (e.g. node id, participant id). */
|
|
46
|
-
id: string
|
|
47
|
-
/** Optional 1-based source line where the element was declared, when known. */
|
|
48
|
-
sourceLine?: number
|
|
49
|
-
/** 0-based row in the rendered canvas where the region starts. */
|
|
50
|
-
canvasRow: number
|
|
51
|
-
/** 0-based column where the region starts (inclusive). */
|
|
52
|
-
canvasColStart: number
|
|
53
|
-
/** 0-based column where the region ends (exclusive). */
|
|
54
|
-
canvasColEnd: number
|
|
55
|
-
/** Optional row count when the region spans multiple lines (boxed node). */
|
|
56
|
-
rowSpan?: number
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface AsciiWithMeta {
|
|
60
|
-
ascii: string
|
|
61
|
-
regions: AsciiRegion[]
|
|
62
|
-
warnings: AsciiWarning[]
|
|
63
|
-
routeParity: typeof ASCII_ROUTE_PARITY_CONTRACT
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function renderMermaidASCIIWithMeta(input: string, opts: AsciiRenderOptions = {}): AsciiWithMeta {
|
|
67
|
-
// Mirror parseMermaid's structured-or-opaque rule: if the renderer rejects
|
|
68
|
-
// the source (parse failure), surface empty regions rather than throwing —
|
|
69
|
-
// the meta API is read-only and shouldn't propagate parser errors.
|
|
70
|
-
try {
|
|
71
|
-
const ascii = renderMermaidASCII(input, opts)
|
|
72
|
-
const regions = deriveRegions(ascii, input)
|
|
73
|
-
return { ascii, regions, warnings: deriveWarnings(input, regions), routeParity: ASCII_ROUTE_PARITY_CONTRACT }
|
|
74
|
-
} catch {
|
|
75
|
-
return { ascii: '', regions: [], warnings: [{ code: 'ASCII_RENDER_FAILED', severity: 'degraded', message: 'ASCII/Unicode renderer failed; no route parity evidence is available.' }], routeParity: ASCII_ROUTE_PARITY_CONTRACT }
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function deriveWarnings(source: string, regions: AsciiRegion[]): AsciiWarning[] {
|
|
80
|
-
const parsed = parseMermaid(source)
|
|
81
|
-
if (!parsed.ok) return []
|
|
82
|
-
if (parsed.value.body.kind !== 'flowchart' && parsed.value.body.kind !== 'state') return []
|
|
83
|
-
const edgeRegions = regions.filter(r => r.kind === 'edge').length
|
|
84
|
-
const edgeCount = parsed.value.body.kind === 'flowchart' ? parsed.value.body.graph.edges.length : parsed.value.body.transitions.length
|
|
85
|
-
if (edgeCount > 0 && edgeRegions === 0) {
|
|
86
|
-
return [{
|
|
87
|
-
code: 'ASCII_EDGE_REGION_UNMAPPED',
|
|
88
|
-
severity: 'degraded',
|
|
89
|
-
message: 'ASCII route drawing follows the explicit route-intent parity mapping, but per-edge cell spans are not instrumented yet.',
|
|
90
|
-
}]
|
|
91
|
-
}
|
|
92
|
-
return []
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
interface Candidate { id: string; label: string; sourceLine?: number; kind?: RegionKind }
|
|
96
|
-
|
|
97
|
-
function addCandidate(out: Candidate[], id: string, label: string | undefined, sourceLine?: number, kind: RegionKind = 'node'): void {
|
|
98
|
-
const normalized = label?.trim()
|
|
99
|
-
if (!normalized) return
|
|
100
|
-
out.push({ id, label: normalized, sourceLine, kind })
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function addCandidateWithFallback(out: Candidate[], id: string, label: string | undefined, sourceLine?: number, kind: RegionKind = 'node'): void {
|
|
104
|
-
addCandidate(out, id, label, sourceLine, kind)
|
|
105
|
-
if (label !== id) addCandidate(out, id, id, sourceLine, kind)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function deriveRegions(ascii: string, source: string): AsciiRegion[] {
|
|
109
|
-
const candidates = candidatesForDiagram(source)
|
|
110
|
-
if (candidates.length === 0) return []
|
|
111
|
-
const lines = ascii.split('\n')
|
|
112
|
-
// Sort longest label first so 'Alpha' wins over 'A' on the same row.
|
|
113
|
-
const sorted = [...candidates].sort((a, b) => b.label.length - a.label.length)
|
|
114
|
-
// Track regions already assigned so a label scan doesn't double-match.
|
|
115
|
-
const used = new Set<string>()
|
|
116
|
-
const out: AsciiRegion[] = []
|
|
117
|
-
for (let row = 0; row < lines.length; row++) {
|
|
118
|
-
const line = lines[row]!
|
|
119
|
-
for (const c of sorted) {
|
|
120
|
-
if (used.has(c.id)) continue
|
|
121
|
-
const idx = line.indexOf(c.label)
|
|
122
|
-
if (idx < 0) continue
|
|
123
|
-
out.push({
|
|
124
|
-
kind: c.kind ?? 'node',
|
|
125
|
-
id: c.id,
|
|
126
|
-
sourceLine: c.sourceLine,
|
|
127
|
-
canvasRow: row,
|
|
128
|
-
canvasColStart: idx,
|
|
129
|
-
canvasColEnd: idx + c.label.length,
|
|
130
|
-
})
|
|
131
|
-
used.add(c.id)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Stable order: by canvasRow then canvasColStart.
|
|
135
|
-
out.sort((a, b) => a.canvasRow - b.canvasRow || a.canvasColStart - b.canvasColStart)
|
|
136
|
-
return out
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function candidatesForDiagram(source: string): Candidate[] {
|
|
140
|
-
const r = parseMermaid(source)
|
|
141
|
-
if (!r.ok) return []
|
|
142
|
-
const d = r.value
|
|
143
|
-
if (d.body.kind === 'flowchart') {
|
|
144
|
-
const out: Candidate[] = []
|
|
145
|
-
for (const n of d.body.graph.nodes.values()) addCandidateWithFallback(out, n.id, n.label && n.label.length > 0 ? n.label : n.id, d.source.nodes.get(n.id)?.line)
|
|
146
|
-
const visit = (groups: typeof d.body.graph.subgraphs): void => {
|
|
147
|
-
for (const g of groups) {
|
|
148
|
-
addCandidateWithFallback(out, g.id, g.label || g.id, d.source.groups.get(g.id)?.line, 'subgraph')
|
|
149
|
-
visit(g.children)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
visit(d.body.graph.subgraphs)
|
|
153
|
-
return out
|
|
154
|
-
}
|
|
155
|
-
if (d.body.kind === 'state') {
|
|
156
|
-
const out: Candidate[] = []
|
|
157
|
-
const visit = (states: typeof d.body.states): void => {
|
|
158
|
-
for (const s of states) {
|
|
159
|
-
addCandidateWithFallback(out, s.id, s.label ?? s.id)
|
|
160
|
-
if (s.states) visit(s.states)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
visit(d.body.states)
|
|
164
|
-
return out
|
|
165
|
-
}
|
|
166
|
-
if (d.body.kind === 'sequence') {
|
|
167
|
-
return d.body.participants.flatMap(p => {
|
|
168
|
-
const out: Candidate[] = []
|
|
169
|
-
addCandidateWithFallback(out, p.id, p.label || p.id)
|
|
170
|
-
return out
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
if (d.body.kind === 'class') {
|
|
174
|
-
return d.body.classes.flatMap(c => {
|
|
175
|
-
const out: Candidate[] = []
|
|
176
|
-
addCandidateWithFallback(out, c.id, c.label || c.id)
|
|
177
|
-
return out
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
if (d.body.kind === 'er') {
|
|
181
|
-
return d.body.entities.map(e => ({ id: e.id, label: e.id }))
|
|
182
|
-
}
|
|
183
|
-
if (d.body.kind === 'timeline') {
|
|
184
|
-
const out: Candidate[] = []
|
|
185
|
-
for (const s of d.body.sections) {
|
|
186
|
-
addCandidate(out, s.id, s.label)
|
|
187
|
-
for (const p of s.periods) addCandidate(out, p.id, p.label)
|
|
188
|
-
}
|
|
189
|
-
return out
|
|
190
|
-
}
|
|
191
|
-
if (d.body.kind === 'journey') {
|
|
192
|
-
const out: Candidate[] = []
|
|
193
|
-
addCandidate(out, 'title', d.body.title)
|
|
194
|
-
for (const s of d.body.sections) {
|
|
195
|
-
addCandidate(out, s.id, s.label)
|
|
196
|
-
for (const t of s.tasks) addCandidate(out, t.id, t.text)
|
|
197
|
-
}
|
|
198
|
-
return out
|
|
199
|
-
}
|
|
200
|
-
if (d.body.kind === 'architecture') {
|
|
201
|
-
const out: Candidate[] = []
|
|
202
|
-
for (const g of d.body.groups) addCandidateWithFallback(out, g.id, g.label || g.id)
|
|
203
|
-
for (const s of d.body.services) addCandidateWithFallback(out, s.id, s.label || s.id)
|
|
204
|
-
return out
|
|
205
|
-
}
|
|
206
|
-
if (d.body.kind === 'xychart') {
|
|
207
|
-
const out: Candidate[] = []
|
|
208
|
-
addCandidate(out, 'title', d.body.title)
|
|
209
|
-
addCandidate(out, 'x-axis', d.body.xAxis?.name)
|
|
210
|
-
addCandidate(out, 'y-axis', d.body.yAxis?.name)
|
|
211
|
-
d.body.xAxis?.categories?.forEach((label, index) => addCandidate(out, `x-category-${index}`, label))
|
|
212
|
-
for (const s of d.body.series) addCandidate(out, s.id, s.name)
|
|
213
|
-
return out
|
|
214
|
-
}
|
|
215
|
-
if (d.body.kind === 'pie') {
|
|
216
|
-
const out: Candidate[] = []
|
|
217
|
-
addCandidate(out, 'title', d.body.title)
|
|
218
|
-
for (const s of d.body.slices) addCandidate(out, s.id, s.label)
|
|
219
|
-
return out
|
|
220
|
-
}
|
|
221
|
-
if (d.body.kind === 'quadrant') {
|
|
222
|
-
const out: Candidate[] = []
|
|
223
|
-
addCandidate(out, 'title', d.body.title)
|
|
224
|
-
addCandidate(out, 'x-axis-near', d.body.xAxis?.near)
|
|
225
|
-
addCandidate(out, 'x-axis-far', d.body.xAxis?.far)
|
|
226
|
-
addCandidate(out, 'y-axis-near', d.body.yAxis?.near)
|
|
227
|
-
addCandidate(out, 'y-axis-far', d.body.yAxis?.far)
|
|
228
|
-
d.body.quadrants.forEach((label, index) => addCandidate(out, `quadrant-${index + 1}`, label))
|
|
229
|
-
d.body.points.forEach((p, index) => addCandidate(out, `point-${index}`, p.label))
|
|
230
|
-
return out
|
|
231
|
-
}
|
|
232
|
-
if (d.body.kind === 'gantt') {
|
|
233
|
-
// Issue #26 WS10: gantt tasks/sections as stable click-mappable regions.
|
|
234
|
-
// Region ids prefer the Mermaid task id (the durable handle agents use in
|
|
235
|
-
// after/until/click) over the parse-order internal id.
|
|
236
|
-
const out: Candidate[] = []
|
|
237
|
-
for (const s of d.body.sections) {
|
|
238
|
-
addCandidate(out, s.id, s.label)
|
|
239
|
-
for (const t of s.tasks) addCandidate(out, t.taskId ?? t.id, t.label)
|
|
240
|
-
}
|
|
241
|
-
return out
|
|
242
|
-
}
|
|
243
|
-
return []
|
|
244
|
-
}
|