agentlas 1.0.42 → 1.0.44
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/CHANGELOG.md +24 -0
- package/engine/agentlas-workforce.cjs +1 -1
- package/engine/hephaestus/runtime.cjs +1 -1
- package/engine/storm/storm.cjs +1 -1
- package/engine/storm/swarm.cjs +1 -1
- package/engine/ui/palette.cjs +1 -0
- package/engine/ui/repl.cjs +34 -4
- package/engine/ui/shell.cjs +22 -3
- package/engine/vendor/mermaid/LICENSE +205 -0
- package/engine/vendor/mermaid/ansi.js +23 -0
- package/engine/vendor/mermaid/canvas.js +366 -0
- package/engine/vendor/mermaid/graph.js +91 -0
- package/engine/vendor/mermaid/index.js +100 -0
- package/engine/vendor/mermaid/labels.js +324 -0
- package/engine/vendor/mermaid/layout-seq.js +194 -0
- package/engine/vendor/mermaid/layout.js +881 -0
- package/engine/vendor/mermaid/package.json +1 -0
- package/engine/vendor/mermaid/parse.js +1108 -0
- package/engine/vendor/mermaid/source-box.js +78 -0
- package/engine/vendor/mermaid/types.js +1 -0
- package/engine/vendor/mermaid/width-data.js +994 -0
- package/engine/vendor/mermaid/width.js +76 -0
- package/engine/vendor/tui/LICENSE +20 -0
- package/engine/vendor/tui/autocomplete.js +632 -0
- package/engine/vendor/tui/components/alt-screen-flash.js +37 -0
- package/engine/vendor/tui/components/box.js +104 -0
- package/engine/vendor/tui/components/cancellable-loader.js +35 -0
- package/engine/vendor/tui/components/editor.js +1961 -0
- package/engine/vendor/tui/components/h-stack.js +43 -0
- package/engine/vendor/tui/components/image.js +90 -0
- package/engine/vendor/tui/components/input.js +378 -0
- package/engine/vendor/tui/components/loader.js +69 -0
- package/engine/vendor/tui/components/markdown.js +806 -0
- package/engine/vendor/tui/components/scroll-view.js +173 -0
- package/engine/vendor/tui/components/select-list.js +159 -0
- package/engine/vendor/tui/components/settings-list.js +182 -0
- package/engine/vendor/tui/components/spacer.js +23 -0
- package/engine/vendor/tui/components/stack.js +111 -0
- package/engine/vendor/tui/components/text.js +89 -0
- package/engine/vendor/tui/components/truncated-text.js +51 -0
- package/engine/vendor/tui/components/v-stack.js +26 -0
- package/engine/vendor/tui/deps/east-asian-width/LICENSE +9 -0
- package/engine/vendor/tui/deps/east-asian-width/index.js +30 -0
- package/engine/vendor/tui/deps/east-asian-width/lookup-data.js +21 -0
- package/engine/vendor/tui/deps/east-asian-width/lookup.js +138 -0
- package/engine/vendor/tui/deps/east-asian-width/utilities.js +24 -0
- package/engine/vendor/tui/deps/marked/LICENSE +44 -0
- package/engine/vendor/tui/deps/marked/index.js +77 -0
- package/engine/vendor/tui/editor-component.js +2 -0
- package/engine/vendor/tui/fuzzy.js +110 -0
- package/engine/vendor/tui/index.js +42 -0
- package/engine/vendor/tui/keybindings.js +209 -0
- package/engine/vendor/tui/keys.js +1174 -0
- package/engine/vendor/tui/kill-ring.js +44 -0
- package/engine/vendor/tui/latex.js +1264 -0
- package/engine/vendor/tui/layout-node.js +6 -0
- package/engine/vendor/tui/layout.js +314 -0
- package/engine/vendor/tui/native-modifiers.js +60 -0
- package/engine/vendor/tui/package.json +1 -0
- package/engine/vendor/tui/stdin-buffer.js +361 -0
- package/engine/vendor/tui/terminal-colors.js +59 -0
- package/engine/vendor/tui/terminal-image.js +518 -0
- package/engine/vendor/tui/terminal.js +436 -0
- package/engine/vendor/tui/tui-alt-screen.js +902 -0
- package/engine/vendor/tui/tui-main-screen.js +533 -0
- package/engine/vendor/tui/tui.js +937 -0
- package/engine/vendor/tui/undo-stack.js +25 -0
- package/engine/vendor/tui/utils.js +1191 -0
- package/engine/vendor/tui/word-navigation.js +96 -0
- package/package.json +2 -6
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { measured } from './width.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sentinel occupying the trailing column of a wide glyph. Never emitted: the
|
|
4
|
+
* line builder skips it so a CJK character claims two cells of layout but
|
|
5
|
+
* contributes one character of output.
|
|
6
|
+
*/
|
|
7
|
+
export const CONT = String.fromCharCode(0);
|
|
8
|
+
/** Connection direction bits, combined into a box-drawing glyph by `maskChar`. */
|
|
9
|
+
export const U = 1;
|
|
10
|
+
export const D = 2;
|
|
11
|
+
export const L = 4;
|
|
12
|
+
export const R = 8;
|
|
13
|
+
/** Line styles, tracked per cell so crossing edges keep their own stroke. */
|
|
14
|
+
export const STY_DOT = 1;
|
|
15
|
+
export const STY_THICK = 2;
|
|
16
|
+
export const STY_SOLID = 4;
|
|
17
|
+
/**
|
|
18
|
+
* A grid of cells. Edges accumulate as direction bits rather than glyphs so
|
|
19
|
+
* that crossings and junctions resolve correctly whatever order they are drawn
|
|
20
|
+
* in; `finalizeMask` turns the accumulated bits into characters at the end.
|
|
21
|
+
*
|
|
22
|
+
* `occupied` marks cells claimed by a box, which edge bits must not overwrite.
|
|
23
|
+
*/
|
|
24
|
+
export class Canvas {
|
|
25
|
+
w;
|
|
26
|
+
h;
|
|
27
|
+
ch;
|
|
28
|
+
cls;
|
|
29
|
+
mask;
|
|
30
|
+
style;
|
|
31
|
+
occupied;
|
|
32
|
+
curStyle = STY_SOLID;
|
|
33
|
+
constructor(w, h) {
|
|
34
|
+
const n = w * h;
|
|
35
|
+
this.w = w;
|
|
36
|
+
this.h = h;
|
|
37
|
+
this.ch = new Array(n).fill(' ');
|
|
38
|
+
this.cls = new Array(n).fill('none');
|
|
39
|
+
this.mask = new Uint8Array(n);
|
|
40
|
+
this.style = new Uint8Array(n);
|
|
41
|
+
this.occupied = new Uint8Array(n);
|
|
42
|
+
}
|
|
43
|
+
idx(x, y) {
|
|
44
|
+
return y * this.w + x;
|
|
45
|
+
}
|
|
46
|
+
set(x, y, c, cls) {
|
|
47
|
+
if (x >= this.w || y >= this.h)
|
|
48
|
+
return;
|
|
49
|
+
const i = this.idx(x, y);
|
|
50
|
+
this.ch[i] = c;
|
|
51
|
+
this.cls[i] = cls;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Accumulate direction bits on a free cell.
|
|
55
|
+
*
|
|
56
|
+
* `cls` is the class to claim the cell for; `border` cells are never
|
|
57
|
+
* reclassified, so a connector meeting a box keeps the box's styling.
|
|
58
|
+
*/
|
|
59
|
+
addBits(x, y, bits, cls = 'edge') {
|
|
60
|
+
if (x >= this.w || y >= this.h)
|
|
61
|
+
return;
|
|
62
|
+
const i = this.idx(x, y);
|
|
63
|
+
if (this.occupied[i])
|
|
64
|
+
return;
|
|
65
|
+
this.mask[i] |= bits;
|
|
66
|
+
this.style[i] |= this.curStyle;
|
|
67
|
+
if (this.cls[i] !== 'border')
|
|
68
|
+
this.cls[i] = cls;
|
|
69
|
+
}
|
|
70
|
+
/** Stamp a finished sub-canvas (a subgraph frame's contents) at an offset. */
|
|
71
|
+
blit(sub, ox, oy) {
|
|
72
|
+
for (let sy = 0; sy < sub.h; sy++) {
|
|
73
|
+
for (let sx = 0; sx < sub.w; sx++) {
|
|
74
|
+
const x = ox + sx;
|
|
75
|
+
const y = oy + sy;
|
|
76
|
+
if (x >= this.w || y >= this.h)
|
|
77
|
+
continue;
|
|
78
|
+
const si = sub.idx(sx, sy);
|
|
79
|
+
const di = this.idx(x, y);
|
|
80
|
+
this.ch[di] = sub.ch[si];
|
|
81
|
+
this.cls[di] = sub.cls[si];
|
|
82
|
+
this.style[di] = sub.style[si];
|
|
83
|
+
this.occupied[di] = 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Add direction bits even to an occupied cell, so an edge can meet a border. */
|
|
88
|
+
junction(x, y, bits) {
|
|
89
|
+
if (x >= this.w || y >= this.h)
|
|
90
|
+
return;
|
|
91
|
+
const i = this.idx(x, y);
|
|
92
|
+
this.mask[i] |= bits;
|
|
93
|
+
if (this.cls[i] !== 'border')
|
|
94
|
+
this.cls[i] = 'edge';
|
|
95
|
+
}
|
|
96
|
+
segV(x, y0, y1) {
|
|
97
|
+
const a = Math.min(y0, y1);
|
|
98
|
+
const b = Math.max(y0, y1);
|
|
99
|
+
for (let y = a; y <= b; y++) {
|
|
100
|
+
let bits = 0;
|
|
101
|
+
if (y > a)
|
|
102
|
+
bits |= U;
|
|
103
|
+
if (y < b)
|
|
104
|
+
bits |= D;
|
|
105
|
+
this.addBits(x, y, bits);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
segH(y, x0, x1) {
|
|
109
|
+
const a = Math.min(x0, x1);
|
|
110
|
+
const b = Math.max(x0, x1);
|
|
111
|
+
for (let x = a; x <= b; x++) {
|
|
112
|
+
let bits = 0;
|
|
113
|
+
if (x > a)
|
|
114
|
+
bits |= L;
|
|
115
|
+
if (x < b)
|
|
116
|
+
bits |= R;
|
|
117
|
+
this.addBits(x, y, bits);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Resolve accumulated direction bits into glyphs, honouring line style. */
|
|
121
|
+
finalizeMask() {
|
|
122
|
+
for (let i = 0; i < this.ch.length; i++) {
|
|
123
|
+
if (this.mask[i] !== 0 && this.ch[i] === ' ') {
|
|
124
|
+
const c = maskChar(this.mask[i]);
|
|
125
|
+
this.ch[i] =
|
|
126
|
+
this.style[i] === STY_DOT ? dottedChar(c) : this.style[i] === STY_THICK ? thickChar(c) : c;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Mirror top-to-bottom for `BT`. Rows reorder but within-row text does not,
|
|
132
|
+
* so labels stay readable; box-drawing glyphs flip to match.
|
|
133
|
+
*/
|
|
134
|
+
flipVertical() {
|
|
135
|
+
for (let y = 0; y < Math.floor(this.h / 2); y++) {
|
|
136
|
+
const y2 = this.h - 1 - y;
|
|
137
|
+
for (let x = 0; x < this.w; x++) {
|
|
138
|
+
const i = this.idx(x, y);
|
|
139
|
+
const j = this.idx(x, y2);
|
|
140
|
+
[this.ch[i], this.ch[j]] = [this.ch[j], this.ch[i]];
|
|
141
|
+
[this.cls[i], this.cls[j]] = [this.cls[j], this.cls[i]];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (let i = 0; i < this.ch.length; i++)
|
|
145
|
+
this.ch[i] = flipGlyphV(this.ch[i]);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Mirror left-to-right for `RL`. Mirroring reverses each row, so after
|
|
149
|
+
* flipping glyphs each text/label run is reversed back to reading order.
|
|
150
|
+
*/
|
|
151
|
+
flipHorizontal() {
|
|
152
|
+
for (let y = 0; y < this.h; y++) {
|
|
153
|
+
for (let x = 0; x < Math.floor(this.w / 2); x++) {
|
|
154
|
+
const x2 = this.w - 1 - x;
|
|
155
|
+
const i = this.idx(x, y);
|
|
156
|
+
const j = this.idx(x2, y);
|
|
157
|
+
[this.ch[i], this.ch[j]] = [this.ch[j], this.ch[i]];
|
|
158
|
+
[this.cls[i], this.cls[j]] = [this.cls[j], this.cls[i]];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (let i = 0; i < this.ch.length; i++)
|
|
162
|
+
this.ch[i] = flipGlyphH(this.ch[i]);
|
|
163
|
+
for (let y = 0; y < this.h; y++) {
|
|
164
|
+
let x = 0;
|
|
165
|
+
while (x < this.w) {
|
|
166
|
+
const cls = this.cls[this.idx(x, y)];
|
|
167
|
+
if (cls === 'text' || cls === 'edgeLabel') {
|
|
168
|
+
const start = this.idx(x, y);
|
|
169
|
+
while (x < this.w && this.cls[this.idx(x, y)] === cls)
|
|
170
|
+
x++;
|
|
171
|
+
const end = this.idx(x, y);
|
|
172
|
+
reverseSlice(this.ch, start, end);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
x++;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Group each row into runs of one class, dropping wide-glyph continuations. */
|
|
181
|
+
toLines() {
|
|
182
|
+
const plain = [];
|
|
183
|
+
const styled = [];
|
|
184
|
+
let width = 0;
|
|
185
|
+
for (let y = 0; y < this.h; y++) {
|
|
186
|
+
// A trailing CONT counts as painted: it is the second cell of a wide
|
|
187
|
+
// glyph, so the row really does reach that column.
|
|
188
|
+
let last = 0;
|
|
189
|
+
for (let x = this.w - 1; x >= 0; x--) {
|
|
190
|
+
if (this.ch[this.idx(x, y)] !== ' ') {
|
|
191
|
+
last = x + 1;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
width = Math.max(width, last);
|
|
196
|
+
const spans = [];
|
|
197
|
+
let plainRow = '';
|
|
198
|
+
let run = '';
|
|
199
|
+
let runCls = 'none';
|
|
200
|
+
for (let x = 0; x < last; x++) {
|
|
201
|
+
const i = this.idx(x, y);
|
|
202
|
+
const c = this.ch[i];
|
|
203
|
+
if (c === CONT)
|
|
204
|
+
continue;
|
|
205
|
+
const cls = this.cls[i];
|
|
206
|
+
plainRow += c;
|
|
207
|
+
if (cls !== runCls && run !== '') {
|
|
208
|
+
spans.push({ text: run, cls: runCls });
|
|
209
|
+
run = '';
|
|
210
|
+
}
|
|
211
|
+
runCls = cls;
|
|
212
|
+
run += c;
|
|
213
|
+
}
|
|
214
|
+
if (run !== '')
|
|
215
|
+
spans.push({ text: run, cls: runCls });
|
|
216
|
+
styled.push(spans);
|
|
217
|
+
// Only ASCII spaces, which is all a blank cell ever holds. Trimming `\s`
|
|
218
|
+
// would eat a trailing NBSP that `styled` keeps, desyncing the two.
|
|
219
|
+
plain.push(plainRow.replace(/ +$/, ''));
|
|
220
|
+
}
|
|
221
|
+
let first = 0;
|
|
222
|
+
while (first < plain.length && plain[first] === '')
|
|
223
|
+
first++;
|
|
224
|
+
let end = plain.length;
|
|
225
|
+
while (end > first && plain[end - 1] === '')
|
|
226
|
+
end--;
|
|
227
|
+
return { plain: plain.slice(first, end), styled: styled.slice(first, end), width };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function reverseSlice(arr, start, end) {
|
|
231
|
+
for (let i = start, j = end - 1; i < j; i++, j--) {
|
|
232
|
+
;
|
|
233
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Paint `text` at `x, y`, one grapheme cluster per cell.
|
|
238
|
+
*
|
|
239
|
+
* A wide cluster claims a second cell, marked with `CONT` so the line builder
|
|
240
|
+
* emits one character for it rather than a stray space.
|
|
241
|
+
*/
|
|
242
|
+
export function drawText(canvas, text, x, y, cls) {
|
|
243
|
+
let cur = x;
|
|
244
|
+
for (const [cluster, cw] of measured(text)) {
|
|
245
|
+
if (cw === 0)
|
|
246
|
+
continue;
|
|
247
|
+
canvas.set(cur, y, cluster, cls);
|
|
248
|
+
for (let k = 1; k < cw; k++)
|
|
249
|
+
canvas.set(cur + k, y, CONT, cls);
|
|
250
|
+
cur += cw;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Paint `text` at `x, y`, clearing any edge bits underneath first.
|
|
255
|
+
*
|
|
256
|
+
* Used where text sits on top of a drawn line (sequence messages, dividers,
|
|
257
|
+
* compartment rows) and must win over it.
|
|
258
|
+
*/
|
|
259
|
+
export function drawTextOverEdges(canvas, text, x, y, cls) {
|
|
260
|
+
let cur = x;
|
|
261
|
+
for (const [cluster, cw] of measured(text)) {
|
|
262
|
+
if (cw === 0)
|
|
263
|
+
continue;
|
|
264
|
+
for (let k = 0; k < cw; k++) {
|
|
265
|
+
if (cur + k < canvas.w && y < canvas.h)
|
|
266
|
+
canvas.mask[canvas.idx(cur + k, y)] = 0;
|
|
267
|
+
canvas.set(cur + k, y, k === 0 ? cluster : CONT, cls);
|
|
268
|
+
}
|
|
269
|
+
cur += cw;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
export function maskChar(mask) {
|
|
273
|
+
switch (mask) {
|
|
274
|
+
case 0:
|
|
275
|
+
return ' ';
|
|
276
|
+
case U:
|
|
277
|
+
case D:
|
|
278
|
+
case U | D:
|
|
279
|
+
return '│';
|
|
280
|
+
case L:
|
|
281
|
+
case R:
|
|
282
|
+
case L | R:
|
|
283
|
+
return '─';
|
|
284
|
+
case D | R:
|
|
285
|
+
return '┌';
|
|
286
|
+
case D | L:
|
|
287
|
+
return '┐';
|
|
288
|
+
case U | R:
|
|
289
|
+
return '└';
|
|
290
|
+
case U | L:
|
|
291
|
+
return '┘';
|
|
292
|
+
case U | D | R:
|
|
293
|
+
return '├';
|
|
294
|
+
case U | D | L:
|
|
295
|
+
return '┤';
|
|
296
|
+
case D | L | R:
|
|
297
|
+
return '┬';
|
|
298
|
+
case U | L | R:
|
|
299
|
+
return '┴';
|
|
300
|
+
default:
|
|
301
|
+
return '┼';
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const DOTTED = { '─': '╌', '│': '╎' };
|
|
305
|
+
const THICK = {
|
|
306
|
+
'─': '━',
|
|
307
|
+
'│': '┃',
|
|
308
|
+
'┌': '┏',
|
|
309
|
+
'┐': '┓',
|
|
310
|
+
'└': '┗',
|
|
311
|
+
'┘': '┛',
|
|
312
|
+
'├': '┣',
|
|
313
|
+
'┤': '┫',
|
|
314
|
+
'┬': '┳',
|
|
315
|
+
'┴': '┻',
|
|
316
|
+
'┼': '╋',
|
|
317
|
+
};
|
|
318
|
+
const FLIP_V = {
|
|
319
|
+
'┌': '└',
|
|
320
|
+
'└': '┌',
|
|
321
|
+
'┐': '┘',
|
|
322
|
+
'┘': '┐',
|
|
323
|
+
'┏': '┗',
|
|
324
|
+
'┗': '┏',
|
|
325
|
+
'┓': '┛',
|
|
326
|
+
'┛': '┓',
|
|
327
|
+
'╭': '╰',
|
|
328
|
+
'╰': '╭',
|
|
329
|
+
'╮': '╯',
|
|
330
|
+
'╯': '╮',
|
|
331
|
+
'┬': '┴',
|
|
332
|
+
'┴': '┬',
|
|
333
|
+
'┳': '┻',
|
|
334
|
+
'┻': '┳',
|
|
335
|
+
'▼': '▲',
|
|
336
|
+
'▲': '▼',
|
|
337
|
+
'▽': '△',
|
|
338
|
+
'△': '▽',
|
|
339
|
+
};
|
|
340
|
+
const FLIP_H = {
|
|
341
|
+
'┌': '┐',
|
|
342
|
+
'┐': '┌',
|
|
343
|
+
'└': '┘',
|
|
344
|
+
'┘': '└',
|
|
345
|
+
'┏': '┓',
|
|
346
|
+
'┓': '┏',
|
|
347
|
+
'┗': '┛',
|
|
348
|
+
'┛': '┗',
|
|
349
|
+
'╭': '╮',
|
|
350
|
+
'╮': '╭',
|
|
351
|
+
'╰': '╯',
|
|
352
|
+
'╯': '╰',
|
|
353
|
+
'├': '┤',
|
|
354
|
+
'┤': '├',
|
|
355
|
+
'┣': '┫',
|
|
356
|
+
'┫': '┣',
|
|
357
|
+
'▶': '◄',
|
|
358
|
+
'◄': '▶',
|
|
359
|
+
'▷': '◁',
|
|
360
|
+
'◁': '▷',
|
|
361
|
+
};
|
|
362
|
+
export const dottedChar = (c) => DOTTED[c] ?? c;
|
|
363
|
+
export const thickChar = (c) => THICK[c] ?? c;
|
|
364
|
+
export const flipGlyphV = (c) => FLIP_V[c] ?? c;
|
|
365
|
+
export const flipGlyphH = (c) => FLIP_H[c] ?? c;
|
|
366
|
+
//# sourceMappingURL=canvas.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared diagram model. Flowchart, state, class and ER sources all parse
|
|
3
|
+
* into a `Graph`; only sequence diagrams have their own model.
|
|
4
|
+
*/
|
|
5
|
+
import { asciiUpper } from './labels.js';
|
|
6
|
+
/** Caps that keep layout bounded; exceeding one drops the diagram to fallback. */
|
|
7
|
+
export const MAX_NODES = 128;
|
|
8
|
+
export const MAX_EDGES = 512;
|
|
9
|
+
export const MAX_GROUPS = 24;
|
|
10
|
+
export const MAX_GROUP_DEPTH = 6;
|
|
11
|
+
/** Class members / ER attributes listed per box before eliding with `…`. */
|
|
12
|
+
export const MAX_MEMBERS = 8;
|
|
13
|
+
export const emptyClassInfo = () => ({ annotation: null, attrs: [], methods: [] });
|
|
14
|
+
/** `LR`/`RL`/`BT` as written in a header or `direction` statement; else `down`. */
|
|
15
|
+
export function parseDir(token) {
|
|
16
|
+
switch (asciiUpper(token)) {
|
|
17
|
+
case 'LR':
|
|
18
|
+
return 'right';
|
|
19
|
+
case 'RL':
|
|
20
|
+
return 'left';
|
|
21
|
+
case 'BT':
|
|
22
|
+
return 'up';
|
|
23
|
+
default:
|
|
24
|
+
return 'down';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class Graph {
|
|
28
|
+
nodes = [];
|
|
29
|
+
edges = [];
|
|
30
|
+
index = new Map();
|
|
31
|
+
groups = [];
|
|
32
|
+
/** Innermost subgraph each node was declared in, parallel to `nodes`. */
|
|
33
|
+
nodeGroup = [];
|
|
34
|
+
curGroup = null;
|
|
35
|
+
/** Set when a cap was hit; the caller abandons the parse. */
|
|
36
|
+
overCap = false;
|
|
37
|
+
/**
|
|
38
|
+
* Text the flowchart grammar could not read and silently discarded.
|
|
39
|
+
*
|
|
40
|
+
* Flowchart parsing is deliberately lenient — a malformed statement
|
|
41
|
+
* contributes whatever prefix parsed and the rest is dropped — so without
|
|
42
|
+
* these the reader gets a clean diagram that is not what they wrote.
|
|
43
|
+
*/
|
|
44
|
+
warnings = [];
|
|
45
|
+
dir = 'down';
|
|
46
|
+
constructor(dir = 'down') {
|
|
47
|
+
this.dir = dir;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Index of `id`, creating the node if new. A later declaration carrying a
|
|
51
|
+
* label overwrites the placeholder one an edge created. Returns `null` once
|
|
52
|
+
* `MAX_NODES` is reached, which aborts the parse.
|
|
53
|
+
*/
|
|
54
|
+
nodeIndex(id, label, shape) {
|
|
55
|
+
const existing = this.index.get(id);
|
|
56
|
+
if (existing !== undefined) {
|
|
57
|
+
if (label !== null) {
|
|
58
|
+
this.nodes[existing].label = label;
|
|
59
|
+
this.nodes[existing].shape = shape;
|
|
60
|
+
}
|
|
61
|
+
return existing;
|
|
62
|
+
}
|
|
63
|
+
if (this.nodes.length >= MAX_NODES) {
|
|
64
|
+
this.overCap = true;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
this.index.set(id, this.nodes.length);
|
|
68
|
+
this.nodes.push({ label: label ?? id, shape });
|
|
69
|
+
this.nodeGroup.push(this.curGroup);
|
|
70
|
+
return this.nodes.length - 1;
|
|
71
|
+
}
|
|
72
|
+
/** Set a node's label without disturbing its shape, creating it if new. */
|
|
73
|
+
nodeLabel(id, label) {
|
|
74
|
+
const existing = this.index.get(id);
|
|
75
|
+
if (existing !== undefined) {
|
|
76
|
+
this.nodes[existing].label = label;
|
|
77
|
+
return existing;
|
|
78
|
+
}
|
|
79
|
+
return this.nodeIndex(id, label, 'round');
|
|
80
|
+
}
|
|
81
|
+
/** Append an edge, or flag `overCap` when `MAX_EDGES` is reached. */
|
|
82
|
+
pushEdge(edge) {
|
|
83
|
+
if (this.edges.length >= MAX_EDGES) {
|
|
84
|
+
this.overCap = true;
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
this.edges.push(edge);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { stripControls } from './labels.js';
|
|
2
|
+
import { layoutClass, layoutFlowchart, layoutGrouped } from './layout.js';
|
|
3
|
+
import { layoutSequence } from './layout-seq.js';
|
|
4
|
+
import { diagramKind, parseClass, parseEr, parseGraph, parseSequence, parseState } from './parse.js';
|
|
5
|
+
export { DEFAULT_THEME, toAnsi } from './ansi.js';
|
|
6
|
+
export { diagramKind } from './parse.js';
|
|
7
|
+
export { sourceBox } from './source-box.js';
|
|
8
|
+
/**
|
|
9
|
+
* Render a Mermaid source block as Unicode box-drawing art.
|
|
10
|
+
*
|
|
11
|
+
* Supported: `graph`/`flowchart` (including `subgraph`), `stateDiagram`,
|
|
12
|
+
* `classDiagram`, `erDiagram` and `sequenceDiagram`.
|
|
13
|
+
*
|
|
14
|
+
* The diagram is laid out at whatever size it needs; `art.width` reports the
|
|
15
|
+
* columns that turned out to be. Deciding what to do when that exceeds the
|
|
16
|
+
* space at hand is the caller's — `sourceBox` is the usual answer:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const art = render(src)
|
|
20
|
+
* show(art && art.width <= cols ? art : sourceBox(src, cols))
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* `null` means there is no art to show: blank input, a syntax error, a diagram
|
|
24
|
+
* type this renderer does not draw, or one large enough that laying it out is
|
|
25
|
+
* refused. `diagramKind` separates the middle two.
|
|
26
|
+
*
|
|
27
|
+
* Rendering is best-effort. A flowchart keeps whatever parsed; the stricter
|
|
28
|
+
* grammars additionally get one retry without their final line, which is what
|
|
29
|
+
* keeps a streaming diagram on screen while its last statement is half-typed.
|
|
30
|
+
* Everything given up on is listed in `art.warnings` — advisory only, never a
|
|
31
|
+
* reason to withhold the art.
|
|
32
|
+
*/
|
|
33
|
+
export function render(src) {
|
|
34
|
+
src = stripControls(src);
|
|
35
|
+
if (src.trim() === '')
|
|
36
|
+
return null;
|
|
37
|
+
const drawn = attempt(src);
|
|
38
|
+
if (drawn === null)
|
|
39
|
+
return null;
|
|
40
|
+
return { ...drawn.canvas.toLines(), warnings: drawn.warnings };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Draw `src`, retrying once without its last line if the grammar rejects it.
|
|
44
|
+
*
|
|
45
|
+
* State, class, ER and sequence fail a whole diagram on one unreadable
|
|
46
|
+
* statement, and while a source is streaming its last line is usually still
|
|
47
|
+
* being typed — so without this a diagram alternates with the source box all
|
|
48
|
+
* the way in. Only the final line is dropped, and doing so is always reported,
|
|
49
|
+
* so a finished document with a bad last line still says what it lost rather
|
|
50
|
+
* than quietly rendering short.
|
|
51
|
+
*/
|
|
52
|
+
function attempt(src) {
|
|
53
|
+
const drawn = draw(src);
|
|
54
|
+
if (drawn !== null)
|
|
55
|
+
return drawn;
|
|
56
|
+
const body = src.replace(/\s+$/, '');
|
|
57
|
+
const cut = body.lastIndexOf('\n');
|
|
58
|
+
if (cut === -1)
|
|
59
|
+
return null;
|
|
60
|
+
const salvaged = draw(body.slice(0, cut));
|
|
61
|
+
if (salvaged === null)
|
|
62
|
+
return null;
|
|
63
|
+
const dropped = body.slice(cut + 1).trim();
|
|
64
|
+
return {
|
|
65
|
+
canvas: salvaged.canvas,
|
|
66
|
+
warnings: [...salvaged.warnings, `dropped, unreadable final line: "${dropped}"`],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Dispatch on the declared diagram type; `null` means nothing was drawn. */
|
|
70
|
+
function draw(src) {
|
|
71
|
+
const plain = (canvas) => (canvas === null ? null : { canvas, warnings: [] });
|
|
72
|
+
switch (diagramKind(src)) {
|
|
73
|
+
case 'flowchart': {
|
|
74
|
+
const graph = parseGraph(src);
|
|
75
|
+
if (graph === null)
|
|
76
|
+
return null;
|
|
77
|
+
const canvas = graph.groups.length === 0 ? layoutFlowchart(graph) : layoutGrouped(graph);
|
|
78
|
+
return canvas === null ? null : { canvas, warnings: graph.warnings };
|
|
79
|
+
}
|
|
80
|
+
case 'state': {
|
|
81
|
+
const state = parseState(src);
|
|
82
|
+
return state === null ? null : plain(layoutFlowchart(state));
|
|
83
|
+
}
|
|
84
|
+
case 'class': {
|
|
85
|
+
const cls = parseClass(src);
|
|
86
|
+
return cls === null ? null : plain(layoutClass(cls.graph, cls.infos));
|
|
87
|
+
}
|
|
88
|
+
case 'er': {
|
|
89
|
+
const er = parseEr(src);
|
|
90
|
+
return er === null ? null : plain(layoutClass(er.graph, er.infos));
|
|
91
|
+
}
|
|
92
|
+
case 'sequence': {
|
|
93
|
+
const seq = parseSequence(src);
|
|
94
|
+
return seq === null ? null : plain(layoutSequence(seq));
|
|
95
|
+
}
|
|
96
|
+
default:
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=index.js.map
|