@dryui/ui 0.2.1 → 0.3.0

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 (588) hide show
  1. package/dist/accordion/accordion-trigger.svelte +9 -1
  2. package/dist/accordion/index.d.ts +24 -10
  3. package/dist/adjust/adjust.svelte +1 -1
  4. package/dist/adjust/index.d.ts +10 -10
  5. package/dist/alert/index.d.ts +7 -13
  6. package/dist/alert-dialog/index.d.ts +10 -20
  7. package/dist/alpha-slider/index.d.ts +6 -6
  8. package/dist/aspect-ratio/index.d.ts +2 -2
  9. package/dist/aurora/aurora.svelte +58 -11
  10. package/dist/aurora/aurora.svelte.d.ts +1 -0
  11. package/dist/aurora/index.d.ts +10 -8
  12. package/dist/avatar/index.d.ts +11 -4
  13. package/dist/badge/index.d.ts +3 -3
  14. package/dist/beam/index.d.ts +7 -7
  15. package/dist/breadcrumb/index.d.ts +24 -12
  16. package/dist/button/button.svelte +5 -1
  17. package/dist/button/index.d.ts +3 -3
  18. package/dist/button-group/index.d.ts +1 -1
  19. package/dist/calendar/index.d.ts +7 -14
  20. package/dist/card/index.d.ts +15 -15
  21. package/dist/carousel/index.d.ts +8 -16
  22. package/dist/chart/index.d.ts +49 -44
  23. package/dist/chat-thread/index.d.ts +9 -11
  24. package/dist/checkbox/checkbox.svelte +1 -5
  25. package/dist/checkbox/index.d.ts +1 -1
  26. package/dist/chip/index.d.ts +13 -18
  27. package/dist/chip-group/index.d.ts +3 -3
  28. package/dist/chromatic-aberration/index.d.ts +3 -3
  29. package/dist/chromatic-shift/index.d.ts +2 -1
  30. package/dist/code-block/highlighter/types.d.ts +3 -3
  31. package/dist/code-block/index.d.ts +3 -3
  32. package/dist/collapsible/collapsible-trigger.svelte +9 -1
  33. package/dist/collapsible/index.d.ts +16 -8
  34. package/dist/color-picker/color-picker-eyedropper.svelte +13 -1
  35. package/dist/color-picker/index.d.ts +11 -19
  36. package/dist/combobox/index.d.ts +10 -15
  37. package/dist/command-palette/index.d.ts +8 -16
  38. package/dist/container/index.d.ts +3 -3
  39. package/dist/context-menu/index.d.ts +8 -16
  40. package/dist/data-grid/index.d.ts +22 -29
  41. package/dist/date-field/date-field-separator.svelte +1 -7
  42. package/dist/date-field/index.d.ts +4 -4
  43. package/dist/date-picker/datepicker-trigger.svelte +15 -1
  44. package/dist/date-picker/index.d.ts +6 -10
  45. package/dist/date-range-picker/date-range-picker-trigger.svelte +12 -1
  46. package/dist/date-range-picker/index.d.ts +6 -12
  47. package/dist/date-time-input/index.d.ts +1 -1
  48. package/dist/description-list/index.d.ts +8 -8
  49. package/dist/diagram/diagram.svelte +769 -0
  50. package/dist/diagram/diagram.svelte.d.ts +10 -0
  51. package/dist/diagram/edge-routing.d.ts +9 -0
  52. package/dist/diagram/edge-routing.js +281 -0
  53. package/dist/diagram/index.d.ts +9 -0
  54. package/dist/diagram/index.js +1 -0
  55. package/dist/diagram/layout.d.ts +2 -0
  56. package/dist/diagram/layout.js +985 -0
  57. package/dist/diagram/types.d.ts +196 -0
  58. package/dist/dialog/index.d.ts +9 -18
  59. package/dist/displacement/displacement.svelte +39 -3
  60. package/dist/displacement/index.d.ts +2 -1
  61. package/dist/drag-and-drop/context.svelte.d.ts +1 -0
  62. package/dist/drag-and-drop/drag-and-drop-group.svelte +37 -0
  63. package/dist/drag-and-drop/drag-and-drop-group.svelte.d.ts +8 -0
  64. package/dist/drag-and-drop/drag-and-drop-handle.svelte +2 -0
  65. package/dist/drag-and-drop/drag-and-drop-item.svelte +25 -6
  66. package/dist/drag-and-drop/drag-and-drop-root.svelte +624 -34
  67. package/dist/drag-and-drop/group-context.svelte.d.ts +13 -0
  68. package/dist/drag-and-drop/group-context.svelte.js +8 -0
  69. package/dist/drag-and-drop/index.d.ts +6 -8
  70. package/dist/drag-and-drop/index.js +3 -1
  71. package/dist/drawer/drawer-content.svelte +2 -1
  72. package/dist/drawer/drawer-footer.svelte +1 -1
  73. package/dist/drawer/index.d.ts +10 -18
  74. package/dist/drop-zone/index.d.ts +2 -2
  75. package/dist/dropdown-menu/index.d.ts +8 -16
  76. package/dist/field/index.d.ts +3 -3
  77. package/dist/fieldset/index.d.ts +8 -8
  78. package/dist/file-select/file-select-clear.svelte +8 -1
  79. package/dist/file-select/index.d.ts +8 -10
  80. package/dist/file-upload/file-upload-item-delete.svelte +8 -1
  81. package/dist/file-upload/file-upload-root.svelte +0 -1
  82. package/dist/file-upload/index.d.ts +14 -19
  83. package/dist/flip-card/index.d.ts +3 -3
  84. package/dist/float-button/float-button-root.svelte +7 -1
  85. package/dist/float-button/index.d.ts +6 -10
  86. package/dist/glass/index.d.ts +4 -4
  87. package/dist/glow/index.d.ts +5 -5
  88. package/dist/god-rays/index.d.ts +10 -10
  89. package/dist/gradient-mesh/gradient-mesh.svelte +42 -5
  90. package/dist/gradient-mesh/index.d.ts +2 -1
  91. package/dist/halftone/halftone.svelte +16 -36
  92. package/dist/halftone/index.d.ts +5 -5
  93. package/dist/heading/index.d.ts +3 -3
  94. package/dist/hover-card/index.d.ts +4 -8
  95. package/dist/icon/index.d.ts +5 -5
  96. package/dist/index.d.ts +65 -590
  97. package/dist/index.js +1 -2
  98. package/dist/input/index.d.ts +2 -2
  99. package/dist/input-group/index.d.ts +11 -17
  100. package/dist/input-group/input-group-root.svelte +0 -1
  101. package/dist/internal/color-aliases.d.ts +5 -8
  102. package/dist/kbd/index.d.ts +6 -1
  103. package/dist/label/index.d.ts +1 -1
  104. package/dist/link/index.d.ts +1 -1
  105. package/dist/link-preview/index.d.ts +4 -8
  106. package/dist/list/index.d.ts +6 -12
  107. package/dist/list/list-root.svelte +0 -1
  108. package/dist/listbox/index.d.ts +2 -2
  109. package/dist/logo-mark/logo-mark.svelte +2 -2
  110. package/dist/logo-mark/logo-mark.svelte.d.ts +7 -7
  111. package/dist/map/index.d.ts +31 -43
  112. package/dist/mask-reveal/index.d.ts +2 -1
  113. package/dist/mega-menu/index.d.ts +7 -14
  114. package/dist/menubar/index.d.ts +8 -16
  115. package/dist/multi-select-combobox/index.d.ts +10 -20
  116. package/dist/multi-select-combobox/multi-select-combobox-input.svelte +0 -1
  117. package/dist/navigation-menu/index.d.ts +27 -14
  118. package/dist/navigation-menu/navigation-menu-list.svelte +0 -1
  119. package/dist/noise/index.d.ts +5 -5
  120. package/dist/noise/noise.svelte +2 -2
  121. package/dist/notification-center/index.d.ts +6 -13
  122. package/dist/notification-center/notification-center-panel.svelte +4 -1
  123. package/dist/notification-center/notification-center-trigger.svelte +0 -1
  124. package/dist/number-input/index.d.ts +1 -1
  125. package/dist/number-input/number-input.svelte +3 -1
  126. package/dist/option-swatch-group/index.d.ts +8 -8
  127. package/dist/pagination/index.d.ts +8 -16
  128. package/dist/phone-input/index.d.ts +1 -1
  129. package/dist/pin-input/index.d.ts +7 -13
  130. package/dist/popover/index.d.ts +3 -3
  131. package/dist/progress/index.d.ts +3 -7
  132. package/dist/progress-ring/index.d.ts +2 -2
  133. package/dist/radio-group/index.d.ts +2 -2
  134. package/dist/radio-group/radio-group.svelte +0 -1
  135. package/dist/range-calendar/index.d.ts +2 -2
  136. package/dist/range-calendar/range-calendar-grid.svelte +10 -4
  137. package/dist/range-calendar/range-calendar-root.svelte +1 -5
  138. package/dist/rating/index.d.ts +1 -1
  139. package/dist/reveal/index.d.ts +3 -1
  140. package/dist/rich-text-editor/index.d.ts +5 -4
  141. package/dist/rich-text-editor/rich-text-editor-content.svelte +9 -1
  142. package/dist/rich-text-editor/rich-text-editor-toolbar.svelte +1 -2
  143. package/dist/scroll-to-top/index.d.ts +2 -2
  144. package/dist/segmented-control/index.d.ts +2 -2
  145. package/dist/select/index.d.ts +6 -12
  146. package/dist/select/select-trigger.svelte +9 -1
  147. package/dist/separator/index.d.ts +3 -3
  148. package/dist/shader-canvas/index.d.ts +13 -18
  149. package/dist/shader-canvas/presets.d.ts +5 -10
  150. package/dist/shader-canvas/presets.js +1 -1
  151. package/dist/shader-canvas/shader-canvas.svelte +4 -2
  152. package/dist/sidebar/index.d.ts +37 -18
  153. package/dist/sidebar/sidebar-footer.svelte +5 -4
  154. package/dist/sidebar/sidebar-header.svelte +4 -1
  155. package/dist/sidebar/sidebar-root.svelte +1 -1
  156. package/dist/skeleton/index.d.ts +5 -5
  157. package/dist/slider/index.d.ts +1 -1
  158. package/dist/spacer/index.d.ts +2 -2
  159. package/dist/spinner/index.d.ts +5 -4
  160. package/dist/splitter/index.d.ts +3 -3
  161. package/dist/spotlight/index.d.ts +2 -1
  162. package/dist/spotlight/spotlight.svelte +42 -4
  163. package/dist/star-rating/index.d.ts +5 -5
  164. package/dist/stepper/index.d.ts +5 -10
  165. package/dist/svg/index.d.ts +2 -1
  166. package/dist/table/index.d.ts +9 -18
  167. package/dist/table-of-contents/index.d.ts +4 -9
  168. package/dist/tabs/index.d.ts +25 -8
  169. package/dist/tabs/tabs-trigger.svelte +4 -2
  170. package/dist/tag/index.d.ts +3 -3
  171. package/dist/tags-input/index.d.ts +7 -12
  172. package/dist/text/index.d.ts +7 -7
  173. package/dist/textarea/index.d.ts +1 -1
  174. package/dist/themes/aurora.css +229 -0
  175. package/dist/themes/dark.css +134 -0
  176. package/dist/themes/default.css +80 -1
  177. package/dist/themes/midnight.css +142 -0
  178. package/dist/themes/terminal.css +175 -0
  179. package/dist/themes/use-theme-override.svelte.d.ts +1 -17
  180. package/dist/time-input/index.d.ts +6 -6
  181. package/dist/time-input/time-input.svelte +1 -3
  182. package/dist/timeline/index.d.ts +9 -16
  183. package/dist/timeline/timeline-item.svelte +2 -1
  184. package/dist/toast/index.d.ts +7 -15
  185. package/dist/toggle/index.d.ts +2 -2
  186. package/dist/toggle-group/index.d.ts +3 -3
  187. package/dist/toolbar/index.d.ts +5 -10
  188. package/dist/tooltip/index.d.ts +3 -3
  189. package/dist/tour/index.d.ts +2 -2
  190. package/dist/tour/tour-root.svelte +4 -1
  191. package/dist/transfer/index.d.ts +9 -16
  192. package/dist/tree/index.d.ts +5 -10
  193. package/dist/typing-indicator/index.d.ts +2 -1
  194. package/dist/typography/index.d.ts +9 -12
  195. package/dist/visually-hidden/index.d.ts +5 -1
  196. package/package.json +22 -27
  197. package/skills/dryui/SKILL.md +29 -16
  198. package/skills/dryui/rules/composition.md +262 -225
  199. package/dist/system-map/index.d.ts +0 -45
  200. package/dist/system-map/index.js +0 -1
  201. package/dist/system-map/system-map.svelte +0 -311
  202. package/dist/system-map/system-map.svelte.d.ts +0 -25
  203. package/dist/system-map/types.d.ts +0 -116
  204. package/dist/thumbnail/_layout-content.svelte +0 -89
  205. package/dist/thumbnail/_layout-content.svelte.d.ts +0 -9
  206. package/dist/thumbnail/_layout-footer.svelte +0 -54
  207. package/dist/thumbnail/_layout-footer.svelte.d.ts +0 -9
  208. package/dist/thumbnail/_layout-header.svelte +0 -76
  209. package/dist/thumbnail/_layout-header.svelte.d.ts +0 -9
  210. package/dist/thumbnail/_layout-sidebar.svelte +0 -75
  211. package/dist/thumbnail/_layout-sidebar.svelte.d.ts +0 -9
  212. package/dist/thumbnail/accordion.svelte +0 -144
  213. package/dist/thumbnail/accordion.svelte.d.ts +0 -7
  214. package/dist/thumbnail/add-on-selector.svelte +0 -213
  215. package/dist/thumbnail/add-on-selector.svelte.d.ts +0 -7
  216. package/dist/thumbnail/affix-group.svelte +0 -55
  217. package/dist/thumbnail/affix-group.svelte.d.ts +0 -7
  218. package/dist/thumbnail/alert-dialog.svelte +0 -100
  219. package/dist/thumbnail/alert-dialog.svelte.d.ts +0 -7
  220. package/dist/thumbnail/alert.svelte +0 -79
  221. package/dist/thumbnail/alert.svelte.d.ts +0 -7
  222. package/dist/thumbnail/alpha-slider.svelte +0 -55
  223. package/dist/thumbnail/alpha-slider.svelte.d.ts +0 -7
  224. package/dist/thumbnail/amenity-grid.svelte +0 -236
  225. package/dist/thumbnail/amenity-grid.svelte.d.ts +0 -7
  226. package/dist/thumbnail/app-frame.svelte.d.ts +0 -7
  227. package/dist/thumbnail/apply-size.d.ts +0 -3
  228. package/dist/thumbnail/apply-size.js +0 -14
  229. package/dist/thumbnail/aspect-ratio.svelte +0 -64
  230. package/dist/thumbnail/aspect-ratio.svelte.d.ts +0 -7
  231. package/dist/thumbnail/aurora.svelte +0 -88
  232. package/dist/thumbnail/aurora.svelte.d.ts +0 -7
  233. package/dist/thumbnail/avatar-group.svelte.d.ts +0 -7
  234. package/dist/thumbnail/avatar.svelte +0 -60
  235. package/dist/thumbnail/avatar.svelte.d.ts +0 -7
  236. package/dist/thumbnail/backdrop.svelte +0 -73
  237. package/dist/thumbnail/backdrop.svelte.d.ts +0 -7
  238. package/dist/thumbnail/badge.svelte +0 -100
  239. package/dist/thumbnail/badge.svelte.d.ts +0 -7
  240. package/dist/thumbnail/booking-confirmation.svelte +0 -109
  241. package/dist/thumbnail/booking-confirmation.svelte.d.ts +0 -7
  242. package/dist/thumbnail/breadcrumb.svelte +0 -80
  243. package/dist/thumbnail/breadcrumb.svelte.d.ts +0 -7
  244. package/dist/thumbnail/button-group.svelte +0 -76
  245. package/dist/thumbnail/button-group.svelte.d.ts +0 -7
  246. package/dist/thumbnail/button.svelte +0 -62
  247. package/dist/thumbnail/button.svelte.d.ts +0 -7
  248. package/dist/thumbnail/calendar.svelte +0 -86
  249. package/dist/thumbnail/calendar.svelte.d.ts +0 -7
  250. package/dist/thumbnail/card.svelte +0 -113
  251. package/dist/thumbnail/card.svelte.d.ts +0 -7
  252. package/dist/thumbnail/carousel.svelte +0 -111
  253. package/dist/thumbnail/carousel.svelte.d.ts +0 -7
  254. package/dist/thumbnail/chart.svelte +0 -93
  255. package/dist/thumbnail/chart.svelte.d.ts +0 -7
  256. package/dist/thumbnail/chat-message.svelte.d.ts +0 -7
  257. package/dist/thumbnail/chat-thread.svelte +0 -140
  258. package/dist/thumbnail/chat-thread.svelte.d.ts +0 -7
  259. package/dist/thumbnail/checkbox.svelte +0 -71
  260. package/dist/thumbnail/checkbox.svelte.d.ts +0 -7
  261. package/dist/thumbnail/chip-group.svelte +0 -79
  262. package/dist/thumbnail/chip-group.svelte.d.ts +0 -7
  263. package/dist/thumbnail/chip.svelte +0 -76
  264. package/dist/thumbnail/chip.svelte.d.ts +0 -7
  265. package/dist/thumbnail/chromatic-shift.svelte +0 -55
  266. package/dist/thumbnail/chromatic-shift.svelte.d.ts +0 -7
  267. package/dist/thumbnail/clipboard.svelte +0 -68
  268. package/dist/thumbnail/clipboard.svelte.d.ts +0 -7
  269. package/dist/thumbnail/code-block.svelte +0 -125
  270. package/dist/thumbnail/code-block.svelte.d.ts +0 -7
  271. package/dist/thumbnail/collapsible.svelte +0 -101
  272. package/dist/thumbnail/collapsible.svelte.d.ts +0 -7
  273. package/dist/thumbnail/color-picker.svelte +0 -108
  274. package/dist/thumbnail/color-picker.svelte.d.ts +0 -7
  275. package/dist/thumbnail/combobox.svelte +0 -122
  276. package/dist/thumbnail/combobox.svelte.d.ts +0 -7
  277. package/dist/thumbnail/command-palette.svelte +0 -134
  278. package/dist/thumbnail/command-palette.svelte.d.ts +0 -7
  279. package/dist/thumbnail/commerce-header.svelte +0 -55
  280. package/dist/thumbnail/commerce-header.svelte.d.ts +0 -7
  281. package/dist/thumbnail/comparison-table.svelte +0 -193
  282. package/dist/thumbnail/comparison-table.svelte.d.ts +0 -7
  283. package/dist/thumbnail/container.svelte +0 -119
  284. package/dist/thumbnail/container.svelte.d.ts +0 -7
  285. package/dist/thumbnail/context-menu.svelte +0 -108
  286. package/dist/thumbnail/context-menu.svelte.d.ts +0 -7
  287. package/dist/thumbnail/country-select.svelte +0 -111
  288. package/dist/thumbnail/country-select.svelte.d.ts +0 -7
  289. package/dist/thumbnail/currency-selector.svelte +0 -111
  290. package/dist/thumbnail/currency-selector.svelte.d.ts +0 -7
  291. package/dist/thumbnail/data-grid.svelte +0 -230
  292. package/dist/thumbnail/data-grid.svelte.d.ts +0 -7
  293. package/dist/thumbnail/date-field.svelte +0 -132
  294. package/dist/thumbnail/date-field.svelte.d.ts +0 -7
  295. package/dist/thumbnail/date-picker.svelte +0 -126
  296. package/dist/thumbnail/date-picker.svelte.d.ts +0 -7
  297. package/dist/thumbnail/date-range-picker.svelte +0 -100
  298. package/dist/thumbnail/date-range-picker.svelte.d.ts +0 -7
  299. package/dist/thumbnail/date-time-input.svelte +0 -182
  300. package/dist/thumbnail/date-time-input.svelte.d.ts +0 -7
  301. package/dist/thumbnail/description-list.svelte +0 -142
  302. package/dist/thumbnail/description-list.svelte.d.ts +0 -7
  303. package/dist/thumbnail/dialog.svelte +0 -136
  304. package/dist/thumbnail/dialog.svelte.d.ts +0 -7
  305. package/dist/thumbnail/displacement.svelte +0 -55
  306. package/dist/thumbnail/displacement.svelte.d.ts +0 -7
  307. package/dist/thumbnail/drag-and-drop.svelte +0 -139
  308. package/dist/thumbnail/drag-and-drop.svelte.d.ts +0 -7
  309. package/dist/thumbnail/drawer.svelte +0 -112
  310. package/dist/thumbnail/drawer.svelte.d.ts +0 -7
  311. package/dist/thumbnail/drop-zone.svelte +0 -55
  312. package/dist/thumbnail/drop-zone.svelte.d.ts +0 -7
  313. package/dist/thumbnail/dropdown-menu.svelte +0 -128
  314. package/dist/thumbnail/dropdown-menu.svelte.d.ts +0 -7
  315. package/dist/thumbnail/empty-state.svelte.d.ts +0 -7
  316. package/dist/thumbnail/fare-class-picker.svelte +0 -225
  317. package/dist/thumbnail/fare-class-picker.svelte.d.ts +0 -7
  318. package/dist/thumbnail/feature-split-section.svelte.d.ts +0 -7
  319. package/dist/thumbnail/field.svelte +0 -82
  320. package/dist/thumbnail/field.svelte.d.ts +0 -7
  321. package/dist/thumbnail/fieldset.svelte +0 -123
  322. package/dist/thumbnail/fieldset.svelte.d.ts +0 -7
  323. package/dist/thumbnail/file-select.svelte +0 -90
  324. package/dist/thumbnail/file-select.svelte.d.ts +0 -7
  325. package/dist/thumbnail/file-upload.svelte +0 -84
  326. package/dist/thumbnail/file-upload.svelte.d.ts +0 -7
  327. package/dist/thumbnail/filter-sidebar.svelte +0 -201
  328. package/dist/thumbnail/filter-sidebar.svelte.d.ts +0 -7
  329. package/dist/thumbnail/flexible-dates-grid.svelte +0 -210
  330. package/dist/thumbnail/flexible-dates-grid.svelte.d.ts +0 -7
  331. package/dist/thumbnail/flight-timeline.svelte +0 -155
  332. package/dist/thumbnail/flight-timeline.svelte.d.ts +0 -7
  333. package/dist/thumbnail/flip-card.svelte +0 -128
  334. package/dist/thumbnail/flip-card.svelte.d.ts +0 -7
  335. package/dist/thumbnail/float-button.svelte +0 -63
  336. package/dist/thumbnail/float-button.svelte.d.ts +0 -7
  337. package/dist/thumbnail/focus-trap.svelte +0 -110
  338. package/dist/thumbnail/focus-trap.svelte.d.ts +0 -7
  339. package/dist/thumbnail/format-bytes.svelte +0 -51
  340. package/dist/thumbnail/format-bytes.svelte.d.ts +0 -7
  341. package/dist/thumbnail/format-date.svelte +0 -51
  342. package/dist/thumbnail/format-date.svelte.d.ts +0 -7
  343. package/dist/thumbnail/format-number.svelte +0 -51
  344. package/dist/thumbnail/format-number.svelte.d.ts +0 -7
  345. package/dist/thumbnail/gauge.svelte +0 -90
  346. package/dist/thumbnail/gauge.svelte.d.ts +0 -7
  347. package/dist/thumbnail/glass-surface.svelte.d.ts +0 -7
  348. package/dist/thumbnail/glow.svelte +0 -55
  349. package/dist/thumbnail/glow.svelte.d.ts +0 -7
  350. package/dist/thumbnail/gradient-mesh.svelte +0 -55
  351. package/dist/thumbnail/gradient-mesh.svelte.d.ts +0 -7
  352. package/dist/thumbnail/guest-room-selector.svelte +0 -214
  353. package/dist/thumbnail/guest-room-selector.svelte.d.ts +0 -7
  354. package/dist/thumbnail/halftone.svelte +0 -55
  355. package/dist/thumbnail/halftone.svelte.d.ts +0 -7
  356. package/dist/thumbnail/heading.svelte +0 -91
  357. package/dist/thumbnail/heading.svelte.d.ts +0 -7
  358. package/dist/thumbnail/hotel-gallery.svelte +0 -117
  359. package/dist/thumbnail/hotel-gallery.svelte.d.ts +0 -7
  360. package/dist/thumbnail/hotkey.svelte +0 -108
  361. package/dist/thumbnail/hotkey.svelte.d.ts +0 -7
  362. package/dist/thumbnail/hover-card.svelte +0 -117
  363. package/dist/thumbnail/hover-card.svelte.d.ts +0 -7
  364. package/dist/thumbnail/icon.svelte +0 -55
  365. package/dist/thumbnail/icon.svelte.d.ts +0 -7
  366. package/dist/thumbnail/image-comparison.svelte +0 -96
  367. package/dist/thumbnail/image-comparison.svelte.d.ts +0 -7
  368. package/dist/thumbnail/image.svelte +0 -74
  369. package/dist/thumbnail/image.svelte.d.ts +0 -7
  370. package/dist/thumbnail/index.d.ts +0 -12
  371. package/dist/thumbnail/index.js +0 -359
  372. package/dist/thumbnail/infinite-scroll.svelte +0 -129
  373. package/dist/thumbnail/infinite-scroll.svelte.d.ts +0 -7
  374. package/dist/thumbnail/input-group.svelte +0 -55
  375. package/dist/thumbnail/input-group.svelte.d.ts +0 -7
  376. package/dist/thumbnail/input.svelte +0 -74
  377. package/dist/thumbnail/input.svelte.d.ts +0 -7
  378. package/dist/thumbnail/itinerary-timeline.svelte +0 -143
  379. package/dist/thumbnail/itinerary-timeline.svelte.d.ts +0 -7
  380. package/dist/thumbnail/kbd.svelte +0 -112
  381. package/dist/thumbnail/kbd.svelte.d.ts +0 -7
  382. package/dist/thumbnail/label.svelte +0 -54
  383. package/dist/thumbnail/label.svelte.d.ts +0 -7
  384. package/dist/thumbnail/layout-header-content-footer.svelte +0 -64
  385. package/dist/thumbnail/layout-header-content-footer.svelte.d.ts +0 -7
  386. package/dist/thumbnail/layout-header-sidebar-main.svelte +0 -64
  387. package/dist/thumbnail/layout-header-sidebar-main.svelte.d.ts +0 -7
  388. package/dist/thumbnail/layout-sidebar-main.svelte +0 -57
  389. package/dist/thumbnail/layout-sidebar-main.svelte.d.ts +0 -7
  390. package/dist/thumbnail/link-preview.svelte +0 -141
  391. package/dist/thumbnail/link-preview.svelte.d.ts +0 -7
  392. package/dist/thumbnail/link.svelte +0 -70
  393. package/dist/thumbnail/link.svelte.d.ts +0 -7
  394. package/dist/thumbnail/list.svelte +0 -86
  395. package/dist/thumbnail/list.svelte.d.ts +0 -7
  396. package/dist/thumbnail/listbox.svelte +0 -102
  397. package/dist/thumbnail/listbox.svelte.d.ts +0 -7
  398. package/dist/thumbnail/location-autocomplete.svelte +0 -219
  399. package/dist/thumbnail/location-autocomplete.svelte.d.ts +0 -7
  400. package/dist/thumbnail/logo-cloud.svelte.d.ts +0 -7
  401. package/dist/thumbnail/loyalty-points-display.svelte +0 -147
  402. package/dist/thumbnail/loyalty-points-display.svelte.d.ts +0 -7
  403. package/dist/thumbnail/map-list-toggle.svelte +0 -140
  404. package/dist/thumbnail/map-list-toggle.svelte.d.ts +0 -7
  405. package/dist/thumbnail/map.svelte +0 -112
  406. package/dist/thumbnail/map.svelte.d.ts +0 -7
  407. package/dist/thumbnail/markdown-renderer.svelte +0 -113
  408. package/dist/thumbnail/markdown-renderer.svelte.d.ts +0 -7
  409. package/dist/thumbnail/marquee.svelte +0 -114
  410. package/dist/thumbnail/marquee.svelte.d.ts +0 -7
  411. package/dist/thumbnail/mask-reveal.svelte +0 -55
  412. package/dist/thumbnail/mask-reveal.svelte.d.ts +0 -7
  413. package/dist/thumbnail/mega-menu.svelte +0 -176
  414. package/dist/thumbnail/mega-menu.svelte.d.ts +0 -7
  415. package/dist/thumbnail/menubar.svelte +0 -99
  416. package/dist/thumbnail/menubar.svelte.d.ts +0 -7
  417. package/dist/thumbnail/multi-city-search-form.svelte +0 -241
  418. package/dist/thumbnail/multi-city-search-form.svelte.d.ts +0 -7
  419. package/dist/thumbnail/multi-select-combobox.svelte +0 -142
  420. package/dist/thumbnail/multi-select-combobox.svelte.d.ts +0 -7
  421. package/dist/thumbnail/navigation-menu.svelte +0 -102
  422. package/dist/thumbnail/navigation-menu.svelte.d.ts +0 -7
  423. package/dist/thumbnail/noise.svelte +0 -72
  424. package/dist/thumbnail/noise.svelte.d.ts +0 -7
  425. package/dist/thumbnail/notification-center.svelte +0 -152
  426. package/dist/thumbnail/notification-center.svelte.d.ts +0 -7
  427. package/dist/thumbnail/number-input.svelte +0 -113
  428. package/dist/thumbnail/number-input.svelte.d.ts +0 -7
  429. package/dist/thumbnail/option-swatch-group.svelte +0 -55
  430. package/dist/thumbnail/option-swatch-group.svelte.d.ts +0 -7
  431. package/dist/thumbnail/page-header.svelte.d.ts +0 -7
  432. package/dist/thumbnail/pagination.svelte +0 -121
  433. package/dist/thumbnail/pagination.svelte.d.ts +0 -7
  434. package/dist/thumbnail/passenger-class-selector.svelte +0 -142
  435. package/dist/thumbnail/passenger-class-selector.svelte.d.ts +0 -7
  436. package/dist/thumbnail/payment-card-input.svelte +0 -172
  437. package/dist/thumbnail/payment-card-input.svelte.d.ts +0 -7
  438. package/dist/thumbnail/phone-input.svelte +0 -103
  439. package/dist/thumbnail/phone-input.svelte.d.ts +0 -7
  440. package/dist/thumbnail/pin-input.svelte +0 -72
  441. package/dist/thumbnail/pin-input.svelte.d.ts +0 -7
  442. package/dist/thumbnail/popover.svelte +0 -122
  443. package/dist/thumbnail/popover.svelte.d.ts +0 -7
  444. package/dist/thumbnail/portal.svelte +0 -115
  445. package/dist/thumbnail/portal.svelte.d.ts +0 -7
  446. package/dist/thumbnail/price-calendar.svelte +0 -366
  447. package/dist/thumbnail/price-calendar.svelte.d.ts +0 -7
  448. package/dist/thumbnail/price-summary-panel.svelte +0 -165
  449. package/dist/thumbnail/price-summary-panel.svelte.d.ts +0 -7
  450. package/dist/thumbnail/progress-ring.svelte +0 -63
  451. package/dist/thumbnail/progress-ring.svelte.d.ts +0 -7
  452. package/dist/thumbnail/progress.svelte +0 -66
  453. package/dist/thumbnail/progress.svelte.d.ts +0 -7
  454. package/dist/thumbnail/promo-code-input.svelte +0 -111
  455. package/dist/thumbnail/promo-code-input.svelte.d.ts +0 -7
  456. package/dist/thumbnail/promo-mosaic.svelte +0 -55
  457. package/dist/thumbnail/promo-mosaic.svelte.d.ts +0 -7
  458. package/dist/thumbnail/prompt-input.svelte +0 -91
  459. package/dist/thumbnail/prompt-input.svelte.d.ts +0 -7
  460. package/dist/thumbnail/qr-code.svelte +0 -217
  461. package/dist/thumbnail/qr-code.svelte.d.ts +0 -7
  462. package/dist/thumbnail/radio-group.svelte +0 -97
  463. package/dist/thumbnail/radio-group.svelte.d.ts +0 -7
  464. package/dist/thumbnail/range-calendar.svelte +0 -92
  465. package/dist/thumbnail/range-calendar.svelte.d.ts +0 -7
  466. package/dist/thumbnail/rating.svelte +0 -61
  467. package/dist/thumbnail/rating.svelte.d.ts +0 -7
  468. package/dist/thumbnail/recent-searches.svelte +0 -197
  469. package/dist/thumbnail/recent-searches.svelte.d.ts +0 -7
  470. package/dist/thumbnail/relative-time.svelte +0 -51
  471. package/dist/thumbnail/relative-time.svelte.d.ts +0 -7
  472. package/dist/thumbnail/result-card-car.svelte +0 -149
  473. package/dist/thumbnail/result-card-car.svelte.d.ts +0 -7
  474. package/dist/thumbnail/result-card-flight.svelte +0 -170
  475. package/dist/thumbnail/result-card-flight.svelte.d.ts +0 -7
  476. package/dist/thumbnail/result-card-hotel.svelte +0 -174
  477. package/dist/thumbnail/result-card-hotel.svelte.d.ts +0 -7
  478. package/dist/thumbnail/reveal.svelte +0 -109
  479. package/dist/thumbnail/reveal.svelte.d.ts +0 -7
  480. package/dist/thumbnail/review-card.svelte +0 -153
  481. package/dist/thumbnail/review-card.svelte.d.ts +0 -7
  482. package/dist/thumbnail/rich-text-editor.svelte +0 -134
  483. package/dist/thumbnail/rich-text-editor.svelte.d.ts +0 -7
  484. package/dist/thumbnail/room-type-picker.svelte +0 -212
  485. package/dist/thumbnail/room-type-picker.svelte.d.ts +0 -7
  486. package/dist/thumbnail/root.svelte +0 -52
  487. package/dist/thumbnail/root.svelte.d.ts +0 -10
  488. package/dist/thumbnail/route-map.svelte +0 -132
  489. package/dist/thumbnail/route-map.svelte.d.ts +0 -7
  490. package/dist/thumbnail/scroll-area.svelte +0 -124
  491. package/dist/thumbnail/scroll-area.svelte.d.ts +0 -7
  492. package/dist/thumbnail/scroll-to-top.svelte +0 -60
  493. package/dist/thumbnail/scroll-to-top.svelte.d.ts +0 -7
  494. package/dist/thumbnail/search-form-tabs.svelte +0 -192
  495. package/dist/thumbnail/search-form-tabs.svelte.d.ts +0 -7
  496. package/dist/thumbnail/seat-map.svelte +0 -358
  497. package/dist/thumbnail/seat-map.svelte.d.ts +0 -7
  498. package/dist/thumbnail/segmented-control.svelte +0 -93
  499. package/dist/thumbnail/segmented-control.svelte.d.ts +0 -7
  500. package/dist/thumbnail/select.svelte +0 -82
  501. package/dist/thumbnail/select.svelte.d.ts +0 -7
  502. package/dist/thumbnail/selectable-tile-group.svelte +0 -55
  503. package/dist/thumbnail/selectable-tile-group.svelte.d.ts +0 -7
  504. package/dist/thumbnail/separator.svelte +0 -94
  505. package/dist/thumbnail/separator.svelte.d.ts +0 -7
  506. package/dist/thumbnail/shader-canvas.svelte +0 -55
  507. package/dist/thumbnail/shader-canvas.svelte.d.ts +0 -7
  508. package/dist/thumbnail/sidebar.svelte +0 -109
  509. package/dist/thumbnail/sidebar.svelte.d.ts +0 -7
  510. package/dist/thumbnail/skeleton.svelte +0 -94
  511. package/dist/thumbnail/skeleton.svelte.d.ts +0 -7
  512. package/dist/thumbnail/slider.svelte +0 -71
  513. package/dist/thumbnail/slider.svelte.d.ts +0 -7
  514. package/dist/thumbnail/sort-bar.svelte +0 -156
  515. package/dist/thumbnail/sort-bar.svelte.d.ts +0 -7
  516. package/dist/thumbnail/spacer.svelte +0 -95
  517. package/dist/thumbnail/spacer.svelte.d.ts +0 -7
  518. package/dist/thumbnail/sparkline.svelte +0 -59
  519. package/dist/thumbnail/sparkline.svelte.d.ts +0 -7
  520. package/dist/thumbnail/spinner.svelte +0 -63
  521. package/dist/thumbnail/spinner.svelte.d.ts +0 -7
  522. package/dist/thumbnail/splitter.svelte +0 -79
  523. package/dist/thumbnail/splitter.svelte.d.ts +0 -7
  524. package/dist/thumbnail/spotlight.svelte +0 -90
  525. package/dist/thumbnail/spotlight.svelte.d.ts +0 -7
  526. package/dist/thumbnail/star-rating.svelte +0 -106
  527. package/dist/thumbnail/star-rating.svelte.d.ts +0 -7
  528. package/dist/thumbnail/stat-card.svelte.d.ts +0 -7
  529. package/dist/thumbnail/stepper.svelte +0 -94
  530. package/dist/thumbnail/stepper.svelte.d.ts +0 -7
  531. package/dist/thumbnail/svg.svelte +0 -55
  532. package/dist/thumbnail/svg.svelte.d.ts +0 -8
  533. package/dist/thumbnail/swatch-strip.svelte.d.ts +0 -7
  534. package/dist/thumbnail/switch.svelte.d.ts +0 -7
  535. package/dist/thumbnail/system-map.svelte +0 -154
  536. package/dist/thumbnail/system-map.svelte.d.ts +0 -7
  537. package/dist/thumbnail/table-of-contents.svelte +0 -101
  538. package/dist/thumbnail/table-of-contents.svelte.d.ts +0 -7
  539. package/dist/thumbnail/table.svelte +0 -214
  540. package/dist/thumbnail/table.svelte.d.ts +0 -7
  541. package/dist/thumbnail/tabs.svelte +0 -133
  542. package/dist/thumbnail/tabs.svelte.d.ts +0 -7
  543. package/dist/thumbnail/tag.svelte +0 -95
  544. package/dist/thumbnail/tag.svelte.d.ts +0 -7
  545. package/dist/thumbnail/tags-input.svelte +0 -142
  546. package/dist/thumbnail/tags-input.svelte.d.ts +0 -7
  547. package/dist/thumbnail/text.svelte +0 -72
  548. package/dist/thumbnail/text.svelte.d.ts +0 -7
  549. package/dist/thumbnail/textarea.svelte +0 -111
  550. package/dist/thumbnail/textarea.svelte.d.ts +0 -7
  551. package/dist/thumbnail/time-input.svelte +0 -94
  552. package/dist/thumbnail/time-input.svelte.d.ts +0 -7
  553. package/dist/thumbnail/timeline.svelte +0 -123
  554. package/dist/thumbnail/timeline.svelte.d.ts +0 -7
  555. package/dist/thumbnail/toast.svelte +0 -105
  556. package/dist/thumbnail/toast.svelte.d.ts +0 -7
  557. package/dist/thumbnail/toggle-group.svelte +0 -76
  558. package/dist/thumbnail/toggle-group.svelte.d.ts +0 -7
  559. package/dist/thumbnail/toggle.svelte +0 -46
  560. package/dist/thumbnail/toggle.svelte.d.ts +0 -7
  561. package/dist/thumbnail/token-preview.svelte.d.ts +0 -7
  562. package/dist/thumbnail/toolbar.svelte +0 -109
  563. package/dist/thumbnail/toolbar.svelte.d.ts +0 -7
  564. package/dist/thumbnail/tooltip.svelte +0 -85
  565. package/dist/thumbnail/tooltip.svelte.d.ts +0 -7
  566. package/dist/thumbnail/tour.svelte +0 -149
  567. package/dist/thumbnail/tour.svelte.d.ts +0 -7
  568. package/dist/thumbnail/transfer.svelte +0 -168
  569. package/dist/thumbnail/transfer.svelte.d.ts +0 -7
  570. package/dist/thumbnail/tree.svelte +0 -215
  571. package/dist/thumbnail/tree.svelte.d.ts +0 -7
  572. package/dist/thumbnail/trip-card.svelte +0 -137
  573. package/dist/thumbnail/trip-card.svelte.d.ts +0 -7
  574. package/dist/thumbnail/trust-badges.svelte +0 -207
  575. package/dist/thumbnail/trust-badges.svelte.d.ts +0 -7
  576. package/dist/thumbnail/typing-indicator.svelte +0 -65
  577. package/dist/thumbnail/typing-indicator.svelte.d.ts +0 -7
  578. package/dist/thumbnail/typography.svelte +0 -80
  579. package/dist/thumbnail/typography.svelte.d.ts +0 -7
  580. package/dist/thumbnail/user.svelte.d.ts +0 -7
  581. package/dist/thumbnail/video-embed.svelte +0 -87
  582. package/dist/thumbnail/video-embed.svelte.d.ts +0 -7
  583. package/dist/thumbnail/virtual-list.svelte +0 -178
  584. package/dist/thumbnail/virtual-list.svelte.d.ts +0 -7
  585. package/dist/thumbnail/visually-hidden.svelte +0 -106
  586. package/dist/thumbnail/visually-hidden.svelte.d.ts +0 -7
  587. package/dist/thumbnail/wave-divider.svelte.d.ts +0 -7
  588. /package/dist/{system-map → diagram}/types.js +0 -0
