@shibayama/pdgkit 0.1.0 → 0.1.1
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 +45 -27
- package/dist/core.cjs +43 -14
- package/dist/core.js +43 -14
- package/dist/index.cjs +44 -15
- package/dist/index.d.cts +20 -78
- package/dist/index.d.ts +20 -78
- package/dist/index.js +44 -15
- package/dist/pdgkit-mcp.cjs +45 -16
- package/dist/pdgkit-mcp.js +45 -16
- package/dist/pdgkit.cjs +44 -15
- package/dist/pdgkit.global.js +6 -6
- package/dist/pdgkit.js +44 -15
- package/docs/ai-authoring-guide.md +233 -283
- package/docs/spec.md +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import { Lang, DiagramKind, Diagnostic } from './core.js';
|
|
2
2
|
export { Bilingual, Box, Containment, Doc, Edge, EdgeOp, LabelPlacement, LaidOut, LaidOutEdge, LaidOutNode, Node, OP_TABLE, PATTERN_LABEL, PATTERN_SOURCE, PatternId, RenderOptions, SAMPLES, SAMPLE_ORDER, SampleId, Shape, chooseLabelPlacement, estimateTextWidth, layout, parse, refsToCsv, refsToMarkdown, render, splitBilingual, stripComment } from './core.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
* renderer needs (createElementNS, setAttribute, appendChild, textContent) and
|
|
7
|
-
* serializes the tree to XML. `withShimDocument()` scopes the shim around a render
|
|
8
|
-
* call, so rendering also works in a real browser without altering the page's document.
|
|
9
|
-
*/
|
|
10
|
-
/** A serializable SVG node. The shape `render()` actually produces and consumes. */
|
|
4
|
+
/** Minimal, zero-dependency SVG DOM shim: the element operations render() needs, plus XML serialization. */
|
|
5
|
+
/** A serializable SVG node. */
|
|
11
6
|
interface SvgNode {
|
|
12
7
|
readonly nodeType: 'element';
|
|
13
8
|
tagName: string;
|
|
14
9
|
namespaceURI: string;
|
|
15
|
-
/**
|
|
10
|
+
/** Attributes, in insertion order. */
|
|
16
11
|
attrs: Map<string, string>;
|
|
17
12
|
children: SvgNode[];
|
|
18
|
-
/** Text content, or null when the
|
|
13
|
+
/** Text content, or null when the node has element children. */
|
|
19
14
|
text: string | null;
|
|
20
15
|
setAttribute(name: string, value: string): void;
|
|
21
16
|
getAttribute(name: string): string | null;
|
|
@@ -24,24 +19,12 @@ interface SvgNode {
|
|
|
24
19
|
set textContent(value: string);
|
|
25
20
|
get textContent(): string;
|
|
26
21
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Install the shim as `globalThis.document`, idempotently, but only when no DOM
|
|
29
|
-
* with `createElementNS` is already present. In a real browser this is a no-op,
|
|
30
|
-
* so the same code path serves both environments.
|
|
31
|
-
*/
|
|
22
|
+
/** Install the shim as `globalThis.document`, unless a real DOM is already present. Idempotent. */
|
|
32
23
|
declare function installDomShim(): void;
|
|
33
|
-
/**
|
|
34
|
-
* Serialize a shim element tree to an XML string. Produces self-closing tags for
|
|
35
|
-
* empty elements and correctly escapes attribute values and text content.
|
|
36
|
-
*/
|
|
24
|
+
/** Serialize a shim element tree to an XML string (self-closing empty tags, escaped values). */
|
|
37
25
|
declare function serializeSvg(node: SvgNode): string;
|
|
38
26
|
|
|
39
|
-
/**
|
|
40
|
-
* Compute the tight content bounding box of a rendered SVG tree analytically
|
|
41
|
-
* (without `getBBox`), by walking the element tree and measuring each primitive;
|
|
42
|
-
* text width reuses {@link estimateTextWidth}. Used to crop exports to the drawn
|
|
43
|
-
* extent. Also provides raster and display dimension helpers.
|
|
44
|
-
*/
|
|
27
|
+
/** Tight content bounding box of a rendered SVG tree, computed analytically (no `getBBox`), reusing {@link estimateTextWidth} for text. Also provides raster/display dimension helpers. */
|
|
45
28
|
|
|
46
29
|
interface ViewBox {
|
|
47
30
|
minX: number;
|
|
@@ -49,26 +32,16 @@ interface ViewBox {
|
|
|
49
32
|
width: number;
|
|
50
33
|
height: number;
|
|
51
34
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Compute the tight content bounding box of a rendered SVG tree, padded by `bleed`
|
|
54
|
-
* (default 3 units).
|
|
55
|
-
*/
|
|
35
|
+
/** Tight content bounding box of a rendered SVG tree, padded by `bleed` (default 3 units). */
|
|
56
36
|
declare function computeContentBox(root: SvgNode, bleed?: number): ViewBox;
|
|
57
|
-
/**
|
|
58
|
-
* Display dimensions for a viewBox so the long side is at least `targetSide` px
|
|
59
|
-
* (browser default: 1600). Mirrors `svgDisplayDimensionsForViewBox()`.
|
|
60
|
-
*/
|
|
37
|
+
/** Display dimensions for a viewBox so the long side is at least `targetSide` px (default 1600). */
|
|
61
38
|
declare function svgDisplayDimensions(viewBox: Pick<ViewBox, 'width' | 'height'>, targetSide?: number): {
|
|
62
39
|
width: number;
|
|
63
40
|
height: number;
|
|
64
41
|
scale: number;
|
|
65
42
|
};
|
|
66
43
|
|
|
67
|
-
/**
|
|
68
|
-
* Headless SVG rendering: `.pdg` source -> standalone SVG string.
|
|
69
|
-
* Crops to the real drawn extent (bleed 3) and sizes the document so the long side
|
|
70
|
-
* is at least `targetSide` px (default 1600).
|
|
71
|
-
*/
|
|
44
|
+
/** Headless SVG rendering: `.pdg` source → standalone SVG string. */
|
|
72
45
|
|
|
73
46
|
interface RenderToSvgOptions {
|
|
74
47
|
/** Display language: 'ja' (default), 'en', or 'both' (bilingual two-line labels). */
|
|
@@ -100,16 +73,10 @@ interface RenderToSvgResult {
|
|
|
100
73
|
declare function renderToSvg(source: string, opts?: RenderToSvgOptions): RenderToSvgResult;
|
|
101
74
|
|
|
102
75
|
/**
|
|
103
|
-
* Validation for AI authoring
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* in a comment) is checked against the kind inferred from structure; a mismatch
|
|
108
|
-
* is reported as an error. Guards against producing the wrong diagram type.
|
|
109
|
-
* - Lints: clearer hints for chained connections (`A -> B -> C`) and operators
|
|
110
|
-
* without surrounding spaces (`11-12`).
|
|
111
|
-
*
|
|
112
|
-
* Run on AI-generated `.pdg` and feed the diagnostics back until there are no errors.
|
|
76
|
+
* Validation for AI authoring: runs the core parser, checks the optional
|
|
77
|
+
* `#! kind: block|flow|state|seq` directive against the kind inferred from
|
|
78
|
+
* structure (mismatch = error), and adds lints for chained connections and
|
|
79
|
+
* unspaced operators. Feed the diagnostics back to the author until no errors remain.
|
|
113
80
|
*/
|
|
114
81
|
|
|
115
82
|
interface ValidateResult {
|
|
@@ -135,11 +102,7 @@ interface ValidateResult {
|
|
|
135
102
|
*/
|
|
136
103
|
declare function validate(source: string): ValidateResult;
|
|
137
104
|
|
|
138
|
-
/**
|
|
139
|
-
* Raster output (PNG / JPEG) via `@resvg/resvg-js`, with the bundled IPAex Gothic
|
|
140
|
-
* font embedded and a white background. Default scale is 8x the content box (capped
|
|
141
|
-
* to 24000 px/side and 1e8 px total). Native dependencies are imported lazily.
|
|
142
|
-
*/
|
|
105
|
+
/** Raster output (PNG / JPEG) via `@resvg/resvg-js` with the bundled IPAex Gothic font and a white background. Native dependencies are imported lazily. */
|
|
143
106
|
|
|
144
107
|
interface RasterOptions {
|
|
145
108
|
lang?: Lang;
|
|
@@ -153,12 +116,7 @@ declare function renderToPng(source: string, opts?: RasterOptions): Promise<Uint
|
|
|
153
116
|
/** Render `.pdg` source to a JPEG (8× by default, white background). */
|
|
154
117
|
declare function renderToJpeg(source: string, opts?: RasterOptions): Promise<Uint8Array>;
|
|
155
118
|
|
|
156
|
-
/**
|
|
157
|
-
* PDF output (A4, IPAex font embedded). Vector by default (jsPDF + svg2pdf.js under
|
|
158
|
-
* jsdom, with analytic getBBox / getComputedTextLength shims), falling back to a
|
|
159
|
-
* high-resolution raster PDF if the vector path fails. The figure is centered with
|
|
160
|
-
* a 10 mm margin and the page orientation follows its aspect ratio.
|
|
161
|
-
*/
|
|
119
|
+
/** PDF output (A4, IPAex font embedded). Vector by default (jsPDF + svg2pdf.js under jsdom, with analytic getBBox/getComputedTextLength shims), falling back to a high-resolution raster PDF if the vector path fails. */
|
|
162
120
|
|
|
163
121
|
interface PdfOptions {
|
|
164
122
|
lang?: Lang;
|
|
@@ -172,14 +130,7 @@ interface PdfOptions {
|
|
|
172
130
|
/** Render `.pdg` source to a PDF (A4, IPAex font, vector preferred). */
|
|
173
131
|
declare function renderToPdf(source: string, opts?: PdfOptions): Promise<Uint8Array>;
|
|
174
132
|
|
|
175
|
-
/**
|
|
176
|
-
* PPTX output: `.pdg` → PowerPoint presentation (one 16:9 slide).
|
|
177
|
-
*
|
|
178
|
-
* - image mode (default): the figure is rasterized (resvg) and placed as a
|
|
179
|
-
* centered picture — highest visual fidelity.
|
|
180
|
-
* - editable mode (`editable: true`): every SVG primitive is converted to an
|
|
181
|
-
* editable PowerPoint shape / connector / text box, for later tweaking.
|
|
182
|
-
*/
|
|
133
|
+
/** PPTX output: `.pdg` → PowerPoint (one 16:9 slide). Image mode (default) places a rasterized picture; editable mode converts each SVG primitive to an editable PowerPoint shape, connector, or text box. */
|
|
183
134
|
|
|
184
135
|
interface PptxOptions {
|
|
185
136
|
lang?: Lang;
|
|
@@ -193,22 +144,13 @@ interface PptxOptions {
|
|
|
193
144
|
/** Render `.pdg` source to a PPTX (image by default, or editable shapes). */
|
|
194
145
|
declare function renderToPptx(source: string, opts?: PptxOptions): Promise<Uint8Array>;
|
|
195
146
|
|
|
196
|
-
/**
|
|
197
|
-
* pdgkit Node API — the browser-free rendering surface a host tool calls.
|
|
198
|
-
*
|
|
199
|
-
* Implemented: SVG, PNG, JPEG, PDF, PPTX (image + editable), validation, and the
|
|
200
|
-
* reference-sign table exporters. All run with no browser.
|
|
201
|
-
*/
|
|
147
|
+
/** pdgkit Node API — the browser-free rendering surface a host tool calls. */
|
|
202
148
|
|
|
203
149
|
/** The pdgkit package version. Keep in sync with package.json. */
|
|
204
|
-
declare const VERSION = "0.1.
|
|
150
|
+
declare const VERSION = "0.1.1";
|
|
205
151
|
/** Convenience: render to SVG and return just the string. */
|
|
206
152
|
declare function toSvgString(source: string, lang?: Lang): string;
|
|
207
|
-
/**
|
|
208
|
-
* Read the bundled AI authoring guide (docs/ai-authoring-guide.md) as a string.
|
|
209
|
-
* Inject this into an LLM's system prompt so it can write valid `.pdg`, instead of
|
|
210
|
-
* pasting the guide by hand.
|
|
211
|
-
*/
|
|
153
|
+
/** Read the bundled AI authoring guide (docs/ai-authoring-guide.md) as a string, for injecting into an LLM's system prompt. */
|
|
212
154
|
declare function loadAuthoringGuide(): string;
|
|
213
155
|
|
|
214
156
|
export { Diagnostic, DiagramKind, Lang, type PdfOptions, type PptxOptions, type RasterOptions, type RenderToSvgOptions, type RenderToSvgResult, type SvgNode, VERSION, type ValidateResult, type ViewBox, computeContentBox, installDomShim, loadAuthoringGuide, renderToJpeg, renderToPdf, renderToPng, renderToPptx, renderToSvg, serializeSvg, svgDisplayDimensions, toSvgString, validate };
|
package/dist/index.js
CHANGED
|
@@ -189,6 +189,8 @@ var THICK_ARROW_TERMINAL_CLEARANCE = 5.4;
|
|
|
189
189
|
var VERTICAL_PORT_RATIO = 0.25;
|
|
190
190
|
var PORT_STUB = 6;
|
|
191
191
|
var MAX_ROUTE_LANES = 18;
|
|
192
|
+
var LOOP_LANE_GAP = 10;
|
|
193
|
+
var LOOP_LANE_STEP = 7;
|
|
192
194
|
var EPS = 1e-3;
|
|
193
195
|
function layout(doc) {
|
|
194
196
|
switch (doc.kind) {
|
|
@@ -368,7 +370,6 @@ function layoutFlow(doc) {
|
|
|
368
370
|
const positions = /* @__PURE__ */ new Map();
|
|
369
371
|
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
370
372
|
let y = MARGIN;
|
|
371
|
-
let maxX = 0;
|
|
372
373
|
for (const r of sortedRanks) {
|
|
373
374
|
const lane = byRank.get(r);
|
|
374
375
|
const widths = lane.map((id) => shapeOf(id) === "diamond" ? NODE_W * 1.2 : NODE_W);
|
|
@@ -380,7 +381,6 @@ function layoutFlow(doc) {
|
|
|
380
381
|
positions.set(lane[i], { x, y, w: widths[i], h: NODE_H });
|
|
381
382
|
x += widths[i] + H_GAP;
|
|
382
383
|
}
|
|
383
|
-
if (x > maxX) maxX = x;
|
|
384
384
|
y += NODE_H + V_GAP;
|
|
385
385
|
}
|
|
386
386
|
for (const id of ids) {
|
|
@@ -400,7 +400,8 @@ function layoutFlow(doc) {
|
|
|
400
400
|
});
|
|
401
401
|
}
|
|
402
402
|
const edges = makeEdges(doc.edges, positions);
|
|
403
|
-
|
|
403
|
+
const { width, height } = flowExtent(placed, edges);
|
|
404
|
+
return { nodes: placed, edges, width, height, kind: "flow" };
|
|
404
405
|
}
|
|
405
406
|
function layoutState(doc) {
|
|
406
407
|
const { byRank } = computeRanks(doc);
|
|
@@ -412,7 +413,6 @@ function layoutState(doc) {
|
|
|
412
413
|
const positions = /* @__PURE__ */ new Map();
|
|
413
414
|
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
414
415
|
let y = MARGIN;
|
|
415
|
-
let maxX = 0;
|
|
416
416
|
for (const r of sortedRanks) {
|
|
417
417
|
const lane = byRank.get(r);
|
|
418
418
|
const widths = lane.map((id) => shapeOf(id) === "circle" ? 6 : NODE_W);
|
|
@@ -424,7 +424,6 @@ function layoutState(doc) {
|
|
|
424
424
|
positions.set(lane[i], { x, y: y + (NODE_H - heights[i]) / 2, w: widths[i], h: heights[i] });
|
|
425
425
|
x += widths[i] + H_GAP;
|
|
426
426
|
}
|
|
427
|
-
if (x > maxX) maxX = x;
|
|
428
427
|
y += NODE_H + V_GAP;
|
|
429
428
|
}
|
|
430
429
|
for (const id of doc.nodes.keys()) {
|
|
@@ -444,7 +443,8 @@ function layoutState(doc) {
|
|
|
444
443
|
});
|
|
445
444
|
}
|
|
446
445
|
const edges = makeEdges(doc.edges, positions);
|
|
447
|
-
|
|
446
|
+
const { width, height } = flowExtent(placed, edges);
|
|
447
|
+
return { nodes: placed, edges, width, height, kind: "state" };
|
|
448
448
|
}
|
|
449
449
|
function layoutSeq(doc) {
|
|
450
450
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -563,21 +563,50 @@ function computeRanks(doc) {
|
|
|
563
563
|
return { byRank };
|
|
564
564
|
}
|
|
565
565
|
function makeEdges(srcEdges, positions) {
|
|
566
|
-
|
|
566
|
+
const boxes = [...positions.values()];
|
|
567
|
+
const rightLimit = boxes.length ? Math.max(...boxes.map((b) => b.x + b.w)) : MARGIN;
|
|
568
|
+
const feedback = srcEdges.map((e, index) => ({ index, a: positions.get(e.from), b: positions.get(e.to) })).filter((r) => !!r.a && !!r.b && r.b.y + r.b.h <= r.a.y + EPS).sort((p, q) => feedbackSpan(p.a, p.b) - feedbackSpan(q.a, q.b));
|
|
569
|
+
const laneOf = /* @__PURE__ */ new Map();
|
|
570
|
+
feedback.forEach((r, nest) => laneOf.set(r.index, nest));
|
|
571
|
+
return srcEdges.map((e, index) => {
|
|
567
572
|
const a = positions.get(e.from);
|
|
568
573
|
const b = positions.get(e.to);
|
|
569
574
|
if (!a || !b) {
|
|
570
575
|
return { from: e.from, to: e.to, points: [], label: e.label, op: e.op };
|
|
571
576
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
points: orthogonalRoute(a, b),
|
|
576
|
-
label: e.label,
|
|
577
|
-
op: e.op
|
|
578
|
-
};
|
|
577
|
+
const nest = laneOf.get(index);
|
|
578
|
+
const points = nest === void 0 ? orthogonalRoute(a, b) : feedbackRoute(a, b, rightLimit + LOOP_LANE_GAP + nest * LOOP_LANE_STEP);
|
|
579
|
+
return { from: e.from, to: e.to, points, label: e.label, op: e.op };
|
|
579
580
|
});
|
|
580
581
|
}
|
|
582
|
+
function feedbackSpan(a, b) {
|
|
583
|
+
return a.y + a.h / 2 - (b.y + b.h / 2);
|
|
584
|
+
}
|
|
585
|
+
function feedbackRoute(a, b, laneX) {
|
|
586
|
+
const ay = a.y + a.h / 2;
|
|
587
|
+
const by = b.y + b.h / 2;
|
|
588
|
+
return [
|
|
589
|
+
[a.x + a.w, ay],
|
|
590
|
+
[laneX, ay],
|
|
591
|
+
[laneX, by],
|
|
592
|
+
[b.x + b.w, by]
|
|
593
|
+
];
|
|
594
|
+
}
|
|
595
|
+
function flowExtent(placed, edges) {
|
|
596
|
+
let maxX = MARGIN;
|
|
597
|
+
let maxY = MARGIN;
|
|
598
|
+
for (const n of placed) {
|
|
599
|
+
if (n.x + n.w > maxX) maxX = n.x + n.w;
|
|
600
|
+
if (n.y + n.h > maxY) maxY = n.y + n.h;
|
|
601
|
+
}
|
|
602
|
+
for (const e of edges) {
|
|
603
|
+
for (const [x, y] of e.points) {
|
|
604
|
+
if (x > maxX) maxX = x;
|
|
605
|
+
if (y > maxY) maxY = y;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return { width: maxX + MARGIN, height: maxY + MARGIN };
|
|
609
|
+
}
|
|
581
610
|
function hasLinearChildFlow(children, edges, childMap) {
|
|
582
611
|
const childSet = new Set(children);
|
|
583
612
|
const pairs = /* @__PURE__ */ new Set();
|
|
@@ -3437,7 +3466,7 @@ async function renderToPptx(source, opts = {}) {
|
|
|
3437
3466
|
}
|
|
3438
3467
|
|
|
3439
3468
|
// src/node/index.ts
|
|
3440
|
-
var VERSION = "0.1.
|
|
3469
|
+
var VERSION = "0.1.1";
|
|
3441
3470
|
function toSvgString(source, lang = "ja") {
|
|
3442
3471
|
return renderToSvg(source, { lang }).svg;
|
|
3443
3472
|
}
|
package/dist/pdgkit-mcp.cjs
CHANGED
|
@@ -320,6 +320,8 @@ var THICK_ARROW_TERMINAL_CLEARANCE = 5.4;
|
|
|
320
320
|
var VERTICAL_PORT_RATIO = 0.25;
|
|
321
321
|
var PORT_STUB = 6;
|
|
322
322
|
var MAX_ROUTE_LANES = 18;
|
|
323
|
+
var LOOP_LANE_GAP = 10;
|
|
324
|
+
var LOOP_LANE_STEP = 7;
|
|
323
325
|
var EPS = 1e-3;
|
|
324
326
|
function layout(doc) {
|
|
325
327
|
switch (doc.kind) {
|
|
@@ -499,7 +501,6 @@ function layoutFlow(doc) {
|
|
|
499
501
|
const positions = /* @__PURE__ */ new Map();
|
|
500
502
|
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
501
503
|
let y = MARGIN;
|
|
502
|
-
let maxX = 0;
|
|
503
504
|
for (const r of sortedRanks) {
|
|
504
505
|
const lane = byRank.get(r);
|
|
505
506
|
const widths = lane.map((id) => shapeOf(id) === "diamond" ? NODE_W * 1.2 : NODE_W);
|
|
@@ -511,7 +512,6 @@ function layoutFlow(doc) {
|
|
|
511
512
|
positions.set(lane[i], { x, y, w: widths[i], h: NODE_H });
|
|
512
513
|
x += widths[i] + H_GAP;
|
|
513
514
|
}
|
|
514
|
-
if (x > maxX) maxX = x;
|
|
515
515
|
y += NODE_H + V_GAP;
|
|
516
516
|
}
|
|
517
517
|
for (const id of ids) {
|
|
@@ -531,7 +531,8 @@ function layoutFlow(doc) {
|
|
|
531
531
|
});
|
|
532
532
|
}
|
|
533
533
|
const edges = makeEdges(doc.edges, positions);
|
|
534
|
-
|
|
534
|
+
const { width, height } = flowExtent(placed, edges);
|
|
535
|
+
return { nodes: placed, edges, width, height, kind: "flow" };
|
|
535
536
|
}
|
|
536
537
|
function layoutState(doc) {
|
|
537
538
|
const { byRank } = computeRanks(doc);
|
|
@@ -543,7 +544,6 @@ function layoutState(doc) {
|
|
|
543
544
|
const positions = /* @__PURE__ */ new Map();
|
|
544
545
|
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
545
546
|
let y = MARGIN;
|
|
546
|
-
let maxX = 0;
|
|
547
547
|
for (const r of sortedRanks) {
|
|
548
548
|
const lane = byRank.get(r);
|
|
549
549
|
const widths = lane.map((id) => shapeOf(id) === "circle" ? 6 : NODE_W);
|
|
@@ -555,7 +555,6 @@ function layoutState(doc) {
|
|
|
555
555
|
positions.set(lane[i], { x, y: y + (NODE_H - heights[i]) / 2, w: widths[i], h: heights[i] });
|
|
556
556
|
x += widths[i] + H_GAP;
|
|
557
557
|
}
|
|
558
|
-
if (x > maxX) maxX = x;
|
|
559
558
|
y += NODE_H + V_GAP;
|
|
560
559
|
}
|
|
561
560
|
for (const id of doc.nodes.keys()) {
|
|
@@ -575,7 +574,8 @@ function layoutState(doc) {
|
|
|
575
574
|
});
|
|
576
575
|
}
|
|
577
576
|
const edges = makeEdges(doc.edges, positions);
|
|
578
|
-
|
|
577
|
+
const { width, height } = flowExtent(placed, edges);
|
|
578
|
+
return { nodes: placed, edges, width, height, kind: "state" };
|
|
579
579
|
}
|
|
580
580
|
function layoutSeq(doc) {
|
|
581
581
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -694,21 +694,50 @@ function computeRanks(doc) {
|
|
|
694
694
|
return { byRank };
|
|
695
695
|
}
|
|
696
696
|
function makeEdges(srcEdges, positions) {
|
|
697
|
-
|
|
697
|
+
const boxes = [...positions.values()];
|
|
698
|
+
const rightLimit = boxes.length ? Math.max(...boxes.map((b) => b.x + b.w)) : MARGIN;
|
|
699
|
+
const feedback = srcEdges.map((e, index) => ({ index, a: positions.get(e.from), b: positions.get(e.to) })).filter((r) => !!r.a && !!r.b && r.b.y + r.b.h <= r.a.y + EPS).sort((p, q) => feedbackSpan(p.a, p.b) - feedbackSpan(q.a, q.b));
|
|
700
|
+
const laneOf = /* @__PURE__ */ new Map();
|
|
701
|
+
feedback.forEach((r, nest) => laneOf.set(r.index, nest));
|
|
702
|
+
return srcEdges.map((e, index) => {
|
|
698
703
|
const a = positions.get(e.from);
|
|
699
704
|
const b = positions.get(e.to);
|
|
700
705
|
if (!a || !b) {
|
|
701
706
|
return { from: e.from, to: e.to, points: [], label: e.label, op: e.op };
|
|
702
707
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
points: orthogonalRoute(a, b),
|
|
707
|
-
label: e.label,
|
|
708
|
-
op: e.op
|
|
709
|
-
};
|
|
708
|
+
const nest = laneOf.get(index);
|
|
709
|
+
const points = nest === void 0 ? orthogonalRoute(a, b) : feedbackRoute(a, b, rightLimit + LOOP_LANE_GAP + nest * LOOP_LANE_STEP);
|
|
710
|
+
return { from: e.from, to: e.to, points, label: e.label, op: e.op };
|
|
710
711
|
});
|
|
711
712
|
}
|
|
713
|
+
function feedbackSpan(a, b) {
|
|
714
|
+
return a.y + a.h / 2 - (b.y + b.h / 2);
|
|
715
|
+
}
|
|
716
|
+
function feedbackRoute(a, b, laneX) {
|
|
717
|
+
const ay = a.y + a.h / 2;
|
|
718
|
+
const by = b.y + b.h / 2;
|
|
719
|
+
return [
|
|
720
|
+
[a.x + a.w, ay],
|
|
721
|
+
[laneX, ay],
|
|
722
|
+
[laneX, by],
|
|
723
|
+
[b.x + b.w, by]
|
|
724
|
+
];
|
|
725
|
+
}
|
|
726
|
+
function flowExtent(placed, edges) {
|
|
727
|
+
let maxX = MARGIN;
|
|
728
|
+
let maxY = MARGIN;
|
|
729
|
+
for (const n of placed) {
|
|
730
|
+
if (n.x + n.w > maxX) maxX = n.x + n.w;
|
|
731
|
+
if (n.y + n.h > maxY) maxY = n.y + n.h;
|
|
732
|
+
}
|
|
733
|
+
for (const e of edges) {
|
|
734
|
+
for (const [x, y] of e.points) {
|
|
735
|
+
if (x > maxX) maxX = x;
|
|
736
|
+
if (y > maxY) maxY = y;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return { width: maxX + MARGIN, height: maxY + MARGIN };
|
|
740
|
+
}
|
|
712
741
|
function hasLinearChildFlow(children, edges, childMap) {
|
|
713
742
|
const childSet = new Set(children);
|
|
714
743
|
const pairs = /* @__PURE__ */ new Set();
|
|
@@ -3131,7 +3160,7 @@ var langSchema = import_zod.z.enum(["ja", "en", "both"]).default("ja");
|
|
|
3131
3160
|
function textResult(text) {
|
|
3132
3161
|
return { content: [{ type: "text", text }] };
|
|
3133
3162
|
}
|
|
3134
|
-
function buildServer(version = "0.1.
|
|
3163
|
+
function buildServer(version = "0.1.1") {
|
|
3135
3164
|
const server = new import_mcp.McpServer({ name: "pdgkit", version });
|
|
3136
3165
|
server.registerTool(
|
|
3137
3166
|
"pdg_validate",
|
|
@@ -3205,7 +3234,7 @@ function buildServer(version = "0.1.0") {
|
|
|
3205
3234
|
|
|
3206
3235
|
// src/node/index.ts
|
|
3207
3236
|
var import_node_fs3 = require("fs");
|
|
3208
|
-
var VERSION = "0.1.
|
|
3237
|
+
var VERSION = "0.1.1";
|
|
3209
3238
|
|
|
3210
3239
|
// bin/pdgkit-mcp.ts
|
|
3211
3240
|
async function main() {
|
package/dist/pdgkit-mcp.js
CHANGED
|
@@ -297,6 +297,8 @@ var THICK_ARROW_TERMINAL_CLEARANCE = 5.4;
|
|
|
297
297
|
var VERTICAL_PORT_RATIO = 0.25;
|
|
298
298
|
var PORT_STUB = 6;
|
|
299
299
|
var MAX_ROUTE_LANES = 18;
|
|
300
|
+
var LOOP_LANE_GAP = 10;
|
|
301
|
+
var LOOP_LANE_STEP = 7;
|
|
300
302
|
var EPS = 1e-3;
|
|
301
303
|
function layout(doc) {
|
|
302
304
|
switch (doc.kind) {
|
|
@@ -476,7 +478,6 @@ function layoutFlow(doc) {
|
|
|
476
478
|
const positions = /* @__PURE__ */ new Map();
|
|
477
479
|
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
478
480
|
let y = MARGIN;
|
|
479
|
-
let maxX = 0;
|
|
480
481
|
for (const r of sortedRanks) {
|
|
481
482
|
const lane = byRank.get(r);
|
|
482
483
|
const widths = lane.map((id) => shapeOf(id) === "diamond" ? NODE_W * 1.2 : NODE_W);
|
|
@@ -488,7 +489,6 @@ function layoutFlow(doc) {
|
|
|
488
489
|
positions.set(lane[i], { x, y, w: widths[i], h: NODE_H });
|
|
489
490
|
x += widths[i] + H_GAP;
|
|
490
491
|
}
|
|
491
|
-
if (x > maxX) maxX = x;
|
|
492
492
|
y += NODE_H + V_GAP;
|
|
493
493
|
}
|
|
494
494
|
for (const id of ids) {
|
|
@@ -508,7 +508,8 @@ function layoutFlow(doc) {
|
|
|
508
508
|
});
|
|
509
509
|
}
|
|
510
510
|
const edges = makeEdges(doc.edges, positions);
|
|
511
|
-
|
|
511
|
+
const { width, height } = flowExtent(placed, edges);
|
|
512
|
+
return { nodes: placed, edges, width, height, kind: "flow" };
|
|
512
513
|
}
|
|
513
514
|
function layoutState(doc) {
|
|
514
515
|
const { byRank } = computeRanks(doc);
|
|
@@ -520,7 +521,6 @@ function layoutState(doc) {
|
|
|
520
521
|
const positions = /* @__PURE__ */ new Map();
|
|
521
522
|
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
522
523
|
let y = MARGIN;
|
|
523
|
-
let maxX = 0;
|
|
524
524
|
for (const r of sortedRanks) {
|
|
525
525
|
const lane = byRank.get(r);
|
|
526
526
|
const widths = lane.map((id) => shapeOf(id) === "circle" ? 6 : NODE_W);
|
|
@@ -532,7 +532,6 @@ function layoutState(doc) {
|
|
|
532
532
|
positions.set(lane[i], { x, y: y + (NODE_H - heights[i]) / 2, w: widths[i], h: heights[i] });
|
|
533
533
|
x += widths[i] + H_GAP;
|
|
534
534
|
}
|
|
535
|
-
if (x > maxX) maxX = x;
|
|
536
535
|
y += NODE_H + V_GAP;
|
|
537
536
|
}
|
|
538
537
|
for (const id of doc.nodes.keys()) {
|
|
@@ -552,7 +551,8 @@ function layoutState(doc) {
|
|
|
552
551
|
});
|
|
553
552
|
}
|
|
554
553
|
const edges = makeEdges(doc.edges, positions);
|
|
555
|
-
|
|
554
|
+
const { width, height } = flowExtent(placed, edges);
|
|
555
|
+
return { nodes: placed, edges, width, height, kind: "state" };
|
|
556
556
|
}
|
|
557
557
|
function layoutSeq(doc) {
|
|
558
558
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -671,21 +671,50 @@ function computeRanks(doc) {
|
|
|
671
671
|
return { byRank };
|
|
672
672
|
}
|
|
673
673
|
function makeEdges(srcEdges, positions) {
|
|
674
|
-
|
|
674
|
+
const boxes = [...positions.values()];
|
|
675
|
+
const rightLimit = boxes.length ? Math.max(...boxes.map((b) => b.x + b.w)) : MARGIN;
|
|
676
|
+
const feedback = srcEdges.map((e, index) => ({ index, a: positions.get(e.from), b: positions.get(e.to) })).filter((r) => !!r.a && !!r.b && r.b.y + r.b.h <= r.a.y + EPS).sort((p, q) => feedbackSpan(p.a, p.b) - feedbackSpan(q.a, q.b));
|
|
677
|
+
const laneOf = /* @__PURE__ */ new Map();
|
|
678
|
+
feedback.forEach((r, nest) => laneOf.set(r.index, nest));
|
|
679
|
+
return srcEdges.map((e, index) => {
|
|
675
680
|
const a = positions.get(e.from);
|
|
676
681
|
const b = positions.get(e.to);
|
|
677
682
|
if (!a || !b) {
|
|
678
683
|
return { from: e.from, to: e.to, points: [], label: e.label, op: e.op };
|
|
679
684
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
points: orthogonalRoute(a, b),
|
|
684
|
-
label: e.label,
|
|
685
|
-
op: e.op
|
|
686
|
-
};
|
|
685
|
+
const nest = laneOf.get(index);
|
|
686
|
+
const points = nest === void 0 ? orthogonalRoute(a, b) : feedbackRoute(a, b, rightLimit + LOOP_LANE_GAP + nest * LOOP_LANE_STEP);
|
|
687
|
+
return { from: e.from, to: e.to, points, label: e.label, op: e.op };
|
|
687
688
|
});
|
|
688
689
|
}
|
|
690
|
+
function feedbackSpan(a, b) {
|
|
691
|
+
return a.y + a.h / 2 - (b.y + b.h / 2);
|
|
692
|
+
}
|
|
693
|
+
function feedbackRoute(a, b, laneX) {
|
|
694
|
+
const ay = a.y + a.h / 2;
|
|
695
|
+
const by = b.y + b.h / 2;
|
|
696
|
+
return [
|
|
697
|
+
[a.x + a.w, ay],
|
|
698
|
+
[laneX, ay],
|
|
699
|
+
[laneX, by],
|
|
700
|
+
[b.x + b.w, by]
|
|
701
|
+
];
|
|
702
|
+
}
|
|
703
|
+
function flowExtent(placed, edges) {
|
|
704
|
+
let maxX = MARGIN;
|
|
705
|
+
let maxY = MARGIN;
|
|
706
|
+
for (const n of placed) {
|
|
707
|
+
if (n.x + n.w > maxX) maxX = n.x + n.w;
|
|
708
|
+
if (n.y + n.h > maxY) maxY = n.y + n.h;
|
|
709
|
+
}
|
|
710
|
+
for (const e of edges) {
|
|
711
|
+
for (const [x, y] of e.points) {
|
|
712
|
+
if (x > maxX) maxX = x;
|
|
713
|
+
if (y > maxY) maxY = y;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return { width: maxX + MARGIN, height: maxY + MARGIN };
|
|
717
|
+
}
|
|
689
718
|
function hasLinearChildFlow(children, edges, childMap) {
|
|
690
719
|
const childSet = new Set(children);
|
|
691
720
|
const pairs = /* @__PURE__ */ new Set();
|
|
@@ -3107,7 +3136,7 @@ var langSchema = z.enum(["ja", "en", "both"]).default("ja");
|
|
|
3107
3136
|
function textResult(text) {
|
|
3108
3137
|
return { content: [{ type: "text", text }] };
|
|
3109
3138
|
}
|
|
3110
|
-
function buildServer(version = "0.1.
|
|
3139
|
+
function buildServer(version = "0.1.1") {
|
|
3111
3140
|
const server = new McpServer({ name: "pdgkit", version });
|
|
3112
3141
|
server.registerTool(
|
|
3113
3142
|
"pdg_validate",
|
|
@@ -3181,7 +3210,7 @@ function buildServer(version = "0.1.0") {
|
|
|
3181
3210
|
|
|
3182
3211
|
// src/node/index.ts
|
|
3183
3212
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3184
|
-
var VERSION = "0.1.
|
|
3213
|
+
var VERSION = "0.1.1";
|
|
3185
3214
|
|
|
3186
3215
|
// bin/pdgkit-mcp.ts
|
|
3187
3216
|
async function main() {
|