@zukhruf/diagram 0.3.32 → 0.3.33

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 (49) hide show
  1. package/dist/lib/layouts/graph/attach.d.ts +25 -0
  2. package/dist/lib/layouts/graph/attach.d.ts.map +1 -0
  3. package/dist/lib/layouts/graph/attach.js +216 -0
  4. package/dist/lib/layouts/graph/attach.js.map +1 -0
  5. package/dist/lib/layouts/graph/connections.d.ts +38 -0
  6. package/dist/lib/layouts/graph/connections.d.ts.map +1 -0
  7. package/dist/lib/layouts/graph/connections.js +112 -0
  8. package/dist/lib/layouts/graph/connections.js.map +1 -0
  9. package/dist/lib/layouts/graph/graph-layout-result.d.ts +1 -1
  10. package/dist/lib/layouts/graph/graph-layout-result.d.ts.map +1 -1
  11. package/dist/lib/layouts/graph/graph-layout-result.js.map +1 -1
  12. package/dist/lib/layouts/graph/gutters.js +5 -5
  13. package/dist/lib/layouts/graph/gutters.js.map +1 -1
  14. package/dist/lib/layouts/graph/labels.d.ts +16 -0
  15. package/dist/lib/layouts/graph/labels.d.ts.map +1 -0
  16. package/dist/lib/layouts/graph/labels.js +76 -0
  17. package/dist/lib/layouts/graph/labels.js.map +1 -0
  18. package/dist/lib/layouts/graph/layout.d.ts.map +1 -1
  19. package/dist/lib/layouts/graph/layout.js +31 -9
  20. package/dist/lib/layouts/graph/layout.js.map +1 -1
  21. package/dist/lib/layouts/graph/measure.d.ts +7 -6
  22. package/dist/lib/layouts/graph/measure.d.ts.map +1 -1
  23. package/dist/lib/layouts/graph/measure.js +35 -29
  24. package/dist/lib/layouts/graph/measure.js.map +1 -1
  25. package/dist/lib/layouts/graph/min-heap.d.ts +12 -0
  26. package/dist/lib/layouts/graph/min-heap.d.ts.map +1 -0
  27. package/dist/lib/layouts/graph/min-heap.js +60 -0
  28. package/dist/lib/layouts/graph/min-heap.js.map +1 -0
  29. package/dist/lib/layouts/graph/node-content.d.ts +1 -2
  30. package/dist/lib/layouts/graph/node-content.d.ts.map +1 -1
  31. package/dist/lib/layouts/graph/node-content.js +43 -42
  32. package/dist/lib/layouts/graph/node-content.js.map +1 -1
  33. package/dist/lib/layouts/graph/path.d.ts +14 -0
  34. package/dist/lib/layouts/graph/path.d.ts.map +1 -0
  35. package/dist/lib/layouts/graph/path.js +55 -0
  36. package/dist/lib/layouts/graph/path.js.map +1 -0
  37. package/dist/lib/layouts/graph/route-search.d.ts +34 -0
  38. package/dist/lib/layouts/graph/route-search.d.ts.map +1 -0
  39. package/dist/lib/layouts/graph/route-search.js +208 -0
  40. package/dist/lib/layouts/graph/route-search.js.map +1 -0
  41. package/dist/lib/layouts/graph/route.d.ts +16 -0
  42. package/dist/lib/layouts/graph/route.d.ts.map +1 -0
  43. package/dist/lib/layouts/graph/route.js +241 -0
  44. package/dist/lib/layouts/graph/route.js.map +1 -0
  45. package/dist/lib/layouts/graph/sides.d.ts +23 -0
  46. package/dist/lib/layouts/graph/sides.d.ts.map +1 -0
  47. package/dist/lib/layouts/graph/sides.js +91 -0
  48. package/dist/lib/layouts/graph/sides.js.map +1 -0
  49. package/package.json +1 -1
