flowfig 0.8.2 → 0.8.3
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/README.md +2 -2
- package/dist/code.d.ts +1 -1
- package/dist/code.js +30 -1
- package/dist/geometry.js +17 -11
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ jobs:
|
|
|
65
65
|
- uses: actions/setup-node@v4
|
|
66
66
|
with:
|
|
67
67
|
node-version: 22
|
|
68
|
-
- uses: iamalvisng/flowfig@v0.8.
|
|
68
|
+
- uses: iamalvisng/flowfig@v0.8.3
|
|
69
69
|
with:
|
|
70
70
|
figures: 'docs/**/*.svg' # default **/*.svg
|
|
71
71
|
```
|
|
@@ -478,7 +478,7 @@ jobs:
|
|
|
478
478
|
- uses: actions/setup-node@v4
|
|
479
479
|
with:
|
|
480
480
|
node-version: 22
|
|
481
|
-
- uses: iamalvisng/flowfig@v0.8.
|
|
481
|
+
- uses: iamalvisng/flowfig@v0.8.3
|
|
482
482
|
with:
|
|
483
483
|
figures: 'docs/**/*.svg' # default **/*.svg
|
|
484
484
|
```
|
package/dist/code.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare function matchClose(code: string, open: number): number;
|
|
|
18
18
|
export declare function pyBody(code: string, start: number): Extent;
|
|
19
19
|
export declare function outside(root: string, path: string): boolean;
|
|
20
20
|
export declare function codeFile(root: string, path: string, read?: Read, cache?: Map<string, CodeFile | null>): CodeFile | null;
|
|
21
|
-
export declare
|
|
21
|
+
export declare const locate: (file: CodeFile, symbol: string) => {
|
|
22
22
|
start: number;
|
|
23
23
|
end: number;
|
|
24
24
|
} | null;
|
package/dist/code.js
CHANGED
|
@@ -511,7 +511,7 @@ export function codeFile(root, path, read = readFile, cache = new Map()) {
|
|
|
511
511
|
cache.set(full, file);
|
|
512
512
|
return file;
|
|
513
513
|
}
|
|
514
|
-
|
|
514
|
+
function locateDefinition(file, symbol) {
|
|
515
515
|
const { code, lang } = file;
|
|
516
516
|
const parts = symbol.split('.');
|
|
517
517
|
if (!parts.every((p) => /^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(p)))
|
|
@@ -565,4 +565,33 @@ export function locate(file, symbol) {
|
|
|
565
565
|
}
|
|
566
566
|
return null;
|
|
567
567
|
}
|
|
568
|
+
function enumMember(file, symbol) {
|
|
569
|
+
const { code, lang } = file;
|
|
570
|
+
const [owner, name, ...rest] = symbol.split('.');
|
|
571
|
+
if (!name || rest.length || lang === 'go' || lang === 'py')
|
|
572
|
+
return null;
|
|
573
|
+
const head = new RegExp(`\\benum\\s+${esc(owner)}${E}[^;{}]*\\{`, 'u').exec(code);
|
|
574
|
+
if (!head)
|
|
575
|
+
return null;
|
|
576
|
+
const open = head.index + head[0].length - 1;
|
|
577
|
+
const close = matchClose(code, open);
|
|
578
|
+
let from = open + 1;
|
|
579
|
+
for (let i = from, d = 0; i <= close; i++) {
|
|
580
|
+
const c = code[i];
|
|
581
|
+
if ('{(['.includes(c))
|
|
582
|
+
d++;
|
|
583
|
+
else if (')]'.includes(c) || (c === '}' && i < close))
|
|
584
|
+
d--;
|
|
585
|
+
else if (d > 0 || (c !== ',' && c !== ';' && i < close))
|
|
586
|
+
continue;
|
|
587
|
+
const seg = code.slice(from, i).replace(/^(\s|@\w+(\([^)]*\))?|#\[[^\]]*\]|\[[^\]]*\])+/, '');
|
|
588
|
+
if (new RegExp(`^${esc(name)}${E}`, 'u').test(seg))
|
|
589
|
+
return { start: i - seg.length, end: i };
|
|
590
|
+
if (c === ';')
|
|
591
|
+
return null;
|
|
592
|
+
from = i + 1;
|
|
593
|
+
}
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
export const locate = (file, symbol) => locateDefinition(file, symbol) ?? enumMember(file, symbol);
|
|
568
597
|
export const isDefined = (file, symbol) => locate(file, symbol) != null;
|
package/dist/geometry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STUB } from './model.js';
|
|
1
|
+
import { EDGE_ON, STUB } from './model.js';
|
|
2
2
|
export const avoidOf = (boxes, extra, lanes) => [
|
|
3
3
|
...extra,
|
|
4
4
|
...boxes.map((r) => (lanes ? r : { ...r, box: true })),
|
|
@@ -78,6 +78,8 @@ export function route(edges, rects, tips = new Set(), avoid = [], area, lanes, p
|
|
|
78
78
|
if (around && blocked(around, arc)) {
|
|
79
79
|
const fits = Array.from({ length: ARC_STEPS + 1 }, (_, k) => [around, outward(around, k)]);
|
|
80
80
|
arc = fits.find(([way, at]) => !blocked(way, at))?.[1] ?? arc;
|
|
81
|
+
if (blocked(around, arc))
|
|
82
|
+
around = undefined;
|
|
81
83
|
}
|
|
82
84
|
if (!around && !e.sides && !e.stub && !e.elbow && past.length) {
|
|
83
85
|
const ways = stacked ? ['left', 'right'] : ['above', 'below'];
|
|
@@ -177,6 +179,7 @@ export function route(edges, rects, tips = new Set(), avoid = [], area, lanes, p
|
|
|
177
179
|
const all = Object.values(rects);
|
|
178
180
|
const [x0, y0] = [Math.min(...all.map((r) => r.x)), Math.min(...all.map((r) => r.y))];
|
|
179
181
|
const [x1, y1] = [Math.max(...all.map((r) => r.x + r.w)), Math.max(...all.map((r) => r.y + r.h))];
|
|
182
|
+
const room = (d) => (d > 0 ? d + EDGE_ON / 2 : 0);
|
|
180
183
|
const outside = (way, { x, y }) => (way === 'above' ? y0 - y : way === 'below' ? y - y1 : way === 'left' ? x0 - x : x - x1);
|
|
181
184
|
const roomOf = (way, at, curve, labelW) => Math.max(ARC - 6, lanes || !labelW ? outside(way, { x: at, y: at }) - 6 : outside(way, bezier(curve, 0.5)) + (upright(way) ? labelW / 2 : 9));
|
|
182
185
|
const one = (p) => {
|
|
@@ -335,21 +338,24 @@ export function route(edges, rects, tips = new Set(), avoid = [], area, lanes, p
|
|
|
335
338
|
if (e.x - s.x < 16 && s.y === e.y) {
|
|
336
339
|
return { id: p.id, d: `M ${s.x} ${s.y} H ${e.x}`, mid: { x: (s.x + e.x) / 2, y: s.y }, curve: [s, s, e, e], elbow: [s, e] };
|
|
337
340
|
}
|
|
341
|
+
const own = (r) => [p.a, p.b].some((q) => same(q, r));
|
|
342
|
+
const all = avoid.filter((r) => !own(r));
|
|
343
|
+
const boxes = all.filter((r) => r.box);
|
|
344
|
+
const vClear = (x, ya, yb, m = 2) => !all.some((r) => x > r.x - m && x < r.x + r.w + m && Math.max(ya, yb) > r.y && Math.min(ya, yb) < r.y + r.h);
|
|
345
|
+
const hClear = (y, xa, xb, list = boxes, m = 2) => !list.some((r) => y > r.y - m && y < r.y + r.h + m && Math.max(xa, xb) > r.x && Math.min(xa, xb) < r.x + r.w);
|
|
338
346
|
if (e.x - s.x < 16) {
|
|
339
|
-
const mx = s.x + 8, ex = e.x - 8,
|
|
347
|
+
const mx = s.x + 8, ex = e.x - 8, mid = (s.y + e.y) / 2;
|
|
348
|
+
const ys = Array.from({ length: Math.floor((y1 - y0 + 24) / 4) + 1 }, (_, i) => y0 - 12 + i * 4).sort((u, w) => Math.abs(u - mid) - Math.abs(w - mid));
|
|
349
|
+
const my = ys.find((y) => vClear(mx, s.y, y) && hClear(y, ex, mx, all) && vClear(ex, y, e.y)) ?? mid;
|
|
340
350
|
return {
|
|
341
351
|
id: p.id,
|
|
342
352
|
d: `M ${s.x} ${s.y} H ${mx} V ${my} H ${ex} V ${e.y} H ${e.x}`,
|
|
343
353
|
mid: { x: (mx + ex) / 2, y: my },
|
|
344
354
|
curve: [s, { x: mx, y: my }, { x: ex, y: my }, e],
|
|
345
355
|
elbow: [s, { x: mx, y: s.y }, { x: mx, y: my }, { x: ex, y: my }, { x: ex, y: e.y }, e],
|
|
356
|
+
bleed: { above: room(y0 - my), below: room(my - y1) },
|
|
346
357
|
};
|
|
347
358
|
}
|
|
348
|
-
const own = (r) => [p.a, p.b].some((q) => same(q, r));
|
|
349
|
-
const all = avoid.filter((r) => !own(r));
|
|
350
|
-
const boxes = all.filter((r) => r.box);
|
|
351
|
-
const vClear = (x, ya, yb, m = 2) => !all.some((r) => x > r.x - m && x < r.x + r.w + m && Math.max(ya, yb) > r.y && Math.min(ya, yb) < r.y + r.h);
|
|
352
|
-
const hClear = (y, xa, xb, list = boxes, m = 2) => !list.some((r) => y > r.y - m && y < r.y + r.h + m && Math.max(xa, xb) > r.x && Math.min(xa, xb) < r.x + r.w);
|
|
353
359
|
const mid = (s.x + e.x) / 2;
|
|
354
360
|
const gap = Array.from({ length: Math.floor((e.x - s.x - 16) / 4) + 1 }, (_, i) => s.x + 8 + i * 4).sort((u, w) => Math.abs(u - mid) - Math.abs(w - mid));
|
|
355
361
|
const mx = [mid, e.x - 8, s.x + 8, ...gap].find((x) => vClear(x, s.y, e.y) && hClear(s.y, s.x, x) && hClear(e.y, x, e.x));
|
|
@@ -384,10 +390,10 @@ export function route(edges, rects, tips = new Set(), avoid = [], area, lanes, p
|
|
|
384
390
|
curve: [s, { x, y }, { x: ex, y }, e],
|
|
385
391
|
elbow: [s, { x, y: s.y }, { x, y }, { x: ex, y }, { x: ex, y: e.y }, e],
|
|
386
392
|
bleed: {
|
|
387
|
-
left:
|
|
388
|
-
right:
|
|
389
|
-
above:
|
|
390
|
-
below:
|
|
393
|
+
left: room(x0 - Math.min(x, ex)),
|
|
394
|
+
right: room(Math.max(x, ex) - x1),
|
|
395
|
+
above: room(y0 - y),
|
|
396
|
+
below: room(y - y1),
|
|
391
397
|
},
|
|
392
398
|
};
|
|
393
399
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.8.
|
|
1
|
+
export declare const VERSION = "0.8.3";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// The release list bumps this with package.json.
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.3';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowfig",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Your coding agent draws animated diagrams from your code, and flowfig checks each box and arrow against that code. CI fails when a diagram drifts from the code. The output is one animated SVG with no script, for a README, PR or issue. Works with Claude Code, Cursor, GitHub Copilot, Codex, Gemini CLI, Windsurf and Kiro.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|