@ixfx/components 0.16.5 → 0.17.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.
Files changed (218) hide show
  1. package/bundle/index.d.ts +4093 -1746
  2. package/bundle/index.js +1083 -226
  3. package/bundle/rolldown-runtime-CAoDHAWh.js +1 -0
  4. package/dist/ac-text.d.ts +11 -14
  5. package/dist/ac-text.js +8 -9
  6. package/dist/{ac-token-qEs3FsQv.d.ts → ac-token-CTBa93hF.d.ts} +2 -6
  7. package/dist/ac-token.d.ts +1 -1
  8. package/dist/ac-token.js +8 -9
  9. package/dist/{button-ikByJJPN.js → button-BOX1dfyd.js} +5 -7
  10. package/dist/{button-N92DKoWb.d.ts → button-DyJyyCsb.d.ts} +0 -4
  11. package/dist/button.d.ts +1 -1
  12. package/dist/button.js +1 -2
  13. package/dist/checkbox.d.ts +42 -9
  14. package/dist/checkbox.js +4 -5
  15. package/dist/{checked-select-CBUidG7n.js → checked-select-BmwqpgGr.js} +8 -10
  16. package/dist/{checked-select-CglRukbP.d.ts → checked-select-CNv5zNvR.d.ts} +0 -4
  17. package/dist/checked-select.d.ts +33 -1
  18. package/dist/checked-select.js +1 -2
  19. package/dist/{colour-picker-DwAa6RDh.js → colour-picker-D8g-bTSF.js} +6 -9
  20. package/dist/colour-picker.d.ts +1 -1
  21. package/dist/colour-picker.js +1 -2
  22. package/dist/{crumb-navigation-D3ndPgFg.js → crumb-navigation-D7dAC0H-.js} +11 -13
  23. package/dist/{crumb-navigation-BbqfmkYs.d.ts → crumb-navigation-Tm4fnsvN.d.ts} +6 -10
  24. package/dist/crumbs.d.ts +1 -1
  25. package/dist/crumbs.js +1 -2
  26. package/dist/{data-controller-DOlmqNKb.js → data-controller-q-rXRMzJ.js} +1 -2
  27. package/dist/data-display.d.ts +5 -8
  28. package/dist/data-display.js +3 -4
  29. package/dist/{data-grid-8TsiF2ES.js → data-grid-BcO3SMac.js} +20 -23
  30. package/dist/{data-grid-CslzF16R.d.ts → data-grid-vasJtjFP.d.ts} +9 -15
  31. package/dist/data-grid.d.ts +2 -2
  32. package/dist/data-grid.js +1 -2
  33. package/dist/{data-provider-35TyWclX.js → data-provider-C6ZNAcaT.js} +1 -2
  34. package/dist/{data-provider-BAn_-oR1.d.ts → data-provider-Cq_ylpET.d.ts} +0 -2
  35. package/dist/date-input.d.ts +9 -12
  36. package/dist/date-input.js +3 -4
  37. package/dist/{decorate-VRK8rslU.js → decorate-Dxs8sFX8.js} +2 -4
  38. package/dist/{defaults-B4ePs4LB.js → defaults-_6ELsqLb.js} +2 -3
  39. package/dist/{detail-list-n1MbNbhF.d.ts → detail-list-E9WdMVbE.d.ts} +10 -16
  40. package/dist/{detail-list-w3GXKO2k.js → detail-list-vPVwE1CP.js} +17 -20
  41. package/dist/detail-list.d.ts +5 -5
  42. package/dist/detail-list.js +1 -2
  43. package/dist/{drag-ghost-DtSDvjri.js → drag-ghost-DNWu_RyN.js} +1 -2
  44. package/dist/{editable-label-rzXkjf1H.d.ts → editable-label-CiHCDlhC.d.ts} +0 -2
  45. package/dist/editable-label.d.ts +31 -1
  46. package/dist/editable-label.js +3 -4
  47. package/dist/{fallbacks-DQ_5OsmR.js → fallbacks-BPjjBoED.js} +1 -2
  48. package/dist/{flat-list-D_4y3wgn.d.ts → flat-list-CfutGqKY.d.ts} +8 -12
  49. package/dist/flat-list.d.ts +1 -1
  50. package/dist/flat-list.js +10 -11
  51. package/dist/{form-CY0nibZ5.js → form-Cxx2sO8_.js} +5 -9
  52. package/dist/{form-BGZK3jZd.d.ts → form-raTQNAWF.d.ts} +0 -4
  53. package/dist/form.d.ts +1 -1
  54. package/dist/form.js +1 -2
  55. package/dist/{grid-list-Cz2tKcNZ.js → grid-list-D2hinM64.js} +275 -41
  56. package/dist/{grid-list-CqYS28NY.d.ts → grid-list-OCdjuhng.d.ts} +60 -24
  57. package/dist/grid-list.d.ts +5 -5
  58. package/dist/grid-list.js +1 -2
  59. package/dist/{grouped-item-lister-CS4yInuw.d.ts → grouped-item-lister-1MlvJORP.d.ts} +2 -6
  60. package/dist/grouped-item-lister.d.ts +1 -1
  61. package/dist/grouped-item-lister.js +3 -4
  62. package/dist/{grouping-CUKgmkMm.d.ts → grouping-OoQ8WaKc.d.ts} +0 -2
  63. package/dist/{grouping-RYPnHkrS.js → grouping-kENdqzSV.js} +1 -2
  64. package/dist/{handle-DAXs7kbJ.js → handle-BVUWv97X.js} +1 -2
  65. package/dist/{hex-editor-Caq8vNQk.d.ts → hex-editor-BCBKRU9D.d.ts} +0 -4
  66. package/dist/{hex-editor-DiYpgIKs.js → hex-editor-DephDhuu.js} +12 -13
  67. package/dist/hex.d.ts +1 -1
  68. package/dist/hex.js +1 -2
  69. package/dist/{highlight-B-eQrhhb.js → highlight-noLHFSKZ.js} +1 -2
  70. package/dist/{icon-CDGprSdE.d.ts → icon-Bhz0c6Yf.d.ts} +0 -2
  71. package/dist/{icon-1gpwtPx8.js → icon-DvTQuylG.js} +4 -5
  72. package/dist/icons.d.ts +2 -2
  73. package/dist/icons.js +4 -5
  74. package/dist/{incr-search-Dc6gYpJG.js → incr-search-C5114RxZ.js} +14 -26
  75. package/dist/incr-search.d.ts +1 -1
  76. package/dist/incr-search.js +2 -2
  77. package/dist/{index-ZaA90u09.d.ts → index-0smh4WCH.d.ts} +3 -21
  78. package/dist/{index-VAs9akt4.d.ts → index-88ra6YHP.d.ts} +8 -22
  79. package/dist/{index-UvnoZUAo.d.ts → index-B7odjrlg.d.ts} +4 -8
  80. package/dist/{index-Bm9a9S3_.d.ts → index-CRLTVAyt.d.ts} +4 -30
  81. package/dist/{index-9ttcdwlL.d.ts → index-Cdcek1Oj.d.ts} +1 -3
  82. package/dist/index-Cn0qsz9a.d.ts +1632 -0
  83. package/dist/{index-YCDugjdu.d.ts → index-CzhYJJsx.d.ts} +7 -17
  84. package/dist/{index-CYndvTWH.d.ts → index-D6N4Lzek.d.ts} +1 -5
  85. package/dist/{index-CoqtMSd9.d.ts → index-DN6kC6an.d.ts} +1 -3
  86. package/dist/{index-DV3iaUBW2.d.ts → index-DrYQBts2.d.ts} +0 -14
  87. package/dist/{index-o-CWDt11.d.ts → index-DzocgUef.d.ts} +0 -4
  88. package/dist/{index-DN2BWctP.d.ts → index-PIrgX1bP.d.ts} +1 -7
  89. package/dist/index.d.ts +364 -491
  90. package/dist/index.js +139 -207
  91. package/dist/interaction-8Kbbf6xN.js +3 -0
  92. package/dist/{item-appearance-BjcMyauw.js → item-appearance-FRO3TRFI.js} +1 -2
  93. package/dist/{keyboard-B5xD7Fz-.js → keyboard-DMwjIYh7.js} +0 -1
  94. package/dist/{labelled-input-base-BFa4xlYV.js → labelled-input-base-KUhD02dh.js} +3 -5
  95. package/dist/{labelled-input-base-DiTme1JO.d.ts → labelled-input-base-jyr_SR6t.d.ts} +0 -2
  96. package/dist/labelled-radial-input.d.ts +4 -7
  97. package/dist/labelled-radial-input.js +5 -6
  98. package/dist/labelled-range-input.d.ts +1 -1
  99. package/dist/labelled-range-input.js +3 -4
  100. package/dist/{labelled-slider-input-FdCRi6st.js → labelled-slider-input-CfRKd5kC.js} +9 -11
  101. package/dist/labelled-slider-input.d.ts +1 -1
  102. package/dist/labelled-slider-input.js +1 -2
  103. package/dist/led.d.ts +3 -6
  104. package/dist/led.js +2 -3
  105. package/dist/list-group-header.d.ts +3 -6
  106. package/dist/list-group-header.js +3 -4
  107. package/dist/{list-native-drag-controller-CcXoS0YX.js → list-native-drag-controller-ByftBooJ.js} +5 -9
  108. package/dist/{list-native-drag-controller-D_UrXipS.d.ts → list-native-drag-controller-CgXKdmjr.d.ts} +1 -3
  109. package/dist/{list-selection-controller-D4iIrvJH.js → list-selection-controller-Dkh3m3hX.js} +3 -5
  110. package/dist/{list-selection-types-Dnamh2Lu.d.ts → list-selection-types-CKTt7Rrw.d.ts} +0 -2
  111. package/dist/{list-viewport-types-BRZsLcvp.d.ts → list-viewport-types-BJ8c6qur.d.ts} +0 -2
  112. package/dist/{menu-BX1FHxY8.js → menu-C3DhTjHx.js} +17 -27
  113. package/dist/{menu-item-oxU1VKiJ.js → menu-item-BSxhlWnW.js} +30 -15
  114. package/dist/{menu-item-bF6narIz.d.ts → menu-item-DJhaWy9z.d.ts} +10 -7
  115. package/dist/menu.d.ts +2 -2
  116. package/dist/menu.js +2 -2
  117. package/dist/{miller-BrmBywNZ.js → miller-DqDEaXqT.js} +15 -19
  118. package/dist/miller.d.ts +3 -3
  119. package/dist/miller.js +1 -2
  120. package/dist/narrowed-text.d.ts +1 -1
  121. package/dist/narrowed-text.js +8 -9
  122. package/dist/node-graph-DqEGv5QR.js +5369 -0
  123. package/dist/node-graph.d.ts +2 -0
  124. package/dist/node-graph.js +1 -0
  125. package/dist/{notification-pill-DyZydXoZ.d.ts → notification-pill-BiHEJk-o.d.ts} +0 -4
  126. package/dist/notifier.d.ts +1 -1
  127. package/dist/notifier.js +4 -5
  128. package/dist/{pan-text-CoYmt2qd.d.ts → pan-text-DKe2zinH.d.ts} +0 -2
  129. package/dist/{pan-text-CqtvjPyK.js → pan-text-DrJYKQKN.js} +3 -4
  130. package/dist/panel.d.ts +11 -14
  131. package/dist/panel.js +3 -4
  132. package/dist/{partial-keys-Ck6CiprB.js → partial-keys-D4l_ede_.js} +2 -3
  133. package/dist/{pending-BGidHppL.js → pending-Afl4LT64.js} +1 -2
  134. package/dist/plots.d.ts +1 -1
  135. package/dist/plots.js +1 -2
  136. package/dist/polar-pad.d.ts +5 -7
  137. package/dist/polar-pad.js +6 -7
  138. package/dist/{popup-D80sj2QC.js → popup-D8IxN1Eq.js} +1 -2
  139. package/dist/{prominence-DlFShic3.js → prominence-BOOQVWF-.js} +1 -2
  140. package/dist/{radial-input-KMVltQfe.js → radial-input-Cxys4TOG.js} +7 -9
  141. package/dist/{radial-input-DU6t2hYO.d.ts → radial-input-DNCSwRGF.d.ts} +1 -5
  142. package/dist/radial-input.d.ts +10 -1
  143. package/dist/radial-input.js +1 -2
  144. package/dist/range-input.d.ts +4 -6
  145. package/dist/range-input.js +5 -6
  146. package/dist/range.d.ts +4 -6
  147. package/dist/range.js +6 -7
  148. package/dist/{registry-BEtb8_wB.js → registry-DcF0fO7l.js} +1 -2
  149. package/dist/rolldown-runtime-D3KUYPsf.js +12 -0
  150. package/dist/{scroll-fade-Xx28D7mH.js → scroll-fade-DH8OXSqL.js} +1 -2
  151. package/dist/{scroll-hover-guard-Cjw6179Q.js → scroll-hover-guard-BGAjtyXT.js} +1 -2
  152. package/dist/select-horiz.d.ts +7 -9
  153. package/dist/select-horiz.js +8 -9
  154. package/dist/{slider-input-ByHdotzp.js → slider-input-C3mbUBZi.js} +6 -8
  155. package/dist/{slider-input-BxURAAmD.d.ts → slider-input-DHLGJKWF.d.ts} +1 -5
  156. package/dist/slider-input.d.ts +34 -1
  157. package/dist/slider-input.js +1 -2
  158. package/dist/snackbar.d.ts +5 -8
  159. package/dist/snackbar.js +4 -5
  160. package/dist/snap-container.d.ts +8 -11
  161. package/dist/snap-container.js +2 -3
  162. package/dist/{split-layout-DTY4PJRp.js → split-layout-BMN_s6d7.js} +10 -15
  163. package/dist/{split-layout-CophkNeU.d.ts → split-layout-HytcG1Bn.d.ts} +2 -8
  164. package/dist/split-layout.d.ts +1 -1
  165. package/dist/split-layout.js +1 -2
  166. package/dist/sunburst-tree.d.ts +1 -1
  167. package/dist/sunburst-tree.js +1 -2
  168. package/dist/{sunbursttree-list-DQF6DcsS.js → sunbursttree-list-C-FuZB5z.js} +23 -32
  169. package/dist/{sunbursttree-list-Bk20giTF.d.ts → sunbursttree-list-CsrmVSB8.d.ts} +5 -11
  170. package/dist/swipe.d.ts +7 -9
  171. package/dist/swipe.js +5 -6
  172. package/dist/{tab-list-Df371rqw.d.ts → tab-list-BXMRYJeR.d.ts} +1 -5
  173. package/dist/{tab-list-R_bGbDvO.js → tab-list-CFJy3hVm.js} +6 -15
  174. package/dist/tabs.d.ts +1 -1
  175. package/dist/tabs.js +1 -2
  176. package/dist/{tickled-controller-D-HZymaZ.js → tickled-controller-BVhGKzjZ.js} +1 -2
  177. package/dist/{tickled-controller-DodxD3eQ.d.ts → tickled-controller-DSPoG551.d.ts} +0 -2
  178. package/dist/{tickled-styles-BrAHEj1q.js → tickled-styles-pnieYweS.js} +1 -2
  179. package/dist/{timeline-BP1Ux2EV.js → timeline-Br5JIeLI.js} +9 -15
  180. package/dist/timeline.d.ts +1 -1
  181. package/dist/timeline.js +1 -2
  182. package/dist/{titlebar-Tqu-VvMU.d.ts → titlebar-BtLAF2ws.d.ts} +1 -3
  183. package/dist/titlebar.d.ts +1 -1
  184. package/dist/titlebar.js +4 -5
  185. package/dist/{tooltip-CJt3WLmN.js → tooltip-B7t_u6EY.js} +1 -2
  186. package/dist/{tooltip-BlvBUQxx.d.ts → tooltip-Bl4HpBue.d.ts} +0 -2
  187. package/dist/transitory-label.d.ts +3 -6
  188. package/dist/transitory-label.js +2 -3
  189. package/dist/{tree-BaXYyt5U.js → tree-BP5fxvEX.js} +24 -34
  190. package/dist/{tree-CftHwtEb.js → tree-DUc-iQcg.js} +2 -3
  191. package/dist/{tree-component-DgfkxmVY.d.ts → tree-component-DNuzfo6R.d.ts} +2 -8
  192. package/dist/tree.d.ts +3 -3
  193. package/dist/tree.js +1 -2
  194. package/dist/{types-LU1u0CB8.d.ts → types-16GC5k1z.d.ts} +0 -2
  195. package/dist/{types-Jky6oNAp.d.ts → types-6hpXRKt7.d.ts} +1 -3
  196. package/dist/unit-input.d.ts +11 -14
  197. package/dist/unit-input.js +3 -4
  198. package/dist/{vertical-list-o5hfqH87.js → vertical-list-B9z1lbjM.js} +13 -15
  199. package/dist/{vertical-list-DtHX26JO.d.ts → vertical-list-BzeXehBt.d.ts} +7 -13
  200. package/dist/vertical-list.d.ts +4 -4
  201. package/dist/vertical-list.js +1 -2
  202. package/dist/{view-state-controller-BdyXWUUM.js → view-state-controller-DiPAPiA7.js} +3 -6
  203. package/dist/{wheel-nudge-D5rmht3v.js → wheel-nudge-DgJzrL28.js} +2 -3
  204. package/dist/{wheel-nudge-By5s_ioH.d.ts → wheel-nudge-oMFKat9P.d.ts} +1 -3
  205. package/dist/{xy-axis-DQqd2yXJ.d.ts → xy-axis-Bf6WfOS2.d.ts} +0 -12
  206. package/dist/{xy-axis-CyMd_Bn1.js → xy-axis-CBpml-3S.js} +11 -20
  207. package/dist/xy-pad.d.ts +7 -9
  208. package/dist/xy-pad.js +6 -7
  209. package/docs-user/README.md +1 -0
  210. package/docs-user/grid-list.md +65 -0
  211. package/docs-user/index.json +7 -2
  212. package/docs-user/llms.txt +1 -0
  213. package/docs-user/node-graph.md +376 -0
  214. package/llms.txt +1 -0
  215. package/package.json +39 -24
  216. package/bundle/rolldown-runtime-CEoa-xHZ.js +0 -1
  217. package/dist/interaction-E-uMGHkJ.js +0 -3
  218. package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
