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.
Files changed (333) hide show
  1. package/CHANGELOG.md +149 -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/assets/fonts/FONT-LICENSES.md +9 -5
  6. package/assets/fonts/Inter-Bold.ttf +0 -0
  7. package/assets/fonts/Inter-Medium.ttf +0 -0
  8. package/assets/fonts/Inter-Regular.ttf +0 -0
  9. package/assets/fonts/Inter-SemiBold.ttf +0 -0
  10. package/dist/agent-core.d.ts +463 -0
  11. package/dist/agent-core.js +397 -0
  12. package/dist/agent.d.ts +81 -1822
  13. package/dist/agent.js +336 -36839
  14. package/dist/agentic-mermaid-mcp.js +7 -37908
  15. package/dist/am.js +33638 -36261
  16. package/dist/chunk-4QECACIA.js +4702 -0
  17. package/dist/chunk-CDPKJ2BQ.js +37 -0
  18. package/dist/chunk-QKDLMOJJ.js +3487 -0
  19. package/dist/chunk-WI35L7IR.js +615 -0
  20. package/dist/chunk-WN3YDOBG.js +149 -0
  21. package/dist/chunk-ZYMMKQ3I.js +62637 -0
  22. package/dist/index-D54dqSdC.d.ts +6777 -0
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +310 -32836
  25. package/dist/mcp-cli-SBQSTJFC.js +17 -0
  26. package/docs/README.md +14 -6
  27. package/docs/schemas/style-spec.schema.json +1221 -30
  28. package/examples/styles/catalog.json +54 -0
  29. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  30. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  31. package/examples/styles/cupertino-prototype.mmd +10 -0
  32. package/examples/styles/cupertino-prototype.style.json +63 -0
  33. package/examples/styles/mid-century-report.style.json +2 -2
  34. package/examples/styles/star-chart-atlas.style.json +2 -1
  35. package/examples/styles/transit-route-map.style.json +2 -2
  36. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  37. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  38. package/package.json +70 -43
  39. package/server.json +41 -0
  40. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  41. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  42. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  43. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  44. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  45. package/AGENT_NATIVE.md +0 -602
  46. package/Instructions_for_agents.md +0 -83
  47. package/TODO.md +0 -662
  48. package/assets/hero.png +0 -0
  49. package/dist/index-x2Cj1dVv.d.ts +0 -1089
  50. package/docs/agent-api-cookbook.md +0 -308
  51. package/docs/agent-mutation-policy.md +0 -104
  52. package/docs/agent-workflow-examples.md +0 -46
  53. package/docs/api.md +0 -203
  54. package/docs/ascii.md +0 -103
  55. package/docs/assets/improvements/box-start-connector.png +0 -0
  56. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  57. package/docs/assets/improvements/er-content-loss.png +0 -0
  58. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  59. package/docs/assets/improvements/gantt-family.png +0 -0
  60. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  61. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  62. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  63. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  64. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  65. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  66. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  67. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  68. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  69. package/docs/comparison.md +0 -94
  70. package/docs/config.md +0 -105
  71. package/docs/contributing/adding-diagram-types.md +0 -144
  72. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  73. package/docs/contributing/diagram-family-citizenship.md +0 -82
  74. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  75. package/docs/contributing/lessons-learned.md +0 -276
  76. package/docs/contributing/releasing.md +0 -71
  77. package/docs/contributing/visual-review-evidence.md +0 -53
  78. package/docs/custom-style-cookbook.md +0 -154
  79. package/docs/design/families/README.md +0 -26
  80. package/docs/design/families/architecture-beta.md +0 -48
  81. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  82. package/docs/design/families/gantt-research.md +0 -137
  83. package/docs/design/families/gantt.md +0 -329
  84. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  85. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  86. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  87. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  88. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  89. package/docs/design/families/journey-migration-parity.md +0 -433
  90. package/docs/design/families/journey-usage-research.md +0 -423
  91. package/docs/design/families/journey.md +0 -125
  92. package/docs/design/families/xychart.md +0 -85
  93. package/docs/design/system/README.md +0 -45
  94. package/docs/design/system/abstraction-audit.md +0 -224
  95. package/docs/design/system/abstraction-recommendations.md +0 -655
  96. package/docs/design/system/architecture.mmd +0 -29
  97. package/docs/design/system/architecture.svg +0 -137
  98. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  99. package/docs/design/system/layout-rubric.md +0 -138
  100. package/docs/design/system/route-contracts.md +0 -848
  101. package/docs/design/system/source-preservation-ladder.md +0 -52
  102. package/docs/design/system/styles-rollout.md +0 -216
  103. package/docs/design/system/ugly-layouts.md +0 -119
  104. package/docs/diagram-families.md +0 -193
  105. package/docs/features.md +0 -147
  106. package/docs/fork-differences.md +0 -130
  107. package/docs/getting-started.md +0 -175
  108. package/docs/issue-derived-test-cases.md +0 -156
  109. package/docs/layout-characterization/README.md +0 -245
  110. package/docs/layout-characterization/contact-sheet-families.md +0 -496
  111. package/docs/layout-characterization/contact-sheet.md +0 -638
  112. package/docs/layout-characterization/properties.md +0 -281
  113. package/docs/layout-characterization/visual-quality.md +0 -160
  114. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  115. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  116. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  117. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  118. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  119. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -118
  120. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  121. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  122. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  123. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  124. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  125. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  126. package/docs/mcp-code-mode-rationale.md +0 -101
  127. package/docs/mcp-http-transport.md +0 -223
  128. package/docs/mermaid-layout-complaints.md +0 -837
  129. package/docs/mutation-testing.md +0 -206
  130. package/docs/project/divergences.md +0 -98
  131. package/docs/project/dogfooding-docs-strategy.md +0 -66
  132. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  133. package/docs/project/lessons-learned.md +0 -1019
  134. package/docs/project/remove-role-styling-plan.md +0 -176
  135. package/docs/quality.md +0 -212
  136. package/docs/react.md +0 -97
  137. package/docs/style-authoring.md +0 -133
  138. package/docs/testing-strategy.md +0 -335
  139. package/docs/theming.md +0 -162
  140. package/examples/agent-improve-auth-flow.ts +0 -206
  141. package/examples/agent-loop.ts +0 -53
  142. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  143. package/llms.txt +0 -104
  144. package/skill-evals/README.md +0 -77
  145. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  146. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  147. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  148. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  149. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  150. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  151. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  152. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  153. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  154. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  155. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  156. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  157. package/skill-evals/shared-benchmark.json +0 -510
  158. package/src/agent/analyze.ts +0 -157
  159. package/src/agent/apply.ts +0 -146
  160. package/src/agent/architecture-body.ts +0 -458
  161. package/src/agent/certificates.ts +0 -110
  162. package/src/agent/class-body.ts +0 -319
  163. package/src/agent/core.ts +0 -207
  164. package/src/agent/create.ts +0 -106
  165. package/src/agent/describe.ts +0 -364
  166. package/src/agent/er-body.ts +0 -224
  167. package/src/agent/facts.ts +0 -337
  168. package/src/agent/families-builtin.ts +0 -934
  169. package/src/agent/families.ts +0 -232
  170. package/src/agent/family-layouts.ts +0 -826
  171. package/src/agent/flowchart-body.ts +0 -372
  172. package/src/agent/flowchart-unsupported.ts +0 -243
  173. package/src/agent/gantt-body.ts +0 -495
  174. package/src/agent/index.ts +0 -10
  175. package/src/agent/journey-body.ts +0 -414
  176. package/src/agent/label-metrics.ts +0 -28
  177. package/src/agent/layout-to-rendered.ts +0 -63
  178. package/src/agent/mutate.ts +0 -97
  179. package/src/agent/mutation-ops.ts +0 -51
  180. package/src/agent/op-schema.ts +0 -439
  181. package/src/agent/parse.ts +0 -223
  182. package/src/agent/pie-body.ts +0 -270
  183. package/src/agent/png.ts +0 -117
  184. package/src/agent/quadrant-body.ts +0 -336
  185. package/src/agent/quality.ts +0 -363
  186. package/src/agent/readability-audit.ts +0 -92
  187. package/src/agent/rendered-route-audit.ts +0 -61
  188. package/src/agent/sequence-body.ts +0 -345
  189. package/src/agent/serialize.ts +0 -214
  190. package/src/agent/state-body.ts +0 -589
  191. package/src/agent/structural-count.ts +0 -150
  192. package/src/agent/timeline-body.ts +0 -280
  193. package/src/agent/trace-log.ts +0 -43
  194. package/src/agent/types.ts +0 -979
  195. package/src/agent/verify.ts +0 -571
  196. package/src/agent/xychart-body.ts +0 -472
  197. package/src/architecture/config.ts +0 -282
  198. package/src/architecture/layout.ts +0 -496
  199. package/src/architecture/parser.ts +0 -375
  200. package/src/architecture/renderer.ts +0 -611
  201. package/src/architecture/types.ts +0 -113
  202. package/src/ascii/ansi.ts +0 -423
  203. package/src/ascii/architecture.ts +0 -210
  204. package/src/ascii/canvas.ts +0 -443
  205. package/src/ascii/class-diagram.ts +0 -697
  206. package/src/ascii/converter.ts +0 -475
  207. package/src/ascii/draw.ts +0 -1693
  208. package/src/ascii/edge-bundling.ts +0 -328
  209. package/src/ascii/edge-routing.ts +0 -330
  210. package/src/ascii/er-diagram.ts +0 -439
  211. package/src/ascii/gantt.ts +0 -276
  212. package/src/ascii/grid.ts +0 -747
  213. package/src/ascii/index.ts +0 -196
  214. package/src/ascii/journey.ts +0 -189
  215. package/src/ascii/meta.ts +0 -244
  216. package/src/ascii/multiline-utils.ts +0 -80
  217. package/src/ascii/pathfinder.ts +0 -301
  218. package/src/ascii/pie.ts +0 -67
  219. package/src/ascii/quadrant.ts +0 -158
  220. package/src/ascii/reverse.ts +0 -216
  221. package/src/ascii/sequence.ts +0 -499
  222. package/src/ascii/shapes/circle.ts +0 -27
  223. package/src/ascii/shapes/corners.ts +0 -143
  224. package/src/ascii/shapes/diamond.ts +0 -27
  225. package/src/ascii/shapes/hexagon.ts +0 -27
  226. package/src/ascii/shapes/index.ts +0 -106
  227. package/src/ascii/shapes/rectangle.ts +0 -168
  228. package/src/ascii/shapes/rounded.ts +0 -27
  229. package/src/ascii/shapes/special.ts +0 -332
  230. package/src/ascii/shapes/stadium.ts +0 -110
  231. package/src/ascii/shapes/state.ts +0 -192
  232. package/src/ascii/shapes/types.ts +0 -73
  233. package/src/ascii/timeline.ts +0 -128
  234. package/src/ascii/types.ts +0 -312
  235. package/src/ascii/validate.ts +0 -120
  236. package/src/ascii/width.ts +0 -81
  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 -1117
  247. package/src/cli/init-agent.ts +0 -147
  248. package/src/color-resolver.ts +0 -152
  249. package/src/elk-instance.ts +0 -114
  250. package/src/er/layout.ts +0 -187
  251. package/src/er/parser.ts +0 -226
  252. package/src/er/renderer.ts +0 -792
  253. package/src/er/types.ts +0 -99
  254. package/src/font-manifest.ts +0 -27
  255. package/src/gantt/layout.ts +0 -365
  256. package/src/gantt/parser.ts +0 -282
  257. package/src/gantt/pipeline.ts +0 -27
  258. package/src/gantt/renderer.ts +0 -414
  259. package/src/gantt/schedule.ts +0 -513
  260. package/src/gantt/types.ts +0 -255
  261. package/src/index.ts +0 -368
  262. package/src/journey/layout.ts +0 -520
  263. package/src/journey/parser.ts +0 -218
  264. package/src/journey/renderer.ts +0 -811
  265. package/src/journey/types.ts +0 -144
  266. package/src/layout/geometry.ts +0 -182
  267. package/src/layout/pass.ts +0 -142
  268. package/src/layout/passes/index.ts +0 -3916
  269. package/src/layout-engine.ts +0 -1565
  270. package/src/layout-rubric.ts +0 -659
  271. package/src/mcp/artifacts.ts +0 -137
  272. package/src/mcp/dynamic-harness.ts +0 -31
  273. package/src/mcp/dynamic-modules.d.ts +0 -7
  274. package/src/mcp/facade.ts +0 -608
  275. package/src/mcp/harness-runtime.ts +0 -203
  276. package/src/mcp/hosted-server.ts +0 -468
  277. package/src/mcp/mcp-bin.ts +0 -3
  278. package/src/mcp/mcp-cli.ts +0 -82
  279. package/src/mcp/protocol.ts +0 -13
  280. package/src/mcp/sandbox.ts +0 -195
  281. package/src/mcp/sdk-decl.ts +0 -387
  282. package/src/mcp/server.ts +0 -362
  283. package/src/mcp/tool-surface.ts +0 -146
  284. package/src/mermaid-source.ts +0 -655
  285. package/src/multiline-utils.ts +0 -228
  286. package/src/parser.ts +0 -1031
  287. package/src/pie/layout.ts +0 -168
  288. package/src/pie/parser.ts +0 -136
  289. package/src/pie/renderer.ts +0 -234
  290. package/src/pie/types.ts +0 -82
  291. package/src/quadrant/layout.ts +0 -191
  292. package/src/quadrant/parser.ts +0 -183
  293. package/src/quadrant/renderer.ts +0 -322
  294. package/src/quadrant/types.ts +0 -113
  295. package/src/render-family-hooks.ts +0 -244
  296. package/src/renderer.ts +0 -1099
  297. package/src/route-contracts.ts +0 -2723
  298. package/src/scene/backend.ts +0 -76
  299. package/src/scene/fidelity.ts +0 -183
  300. package/src/scene/hybrid-backend.ts +0 -174
  301. package/src/scene/ir.ts +0 -181
  302. package/src/scene/marks.ts +0 -127
  303. package/src/scene/rough-backend.ts +0 -378
  304. package/src/scene/seed.ts +0 -46
  305. package/src/scene/style-registry.ts +0 -556
  306. package/src/sequence/layout.ts +0 -456
  307. package/src/sequence/parser.ts +0 -255
  308. package/src/sequence/renderer.ts +0 -604
  309. package/src/sequence/types.ts +0 -154
  310. package/src/shape-clipping.ts +0 -351
  311. package/src/shared/accessibility-directives.ts +0 -22
  312. package/src/shared/batched.ts +0 -61
  313. package/src/shared/color-math.ts +0 -72
  314. package/src/shared/svg-a11y.ts +0 -29
  315. package/src/shared/syntax-error.ts +0 -17
  316. package/src/shared/unicode-ranges.ts +0 -135
  317. package/src/styles.ts +0 -318
  318. package/src/svg-paths.ts +0 -28
  319. package/src/text-metrics.ts +0 -248
  320. package/src/theme.ts +0 -639
  321. package/src/timeline/layout.ts +0 -381
  322. package/src/timeline/parser.ts +0 -215
  323. package/src/timeline/renderer.ts +0 -538
  324. package/src/timeline/types.ts +0 -105
  325. package/src/types.ts +0 -454
  326. package/src/website-worker-modules.d.ts +0 -18
  327. package/src/xychart/axis-utils.ts +0 -109
  328. package/src/xychart/colors.ts +0 -124
  329. package/src/xychart/config.ts +0 -88
  330. package/src/xychart/layout.ts +0 -656
  331. package/src/xychart/parser.ts +0 -504
  332. package/src/xychart/renderer.ts +0 -640
  333. package/src/xychart/types.ts +0 -282
