@yassimba/pi-loom-mermaid 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yassimba/pi-loom-mermaid",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Loom's advanced Mermaid renderer for Pi, with better routing and color.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -110,6 +110,6 @@ export function transformMermaidMarkdown(markdown: string, context: TransformCon
110
110
  export default function piLovelyMermaid(pi: ExtensionAPI): void {
111
111
  pi.registerMarkdownTransformer(transformMermaidMarkdown);
112
112
  pi.on("before_agent_start", (event) => ({
113
- systemPrompt: `${event.systemPrompt}\n\nUse fenced \`mermaid\` blocks; they render automatically in the user’s session. Use Mermaid proactively to explain relationships, flows, and changes. Choose by subject: flowchart for dependencies/decisions, sequence for interactions, state for lifecycles, ER/class for models, mindmap for hierarchies, timeline/git graph for history, pie for proportions. Use complementary diagrams when explaining multiple aspects. Keep diagram labels short. Mark changes (like diff but also other changes) with: :::red removed, :::green added, :::orange changed. In general prefer colored outlines to logically group things (if there are no changes involved).`,
113
+ systemPrompt: `${event.systemPrompt}\n\nUse fenced \`mermaid\` blocks; they render automatically in the user’s session. Use Mermaid proactively to explain relationships, flows, and changes. Choose by subject: architecture for deployed services, flowchart for dependencies/decisions, sequence for interactions, state for lifecycles, ER/class for models, mindmap for hierarchies, timeline/git graph for history, pie for proportions. Use complementary diagrams when explaining multiple aspects. Keep diagram labels short. Mark changes (like diff but also other changes) with: :::red removed, :::green added, :::orange changed. In general prefer colored outlines to logically group things (if there are no changes involved).`,
114
114
  }));
115
115
  }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * `architecture-beta`: services and junctions inside nested groups.