@@ -0,0 +1,1632 @@
1
+ import { a as ViewStateBase, o as ViewStateChangeDetail, t as GetViewStateOptions } from "./types-16GC5k1z.js";
2
+ import "./icon-Bhz0c6Yf.js";
3
+ import "./menu-item-DJhaWy9z.js";
4
+ import "./index-Cdcek1Oj.js";
5
+ import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost } from "lit";
6
+ import { Graphs } from "@ixfx/collections";
7
+ /** Direction a port faces: `in` receives wires, `out` sends them. */
8
+ type PortDir = `in` | `out`;
9
+ /**
10
+ * Which way a wire leaves a port. Nested ports (not on the node rails) may
11
+ * override it with the `side` attribute; it defaults from {@link PortDir}.
12
+ */
13
+ type PortSide = `left` | `right` | `top` | `bottom`;
14
+ /**
15
+ * Identifies one end of a wire: a port `key` on the node with the given `key`.
16
+ * Both keys are author-chosen, so two graphs on one page never collide.
17
+ */
18
+ type PortRef = {
19
+ readonly node: string;
20
+ readonly port: string;
21
+ };
22
+ /**
23
+ * A wire between two ports. `from` is always an `out` port and `to` always an
24
+ * `in` port (normalised — a wire dragged backwards from an `in` port commits
25
+ * in this orientation). The id is deterministic (see `edgeId`), so edges stay
26
+ * plain immutable data.
27
+ */
28
+ type GraphEdge = {
29
+ /** `edgeId(from, to)` — e.g. `osc/out->amp/in` (with keys escaped). */
30
+ readonly id: string;
31
+ /** Source: always an `out` port. */
32
+ readonly from: PortRef;
33
+ /** Destination: always an `in` port. */
34
+ readonly to: PortRef;
35
+ };
36
+ /**
37
+ * Pan/zoom state. `x` / `y` are the translation applied to the viewport layer
38
+ * (screen px), `zoom` the scale factor.
39
+ */
40
+ type Viewport = {
41
+ readonly x: number;
42
+ readonly y: number;
43
+ readonly zoom: number;
44
+ };
45
+ /** A point in some coordinate space (flow, screen, node-relative — by context). */
46
+ type Point = {
47
+ readonly x: number;
48
+ readonly y: number;
49
+ };
50
+ /** Everything the connect rules need to know about a port. */
51
+ type PortInfo = {
52
+ readonly ref: PortRef;
53
+ readonly dir: PortDir;
54
+ /** Compatibility tag. Empty or `*` matches anything. */
55
+ readonly type: string;
56
+ /** Maximum connections. `in` defaults to 1, `out` to unlimited. */
57
+ readonly max: number;
58
+ /** Wire tangent at this port. */
59
+ readonly side: PortSide;
60
+ /** Human-visible name (`ref.port` is the unique key). */
61
+ readonly label?: string;
62
+ /** Optional longer explanation. */
63
+ readonly description?: string;
64
+ };
65
+ /** Plain-JSON snapshot of the whole editor state. See `NodeGraphElement.getGraph()`. */
66
+ type GraphSnapshot = {
67
+ readonly nodes: ReadonlyArray<{
68
+ readonly key: string;
69
+ readonly x: number;
70
+ readonly y: number;
71
+ readonly label: string;
72
+ readonly ports: readonly PortInfo[];
73
+ }>;
74
+ readonly edges: readonly GraphEdge[];
75
+ };
76
+ /** Why a connection was rejected by the built-in rules. */
77
+ type ConnectionCheckReason = `same-dir` | `self` | `type` | `max` | `duplicate` | `cycle` | `custom`;
78
+ /**
79
+ * Result of the built-in connection rules. `ok: true` may carry an edge that
80
+ * the new wire replaces (an `in` port at `max === 1`).
81
+ */
82
+ type ConnectionCheck = {
83
+ readonly ok: true;
84
+ readonly replaces?: GraphEdge;
85
+ } | {
86
+ readonly ok: false;
87
+ readonly reason: ConnectionCheckReason;
88
+ };
89
+ /**
90
+ * Extra veto hook, on top of the built-in rules. Return `false` to reject.
91
+ * `graph` is the port graph as it stands when the check runs.
92
+ */
93
+ type IsValidConnection = (from: PortInfo, to: PortInfo, graph: Graphs.Directed.DirectedGraph) => boolean;
94
+ /** A single node position change. */
95
+ type NodeMove = {
96
+ readonly key: string;
97
+ readonly x: number;
98
+ readonly y: number;
99
+ };
100
+ type GraphConnectDetail = {
101
+ readonly edge: GraphEdge;
102
+ /** Existing wire the new one replaces (`in` port at `max === 1`). */
103
+ readonly replaces?: GraphEdge;
104
+ };
105
+ type GraphConnectEvent = CustomEvent<GraphConnectDetail>;
106
+ type DisconnectReason = `delete` | `detach` | `replace` | `api`;
107
+ type GraphDisconnectDetail = {
108
+ readonly edges: readonly GraphEdge[];
109
+ readonly reason: DisconnectReason;
110
+ };
111
+ type GraphDisconnectEvent = CustomEvent<GraphDisconnectDetail>;
112
+ /** A wire was dropped on empty space — the consumer may open a "create node" menu. */
113
+ type GraphConnectEndDetail = {
114
+ readonly from: PortRef;
115
+ /** Drop point in flow coordinates. */
116
+ readonly point: Point;
117
+ /** Drop point in client coordinates. */
118
+ readonly client: Point;
119
+ };
120
+ type GraphConnectEndEvent = CustomEvent<GraphConnectEndDetail>;
121
+ type GraphDeleteDetail = {
122
+ /** Every node being deleted, including the children of a deleted group. */
123
+ readonly nodes: readonly string[];
124
+ readonly edges: readonly GraphEdge[];
125
+ /** Wires added to keep the flow connected through deleted nodes (`bridge-on-delete`). Cancel the event to veto them all. */
126
+ readonly bridged: readonly GraphEdge[];
127
+ };
128
+ type GraphDeleteEvent = CustomEvent<GraphDeleteDetail>;
129
+ type GraphNodeMoveDetail = {
130
+ readonly moves: readonly NodeMove[];
131
+ };
132
+ type GraphNodeMoveEvent = CustomEvent<GraphNodeMoveDetail>;
133
+ type GraphNodeMovedEvent = CustomEvent<GraphNodeMoveDetail>;
134
+ /** A node was resized (`graph-node-resize` while dragging, `graph-node-resized` on release). `x` / `y` are relative to its parent. */
135
+ type GraphNodeResizeDetail = {
136
+ readonly key: string;
137
+ readonly x: number;
138
+ readonly y: number;
139
+ readonly width: number;
140
+ readonly height: number;
141
+ };
142
+ type GraphNodeResizeEvent = CustomEvent<GraphNodeResizeDetail>;
143
+ /** A port was added (`available`) or hidden away (`potential`) by the user. */
144
+ type GraphPortChangeDetail = {
145
+ readonly node: string;
146
+ readonly port: string;
147
+ readonly dir: `in` | `out`;
148
+ readonly availability: `available` | `potential`;
149
+ };
150
+ type GraphPortChangeEvent = CustomEvent<GraphPortChangeDetail>;
151
+ /** A node was collapsed to its title bar or expanded (bubbles from the node). */
152
+ type GraphNodeCollapseDetail = {
153
+ readonly key: string;
154
+ readonly collapsed: boolean;
155
+ };
156
+ type GraphNodeCollapseEvent = CustomEvent<GraphNodeCollapseDetail>;
157
+ /**
158
+ * The user double-clicked a node's title bar (not one of its controls). Cancelable: with the graph's
159
+ * `dblclick-toggle` on, `preventDefault()` stops the node collapsing or expanding.
160
+ */
161
+ type GraphNodeTitleDblclickDetail = {
162
+ readonly key: string;
163
+ /** Whether the node was collapsed when it was double-clicked. */
164
+ readonly collapsed: boolean;
165
+ };
166
+ type GraphNodeTitleDblclickEvent = CustomEvent<GraphNodeTitleDblclickDetail>;
167
+ /** A node made by `nodeFactory` was added (`connectedTo` is the port it was wired to). */
168
+ type GraphNodeAddedDetail = {
169
+ readonly key: string;
170
+ readonly from: PortRef;
171
+ readonly connectedTo?: PortRef;
172
+ };
173
+ type GraphNodeAddedEvent = CustomEvent<GraphNodeAddedDetail>;
174
+ /** Right-click on a node, wire or the background. Cancel it to suppress the menu. */
175
+ type GraphContextMenuDetail = {
176
+ readonly kind: `node` | `edge` | `pane` | `port`;
177
+ /** Node key (`kind: 'node'`), wire id (`kind: 'edge'`) or `node/port` (`kind: 'port'`). */
178
+ readonly id?: string;
179
+ readonly client: Point;
180
+ readonly point: Point;
181
+ };
182
+ type GraphContextMenuEvent = CustomEvent<GraphContextMenuDetail>;
183
+ type GraphSelectionChangeDetail = {
184
+ readonly nodes: readonly string[];
185
+ readonly edges: readonly string[];
186
+ };
187
+ type GraphSelectionChangeEvent = CustomEvent<GraphSelectionChangeDetail>;
188
+ type GraphViewportChangeEvent = CustomEvent<Viewport>;
189
+ type GraphEdgesChangeDetail = {
190
+ readonly edges: readonly GraphEdge[];
191
+ /** The new port graph, for consumers keeping an immutable history (undo). */
192
+ readonly graph: Graphs.Directed.DirectedGraph;
193
+ };
194
+ type GraphEdgesChangeEvent = CustomEvent<GraphEdgesChangeDetail>;
195
+ /** Internal, non-composed event `ixfx-graph-node` fires when its geometry may have changed. */
196
+ type GraphNodeGeometryDetail = {
197
+ readonly key: string;
198
+ readonly x: number;
199
+ readonly y: number;
200
+ readonly width: number;
201
+ readonly height: number;
202
+ };
203
+ type GraphNodeGeometryEvent = CustomEvent<GraphNodeGeometryDetail>;
204
+ interface NodeGraphElementEventMap {
205
+ 'graph-connect': GraphConnectEvent;
206
+ 'graph-disconnect': GraphDisconnectEvent;
207
+ 'graph-connect-end': GraphConnectEndEvent;
208
+ 'graph-delete': GraphDeleteEvent;
209
+ 'graph-node-move': GraphNodeMoveEvent;
210
+ 'graph-node-moved': GraphNodeMovedEvent;
211
+ 'graph-node-resize': GraphNodeResizeEvent;
212
+ 'graph-node-resized': GraphNodeResizeEvent;
213
+ 'graph-node-collapse': GraphNodeCollapseEvent;
214
+ 'graph-node-title-dblclick': GraphNodeTitleDblclickEvent;
215
+ 'graph-port-change': GraphPortChangeEvent;
216
+ 'graph-node-added': GraphNodeAddedEvent;
217
+ 'graph-context-menu': GraphContextMenuEvent;
218
+ 'graph-selection-change': GraphSelectionChangeEvent;
219
+ 'graph-viewport-change': GraphViewportChangeEvent;
220
+ 'graph-edges-change': GraphEdgesChangeEvent;
221
+ 'view-state-change': CustomEvent<ViewStateChangeDetail>;
222
+ }
223
+ /** View state for `ixfx-node-graph`: viewport transform plus selection. */
224
+ type NodeGraphViewState = ViewStateBase & {
225
+ readonly viewport?: {
226
+ readonly x: number;
227
+ readonly y: number;
228
+ readonly zoom: number;
229
+ };
230
+ /** Selected wire ids (`GraphEdge.id`). */
231
+ readonly edges?: readonly string[];
232
+ };
233
+ /** A pointer gesture owning the current drag (pan, node drag, wire drag, marquee, resize). */
234
+ interface PointerGesture {
235
+ onPointerMove: (e: PointerEvent) => void;
236
+ /** Returns `true` to keep the gesture alive (e.g. a multi-pointer pinch). */
237
+ onPointerUp: (e: PointerEvent) => boolean;
238
+ onPointerCancel: (e: PointerEvent) => boolean;
239
+ addPointer?: (e: PointerEvent) => void;
240
+ }
241
+ /**
242
+ * Vertex id of a port in the port graph: node and port joined by `/`,
243
+ * with `\` and `/` in the keys escaped. Use `parsePortId` to reverse it —
244
+ * never hand-parse ids.
245
+ */
246
+ declare function portId(ref: PortRef): string;
247
+ /** Reverse of `portId`. Returns `undefined` for ids without a separator. */
248
+ declare function parsePortId(id: string): PortRef | undefined;
249
+ /** Deterministic edge id: `${portId(from)}->${portId(to)}`. */
250
+ declare function edgeId(from: PortRef, to: PortRef): string;
251
+ /** All the port vertices that exist as ids, whether or not they have wires. */
252
+ declare function ensurePort(g: Graphs.Directed.DirectedGraph, ref: PortRef): Graphs.Directed.DirectedGraph;
253
+ /** Adds a wire `from → to` (out port → in port), creating port vertices as needed. */
254
+ declare function addWire(g: Graphs.Directed.DirectedGraph, from: PortRef, to: PortRef): Graphs.Directed.DirectedGraph;
255
+ /** Removes the wire `from → to`, if present. Exact, because vertices are ports. */
256
+ declare function removeWire(g: Graphs.Directed.DirectedGraph, from: PortRef, to: PortRef): Graphs.Directed.DirectedGraph;
257
+ /**
258
+ * Removes every port of a node (with their wires), keeping the node itself —
259
+ * the element may come back. `portKeys` is the set of port keys the node had.
260
+ */
261
+ declare function removeNode(g: Graphs.Directed.DirectedGraph, nodeKey: string, portKeys: readonly string[]): Graphs.Directed.DirectedGraph;
262
+ /** Builds a fresh port graph from a plain edge array, in one mutation pass. */
263
+ declare function fromEdges(edges: readonly GraphEdge[]): Graphs.Directed.DirectedGraph;
264
+ /**
265
+ * Derives the public edge view from a port graph. Every graph edge runs out →
266
+ * in by construction; ids are recomputed deterministically.
267
+ */
268
+ declare function toEdges(g: Graphs.Directed.DirectedGraph): GraphEdge[];
269
+ /**
270
+ * Collapses a port graph to node keys. Every node key that has a port in the
271
+ * port graph appears as a vertex, and parallel wires between the same node
272
+ * pair collapse to one edge.
273
+ *
274
+ * Ports only become vertices of the port graph once they are wired, so a node
275
+ * with no wires is invisible to `g`. Pass `nodeKeys` (every node that exists)
276
+ * to include those isolated nodes too.
277
+ */
278
+ declare function toNodeGraph(g: Graphs.Directed.DirectedGraph, nodeKeys?: Iterable<string>): Graphs.Directed.DirectedGraph;
279
+ /**
280
+ * Per-drag context for the connection rules. Computed once when a wire drag
281
+ * starts so each candidate port is O(1) to check.
282
+ */
283
+ type ConnectContext = {
284
+ /**
285
+ * The port graph with every edge reversed: the in-degree of an `in` port is
286
+ * `vertex.out.length` in this graph, read in O(1).
287
+ */
288
+ readonly transposed: Graphs.Directed.DirectedGraph;
289
+ /**
290
+ * With `acyclic` set: node keys that the dragged wire may not connect into
291
+ * (the source node itself, plus its ancestors or descendants depending on
292
+ * drag direction). Only computed when `acyclic` is on; otherwise empty.
293
+ */
294
+ readonly blockedNodes: ReadonlySet<string>;
295
+ };
296
+ /**
297
+ * Context for one wire drag. `anchor` is the port the drag started from and
298
+ * `forward` is `true` when the anchor is an `out` port (a normal forward
299
+ * wire) — cycle blocking then uses the anchor's ancestors. For a wire dragged
300
+ * backwards from an `in` port, candidates are `out` ports, so blocking uses
301
+ * the anchor's descendants instead.
302
+ */
303
+ declare function connectContext(portGraph: Graphs.Directed.DirectedGraph, nodeGraph: Graphs.Directed.DirectedGraph, anchor: PortRef, forward: boolean, acyclic: boolean): ConnectContext;
304
+ /**
305
+ * Node keys that must not be the target of a new wire from `nodeKey`.
306
+ * Forward (the target reads from us): its ancestors plus itself.
307
+ * Backward (we read from the target): its descendants plus itself.
308
+ */
309
+ declare function cycleBlockedNodes(nodeGraph: Graphs.Directed.DirectedGraph, nodeKey: string, forward: boolean): Set<string>;
310
+ /** Number of wires arriving at an `in` port, read in O(1) from a transposed graph. */
311
+ declare function inDegreeOf(transposed: Graphs.Directed.DirectedGraph, ref: PortRef): number;
312
+ /** Number of wires leaving an `out` port, read in O(1). */
313
+ declare function outDegreeOf(g: Graphs.Directed.DirectedGraph, ref: PortRef): number;
314
+ /** True when the exact wire `from → to` already exists. */
315
+ declare function hasWire(g: Graphs.Directed.DirectedGraph, from: PortRef, to: PortRef): boolean;
316
+ /**
317
+ * The single wire currently arriving at an `in` port, if any — the edge an
318
+ * `in` port at `max === 1` would replace.
319
+ */
320
+ declare function incomingEdge(transposed: Graphs.Directed.DirectedGraph, ref: PortRef): GraphEdge | undefined;
321
+ /** All wires arriving at an `in` port (for `max > 1` ports). */
322
+ declare function incomingEdges(g: Graphs.Directed.DirectedGraph, ref: PortRef): GraphEdge[];
323
+ /**
324
+ * Pure connection rules for the wire drag and the `connect()` API.
325
+ *
326
+ * All checks take a {@link ConnectContext} (computed once per drag) and the
327
+ * working* port graph — the graph the drag commits into. During a detach
328
+ * the picked-up wire is removed from the working graph first, so the rules
329
+ * naturally ignore it.
330
+ */
331
+ type ConnectionRulesOptions = {
332
+ /** Allow both ends on the same node. Default `false`. */
333
+ readonly allowSelf: boolean;
334
+ /** Reject connections that would create a (node-level) cycle. Default `false`. */
335
+ readonly acyclic: boolean;
336
+ /** Extra consumer veto, checked last. */
337
+ readonly isValidConnection?: IsValidConnection;
338
+ };
339
+ /** Port types are compatible when equal, or either side is empty or `*` (matches anything). */
340
+ declare function typesCompatible(a: string, b: string): boolean;
341
+ /**
342
+ * Normalises an (unordered) pair of ports to out → in. Returns `undefined`
343
+ * when both ports face the same direction — no wire is possible.
344
+ */
345
+ declare function normaliseConnection(a: PortInfo, b: PortInfo): {
346
+ readonly from: PortInfo;
347
+ readonly to: PortInfo;
348
+ } | undefined;
349
+ /**
350
+ * Runs the built-in rules, in order, stopping at the first failure. When
351
+ * everything passes it returns the wire's `PortRef` pair plus an edge it
352
+ * would replace (an `in` port whose `max === 1` already has a wire).
353
+ */
354
+ declare function canConnect(from: PortInfo, to: PortInfo, ctx: ConnectContext, graph: Graphs.Directed.DirectedGraph, options: ConnectionRulesOptions): ConnectionCheck;
355
+ /**
356
+ * Convenience for the `connect()` API: validates a pair of port infos (in
357
+ * either order) against a context built on the fly, returning the normalised
358
+ * wire on success.
359
+ */
360
+ declare function resolveConnection(a: PortInfo, b: PortInfo, graph: Graphs.Directed.DirectedGraph, nodeGraph: Graphs.Directed.DirectedGraph, options: ConnectionRulesOptions): {
361
+ readonly edge: {
362
+ readonly from: PortRef;
363
+ readonly to: PortRef;
364
+ };
365
+ readonly replaces?: GraphEdge;
366
+ } | {
367
+ readonly reason: ConnectionCheckReason;
368
+ };
369
+ /**
370
+ * The first valid wire from one of `outs` to one of `ins` that adds a new
371
+ * wire without replacing another. Used by proximity connect.
372
+ */
373
+ declare function findValidWire(outs: readonly PortInfo[], ins: readonly PortInfo[], graph: Graphs.Directed.DirectedGraph, nodeGraph: Graphs.Directed.DirectedGraph, options: ConnectionRulesOptions): {
374
+ readonly from: PortInfo;
375
+ readonly to: PortInfo;
376
+ } | undefined;
377
+ /**
378
+ * Deleting a node in the middle of a flow without breaking it: each wire that
379
+ * entered the node is reconnected to each wire's destination that left it.
380
+ *
381
+ * "Through" pairs are matched by port type: a wire arriving at an `in` port of
382
+ * type T is bridged to wires leaving an `out` port of a compatible type. Every
383
+ * new wire must also pass the normal connection rules (types, max, cycles,
384
+ * duplicates, `isValidConnection`); one that wouldn't is skipped, and a bridge
385
+ * never replaces an existing wire.
386
+ */
387
+ type BridgeDeps = {
388
+ readonly portInfo: (ref: PortRef) => PortInfo | undefined;
389
+ /** Port keys of a node, so its vertices can be removed. */
390
+ readonly portKeys: (nodeKey: string) => readonly string[];
391
+ /** Every node key (so unwired nodes stay in the node graph used for cycle checks). */
392
+ readonly nodeKeys: readonly string[];
393
+ readonly rules: ConnectionRulesOptions;
394
+ };
395
+ type BridgeResult = {
396
+ /** The port graph with the nodes removed and the bridges added. */
397
+ readonly graph: Graphs.Directed.DirectedGraph;
398
+ /** The wires that were added to keep the flow connected. */
399
+ readonly bridged: readonly GraphEdge[];
400
+ };
401
+ /**
402
+ * Removes `remove` from the graph. Nodes in `bridge` (a subset, normally the
403
+ * ones the user chose rather than a deleted group's children) are bridged first.
404
+ */
405
+ declare function removeNodes(graph: Graphs.Directed.DirectedGraph, remove: readonly string[], bridge: ReadonlySet<string>, deps: BridgeDeps): BridgeResult;
406
+ /**
407
+ * Right-click targets for the graph: a node, a wire, or the background, each
408
+ * with its own optional `ixfx-menu-trigger` (`slot="context-menu"` on a node,
409
+ * `slot="edge-menu"` / `slot="pane-menu"` on the graph).
410
+ */
411
+ type ContextKind = `node` | `edge` | `pane` | `port`;
412
+ /** The slice of `ixfx-menu-trigger` used here. */
413
+ type OpenableMenu = {
414
+ openAt: (clientX: number, clientY: number) => void;
415
+ };
416
+ type ContextTarget = {
417
+ readonly kind: ContextKind;
418
+ /** Node key or wire id. */
419
+ readonly id?: string;
420
+ /** The trigger to open, if the author provided one for this kind. */
421
+ readonly menu?: OpenableMenu;
422
+ };
423
+ /**
424
+ * Pure logic for the selection rectangle ("marquee"): geometry, hit-testing
425
+ * and the rule for which input starts one.
426
+ */
427
+ type GraphRect = {
428
+ readonly x: number;
429
+ readonly y: number;
430
+ readonly width: number;
431
+ readonly height: number;
432
+ };
433
+ /**
434
+ * What an empty-space drag does for touch / pen input (which can't hold a
435
+ * modifier key): `pan` moves the canvas, `select` draws a selection rectangle.
436
+ * Mouse always pans, and draws a rectangle only while Shift is held.
437
+ */
438
+ type TouchMode = `pan` | `select`;
439
+ /** The normalised rectangle spanning two corner points (either order). */
440
+ declare function rectFromPoints(a: Point, b: Point): GraphRect;
441
+ /** True when the rectangles overlap (touching edges don't count). */
442
+ declare function rectsIntersect(a: GraphRect, b: GraphRect): boolean;
443
+ /** Keys of the nodes whose rectangle overlaps `area` at all (partial overlap selects). */
444
+ declare function nodesInRect(area: GraphRect, nodes: ReadonlyArray<{
445
+ readonly key: string;
446
+ readonly rect: GraphRect;
447
+ }>): string[];
448
+ /** The selection while a marquee is live: the hits, plus the starting selection when additive. */
449
+ declare function combineSelection(base: ReadonlySet<string>, hits: readonly string[], additive: boolean): Set<string>;
450
+ type MarqueeStartInput = {
451
+ readonly pointerType: string;
452
+ readonly button: number;
453
+ readonly shiftKey: boolean;
454
+ /** The press landed on empty pane background (not a node, port or wire). */
455
+ readonly onBackground: boolean;
456
+ /** Space is held, which pans from anywhere and wins over selecting. */
457
+ readonly spacePan: boolean;
458
+ readonly touchMode: TouchMode;
459
+ };
460
+ /**
461
+ * Whether a pointerdown starts a selection rectangle instead of a pan.
462
+ * Mouse needs Shift. Touch and pen follow `touchMode`, since neither has a
463
+ * modifier key to hold.
464
+ */
465
+ declare function wantsMarquee(input: MarqueeStartInput): boolean;
466
+ /** Inline style placing the rectangle (client px) inside a pane whose top-left is at `origin` (client px). */
467
+ declare function marqueeStyle(r: GraphRect, origin: Point): Record<string, string>;
468
+ /**
469
+ * Easy connect: drag from anywhere inside a node (not just a port) to start a
470
+ * wire, and drop anywhere on another node to finish one. A margin ring around
471
+ * the node stays free so the node can still be grabbed and moved.
472
+ */
473
+ /** Default width of the move-the-node margin, in flow px. */
474
+ declare const EASY_CONNECT_MARGIN = 10;
475
+ /** True when `p` is inside `rect` and at least `margin` away from every edge. */
476
+ declare function inEasyConnectZone(p: Point, rect: GraphRect, margin: number): boolean;
477
+ /**
478
+ * The port a drag from inside a node starts from: the one named `prefer`, else
479
+ * the first `out` port, else the first `in` port (drawn backwards).
480
+ */
481
+ declare function pickStartPort(ports: readonly PortInfo[], prefer?: string): PortInfo | undefined;
482
+ /**
483
+ * How wires are drawn, and how that can be steered.
484
+ *
485
+ * Per wire the path comes from, in order of priority:
486
+ * 1. an {@link EdgeRouter} result for that wire (e.g. an obstacle-avoiding router such as libavoid),
487
+ * 2. the wire's {@link EdgeHint} `waypoints`,
488
+ * 3. the wire's hint `style`, else the graph's `edge-style`.
489
+ */
490
+ /** `bezier` (default) curves out of each port; `straight` is a line; `step` is orthogonal with rounded corners. */
491
+ type EdgeStyle = `bezier` | `straight` | `step`;
492
+ type EdgeHint = {
493
+ /** Overrides the graph's `edge-style` for this wire. */
494
+ readonly style?: EdgeStyle;
495
+ /** Points (flow coordinates) the wire is drawn through, between its two ends. */
496
+ readonly waypoints?: readonly Point[];
497
+ };
498
+ /** Wire id → hint. */
499
+ type EdgeHintMap = Readonly<Record<string, EdgeHint>>;
500
+ /** One end of a wire as a router sees it. */
501
+ type RouteEnd = {
502
+ readonly node: string;
503
+ readonly port: string;
504
+ /** Port centre, flow coordinates. */
505
+ readonly pos: Point;
506
+ /** The direction the wire should leave the port in. */
507
+ readonly side: PortSide;
508
+ };
509
+ type EdgeRouteRequest = {
510
+ readonly edges: ReadonlyArray<{
511
+ readonly id: string;
512
+ readonly from: RouteEnd;
513
+ readonly to: RouteEnd;
514
+ }>;
515
+ /** The nodes the wires should go around (every node that is shown). */
516
+ readonly obstacles: ReadonlyArray<{
517
+ readonly key: string;
518
+ readonly rect: GraphRect;
519
+ }>;
520
+ };
521
+ /**
522
+ * Plugs in a router. Called synchronously whenever wires need redrawing (so keep it fast, or cache), it
523
+ * returns, for the wires it routed, the full list of points from start to end (flow coordinates). A wire it
524
+ * leaves out is drawn normally.
525
+ */
526
+ type EdgeRouter = (request: EdgeRouteRequest) => ReadonlyMap<string, readonly Point[]> | undefined;
527
+ /**
528
+ * Port states. A port is **connected** when a wire is attached. Otherwise it
529
+ * is **available** (visible, unconnected) or **potential** (unconnected and
530
+ * hidden away). `availability` is what the author or user chose for an
531
+ * unconnected port; the graph works out `connected` from the wires.
532
+ */
533
+ type PortAvailability = `available` | `potential`;
534
+ type PortStatus = `connected` | `available` | `potential`;
535
+ /** Whether a node shows its available (unconnected) ports, or tucks them away until needed. */
536
+ type AvailablePorts = `show` | `hide`;
537
+ type PortEdge = `in` | `out`;
538
+ /**
539
+ * How a node lists its ports: `rail` puts inputs on the left edge and outputs on the right, beside the content;
540
+ * `full` lists every input and then every output as a full-width row, each with its label and description.
541
+ */
542
+ type PortListLayout = `rail` | `full`;
543
+ /**
544
+ * Which edges of a node its ports sit on: `vertical` puts them on the left and right edges (inputs left, outputs
545
+ * right); `horizontal` puts them on the top and bottom edges (inputs top, outputs bottom). The full-width list layout
546
+ * needs the left/right edges, so it is ignored when `horizontal`.
547
+ */
548
+ type PortLocation = `vertical` | `horizontal`;
549
+ /** Extends `p` in the direction `side` faces. */
550
+ declare function extendFromSide(p: {
551
+ readonly x: number;
552
+ readonly y: number;
553
+ }, side: PortSide, reach: number): {
554
+ x: number;
555
+ y: number;
556
+ };
557
+ /** Default wire tangent for a port direction: `in` faces left, `out` faces right. */
558
+ declare function defaultSide(dir: `in` | `out`, location?: PortLocation, flip?: boolean): PortSide;
559
+ /**
560
+ * SVG path data for the wire. Points are in flow coordinates; the edge layer
561
+ * draws inside the transformed viewport layer, so no further scaling applies.
562
+ */
563
+ declare function edgePath(from: {
564
+ readonly x: number;
565
+ readonly y: number;
566
+ }, fromSide: PortSide, to: {
567
+ readonly x: number;
568
+ readonly y: number;
569
+ }, toSide: PortSide): string;
570
+ /**
571
+ * The point halfway along the wire (a cubic bezier at t = 0.5), for placing a
572
+ * label. Uses the same control points as {@link edgePath}.
573
+ */
574
+ declare function edgeMidpoint(from: {
575
+ readonly x: number;
576
+ readonly y: number;
577
+ }, fromSide: PortSide, to: {
578
+ readonly x: number;
579
+ readonly y: number;
580
+ }, toSide: PortSide): {
581
+ x: number;
582
+ y: number;
583
+ };
584
+ /** Corner rounding (flow px) for wires drawn as polylines: `step`, waypoints and routed wires. */
585
+ declare const EDGE_CORNER_RADIUS = 8;
586
+ /**
587
+ * The corners of an orthogonal wire: out of the port along its side, across in an elbow halfway between,
588
+ * and into the other port along its side.
589
+ */
590
+ declare function stepPoints(from: Point, fromSide: PortSide, to: Point, toSide: PortSide): Point[];
591
+ /** SVG path through `points`, with corners rounded to at most `radius`. */
592
+ declare function polylinePath(points: readonly Point[], radius?: number): string;
593
+ /** The point halfway along a polyline, by length (where a label goes). */
594
+ declare function polylineMidpoint(points: readonly Point[]): Point;
595
+ /** Path and label position of a wire between two ports in the given style. */
596
+ declare function edgeShape(style: EdgeStyle, from: Point, fromSide: PortSide, to: Point, toSide: PortSide): {
597
+ d: string;
598
+ mid: Point;
599
+ };
600
+ /** Path and label position of a wire drawn through the given points (a router result, or waypoints). */
601
+ declare function polylineShape(points: readonly Point[]): {
602
+ d: string;
603
+ mid: Point;
604
+ };
605
+ /**
606
+ * A force-directed layout, as plain immutable steps. Nodes repel each other, links pull their two nodes to a
607
+ * target distance, a weak gravity keeps everything near the centre, and overlapping nodes are pushed apart.
608
+ * Call {@link forceStep} repeatedly (e.g. once per animation frame) and apply {@link forcePositions}.
609
+ *
610
+ * Positions are node top-left corners (as `ixfx-graph-node` `x` / `y`); the forces work on node centres.
611
+ */
612
+ type ForceNode = {
613
+ readonly key: string;
614
+ readonly x: number;
615
+ readonly y: number;
616
+ readonly width: number;
617
+ readonly height: number;
618
+ /** A pinned node doesn't move (but still pushes the others), e.g. while the user drags it. */
619
+ readonly pinned?: boolean;
620
+ };
621
+ type ForceLink = {
622
+ readonly source: string;
623
+ readonly target: string;
624
+ };
625
+ type ForceOptions = {
626
+ /** Push between every pair of nodes (strength / distance²). */
627
+ readonly repulsion: number;
628
+ /** Rest length of a link, between node centres. */
629
+ readonly linkDistance: number;
630
+ /** How hard a link pulls back to its rest length (per px of stretch). */
631
+ readonly linkStrength: number;
632
+ /** Pull of every node toward `center`. */
633
+ readonly gravity: number;
634
+ /** Fraction of velocity kept each step (0..1). Lower settles faster. */
635
+ readonly damping: number;
636
+ /** Largest movement per step, px. */
637
+ readonly maxSpeed: number;
638
+ /** Extra space kept between nodes' edges, px. */
639
+ readonly padding: number;
640
+ readonly center: Point;
641
+ };
642
+ declare const DEFAULT_FORCE_OPTIONS: ForceOptions;
643
+ type Body = ForceNode & {
644
+ readonly vx: number;
645
+ readonly vy: number;
646
+ };
647
+ type ForceState = {
648
+ readonly bodies: readonly Body[];
649
+ };
650
+ /** A resting state (no velocity) for the given nodes. */
651
+ declare function forceState(nodes: readonly ForceNode[]): ForceState;
652
+ /** One simulation step. Returns a new state; the input is not changed. */
653
+ declare function forceStep(state: ForceState, links: readonly ForceLink[], options?: Partial<ForceOptions>): ForceState;
654
+ /** Node key → top-left position, for `setNodePositions`. */
655
+ declare function forcePositions(state: ForceState): Record<string, Point>;
656
+ /** Total movement this step: near zero once the layout has settled. */
657
+ declare function forceMovement(state: ForceState): number;
658
+ /** Returns the state with one node moved to `to` and pinned (or released with `pinned: false`), e.g. while it is dragged. */
659
+ declare function forcePin(state: ForceState, key: string, to: Point | undefined, pinned?: boolean): ForceState;
660
+ /**
661
+ * `<ixfx-graph-port>` — a connectable dot on an `ixfx-graph-node`.
662
+ *
663
+ * Author as a child of `ixfx-graph-node`:
664
+ *
665
+ * ```html
666
+ * <ixfx-graph-node key="osc" label="Oscillator">
667
+ * <ixfx-graph-port key="out" dir="out" type="signal" label="Out"></ixfx-graph-port>
668
+ * <ixfx-slider-input></ixfx-slider-input>
669
+ * </ixfx-graph-node>
670
+ * ```
671
+ *
672
+ * Ports that are *direct children* are auto-assigned to the `in` / `out`
673
+ * slots (the node's left/right rails) based on `dir`. Ports nested inside
674
+ * content stay where the author put them; wires follow their measured
675
+ * position, and `side` sets the wire tangent.
676
+ *
677
+ * During a wire drag the graph sets state attributes for CSS:
678
+ * `data-connect="valid|invalid"`, `data-magnet="active"` and the custom
679
+ * property `--magnet-proximity` (0..1).
680
+ *
681
+ * Per-type colours work through plain consumer CSS:
682
+ * `ixfx-graph-port[type=number] { --node-graph-port-color: … }`.
683
+ */
684
+ declare class GraphPortElement extends LitElement {
685
+ #private;
686
+ static styles: import("lit").CSSResult;
687
+ /** Unique within its node. */
688
+ key: string;
689
+ dir: PortDir;
690
+ /** Compatibility tag. Empty or `*` matches anything. */
691
+ type: string;
692
+ /** Max connections; 0 = default (1 for `in`, unlimited for `out`). */
693
+ max: number;
694
+ /** Wire tangent; empty = from `dir` (`in` → left, `out` → right). */
695
+ side: string;
696
+ /** Human-visible name, shown next to the dot. `key` is the unique identifier. */
697
+ label: string;
698
+ /** Optional longer explanation, shown in the tooltip (the label is shown there when there is none). */
699
+ description: string;
700
+ /**
701
+ * What an unconnected port is: `available` (visible) or `potential` (hidden
702
+ * away until added with the node's + button). A wired port is always
703
+ * `connected`, whatever this says. The user can change it at runtime.
704
+ */
705
+ availability: PortAvailability;
706
+ /**
707
+ * Blink rate in Hz (blinks per second). `0` (default) = steady. Works on `in`
708
+ * and `out` ports; clamped to 10 Hz. Honours `prefers-reduced-motion`.
709
+ */
710
+ blink: number;
711
+ /** `connected` (a wire is attached), else `available` or `potential` from `availability`. */
712
+ get status(): PortStatus;
713
+ /** @internal */
714
+ setConnected(connected: boolean): void;
715
+ connectedCallback(): void;
716
+ /** Effective max connections. */
717
+ get portMax(): number;
718
+ /** Effective wire tangent. */
719
+ get portSide(): PortSide;
720
+ /** Builds the `PortInfo` the rules work with. `nodeKey` comes from the graph. */
721
+ portInfo(nodeKey: string): PortInfo;
722
+ /** @internal */
723
+ setDragState(state: `valid` | `invalid` | undefined): void;
724
+ /** @internal */
725
+ setMagnet(active: boolean, proximity: number): void;
726
+ protected render(): import("lit").TemplateResult<1>;
727
+ protected firstUpdated(): void;
728
+ protected updated(changed: Map<string, unknown>): void;
729
+ }
730
+ declare global {
731
+ interface HTMLElementTagNameMap {
732
+ 'ixfx-graph-port': GraphPortElement;
733
+ }
734
+ }
735
+ /**
736
+ * `<ixfx-graph-node>` — one node on an `ixfx-graph-node-graph` canvas.
737
+ *
738
+ * Light-DOM child of the graph, slotted into its transformed viewport layer.
739
+ * Position (`x` / `y`, flow coordinates) is applied as a CSS transform on the
740
+ * host. Arbitrary content (controls, text, markup) is authored as children;
741
+ * consumer CSS reaches it because it stays in the document scope.
742
+ *
743
+ * ```html
744
+ * <ixfx-graph-node key="osc" x="40" y="60" label="Oscillator">
745
+ * <ixfx-graph-port key="freq" dir="in" type="number" label="Freq"></ixfx-graph-port>
746
+ * <ixfx-slider-input></ixfx-slider-input>
747
+ * <ixfx-graph-port key="out" dir="out" type="signal"></ixfx-graph-port>
748
+ * </ixfx-graph-node>
749
+ * ```
750
+ *
751
+ * A `ixfx-graph-port` that is a direct child is auto-assigned to the
752
+ * `in` / `out` slot (left/right rail) from its `dir`, unless it already has
753
+ * an explicit `slot`. A port nested deeper stays in the content flow, like
754
+ * Svelte Flow's `<Handle>`.
755
+ *
756
+ * Optional features, all plain attributes:
757
+ * - `shape` (`rect` `rounded` `pill` `circle` `diamond` `hexagon`) and the
758
+ * `--graph-node-border-*` / `--graph-node-radius` variables, set on the node
759
+ * (one node) or on the graph (all nodes).
760
+ * - `drag-by="handle"`: only the grip in the title bar (or an element with
761
+ * `data-drag-handle`) starts a drag, instead of the whole node.
762
+ * - `resizable` (+ `width` / `height` / `min-*` / `max-*`): edge and corner grips.
763
+ * - `group` and `parent`: a container node, and children placed inside it.
764
+ * - `slot="toolbar"` (e.g. an `ixfx-toolbar`) shown above the selected node.
765
+ * - `slot="context-menu"` (an `ixfx-menu-trigger icon-name="ellipsis"`): shown as a three-dot
766
+ * button in the title bar, and also opened by right-click.
767
+ * - A titled node can be collapsed to just its title bar with the disclosure widget (`collapsed`).
768
+ * Collapsing a group hides its children.
769
+ *
770
+ * Fires the internal, non-composed `graph-node-geometry` event whenever its
771
+ * position, size or ports may have changed, so the graph re-renders wires.
772
+ */
773
+ declare class GraphNodeElement extends LitElement {
774
+ #private;
775
+ static styles: import("lit").CSSResult[];
776
+ /** Unique within its graph. */
777
+ key: string;
778
+ /** Flow-space position. */
779
+ x: number;
780
+ y: number;
781
+ /** Header text; omit for a headerless node. */
782
+ label: string;
783
+ /** Set by the graph, for styling. */
784
+ selected: boolean;
785
+ /**
786
+ * Key of a parent (group) node. `x` / `y` are then relative to that parent,
787
+ * which also carries this node when it moves. Ignored if it names a missing
788
+ * node or this node itself.
789
+ */
790
+ parent: string;
791
+ /** Outline: `rect` `rounded` (default) `pill` `circle` `diamond` `hexagon`. */
792
+ shape: string;
793
+ /** A container for child nodes (which name it with `parent`). */
794
+ group: boolean;
795
+ /** Keep a child within its parent's bounds while dragging (`parent`). */
796
+ extent: `` | `parent`;
797
+ /** Name of an icon from the icon registry, shown at the left of the title. */
798
+ icon: string;
799
+ /** `show` / `hide` the node's available (unconnected) ports. Empty inherits the graph's `available-ports`. */
800
+ availablePorts: `` | AvailablePorts;
801
+ /** The graph's default, set by the graph. */
802
+ inheritedAvailablePorts: AvailablePorts;
803
+ private _potential;
804
+ /** Wires to hidden children shown on a collapsed group's virtual in / out port. */
805
+ private _virtual;
806
+ private _menu;
807
+ /** Whether available ports are tucked away (own setting, else the graph's). */
808
+ get hidesAvailable(): boolean;
809
+ /** `rail` (inputs left, outputs right) or `full` (a full-width row per port). Empty inherits the graph's `port-layout`. */
810
+ portLayout: `` | PortListLayout;
811
+ /** The graph's default, set by the graph. */
812
+ inheritedPortLayout: PortListLayout;
813
+ /** `vertical` (ports on the left and right edges) or `horizontal` (top and bottom). Empty inherits the graph's `port-location`. */
814
+ portLocation: `` | PortLocation;
815
+ inheritedPortLocation: PortLocation;
816
+ /** Swaps the ends ports sit at (inputs right / bottom, outputs left / top). The graph's `flip-ports` also applies. */
817
+ flipPorts: boolean;
818
+ inheritedFlipPorts: boolean;
819
+ /** The graph's `dblclick-toggle`, set by the graph. */
820
+ inheritedDblclickToggle: boolean;
821
+ private _bodyEmpty;
822
+ private _filled;
823
+ /** Which edges the ports sit on (own setting, else the graph's). */
824
+ get effectivePortLocation(): PortLocation;
825
+ get effectiveFlipPorts(): boolean;
826
+ /** How this node lists its ports (own setting, else the graph's). Always `rail` when the ports are on the top and bottom edges. */
827
+ get effectivePortLayout(): PortListLayout;
828
+ /** Show only the title bar. Needs a `label`; the disclosure widget toggles it. */
829
+ collapsed: boolean;
830
+ /** Hide the disclosure widget, so the node can't be collapsed. */
831
+ noCollapse: boolean;
832
+ private _hasMenu;
833
+ /** Collapsed in effect: only a titled node can collapse. */
834
+ get isCollapsed(): boolean;
835
+ /** Edge and corner resize grips. */
836
+ resizable: boolean;
837
+ /** Explicit size in flow px (`0` = size to content / `--graph-node-width`). Set by resizing. */
838
+ width: number;
839
+ height: number;
840
+ minWidth: number;
841
+ minHeight: number;
842
+ /** `0` = unlimited. */
843
+ maxWidth: number;
844
+ maxHeight: number;
845
+ /** `handle`: only the title-bar grip starts a drag. Empty inherits the graph's `drag-by`. */
846
+ dragBy: `` | `node` | `handle`;
847
+ /** The graph's default, set by the graph. */
848
+ inheritedDragBy: `node` | `handle`;
849
+ /** `on` / `off` overrides the graph's `easy-connect` for this node. */
850
+ easyConnect: `` | `on` | `off`;
851
+ /** Key of the port easy-connect wires from / to (default: the first suitable port). */
852
+ connectPort: string;
853
+ /** When the `toolbar` slot shows: while `selected` (default), `always`, or on `hover`. */
854
+ toolbarVisible: `selected` | `always` | `hover`;
855
+ toolbarPosition: `top` | `bottom` | `left` | `right`;
856
+ /** Whether the title-bar grip is the only drag start (own setting, else the graph's). */
857
+ get dragsByHandle(): boolean;
858
+ /**
859
+ * True when this press may start moving the node: always for the default
860
+ * `node` mode, otherwise only from the grip (or any `data-drag-handle`).
861
+ */
862
+ startsDrag(e: Event): boolean;
863
+ /**
864
+ * Asks to show the tucked-away available ports on one side for `ms`
865
+ * (`Infinity` = until `reveal(side, 0)`); `0` withdraws the request. They are
866
+ * also shown whenever the node is selected. No effect when the node isn't
867
+ * hiding its available ports.
868
+ */
869
+ reveal(side: PortEdge, ms: number): void;
870
+ /** Opens the menu of potential ports on one side, at a client point. */
871
+ openAddMenu(side: PortEdge, x: number, y: number): Promise<void>;
872
+ /** Opens the menu for an available port (just "Hide port"), at a client point. */
873
+ openPortMenu(port: GraphPortElement, x: number, y: number): Promise<void>;
874
+ /**
875
+ * Whether a client point is over the revealed overlay of ports on one side (with a margin),
876
+ * so a wire being dragged along the list keeps it shown after leaving the node's edge strip.
877
+ */
878
+ overlayContains(side: PortEdge, client: {
879
+ x: number;
880
+ y: number;
881
+ }, margin?: number): boolean;
882
+ /** @internal */
883
+ refreshPortSlots(): void;
884
+ /** Collapses or expands the node (if it has a title) and tells the graph, which hides or shows a group's children. */
885
+ toggleCollapsed(): void;
886
+ /** @internal */
887
+ setVirtualPorts(inCount: number, outCount: number): void;
888
+ /** @internal */
889
+ setHiddenByAncestor(hidden: boolean): void;
890
+ /** @internal */
891
+ setClip(clipPath: string): void;
892
+ /** @internal */
893
+ setSize(width: number, height: number): void;
894
+ /** @internal */
895
+ setOrigin(x: number, y: number, depth: number): void;
896
+ connectedCallback(): void;
897
+ disconnectedCallback(): void;
898
+ protected firstUpdated(): void;
899
+ protected willUpdate(changed: PropertyValues<this>): void;
900
+ protected updated(changed: PropertyValues<this>): void;
901
+ protected render(): import("lit").TemplateResult<1>;
902
+ }
903
+ declare global {
904
+ interface HTMLElementTagNameMap {
905
+ 'ixfx-graph-node': GraphNodeElement;
906
+ }
907
+ }
908
+ /**
909
+ * The "magnetic" wire drag: while a wire is being pulled, nearby compatible
910
+ * ports attract it. Pure maths — screen-space distances, hysteresis and the
911
+ * smoothing that gives the wire its pulled-in feel.
912
+ */
913
+ /** A port eligible for snapping, in screen coordinates. */
914
+ type SnapCandidate = {
915
+ readonly ref: PortRef;
916
+ readonly x: number;
917
+ readonly y: number;
918
+ };
919
+ /** The port a dragged wire is currently snapped to. */
920
+ type SnapTarget = {
921
+ readonly ref: PortRef;
922
+ readonly x: number;
923
+ readonly y: number;
924
+ /** Screen distance from the pointer to the port centre. */
925
+ readonly distance: number;
926
+ };
927
+ /**
928
+ * Finds the snap target for the pointer, with hysteresis:
929
+ *
930
+ * - The nearest candidate within `snapRadius` snaps.
931
+ * - Once snapped, the wire stays on its target until the pointer leaves
932
+ * `1.5 × snapRadius` of it *or* another candidate is ≥ 25 % closer. This
933
+ * stops flicker between neighbouring ports.
934
+ *
935
+ * Returns `undefined` when nothing is snapped.
936
+ */
937
+ declare function findSnapTarget(candidates: readonly SnapCandidate[], pointer: Point, snapRadius: number, current: SnapTarget | undefined): SnapTarget | undefined;
938
+ /**
939
+ * Proximity glow for a candidate `distance` away, 0..1 within
940
+ * `2 × snapRadius` (0 at the edge of the glow zone, 1 at the port).
941
+ */
942
+ declare function proximityFactor(distance: number, snapRadius: number): number;
943
+ /**
944
+ * One smoothing step toward `goal`: exponential approach with time constant
945
+ * `tau` (ms). `tau <= 0` (e.g. `prefers-reduced-motion`) snaps immediately.
946
+ * `dtMs` is the elapsed time since the previous step.
947
+ */
948
+ declare function smoothStep(p: Point, goal: Point, dtMs: number, tau: number): Point;
949
+ /** True when `p` has effectively arrived at `goal` (sub-pixel). */
950
+ declare function settled(p: Point, goal: Point): boolean;
951
+ /**
952
+ * The selection-rectangle gesture. Dragging on empty space draws a rectangle
953
+ * and live-selects every node it overlaps; selected nodes then drag together
954
+ * (the node-drag controller already moves a whole selection).
955
+ *
956
+ * Which input starts it is decided by `wantsMarquee` (Shift for mouse,
957
+ * `touchMode` for touch/pen); this class only runs the gesture.
958
+ */
959
+ /** Movement (px) before a press becomes a rectangle, so a plain click isn't one. */
960
+ declare const MARQUEE_THRESHOLD_PX = 3;
961
+ type Selection = {
962
+ readonly nodes: ReadonlySet<string>;
963
+ readonly edges: ReadonlySet<string>;
964
+ };
965
+ type MarqueeConfig = {
966
+ readonly getPane: () => HTMLElement | undefined;
967
+ /** Client → flow coordinates. */
968
+ readonly screenToFlow: (p: Point) => Point;
969
+ /** Every node's rectangle in flow coordinates. */
970
+ readonly getNodeRects: () => ReadonlyArray<{
971
+ readonly key: string;
972
+ readonly rect: GraphRect;
973
+ }>;
974
+ readonly getSelection: () => Selection;
975
+ readonly applySelection: (nodes: ReadonlySet<string>, edges: ReadonlySet<string>) => void;
976
+ /** Re-render (the rectangle changed). */
977
+ readonly invalidate: () => void;
978
+ };
979
+ declare class MarqueeController {
980
+ #private;
981
+ constructor(config: MarqueeConfig);
982
+ /** The pointer that owns the gesture, or `null` when idle. */
983
+ get pointerId(): number | null;
984
+ /** The rectangle in **client** px while it is being drawn, else `undefined`. */
985
+ get clientRect(): GraphRect | undefined;
986
+ /** `additive` keeps the existing selection and adds to it. */
987
+ begin(e: PointerEvent, additive: boolean): void;
988
+ onPointerMove(e: PointerEvent): void;
989
+ onPointerUp(e: PointerEvent): boolean;
990
+ onPointerCancel(e: PointerEvent): boolean;
991
+ /** Cancels the gesture and puts the selection back as it was. */
992
+ abort(): void;
993
+ /**
994
+ * A second finger arrived: cancel the selection and hand the first pointer
995
+ * over so the caller can start a pinch. `undefined` when not active.
996
+ */
997
+ cancelForPinch(): {
998
+ readonly pointerId: number;
999
+ readonly point: Point;
1000
+ } | undefined;
1001
+ }
1002
+ /**
1003
+ * Dragging nodes: a 3 px press threshold turns into a move of the whole
1004
+ * selection. Node positions are applied by the host element (`applyMoves`);
1005
+ * no layout is read during the drag — the viewport transform handles it.
1006
+ *
1007
+ * Pointer capture is taken on the pane only once the threshold passes, so
1008
+ * plain clicks on node content keep working.
1009
+ *
1010
+ * Selection itself (click / shift-click) is the host element's job; this
1011
+ * controller only moves.
1012
+ */
1013
+ /** Drag activation threshold in screen px. */
1014
+ declare const DRAG_THRESHOLD_PX = 3;
1015
+ type NodeDragConfig = {
1016
+ readonly getPane: () => HTMLElement | undefined;
1017
+ readonly getViewport: () => Viewport;
1018
+ /** Applies positions and re-renders wires. Returns what was actually applied (e.g. after clamping to a parent). */
1019
+ readonly applyMoves: (moves: readonly NodeMove[]) => readonly NodeMove[];
1020
+ /** Per-frame event while dragging. */
1021
+ readonly emitMove: (moves: readonly NodeMove[]) => void;
1022
+ /** Committed on release. */
1023
+ readonly emitMoved: (moves: readonly NodeMove[]) => void;
1024
+ readonly setGestureActive: (active: boolean) => void;
1025
+ /** A real drag finished (`committed`) or was cancelled. Not called for a click that never became a drag. */
1026
+ readonly onEnd?: (committed: boolean) => void;
1027
+ };
1028
+ /** A node the controller may move: its key and live flow position. */
1029
+ type DraggableNode = {
1030
+ readonly key: string;
1031
+ x: number;
1032
+ y: number;
1033
+ };
1034
+ declare class NodeDragController {
1035
+ #private;
1036
+ constructor(config: NodeDragConfig);
1037
+ get dragging(): boolean;
1038
+ /**
1039
+ * Tracks a potential drag of the given selection. The host applies
1040
+ * selection before calling this; the whole selection moves together.
1041
+ */
1042
+ onPointerDown(e: PointerEvent, selection: readonly DraggableNode[]): boolean;
1043
+ onPointerMove(e: PointerEvent): void;
1044
+ /** @returns `true` to keep the gesture alive — always `false`. */
1045
+ onPointerUp(e: PointerEvent): boolean;
1046
+ onPointerCancel(e: PointerEvent): boolean;
1047
+ }
1048
+ /**
1049
+ * DOM measuring of port positions, cached per node.
1050
+ *
1051
+ * A port offset is the port's centre relative to its node's top-left corner,
1052
+ * in **flow** coordinates. Because we divide measured screen distances by the
1053
+ * zoom level, offsets are zoom-independent — node drags and zoom changes do
1054
+ * not invalidate them, only node resizes and port add/remove do.
1055
+ */
1056
+ type PortOffset = {
1057
+ /** Port key within the node. */
1058
+ readonly key: string;
1059
+ readonly x: number;
1060
+ readonly y: number;
1061
+ /** Wire tangent at this port. */
1062
+ readonly side: PortSide;
1063
+ };
1064
+ /**
1065
+ * Where a port sits on a collapsed node, which shows only its title bar: the
1066
+ * middle of the edge its wire leaves from.
1067
+ */
1068
+ declare function collapsedOffset(side: PortSide, width: number, height: number): {
1069
+ x: number;
1070
+ y: number;
1071
+ };
1072
+ type NodeLayout = {
1073
+ /** Port offsets by port key. */
1074
+ readonly offsets: ReadonlyMap<string, PortOffset>;
1075
+ readonly width: number;
1076
+ readonly height: number;
1077
+ };
1078
+ /**
1079
+ * Measures one node: the flow-space offsets of every `ixfx-graph-port`
1080
+ * descendant, plus the node's flow-space size. `zoom` is the current viewport
1081
+ * zoom; rects are measured in screen px and divided out.
1082
+ */
1083
+ declare function measureNode(node: HTMLElement, zoom: number): NodeLayout;
1084
+ /**
1085
+ * Add-node-on-drop: when a wire is released on empty space, an opt-in
1086
+ * `nodeFactory` makes a new node there, and it is wired to the dragged port.
1087
+ */
1088
+ type NodeFactoryContext = {
1089
+ /** The port the wire was dragged from. */
1090
+ readonly from: PortInfo;
1091
+ /** Drop point in flow coordinates. */
1092
+ readonly point: Point;
1093
+ /** Drop point in client coordinates. */
1094
+ readonly client: Point;
1095
+ };
1096
+ /**
1097
+ * Returns the node to add (a detached `ixfx-graph-node` with a unique `key` and
1098
+ * its ports as children), or `null` / `undefined` to add nothing.
1099
+ */
1100
+ type NodeFactory = (context: NodeFactoryContext) => GraphNodeElement | null | undefined;
1101
+ type AddedNode = {
1102
+ readonly node: GraphNodeElement;
1103
+ /** The wire made to the dragged port, if a compatible port was found. */
1104
+ readonly connectedTo?: PortRef;
1105
+ };
1106
+ /**
1107
+ * Styles for `ixfx-node-graph`: host, pane (dot background), transformed
1108
+ * viewport layer and the SVG edge layer. Node chrome lives in
1109
+ * `graph-node.ts`, port chrome in `graph-port.ts`.
1110
+ */
1111
+ declare const nodeGraphStyles: import("lit").CSSResult;
1112
+ /**
1113
+ * ReactiveController for pan and zoom gestures on the graph pane:
1114
+ *
1115
+ * - **Pan** — drag on the empty pane (primary button), the middle button
1116
+ * anywhere, or Space + drag. Pointer capture is on the pane.
1117
+ * - **Zoom** — wheel (mode `zoom`), ctrl/cmd + wheel (trackpad pinch), or a
1118
+ * two-pointer touch pinch. In mode `pan`, the wheel pans and ctrl/pinch
1119
+ * zooms.
1120
+ * - Escape hatches (as in Svelte Flow): elements with `data-nowheel` opt out
1121
+ * of wheel handling; native form controls and `[contenteditable]` never
1122
+ * have their wheel intercepted.
1123
+ */
1124
+ type WheelMode = `zoom` | `pan`;
1125
+ type PanZoomConfig = {
1126
+ readonly getPane: () => HTMLElement | undefined;
1127
+ readonly getPaneRect: () => DOMRect | undefined;
1128
+ readonly getViewport: () => Viewport;
1129
+ /** Commit a viewport change; the element handles events and rendering. */
1130
+ readonly setViewport: (v: Viewport) => void;
1131
+ readonly getMinZoom: () => number;
1132
+ readonly getMaxZoom: () => number;
1133
+ readonly getWheelMode: () => WheelMode;
1134
+ /** True when the wheel event started on a target that opted out (`data-nowheel`, form controls). Scans the event's `composedPath`, so light-DOM content inside nodes is seen. */
1135
+ readonly isWheelBlockedTarget: (e: WheelEvent) => boolean;
1136
+ /** Gestures enable `will-change: transform` on the layer. */
1137
+ readonly setGestureActive: (active: boolean) => void;
1138
+ /** Toggled while Space is held, for the grab cursor. */
1139
+ readonly setSpacePan: (active: boolean) => void;
1140
+ };
1141
+ /** True when `target` is a native form control or contenteditable element. */
1142
+ declare function isEditableTarget(target: EventTarget | null | undefined): boolean;
1143
+ declare class PanZoomController implements ReactiveController {
1144
+ #private;
1145
+ constructor(host: ReactiveControllerHost, config: PanZoomConfig);
1146
+ hostDisconnected(): void;
1147
+ /** Space starts a pan-from-anywhere. Returns `true` when the key was used. */
1148
+ onKeyDown(e: KeyboardEvent): boolean;
1149
+ /** Releasing Space (or leaving the pane, with `key` omitted) ends it. */
1150
+ onKeyUp(e?: KeyboardEvent): void;
1151
+ /** True while Space is held (space + drag pans from anywhere). */
1152
+ get spacePan(): boolean;
1153
+ setSpacePan(active: boolean): void;
1154
+ /**
1155
+ * Consumes a pointerdown when it starts a pan or pinch: the pane
1156
+ * background (primary button), the middle button anywhere, or Space + drag.
1157
+ * Returns `false` to let node/wire controllers try first.
1158
+ */
1159
+ onPointerDown(e: PointerEvent): boolean;
1160
+ /**
1161
+ * Starts a pinch from a pointer another gesture already owned (`first`, e.g.
1162
+ * a cancelled touch selection) plus the pointer that just arrived.
1163
+ */
1164
+ startPinch(first: {
1165
+ readonly pointerId: number;
1166
+ readonly point: Point;
1167
+ }, second: PointerEvent): void;
1168
+ onPointerMove(e: PointerEvent): void;
1169
+ /** @returns `true` when the gesture should stay alive (multi-pointer pinch). */
1170
+ onPointerUp(e: PointerEvent): boolean;
1171
+ onPointerCancel(e: PointerEvent): boolean;
1172
+ /** A second pointer joined the gesture → pinch. */
1173
+ addPointer(e: PointerEvent): void;
1174
+ onWheel(e: WheelEvent): void;
1175
+ }
1176
+ /**
1177
+ * Edges that cross the boundary of a sub flow (a group and everything inside it).
1178
+ */
1179
+ type SubflowEdges = {
1180
+ /** Wires from a node outside into the sub flow. */
1181
+ readonly incoming: readonly GraphEdge[];
1182
+ /** Wires from the sub flow to a node outside. */
1183
+ readonly outgoing: readonly GraphEdge[];
1184
+ /** Wires with both ends inside. */
1185
+ readonly internal: readonly GraphEdge[];
1186
+ };
1187
+ /**
1188
+ * Per-wire "data is flowing" animation state.
1189
+ *
1190
+ * Animation is presentation, not topology, so it is not stored on the graph
1191
+ * (the ixfx graph's edges carry no data). It is a plain `edgeId → speed` map
1192
+ * held by the element. Edge ids are deterministic (`edgeId(from, to)`), so an
1193
+ * entry survives a wire being removed and re-created.
1194
+ */
1195
+ /** Wire id → flow speed in flow-space px/second. Missing, 0 or invalid = no animation. */
1196
+ type WireFlowMap = Readonly<Record<string, number>>;
1197
+ /** Upper bound, so a bad value can't produce an unreadable strobe. */
1198
+ declare const MAX_FLOW_SPEED = 2000;
1199
+ /** Returns a usable speed (`0` = off) from anything a consumer might pass. */
1200
+ declare function sanitizeFlowSpeed(value: unknown): number;
1201
+ /**
1202
+ * Immutably sets (or, for `undefined` / `0`, clears) one wire's speed. Returns
1203
+ * the same object when nothing changes, so identity checks stay cheap.
1204
+ */
1205
+ declare function withWireFlow(map: WireFlowMap, id: string, speed: number | undefined): WireFlowMap;
1206
+ /**
1207
+ * Immutably sets (or, for `undefined`, clears) one entry of a wire-keyed map: labels, hints and
1208
+ * the like. Returns a new object.
1209
+ */
1210
+ declare function withEntry<T>(map: Readonly<Record<string, T>>, id: string, value: T | undefined): Readonly<Record<string, T>>;
1211
+ type WirePath = {
1212
+ readonly id: string;
1213
+ readonly d: string;
1214
+ readonly selected: boolean;
1215
+ /** Halfway along the wire, where its label goes. */
1216
+ readonly mid: {
1217
+ readonly x: number;
1218
+ readonly y: number;
1219
+ };
1220
+ };
1221
+ /** Wire id → label text. Missing or empty = no label. */
1222
+ type EdgeLabelMap = Readonly<Record<string, string>>;
1223
+ /**
1224
+ * The SVG content of the edge layer: each wire, its hit area, an optional
1225
+ * flow overlay and label, the in-progress wire preview, and the proximity-connect preview.
1226
+ *
1227
+ * The flow overlay is a dashed copy of the wire whose `stroke-dashoffset` is
1228
+ * animated by CSS (no per-frame JS). Speed is set per wire through the
1229
+ * `--_flow-speed` custom property (flow-space px/s).
1230
+ */
1231
+ declare function renderWires(paths: readonly WirePath[], flow: WireFlowMap, labels: EdgeLabelMap, detachedId: string | undefined, preview: string | undefined, proximity?: string | undefined): import("lit").TemplateResult<2>;
1232
+ /**
1233
+ * How wires are drawn, per graph and per wire: flow animation, labels, style, hints and an optional router.
1234
+ * (The base of `ixfx-node-graph`, which redraws its wires when these change.)
1235
+ */
1236
+ declare abstract class EdgeDrawingElement extends LitElement {
1237
+ /**
1238
+ * Wires that show data flowing: wire id → speed in flow-space px/second
1239
+ * (missing or 0 = static). Animated in CSS, not JS. Ids are `edgeId(from, to)`;
1240
+ * prefer `setWireFlow()` for a single wire.
1241
+ */
1242
+ wireFlow: WireFlowMap;
1243
+ /** Wire id → label text, drawn at the middle of the wire. Prefer `setEdgeLabel()` for one wire. */
1244
+ edgeLabels: EdgeLabelMap;
1245
+ /** How wires are drawn: `bezier` (default), `straight`, or `step` (orthogonal). A wire's own hint wins. */
1246
+ edgeStyle: EdgeStyle;
1247
+ /** Per-wire drawing hints (wire id → `{ style?, waypoints? }`). Prefer `setEdgeHint()` for one wire. */
1248
+ edgeHints: EdgeHintMap;
1249
+ /** Opt-in router (e.g. libavoid) that returns the points of each wire; it wins over hints and `edge-style`. */
1250
+ edgeRouter: EdgeRouter | undefined;
1251
+ /** Starts, retunes (`speed` px/s) or stops (`0` / `undefined`) one wire's flow animation. */
1252
+ setWireFlow(id: string, speed: number | undefined): void;
1253
+ /** Sets or clears (`''` / `undefined`) one wire's label. */
1254
+ setEdgeLabel(id: string, text: string | undefined): void;
1255
+ /** Sets or clears one wire's drawing hint (`style`, `waypoints`). */
1256
+ setEdgeHint(id: string, hint: EdgeHint | undefined): void;
1257
+ }
1258
+ declare class NodeGraphElement extends EdgeDrawingElement {
1259
+ #private;
1260
+ static styles: import("lit").CSSResult[];
1261
+ /**
1262
+ * Wires. Plain immutable objects; internally backed by the port graph.
1263
+ * Replaced immutably on every change, so identity can be compared.
1264
+ */
1265
+ get edges(): readonly GraphEdge[];
1266
+ set edges(value: readonly GraphEdge[]);
1267
+ /** Pan/zoom state of the viewport layer. */
1268
+ viewport: Viewport;
1269
+ minZoom: number;
1270
+ maxZoom: number;
1271
+ /** `zoom`: the wheel zooms (ctrl/pinch always zooms). `pan`: the wheel pans. */
1272
+ wheel: WheelMode;
1273
+ /** Snap radius for the magnet, in **screen** px. */
1274
+ snapRadius: number;
1275
+ /** Allow a node to connect to itself. */
1276
+ allowSelf: boolean;
1277
+ /** Reject connections that would create a cycle (node level). */
1278
+ acyclic: boolean;
1279
+ /**
1280
+ * What a touch/pen drag on empty space does: `pan` (default) moves the
1281
+ * canvas, `select` draws a selection rectangle. Mouse always pans; hold
1282
+ * Shift to draw a rectangle instead.
1283
+ */
1284
+ touchMode: TouchMode;
1285
+ /** Extra veto hook, on top of the built-in rules. */
1286
+ isValidConnection: IsValidConnection | undefined;
1287
+ /** What starts moving a node: `node` (anywhere on it) or `handle` (only its title-bar grip). A node's own `drag-by` wins. */
1288
+ dragBy: `node` | `handle`;
1289
+ /** `show` or `hide` the available (unconnected) ports of every node; a node's own `available-ports` wins. Hidden ones are revealed when a wire is dragged toward them, on tap, or on mouse hover. */
1290
+ availablePorts: AvailablePorts;
1291
+ /** `rail`: inputs on the left edge, outputs on the right. `full`: every input then every output as a full-width row with its label and description. A node's own `port-layout` wins. */
1292
+ portLayout: PortListLayout;
1293
+ /** `vertical`: ports on the left (in) and right (out) edges. `horizontal`: top (in) and bottom (out); `full` layout is then ignored. A node's own `port-location` wins. */
1294
+ portLocation: PortLocation;
1295
+ /** Swaps the ends ports sit at (inputs right / bottom, outputs left / top), for flows that run right-to-left or bottom-to-top. A node's `flip-ports` also turns it on. */
1296
+ flipPorts: boolean;
1297
+ /** Double-clicking a node's title bar collapses or expands it. Off by default; the `graph-node-title-dblclick` event fires either way, and `preventDefault()` on it stops the toggle. */
1298
+ dblclickToggle: boolean;
1299
+ /** Drag from anywhere inside a node (not just a port) to start a wire, and drop anywhere on a node to finish one. */
1300
+ easyConnect: boolean;
1301
+ /** Width (flow px) of the margin ring that still moves a node when `easy-connect` is on. */
1302
+ easyConnectMargin: number;
1303
+ /** While dragging a node, offer a wire to the nearest node within this many flow px (`0` = off); it is made on drop. */
1304
+ proximityConnect: number;
1305
+ /** Deleting a node in the middle of a flow reconnects what fed it to what it fed, where the rules allow. */
1306
+ bridgeOnDelete: boolean;
1307
+ /** Opt-in: called when a wire is dropped on empty space. Return a node to add there (it is wired to the dragged port). */
1308
+ nodeFactory: NodeFactory | undefined;
1309
+ private _gestureActive;
1310
+ private _spacePan;
1311
+ constructor();
1312
+ connectedCallback(): void;
1313
+ disconnectedCallback(): void;
1314
+ protected firstUpdated(): void;
1315
+ protected willUpdate(changed: Map<PropertyKey, unknown>): void;
1316
+ protected updated(changed: Map<PropertyKey, unknown>): void;
1317
+ protected render(): import("lit").TemplateResult<1>;
1318
+ /** Plain-JSON snapshot of nodes (from the DOM) and edges. */
1319
+ getGraph(): {
1320
+ nodes: {
1321
+ key: string;
1322
+ x: number;
1323
+ y: number;
1324
+ label: string;
1325
+ ports: PortInfo[];
1326
+ }[];
1327
+ edges: readonly GraphEdge[];
1328
+ };
1329
+ /** The port graph (source of truth for wires). Vertices are ports. */
1330
+ getPortGraph(): Graphs.Directed.DirectedGraph;
1331
+ /**
1332
+ * The node graph: the port graph collapsed to node keys. Directly usable
1333
+ * as a `Graphs.Dependencies.DependencyGraph` (edges point in the direction
1334
+ * of data flow) for topological order, cycle detection and evaluation.
1335
+ */
1336
+ getNodeGraph(): Graphs.Directed.DirectedGraph;
1337
+ /**
1338
+ * Connects two ports (either order), running the same rules and events as
1339
+ * a wire drag. Returns `false` when rejected or cancelled.
1340
+ */
1341
+ connect(from: PortRef, to: PortRef): boolean;
1342
+ /** Removes a wire by id, firing the same events as a drag. */
1343
+ disconnect(id: string): boolean;
1344
+ /**
1345
+ * The wires that cross a sub flow's boundary: `incoming` from nodes outside, `outgoing` to nodes
1346
+ * outside, and the `internal` ones. The sub flow is the group node and everything inside it.
1347
+ */
1348
+ getSubflowEdges(groupKey: string): SubflowEdges;
1349
+ /** Every shown node's rectangle in flow coordinates, for layout algorithms. */
1350
+ getNodeRects(): Array<{
1351
+ key: string;
1352
+ rect: GraphRect;
1353
+ }>;
1354
+ /** Moves nodes (`x` / `y` as set on the node: relative to its parent) in one go, e.g. from a layout. Fires no events. */
1355
+ setNodePositions(positions: Readonly<Record<string, Point>>): void;
1356
+ /** What the last right-click landed on (valid while its menu is open, for handling `menu-command`). */
1357
+ get contextTarget(): GraphContextMenuDetail | undefined;
1358
+ /** Sets the viewport (zoom is clamped) and emits `graph-viewport-change`. */
1359
+ setViewport(v: Viewport): void;
1360
+ /** Pans/zooms so all nodes fit, with `padding` px margin. */
1361
+ fitView(padding?: number): void;
1362
+ /** Client point → flow coordinates. */
1363
+ screenToFlow(p: Point): Point;
1364
+ /** Flow point → client coordinates. */
1365
+ flowToScreen(p: Point): Point;
1366
+ getViewState(options?: GetViewStateOptions<NodeGraphViewState>): NodeGraphViewState;
1367
+ setViewState(state: NodeGraphViewState | null | undefined): void;
1368
+ /** No pending facets: selection and viewport are always live. */
1369
+ clearPendingViewState(): void;
1370
+ }
1371
+ declare global {
1372
+ interface HTMLElementTagNameMap {
1373
+ 'ixfx-node-graph': NodeGraphElement;
1374
+ }
1375
+ }
1376
+ interface NodeGraphElement {
1377
+ addEventListener<K extends keyof NodeGraphElementEventMap>(type: K, listener: (this: NodeGraphElement, event: NodeGraphElementEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
1378
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
1379
+ }
1380
+ /** Which edge or corner of a node is being dragged. */
1381
+ type ResizeHandle = `n` | `s` | `e` | `w` | `ne` | `nw` | `se` | `sw`;
1382
+ declare const RESIZE_HANDLES: readonly ResizeHandle[];
1383
+ type SizeLimits = {
1384
+ readonly minWidth: number;
1385
+ readonly minHeight: number;
1386
+ readonly maxWidth: number;
1387
+ readonly maxHeight: number;
1388
+ };
1389
+ declare function isResizeHandle(value: unknown): value is ResizeHandle;
1390
+ /**
1391
+ * The rectangle after dragging `handle` by (`dx`, `dy`) flow px, from the
1392
+ * rectangle at the start of the drag. Size is clamped to `limits`; dragging a
1393
+ * west/north edge moves the origin so the opposite edge stays put.
1394
+ */
1395
+ declare function resizeRect(start: GraphRect, handle: ResizeHandle, dx: number, dy: number, limits: SizeLimits): GraphRect;
1396
+ /**
1397
+ * Node outline shapes. `box` shapes are plain rounded rectangles and get a
1398
+ * real CSS border (any `border-style`). `polygon` shapes are clipped with
1399
+ * `clip-path`, so their border is drawn as a layered background instead
1400
+ * (solid colour only; thickness is approximate on non-square nodes).
1401
+ */
1402
+ type NodeShape = `rect` | `rounded` | `pill` | `circle` | `diamond` | `hexagon`;
1403
+ type ShapeSpec = {
1404
+ readonly kind: `box`;
1405
+ readonly radius: string;
1406
+ } | {
1407
+ readonly kind: `polygon`;
1408
+ readonly clip: string;
1409
+ };
1410
+ declare const NODE_SHAPES: Readonly<Record<NodeShape, ShapeSpec>>;
1411
+ declare function isNodeShape(value: unknown): value is NodeShape;
1412
+ /** The spec for a shape name; anything unknown (or empty) is `rounded`. */
1413
+ declare function shapeSpec(value: unknown): ShapeSpec;
1414
+ /** Fastest allowed blink, in Hz. Keeps a bad value from becoming a strobe. */
1415
+ declare const MAX_BLINK_HZ = 10;
1416
+ /**
1417
+ * Blink period in seconds for a rate in Hz, or `undefined` for "don't blink"
1418
+ * (zero, negative, non-finite or non-numeric rates). Rates above
1419
+ * `MAX_BLINK_HZ` are clamped.
1420
+ */
1421
+ declare function blinkPeriodSeconds(hz: unknown): number | undefined;
1422
+ /**
1423
+ * Proximity connect: while a node is dragged, the nearest other node within
1424
+ * `threshold` is offered as a connection, which is made on drop.
1425
+ */
1426
+ type ProximityPair = {
1427
+ readonly from: PortInfo;
1428
+ readonly to: PortInfo;
1429
+ };
1430
+ /** Distance between two rectangles' edges; `0` when they touch or overlap. */
1431
+ declare function rectGap(a: GraphRect, b: GraphRect): number;
1432
+ type ProximityQuery = {
1433
+ readonly dragged: string;
1434
+ readonly rects: ReadonlyArray<{
1435
+ readonly key: string;
1436
+ readonly rect: GraphRect;
1437
+ }>;
1438
+ /** Maximum edge-to-edge distance, in flow px. */
1439
+ readonly threshold: number;
1440
+ /** Nodes that must not be offered (e.g. the rest of the dragged selection). */
1441
+ readonly exclude?: ReadonlySet<string>;
1442
+ /** The best valid wire from one node to another, if any. */
1443
+ readonly tryPair: (fromNode: string, toNode: string) => ProximityPair | undefined;
1444
+ };
1445
+ /**
1446
+ * The wire to offer. Candidates are tried nearest first. For each, the node on
1447
+ * the left is the source (data flows left to right), then the other way round.
1448
+ */
1449
+ declare function findProximityConnection(q: ProximityQuery): ProximityPair | undefined;
1450
+ /**
1451
+ * Pure selection transitions for pointer presses and clicks.
1452
+ * Each returns the next selection, or `undefined` when nothing changes.
1453
+ */
1454
+ type SelectionState = {
1455
+ readonly nodes: ReadonlySet<string>;
1456
+ readonly edges: ReadonlySet<string>;
1457
+ };
1458
+ /**
1459
+ * Pressing a node. `toggle` (Shift/Ctrl/Cmd) flips it in the selection; a
1460
+ * plain press on an unselected node selects only it. A plain press on an
1461
+ * already-selected node keeps the selection so the whole group can be dragged.
1462
+ */
1463
+ declare function selectionAfterNodePress(current: SelectionState, key: string, toggle: boolean): SelectionState | undefined;
1464
+ /** Clicking a wire: `toggle` flips it; otherwise it becomes the only selection. */
1465
+ declare function selectionAfterEdgeClick(current: SelectionState, edgeId: string, toggle: boolean): SelectionState;
1466
+ /**
1467
+ * Pure pan/zoom coordinate maths for the node graph.
1468
+ *
1469
+ * Flow coordinates are the stable coordinate space of the canvas: node `x`/`y`
1470
+ * and port offsets live there. Screen coordinates are client (page) px. The
1471
+ * mapping needs the pane's client-space origin (its top-left corner).
1472
+ *
1473
+ * `flow = (client − paneOrigin − viewport) / zoom`
1474
+ */
1475
+ /** Client-space top-left corner of the pane element (`DOMRect.left/top`). */
1476
+ type PaneOrigin = Point;
1477
+ declare function clampZoom(zoom: number, minZoom: number, maxZoom: number): number;
1478
+ /** Client point → flow point. */
1479
+ declare function screenToFlow(p: Point, v: Viewport, paneOrigin: PaneOrigin): Point;
1480
+ /** Flow point → client point. */
1481
+ declare function flowToScreen(p: Point, v: Viewport, paneOrigin: PaneOrigin): Point;
1482
+ /**
1483
+ * Zooms by `factor` keeping the flow point under `screenPoint` fixed on
1484
+ * screen — the canvas appears to grow out of the cursor. Result is clamped
1485
+ * to `[minZoom, maxZoom]`; when the clamp bites, the anchor point may drift
1486
+ * slightly rather than jump.
1487
+ */
1488
+ declare function zoomAt(v: Viewport, screenPoint: Point, paneOrigin: PaneOrigin, factor: number, minZoom: number, maxZoom: number): Viewport;
1489
+ /** Pans by a screen-space delta. */
1490
+ declare function panBy(v: Viewport, dx: number, dy: number): Viewport;
1491
+ /** Zoom clamping and margin for `fitBounds`. */
1492
+ type FitInput = {
1493
+ readonly padding: number;
1494
+ readonly minZoom: number;
1495
+ readonly maxZoom: number;
1496
+ };
1497
+ /**
1498
+ * Computes the viewport that centres `bounds` (flow-space rectangle) in a
1499
+ * pane of the given client size, with `padding` px of margin.
1500
+ */
1501
+ declare function fitBounds(bounds: {
1502
+ readonly x: number;
1503
+ readonly y: number;
1504
+ readonly width: number;
1505
+ readonly height: number;
1506
+ }, paneWidth: number, paneHeight: number, options: FitInput): Viewport;
1507
+ /** Smallest flow-space rectangle covering all the given rects. */
1508
+ declare function boundsOf(rects: ReadonlyArray<{
1509
+ readonly x: number;
1510
+ readonly y: number;
1511
+ readonly width: number;
1512
+ readonly height: number;
1513
+ }>): {
1514
+ x: number;
1515
+ y: number;
1516
+ width: number;
1517
+ height: number;
1518
+ };
1519
+ /** The viewport that frames all `rects` in a pane of the given size (see {@link fitBounds}). */
1520
+ declare function fitRects(rects: ReadonlyArray<{
1521
+ readonly x: number;
1522
+ readonly y: number;
1523
+ readonly width: number;
1524
+ readonly height: number;
1525
+ }>, pane: {
1526
+ readonly width: number;
1527
+ readonly height: number;
1528
+ }, options: FitInput): Viewport;
1529
+ /** The slice of `GraphPortElement` the controller drives (structural, for tests). */
1530
+ interface WireDragPort {
1531
+ setDragState(state: `valid` | `invalid` | undefined): void;
1532
+ setMagnet(active: boolean, proximity: number): void;
1533
+ }
1534
+ type WireDragMode = `new` | `new-backward` | `detach`;
1535
+ /** A port eligible for snapping, in flow coordinates. */
1536
+ type ValidCandidate = {
1537
+ readonly info: PortInfo;
1538
+ readonly el: WireDragPort;
1539
+ readonly pos: Point;
1540
+ };
1541
+ type DragState = {
1542
+ readonly mode: WireDragMode;
1543
+ readonly pointerId: number;
1544
+ /** The fixed end of the preview wire. */
1545
+ readonly anchor: PortInfo;
1546
+ /** Anchor port centre, in flow coordinates. */
1547
+ readonly anchorFlow: Point;
1548
+ /** The picked-up wire (detach mode only). */
1549
+ readonly originalEdge: GraphEdge | undefined;
1550
+ /** Port graph the drag commits into (original edge removed when detaching). */
1551
+ readonly workingGraph: Graphs.Directed.DirectedGraph;
1552
+ readonly context: ConnectContext;
1553
+ /** Valid candidates that have a position, in flow coordinates. */
1554
+ candidates: ValidCandidate[];
1555
+ /** Every port the rules allow, positioned or not (tucked-away ports gain a position when revealed). */
1556
+ readonly valid: ReadonlyArray<{
1557
+ readonly info: PortInfo;
1558
+ readonly el: WireDragPort;
1559
+ }>;
1560
+ /** Every port element marked at drag start, for cleanup. */
1561
+ readonly markedPorts: readonly WireDragPort[];
1562
+ /** Ports with a proximity glow set, cleared when they fall out of range. */
1563
+ readonly glowing: Set<WireDragPort>;
1564
+ pointerFlow: Point;
1565
+ client: Point;
1566
+ readonly startClient: Point;
1567
+ /** True once the pointer has moved past the drag threshold. */
1568
+ moved: boolean;
1569
+ goal: Point;
1570
+ rendered: Point;
1571
+ snap: SnapTarget | undefined;
1572
+ };
1573
+ type WireDragConfig = {
1574
+ readonly getPane: () => HTMLElement | undefined;
1575
+ readonly getPaneRect: () => DOMRect | undefined;
1576
+ readonly getViewport: () => Viewport;
1577
+ readonly getSnapRadius: () => number;
1578
+ readonly getRulesOptions: () => ConnectionRulesOptions;
1579
+ readonly getPortGraph: () => Graphs.Directed.DirectedGraph;
1580
+ readonly getNodeGraph: () => Graphs.Directed.DirectedGraph;
1581
+ readonly getAcyclic: () => boolean;
1582
+ /** Port info for a port element, resolving its node key. */
1583
+ readonly portInfoFor: (portEl: WireDragPort & {
1584
+ readonly key?: string;
1585
+ }) => PortInfo | undefined;
1586
+ /** Port info for a port ref, for edges' stored refs. */
1587
+ readonly portInfoForSource: (ref: PortRef) => PortInfo | undefined;
1588
+ /** Every port element in the graph. */
1589
+ readonly allPorts: () => readonly WireDragPort[];
1590
+ /** Cached port offset (flow, node-relative) for a node/port pair. */
1591
+ readonly portOffset: (nodeKey: string, portKey: string) => PortOffset | undefined;
1592
+ /** Node position (flow) of a node key. */
1593
+ readonly nodePosition: (nodeKey: string) => Point | undefined;
1594
+ readonly setGestureActive: (active: boolean) => void;
1595
+ /** Commits a new port graph and fires `graph-edges-change`. */
1596
+ readonly commitEdges: (g: Graphs.Directed.DirectedGraph) => void;
1597
+ /** Cancelable `graph-connect`. Returns `false` when cancelled. */
1598
+ readonly requestConnect: (edge: GraphEdge, replaces: GraphEdge | undefined) => boolean;
1599
+ /** Cancelable `graph-disconnect`. Returns `false` when cancelled. */
1600
+ readonly requestDisconnect: (edges: readonly GraphEdge[], reason: DisconnectReason) => boolean;
1601
+ /** `graph-connect-end`: a wire dropped on empty space. */
1602
+ readonly emitConnectEnd: (from: PortRef, point: Point, client: Point) => void;
1603
+ /** Easy connect: dropping anywhere on a node snaps to its best valid port. */
1604
+ readonly getEasyConnect?: () => boolean;
1605
+ /** Called with the pointer (flow coordinates) on every move of a wire drag, so tucked-away ports near it can be revealed. */
1606
+ readonly onApproach?: (p: Point, client: Point) => void;
1607
+ /** The topmost node containing a flow-space point. */
1608
+ readonly nodeAt?: (p: Point) => string | undefined;
1609
+ /** Re-render (preview path / edge visibility). */
1610
+ readonly invalidate: () => void;
1611
+ readonly prefersReducedMotion: () => boolean;
1612
+ };
1613
+ declare class WireDragController {
1614
+ #private;
1615
+ constructor(config: WireDragConfig);
1616
+ /** While a wire is being dragged: its state, else `undefined`. */
1617
+ get state(): Readonly<DragState> | undefined;
1618
+ /** Edge id hidden while its loose end is dragged (detach mode). */
1619
+ get detachedEdgeId(): string | undefined;
1620
+ /** Preview path (flow coordinates), or `undefined` when not dragging. */
1621
+ get previewPath(): string | undefined;
1622
+ get active(): boolean;
1623
+ onPointerDown(e: PointerEvent, info: PortInfo): boolean;
1624
+ /** Call after a port was revealed or moved: positions are measured again, so newly visible ports become targets. */
1625
+ relayout(): void;
1626
+ onPointerMove(e: PointerEvent): void;
1627
+ onPointerUp(e: PointerEvent): boolean;
1628
+ onPointerCancel(_e: PointerEvent): boolean;
1629
+ /** Escape pressed mid-drag: abort, restoring a detached wire. */
1630
+ abort(): void;
1631
+ }
1632
+ export { measureNode as $, GraphNodeMovedEvent as $n, ContextKind as $t, ResizeHandle as A, ConnectionCheckReason as An, extendFromSide as At, withEntry as B, GraphDisconnectDetail as Bn, RouteEnd as Bt, blinkPeriodSeconds as C, parsePortId as Cn, forceState as Ct, isNodeShape as D, toEdges as Dn, edgeMidpoint as Dt, ShapeSpec as E, removeWire as En, defaultSide as Et, WirePath as F, GraphConnectEvent as Fn, EdgeHint as Ft, isEditableTarget as G, GraphNodeAddedDetail as Gn, MarqueeStartInput as Gt, PanZoomConfig as H, GraphEdge as Hn, inEasyConnectZone as Ht, renderWires as I, GraphContextMenuDetail as In, EdgeHintMap as It, NodeFactory as J, GraphNodeCollapseEvent as Jn, marqueeStyle as Jt, nodeGraphStyles as K, GraphNodeAddedEvent as Kn, TouchMode as Kt, MAX_FLOW_SPEED as L, GraphContextMenuEvent as Ln, EdgeRouteRequest as Lt, isResizeHandle as M, GraphConnectDetail as Mn, polylinePath as Mt, resizeRect as N, GraphConnectEndDetail as Nn, polylineShape as Nt, shapeSpec as O, toNodeGraph as On, edgePath as Ot, NodeGraphElement as P, GraphConnectEndEvent as Pn, stepPoints as Pt, collapsedOffset as Q, GraphNodeMoveEvent as Qn, wantsMarquee as Qt, WireFlowMap as R, GraphDeleteDetail as Rn, EdgeRouter as Rt, MAX_BLINK_HZ as S, outDegreeOf as Sn, forcePositions as St, NodeShape as T, removeNode as Tn, EDGE_CORNER_RADIUS as Tt, PanZoomController as U, GraphEdgesChangeDetail as Un, pickStartPort as Ut, withWireFlow as V, GraphDisconnectEvent as Vn, EASY_CONNECT_MARGIN as Vt, WheelMode as W, GraphEdgesChangeEvent as Wn, GraphRect as Wt, NodeLayout as X, GraphNodeGeometryEvent as Xn, rectFromPoints as Xt, NodeFactoryContext as Y, GraphNodeGeometryDetail as Yn, nodesInRect as Yt, PortOffset as Z, GraphNodeMoveDetail as Zn, rectsIntersect as Zt, selectionAfterNodePress as _, fromEdges as _n, PortInfo as _r, ForceNode as _t, FitInput as a, ConnectionRulesOptions as an, GraphPortChangeEvent as ar, MarqueeConfig as at, findProximityConnection as b, incomingEdge as bn, Viewport as br, forceMovement as bt, clampZoom as c, normaliseConnection as cn, GraphSnapshot as cr, SnapTarget as ct, flowToScreen as d, ConnectContext as dn, NodeGraphElementEventMap as dr, settled as dt, ContextTarget as en, GraphNodeResizeDetail as er, DRAG_THRESHOLD_PX as et, panBy as f, addWire as fn, NodeGraphViewState as fr, smoothStep as ft, selectionAfterEdgeClick as g, ensurePort as gn, PortDir as gr, ForceLink as gt, SelectionState as h, edgeId as hn, PointerGesture as hr, DEFAULT_FORCE_OPTIONS as ht, WireDragPort as i, removeNodes as in, GraphPortChangeDetail as ir, MARQUEE_THRESHOLD_PX as it, SizeLimits as j, DisconnectReason as jn, polylineMidpoint as jt, RESIZE_HANDLES as k, ConnectionCheck as kn, edgeShape as kt, fitBounds as l, resolveConnection as ln, GraphViewportChangeEvent as lr, findSnapTarget as lt, zoomAt as m, cycleBlockedNodes as mn, Point as mr, GraphPortElement as mt, WireDragController as n, BridgeDeps as nn, GraphNodeTitleDblclickDetail as nr, NodeDragConfig as nt, PaneOrigin as o, canConnect as on, GraphSelectionChangeDetail as or, MarqueeController as ot, screenToFlow as p, connectContext as pn, NodeMove as pr, GraphNodeElement as pt, AddedNode as q, GraphNodeCollapseDetail as qn, combineSelection as qt, WireDragMode as r, BridgeResult as rn, GraphNodeTitleDblclickEvent as rr, NodeDragController as rt, boundsOf as s, findValidWire as sn, GraphSelectionChangeEvent as sr, SnapCandidate as st, WireDragConfig as t, OpenableMenu as tn, GraphNodeResizeEvent as tr, DraggableNode as tt, fitRects as u, typesCompatible as un, IsValidConnection as ur, proximityFactor as ut, ProximityPair as v, hasWire as vn, PortRef as vr, ForceOptions as vt, NODE_SHAPES as w, portId as wn, forceStep as wt, rectGap as x, incomingEdges as xn, forcePin as xt, ProximityQuery as y, inDegreeOf as yn, PortSide as yr, ForceState as yt, sanitizeFlowSpeed as z, GraphDeleteEvent as zn, EdgeStyle as zt };