@herbertgao/pi-extensions 2026.9.7 → 2026.9.8

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 (65) hide show
  1. package/node_modules/grok-mermaid/CHANGELOG.md +46 -0
  2. package/node_modules/grok-mermaid/LICENSE +205 -0
  3. package/node_modules/grok-mermaid/README.md +191 -0
  4. package/node_modules/grok-mermaid/dist/ansi.d.ts +16 -0
  5. package/node_modules/grok-mermaid/dist/ansi.d.ts.map +1 -0
  6. package/node_modules/grok-mermaid/dist/ansi.js +23 -0
  7. package/node_modules/grok-mermaid/dist/ansi.js.map +1 -0
  8. package/node_modules/grok-mermaid/dist/canvas.d.ts +87 -0
  9. package/node_modules/grok-mermaid/dist/canvas.d.ts.map +1 -0
  10. package/node_modules/grok-mermaid/dist/canvas.js +366 -0
  11. package/node_modules/grok-mermaid/dist/canvas.js.map +1 -0
  12. package/node_modules/grok-mermaid/dist/graph.d.ts +74 -0
  13. package/node_modules/grok-mermaid/dist/graph.d.ts.map +1 -0
  14. package/node_modules/grok-mermaid/dist/graph.js +91 -0
  15. package/node_modules/grok-mermaid/dist/graph.js.map +1 -0
  16. package/node_modules/grok-mermaid/dist/index.d.ts +32 -0
  17. package/node_modules/grok-mermaid/dist/index.d.ts.map +1 -0
  18. package/node_modules/grok-mermaid/dist/index.js +100 -0
  19. package/node_modules/grok-mermaid/dist/index.js.map +1 -0
  20. package/node_modules/grok-mermaid/dist/labels.d.ts +62 -0
  21. package/node_modules/grok-mermaid/dist/labels.d.ts.map +1 -0
  22. package/node_modules/grok-mermaid/dist/labels.js +324 -0
  23. package/node_modules/grok-mermaid/dist/labels.js.map +1 -0
  24. package/node_modules/grok-mermaid/dist/layout-seq.d.ts +12 -0
  25. package/node_modules/grok-mermaid/dist/layout-seq.d.ts.map +1 -0
  26. package/node_modules/grok-mermaid/dist/layout-seq.js +194 -0
  27. package/node_modules/grok-mermaid/dist/layout-seq.js.map +1 -0
  28. package/node_modules/grok-mermaid/dist/layout.d.ts +87 -0
  29. package/node_modules/grok-mermaid/dist/layout.d.ts.map +1 -0
  30. package/node_modules/grok-mermaid/dist/layout.js +881 -0
  31. package/node_modules/grok-mermaid/dist/layout.js.map +1 -0
  32. package/node_modules/grok-mermaid/dist/parse.d.ts +83 -0
  33. package/node_modules/grok-mermaid/dist/parse.d.ts.map +1 -0
  34. package/node_modules/grok-mermaid/dist/parse.js +1151 -0
  35. package/node_modules/grok-mermaid/dist/parse.js.map +1 -0
  36. package/node_modules/grok-mermaid/dist/source-box.d.ts +18 -0
  37. package/node_modules/grok-mermaid/dist/source-box.d.ts.map +1 -0
  38. package/node_modules/grok-mermaid/dist/source-box.js +78 -0
  39. package/node_modules/grok-mermaid/dist/source-box.js.map +1 -0
  40. package/node_modules/grok-mermaid/dist/types.d.ts +42 -0
  41. package/node_modules/grok-mermaid/dist/types.d.ts.map +1 -0
  42. package/node_modules/grok-mermaid/dist/types.js +1 -0
  43. package/node_modules/grok-mermaid/dist/types.js.map +1 -0
  44. package/node_modules/grok-mermaid/dist/width-data.d.ts +2 -0
  45. package/node_modules/grok-mermaid/dist/width-data.d.ts.map +1 -0
  46. package/node_modules/grok-mermaid/dist/width-data.js +994 -0
  47. package/node_modules/grok-mermaid/dist/width-data.js.map +1 -0
  48. package/node_modules/grok-mermaid/dist/width.d.ts +18 -0
  49. package/node_modules/grok-mermaid/dist/width.d.ts.map +1 -0
  50. package/node_modules/grok-mermaid/dist/width.js +76 -0
  51. package/node_modules/grok-mermaid/dist/width.js.map +1 -0
  52. package/node_modules/grok-mermaid/package.json +49 -0
  53. package/node_modules/grok-mermaid/src/ansi.ts +34 -0
  54. package/node_modules/grok-mermaid/src/canvas.ts +373 -0
  55. package/node_modules/grok-mermaid/src/graph.ts +142 -0
  56. package/node_modules/grok-mermaid/src/index.ts +104 -0
  57. package/node_modules/grok-mermaid/src/labels.ts +326 -0
  58. package/node_modules/grok-mermaid/src/layout-seq.ts +203 -0
  59. package/node_modules/grok-mermaid/src/layout.ts +1015 -0
  60. package/node_modules/grok-mermaid/src/parse.ts +1189 -0
  61. package/node_modules/grok-mermaid/src/source-box.ts +89 -0
  62. package/node_modules/grok-mermaid/src/types.ts +43 -0
  63. package/node_modules/grok-mermaid/src/width-data.ts +993 -0
  64. package/node_modules/grok-mermaid/src/width.ts +74 -0
  65. package/package.json +2 -1
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Sequence diagram layout.
3
+ *
4
+ * Participants get one column each, with lifelines running the full height and
5
+ * a box repeated at top and bottom. Column gaps are solved from the widest
6
+ * thing that has to fit between any two columns — a message label, a note, a
7
+ * self-message stub — then items stack down the canvas in source order.
8
+ */
9
+
10
+ import { Canvas, D, drawTextOverEdges, L, R, U } from './canvas.ts'
11
+ import { fitLabel, WRAP_WIDTH } from './labels.ts'
12
+ import { type CanvasResult, drawBox, type Placed } from './layout.ts'
13
+ import type { NoteAnchor, SeqItem, Sequence } from './parse.ts'
14
+ import { stringWidth } from './width.ts'
15
+
16
+ const PAD = 1
17
+ /** Minimum columns between adjacent lifelines. */
18
+ const SEQ_GAP = 5
19
+ const MAX_CANVAS_CELLS = 1 << 21
20
+
21
+ const sat = (a: number, b: number): number => Math.max(0, a - b)
22
+ const half = (n: number): number => Math.floor(n / 2)
23
+
24
+ /** Where a note box sits, given the lifeline positions. */
25
+ function noteGeometry(xs: number[], anchor: NoteAnchor, textW: number): { x: number; w: number } {
26
+ if (anchor.kind === 'over') {
27
+ const center = half(xs[anchor.from] + xs[anchor.to])
28
+ const w = Math.max(xs[anchor.to] - xs[anchor.from] + 5, textW + 2 * PAD + 2)
29
+ return { x: sat(center, half(w)), w }
30
+ }
31
+ const w = textW + 2 * PAD + 2
32
+ if (anchor.kind === 'left') return { x: sat(xs[anchor.at], 2 + w - 1), w }
33
+ return { x: xs[anchor.at] + 2, w }
34
+ }
35
+
36
+ const itemTextW = (text: string | null): number => (text === null ? 0 : stringWidth(text))
37
+
38
+ export function layoutSequence(seq: Sequence): CanvasResult {
39
+ const n = seq.labels.length
40
+ const labels = seq.labels.map((l) => fitLabel(l, WRAP_WIDTH))
41
+ const boxW = labels.map((l) => Math.max(1, stringWidth(l)) + 2 * PAD + 2)
42
+ const boxH = 3
43
+
44
+ const gaps = Array.from({ length: sat(n, 1) }, (_, i) =>
45
+ Math.max(SEQ_GAP, Math.ceil(boxW[i] / 2) + Math.ceil(boxW[i + 1] / 2) + 1),
46
+ )
47
+
48
+ // Each requirement is "columns l..r together need at least `need` cells".
49
+ const reqs: [number, number, number][] = []
50
+ for (const item of seq.items) {
51
+ if (item.kind === 'message') {
52
+ const tw = itemTextW(item.text)
53
+ if (item.from !== item.to) {
54
+ reqs.push([Math.min(item.from, item.to), Math.max(item.from, item.to), Math.max(tw + 2, 4)])
55
+ } else if (item.from + 1 < n) {
56
+ reqs.push([item.from, item.from + 1, 5 + tw + 2])
57
+ }
58
+ } else if (item.kind === 'note') {
59
+ const tw = stringWidth(item.text)
60
+ const a = item.anchor
61
+ if (a.kind === 'over' && a.from < a.to) {
62
+ reqs.push([a.from, a.to, sat(tw, 1)])
63
+ } else if (a.kind === 'over') {
64
+ const need = Math.ceil((tw + 4) / 2) + 2
65
+ if (a.from > 0) reqs.push([a.from - 1, a.from, need])
66
+ if (a.from + 1 < n) reqs.push([a.from, a.from + 1, need])
67
+ } else if (a.kind === 'left' && a.at > 0) {
68
+ reqs.push([a.at - 1, a.at, tw + 7])
69
+ } else if (a.kind === 'right' && a.at + 1 < n) {
70
+ reqs.push([a.at, a.at + 1, tw + 7])
71
+ }
72
+ }
73
+ }
74
+ // Narrowest spans first, so a wide requirement absorbs what they already gave.
75
+ reqs.sort((a, b) => a[1] - a[0] - (b[1] - b[0]))
76
+ for (const [l, r, need] of reqs) {
77
+ let cur = 0
78
+ for (let i = l; i < r; i++) cur += gaps[i]
79
+ if (cur < need) gaps[r - 1] += need - cur
80
+ }
81
+
82
+ const xs = new Array<number>(n)
83
+ xs[0] = half(boxW[0])
84
+ for (let i = 1; i < n; i++) xs[i] = xs[i - 1] + gaps[i - 1]
85
+
86
+ let canvasW = xs[n - 1] + Math.ceil(boxW[n - 1] / 2) + 1
87
+ for (const item of seq.items) {
88
+ if (item.kind === 'message' && item.from === item.to) {
89
+ canvasW = Math.max(canvasW, xs[item.from] + 5 + itemTextW(item.text) + 1)
90
+ } else if (item.kind === 'note') {
91
+ const g = noteGeometry(xs, item.anchor, stringWidth(item.text))
92
+ canvasW = Math.max(canvasW, g.x + g.w + 1)
93
+ } else if (item.kind === 'divider') {
94
+ canvasW = Math.max(canvasW, stringWidth(item.text) + 4)
95
+ }
96
+ }
97
+
98
+ const rows: number[] = []
99
+ let y = boxH + 1
100
+ for (const item of seq.items) {
101
+ rows.push(y)
102
+ y += rowHeight(item)
103
+ }
104
+ const bottomTop = y
105
+ const canvasH = bottomTop + boxH
106
+
107
+ if (canvasW * canvasH > MAX_CANVAS_CELLS) return null
108
+
109
+ const canvas = new Canvas(canvasW, canvasH)
110
+
111
+ for (let i = 0; i < n; i++) {
112
+ for (const by of [0, bottomTop]) {
113
+ drawBox(canvas, box(sat(xs[i], half(boxW[i])), by, boxW[i], boxH), [labels[i]], 'rect')
114
+ }
115
+ }
116
+ seq.items.forEach((item, k) => {
117
+ if (item.kind !== 'note') return
118
+ const g = noteGeometry(xs, item.anchor, stringWidth(item.text))
119
+ drawBox(canvas, box(g.x, rows[k], g.w, 3), [item.text], 'rect')
120
+ })
121
+
122
+ for (const x of xs) {
123
+ canvas.junction(x, boxH - 1, D)
124
+ canvas.segV(x, boxH, bottomTop - 1)
125
+ canvas.junction(x, bottomTop, U)
126
+ }
127
+
128
+ seq.items.forEach((item, k) => {
129
+ const r = rows[k]
130
+ if (item.kind === 'message') drawMessage(canvas, item, xs, r)
131
+ else if (item.kind === 'divider') drawDivider(canvas, item.text, r, canvasW)
132
+ })
133
+
134
+ canvas.finalizeMask()
135
+ return canvas
136
+ }
137
+
138
+ function rowHeight(item: SeqItem): number {
139
+ if (item.kind === 'note') return 4
140
+ if (item.kind === 'divider') return 2
141
+ if (item.from === item.to) return 4
142
+ return item.text !== null ? 3 : 2
143
+ }
144
+
145
+ /** Geometry for a box drawn by position and size; ranks are irrelevant here. */
146
+ const box = (x: number, y: number, w: number, h: number): Placed => ({
147
+ x,
148
+ y,
149
+ w,
150
+ h,
151
+ cx: x + half(w),
152
+ cy: y + 1,
153
+ rank: 0,
154
+ })
155
+
156
+ function drawMessage(
157
+ canvas: Canvas,
158
+ item: Extract<SeqItem, { kind: 'message' }>,
159
+ xs: number[],
160
+ r: number,
161
+ ): void {
162
+ const lineCh = item.dashed ? '╌' : '─'
163
+
164
+ if (item.from === item.to) {
165
+ // A stub that leaves the lifeline and returns two rows down.
166
+ const x = xs[item.from]
167
+ canvas.junction(x, r, R)
168
+ canvas.set(x + 1, r, lineCh, 'edge')
169
+ canvas.set(x + 2, r, lineCh, 'edge')
170
+ canvas.set(x + 3, r, '╮', 'edge')
171
+ canvas.set(x + 3, r + 1, '│', 'edge')
172
+ canvas.set(x + 1, r + 2, item.head === 'cross' ? '×' : '◄', 'edge')
173
+ canvas.set(x + 2, r + 2, lineCh, 'edge')
174
+ canvas.set(x + 3, r + 2, '╯', 'edge')
175
+ if (item.text !== null) drawTextOverEdges(canvas, item.text, x + 5, r + 1, 'text')
176
+ return
177
+ }
178
+
179
+ const x0 = xs[item.from]
180
+ const x1 = xs[item.to]
181
+ const rightward = x1 > x0
182
+ // A labelled message writes its text on `r` and draws the arrow below it.
183
+ const arrowRow = item.text !== null ? r + 1 : r
184
+ const lo = Math.min(x0, x1)
185
+ const hi = Math.max(x0, x1)
186
+
187
+ canvas.junction(x0, arrowRow, rightward ? R : L)
188
+ for (let x = lo + 1; x < hi; x++) canvas.set(x, arrowRow, lineCh, 'edge')
189
+ const headCh = item.head === 'cross' ? '×' : rightward ? '▶' : '◄'
190
+ canvas.set(rightward ? x1 - 1 : x1 + 1, arrowRow, headCh, 'edge')
191
+
192
+ if (item.text !== null) {
193
+ const span = hi - lo - 1
194
+ const t = fitLabel(item.text, Math.max(1, span))
195
+ drawTextOverEdges(canvas, t, lo + 1 + half(sat(span, stringWidth(t))), r, 'text')
196
+ }
197
+ }
198
+
199
+ /** A full-width rule labelling a `loop` / `alt` / `opt` block boundary. */
200
+ function drawDivider(canvas: Canvas, text: string, r: number, canvasW: number): void {
201
+ for (let x = 0; x < canvasW; x++) canvas.set(x, r, '─', 'edge')
202
+ drawTextOverEdges(canvas, ` ${fitLabel(text, sat(canvasW, 4))} `, 2, r, 'edgeLabel')
203
+ }