@@ -1,80 +0,0 @@
1
- // ============================================================================
2
- // ASCII renderer — multi-line text utilities
3
- //
4
- // Shared utilities for handling multi-line labels (containing \n from <br> tags)
5
- // in ASCII/Unicode rendering. Provides consistent text splitting, sizing, and
6
- // centered rendering across all diagram types.
7
- // ============================================================================
8
-
9
- import type { Canvas } from './types.ts'
10
- import { drawText } from './canvas.ts'
11
- import { charVisualWidth, visualWidth } from './width.ts'
12
-
13
- export { charVisualWidth, visualWidth }
14
-
15
- /**
16
- * Split a label into lines.
17
- * Labels are already normalized by parsers (br tags → \n).
18
- */
19
- export function splitLines(label: string): string[] {
20
- return label.split('\n')
21
- }
22
-
23
- /**
24
- * Get the maximum line width for sizing calculations.
25
- * Used to determine column widths for multi-line labels.
26
- */
27
- export function maxLineWidth(label: string): number {
28
- const lines = splitLines(label)
29
- return Math.max(...lines.map(l => visualWidth(l)), 0)
30
- }
31
-
32
- /**
33
- * Get the number of lines for height calculations.
34
- * Used to determine row heights for multi-line labels.
35
- */
36
- export function lineCount(label: string): number {
37
- return splitLines(label).length
38
- }
39
-
40
- /**
41
- * Draw multi-line text centered at (cx, cy).
42
- * Expands vertically from the center point.
43
- * Each line is horizontally centered independently.
44
- */
45
- export function drawMultilineTextCentered(
46
- canvas: Canvas,
47
- label: string,
48
- cx: number,
49
- cy: number
50
- ): void {
51
- const lines = splitLines(label)
52
- const totalHeight = lines.length
53
- // Center vertically: start y positions lines evenly around cy
54
- const startY = cy - Math.floor((totalHeight - 1) / 2)
55
-
56
- for (let i = 0; i < lines.length; i++) {
57
- const line = lines[i]!
58
- // Center each line horizontally by terminal column width.
59
- const startX = cx - Math.floor(visualWidth(line) / 2)
60
- // Force overwrite for node labels (they take priority)
61
- drawText(canvas, { x: startX, y: startY + i }, line, true)
62
- }
63
- }
64
-
65
- /**
66
- * Draw multi-line text left-aligned starting at (x, y).
67
- * Each subsequent line is placed one row below.
68
- */
69
- export function drawMultilineTextLeft(
70
- canvas: Canvas,
71
- label: string,
72
- x: number,
73
- y: number
74
- ): void {
75
- const lines = splitLines(label)
76
- for (let i = 0; i < lines.length; i++) {
77
- // Force overwrite for node labels (they take priority)
78
- drawText(canvas, { x, y: y + i }, lines[i]!, true)
79
- }
80
- }
@@ -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
- }