@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
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`),
|
|
18
|
-
* `classDiagram`, `erDiagram`, `sequenceDiagram`, `pie`,
|
|
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,
|