@@ -0,0 +1,12 @@
1
+ /**
2
+ * A binary min-heap of items by priority.
3
+ *
4
+ * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
5
+ */
6
+ export declare class MinHeap<T> {
7
+ #private;
8
+ get size(): number;
9
+ push(item: T, priority: number): void;
10
+ pop(): T | undefined;
11
+ }
12
+ //# sourceMappingURL=min-heap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"min-heap.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/min-heap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,OAAO,CAAC,CAAC;;IAGpB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKrC,GAAG,IAAI,CAAC,GAAG,SAAS;CA2CrB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * A binary min-heap of items by priority.
3
+ *
4
+ * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
5
+ */
6
+ export class MinHeap {
7
+ #data = [];
8
+ get size() {
9
+ return this.#data.length;
10
+ }
11
+ push(item, priority) {
12
+ this.#data.push({
13
+ item,
14
+ priority
15
+ });
16
+ this.#bubbleUp(this.#data.length - 1);
17
+ }
18
+ pop() {
19
+ const top = this.#data[0];
20
+ if (!top) return undefined;
21
+ const last = this.#data.pop();
22
+ if (this.#data.length > 0) {
23
+ this.#data[0] = last;
24
+ this.#bubbleDown(0);
25
+ }
26
+ return top.item;
27
+ }
28
+ #bubbleUp(index) {
29
+ let i = index;
30
+ while (i > 0) {
31
+ const parent = i - 1 >> 1;
32
+ const entry = this.#data[i];
33
+ const parentEntry = this.#data[parent];
34
+ if (entry.priority >= parentEntry.priority) break;
35
+ this.#data[i] = parentEntry;
36
+ this.#data[parent] = entry;
37
+ i = parent;
38
+ }
39
+ }
40
+ #bubbleDown(index) {
41
+ let i = index;
42
+ for (;;) {
43
+ const entry = this.#data[i];
44
+ let smallest = i;
45
+ let smallestEntry = entry;
46
+ for (const child of [2 * i + 1, 2 * i + 2]) {
47
+ const candidate = this.#data[child];
48
+ if (candidate && candidate.priority < smallestEntry.priority) {
49
+ smallest = child;
50
+ smallestEntry = candidate;
51
+ }
52
+ }
53
+ if (smallest === i) break;
54
+ this.#data[i] = smallestEntry;
55
+ this.#data[smallest] = entry;
56
+ i = smallest;
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=min-heap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"min-heap.js","names":["MinHeap","data","size","length","push","item","priority","bubbleUp","pop","top","undefined","last","bubbleDown","#bubbleUp","index","i","parent","entry","parentEntry","#bubbleDown","smallest","smallestEntry","child","candidate"],"sources":["../../../../src/lib/layouts/graph/min-heap.ts"],"sourcesContent":["/**\n * A binary min-heap of items by priority.\n *\n * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.\n */\nexport class MinHeap<T> {\n readonly #data: { item: T; priority: number }[] = [];\n\n get size(): number {\n return this.#data.length;\n }\n\n push(item: T, priority: number): void {\n this.#data.push({ item, priority });\n this.#bubbleUp(this.#data.length - 1);\n }\n\n pop(): T | undefined {\n const top = this.#data[0];\n if (!top) return undefined;\n const last = this.#data.pop()!;\n if (this.#data.length > 0) {\n this.#data[0] = last;\n this.#bubbleDown(0);\n }\n return top.item;\n }\n\n #bubbleUp(index: number): void {\n let i = index;\n while (i > 0) {\n const parent = (i - 1) >> 1;\n const entry = this.#data[i]!;\n const parentEntry = this.#data[parent]!;\n if (entry.priority >= parentEntry.priority) break;\n this.#data[i] = parentEntry;\n this.#data[parent] = entry;\n i = parent;\n }\n }\n\n #bubbleDown(index: number): void {\n let i = index;\n for (;;) {\n const entry = this.#data[i]!;\n let smallest = i;\n let smallestEntry = entry;\n for (const child of [2 * i + 1, 2 * i + 2]) {\n const candidate = this.#data[child];\n if (candidate && candidate.priority < smallestEntry.priority) {\n smallest = child;\n smallestEntry = candidate;\n }\n }\n if (smallest === i) break;\n this.#data[i] = smallestEntry;\n this.#data[smallest] = entry;\n i = smallest;\n }\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,OAAO,CAAI;EACb,CAACC,IAAI,GAAoC,EAAE;EAEpD,IAAIC,IAAIA,CAAA,EAAW;IACjB,OAAO,IAAI,CAAC,CAACD,IAAI,CAACE,MAAM;EAC1B;EAEAC,IAAIA,CAACC,IAAO,EAAEC,QAAgB,EAAQ;IACpC,IAAI,CAAC,CAACL,IAAI,CAACG,IAAI,CAAC;MAAEC,IAAI;MAAEC;IAAS,CAAC,CAAC;IACnC,IAAI,CAAC,CAACC,QAAQ,CAAC,IAAI,CAAC,CAACN,IAAI,CAACE,MAAM,GAAG,CAAC,CAAC;EACvC;EAEAK,GAAGA,CAAA,EAAkB;IACnB,MAAMC,GAAG,GAAG,IAAI,CAAC,CAACR,IAAI,CAAC,CAAC,CAAC;IACzB,IAAI,CAACQ,GAAG,EAAE,OAAOC,SAAS;IAC1B,MAAMC,IAAI,GAAG,IAAI,CAAC,CAACV,IAAI,CAACO,GAAG,CAAC,CAAE;IAC9B,IAAI,IAAI,CAAC,CAACP,IAAI,CAACE,MAAM,GAAG,CAAC,EAAE;MACzB,IAAI,CAAC,CAACF,IAAI,CAAC,CAAC,CAAC,GAAGU,IAAI;MACpB,IAAI,CAAC,CAACC,UAAU,CAAC,CAAC,CAAC;IACrB;IACA,OAAOH,GAAG,CAACJ,IAAI;EACjB;EAEA,CAACE,QAAQM,CAACC,KAAa,EAAQ;IAC7B,IAAIC,CAAC,GAAGD,KAAK;IACb,OAAOC,CAAC,GAAG,CAAC,EAAE;MACZ,MAAMC,MAAM,GAAID,CAAC,GAAG,CAAC,IAAK,CAAC;MAC3B,MAAME,KAAK,GAAG,IAAI,CAAC,CAAChB,IAAI,CAACc,CAAC,CAAE;MAC5B,MAAMG,WAAW,GAAG,IAAI,CAAC,CAACjB,IAAI,CAACe,MAAM,CAAE;MACvC,IAAIC,KAAK,CAACX,QAAQ,IAAIY,WAAW,CAACZ,QAAQ,EAAE;MAC5C,IAAI,CAAC,CAACL,IAAI,CAACc,CAAC,CAAC,GAAGG,WAAW;MAC3B,IAAI,CAAC,CAACjB,IAAI,CAACe,MAAM,CAAC,GAAGC,KAAK;MAC1BF,CAAC,GAAGC,MAAM;IACZ;EACF;EAEA,CAACJ,UAAUO,CAACL,KAAa,EAAQ;IAC/B,IAAIC,CAAC,GAAGD,KAAK;IACb,SAAS;MACP,MAAMG,KAAK,GAAG,IAAI,CAAC,CAAChB,IAAI,CAACc,CAAC,CAAE;MAC5B,IAAIK,QAAQ,GAAGL,CAAC;MAChB,IAAIM,aAAa,GAAGJ,KAAK;MACzB,KAAK,MAAMK,KAAK,IAAI,CAAC,CAAC,GAAGP,CAAC,GAAG,CAAC,EAAE,CAAC,GAAGA,CAAC,GAAG,CAAC,CAAC,EAAE;QAC1C,MAAMQ,SAAS,GAAG,IAAI,CAAC,CAACtB,IAAI,CAACqB,KAAK,CAAC;QACnC,IAAIC,SAAS,IAAIA,SAAS,CAACjB,QAAQ,GAAGe,aAAa,CAACf,QAAQ,EAAE;UAC5Dc,QAAQ,GAAGE,KAAK;UAChBD,aAAa,GAAGE,SAAS;QAC3B;MACF;MACA,IAAIH,QAAQ,KAAKL,CAAC,EAAE;MACpB,IAAI,CAAC,CAACd,IAAI,CAACc,CAAC,CAAC,GAAGM,aAAa;MAC7B,IAAI,CAAC,CAACpB,IAAI,CAACmB,QAAQ,CAAC,GAAGH,KAAK;MAC5BF,CAAC,GAAGK,QAAQ;IACd;EACF;AACF","ignoreList":[]}
@@ -1,6 +1,5 @@
1
1
  import type { Box, DiagramNode } from '../../model/diagram-ir.ts';
2
2
  import type { NodeLayout } from './graph-layout-result.ts';
3
- import type { LayoutTokens } from './tokens.ts';
4
3
  export type ContentPlacement = Pick<NodeLayout, 'text' | 'bullets'> & Partial<Pick<NodeLayout, 'icon' | 'appliance'>>;
5
4
  /** What a node draws inside its frame: its measured size, the padding around it, and where it goes. */
6
5
  export type NodeContent = {
@@ -12,5 +11,5 @@ export type NodeContent = {
12
11
  };
13
12
  place(inner: Box): ContentPlacement;
14
13
  };
15
- export declare function nodeContent(node: DiagramNode, tokens: LayoutTokens): NodeContent;
14
+ export declare function nodeContent(node: DiagramNode): NodeContent;
16
15
  //# sourceMappingURL=node-content.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-content.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/node-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAS,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC;AAElD,uGAAuG;AACvG,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAC;CACrC,CAAC;AAQF,wBAAgB,WAAW,CACzB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,YAAY,GACnB,WAAW,CAeb"}
1
+ {"version":3,"file":"node-content.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/node-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAS,MAAM,2BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI3D,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,GACjE,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC;AAElD,uGAAuG;AACvG,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAC;CACrC,CAAC;AAQF,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAe1D"}
@@ -1,4 +1,5 @@
1
1
  import { labelLines, textBlock } from "./text-block.js";
2
+ import { LAYOUT_TOKENS } from "./tokens.js";
2
3
 
3
4
  /** What a node draws inside its frame: its measured size, the padding around it, and where it goes. */
4
5
 
@@ -12,40 +13,40 @@ const NOTHING = {
12
13
  text: [],
13
14
  bullets: []
14
15
  };
15
- export function nodeContent(node, tokens) {
16
+ export function nodeContent(node) {
16
17
  switch (node.kind) {
17
18
  case 'junction':
18
- return junctionContent(tokens);
19
+ return junctionContent();
19
20
  case 'marker':
20
- return markerContent(node, tokens);
21
+ return markerContent(node);
21
22
  case 'connector':
22
- return connectorContent(node, tokens);
23
+ return connectorContent(node);
23
24
  case 'appliance':
24
- return applianceContent(node, tokens);
25
+ return applianceContent(node);
25
26
  case 'note':
26
- return noteContent(node, tokens);
27
+ return noteContent(node);
27
28
  default:
28
- return boxContent(node, tokens);
29
+ return boxContent(node);
29
30
  }
30
31
  }
31
- function boxContent(node, tokens) {
32
+ function boxContent(node) {
32
33
  const pad = node.frame === 'none' ? {
33
- x: tokens.LABEL_GAP,
34
- y: tokens.LABEL_GAP
34
+ x: LAYOUT_TOKENS.LABEL_GAP,
35
+ y: LAYOUT_TOKENS.LABEL_GAP
35
36
  } : {
36
- x: tokens.PAD_X,
37
- y: tokens.PAD_Y
37
+ x: LAYOUT_TOKENS.PAD_X,
38
+ y: LAYOUT_TOKENS.PAD_Y
38
39
  };
39
40
  const isCard = node.textAlign === 'start' || node.description !== undefined || (node.items?.length ?? 0) > 0;
40
41
  if (isCard) return {
41
- ...cardContent(node, tokens),
42
+ ...cardContent(node),
42
43
  pad
43
44
  };
44
45
  if (node.icon) return {
45
- ...iconRowContent(node, tokens),
46
+ ...iconRowContent(node),
46
47
  pad
47
48
  };
48
- const lines = textBlock(labelLines(node.label, node.textColor), tokens.fonts.boxLabel);
49
+ const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.boxLabel);
49
50
  return {
50
51
  w: lines.w,
51
52
  h: lines.h,
@@ -56,12 +57,12 @@ function boxContent(node, tokens) {
56
57
  })
57
58
  };
58
59
  }
59
- function iconRowContent(node, tokens) {
60
+ function iconRowContent(node) {
60
61
  const {
61
62
  ICON,
62
63
  ICON_GAP
63
- } = tokens;
64
- const title = textBlock(labelLines(node.label, node.textColor), tokens.fonts.title);
64
+ } = LAYOUT_TOKENS;
65
+ const title = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.title);
65
66
  const w = title.w > 0 ? ICON + ICON_GAP + title.w : ICON;
66
67
  const h = Math.max(ICON, title.h);
67
68
  return {
@@ -82,16 +83,16 @@ function iconRowContent(node, tokens) {
82
83
  }
83
84
  };
84
85
  }
