agentic-mermaid 0.1.1 → 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.
Files changed (336) hide show
  1. package/CHANGELOG.md +144 -4
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/README.md +25 -32
  4. package/THIRD_PARTY_NOTICES.md +29 -0
  5. package/dist/agent-core.d.ts +463 -0
  6. package/dist/agent-core.js +397 -0
  7. package/dist/agent.d.ts +73 -1868
  8. package/dist/agent.js +336 -37325
  9. package/dist/agentic-mermaid-mcp.js +7 -38399
  10. package/dist/am.js +33632 -40445
  11. package/dist/chunk-4QECACIA.js +4702 -0
  12. package/dist/chunk-CDPKJ2BQ.js +37 -0
  13. package/dist/chunk-QKDLMOJJ.js +3487 -0
  14. package/dist/chunk-WI35L7IR.js +615 -0
  15. package/dist/chunk-WN3YDOBG.js +149 -0
  16. package/dist/chunk-ZYMMKQ3I.js +62637 -0
  17. package/dist/index-D54dqSdC.d.ts +6777 -0
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.js +310 -33014
  20. package/dist/mcp-cli-SBQSTJFC.js +17 -0
  21. package/docs/README.md +14 -6
  22. package/docs/schemas/style-spec.schema.json +1221 -30
  23. package/examples/styles/catalog.json +54 -0
  24. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  25. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  26. package/examples/styles/cupertino-prototype.mmd +10 -0
  27. package/examples/styles/cupertino-prototype.style.json +63 -0
  28. package/examples/styles/mid-century-report.style.json +2 -2
  29. package/examples/styles/star-chart-atlas.style.json +2 -1
  30. package/examples/styles/transit-route-map.style.json +2 -2
  31. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  32. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  33. package/package.json +68 -43
  34. package/server.json +2 -2
  35. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  36. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  37. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  38. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  39. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  40. package/AGENT_NATIVE.md +0 -607
  41. package/Instructions_for_agents.md +0 -89
  42. package/TODO.md +0 -684
  43. package/assets/hero.png +0 -0
  44. package/dist/index-wjRjbsP-.d.ts +0 -1102
  45. package/docs/agent-api-cookbook.md +0 -308
  46. package/docs/agent-mutation-policy.md +0 -104
  47. package/docs/agent-workflow-examples.md +0 -46
  48. package/docs/api.md +0 -211
  49. package/docs/ascii.md +0 -103
  50. package/docs/assets/improvements/box-start-connector.png +0 -0
  51. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  52. package/docs/assets/improvements/er-content-loss.png +0 -0
  53. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  54. package/docs/assets/improvements/gantt-family.png +0 -0
  55. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  56. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  57. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  58. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  59. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  60. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  61. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  62. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  63. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  64. package/docs/comparison.md +0 -94
  65. package/docs/config.md +0 -105
  66. package/docs/contributing/adding-diagram-types.md +0 -144
  67. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  68. package/docs/contributing/diagram-family-citizenship.md +0 -82
  69. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  70. package/docs/contributing/lessons-learned.md +0 -276
  71. package/docs/contributing/releasing.md +0 -82
  72. package/docs/contributing/visual-review-evidence.md +0 -53
  73. package/docs/custom-style-cookbook.md +0 -154
  74. package/docs/design/families/README.md +0 -26
  75. package/docs/design/families/architecture-beta.md +0 -48
  76. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  77. package/docs/design/families/gantt-research.md +0 -137
  78. package/docs/design/families/gantt.md +0 -329
  79. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  80. package/docs/design/families/journey-experience-curve-implementation.png +0 -0
  81. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  82. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  83. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  84. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  85. package/docs/design/families/journey-migration-parity.md +0 -439
  86. package/docs/design/families/journey-section-overlap-after.png +0 -0
  87. package/docs/design/families/journey-section-overlap-before.png +0 -0
  88. package/docs/design/families/journey-usage-research.md +0 -423
  89. package/docs/design/families/journey.md +0 -153
  90. package/docs/design/families/xychart.md +0 -85
  91. package/docs/design/system/README.md +0 -45
  92. package/docs/design/system/abstraction-audit.md +0 -224
  93. package/docs/design/system/abstraction-recommendations.md +0 -655
  94. package/docs/design/system/architecture.mmd +0 -29
  95. package/docs/design/system/architecture.svg +0 -137
  96. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  97. package/docs/design/system/layout-rubric.md +0 -138
  98. package/docs/design/system/route-contracts.md +0 -848
  99. package/docs/design/system/source-preservation-ladder.md +0 -52
  100. package/docs/design/system/styles-rollout.md +0 -216
  101. package/docs/design/system/ugly-layouts.md +0 -119
  102. package/docs/diagram-families.md +0 -193
  103. package/docs/features.md +0 -147
  104. package/docs/fork-differences.md +0 -130
  105. package/docs/getting-started.md +0 -175
  106. package/docs/issue-derived-test-cases.md +0 -156
  107. package/docs/layout-characterization/README.md +0 -245
  108. package/docs/layout-characterization/contact-sheet-families.md +0 -498
  109. package/docs/layout-characterization/contact-sheet.md +0 -638
  110. package/docs/layout-characterization/properties.md +0 -281
  111. package/docs/layout-characterization/visual-quality.md +0 -160
  112. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  113. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  114. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  115. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  116. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  117. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
  118. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  119. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  120. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  121. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  122. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  123. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  124. package/docs/mcp-code-mode-rationale.md +0 -101
  125. package/docs/mcp-http-transport.md +0 -223
  126. package/docs/mermaid-layout-complaints.md +0 -837
  127. package/docs/mutation-testing.md +0 -206
  128. package/docs/project/divergences.md +0 -98
  129. package/docs/project/dogfooding-docs-strategy.md +0 -66
  130. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  131. package/docs/project/lessons-learned.md +0 -1019
  132. package/docs/project/remove-role-styling-plan.md +0 -176
  133. package/docs/quality.md +0 -215
  134. package/docs/react.md +0 -97
  135. package/docs/style-authoring.md +0 -133
  136. package/docs/testing-strategy.md +0 -335
  137. package/docs/theming.md +0 -162
  138. package/examples/agent-improve-auth-flow.ts +0 -206
  139. package/examples/agent-loop.ts +0 -53
  140. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  141. package/llms.txt +0 -104
  142. package/skill-evals/README.md +0 -77
  143. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  144. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  145. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  146. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  147. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  148. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  149. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  150. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  151. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  152. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  153. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  154. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  155. package/skill-evals/shared-benchmark.json +0 -510
  156. package/src/agent/analyze.ts +0 -157
  157. package/src/agent/apply.ts +0 -146
  158. package/src/agent/architecture-body.ts +0 -458
  159. package/src/agent/certificates.ts +0 -110
  160. package/src/agent/class-body.ts +0 -319
  161. package/src/agent/core.ts +0 -207
  162. package/src/agent/create.ts +0 -106
  163. package/src/agent/describe.ts +0 -368
  164. package/src/agent/er-body.ts +0 -224
  165. package/src/agent/facts.ts +0 -360
  166. package/src/agent/families-builtin.ts +0 -920
  167. package/src/agent/families.ts +0 -232
  168. package/src/agent/family-layouts.ts +0 -832
  169. package/src/agent/flowchart-body.ts +0 -372
  170. package/src/agent/flowchart-unsupported.ts +0 -243
  171. package/src/agent/font-coverage.ts +0 -288
  172. package/src/agent/gantt-body.ts +0 -495
  173. package/src/agent/index.ts +0 -10
  174. package/src/agent/journey-body.ts +0 -374
  175. package/src/agent/label-metrics.ts +0 -28
  176. package/src/agent/layout-to-rendered.ts +0 -63
  177. package/src/agent/mutate.ts +0 -97
  178. package/src/agent/mutation-ops.ts +0 -51
  179. package/src/agent/op-schema.ts +0 -443
  180. package/src/agent/parse.ts +0 -223
  181. package/src/agent/pie-body.ts +0 -270
  182. package/src/agent/png.ts +0 -171
  183. package/src/agent/quadrant-body.ts +0 -336
  184. package/src/agent/quality.ts +0 -363
  185. package/src/agent/readability-audit.ts +0 -92
  186. package/src/agent/rendered-route-audit.ts +0 -61
  187. package/src/agent/sequence-body.ts +0 -345
  188. package/src/agent/serialize.ts +0 -214
  189. package/src/agent/state-body.ts +0 -589
  190. package/src/agent/structural-count.ts +0 -150
  191. package/src/agent/timeline-body.ts +0 -280
  192. package/src/agent/trace-log.ts +0 -43
  193. package/src/agent/types.ts +0 -995
  194. package/src/agent/verify.ts +0 -603
  195. package/src/agent/xychart-body.ts +0 -472
  196. package/src/architecture/config.ts +0 -282
  197. package/src/architecture/layout.ts +0 -496
  198. package/src/architecture/parser.ts +0 -375
  199. package/src/architecture/renderer.ts +0 -611
  200. package/src/architecture/types.ts +0 -113
  201. package/src/ascii/ansi.ts +0 -423
  202. package/src/ascii/architecture.ts +0 -210
  203. package/src/ascii/canvas.ts +0 -443
  204. package/src/ascii/class-diagram.ts +0 -697
  205. package/src/ascii/converter.ts +0 -475
  206. package/src/ascii/draw.ts +0 -1693
  207. package/src/ascii/edge-bundling.ts +0 -328
  208. package/src/ascii/edge-routing.ts +0 -330
  209. package/src/ascii/er-diagram.ts +0 -439
  210. package/src/ascii/gantt.ts +0 -276
  211. package/src/ascii/grid.ts +0 -747
  212. package/src/ascii/index.ts +0 -196
  213. package/src/ascii/journey.ts +0 -169
  214. package/src/ascii/meta.ts +0 -244
  215. package/src/ascii/multiline-utils.ts +0 -80
  216. package/src/ascii/pathfinder.ts +0 -301
  217. package/src/ascii/pie.ts +0 -67
  218. package/src/ascii/quadrant.ts +0 -158
  219. package/src/ascii/reverse.ts +0 -216
  220. package/src/ascii/sequence.ts +0 -499
  221. package/src/ascii/shapes/circle.ts +0 -27
  222. package/src/ascii/shapes/corners.ts +0 -143
  223. package/src/ascii/shapes/diamond.ts +0 -27
  224. package/src/ascii/shapes/hexagon.ts +0 -27
  225. package/src/ascii/shapes/index.ts +0 -106
  226. package/src/ascii/shapes/rectangle.ts +0 -168
  227. package/src/ascii/shapes/rounded.ts +0 -27
  228. package/src/ascii/shapes/special.ts +0 -332
  229. package/src/ascii/shapes/stadium.ts +0 -110
  230. package/src/ascii/shapes/state.ts +0 -192
  231. package/src/ascii/shapes/types.ts +0 -73
  232. package/src/ascii/timeline.ts +0 -141
  233. package/src/ascii/types.ts +0 -312
  234. package/src/ascii/validate.ts +0 -120
  235. package/src/ascii/width.ts +0 -81
  236. package/src/ascii/wrap.ts +0 -131
  237. package/src/ascii/xychart.ts +0 -919
  238. package/src/browser.ts +0 -29
  239. package/src/class/layout.ts +0 -240
  240. package/src/class/parser.ts +0 -332
  241. package/src/class/renderer.ts +0 -638
  242. package/src/class/types.ts +0 -129
  243. package/src/cli/agent-instructions.ts +0 -1
  244. package/src/cli/am-bin.ts +0 -6
  245. package/src/cli/exit-codes.ts +0 -18
  246. package/src/cli/index.ts +0 -1132
  247. package/src/cli/init-agent.ts +0 -147
  248. package/src/cli/run-entrypoint.ts +0 -10
  249. package/src/color-resolver.ts +0 -152
  250. package/src/elk-instance.ts +0 -114
  251. package/src/er/layout.ts +0 -187
  252. package/src/er/parser.ts +0 -226
  253. package/src/er/renderer.ts +0 -792
  254. package/src/er/types.ts +0 -99
  255. package/src/family-rubric.ts +0 -425
  256. package/src/font-manifest.ts +0 -31
  257. package/src/gantt/layout.ts +0 -365
  258. package/src/gantt/parser.ts +0 -282
  259. package/src/gantt/pipeline.ts +0 -27
  260. package/src/gantt/renderer.ts +0 -414
  261. package/src/gantt/schedule.ts +0 -513
  262. package/src/gantt/types.ts +0 -255
  263. package/src/index.ts +0 -368
  264. package/src/journey/layout.ts +0 -554
  265. package/src/journey/parse-core.ts +0 -251
  266. package/src/journey/parser.ts +0 -101
  267. package/src/journey/renderer.ts +0 -917
  268. package/src/journey/types.ts +0 -144
  269. package/src/layout/geometry.ts +0 -182
  270. package/src/layout/pass.ts +0 -142
  271. package/src/layout/passes/index.ts +0 -3916
  272. package/src/layout-engine.ts +0 -1565
  273. package/src/layout-rubric.ts +0 -659
  274. package/src/mcp/artifacts.ts +0 -137
  275. package/src/mcp/dynamic-harness.ts +0 -31
  276. package/src/mcp/dynamic-modules.d.ts +0 -7
  277. package/src/mcp/facade.ts +0 -608
  278. package/src/mcp/harness-runtime.ts +0 -203
  279. package/src/mcp/hosted-server.ts +0 -468
  280. package/src/mcp/mcp-bin.ts +0 -3
  281. package/src/mcp/mcp-cli.ts +0 -82
  282. package/src/mcp/protocol.ts +0 -13
  283. package/src/mcp/sandbox.ts +0 -195
  284. package/src/mcp/sdk-decl.ts +0 -391
  285. package/src/mcp/server.ts +0 -362
  286. package/src/mcp/tool-surface.ts +0 -146
  287. package/src/mermaid-source.ts +0 -655
  288. package/src/multiline-utils.ts +0 -228
  289. package/src/parser.ts +0 -1031
  290. package/src/pie/layout.ts +0 -168
  291. package/src/pie/parser.ts +0 -136
  292. package/src/pie/renderer.ts +0 -234
  293. package/src/pie/types.ts +0 -82
  294. package/src/quadrant/layout.ts +0 -191
  295. package/src/quadrant/parser.ts +0 -183
  296. package/src/quadrant/renderer.ts +0 -322
  297. package/src/quadrant/types.ts +0 -113
  298. package/src/render-family-hooks.ts +0 -244
  299. package/src/renderer.ts +0 -1099
  300. package/src/route-contracts.ts +0 -2723
  301. package/src/scene/backend.ts +0 -76
  302. package/src/scene/fidelity.ts +0 -183
  303. package/src/scene/hybrid-backend.ts +0 -174
  304. package/src/scene/ir.ts +0 -181
  305. package/src/scene/marks.ts +0 -127
  306. package/src/scene/rough-backend.ts +0 -378
  307. package/src/scene/seed.ts +0 -46
  308. package/src/scene/style-registry.ts +0 -556
  309. package/src/sequence/layout.ts +0 -456
  310. package/src/sequence/parser.ts +0 -255
  311. package/src/sequence/renderer.ts +0 -604
  312. package/src/sequence/types.ts +0 -154
  313. package/src/shape-clipping.ts +0 -351
  314. package/src/shared/accessibility-directives.ts +0 -22
  315. package/src/shared/batched.ts +0 -61
  316. package/src/shared/color-math.ts +0 -93
  317. package/src/shared/svg-a11y.ts +0 -29
  318. package/src/shared/syntax-error.ts +0 -17
  319. package/src/shared/unicode-ranges.ts +0 -135
  320. package/src/styles.ts +0 -318
  321. package/src/svg-paths.ts +0 -28
  322. package/src/text-metrics.ts +0 -248
  323. package/src/theme.ts +0 -710
  324. package/src/timeline/layout.ts +0 -381
  325. package/src/timeline/parser.ts +0 -215
  326. package/src/timeline/renderer.ts +0 -538
  327. package/src/timeline/types.ts +0 -105
  328. package/src/types.ts +0 -462
  329. package/src/website-worker-modules.d.ts +0 -18
  330. package/src/xychart/axis-utils.ts +0 -109
  331. package/src/xychart/colors.ts +0 -124
  332. package/src/xychart/config.ts +0 -88
  333. package/src/xychart/layout.ts +0 -656
  334. package/src/xychart/parser.ts +0 -504
  335. package/src/xychart/renderer.ts +0 -640
  336. package/src/xychart/types.ts +0 -282