@@ -0,0 +1,985 @@
1
+ import { computeEdgePaths, emptyEdge } from './edge-routing.js';
2
+ const DEFAULT_NODE_GAP = 28;
3
+ const DEFAULT_LAYER_GAP = 56;
4
+ const DEFAULT_CLUSTER_PADDING = 32;
5
+ const DEFAULT_NODE_HEIGHT = 44;
6
+ const DESC_NODE_HEIGHT = 80;
7
+ const MIN_NODE_WIDTH = 140;
8
+ const CHAR_WIDTH = 8.5;
9
+ const NODE_PADDING_X = 48;
10
+ const MARGIN = 40;
11
+ // ── Helpers ────────────────────────────────────────────────
12
+ function estimateNodeWidth(label, description) {
13
+ const labelWidth = label.length * CHAR_WIDTH + NODE_PADDING_X;
14
+ const descWidth = description ? description.length * 6.5 + NODE_PADDING_X + 16 : 0;
15
+ return Math.max(MIN_NODE_WIDTH, labelWidth, descWidth);
16
+ }
17
+ function isHorizontal(dir) {
18
+ return dir === 'LR' || dir === 'RL';
19
+ }
20
+ function isReversed(dir) {
21
+ return dir === 'BT' || dir === 'RL';
22
+ }
23
+ function buildGraph(nodeIds, edges) {
24
+ const adjacencyOut = new Map();
25
+ const adjacencyIn = new Map();
26
+ const inDegree = new Map();
27
+ for (const id of nodeIds) {
28
+ adjacencyOut.set(id, []);
29
+ adjacencyIn.set(id, []);
30
+ inDegree.set(id, 0);
31
+ }
32
+ for (const e of edges) {
33
+ if (!adjacencyOut.has(e.from) || !adjacencyOut.has(e.to))
34
+ continue;
35
+ adjacencyOut.get(e.from).push(e.to);
36
+ adjacencyIn.get(e.to).push(e.from);
37
+ inDegree.set(e.to, (inDegree.get(e.to) || 0) + 1);
38
+ }
39
+ // Kahn's algorithm
40
+ const queue = [];
41
+ for (const id of nodeIds) {
42
+ if (inDegree.get(id) === 0)
43
+ queue.push(id);
44
+ }
45
+ const order = [];
46
+ const visited = new Set();
47
+ while (queue.length > 0) {
48
+ const node = queue.shift();
49
+ if (visited.has(node))
50
+ continue;
51
+ visited.add(node);
52
+ order.push(node);
53
+ for (const succ of adjacencyOut.get(node)) {
54
+ const deg = inDegree.get(succ) - 1;
55
+ inDegree.set(succ, deg);
56
+ if (deg === 0)
57
+ queue.push(succ);
58
+ }
59
+ }
60
+ // Handle cycles: any unvisited nodes have cycles
61
+ const reversedEdges = new Set();
62
+ if (visited.size < nodeIds.length) {
63
+ for (const id of nodeIds) {
64
+ if (!visited.has(id)) {
65
+ // Break cycle by removing an incoming edge
66
+ const incoming = adjacencyIn.get(id);
67
+ for (const src of incoming) {
68
+ if (!visited.has(src)) {
69
+ reversedEdges.add(`${src}->${id}`);
70
+ const outList = adjacencyOut.get(src);
71
+ const idx = outList.indexOf(id);
72
+ if (idx >= 0)
73
+ outList.splice(idx, 1);
74
+ inDegree.set(id, (inDegree.get(id) || 1) - 1);
75
+ }
76
+ }
77
+ if (!visited.has(id)) {
78
+ visited.add(id);
79
+ order.push(id);
80
+ }
81
+ }
82
+ }
83
+ }
84
+ return { adjacencyOut, adjacencyIn, order, reversedEdges };
85
+ }
86
+ // ── Layer Assignment (longest-path) ────────────────────────
87
+ function assignLayers(order, adjacencyOut) {
88
+ const layer = new Map();
89
+ for (const id of order)
90
+ layer.set(id, 0);
91
+ for (const id of order) {
92
+ const currentLayer = layer.get(id);
93
+ for (const succ of adjacencyOut.get(id)) {
94
+ layer.set(succ, Math.max(layer.get(succ), currentLayer + 1));
95
+ }
96
+ }
97
+ return layer;
98
+ }
99
+ // ── Within-Layer Ordering (barycenter, 2 sweeps, cluster-aware) ───────────
100
+ function orderWithinLayers(layers, adjacencyOut, adjacencyIn, clusterMap) {
101
+ const posInLayer = new Map();
102
+ // Initialize positions
103
+ for (const layer of layers) {
104
+ for (let i = 0; i < layer.length; i++) {
105
+ posInLayer.set(layer[i], i);
106
+ }
107
+ }
108
+ // Down sweep
109
+ for (let i = 1; i < layers.length; i++) {
110
+ const sorted = layers[i].map((nodeId) => {
111
+ const preds = adjacencyIn.get(nodeId) || [];
112
+ const positions = preds
113
+ .map((p) => posInLayer.get(p))
114
+ .filter((p) => p !== undefined);
115
+ const barycenter = positions.length > 0
116
+ ? positions.reduce((a, b) => a + b, 0) / positions.length
117
+ : (posInLayer.get(nodeId) ?? 0);
118
+ return { id: nodeId, bary: barycenter };
119
+ });
120
+ sorted.sort((a, b) => a.bary - b.bary);
121
+ layers[i] = sorted.map((s) => s.id);
122
+ for (let j = 0; j < layers[i].length; j++) {
123
+ posInLayer.set(layers[i][j], j);
124
+ }
125
+ }
126
+ // Up sweep
127
+ for (let i = layers.length - 2; i >= 0; i--) {
128
+ const sorted = layers[i].map((nodeId) => {
129
+ const succs = adjacencyOut.get(nodeId) || [];
130
+ const positions = succs
131
+ .map((s) => posInLayer.get(s))
132
+ .filter((p) => p !== undefined);
133
+ const barycenter = positions.length > 0
134
+ ? positions.reduce((a, b) => a + b, 0) / positions.length
135
+ : (posInLayer.get(nodeId) ?? 0);
136
+ return { id: nodeId, bary: barycenter };
137
+ });
138
+ sorted.sort((a, b) => a.bary - b.bary);
139
+ layers[i] = sorted.map((s) => s.id);
140
+ for (let j = 0; j < layers[i].length; j++) {
141
+ posInLayer.set(layers[i][j], j);
142
+ }
143
+ }
144
+ // Cluster-aware grouping: after barycenter ordering, group cluster members together
145
+ // and push non-clustered nodes to the edges
146
+ if (clusterMap && clusterMap.size > 0) {
147
+ for (let i = 0; i < layers.length; i++) {
148
+ layers[i] = groupByCluster(layers[i], clusterMap, adjacencyIn, adjacencyOut, posInLayer);
149
+ for (let j = 0; j < layers[i].length; j++) {
150
+ posInLayer.set(layers[i][j], j);
151
+ }
152
+ }
153
+ }
154
+ return layers;
155
+ }
156
+ /** Group nodes in a layer so cluster members are adjacent, non-clustered nodes at edges */
157
+ function groupByCluster(layer, clusterMap, adjacencyIn, adjacencyOut, posInLayer) {
158
+ if (layer.length <= 1)
159
+ return layer;
160
+ // Separate into cluster groups and non-clustered nodes
161
+ const clusterGroups = new Map();
162
+ const nonClustered = [];
163
+ for (const nodeId of layer) {
164
+ const clusterId = clusterMap.get(nodeId);
165
+ if (clusterId) {
166
+ if (!clusterGroups.has(clusterId))
167
+ clusterGroups.set(clusterId, []);
168
+ clusterGroups.get(clusterId).push(nodeId);
169
+ }
170
+ else {
171
+ nonClustered.push(nodeId);
172
+ }
173
+ }
174
+ // If no clusters in this layer, nothing to reorder
175
+ if (clusterGroups.size === 0)
176
+ return layer;
177
+ // If no non-clustered nodes, just keep cluster groups in barycenter order
178
+ if (nonClustered.length === 0) {
179
+ // Order cluster groups by average barycenter position of their members
180
+ const groups = [...clusterGroups.entries()].map(([cid, nodes]) => {
181
+ const avgPos = nodes.reduce((s, n) => s + (posInLayer.get(n) ?? 0), 0) / nodes.length;
182
+ return { cid, nodes, avgPos };
183
+ });
184
+ groups.sort((a, b) => a.avgPos - b.avgPos);
185
+ return groups.flatMap((g) => g.nodes);
186
+ }
187
+ // Compute average barycenter position for each cluster group
188
+ const groupEntries = [...clusterGroups.entries()].map(([cid, nodes]) => {
189
+ const avgPos = nodes.reduce((s, n) => s + (posInLayer.get(n) ?? 0), 0) / nodes.length;
190
+ return { cid, nodes, avgPos };
191
+ });
192
+ groupEntries.sort((a, b) => a.avgPos - b.avgPos);
193
+ // Compute average connected position for non-clustered nodes to decide
194
+ // whether they go before or after the cluster groups
195
+ const allClusterPositions = groupEntries.flatMap((g) => g.nodes.map((n) => posInLayer.get(n) ?? 0));
196
+ const clusterCenter = allClusterPositions.reduce((a, b) => a + b, 0) / allClusterPositions.length;
197
+ // Split non-clustered into before/after based on their barycenter relative to cluster center
198
+ const before = [];
199
+ const after = [];
200
+ for (const nodeId of nonClustered) {
201
+ const pos = posInLayer.get(nodeId) ?? 0;
202
+ if (pos <= clusterCenter) {
203
+ before.push(nodeId);
204
+ }
205
+ else {
206
+ after.push(nodeId);
207
+ }
208
+ }
209
+ // If all ended up on one side, that's fine — just keep them there
210
+ // If none on either side, push all to the end
211
+ if (before.length === 0 && after.length === 0) {
212
+ after.push(...nonClustered);
213
+ }
214
+ return [...before, ...groupEntries.flatMap((g) => g.nodes), ...after];
215
+ }
216
+ // ── Coordinate Assignment ──────────────────────────────────
217
+ const CLUSTER_GROUP_GAP = 40;
218
+ function assignCoordinates(layers, nodeDims, direction, nodeGap, layerGap, clusterMap) {
219
+ const positions = new Map();
220
+ const horizontal = isHorizontal(direction);
221
+ const reversed = isReversed(direction);
222
+ // Compute layer extents (max node size in layer direction)
223
+ const layerSizes = [];
224
+ for (const layer of layers) {
225
+ let maxSize = 0;
226
+ for (const id of layer) {
227
+ const dims = nodeDims.get(id);
228
+ maxSize = Math.max(maxSize, horizontal ? dims.w : dims.h);
229
+ }
230
+ layerSizes.push(maxSize);
231
+ }
232
+ // Find max cross-axis extent per layer for centering (including cluster group gaps)
233
+ const layerCrossExtents = [];
234
+ for (const layer of layers) {
235
+ let total = 0;
236
+ for (const id of layer) {
237
+ const dims = nodeDims.get(id);
238
+ total += horizontal ? dims.h : dims.w;
239
+ }
240
+ total += (layer.length - 1) * nodeGap;
241
+ // Add extra gap at cluster/non-cluster boundaries
242
+ if (clusterMap && clusterMap.size > 0) {
243
+ total += countClusterBoundaries(layer, clusterMap) * CLUSTER_GROUP_GAP;
244
+ }
245
+ layerCrossExtents.push(total);
246
+ }
247
+ const maxCrossExtent = Math.max(...layerCrossExtents);
248
+ // Assign positions
249
+ let layerOffset = MARGIN;
250
+ const layerOrder = reversed ? [...layers].reverse() : layers;
251
+ const sizeOrder = reversed ? [...layerSizes].reverse() : layerSizes;
252
+ for (let li = 0; li < layerOrder.length; li++) {
253
+ const layer = layerOrder[li];
254
+ const layerSize = sizeOrder[li];
255
+ // Center this layer's nodes
256
+ const crossExtent = layerCrossExtents[reversed ? layerOrder.length - 1 - li : li];
257
+ let crossOffset = MARGIN + (maxCrossExtent - crossExtent) / 2;
258
+ for (let ni = 0; ni < layer.length; ni++) {
259
+ const id = layer[ni];
260
+ const dims = nodeDims.get(id);
261
+ const primaryOffset = reversed ? (horizontal ? layerSize - dims.w : layerSize - dims.h) : 0;
262
+ // Add extra gap at cluster/non-cluster boundary
263
+ if (ni > 0 && clusterMap && clusterMap.size > 0) {
264
+ const prevCluster = clusterMap.get(layer[ni - 1]);
265
+ const currCluster = clusterMap.get(id);
266
+ if (prevCluster !== currCluster &&
267
+ (prevCluster !== undefined || currCluster !== undefined)) {
268
+ crossOffset += CLUSTER_GROUP_GAP;
269
+ }
270
+ }
271
+ if (horizontal) {
272
+ positions.set(id, {
273
+ x: layerOffset + primaryOffset,
274
+ y: crossOffset
275
+ });
276
+ crossOffset += dims.h + nodeGap;
277
+ }
278
+ else {
279
+ positions.set(id, {
280
+ x: crossOffset,
281
+ y: layerOffset + primaryOffset
282
+ });
283
+ crossOffset += dims.w + nodeGap;
284
+ }
285
+ }
286
+ layerOffset += layerSize + layerGap;
287
+ }
288
+ return positions;
289
+ }
290
+ /** Count how many cluster/non-cluster boundaries exist in a layer ordering */
291
+ function countClusterBoundaries(layer, clusterMap) {
292
+ let count = 0;
293
+ for (let i = 1; i < layer.length; i++) {
294
+ const prevCluster = clusterMap.get(layer[i - 1]);
295
+ const currCluster = clusterMap.get(layer[i]);
296
+ if (prevCluster !== currCluster && (prevCluster !== undefined || currCluster !== undefined)) {
297
+ count++;
298
+ }
299
+ }
300
+ return count;
301
+ }
302
+ // ── Cluster Bounds ─────────────────────────────────────────
303
+ function computeClusterBounds(config, positions, nodeDims, padding) {
304
+ if (!config.clusters)
305
+ return [];
306
+ return config.clusters.map((cluster) => {
307
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
308
+ for (const nodeId of cluster.nodes) {
309
+ const pos = positions.get(nodeId);
310
+ const dims = nodeDims.get(nodeId);
311
+ if (!pos || !dims)
312
+ continue;
313
+ minX = Math.min(minX, pos.x);
314
+ minY = Math.min(minY, pos.y);
315
+ maxX = Math.max(maxX, pos.x + dims.w);
316
+ maxY = Math.max(maxY, pos.y + dims.h);
317
+ }
318
+ if (minX === Infinity) {
319
+ return {
320
+ id: cluster.id,
321
+ x: 0,
322
+ y: 0,
323
+ width: 0,
324
+ height: 0,
325
+ label: cluster.label,
326
+ color: cluster.color || 'neutral',
327
+ dashed: cluster.dashed ?? true
328
+ };
329
+ }
330
+ // Add space for label above
331
+ const labelPad = cluster.label ? 24 : 0;
332
+ return {
333
+ id: cluster.id,
334
+ x: minX - padding,
335
+ y: minY - padding - labelPad,
336
+ width: maxX - minX + padding * 2,
337
+ height: maxY - minY + padding * 2 + labelPad,
338
+ label: cluster.label,
339
+ color: cluster.color || 'neutral',
340
+ dashed: cluster.dashed ?? true
341
+ };
342
+ });
343
+ }
344
+ // ── Annotations ────────────────────────────────────────────
345
+ const ANNOTATION_CHAR_WIDTH = 6;
346
+ const ANNOTATION_HEIGHT = 14;
347
+ const ANNOTATION_COLLISION_PAD = 20;
348
+ function resolveAnnotations(config, positions, nodeDims) {
349
+ if (!config.annotations)
350
+ return [];
351
+ const resolved = config.annotations.map((ann) => {
352
+ let x, y;
353
+ if (typeof ann.anchor === 'string') {
354
+ const pos = positions.get(ann.anchor);
355
+ const dims = nodeDims.get(ann.anchor);
356
+ if (pos && dims) {
357
+ x = pos.x + dims.w / 2;
358
+ y = pos.y - 12;
359
+ }
360
+ else {
361
+ x = 0;
362
+ y = 0;
363
+ }
364
+ }
365
+ else {
366
+ x = ann.anchor.x;
367
+ y = ann.anchor.y;
368
+ }
369
+ return {
370
+ text: ann.text,
371
+ x: x + (ann.offset?.dx ?? 0),
372
+ y: y + (ann.offset?.dy ?? 0),
373
+ color: ann.color || 'neutral'
374
+ };
375
+ });
376
+ // Collision avoidance: check annotations against nodes and other annotations
377
+ for (let i = 0; i < resolved.length; i++) {
378
+ const ann = resolved[i];
379
+ const annW = ann.text.length * ANNOTATION_CHAR_WIDTH;
380
+ const annH = ANNOTATION_HEIGHT;
381
+ // Check against all nodes — shift upward if overlapping
382
+ let shifted = true;
383
+ let maxIter = 10; // prevent infinite loop
384
+ while (shifted && maxIter-- > 0) {
385
+ shifted = false;
386
+ for (const [nodeId, pos] of positions) {
387
+ const dims = nodeDims.get(nodeId);
388
+ if (!dims)
389
+ continue;
390
+ // Check overlap with padding
391
+ if (ann.x + annW > pos.x - ANNOTATION_COLLISION_PAD &&
392
+ ann.x < pos.x + dims.w + ANNOTATION_COLLISION_PAD &&
393
+ ann.y + annH > pos.y - ANNOTATION_COLLISION_PAD &&
394
+ ann.y < pos.y + dims.h + ANNOTATION_COLLISION_PAD) {
395
+ // Shift annotation above the node
396
+ ann.y = pos.y - ANNOTATION_COLLISION_PAD - annH;
397
+ shifted = true;
398
+ }
399
+ }
400
+ }
401
+ // Check against previously placed annotations
402
+ for (let j = 0; j < i; j++) {
403
+ const other = resolved[j];
404
+ const otherW = other.text.length * ANNOTATION_CHAR_WIDTH;
405
+ const otherH = ANNOTATION_HEIGHT;
406
+ if (ann.x + annW > other.x - ANNOTATION_COLLISION_PAD &&
407
+ ann.x < other.x + otherW + ANNOTATION_COLLISION_PAD &&
408
+ ann.y + annH > other.y - ANNOTATION_COLLISION_PAD &&
409
+ ann.y < other.y + otherH + ANNOTATION_COLLISION_PAD) {
410
+ // Shift this annotation above the other
411
+ ann.y = other.y - ANNOTATION_COLLISION_PAD - annH;
412
+ }
413
+ }
414
+ }
415
+ return resolved;
416
+ }
417
+ // ── Layered Layout (main entry) ────────────────────────────
418
+ function layoutLayered(config) {
419
+ const direction = config.direction || 'TB';
420
+ const nodeGap = config.spacing?.nodeGap ?? DEFAULT_NODE_GAP;
421
+ const layerGap = config.spacing?.layerGap ?? DEFAULT_LAYER_GAP;
422
+ const clusterPadding = config.spacing?.clusterPadding ?? DEFAULT_CLUSTER_PADDING;
423
+ const nodeIds = config.nodes.map((n) => n.id);
424
+ const nodeDims = buildNodeDims(config.nodes);
425
+ // Build cluster membership map: nodeId -> clusterId
426
+ const clusterMap = buildClusterMap(config);
427
+ // Build graph and sort
428
+ const graph = buildGraph(nodeIds, config.edges);
429
+ // Assign layers
430
+ const layerMap = assignLayers(graph.order, graph.adjacencyOut);
431
+ // Group by layer
432
+ const maxLayer = Math.max(0, ...layerMap.values());
433
+ const layers = Array.from({ length: maxLayer + 1 }, () => []);
434
+ for (const id of graph.order) {
435
+ layers[layerMap.get(id)].push(id);
436
+ }
437
+ // Order within layers (cluster-aware)
438
+ const orderedLayers = orderWithinLayers(layers, graph.adjacencyOut, graph.adjacencyIn, clusterMap);
439
+ // Assign coordinates (cluster-aware gaps)
440
+ const positions = assignCoordinates(orderedLayers, nodeDims, direction, nodeGap, layerGap, clusterMap);
441
+ // Build positioned nodes
442
+ const positionedNodes = buildPositionedNodes(config.nodes, positions, nodeDims);
443
+ // Compute clusters
444
+ const clusters = computeClusterBounds(config, positions, nodeDims, clusterPadding);
445
+ // Compute edges
446
+ const positionedEdges = computeEdgePaths(config.edges, positions, nodeDims, direction);
447
+ // Compute annotations
448
+ const annotations = resolveAnnotations(config, positions, nodeDims);
449
+ // Compute viewBox with full bounds coverage
450
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
451
+ // Include all node bounds
452
+ for (const n of positionedNodes) {
453
+ minX = Math.min(minX, n.x);
454
+ minY = Math.min(minY, n.y);
455
+ maxX = Math.max(maxX, n.x + n.width);
456
+ maxY = Math.max(maxY, n.y + n.height);
457
+ }
458
+ // Include all cluster bounds
459
+ for (const c of clusters) {
460
+ minX = Math.min(minX, c.x);
461
+ minY = Math.min(minY, c.y);
462
+ maxX = Math.max(maxX, c.x + c.width);
463
+ maxY = Math.max(maxY, c.y + c.height);
464
+ }
465
+ // Include annotation positions (estimated text width)
466
+ for (const ann of annotations) {
467
+ const annW = ann.text.length * ANNOTATION_CHAR_WIDTH;
468
+ minX = Math.min(minX, ann.x);
469
+ minY = Math.min(minY, ann.y - ANNOTATION_HEIGHT);
470
+ maxX = Math.max(maxX, ann.x + annW);
471
+ maxY = Math.max(maxY, ann.y);
472
+ }
473
+ // Include edge label positions (estimated text width)
474
+ for (const e of positionedEdges) {
475
+ if (e.label) {
476
+ const labelW = e.label.length * 5;
477
+ minX = Math.min(minX, e.labelX - labelW / 2);
478
+ minY = Math.min(minY, e.labelY - 7);
479
+ maxX = Math.max(maxX, e.labelX + labelW / 2);
480
+ maxY = Math.max(maxY, e.labelY + 7);
481
+ }
482
+ }
483
+ // Handle empty diagram
484
+ if (minX === Infinity) {
485
+ minX = 0;
486
+ minY = 0;
487
+ maxX = MARGIN * 2;
488
+ maxY = MARGIN * 2;
489
+ }
490
+ // Shift everything so all coordinates are non-negative
491
+ const shiftX = minX < 0 ? -minX + MARGIN : 0;
492
+ const shiftY = minY < 0 ? -minY + MARGIN : 0;
493
+ if (shiftX > 0 || shiftY > 0) {
494
+ shiftAllPositions(positionedNodes, clusters, annotations, positionedEdges, shiftX, shiftY);
495
+ maxX += shiftX;
496
+ maxY += shiftY;
497
+ }
498
+ return {
499
+ nodes: positionedNodes,
500
+ edges: positionedEdges,
501
+ clusters,
502
+ swimlanes: [],
503
+ regions: [],
504
+ annotations,
505
+ messages: [],
506
+ lifelines: [],
507
+ positionedFragments: [],
508
+ viewBox: { width: maxX + MARGIN, height: maxY + MARGIN }
509
+ };
510
+ }
511
+ // ── Swimlane Layout ────────────────────────────────────────
512
+ function layoutSwimlane(config) {
513
+ const nodeGap = config.spacing?.nodeGap ?? 32;
514
+ const lanes = config.swimlanes || [];
515
+ const nodeDims = buildNodeDims(config.nodes);
516
+ const headerHeight = 50;
517
+ // Assign lanes
518
+ const laneMap = new Map();
519
+ for (let i = 0; i < lanes.length; i++) {
520
+ for (const nid of lanes[i].nodes) {
521
+ laneMap.set(nid, i);
522
+ }
523
+ }
524
+ // Compute dynamic lane widths: max(180, widest node in lane + 40)
525
+ const laneWidths = lanes.map((lane) => {
526
+ let maxW = 0;
527
+ for (const nid of lane.nodes) {
528
+ const dims = nodeDims.get(nid);
529
+ if (dims)
530
+ maxW = Math.max(maxW, dims.w);
531
+ }
532
+ return Math.max(180, maxW + 40);
533
+ });
534
+ // Compute lane start X offsets
535
+ const laneStartX = [];
536
+ let laneX = MARGIN;
537
+ for (let i = 0; i < laneWidths.length; i++) {
538
+ laneStartX.push(laneX);
539
+ laneX += laneWidths[i];
540
+ }
541
+ // Topological order for Y positions
542
+ const nodeIds = config.nodes.map((n) => n.id);
543
+ const graph = buildGraph(nodeIds, config.edges);
544
+ // Assign Y positions sequentially
545
+ const positions = new Map();
546
+ let currentY = MARGIN + headerHeight + 20;
547
+ for (const id of graph.order) {
548
+ const laneIdx = laneMap.get(id) ?? 0;
549
+ const dims = nodeDims.get(id);
550
+ const lw = laneWidths[laneIdx] ?? 180;
551
+ const startX = laneStartX[laneIdx] ?? MARGIN;
552
+ const x = startX + (lw - dims.w) / 2;
553
+ positions.set(id, { x, y: currentY });
554
+ currentY += dims.h + nodeGap;
555
+ }
556
+ const totalHeight = currentY + MARGIN;
557
+ const totalWidth = MARGIN + laneX;
558
+ const positionedNodes = buildPositionedNodes(config.nodes, positions, nodeDims);
559
+ // Build swimlane positioned data
560
+ const positionedSwimlanes = lanes.map((lane, i) => ({
561
+ id: lane.id,
562
+ label: lane.label,
563
+ x: laneStartX[i],
564
+ lineX: laneStartX[i] + laneWidths[i] / 2,
565
+ headerY: MARGIN,
566
+ footerY: totalHeight - MARGIN,
567
+ lineY1: MARGIN + headerHeight,
568
+ lineY2: totalHeight - MARGIN,
569
+ color: lane.color || 'neutral'
570
+ }));
571
+ // Edges: horizontal arrows between lanes
572
+ const positionedEdges = computeSwimEdgePaths(config.edges, positions, nodeDims, laneMap, laneWidths[0] ?? 180);
573
+ // Regions
574
+ const positionedRegions = (config.regions || []).map((region) => {
575
+ let minY = Infinity, maxY = -Infinity;
576
+ let minLane = Infinity, maxLane = -Infinity;
577
+ for (const nid of region.contains) {
578
+ const pos = positions.get(nid);
579
+ const dims = nodeDims.get(nid);
580
+ const laneIdx = laneMap.get(nid);
581
+ if (pos && dims) {
582
+ minY = Math.min(minY, pos.y);
583
+ maxY = Math.max(maxY, pos.y + dims.h);
584
+ }
585
+ if (laneIdx !== undefined) {
586
+ minLane = Math.min(minLane, laneIdx);
587
+ maxLane = Math.max(maxLane, laneIdx);
588
+ }
589
+ }
590
+ if (minY === Infinity) {
591
+ return {
592
+ id: region.id,
593
+ x: 0,
594
+ y: 0,
595
+ width: 0,
596
+ height: 0,
597
+ label: region.label,
598
+ color: region.color || 'neutral',
599
+ dashed: region.dashed ?? true
600
+ };
601
+ }
602
+ const pad = 16;
603
+ const regionX = laneStartX[minLane] - pad;
604
+ let regionEndX = laneStartX[maxLane] + laneWidths[maxLane] + pad;
605
+ return {
606
+ id: region.id,
607
+ x: regionX,
608
+ y: minY - pad - 20,
609
+ width: regionEndX - regionX,
610
+ height: maxY - minY + pad * 2 + 20,
611
+ label: region.label,
612
+ color: region.color || 'neutral',
613
+ dashed: region.dashed ?? true
614
+ };
615
+ });
616
+ // Annotations
617
+ const annotations = resolveAnnotations(config, positions, nodeDims);
618
+ // Compute viewBox with full bounds coverage
619
+ let minX = Infinity, minY = Infinity, maxViewX = -Infinity, maxViewY = -Infinity;
620
+ for (const n of positionedNodes) {
621
+ minX = Math.min(minX, n.x);
622
+ minY = Math.min(minY, n.y);
623
+ maxViewX = Math.max(maxViewX, n.x + n.width);
624
+ maxViewY = Math.max(maxViewY, n.y + n.height);
625
+ }
626
+ // Include swimlane headers
627
+ for (let si = 0; si < positionedSwimlanes.length; si++) {
628
+ const sl = positionedSwimlanes[si];
629
+ minX = Math.min(minX, sl.x);
630
+ minY = Math.min(minY, sl.headerY);
631
+ maxViewX = Math.max(maxViewX, sl.x + (laneWidths[si] ?? 180));
632
+ maxViewY = Math.max(maxViewY, sl.lineY2);
633
+ }
634
+ // Include annotation positions
635
+ for (const ann of annotations) {
636
+ const annW = ann.text.length * ANNOTATION_CHAR_WIDTH;
637
+ minX = Math.min(minX, ann.x);
638
+ minY = Math.min(minY, ann.y - ANNOTATION_HEIGHT);
639
+ maxViewX = Math.max(maxViewX, ann.x + annW);
640
+ maxViewY = Math.max(maxViewY, ann.y);
641
+ }
642
+ // Include edge labels
643
+ for (const e of positionedEdges) {
644
+ if (e.label) {
645
+ const labelW = e.label.length * 5;
646
+ minX = Math.min(minX, e.labelX - labelW / 2);
647
+ minY = Math.min(minY, e.labelY - 7);
648
+ maxViewX = Math.max(maxViewX, e.labelX + labelW / 2);
649
+ maxViewY = Math.max(maxViewY, e.labelY + 7);
650
+ }
651
+ }
652
+ // Include regions
653
+ for (const r of positionedRegions) {
654
+ minX = Math.min(minX, r.x);
655
+ minY = Math.min(minY, r.y);
656
+ maxViewX = Math.max(maxViewX, r.x + r.width);
657
+ maxViewY = Math.max(maxViewY, r.y + r.height);
658
+ }
659
+ // Handle empty diagram
660
+ if (minX === Infinity) {
661
+ minX = 0;
662
+ minY = 0;
663
+ maxViewX = totalWidth;
664
+ maxViewY = totalHeight;
665
+ }
666
+ // Shift everything so all coordinates are non-negative
667
+ const shiftX = minX < 0 ? -minX + MARGIN : 0;
668
+ const shiftY = minY < 0 ? -minY + MARGIN : 0;
669
+ if (shiftX > 0 || shiftY > 0) {
670
+ shiftAllPositions(positionedNodes, [], annotations, positionedEdges, shiftX, shiftY);
671
+ // Also shift swimlanes and regions
672
+ for (const sl of positionedSwimlanes) {
673
+ sl.x += shiftX;
674
+ sl.lineX += shiftX;
675
+ sl.headerY += shiftY;
676
+ sl.lineY1 += shiftY;
677
+ sl.lineY2 += shiftY;
678
+ }
679
+ for (const r of positionedRegions) {
680
+ r.x += shiftX;
681
+ r.y += shiftY;
682
+ }
683
+ maxViewX += shiftX;
684
+ maxViewY += shiftY;
685
+ }
686
+ return {
687
+ nodes: positionedNodes,
688
+ edges: positionedEdges,
689
+ clusters: [],
690
+ swimlanes: positionedSwimlanes,
691
+ regions: positionedRegions,
692
+ annotations,
693
+ messages: [],
694
+ lifelines: [],
695
+ positionedFragments: [],
696
+ viewBox: {
697
+ width: Math.max(totalWidth, maxViewX + MARGIN),
698
+ height: Math.max(totalHeight, maxViewY + MARGIN)
699
+ }
700
+ };
701
+ }
702
+ // ── Shared Helpers ─────────────────────────────────────────
703
+ /** Build a map from nodeId -> clusterId for cluster-aware ordering */
704
+ function buildClusterMap(config) {
705
+ const map = new Map();
706
+ if (!config.clusters)
707
+ return map;
708
+ for (const cluster of config.clusters) {
709
+ for (const nodeId of cluster.nodes) {
710
+ map.set(nodeId, cluster.id);
711
+ }
712
+ }
713
+ return map;
714
+ }
715
+ /** Shift all positioned elements by (dx, dy) to ensure non-negative coordinates */
716
+ function shiftAllPositions(nodes, clusters, annotations, edges, dx, dy) {
717
+ for (const n of nodes) {
718
+ n.x += dx;
719
+ n.y += dy;
720
+ }
721
+ for (const c of clusters) {
722
+ c.x += dx;
723
+ c.y += dy;
724
+ }
725
+ for (const a of annotations) {
726
+ a.x += dx;
727
+ a.y += dy;
728
+ }
729
+ for (const e of edges) {
730
+ e.labelX += dx;
731
+ e.labelY += dy;
732
+ // Shift SVG path coordinates
733
+ e.path = shiftSvgPath(e.path, dx, dy);
734
+ }
735
+ }
736
+ /** Shift all absolute coordinates in an SVG path string by (dx, dy) */
737
+ function shiftSvgPath(path, dx, dy) {
738
+ // Match SVG path commands and their coordinate pairs
739
+ // This handles M, L, C, Q, S, T commands with absolute coords
740
+ return path.replace(/([MLCSQT])\s*([-\d.]+)\s+([-\d.]+)/gi, (_match, cmd, x, y) => {
741
+ const upper = cmd.toUpperCase();
742
+ // Only shift absolute commands (uppercase)
743
+ if (cmd === upper) {
744
+ return `${cmd} ${parseFloat(x) + dx} ${parseFloat(y) + dy}`;
745
+ }
746
+ return `${cmd} ${x} ${y}`;
747
+ });
748
+ }
749
+ function buildNodeDims(nodes) {
750
+ const dims = new Map();
751
+ for (const node of nodes) {
752
+ const w = node.width ?? estimateNodeWidth(node.label, node.description);
753
+ const h = node.height ?? (node.description ? DESC_NODE_HEIGHT : DEFAULT_NODE_HEIGHT);
754
+ dims.set(node.id, { w, h });
755
+ }
756
+ return dims;
757
+ }
758
+ function buildPositionedNodes(nodes, positions, nodeDims) {
759
+ return nodes.map((node) => {
760
+ const pos = positions.get(node.id) || { x: 0, y: 0 };
761
+ const dims = nodeDims.get(node.id);
762
+ return {
763
+ id: node.id,
764
+ x: pos.x,
765
+ y: pos.y,
766
+ width: dims.w,
767
+ height: dims.h,
768
+ label: node.label,
769
+ description: node.description,
770
+ icon: node.icon,
771
+ variant: node.variant || 'default',
772
+ color: node.color || 'neutral',
773
+ state: node.state || 'default'
774
+ };
775
+ });
776
+ }
777
+ // ── Swimlane Edge Paths ────────────────────────────────────
778
+ function computeSwimEdgePaths(edges, positions, nodeDims, laneMap, laneWidth) {
779
+ return edges.map((edge) => {
780
+ const fromPos = positions.get(edge.from);
781
+ const toPos = positions.get(edge.to);
782
+ const fromDims = nodeDims.get(edge.from);
783
+ const toDims = nodeDims.get(edge.to);
784
+ if (!fromPos || !toPos || !fromDims || !toDims) {
785
+ return emptyEdge(edge);
786
+ }
787
+ const fromLane = laneMap.get(edge.from) ?? 0;
788
+ const toLane = laneMap.get(edge.to) ?? 0;
789
+ let path;
790
+ let labelX;
791
+ let labelY;
792
+ if (fromLane === toLane) {
793
+ // Same lane: vertical arrow
794
+ const cx = fromPos.x + fromDims.w / 2;
795
+ const y1 = fromPos.y + fromDims.h;
796
+ const y2 = toPos.y;
797
+ path = `M ${cx} ${y1} L ${cx} ${y2}`;
798
+ labelX = cx + 10;
799
+ labelY = (y1 + y2) / 2;
800
+ }
801
+ else {
802
+ // Cross-lane: horizontal arrow at midpoint Y
803
+ const midY = fromPos.y + fromDims.h / 2;
804
+ const x1 = fromLane < toLane ? fromPos.x + fromDims.w : fromPos.x;
805
+ const x2 = fromLane < toLane ? toPos.x : toPos.x + toDims.w;
806
+ path = `M ${x1} ${midY} L ${x2} ${midY}`;
807
+ labelX = (x1 + x2) / 2;
808
+ labelY = midY - 8;
809
+ }
810
+ return {
811
+ from: edge.from,
812
+ to: edge.to,
813
+ path,
814
+ label: edge.label,
815
+ labelX,
816
+ labelY,
817
+ arrow: edge.arrow || 'end',
818
+ dashed: edge.dashed || false,
819
+ color: edge.color || 'neutral'
820
+ };
821
+ });
822
+ }
823
+ // ── Public API ─────────────────────────────────────────────
824
+ // ── Sequence Layout ───────────────────────────────────────
825
+ const SEQ_ACTOR_GAP = 180;
826
+ const SEQ_MESSAGE_GAP = 40;
827
+ const SEQ_ACTOR_BOX_HEIGHT = 36;
828
+ const SEQ_TOP_MARGIN = 40;
829
+ const SEQ_SELF_LOOP_WIDTH = 30;
830
+ const SEQ_SELF_LOOP_HEIGHT = 24;
831
+ const SEQ_FRAGMENT_PAD_X = 20;
832
+ const SEQ_FRAGMENT_PAD_Y = 16;
833
+ const SEQ_FRAGMENT_TAG_HEIGHT = 20;
834
+ function layoutSequence(config) {
835
+ const actors = config.nodes;
836
+ const messages = config.messages || [];
837
+ const fragments = config.fragments || [];
838
+ // Position actors evenly across the x-axis
839
+ const actorXMap = new Map();
840
+ const actorColorMap = new Map();
841
+ for (let i = 0; i < actors.length; i++) {
842
+ const x = MARGIN + i * SEQ_ACTOR_GAP + SEQ_ACTOR_GAP / 2;
843
+ actorXMap.set(actors[i].id, x);
844
+ actorColorMap.set(actors[i].id, actors[i].color || 'neutral');
845
+ }
846
+ // Compute message Y positions (increment per message)
847
+ const messageStartY = SEQ_TOP_MARGIN + SEQ_ACTOR_BOX_HEIGHT + 30;
848
+ const messageYPositions = [];
849
+ let currentY = messageStartY;
850
+ for (let i = 0; i < messages.length; i++) {
851
+ messageYPositions.push(currentY);
852
+ const isSelf = messages[i].from === messages[i].to;
853
+ currentY += isSelf ? SEQ_MESSAGE_GAP + SEQ_SELF_LOOP_HEIGHT : SEQ_MESSAGE_GAP;
854
+ }
855
+ // Bottom of the diagram
856
+ const bottomY = currentY + 30 + SEQ_ACTOR_BOX_HEIGHT;
857
+ const totalHeight = bottomY + MARGIN;
858
+ const totalWidth = MARGIN * 2 + actors.length * SEQ_ACTOR_GAP;
859
+ // Build lifelines
860
+ const lifelines = actors.map((actor) => ({
861
+ id: actor.id,
862
+ label: actor.label,
863
+ x: actorXMap.get(actor.id),
864
+ topY: SEQ_TOP_MARGIN,
865
+ bottomY: bottomY,
866
+ color: (actor.color || 'neutral')
867
+ }));
868
+ // Build positioned messages
869
+ const positionedMessages = messages.map((msg, i) => {
870
+ const fromX = actorXMap.get(msg.from) ?? 0;
871
+ const toX = actorXMap.get(msg.to) ?? 0;
872
+ const y = messageYPositions[i];
873
+ const isSelf = msg.from === msg.to;
874
+ let labelX;
875
+ let labelY;
876
+ if (isSelf) {
877
+ labelX = fromX + SEQ_SELF_LOOP_WIDTH + 8;
878
+ labelY = y + SEQ_SELF_LOOP_HEIGHT / 2;
879
+ }
880
+ else {
881
+ labelX = (fromX + toX) / 2;
882
+ labelY = y - 8;
883
+ }
884
+ return {
885
+ from: msg.from,
886
+ to: msg.to,
887
+ label: msg.label,
888
+ x1: fromX,
889
+ y,
890
+ x2: toX,
891
+ labelX,
892
+ labelY,
893
+ arrow: msg.arrow || 'end',
894
+ dashed: msg.dashed || false,
895
+ color: (msg.color || 'neutral'),
896
+ isSelf
897
+ };
898
+ });
899
+ // Build positioned fragments
900
+ const positionedFragments = fragments.map((frag) => {
901
+ if (frag.messages.length === 0) {
902
+ return {
903
+ id: frag.id,
904
+ label: frag.label,
905
+ condition: frag.condition,
906
+ x: 0,
907
+ y: 0,
908
+ width: 0,
909
+ height: 0,
910
+ color: (frag.color || 'neutral'),
911
+ dashed: frag.dashed ?? false
912
+ };
913
+ }
914
+ // Find bounds from contained messages
915
+ let minX = Infinity;
916
+ let maxX = -Infinity;
917
+ let minY = Infinity;
918
+ let maxY = -Infinity;
919
+ for (const msgIdx of frag.messages) {
920
+ if (msgIdx < 0 || msgIdx >= positionedMessages.length)
921
+ continue;
922
+ const msg = positionedMessages[msgIdx];
923
+ const leftX = Math.min(msg.x1, msg.x2);
924
+ const rightX = msg.isSelf ? msg.x1 + SEQ_SELF_LOOP_WIDTH : Math.max(msg.x1, msg.x2);
925
+ minX = Math.min(minX, leftX);
926
+ maxX = Math.max(maxX, rightX);
927
+ minY = Math.min(minY, msg.y);
928
+ maxY = Math.max(maxY, msg.isSelf ? msg.y + SEQ_SELF_LOOP_HEIGHT : msg.y);
929
+ }
930
+ if (minX === Infinity) {
931
+ return {
932
+ id: frag.id,
933
+ label: frag.label,
934
+ condition: frag.condition,
935
+ x: 0,
936
+ y: 0,
937
+ width: 0,
938
+ height: 0,
939
+ color: (frag.color || 'neutral'),
940
+ dashed: frag.dashed ?? false
941
+ };
942
+ }
943
+ return {
944
+ id: frag.id,
945
+ label: frag.label,
946
+ condition: frag.condition,
947
+ x: minX - SEQ_FRAGMENT_PAD_X,
948
+ y: minY - SEQ_FRAGMENT_PAD_Y - SEQ_FRAGMENT_TAG_HEIGHT,
949
+ width: maxX - minX + SEQ_FRAGMENT_PAD_X * 2,
950
+ height: maxY - minY + SEQ_FRAGMENT_PAD_Y * 2 + SEQ_FRAGMENT_TAG_HEIGHT,
951
+ color: (frag.color || 'neutral'),
952
+ dashed: frag.dashed ?? false
953
+ };
954
+ });
955
+ // Annotations
956
+ const positions = new Map();
957
+ const nodeDims = new Map();
958
+ for (const actor of actors) {
959
+ const x = actorXMap.get(actor.id);
960
+ positions.set(actor.id, { x: x - 60, y: SEQ_TOP_MARGIN });
961
+ nodeDims.set(actor.id, { w: 120, h: SEQ_ACTOR_BOX_HEIGHT });
962
+ }
963
+ const annotations = resolveAnnotations(config, positions, nodeDims);
964
+ return {
965
+ nodes: [],
966
+ edges: [],
967
+ clusters: [],
968
+ swimlanes: [],
969
+ regions: [],
970
+ annotations,
971
+ messages: positionedMessages,
972
+ lifelines,
973
+ positionedFragments,
974
+ viewBox: { width: totalWidth, height: totalHeight }
975
+ };
976
+ }
977
+ // ── Public API ─────────────────────────────────────────────
978
+ export function computeLayout(config) {
979
+ if (config.layout === 'sequence')
980
+ return layoutSequence(config);
981
+ if (config.layout === 'swimlane') {
982
+ return layoutSwimlane(config);
983
+ }
984
+ return layoutLayered(config);
985
+ }