85
- function cardContent(node, tokens) {
86
+ function cardContent(node) {
86
87
  const {
87
88
  ICON,
88
89
  ICON_GAP,
89
90
  BLOCK_GAP,
90
91
  BULLET_INDENT
91
- } = tokens;
92
- const title = textBlock(labelLines(node.label, node.textColor), tokens.fonts.title);
93
- const description = textBlock(labelLines(node.description, node.textColor), tokens.fonts.body);
94
- const items = textBlock(labelLines(node.items, node.textColor), tokens.fonts.body);
92
+ } = LAYOUT_TOKENS;
93
+ const title = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.title);
94
+ const description = textBlock(labelLines(node.description, node.textColor), LAYOUT_TOKENS.fonts.body);
95
+ const items = textBlock(labelLines(node.items, node.textColor), LAYOUT_TOKENS.fonts.body);
95
96
  let iconW = 0;
96
97
  if (node.icon) iconW = title.w > 0 ? ICON + ICON_GAP : ICON;
97
98
  const headerH = Math.max(node.icon ? ICON : 0, title.h);
@@ -105,7 +106,7 @@ function cardContent(node, tokens) {
105
106
  h: items.h,
106
107
  w: items.h > 0 ? BULLET_INDENT + items.w : 0
107
108
  }].filter(section => section.h > 0);
108
- const bulletRise = tokens.fonts.body.size * 0.3;
109
+ const bulletRise = LAYOUT_TOKENS.fonts.body.size * 0.3;
109
110
  return {
110
111
  w: Math.max(0, ...sections.map(section => section.w)),
111
112
  h: sections.reduce((sum, section) => sum + section.h, 0) + Math.max(0, sections.length - 1) * BLOCK_GAP,
@@ -137,14 +138,14 @@ function cardContent(node, tokens) {
137
138
  }
138
139
  };
139
140
  }