3
+ *
4
+ * Architecture's cardinal ports choose exact SVG attachment points. The
5
+ * terminal graph router already chooses reachable box sides, so this parser
6
+ * preserves endpoints and arrowheads while leaving port placement to it.
7
+ */
8
+
9
+ import { Graph, MAX_GROUP_DEPTH, MAX_GROUPS } from '../graph.ts'
10
+ import { cleanLabel } from '../labels.ts'
11
+ import { layoutFlowchart, layoutGrouped } from '../graph-render.ts'
12
+ import type { Diagram } from '../registry.ts'
13
+ import { firstWord, headerKind, statementsOf } from '../statements.ts'
14
+
15
+ export const architecture: Diagram = {
16
+ kind: 'architecture',
17
+ headers: ['architecture-beta'],
18
+ render(src, limits) {
19
+ const graph = parseArchitecture(src)
20
+ if (graph === null) return null
21
+ const canvas = graph.groups.length === 0 ? layoutFlowchart(graph, limits) : layoutGrouped(graph, limits)
22
+ if (canvas === null) return null
23
+ return { canvas, warnings: graph.warnings, classDefs: {} }
24
+ },
25
+ }
26
+
27
+ const DECLARATION = /^(group|service)\s+([^\s()[\]:{}]+)\(([^)]*)\)\[([^\]]*)\](?:\s+in\s+([^\s]+))?$/i
28
+ const JUNCTION = /^junction\s+([^\s:{}]+)(?:\s+in\s+([^\s]+))?$/i
29
+ const EDGE = /^([^\s:{}]+)(?:\{group\})?:([TBLR])\s*(<)?--(>)?\s*([TBLR]):([^\s:{}]+)(?:\{group\})?$/i
30
+
31
+ function parseArchitecture(src: string): Graph | null {
32
+ const statements = statementsOf(src)
33
+ if (headerKind(statements) !== 'architecture-beta') return null
34
+
35
+ // Architecture has no global direction. LR gives grouped boundary edges the
36
+ // existing router's more precise inner-node anchors.
37
+ const graph = new Graph('right')
38
+ const groupIndex = new Map<string, number>()
39
+
40
+ for (const st of statements.slice(1)) {
41
+ const declaration = st.match(DECLARATION)
42
+ const junction = st.match(JUNCTION)
43
+ const edge = st.match(EDGE)
44
+
45
+ if (declaration) {
46
+ const [, kind, id, , rawLabel, parentId] = declaration
47
+ const parent = parentId === undefined ? null : groupIndex.get(parentId)
48
+ if (parentId !== undefined && parent === undefined) {
49
+ graph.drop(st)
50
+ } else if (kind.toLowerCase() === 'group') {
51
+ if (groupIndex.has(id)) {
52
+ graph.drop(st)
53
+ } else if (
54
+ graph.groups.length >= MAX_GROUPS ||
55
+ groupDepth(graph, parent ?? null) >= MAX_GROUP_DEPTH
56
+ ) {
57
+ graph.truncated ??= `subgraph cap (${MAX_GROUPS} groups, depth ${MAX_GROUP_DEPTH}) reached`
58
+ } else {
59
+ groupIndex.set(id, graph.groups.length)
60
+ graph.groups.push({ id, label: cleanLabel(rawLabel) || id, parent: parent ?? null })
61
+ }
62
+ } else {
63
+ addNode(graph, id, cleanLabel(rawLabel) || id, parent ?? null, 'rect')
64
+ }
65
+ } else if (junction) {
66
+ const [, id, parentId] = junction
67
+ const parent = parentId === undefined ? null : groupIndex.get(parentId)
68
+ if (parentId !== undefined && parent === undefined) graph.drop(st)
69
+ else addNode(graph, id, '•', parent ?? null, 'round')
70
+ } else if (edge) {
71
+ const [, fromId, , leftArrow, rightArrow, , toId] = edge
72
+ const from = graph.index.get(fromId)
73
+ const to = graph.index.get(toId)
74
+ if (from === undefined || to === undefined) {
75
+ graph.drop(st)
76
+ } else {
77
+ graph.pushEdge({
78
+ from,
79
+ to,
80
+ label: null,
81
+ headFrom: leftArrow ? 'arrow' : 'none',
82
+ headTo: rightArrow ? 'arrow' : 'none',
83
+ line: 'solid',
84
+ })
85
+ }
86
+ } else if (firstWord(st).toLowerCase() !== 'title') {
87
+ graph.drop(st)
88
+ }
89
+
90
+ if (graph.truncated !== null) {
91
+ graph.warnings.push(`diagram truncated: ${graph.truncated}`)
92
+ break
93
+ }
94
+ }
95
+
96
+ return graph.nodes.length === 0 ? null : graph
97
+ }
98
+
99
+ function addNode(
100
+ graph: Graph,
101
+ id: string,
102
+ label: string,
103
+ group: number | null,
104
+ shape: 'rect' | 'round',
105
+ ): void {
106
+ const previous = graph.curGroup
107
+ graph.curGroup = group
108
+ graph.nodeIndex(id, label, shape)
109
+ graph.curGroup = previous
110
+ }
111
+
112
+ function groupDepth(graph: Graph, parent: number | null): number {
113
+ let depth = 0
114
+ for (let at = parent; at !== null; at = graph.groups[at].parent) depth++
115
+ return depth
116
+ }
@@ -14,9 +14,9 @@ export type { MermaidArt, Role, Span } from './types.ts'
14
14
  /**
15
15
  * Render a Mermaid source block as Unicode box-drawing art.
16
16
  *
17
- * Supported: `graph`/`flowchart` (including `subgraph`), `stateDiagram`,
18
- * `classDiagram`, `erDiagram`, `sequenceDiagram`, `pie`, `mindmap`,
19
- * `timeline` and `gitGraph`.
17
+ * Supported: `architecture-beta`, `graph`/`flowchart` (including `subgraph`),
18
+ * `stateDiagram`, `classDiagram`, `erDiagram`, `sequenceDiagram`, `pie`,
19
+ * `mindmap`, `timeline` and `gitGraph`.
20
20
  *
21
21
  * The diagram is laid out at whatever size it needs; `art.width` reports the
22
22
  * columns that turned out to be. Given `maxWidth`, a diagram wider than that
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { Canvas } from './canvas.ts'
11
+ import { architecture } from './diagrams/architecture.ts'
11
12
  import { classDiagram } from './diagrams/class.ts'
12
13
  import { er } from './diagrams/er.ts'
13
14
  import { flowchart } from './diagrams/flowchart.ts'
@@ -22,6 +23,7 @@ import { headerKind, statementsOf } from './statements.ts'
22
23
 
23
24
  /** A diagram type this renderer draws. */
24
25
  export type DiagramKind =
26
+ | 'architecture'
25
27
  | 'flowchart'
26
28
  | 'state'
27
29
  | 'class'
@@ -52,6 +54,7 @@ export interface Diagram {
52
54
  }
53
55
 
54
56
  const DIAGRAMS: Diagram[] = [
57
+ architecture,
55
58
  flowchart,
56
59
  state,
57
60
  classDiagram,