@@ -1,301 +0,0 @@
1
- // ============================================================================
2
- // ASCII renderer — A* pathfinding for edge routing
3
- //
4
- // Ported from AlexanderGrooff/mermaid-ascii cmd/arrow.go.
5
- // Uses A* search with a corner-penalizing heuristic to find clean
6
- // paths between nodes on the grid. Prefers straight lines over zigzags.
7
- // ============================================================================
8
-
9
- import type { GridCoord, AsciiNode, Direction } from './types.ts'
10
- import { gridKey, gridCoordEquals } from './types.ts'
11
-
12
- // ============================================================================
13
- // Priority queue (min-heap) for A* open set
14
- // ============================================================================
15
-
16
- interface PQItem {
17
- coord: GridCoord
18
- priority: number
19
- /**
20
- * Monotonic insertion sequence. Used as a deterministic FIFO tie-breaker so
21
- * equal-priority nodes pop in insertion order regardless of heap topology
22
- * (upstream lukilabs#113). Without this, the order two equal-priority cells
23
- * are visited depends on incidental heap shape, which let sibling fan-out
24
- * edges pick inconsistent corners and produce L-shaped detours.
25
- */
26
- seq: number
27
- }
28
-
29
- /**
30
- * Simple min-heap priority queue with deterministic FIFO tie-breaking.
31
- * For the grid sizes we handle (~100s of cells), this is more than fast enough.
32
- */
33
- class MinHeap {
34
- private items: PQItem[] = []
35
-
36
- get length(): number {
37
- return this.items.length
38
- }
39
-
40
- push(item: PQItem): void {
41
- this.items.push(item)
42
- this.bubbleUp(this.items.length - 1)
43
- }
44
-
45
- pop(): PQItem | undefined {
46
- if (this.items.length === 0) return undefined
47
- const top = this.items[0]!
48
- const last = this.items.pop()!
49
- if (this.items.length > 0) {
50
- this.items[0] = last
51
- this.sinkDown(0)
52
- }
53
- return top
54
- }
55
-
56
- /**
57
- * Order two heap items: by priority, then by insertion sequence (FIFO).
58
- * Returns true if `a` should sit above `b` in the min-heap.
59
- */
60
- private before(a: PQItem, b: PQItem): boolean {
61
- if (a.priority !== b.priority) return a.priority < b.priority
62
- return a.seq < b.seq
63
- }
64
-
65
- private bubbleUp(i: number): void {
66
- while (i > 0) {
67
- const parent = (i - 1) >> 1
68
- if (this.before(this.items[i]!, this.items[parent]!)) {
69
- ;[this.items[i], this.items[parent]] = [this.items[parent]!, this.items[i]!]
70
- i = parent
71
- } else {
72
- break
73
- }
74
- }
75
- }
76
-
77
- private sinkDown(i: number): void {
78
- const n = this.items.length
79
- while (true) {
80
- let smallest = i
81
- const left = 2 * i + 1
82
- const right = 2 * i + 2
83
- // Upstream lukilabs#113 used `<=` here to break ties; we encode the same
84
- // determinism explicitly via the seq tie-breaker in `before`, which is
85
- // unambiguous regardless of traversal order.
86
- if (left < n && this.before(this.items[left]!, this.items[smallest]!)) {
87
- smallest = left
88
- }
89
- if (right < n && this.before(this.items[right]!, this.items[smallest]!)) {
90
- smallest = right
91
- }
92
- if (smallest !== i) {
93
- ;[this.items[i], this.items[smallest]] = [this.items[smallest]!, this.items[i]!]
94
- i = smallest
95
- } else {
96
- break
97
- }
98
- }
99
- }
100
- }
101
-
102
- // ============================================================================
103
- // A* heuristic
104
- // ============================================================================
105
-
106
- /**
107
- * Manhattan distance with a +1 penalty when both dx and dy are non-zero.
108
- * This encourages the pathfinder to prefer straight lines and minimize corners.
109
- */
110
- export function heuristic(a: GridCoord, b: GridCoord): number {
111
- const absX = Math.abs(a.x - b.x)
112
- const absY = Math.abs(a.y - b.y)
113
- if (absX === 0 || absY === 0) {
114
- return absX + absY
115
- }
116
- return absX + absY + 1
117
- }
118
-
119
- // ============================================================================
120
- // A* pathfinding
121
- // ============================================================================
122
-
123
- /** 4-directional movement (no diagonals in grid pathfinding). */
124
- const MOVE_DIRS: GridCoord[] = [
125
- { x: 1, y: 0 },
126
- { x: -1, y: 0 },
127
- { x: 0, y: 1 },
128
- { x: 0, y: -1 },
129
- ]
130
-
131
- /** Build a move-direction order that tries the edge's initial cardinal direction first. */
132
- function buildMoveDirs(preferredDir?: Direction): GridCoord[] {
133
- if (!preferredDir) return [...MOVE_DIRS]
134
-
135
- // Direction values are 3x3 node attachment offsets, not signed deltas:
136
- // Up={1,0}, Down={1,2}, Left={0,1}, Right={2,1}.
137
- if (preferredDir.x === 2 && preferredDir.y === 1) {
138
- return [{ x: 1, y: 0 }, { x: -1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }]
139
- }
140
- if (preferredDir.x === 0 && preferredDir.y === 1) {
141
- return [{ x: -1, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }]
142
- }
143
- if (preferredDir.x === 1 && preferredDir.y === 2) {
144
- return [{ x: 0, y: 1 }, { x: 0, y: -1 }, { x: 1, y: 0 }, { x: -1, y: 0 }]
145
- }
146
- if (preferredDir.x === 1 && preferredDir.y === 0) {
147
- return [{ x: 0, y: -1 }, { x: 0, y: 1 }, { x: 1, y: 0 }, { x: -1, y: 0 }]
148
- }
149
-
150
- const dx = preferredDir.x - 1
151
- const dy = preferredDir.y - 1
152
- if (Math.abs(dx) >= Math.abs(dy)) {
153
- return dx >= 0
154
- ? [{ x: 1, y: 0 }, { x: -1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }]
155
- : [{ x: -1, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }]
156
- }
157
- return dy >= 0
158
- ? [{ x: 0, y: 1 }, { x: 0, y: -1 }, { x: 1, y: 0 }, { x: -1, y: 0 }]
159
- : [{ x: 0, y: -1 }, { x: 0, y: 1 }, { x: 1, y: 0 }, { x: -1, y: 0 }]
160
- }
161
-
162
- /** Check if a grid cell is unoccupied and has non-negative coordinates. */
163
- function isFreeInGrid(grid: Map<string, AsciiNode>, c: GridCoord): boolean {
164
- if (c.x < 0 || c.y < 0) return false
165
- return !grid.has(gridKey(c))
166
- }
167
-
168
-
169
- /**
170
- * Find a path from `from` to `to` on the grid using A*.
171
- * Returns the path as an array of GridCoords, or null if no path exists.
172
- *
173
- * @param preferredDir - Optional exit direction tried first during neighbour
174
- * expansion (see `orderedDirs`). Currently unused by the edge router; kept as
175
- * a tested capability and exercised by ascii-pathfinder-determinism.test.ts.
176
- */
177
- export function getPath(
178
- grid: Map<string, AsciiNode>,
179
- from: GridCoord,
180
- to: GridCoord,
181
- preferredDir?: Direction,
182
- ): GridCoord[] | null {
183
- // #66 (ktrysmt): bound the search. `isFreeInGrid` only guards x/y < 0, so an
184
- // unreachable target (walled off) would let A* explore +x/+y unboundedly →
185
- // OOM/hang. Derive an upper bound from the grid extent + the from/to coords,
186
- // plus a hard iteration cap. On exhaustion we return null and the caller
187
- // falls back to a direct route, rather than exhausting memory.
188
- let maxX = Math.max(from.x, to.x)
189
- let maxY = Math.max(from.y, to.y)
190
- for (const key of grid.keys()) {
191
- const comma = key.indexOf(',')
192
- const kx = Number(key.slice(0, comma))
193
- const ky = Number(key.slice(comma + 1))
194
- if (kx > maxX) maxX = kx
195
- if (ky > maxY) maxY = ky
196
- }
197
- // Margin lets routes detour slightly around obstacles past the extent.
198
- const boundX = maxX + 4
199
- const boundY = maxY + 4
200
- // Iteration cap proportional to the bounded grid area (with a floor).
201
- const maxIterations = Math.max(10_000, (boundX + 1) * (boundY + 1) * 4)
202
-
203
- const moveDirs = buildMoveDirs(preferredDir)
204
-
205
- let seq = 0
206
- const pq = new MinHeap()
207
- pq.push({ coord: from, priority: 0, seq: seq++ })
208
-
209
- const costSoFar = new Map<string, number>()
210
- costSoFar.set(gridKey(from), 0)
211
-
212
- const cameFrom = new Map<string, GridCoord | null>()
213
- cameFrom.set(gridKey(from), null)
214
-
215
- let iterations = 0
216
- while (pq.length > 0) {
217
- if (++iterations > maxIterations) return null // #66 guard: bail to caller's fallback
218
- const current = pq.pop()!.coord
219
-
220
- if (gridCoordEquals(current, to)) {
221
- // Reconstruct path by walking backwards through cameFrom
222
- const path: GridCoord[] = []
223
- let c: GridCoord | null = current
224
- while (c !== null) {
225
- path.unshift(c)
226
- c = cameFrom.get(gridKey(c)) ?? null
227
- }
228
- return path
229
- }
230
-
231
- const currentCost = costSoFar.get(gridKey(current))!
232
-
233
- for (const dir of moveDirs) {
234
- const next: GridCoord = { x: current.x + dir.x, y: current.y + dir.y }
235
-
236
- // #66 guard: never expand past the bounded extent. Without this, an
237
- // unreachable target lets the search wander the unbounded +x/+y plane.
238
- if (next.x > boundX || next.y > boundY) continue
239
-
240
- // Allow moving to the destination even if it's occupied (it's a node boundary)
241
- if (!isFreeInGrid(grid, next) && !gridCoordEquals(next, to)) {
242
- continue
243
- }
244
-
245
- const newCost = currentCost + 1
246
- const nextKey = gridKey(next)
247
- const existingCost = costSoFar.get(nextKey)
248
-
249
- if (existingCost === undefined || newCost < existingCost) {
250
- costSoFar.set(nextKey, newCost)
251
- const priority = newCost + heuristic(next, to)
252
- pq.push({ coord: next, priority, seq: seq++ })
253
- cameFrom.set(nextKey, current)
254
- }
255
- }
256
- }
257
-
258
- return null // No path found
259
- }
260
-
261
- /**
262
- * Simplify a path by removing intermediate waypoints on straight segments.
263
- * E.g., [(0,0), (1,0), (2,0), (2,1)] becomes [(0,0), (2,0), (2,1)].
264
- * This reduces the number of line-drawing operations.
265
- */
266
- export function mergePath(path: GridCoord[]): GridCoord[] {
267
- if (path.length <= 2) return path
268
-
269
- const toRemove = new Set<number>()
270
- let step0 = path[0]!
271
- let step1 = path[1]!
272
-
273
- for (let idx = 2; idx < path.length; idx++) {
274
- const step2 = path[idx]!
275
- const prevDx = step1.x - step0.x
276
- const prevDy = step1.y - step0.y
277
- const dx = step2.x - step1.x
278
- const dy = step2.y - step1.y
279
-
280
- // Same direction — the middle point is redundant
281
- if (prevDx === dx && prevDy === dy) {
282
- // In Go: indexToRemove = append(indexToRemove, idx+1) but idx is 0-based from path[2:]
283
- // which corresponds to index idx in the full path. Go uses idx+1 because idx iterates
284
- // from 0 in the [2:] slice, mapping to full-array index idx+1.
285
- // Actually re-checking Go code: the loop is `for idx, step2 := range path[2:]`
286
- // so idx=0 → path[2], and it removes idx+1 which is index 1 in the full array.
287
- // Wait, that doesn't look right. Let me re-read:
288
- // step0 = path[0], step1 = path[1]
289
- // for idx, step2 := range path[2:] { ... indexToRemove = append(indexToRemove, idx+1) ... }
290
- // When idx=0, step2=path[2], and it removes index 1 (step1 = path[1]) if directions match
291
- // So it removes the middle point (step1) which is at index idx+1 in the original array
292
- // when counting from the 2-ahead loop. Let me just track which middle indices to remove.
293
- toRemove.add(idx - 1) // Remove the middle point (step1's position)
294
- }
295
-
296
- step0 = step1
297
- step1 = step2
298
- }
299
-
300
- return path.filter((_, i) => !toRemove.has(i))
301
- }
package/src/ascii/pie.ts DELETED
@@ -1,67 +0,0 @@
1
- // ============================================================================
2
- // ASCII renderer — Pie chart
3
- //
4
- // A pie chart doesn't map cleanly to a character grid, so we render it as a
5
- // proportional horizontal bar list (the conventional terminal pie rendering):
6
- //
7
- // Pets (title, if present)
8
- // Dogs ████████████████████████ 77.2%
9
- // Cats █████ 17.0%
10
- // Rats █ 5.8%
11
- //
12
- // Each bar's length is proportional to the slice's share of the total. With
13
- // `showData` the raw value is shown alongside the percentage. Bars use █
14
- // (Unicode) or # (ASCII fallback), colored per-slice with the same xychart
15
- // palette as the SVG renderer for cross-format consistency.
16
- // ============================================================================
17
-
18
- import { parsePieChart } from '../pie/parser.ts'
19
- import { formatPieValue, formatPiePercent } from '../pie/layout.ts'
20
- import type { AsciiConfig, AsciiTheme, ColorMode } from './types.ts'
21
- import { colorizeText } from './ansi.ts'
22
- import { getSeriesColor, CHART_ACCENT_FALLBACK, isValidHex } from '../xychart/colors.ts'
23
-
24
- /** Maximum bar length in characters (the largest slice fills this). */
25
- const MAX_BAR = 30
26
-
27
- function sliceColor(index: number, theme: AsciiTheme): string {
28
- const accent = theme.accent && isValidHex(theme.accent) ? theme.accent : CHART_ACCENT_FALLBACK
29
- return getSeriesColor(index, accent, theme.bg)
30
- }
31
-
32
- export function renderPieAscii(
33
- lines: string[],
34
- config: AsciiConfig,
35
- colorMode: ColorMode,
36
- theme: AsciiTheme,
37
- ): string {
38
- const chart = parsePieChart(lines)
39
- const barChar = config.useAscii ? '#' : '█'
40
-
41
- const total = chart.entries.reduce((sum, e) => sum + e.value, 0)
42
- if (total <= 0) return ''
43
-
44
- // Column widths.
45
- const labelWidth = Math.max(...chart.entries.map(e => e.label.length))
46
- const maxValue = Math.max(...chart.entries.map(e => e.value))
47
-
48
- const out: string[] = []
49
- if (chart.title) out.push(chart.title)
50
-
51
- chart.entries.forEach((entry, index) => {
52
- const fraction = entry.value / total
53
- // Bar length proportional to the largest slice, min 1 char for nonzero.
54
- const barLen = Math.max(1, Math.round((entry.value / maxValue) * MAX_BAR))
55
- const bar = barChar.repeat(barLen)
56
- const coloredBar = colorMode === 'none' ? bar : colorizeText(bar, sliceColor(index, theme), colorMode)
57
-
58
- const label = entry.label.padEnd(labelWidth)
59
- const barPad = ' '.repeat(MAX_BAR - barLen + 2)
60
- const pct = formatPiePercent(fraction).padStart(6)
61
- const valuePart = chart.showData ? ` [${formatPieValue(entry.value)}]` : ''
62
-
63
- out.push(`${label} ${coloredBar}${barPad}${pct}${valuePart}`)
64
- })
65
-
66
- return out.join('\n')
67
- }
@@ -1,158 +0,0 @@
1
- // ============================================================================
2
- // ASCII renderer — Quadrant chart
3
- //
4
- // Renders a bordered SQUARE grid divided into four quadrants. Quadrant labels
5
- // sit in their regions, points are plotted at scaled positions marked ●
6
- // (Unicode) / * (ASCII), and a legend below lists every point's coordinates.
7
- //
8
- // Mermaid quadrant numbering: 1=top-right, 2=top-left, 3=bottom-left,
9
- // 4=bottom-right. Normalized coords (x,y) ∈ [0,1]², y grows UP. Scaling
10
- // follows the xychart convention: round(t * (extent - 1)).
11
- // ============================================================================
12
-
13
- import { parseQuadrantChart } from '../quadrant/parser.ts'
14
- import type { QuadrantChart } from '../quadrant/types.ts'
15
- import type { AsciiConfig, AsciiTheme, ColorMode } from './types.ts'
16
- import { colorizeText } from './ansi.ts'
17
-
18
- /** Interior plot dimensions in cells (odd so the dividers land cleanly). */
19
- const GRID_W = 41
20
- const GRID_H = 21
21
-
22
- const UNI = {
23
- tl: '┌', tr: '┐', bl: '└', br: '┘',
24
- h: '─', v: '│',
25
- tDown: '┬', tUp: '┴', tLeft: '┤', tRight: '├', cross: '┼',
26
- point: '●',
27
- } as const
28
-
29
- const ASC = {
30
- tl: '+', tr: '+', bl: '+', br: '+',
31
- h: '-', v: '|',
32
- tDown: '+', tUp: '+', tLeft: '+', tRight: '+', cross: '+',
33
- point: '*',
34
- } as const
35
-
36
- export function renderQuadrantAscii(
37
- lines: string[],
38
- config: AsciiConfig,
39
- colorMode: ColorMode,
40
- theme: AsciiTheme,
41
- ): string {
42
- const chart = parseQuadrantChart(lines)
43
- const ch = config.useAscii ? ASC : UNI
44
-
45
- // Interior grid (rows × cols of single characters). Row 0 = top.
46
- const grid: string[][] = Array.from({ length: GRID_H }, () => Array.from({ length: GRID_W }, () => ' '))
47
- const midCol = Math.floor(GRID_W / 2)
48
- const midRow = Math.floor(GRID_H / 2)
49
-
50
- // Dividers.
51
- for (let r = 0; r < GRID_H; r++) grid[r]![midCol] = ch.v
52
- for (let c = 0; c < GRID_W; c++) grid[midRow]![c] = ch.h
53
- grid[midRow]![midCol] = ch.cross
54
-
55
- // Points — scaled into the grid. x grows right, y grows up (invert row).
56
- // Plotted first so quadrant labels (decorative) never clobber a data point.
57
- const occupied = new Set<string>()
58
- chart.points.forEach(p => {
59
- const col = Math.round(p.x * (GRID_W - 1))
60
- const row = Math.round((1 - p.y) * (GRID_H - 1))
61
- grid[row]![col] = ch.point
62
- occupied.add(`${row},${col}`)
63
- })
64
-
65
- // Quadrant labels. When points exist, Mermaid renders quadrant text near the
66
- // TOP of each region (so it doesn't fight the data); with no points it sits
67
- // in the center. We follow that, and never overwrite a plotted point.
68
- const top = chart.points.length > 0
69
- placeLabel(grid, occupied, chart.quadrants[1], 0, 0, midRow, midCol, top) // q2 top-left
70
- placeLabel(grid, occupied, chart.quadrants[0], 0, midCol + 1, midRow, GRID_W, top) // q1 top-right
71
- placeLabel(grid, occupied, chart.quadrants[2], midRow + 1, 0, GRID_H, midCol, top) // q3 bottom-left
72
- placeLabel(grid, occupied, chart.quadrants[3], midRow + 1, midCol + 1, GRID_H, GRID_W, top) // q4 bottom-right
73
-
74
- // Frame the grid with a border, with axis labels on the edges.
75
- const out: string[] = []
76
- if (chart.title) out.push(centerText(chart.title, GRID_W + 2))
77
-
78
- out.push(ch.tl + ch.h.repeat(GRID_W) + ch.tr)
79
- for (let r = 0; r < GRID_H; r++) {
80
- // The mid divider row meets the border as ├ … ┤ junctions.
81
- const left = r === midRow ? ch.tRight : ch.v
82
- const right = r === midRow ? ch.tLeft : ch.v
83
- out.push(left + grid[r]!.join('') + right)
84
- }
85
- out.push(ch.bl + ch.h.repeat(GRID_W) + ch.br)
86
-
87
- // x-axis labels under the grid (left + right).
88
- if (chart.xAxis) {
89
- out.push(edgeAxisRow(chart.xAxis.near, chart.xAxis.far, GRID_W + 2))
90
- }
91
- // y-axis labels as a separate annotated line.
92
- if (chart.yAxis) {
93
- const top = chart.yAxis.far ? `top: ${chart.yAxis.far}` : ''
94
- const bottom = `bottom: ${chart.yAxis.near}`
95
- out.push(`y-axis ${bottom}${top ? ` | ${top}` : ''}`)
96
- }
97
-
98
- // Legend — every point with its coordinates.
99
- if (chart.points.length > 0) {
100
- out.push('')
101
- const pointGlyph = colorMode === 'none' ? ch.point : colorizeText(ch.point, pointColor(theme), colorMode)
102
- for (const p of chart.points) {
103
- out.push(`${pointGlyph} ${p.label}: [${fmt(p.x)}, ${fmt(p.y)}]`)
104
- }
105
- }
106
-
107
- return out.join('\n')
108
- }
109
-
110
- function pointColor(theme: AsciiTheme): string {
111
- return theme.accent ?? theme.fg
112
- }
113
-
114
- function fmt(n: number): string {
115
- return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100)
116
- }
117
-
118
- /**
119
- * Place a label horizontally centered within a sub-rectangle of the grid.
120
- * When `top` is set the label sits one row down from the region top; otherwise
121
- * it is vertically centered. Never overwrites a plotted point.
122
- */
123
- function placeLabel(
124
- grid: string[][],
125
- occupied: Set<string>,
126
- label: string | undefined,
127
- rowStart: number,
128
- colStart: number,
129
- rowEnd: number,
130
- colEnd: number,
131
- top: boolean,
132
- ): void {
133
- if (!label) return
134
- const regionW = colEnd - colStart
135
- const regionH = rowEnd - rowStart
136
- if (regionW <= 0 || regionH <= 0) return
137
- const text = label.length > regionW ? label.slice(0, regionW) : label
138
- const row = top ? rowStart + 1 : rowStart + Math.floor(regionH / 2)
139
- const col = colStart + Math.max(0, Math.floor((regionW - text.length) / 2))
140
- for (let i = 0; i < text.length && col + i < colEnd; i++) {
141
- if (occupied.has(`${row},${col + i}`)) continue
142
- grid[row]![col + i] = text[i]!
143
- }
144
- }
145
-
146
- function centerText(text: string, width: number): string {
147
- if (text.length >= width) return text
148
- const pad = Math.floor((width - text.length) / 2)
149
- return ' '.repeat(pad) + text
150
- }
151
-
152
- /** Bottom edge axis row: near label left-aligned, far label right-aligned. */
153
- function edgeAxisRow(near: string, far: string | undefined, width: number): string {
154
- if (!far) return near
155
- const gap = width - near.length - far.length
156
- if (gap < 1) return `${near} ${far}`
157
- return near + ' '.repeat(gap) + far
158
- }