140
- function noteContent(node, tokens) {
141
- const lines = textBlock(labelLines(node.label, node.textColor ?? 'dim'), tokens.fonts.note);
141
+ function noteContent(node) {
142
+ const lines = textBlock(labelLines(node.label, node.textColor ?? 'dim'), LAYOUT_TOKENS.fonts.note);
142
143
  return {
143
144
  w: lines.w,
144
145
  h: lines.h,
145
146
  pad: {
146
- x: tokens.LABEL_GAP,
147
- y: tokens.LABEL_GAP
147
+ x: LAYOUT_TOKENS.LABEL_GAP,
148
+ y: LAYOUT_TOKENS.LABEL_GAP
148
149
  },
149
150
  place: inner => ({
150
151
  text: lines.place(inner.x, inner.y, 'start'),
@@ -152,20 +153,20 @@ function noteContent(node, tokens) {
152
153
  })
153
154
  };
154
155
  }
155
- function markerContent(node, tokens) {
156
+ function markerContent(node) {
156
157
  const {
157
158
  MARKER_ICON,
158
159
  BLOCK_GAP
159
- } = tokens;
160
- const lines = textBlock(labelLines(node.label, node.textColor), tokens.fonts.marker);
160
+ } = LAYOUT_TOKENS;
161
+ const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.marker);
161
162
  const gap = lines.h > 0 ? BLOCK_GAP : 0;
162
163
  const h = MARKER_ICON + gap + lines.h;
163
164
  return {
164
165
  w: Math.max(MARKER_ICON, lines.w),
165
166
  h,
166
167
  pad: {
167
- x: tokens.LABEL_GAP,
168
- y: tokens.LABEL_GAP
168
+ x: LAYOUT_TOKENS.LABEL_GAP,
169
+ y: LAYOUT_TOKENS.LABEL_GAP
169
170
  },
170
171
  place: inner => {
171
172
  const top = centredTop(inner, h);
@@ -182,12 +183,12 @@ function markerContent(node, tokens) {
182
183
  }
183
184
  };
184
185
  }
185
- function connectorContent(node, tokens) {
186
+ function connectorContent(node) {
186
187
  const {
187
188
  CONNECTOR_ICON,
188
189
  BLOCK_GAP
189
- } = tokens;
190
- const lines = textBlock(labelLines(node.label, node.textColor), tokens.fonts.connector);
190
+ } = LAYOUT_TOKENS;
191
+ const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.connector);
191
192
  const iconW = node.icon ? CONNECTOR_ICON + (lines.w > 0 ? BLOCK_GAP : 0) : 0;
192
193
  const h = Math.max(CONNECTOR_MIN_HEIGHT - 2 * CONNECTOR_PAD.y, lines.h);
193
194
  return {
@@ -215,10 +216,10 @@ function connectorContent(node, tokens) {
215
216
  }
216
217
  };
217
218
  }
218
- function junctionContent(tokens) {
219
+ function junctionContent() {
219
220
  return {
220
- w: tokens.JUNCTION,
221
- h: tokens.JUNCTION,
221
+ w: LAYOUT_TOKENS.JUNCTION,
222
+ h: LAYOUT_TOKENS.JUNCTION,
222
223
  pad: {
223
224
  x: 0,
224
225
  y: 0
@@ -226,12 +227,12 @@ function junctionContent(tokens) {
226
227
  place: () => NOTHING
227
228
  };
228
229
  }
229
- function applianceContent(node, tokens) {
230
+ function applianceContent(node) {
230
231
  const {
231
232
  APPLIANCE,
232
233
  LABEL_GAP
233
- } = tokens;
234
- const lines = textBlock(labelLines(node.label, node.textColor), tokens.fonts.appliance);
234
+ } = LAYOUT_TOKENS;
235
+ const lines = textBlock(labelLines(node.label, node.textColor), LAYOUT_TOKENS.fonts.appliance);
235
236
  return {
236
237
  w: Math.max(APPLIANCE.w, lines.w + 2 * LABEL_GAP),
237
238
  h: APPLIANCE.h + (lines.h > 0 ? lines.h + LABEL_GAP : 0),
@@ -1 +1 @@
1
- {"version":3,"file":"node-content.js","names":["labelLines","textBlock","CONNECTOR_PAD","x","y","CONNECTOR_MIN_HEIGHT","NOTHING","text","bullets","nodeContent","node","tokens","kind","junctionContent","markerContent","connectorContent","applianceContent","noteContent","boxContent","pad","frame","LABEL_GAP","PAD_X","PAD_Y","isCard","textAlign","description","undefined","items","length","cardContent","icon","iconRowContent","lines","label","textColor","fonts","boxLabel","w","h","place","inner","centreX","centredTop","ICON","ICON_GAP","title","Math","max","size","BLOCK_GAP","BULLET_INDENT","body","iconW","headerH","sections","filter","section","bulletRise","map","reduce","sum","top","next","at","headerTop","descriptionTop","itemsTop","itemRuns","run","note","MARKER_ICON","marker","gap","cx","CONNECTOR_ICON","connector","cy","JUNCTION","APPLIANCE","appliance","glyph","applianceGlyph","bodyX","bodyY","min","bodyW","bodyH","slant","mastX","antenna","jackCount","floor","appliancePorts","jackSpacing","jacks","Array","from","_","index","face","parts","box"],"sources":["../../../../src/lib/layouts/graph/node-content.ts"],"sourcesContent":["import type { Box, DiagramNode, Point } from '../../model/diagram-ir.ts';\nimport type { NodeLayout } from './graph-layout-result.ts';\nimport { labelLines, textBlock } from './text-block.ts';\nimport type { LayoutTokens } from './tokens.ts';\n\nexport type ContentPlacement = Pick<NodeLayout, 'text' | 'bullets'> &\n Partial<Pick<NodeLayout, 'icon' | 'appliance'>>;\n\n/** What a node draws inside its frame: its measured size, the padding around it, and where it goes. */\nexport type NodeContent = {\n w: number;\n h: number;\n pad: { x: number; y: number };\n place(inner: Box): ContentPlacement;\n};\n\n/** Mirrors the connector pill drawn before content sizing: 10px side inset, 4px above and below the text, 28px tall at least. */\nconst CONNECTOR_PAD = { x: 10, y: 4 };\nconst CONNECTOR_MIN_HEIGHT = 28;\n\nconst NOTHING: ContentPlacement = { text: [], bullets: [] };\n\nexport function nodeContent(\n node: DiagramNode,\n tokens: LayoutTokens,\n): NodeContent {\n switch (node.kind) {\n case 'junction':\n return junctionContent(tokens);\n case 'marker':\n return markerContent(node, tokens);\n case 'connector':\n return connectorContent(node, tokens);\n case 'appliance':\n return applianceContent(node, tokens);\n case 'note':\n return noteContent(node, tokens);\n default:\n return boxContent(node, tokens);\n }\n}\n\nfunction boxContent(node: DiagramNode, tokens: LayoutTokens): NodeContent {\n const pad =\n node.frame === 'none'\n ? { x: tokens.LABEL_GAP, y: tokens.LABEL_GAP }\n : { x: tokens.PAD_X, y: tokens.PAD_Y };\n const isCard =\n node.textAlign === 'start' ||\n node.description !== undefined ||\n (node.items?.length ?? 0) > 0;\n if (isCard) return { ...cardContent(node, tokens), pad };\n if (node.icon) return { ...iconRowContent(node, tokens), pad };\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n tokens.fonts.boxLabel,\n );\n return {\n w: lines.w,\n h: lines.h,\n pad,\n place: (inner) => ({\n text: lines.place(centreX(inner), centredTop(inner, lines.h), 'middle'),\n bullets: [],\n }),\n };\n}\n\nfunction iconRowContent(\n node: DiagramNode,\n tokens: LayoutTokens,\n): Omit<NodeContent, 'pad'> {\n const { ICON, ICON_GAP } = tokens;\n const title = textBlock(\n labelLines(node.label, node.textColor),\n tokens.fonts.title,\n );\n const w = title.w > 0 ? ICON + ICON_GAP + title.w : ICON;\n const h = Math.max(ICON, title.h);\n return {\n w,\n h,\n place: (inner) => {\n const x = inner.x + (inner.w - w) / 2;\n const y = centredTop(inner, h);\n return {\n icon: { x, y: y + (h - ICON) / 2, size: ICON },\n text: title.place(x + ICON + ICON_GAP, y + (h - title.h) / 2, 'start'),\n bullets: [],\n };\n },\n };\n}\n\nfunction cardContent(\n node: DiagramNode,\n tokens: LayoutTokens,\n): Omit<NodeContent, 'pad'> {\n const { ICON, ICON_GAP, BLOCK_GAP, BULLET_INDENT } = tokens;\n const title = textBlock(\n labelLines(node.label, node.textColor),\n tokens.fonts.title,\n );\n const description = textBlock(\n labelLines(node.description, node.textColor),\n tokens.fonts.body,\n );\n const items = textBlock(\n labelLines(node.items, node.textColor),\n tokens.fonts.body,\n );\n let iconW = 0;\n if (node.icon) iconW = title.w > 0 ? ICON + ICON_GAP : ICON;\n const headerH = Math.max(node.icon ? ICON : 0, title.h);\n const sections = [\n { h: headerH, w: iconW + title.w },\n { h: description.h, w: description.w },\n { h: items.h, w: items.h > 0 ? BULLET_INDENT + items.w : 0 },\n ].filter((section) => section.h > 0);\n const bulletRise = tokens.fonts.body.size * 0.3;\n return {\n w: Math.max(0, ...sections.map((section) => section.w)),\n h:\n sections.reduce((sum, section) => sum + section.h, 0) +\n Math.max(0, sections.length - 1) * BLOCK_GAP,\n place: (inner) => {\n let top = inner.y;\n const next = (h: number) => {\n const at = top;\n if (h > 0) top += h + BLOCK_GAP;\n return at;\n };\n const headerTop = next(headerH);\n const descriptionTop = next(description.h);\n const itemsTop = next(items.h);\n const itemRuns = items.place(inner.x + BULLET_INDENT, itemsTop, 'start');\n return {\n ...(node.icon\n ? {\n icon: {\n x: inner.x,\n y: headerTop + (headerH - ICON) / 2,\n size: ICON,\n },\n }\n : {}),\n text: [\n ...title.place(\n inner.x + iconW,\n headerTop + (headerH - title.h) / 2,\n 'start',\n ),\n ...description.place(inner.x, descriptionTop, 'start'),\n ...itemRuns,\n ],\n bullets: itemRuns.map((run) => ({\n x: inner.x + BULLET_INDENT / 4,\n y: run.y - bulletRise,\n })),\n };\n },\n };\n}\n\nfunction noteContent(node: DiagramNode, tokens: LayoutTokens): NodeContent {\n const lines = textBlock(\n labelLines(node.label, node.textColor ?? 'dim'),\n tokens.fonts.note,\n );\n return {\n w: lines.w,\n h: lines.h,\n pad: { x: tokens.LABEL_GAP, y: tokens.LABEL_GAP },\n place: (inner) => ({\n text: lines.place(inner.x, inner.y, 'start'),\n bullets: [],\n }),\n };\n}\n\nfunction markerContent(node: DiagramNode, tokens: LayoutTokens): NodeContent {\n const { MARKER_ICON, BLOCK_GAP } = tokens;\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n tokens.fonts.marker,\n );\n const gap = lines.h > 0 ? BLOCK_GAP : 0;\n const h = MARKER_ICON + gap + lines.h;\n return {\n w: Math.max(MARKER_ICON, lines.w),\n h,\n pad: { x: tokens.LABEL_GAP, y: tokens.LABEL_GAP },\n place: (inner) => {\n const top = centredTop(inner, h);\n const cx = centreX(inner);\n return {\n icon: { x: cx - MARKER_ICON / 2, y: top, size: MARKER_ICON },\n text: lines.place(cx, top + MARKER_ICON + gap, 'middle'),\n bullets: [],\n };\n },\n };\n}\n\nfunction connectorContent(\n node: DiagramNode,\n tokens: LayoutTokens,\n): NodeContent {\n const { CONNECTOR_ICON, BLOCK_GAP } = tokens;\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n tokens.fonts.connector,\n );\n const iconW = node.icon ? CONNECTOR_ICON + (lines.w > 0 ? BLOCK_GAP : 0) : 0;\n const h = Math.max(CONNECTOR_MIN_HEIGHT - 2 * CONNECTOR_PAD.y, lines.h);\n return {\n w: iconW + lines.w,\n h,\n pad: CONNECTOR_PAD,\n place: (inner) => {\n const cy = inner.y + inner.h / 2;\n const top = cy - lines.h / 2;\n if (!node.icon) {\n return {\n text: lines.place(centreX(inner), top, 'middle'),\n bullets: [],\n };\n }\n return {\n icon: { x: inner.x, y: cy - CONNECTOR_ICON / 2, size: CONNECTOR_ICON },\n text: lines.place(inner.x + iconW, top, 'start'),\n bullets: [],\n };\n },\n };\n}\n\nfunction junctionContent(tokens: LayoutTokens): NodeContent {\n return {\n w: tokens.JUNCTION,\n h: tokens.JUNCTION,\n pad: { x: 0, y: 0 },\n place: () => NOTHING,\n };\n}\n\nfunction applianceContent(\n node: DiagramNode,\n tokens: LayoutTokens,\n): NodeContent {\n const { APPLIANCE, LABEL_GAP } = tokens;\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n tokens.fonts.appliance,\n );\n return {\n w: Math.max(APPLIANCE.w, lines.w + 2 * LABEL_GAP),\n h: APPLIANCE.h + (lines.h > 0 ? lines.h + LABEL_GAP : 0),\n pad: { x: 0, y: 0 },\n place: (inner) => {\n const glyph = {\n x: centreX(inner) - APPLIANCE.w / 2,\n y: inner.y,\n ...APPLIANCE,\n };\n return {\n appliance: applianceGlyph(node, glyph),\n text: lines.place(centreX(inner), glyph.y + glyph.h, 'middle'),\n bullets: [],\n };\n },\n };\n}\n\n/** The router glyph: a slanted body, an optional antenna and a row of jacks, as the renderers drew it before content sizing. */\nfunction applianceGlyph(\n node: DiagramNode,\n glyph: Box,\n): NonNullable<NodeLayout['appliance']> {\n const bodyX = glyph.x + 4;\n const bodyY = glyph.y + Math.max(12, Math.min(22, glyph.h * 0.28));\n const bodyW = glyph.w - 8;\n const bodyH = glyph.h - (bodyY - glyph.y) - 12;\n const slant = Math.max(4, Math.min(16, bodyW * 0.28, bodyH * 0.75));\n const body: Point[] = [\n { x: bodyX + slant, y: bodyY },\n { x: bodyX + bodyW, y: bodyY + 6 },\n { x: bodyX + bodyW - slant, y: bodyY + bodyH },\n { x: bodyX, y: bodyY + bodyH - 6 },\n ];\n const mastX = bodyX + slant + bodyW * 0.2;\n const antenna: [Point, Point][] =\n (node.antenna ?? true)\n ? [\n [\n { x: bodyX + slant + bodyW * 0.18, y: bodyY + 1 },\n { x: mastX - 3, y: glyph.y + 3 },\n ],\n [\n { x: mastX + 1, y: bodyY + 2 },\n { x: mastX + 1, y: glyph.y + 8 },\n ],\n ]\n : [];\n const jackCount = Math.max(0, Math.floor(node.appliancePorts ?? 4));\n const jackSpacing = Math.min(\n 13,\n Math.max(8, (bodyW - slant - 42) / Math.max(jackCount, 1)),\n );\n const jacks = Array.from({ length: jackCount }, (_, index) => ({\n x: bodyX + slant + 16 + index * jackSpacing,\n y: bodyY + bodyH - 15,\n w: 5,\n h: 12,\n }));\n const face: [Point, Point] = [\n { x: bodyX + slant, y: bodyY + bodyH - 1 },\n { x: bodyX + bodyW - slant, y: bodyY + bodyH + 3 },\n ];\n // Through a variable, not a literal, so the return skips the excess-property check against NodeLayout['appliance'].\n const parts = { body, face, antenna, jacks };\n return parts;\n}\n\nfunction centreX(box: Box): number {\n return box.x + box.w / 2;\n}\n\nfunction centredTop(box: Box, h: number): number {\n return box.y + (box.h - h) / 2;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"node-content.js","names":["labelLines","textBlock","LAYOUT_TOKENS","CONNECTOR_PAD","x","y","CONNECTOR_MIN_HEIGHT","NOTHING","text","bullets","nodeContent","node","kind","junctionContent","markerContent","connectorContent","applianceContent","noteContent","boxContent","pad","frame","LABEL_GAP","PAD_X","PAD_Y","isCard","textAlign","description","undefined","items","length","cardContent","icon","iconRowContent","lines","label","textColor","fonts","boxLabel","w","h","place","inner","centreX","centredTop","ICON","ICON_GAP","title","Math","max","size","BLOCK_GAP","BULLET_INDENT","body","iconW","headerH","sections","filter","section","bulletRise","map","reduce","sum","top","next","at","headerTop","descriptionTop","itemsTop","itemRuns","run","note","MARKER_ICON","marker","gap","cx","CONNECTOR_ICON","connector","cy","JUNCTION","APPLIANCE","appliance","glyph","applianceGlyph","bodyX","bodyY","min","bodyW","bodyH","slant","mastX","antenna","jackCount","floor","appliancePorts","jackSpacing","jacks","Array","from","_","index","face","parts","box"],"sources":["../../../../src/lib/layouts/graph/node-content.ts"],"sourcesContent":["import type { Box, DiagramNode, Point } from '../../model/diagram-ir.ts';\nimport type { NodeLayout } from './graph-layout-result.ts';\nimport { labelLines, textBlock } from './text-block.ts';\nimport { LAYOUT_TOKENS } from './tokens.ts';\n\nexport type ContentPlacement = Pick<NodeLayout, 'text' | 'bullets'> &\n Partial<Pick<NodeLayout, 'icon' | 'appliance'>>;\n\n/** What a node draws inside its frame: its measured size, the padding around it, and where it goes. */\nexport type NodeContent = {\n w: number;\n h: number;\n pad: { x: number; y: number };\n place(inner: Box): ContentPlacement;\n};\n\n/** Mirrors the connector pill drawn before content sizing: 10px side inset, 4px above and below the text, 28px tall at least. */\nconst CONNECTOR_PAD = { x: 10, y: 4 };\nconst CONNECTOR_MIN_HEIGHT = 28;\n\nconst NOTHING: ContentPlacement = { text: [], bullets: [] };\n\nexport function nodeContent(node: DiagramNode): NodeContent {\n switch (node.kind) {\n case 'junction':\n return junctionContent();\n case 'marker':\n return markerContent(node);\n case 'connector':\n return connectorContent(node);\n case 'appliance':\n return applianceContent(node);\n case 'note':\n return noteContent(node);\n default:\n return boxContent(node);\n }\n}\n\nfunction boxContent(node: DiagramNode): NodeContent {\n const pad =\n node.frame === 'none'\n ? { x: LAYOUT_TOKENS.LABEL_GAP, y: LAYOUT_TOKENS.LABEL_GAP }\n : { x: LAYOUT_TOKENS.PAD_X, y: LAYOUT_TOKENS.PAD_Y };\n const isCard =\n node.textAlign === 'start' ||\n node.description !== undefined ||\n (node.items?.length ?? 0) > 0;\n if (isCard) return { ...cardContent(node), pad };\n if (node.icon) return { ...iconRowContent(node), pad };\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n LAYOUT_TOKENS.fonts.boxLabel,\n );\n return {\n w: lines.w,\n h: lines.h,\n pad,\n place: (inner) => ({\n text: lines.place(centreX(inner), centredTop(inner, lines.h), 'middle'),\n bullets: [],\n }),\n };\n}\n\nfunction iconRowContent(node: DiagramNode): Omit<NodeContent, 'pad'> {\n const { ICON, ICON_GAP } = LAYOUT_TOKENS;\n const title = textBlock(\n labelLines(node.label, node.textColor),\n LAYOUT_TOKENS.fonts.title,\n );\n const w = title.w > 0 ? ICON + ICON_GAP + title.w : ICON;\n const h = Math.max(ICON, title.h);\n return {\n w,\n h,\n place: (inner) => {\n const x = inner.x + (inner.w - w) / 2;\n const y = centredTop(inner, h);\n return {\n icon: { x, y: y + (h - ICON) / 2, size: ICON },\n text: title.place(x + ICON + ICON_GAP, y + (h - title.h) / 2, 'start'),\n bullets: [],\n };\n },\n };\n}\n\nfunction cardContent(node: DiagramNode): Omit<NodeContent, 'pad'> {\n const { ICON, ICON_GAP, BLOCK_GAP, BULLET_INDENT } = LAYOUT_TOKENS;\n const title = textBlock(\n labelLines(node.label, node.textColor),\n LAYOUT_TOKENS.fonts.title,\n );\n const description = textBlock(\n labelLines(node.description, node.textColor),\n LAYOUT_TOKENS.fonts.body,\n );\n const items = textBlock(\n labelLines(node.items, node.textColor),\n LAYOUT_TOKENS.fonts.body,\n );\n let iconW = 0;\n if (node.icon) iconW = title.w > 0 ? ICON + ICON_GAP : ICON;\n const headerH = Math.max(node.icon ? ICON : 0, title.h);\n const sections = [\n { h: headerH, w: iconW + title.w },\n { h: description.h, w: description.w },\n { h: items.h, w: items.h > 0 ? BULLET_INDENT + items.w : 0 },\n ].filter((section) => section.h > 0);\n const bulletRise = LAYOUT_TOKENS.fonts.body.size * 0.3;\n return {\n w: Math.max(0, ...sections.map((section) => section.w)),\n h:\n sections.reduce((sum, section) => sum + section.h, 0) +\n Math.max(0, sections.length - 1) * BLOCK_GAP,\n place: (inner) => {\n let top = inner.y;\n const next = (h: number) => {\n const at = top;\n if (h > 0) top += h + BLOCK_GAP;\n return at;\n };\n const headerTop = next(headerH);\n const descriptionTop = next(description.h);\n const itemsTop = next(items.h);\n const itemRuns = items.place(inner.x + BULLET_INDENT, itemsTop, 'start');\n return {\n ...(node.icon\n ? {\n icon: {\n x: inner.x,\n y: headerTop + (headerH - ICON) / 2,\n size: ICON,\n },\n }\n : {}),\n text: [\n ...title.place(\n inner.x + iconW,\n headerTop + (headerH - title.h) / 2,\n 'start',\n ),\n ...description.place(inner.x, descriptionTop, 'start'),\n ...itemRuns,\n ],\n bullets: itemRuns.map((run) => ({\n x: inner.x + BULLET_INDENT / 4,\n y: run.y - bulletRise,\n })),\n };\n },\n };\n}\n\nfunction noteContent(node: DiagramNode): NodeContent {\n const lines = textBlock(\n labelLines(node.label, node.textColor ?? 'dim'),\n LAYOUT_TOKENS.fonts.note,\n );\n return {\n w: lines.w,\n h: lines.h,\n pad: { x: LAYOUT_TOKENS.LABEL_GAP, y: LAYOUT_TOKENS.LABEL_GAP },\n place: (inner) => ({\n text: lines.place(inner.x, inner.y, 'start'),\n bullets: [],\n }),\n };\n}\n\nfunction markerContent(node: DiagramNode): NodeContent {\n const { MARKER_ICON, BLOCK_GAP } = LAYOUT_TOKENS;\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n LAYOUT_TOKENS.fonts.marker,\n );\n const gap = lines.h > 0 ? BLOCK_GAP : 0;\n const h = MARKER_ICON + gap + lines.h;\n return {\n w: Math.max(MARKER_ICON, lines.w),\n h,\n pad: { x: LAYOUT_TOKENS.LABEL_GAP, y: LAYOUT_TOKENS.LABEL_GAP },\n place: (inner) => {\n const top = centredTop(inner, h);\n const cx = centreX(inner);\n return {\n icon: { x: cx - MARKER_ICON / 2, y: top, size: MARKER_ICON },\n text: lines.place(cx, top + MARKER_ICON + gap, 'middle'),\n bullets: [],\n };\n },\n };\n}\n\nfunction connectorContent(node: DiagramNode): NodeContent {\n const { CONNECTOR_ICON, BLOCK_GAP } = LAYOUT_TOKENS;\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n LAYOUT_TOKENS.fonts.connector,\n );\n const iconW = node.icon ? CONNECTOR_ICON + (lines.w > 0 ? BLOCK_GAP : 0) : 0;\n const h = Math.max(CONNECTOR_MIN_HEIGHT - 2 * CONNECTOR_PAD.y, lines.h);\n return {\n w: iconW + lines.w,\n h,\n pad: CONNECTOR_PAD,\n place: (inner) => {\n const cy = inner.y + inner.h / 2;\n const top = cy - lines.h / 2;\n if (!node.icon) {\n return {\n text: lines.place(centreX(inner), top, 'middle'),\n bullets: [],\n };\n }\n return {\n icon: { x: inner.x, y: cy - CONNECTOR_ICON / 2, size: CONNECTOR_ICON },\n text: lines.place(inner.x + iconW, top, 'start'),\n bullets: [],\n };\n },\n };\n}\n\nfunction junctionContent(): NodeContent {\n return {\n w: LAYOUT_TOKENS.JUNCTION,\n h: LAYOUT_TOKENS.JUNCTION,\n pad: { x: 0, y: 0 },\n place: () => NOTHING,\n };\n}\n\nfunction applianceContent(node: DiagramNode): NodeContent {\n const { APPLIANCE, LABEL_GAP } = LAYOUT_TOKENS;\n const lines = textBlock(\n labelLines(node.label, node.textColor),\n LAYOUT_TOKENS.fonts.appliance,\n );\n return {\n w: Math.max(APPLIANCE.w, lines.w + 2 * LABEL_GAP),\n h: APPLIANCE.h + (lines.h > 0 ? lines.h + LABEL_GAP : 0),\n pad: { x: 0, y: 0 },\n place: (inner) => {\n const glyph = {\n x: centreX(inner) - APPLIANCE.w / 2,\n y: inner.y,\n ...APPLIANCE,\n };\n return {\n appliance: applianceGlyph(node, glyph),\n text: lines.place(centreX(inner), glyph.y + glyph.h, 'middle'),\n bullets: [],\n };\n },\n };\n}\n\n/** The router glyph: a slanted body, an optional antenna and a row of jacks, as the renderers drew it before content sizing. */\nfunction applianceGlyph(\n node: DiagramNode,\n glyph: Box,\n): NonNullable<NodeLayout['appliance']> {\n const bodyX = glyph.x + 4;\n const bodyY = glyph.y + Math.max(12, Math.min(22, glyph.h * 0.28));\n const bodyW = glyph.w - 8;\n const bodyH = glyph.h - (bodyY - glyph.y) - 12;\n const slant = Math.max(4, Math.min(16, bodyW * 0.28, bodyH * 0.75));\n const body: Point[] = [\n { x: bodyX + slant, y: bodyY },\n { x: bodyX + bodyW, y: bodyY + 6 },\n { x: bodyX + bodyW - slant, y: bodyY + bodyH },\n { x: bodyX, y: bodyY + bodyH - 6 },\n ];\n const mastX = bodyX + slant + bodyW * 0.2;\n const antenna: [Point, Point][] =\n (node.antenna ?? true)\n ? [\n [\n { x: bodyX + slant + bodyW * 0.18, y: bodyY + 1 },\n { x: mastX - 3, y: glyph.y + 3 },\n ],\n [\n { x: mastX + 1, y: bodyY + 2 },\n { x: mastX + 1, y: glyph.y + 8 },\n ],\n ]\n : [];\n const jackCount = Math.max(0, Math.floor(node.appliancePorts ?? 4));\n const jackSpacing = Math.min(\n 13,\n Math.max(8, (bodyW - slant - 42) / Math.max(jackCount, 1)),\n );\n const jacks = Array.from({ length: jackCount }, (_, index) => ({\n x: bodyX + slant + 16 + index * jackSpacing,\n y: bodyY + bodyH - 15,\n w: 5,\n h: 12,\n }));\n const face: [Point, Point] = [\n { x: bodyX + slant, y: bodyY + bodyH - 1 },\n { x: bodyX + bodyW - slant, y: bodyY + bodyH + 3 },\n ];\n // Through a variable, not a literal, so the return skips the excess-property check against NodeLayout['appliance'].\n const parts = { body, face, antenna, jacks };\n return parts;\n}\n\nfunction centreX(box: Box): number {\n return box.x + box.w / 2;\n}\n\nfunction centredTop(box: Box, h: number): number {\n return box.y + (box.h - h) / 2;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,14 @@
1
+ import type { Point } from '../../model/diagram-ir.ts';
2
+ /**
3
+ * Drops every point that lies on the straight line through its neighbours.
4
+ *
5
+ * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
6
+ */
7
+ export declare function simplifyPath(path: Point[]): Point[];
8
+ /**
9
+ * An SVG path through the points, with each bend rounded.
10
+ *
11
+ * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
12
+ */
13
+ export declare function pathToSvg(points: Point[]): string;
14
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAKvD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAanD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAwBjD"}
@@ -0,0 +1,55 @@
1
+ /** The radius of the rounded corner the SVG path draws at each bend. */
2
+ const CORNER_RADIUS = 8;
3
+
4
+ /**
5
+ * Drops every point that lies on the straight line through its neighbours.
6
+ *
7
+ * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
8
+ */
9
+ export function simplifyPath(path) {
10
+ if (path.length < 3) return path;
11
+ const result = [path[0]];
12
+ for (const [index, current] of path.slice(1, -1).entries()) {
13
+ const previous = result.at(-1);
14
+ const next = path[index + 2];
15
+ const cross = (current.x - previous.x) * (next.y - current.y) - (current.y - previous.y) * (next.x - current.x);
16
+ if (cross !== 0) result.push(current);
17
+ }
18
+ result.push(path.at(-1));
19
+ return result;
20
+ }
21
+
22
+ /**
23
+ * An SVG path through the points, with each bend rounded.
24
+ *
25
+ * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.
26
+ */
27
+ export function pathToSvg(points) {
28
+ const [first, ...rest] = points;
29
+ if (!first) return '';
30
+ const parts = [`M ${first.x} ${first.y}`];
31
+ for (const [index, current] of rest.slice(0, -1).entries()) {
32
+ const previous = points[index];
33
+ const next = points[index + 2];
34
+ const dpx = current.x - previous.x;
35
+ const dpy = current.y - previous.y;
36
+ const dnx = next.x - current.x;
37
+ const dny = next.y - current.y;
38
+ const lp = Math.hypot(dpx, dpy);
39
+ const ln = Math.hypot(dnx, dny);
40
+ const r = Math.min(CORNER_RADIUS, lp / 2, ln / 2);
41
+ const cs = {
42
+ x: current.x - dpx / lp * r,
43
+ y: current.y - dpy / lp * r
44
+ };
45
+ const ce = {
46
+ x: current.x + dnx / ln * r,
47
+ y: current.y + dny / ln * r
48
+ };
49
+ parts.push(`L ${cs.x} ${cs.y}`, `Q ${current.x} ${current.y} ${ce.x} ${ce.y}`);
50
+ }
51
+ const last = rest.at(-1);
52
+ if (last) parts.push(`L ${last.x} ${last.y}`);
53
+ return parts.join(' ');
54
+ }
55
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","names":["CORNER_RADIUS","simplifyPath","path","length","result","index","current","slice","entries","previous","at","next","cross","x","y","push","pathToSvg","points","first","rest","parts","dpx","dpy","dnx","dny","lp","Math","hypot","ln","r","min","cs","ce","last","join"],"sources":["../../../../src/lib/layouts/graph/path.ts"],"sourcesContent":["import type { Point } from '../../model/diagram-ir.ts';\n\n/** The radius of the rounded corner the SVG path draws at each bend. */\nconst CORNER_RADIUS = 8;\n\n/**\n * Drops every point that lies on the straight line through its neighbours.\n *\n * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.\n */\nexport function simplifyPath(path: Point[]): Point[] {\n if (path.length < 3) return path;\n const result: Point[] = [path[0]!];\n for (const [index, current] of path.slice(1, -1).entries()) {\n const previous = result.at(-1)!;\n const next = path[index + 2]!;\n const cross =\n (current.x - previous.x) * (next.y - current.y) -\n (current.y - previous.y) * (next.x - current.x);\n if (cross !== 0) result.push(current);\n }\n result.push(path.at(-1)!);\n return result;\n}\n\n/**\n * An SVG path through the points, with each bend rounded.\n *\n * Moved from graph-layout.ts, whose copy step 6 deletes with the old engine.\n */\nexport function pathToSvg(points: Point[]): string {\n const [first, ...rest] = points;\n if (!first) return '';\n const parts = [`M ${first.x} ${first.y}`];\n for (const [index, current] of rest.slice(0, -1).entries()) {\n const previous = points[index]!;\n const next = points[index + 2]!;\n const dpx = current.x - previous.x;\n const dpy = current.y - previous.y;\n const dnx = next.x - current.x;\n const dny = next.y - current.y;\n const lp = Math.hypot(dpx, dpy);\n const ln = Math.hypot(dnx, dny);\n const r = Math.min(CORNER_RADIUS, lp / 2, ln / 2);\n const cs = { x: current.x - (dpx / lp) * r, y: current.y - (dpy / lp) * r };\n const ce = { x: current.x + (dnx / ln) * r, y: current.y + (dny / ln) * r };\n parts.push(\n `L ${cs.x} ${cs.y}`,\n `Q ${current.x} ${current.y} ${ce.x} ${ce.y}`,\n );\n }\n const last = rest.at(-1);\n if (last) parts.push(`L ${last.x} ${last.y}`);\n return parts.join(' ');\n}\n"],"mappings":"AAEA;AACA,MAAMA,aAAa,GAAG,CAAC;;AAEvB;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,YAAYA,CAACC,IAAa,EAAW;EACnD,IAAIA,IAAI,CAACC,MAAM,GAAG,CAAC,EAAE,OAAOD,IAAI;EAChC,MAAME,MAAe,GAAG,CAACF,IAAI,CAAC,CAAC,CAAC,CAAE;EAClC,KAAK,MAAM,CAACG,KAAK,EAAEC,OAAO,CAAC,IAAIJ,IAAI,CAACK,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAACC,OAAO,CAAC,CAAC,EAAE;IAC1D,MAAMC,QAAQ,GAAGL,MAAM,CAACM,EAAE,CAAC,CAAC,CAAC,CAAE;IAC/B,MAAMC,IAAI,GAAGT,IAAI,CAACG,KAAK,GAAG,CAAC,CAAE;IAC7B,MAAMO,KAAK,GACT,CAACN,OAAO,CAACO,CAAC,GAAGJ,QAAQ,CAACI,CAAC,KAAKF,IAAI,CAACG,CAAC,GAAGR,OAAO,CAACQ,CAAC,CAAC,GAC/C,CAACR,OAAO,CAACQ,CAAC,GAAGL,QAAQ,CAACK,CAAC,KAAKH,IAAI,CAACE,CAAC,GAAGP,OAAO,CAACO,CAAC,CAAC;IACjD,IAAID,KAAK,KAAK,CAAC,EAAER,MAAM,CAACW,IAAI,CAACT,OAAO,CAAC;EACvC;EACAF,MAAM,CAACW,IAAI,CAACb,IAAI,CAACQ,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC;EACzB,OAAON,MAAM;AACf;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASY,SAASA,CAACC,MAAe,EAAU;EACjD,MAAM,CAACC,KAAK,EAAE,GAAGC,IAAI,CAAC,GAAGF,MAAM;EAC/B,IAAI,CAACC,KAAK,EAAE,OAAO,EAAE;EACrB,MAAME,KAAK,GAAG,CAAC,KAAKF,KAAK,CAACL,CAAC,IAAIK,KAAK,CAACJ,CAAC,EAAE,CAAC;EACzC,KAAK,MAAM,CAACT,KAAK,EAAEC,OAAO,CAAC,IAAIa,IAAI,CAACZ,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAACC,OAAO,CAAC,CAAC,EAAE;IAC1D,MAAMC,QAAQ,GAAGQ,MAAM,CAACZ,KAAK,CAAE;IAC/B,MAAMM,IAAI,GAAGM,MAAM,CAACZ,KAAK,GAAG,CAAC,CAAE;IAC/B,MAAMgB,GAAG,GAAGf,OAAO,CAACO,CAAC,GAAGJ,QAAQ,CAACI,CAAC;IAClC,MAAMS,GAAG,GAAGhB,OAAO,CAACQ,CAAC,GAAGL,QAAQ,CAACK,CAAC;IAClC,MAAMS,GAAG,GAAGZ,IAAI,CAACE,CAAC,GAAGP,OAAO,CAACO,CAAC;IAC9B,MAAMW,GAAG,GAAGb,IAAI,CAACG,CAAC,GAAGR,OAAO,CAACQ,CAAC;IAC9B,MAAMW,EAAE,GAAGC,IAAI,CAACC,KAAK,CAACN,GAAG,EAAEC,GAAG,CAAC;IAC/B,MAAMM,EAAE,GAAGF,IAAI,CAACC,KAAK,CAACJ,GAAG,EAAEC,GAAG,CAAC;IAC/B,MAAMK,CAAC,GAAGH,IAAI,CAACI,GAAG,CAAC9B,aAAa,EAAEyB,EAAE,GAAG,CAAC,EAAEG,EAAE,GAAG,CAAC,CAAC;IACjD,MAAMG,EAAE,GAAG;MAAElB,CAAC,EAAEP,OAAO,CAACO,CAAC,GAAIQ,GAAG,GAAGI,EAAE,GAAII,CAAC;MAAEf,CAAC,EAAER,OAAO,CAACQ,CAAC,GAAIQ,GAAG,GAAGG,EAAE,GAAII;IAAE,CAAC;IAC3E,MAAMG,EAAE,GAAG;MAAEnB,CAAC,EAAEP,OAAO,CAACO,CAAC,GAAIU,GAAG,GAAGK,EAAE,GAAIC,CAAC;MAAEf,CAAC,EAAER,OAAO,CAACQ,CAAC,GAAIU,GAAG,GAAGI,EAAE,GAAIC;IAAE,CAAC;IAC3ET,KAAK,CAACL,IAAI,CACR,KAAKgB,EAAE,CAAClB,CAAC,IAAIkB,EAAE,CAACjB,CAAC,EAAE,EACnB,KAAKR,OAAO,CAACO,CAAC,IAAIP,OAAO,CAACQ,CAAC,IAAIkB,EAAE,CAACnB,CAAC,IAAImB,EAAE,CAAClB,CAAC,EAC7C,CAAC;EACH;EACA,MAAMmB,IAAI,GAAGd,IAAI,CAACT,EAAE,CAAC,CAAC,CAAC,CAAC;EACxB,IAAIuB,IAAI,EAAEb,KAAK,CAACL,IAAI,CAAC,KAAKkB,IAAI,CAACpB,CAAC,IAAIoB,IAAI,CAACnB,CAAC,EAAE,CAAC;EAC7C,OAAOM,KAAK,CAACc,IAAI,CAAC,GAAG,CAAC;AACxB","ignoreList":[]}
@@ -0,0 +1,34 @@
1
+ import type { Box, Point } from '../../model/diagram-ir.ts';
2
+ /** A direction of travel: right, left, down, up. */
3
+ export type Heading = 0 | 1 | 2 | 3;
4
+ /** The routing grid's pitch. */
5
+ export declare const GRID = 4;
6
+ export type RouteSearch = {
7
+ /** Only grid points inside this box are searched. */
8
+ window: Box;
9
+ /** Coordinates the grid holds besides every multiple of GRID: the route's own ends, slots and stubs. */
10
+ lines: {
11
+ x: number[];
12
+ y: number[];
13
+ };
14
+ /** No route passes a grid point strictly inside one of these. */
15
+ walls: Box[];
16
+ /** Segments of routes already drawn: a route may cross them at a cost, never run along them. */
17
+ runs: [Point, Point][];
18
+ /** Group rects and title bands: crossing or hugging their borders costs extra. */
19
+ borders: Box[];
20
+ start: Point;
21
+ /** The heading of the stub that reaches `start`. */
22
+ startHeading: Heading;
23
+ goal: Point;
24
+ /** The heading of the stub that leaves `goal` for the entry side. */
25
+ goalHeading: Heading;
26
+ };
27
+ /**
28
+ * The cheapest orthogonal path from `start` to `goal` on a grid of GRID
29
+ * pitch, or undefined when the walls leave none. The search state is a grid
30
+ * point together with the heading that reached it, so a turn costs the same
31
+ * whichever way a point was reached.
32
+ */
33
+ export declare function searchRoute(search: RouteSearch): Point[] | undefined;
34
+ //# sourceMappingURL=route-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-search.d.ts","sourceRoot":"","sources":["../../../../src/lib/layouts/graph/route-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAG5D,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEpC,gCAAgC;AAChC,eAAO,MAAM,IAAI,IAAI,CAAC;AA4BtB,MAAM,MAAM,WAAW,GAAG;IACxB,qDAAqD;IACrD,MAAM,EAAE,GAAG,CAAC;IACZ,wGAAwG;IACxG,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpC,iEAAiE;IACjE,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,gGAAgG;IAChG,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;IACvB,kFAAkF;IAClF,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,oDAAoD;IACpD,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,KAAK,CAAC;IACZ,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,EAAE,GAAG,SAAS,CA6CpE"}