@nyaruka/temba-components 0.139.0 → 0.141.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 (385) hide show
  1. package/.github/workflows/cla.yml +1 -1
  2. package/.github/workflows/copilot-setup-steps.yml +6 -1
  3. package/.lintstagedrc.js +10 -0
  4. package/CHANGELOG.md +32 -0
  5. package/demo/data/flows/sample-flow.json +24 -0
  6. package/dist/locales/es.js +5 -5
  7. package/dist/locales/es.js.map +1 -1
  8. package/dist/locales/fr.js +5 -5
  9. package/dist/locales/fr.js.map +1 -1
  10. package/dist/locales/locale-codes.js +11 -2
  11. package/dist/locales/locale-codes.js.map +1 -1
  12. package/dist/locales/pt.js +5 -5
  13. package/dist/locales/pt.js.map +1 -1
  14. package/dist/temba-components.js +702 -338
  15. package/dist/temba-components.js.map +1 -1
  16. package/out-tsc/src/display/Chat.js +10 -7
  17. package/out-tsc/src/display/Chat.js.map +1 -1
  18. package/out-tsc/src/display/Dropdown.js +3 -1
  19. package/out-tsc/src/display/Dropdown.js.map +1 -1
  20. package/out-tsc/src/display/FloatingTab.js +4 -4
  21. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  22. package/out-tsc/src/display/Thumbnail.js +163 -5
  23. package/out-tsc/src/display/Thumbnail.js.map +1 -1
  24. package/out-tsc/src/flow/CanvasNode.js +65 -23
  25. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  26. package/out-tsc/src/flow/Editor.js +369 -49
  27. package/out-tsc/src/flow/Editor.js.map +1 -1
  28. package/out-tsc/src/flow/NodeEditor.js +118 -10
  29. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  30. package/out-tsc/src/flow/Plumber.js +61 -14
  31. package/out-tsc/src/flow/Plumber.js.map +1 -1
  32. package/out-tsc/src/flow/StickyNote.js +13 -4
  33. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  34. package/out-tsc/src/flow/actions/add_contact_groups.js +4 -1
  35. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  36. package/out-tsc/src/flow/actions/add_input_labels.js +4 -1
  37. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  38. package/out-tsc/src/flow/actions/audio-player.js +112 -0
  39. package/out-tsc/src/flow/actions/audio-player.js.map +1 -0
  40. package/out-tsc/src/flow/actions/enter_flow.js +43 -0
  41. package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
  42. package/out-tsc/src/flow/actions/play_audio.js +57 -4
  43. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  44. package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -1
  45. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  46. package/out-tsc/src/flow/actions/say_msg.js +86 -3
  47. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  48. package/out-tsc/src/flow/actions/send_broadcast.js +6 -2
  49. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  50. package/out-tsc/src/flow/actions/set_contact_channel.js +13 -0
  51. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  52. package/out-tsc/src/flow/actions/set_contact_status.js +7 -5
  53. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  54. package/out-tsc/src/flow/actions/start_session.js +10 -3
  55. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  56. package/out-tsc/src/flow/config.js +11 -3
  57. package/out-tsc/src/flow/config.js.map +1 -1
  58. package/out-tsc/src/flow/nodes/shared-rules.js +1 -1
  59. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -1
  60. package/out-tsc/src/flow/nodes/split_by_contact_field.js +18 -5
  61. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  62. package/out-tsc/src/flow/nodes/split_by_expression.js +1 -1
  63. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  64. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +0 -1
  65. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  66. package/out-tsc/src/flow/nodes/split_by_random.js +0 -1
  67. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  68. package/out-tsc/src/flow/nodes/split_by_run_result.js +10 -4
  69. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  70. package/out-tsc/src/flow/nodes/terminal.js +7 -0
  71. package/out-tsc/src/flow/nodes/terminal.js.map +1 -0
  72. package/out-tsc/src/flow/nodes/wait_for_audio.js +77 -0
  73. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
  74. package/out-tsc/src/flow/nodes/wait_for_dial.js +151 -0
  75. package/out-tsc/src/flow/nodes/wait_for_dial.js.map +1 -0
  76. package/out-tsc/src/flow/nodes/wait_for_digits.js +61 -1
  77. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  78. package/out-tsc/src/flow/nodes/wait_for_menu.js +173 -2
  79. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  80. package/out-tsc/src/flow/nodes/wait_for_response.js +1 -1
  81. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  82. package/out-tsc/src/flow/operators.js +21 -5
  83. package/out-tsc/src/flow/operators.js.map +1 -1
  84. package/out-tsc/src/flow/types.js.map +1 -1
  85. package/out-tsc/src/flow/utils.js +79 -3
  86. package/out-tsc/src/flow/utils.js.map +1 -1
  87. package/out-tsc/src/form/ArrayEditor.js +4 -2
  88. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  89. package/out-tsc/src/form/FieldRenderer.js +56 -0
  90. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  91. package/out-tsc/src/interfaces.js +1 -0
  92. package/out-tsc/src/interfaces.js.map +1 -1
  93. package/out-tsc/src/layout/Dialog.js +51 -7
  94. package/out-tsc/src/layout/Dialog.js.map +1 -1
  95. package/out-tsc/src/layout/Modax.js +20 -2
  96. package/out-tsc/src/layout/Modax.js.map +1 -1
  97. package/out-tsc/src/list/ContentMenu.js +14 -1
  98. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  99. package/out-tsc/src/locales/es.js +5 -5
  100. package/out-tsc/src/locales/es.js.map +1 -1
  101. package/out-tsc/src/locales/fr.js +5 -5
  102. package/out-tsc/src/locales/fr.js.map +1 -1
  103. package/out-tsc/src/locales/locale-codes.js +11 -2
  104. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  105. package/out-tsc/src/locales/pt.js +5 -5
  106. package/out-tsc/src/locales/pt.js.map +1 -1
  107. package/out-tsc/src/simulator/Simulator.js +21 -4
  108. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  109. package/out-tsc/src/store/AppState.js +102 -3
  110. package/out-tsc/src/store/AppState.js.map +1 -1
  111. package/out-tsc/test/actions/add_contact_groups.test.js +35 -0
  112. package/out-tsc/test/actions/add_contact_groups.test.js.map +1 -1
  113. package/out-tsc/test/actions/add_input_labels.test.js +53 -0
  114. package/out-tsc/test/actions/add_input_labels.test.js.map +1 -0
  115. package/out-tsc/test/actions/enter_flow.test.js +71 -0
  116. package/out-tsc/test/actions/enter_flow.test.js.map +1 -0
  117. package/out-tsc/test/actions/play_audio.test.js +118 -0
  118. package/out-tsc/test/actions/play_audio.test.js.map +1 -0
  119. package/out-tsc/test/actions/remove_contact_groups.test.js +24 -0
  120. package/out-tsc/test/actions/remove_contact_groups.test.js.map +1 -1
  121. package/out-tsc/test/actions/say_msg.test.js +158 -0
  122. package/out-tsc/test/actions/say_msg.test.js.map +1 -0
  123. package/out-tsc/test/actions/send_broadcast.test.js +41 -0
  124. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -1
  125. package/out-tsc/test/actions/set_contact_channel.test.js +67 -0
  126. package/out-tsc/test/actions/set_contact_channel.test.js.map +1 -0
  127. package/out-tsc/test/actions/set_contact_field.test.js +52 -0
  128. package/out-tsc/test/actions/set_contact_field.test.js.map +1 -0
  129. package/out-tsc/test/actions/set_contact_language.test.js +39 -0
  130. package/out-tsc/test/actions/set_contact_language.test.js.map +1 -0
  131. package/out-tsc/test/actions/set_contact_name.test.js +28 -0
  132. package/out-tsc/test/actions/set_contact_name.test.js.map +1 -0
  133. package/out-tsc/test/actions/set_contact_status.test.js +44 -0
  134. package/out-tsc/test/actions/set_contact_status.test.js.map +1 -0
  135. package/out-tsc/test/actions/set_run_result.test.js +47 -0
  136. package/out-tsc/test/actions/set_run_result.test.js.map +1 -0
  137. package/out-tsc/test/actions/start_session.test.js +76 -0
  138. package/out-tsc/test/actions/start_session.test.js.map +1 -1
  139. package/out-tsc/test/nodes/split_by_contact_field.test.js +50 -0
  140. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -1
  141. package/out-tsc/test/nodes/split_by_run_result.test.js +82 -0
  142. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -1
  143. package/out-tsc/test/nodes/split_by_ticket.test.js +139 -0
  144. package/out-tsc/test/nodes/split_by_ticket.test.js.map +1 -0
  145. package/out-tsc/test/nodes/split_by_webhook.test.js +111 -0
  146. package/out-tsc/test/nodes/split_by_webhook.test.js.map +1 -0
  147. package/out-tsc/test/nodes/wait_for_audio.test.js +156 -0
  148. package/out-tsc/test/nodes/wait_for_audio.test.js.map +1 -0
  149. package/out-tsc/test/nodes/wait_for_dial.test.js +336 -0
  150. package/out-tsc/test/nodes/wait_for_dial.test.js.map +1 -0
  151. package/out-tsc/test/nodes/wait_for_digits.test.js +198 -84
  152. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  153. package/out-tsc/test/nodes/wait_for_menu.test.js +340 -0
  154. package/out-tsc/test/nodes/wait_for_menu.test.js.map +1 -0
  155. package/out-tsc/test/temba-flow-collision.test.js +261 -6
  156. package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
  157. package/out-tsc/test/temba-flow-editor.test.js +187 -0
  158. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  159. package/out-tsc/test/temba-flow-plumber.test.js +19 -0
  160. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  161. package/out-tsc/test/temba-node-type-selector.test.js +6 -6
  162. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
  163. package/out-tsc/test/temba-select.test.js +4 -1
  164. package/out-tsc/test/temba-select.test.js.map +1 -1
  165. package/out-tsc/test/utils.test.js +4 -2
  166. package/out-tsc/test/utils.test.js.map +1 -1
  167. package/package.json +3 -9
  168. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  169. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  170. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  171. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  172. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  173. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  174. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  175. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  176. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  177. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  178. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  179. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  180. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  181. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  182. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  183. package/screenshots/truth/actions/add_input_labels/editor/multiple-labels.png +0 -0
  184. package/screenshots/truth/actions/add_input_labels/editor/single-label.png +0 -0
  185. package/screenshots/truth/actions/add_input_labels/render/multiple-labels.png +0 -0
  186. package/screenshots/truth/actions/add_input_labels/render/single-label.png +0 -0
  187. package/screenshots/truth/actions/enter_flow/editor/basic-flow.png +0 -0
  188. package/screenshots/truth/actions/enter_flow/editor/long-flow-name.png +0 -0
  189. package/screenshots/truth/actions/enter_flow/render/basic-flow.png +0 -0
  190. package/screenshots/truth/actions/enter_flow/render/long-flow-name.png +0 -0
  191. package/screenshots/truth/actions/play_audio/editor/expression-url.png +0 -0
  192. package/screenshots/truth/actions/play_audio/editor/static-url.png +0 -0
  193. package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
  194. package/screenshots/truth/actions/play_audio/render/static-url.png +0 -0
  195. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  196. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  197. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  198. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  199. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  200. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  201. package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
  202. package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
  203. package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
  204. package/screenshots/truth/actions/say_msg/render/multiline-text.png +0 -0
  205. package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
  206. package/screenshots/truth/actions/say_msg/render/text-with-audio-url.png +0 -0
  207. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  208. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  209. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  210. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  211. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  212. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  213. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  214. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  215. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  216. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  217. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  218. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  219. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  220. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  221. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  222. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  223. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  224. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  225. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  226. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  227. package/screenshots/truth/actions/set_contact_channel/editor/sms-channel.png +0 -0
  228. package/screenshots/truth/actions/set_contact_channel/editor/whatsapp-channel.png +0 -0
  229. package/screenshots/truth/actions/set_contact_channel/render/sms-channel.png +0 -0
  230. package/screenshots/truth/actions/set_contact_channel/render/whatsapp-channel.png +0 -0
  231. package/screenshots/truth/actions/set_contact_field/editor/clear-value.png +0 -0
  232. package/screenshots/truth/actions/set_contact_field/editor/set-value.png +0 -0
  233. package/screenshots/truth/actions/set_contact_field/render/clear-value.png +0 -0
  234. package/screenshots/truth/actions/set_contact_field/render/set-value.png +0 -0
  235. package/screenshots/truth/actions/set_contact_language/editor/english.png +0 -0
  236. package/screenshots/truth/actions/set_contact_language/editor/french.png +0 -0
  237. package/screenshots/truth/actions/set_contact_language/render/english.png +0 -0
  238. package/screenshots/truth/actions/set_contact_language/render/french.png +0 -0
  239. package/screenshots/truth/actions/set_contact_name/editor/expression-name.png +0 -0
  240. package/screenshots/truth/actions/set_contact_name/editor/static-name.png +0 -0
  241. package/screenshots/truth/actions/set_contact_name/render/expression-name.png +0 -0
  242. package/screenshots/truth/actions/set_contact_name/render/static-name.png +0 -0
  243. package/screenshots/truth/actions/set_contact_status/editor/active.png +0 -0
  244. package/screenshots/truth/actions/set_contact_status/editor/archived.png +0 -0
  245. package/screenshots/truth/actions/set_contact_status/editor/blocked.png +0 -0
  246. package/screenshots/truth/actions/set_contact_status/render/active.png +0 -0
  247. package/screenshots/truth/actions/set_contact_status/render/archived.png +0 -0
  248. package/screenshots/truth/actions/set_contact_status/render/blocked.png +0 -0
  249. package/screenshots/truth/actions/set_run_result/editor/expression-value.png +0 -0
  250. package/screenshots/truth/actions/set_run_result/editor/with-category.png +0 -0
  251. package/screenshots/truth/actions/set_run_result/render/expression-value.png +0 -0
  252. package/screenshots/truth/actions/set_run_result/render/with-category.png +0 -0
  253. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  254. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  255. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  256. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  257. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  258. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  259. package/screenshots/truth/editor/router.png +0 -0
  260. package/screenshots/truth/editor/wait.png +0 -0
  261. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  262. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  263. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  264. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  265. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  266. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  267. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  268. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  269. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  270. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  271. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  272. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  273. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  274. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  275. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  276. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  277. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  278. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  279. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  280. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  281. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  282. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  283. package/screenshots/truth/nodes/wait_for_audio/editor/basic-audio-wait.png +0 -0
  284. package/screenshots/truth/nodes/wait_for_audio/render/basic-audio-wait.png +0 -0
  285. package/screenshots/truth/nodes/wait_for_dial/editor/basic-dial.png +0 -0
  286. package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
  287. package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
  288. package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.png +0 -0
  289. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  290. package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
  291. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  292. package/screenshots/truth/nodes/wait_for_digits/render/digits-with-rules.png +0 -0
  293. package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
  294. package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.png +0 -0
  295. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  296. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  297. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  298. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  299. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  300. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  301. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  302. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  303. package/src/display/Chat.ts +13 -7
  304. package/src/display/Dropdown.ts +3 -1
  305. package/src/display/FloatingTab.ts +4 -4
  306. package/src/display/Thumbnail.ts +162 -2
  307. package/src/flow/CanvasNode.ts +70 -24
  308. package/src/flow/Editor.ts +440 -99
  309. package/src/flow/NodeEditor.ts +137 -9
  310. package/src/flow/Plumber.ts +89 -14
  311. package/src/flow/StickyNote.ts +14 -4
  312. package/src/flow/actions/add_contact_groups.ts +4 -1
  313. package/src/flow/actions/add_input_labels.ts +4 -1
  314. package/src/flow/actions/audio-player.ts +127 -0
  315. package/src/flow/actions/enter_flow.ts +44 -0
  316. package/src/flow/actions/play_audio.ts +64 -5
  317. package/src/flow/actions/remove_contact_groups.ts +6 -1
  318. package/src/flow/actions/say_msg.ts +94 -4
  319. package/src/flow/actions/send_broadcast.ts +6 -2
  320. package/src/flow/actions/set_contact_channel.ts +13 -1
  321. package/src/flow/actions/set_contact_status.ts +7 -5
  322. package/src/flow/actions/start_session.ts +10 -3
  323. package/src/flow/config.ts +11 -3
  324. package/src/flow/nodes/shared-rules.ts +1 -1
  325. package/src/flow/nodes/split_by_contact_field.ts +16 -5
  326. package/src/flow/nodes/split_by_expression.ts +1 -1
  327. package/src/flow/nodes/split_by_llm_categorize.ts +0 -1
  328. package/src/flow/nodes/split_by_random.ts +0 -1
  329. package/src/flow/nodes/split_by_run_result.ts +10 -4
  330. package/src/flow/nodes/terminal.ts +9 -0
  331. package/src/flow/nodes/wait_for_audio.ts +88 -0
  332. package/src/flow/nodes/wait_for_dial.ts +176 -0
  333. package/src/flow/nodes/wait_for_digits.ts +87 -2
  334. package/src/flow/nodes/wait_for_menu.ts +209 -3
  335. package/src/flow/nodes/wait_for_response.ts +1 -1
  336. package/src/flow/operators.ts +23 -5
  337. package/src/flow/types.ts +23 -1
  338. package/src/flow/utils.ts +82 -3
  339. package/src/form/ArrayEditor.ts +4 -2
  340. package/src/form/FieldRenderer.ts +71 -1
  341. package/src/interfaces.ts +2 -1
  342. package/src/layout/Dialog.ts +52 -7
  343. package/src/layout/Modax.ts +19 -2
  344. package/src/list/ContentMenu.ts +15 -1
  345. package/src/locales/es.ts +18 -13
  346. package/src/locales/fr.ts +18 -13
  347. package/src/locales/locale-codes.ts +11 -2
  348. package/src/locales/pt.ts +18 -13
  349. package/src/simulator/Simulator.ts +25 -4
  350. package/src/store/AppState.ts +120 -1
  351. package/src/store/flow-definition.d.ts +2 -0
  352. package/test/actions/add_contact_groups.test.ts +38 -0
  353. package/test/actions/add_input_labels.test.ts +67 -0
  354. package/test/actions/enter_flow.test.ts +88 -0
  355. package/test/actions/play_audio.test.ts +155 -0
  356. package/test/actions/remove_contact_groups.test.ts +29 -0
  357. package/test/actions/say_msg.test.ts +196 -0
  358. package/test/actions/send_broadcast.test.ts +44 -0
  359. package/test/actions/set_contact_channel.test.ts +88 -0
  360. package/test/actions/set_contact_field.test.ts +68 -0
  361. package/test/actions/set_contact_language.test.ts +55 -0
  362. package/test/actions/set_contact_name.test.ts +39 -0
  363. package/test/actions/set_contact_status.test.ts +64 -0
  364. package/test/actions/set_run_result.test.ts +61 -0
  365. package/test/actions/start_session.test.ts +82 -0
  366. package/test/nodes/split_by_contact_field.test.ts +59 -0
  367. package/test/nodes/split_by_run_result.test.ts +100 -0
  368. package/test/nodes/split_by_ticket.test.ts +157 -0
  369. package/test/nodes/split_by_webhook.test.ts +131 -0
  370. package/test/nodes/wait_for_audio.test.ts +182 -0
  371. package/test/nodes/wait_for_dial.test.ts +382 -0
  372. package/test/nodes/wait_for_digits.test.ts +233 -109
  373. package/test/nodes/wait_for_menu.test.ts +383 -0
  374. package/test/temba-flow-collision.test.ts +286 -6
  375. package/test/temba-flow-editor.test.ts +240 -0
  376. package/test/temba-flow-plumber.test.ts +62 -0
  377. package/test/temba-node-type-selector.test.ts +6 -6
  378. package/test/temba-select.test.ts +6 -1
  379. package/test/utils.test.ts +4 -2
  380. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  381. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  382. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  383. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  384. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  385. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
@@ -401,10 +401,11 @@ describe('Collision Detection Utilities', () => {
401
401
  expect(newPos.top).to.be.lessThan(200);
402
402
  expect(newPos.left).to.equal(100); // horizontal position unchanged
403
403
  });
404
- it('prefers direction with fewer cascading collisions', () => {
404
+ it('prefers axis-matching direction even with a cascade', () => {
405
405
  // Sacred at (100,100)-(200,200), collider at (100,150)-(200,250)
406
- // A node sits below at (100,280)-(200,380) blocking the downward path
407
- // Down causes cascade, right does not
406
+ // Overlap is 100w x 50h (wide) = vertical collision = prefer down
407
+ // A blocker sits below at (100,280)-(200,380), so down causes a cascade
408
+ // But axis bias still prefers down over moving right
408
409
  const allBounds = [
409
410
  {
410
411
  uuid: 'sacred',
@@ -436,9 +437,10 @@ describe('Collision Detection Utilities', () => {
436
437
  ];
437
438
  const positions = calculateReflowPositions(['sacred'], allBounds);
438
439
  expect(positions.has('collider')).to.be.true;
439
- // Should avoid moving down (would cascade into blocker)
440
- // blocker should not need to move
441
- expect(positions.has('blocker')).to.be.false;
440
+ const newPos = positions.get('collider');
441
+ // Axis bias prefers down (vertical) even though it cascades into blocker
442
+ expect(newPos.top).to.be.greaterThan(200);
443
+ expect(newPos.left).to.equal(100); // horizontal position unchanged
442
444
  });
443
445
  it('resolves cascading collisions', () => {
444
446
  const allBounds = [
@@ -619,6 +621,259 @@ describe('Collision Detection Utilities', () => {
619
621
  expect(newPos.left).to.be.at.least(0);
620
622
  expect(newPos.top).to.be.at.least(0);
621
623
  });
624
+ it('does not move a lower node above the sacred node', () => {
625
+ // Collider is below sacred (collider.top > sacred.top)
626
+ // Up should be filtered, so collider goes down or to the side
627
+ const allBounds = [
628
+ {
629
+ uuid: 'sacred',
630
+ left: 100,
631
+ top: 100,
632
+ right: 200,
633
+ bottom: 200,
634
+ width: 100,
635
+ height: 100
636
+ },
637
+ {
638
+ uuid: 'collider',
639
+ left: 100,
640
+ top: 180,
641
+ right: 200,
642
+ bottom: 280,
643
+ width: 100,
644
+ height: 100
645
+ }
646
+ ];
647
+ const positions = calculateReflowPositions(['sacred'], allBounds);
648
+ expect(positions.has('collider')).to.be.true;
649
+ const newPos = positions.get('collider');
650
+ // Should NOT move above the sacred node's top
651
+ expect(newPos.top).to.be.at.least(100);
652
+ });
653
+ it('does not move a right-of node left of the sacred node', () => {
654
+ // Collider is to the right of sacred (collider.left > sacred.left)
655
+ // Left should be filtered
656
+ const allBounds = [
657
+ {
658
+ uuid: 'sacred',
659
+ left: 100,
660
+ top: 100,
661
+ right: 200,
662
+ bottom: 200,
663
+ width: 100,
664
+ height: 100
665
+ },
666
+ {
667
+ uuid: 'collider',
668
+ left: 180,
669
+ top: 100,
670
+ right: 280,
671
+ bottom: 200,
672
+ width: 100,
673
+ height: 100
674
+ }
675
+ ];
676
+ const positions = calculateReflowPositions(['sacred'], allBounds);
677
+ expect(positions.has('collider')).to.be.true;
678
+ const newPos = positions.get('collider');
679
+ // Should NOT move left of the sacred node's left
680
+ expect(newPos.left).to.be.at.least(100);
681
+ });
682
+ it('prefers vertical for wide overlap (vertical collision)', () => {
683
+ // Nodes stacked: same horizontal position, slight vertical overlap
684
+ // Overlap: 100w x 30h (wider than tall) = vertical collision = prefer up/down
685
+ const allBounds = [
686
+ {
687
+ uuid: 'sacred',
688
+ left: 100,
689
+ top: 100,
690
+ right: 200,
691
+ bottom: 200,
692
+ width: 100,
693
+ height: 100
694
+ },
695
+ {
696
+ uuid: 'collider',
697
+ left: 100,
698
+ top: 170,
699
+ right: 200,
700
+ bottom: 270,
701
+ width: 100,
702
+ height: 100
703
+ }
704
+ ];
705
+ const positions = calculateReflowPositions(['sacred'], allBounds);
706
+ expect(positions.has('collider')).to.be.true;
707
+ const newPos = positions.get('collider');
708
+ // Should move vertically (down since collider is below)
709
+ expect(newPos.top).to.be.greaterThan(200);
710
+ expect(newPos.left).to.equal(100); // horizontal position unchanged
711
+ });
712
+ it('prefers horizontal for tall overlap (horizontal collision)', () => {
713
+ // Nodes side-by-side: same vertical position, slight horizontal overlap
714
+ // Overlap: 30w x 100h (taller than wide) = horizontal collision = prefer left/right
715
+ const allBounds = [
716
+ {
717
+ uuid: 'sacred',
718
+ left: 100,
719
+ top: 100,
720
+ right: 200,
721
+ bottom: 200,
722
+ width: 100,
723
+ height: 100
724
+ },
725
+ {
726
+ uuid: 'collider',
727
+ left: 170,
728
+ top: 100,
729
+ right: 270,
730
+ bottom: 200,
731
+ width: 100,
732
+ height: 100
733
+ }
734
+ ];
735
+ const positions = calculateReflowPositions(['sacred'], allBounds);
736
+ expect(positions.has('collider')).to.be.true;
737
+ const newPos = positions.get('collider');
738
+ // Should move horizontally (right since collider is right of sacred)
739
+ expect(newPos.left).to.be.greaterThan(200);
740
+ expect(newPos.top).to.equal(100); // vertical position unchanged
741
+ });
742
+ it('axis bias tolerates a few cascading collisions', () => {
743
+ // Sacred at (100,100)-(200,200), collider at (100,170)-(200,270)
744
+ // Overlap: 100w x 30h = vertical collision = prefer down
745
+ // Two blockers below: moving down causes 2 cascades
746
+ // Moving right causes 0 cascades but is axis-mismatched
747
+ // Axis bias should still prefer down with 2 cascades
748
+ const allBounds = [
749
+ {
750
+ uuid: 'sacred',
751
+ left: 100,
752
+ top: 100,
753
+ right: 200,
754
+ bottom: 200,
755
+ width: 100,
756
+ height: 100
757
+ },
758
+ {
759
+ uuid: 'collider',
760
+ left: 100,
761
+ top: 170,
762
+ right: 200,
763
+ bottom: 270,
764
+ width: 100,
765
+ height: 100
766
+ },
767
+ {
768
+ uuid: 'blocker1',
769
+ left: 100,
770
+ top: 260,
771
+ right: 200,
772
+ bottom: 360,
773
+ width: 100,
774
+ height: 100
775
+ },
776
+ {
777
+ uuid: 'blocker2',
778
+ left: 100,
779
+ top: 350,
780
+ right: 200,
781
+ bottom: 450,
782
+ width: 100,
783
+ height: 100
784
+ }
785
+ ];
786
+ const positions = calculateReflowPositions(['sacred'], allBounds);
787
+ expect(positions.has('collider')).to.be.true;
788
+ const newPos = positions.get('collider');
789
+ // Should still prefer down (axis match) despite 2 cascades
790
+ expect(newPos.top).to.be.greaterThan(200);
791
+ expect(newPos.left).to.equal(100);
792
+ });
793
+ it('sacred node yields to existing top node when dropped below its top', () => {
794
+ // Existing node at top of canvas, sacred dropped overlapping from below
795
+ const allBounds = [
796
+ {
797
+ uuid: 'existing',
798
+ left: 100,
799
+ top: 0,
800
+ right: 200,
801
+ bottom: 100,
802
+ width: 100,
803
+ height: 100
804
+ },
805
+ {
806
+ uuid: 'dropped',
807
+ left: 100,
808
+ top: 50,
809
+ right: 200,
810
+ bottom: 150,
811
+ width: 100,
812
+ height: 100
813
+ }
814
+ ];
815
+ const positions = calculateReflowPositions(['dropped'], allBounds);
816
+ // Sacred (dropped) should yield since it didn't drop above existing
817
+ // and existing has no room to move up
818
+ expect(positions.has('dropped')).to.be.true;
819
+ expect(positions.has('existing')).to.be.false;
820
+ const newPos = positions.get('dropped');
821
+ expect(newPos.top).to.be.greaterThanOrEqual(100); // moved below existing
822
+ });
823
+ it('sacred keeps position when dropped above existing node', () => {
824
+ // Sacred node dropped above existing node - sacred gets priority
825
+ const allBounds = [
826
+ {
827
+ uuid: 'existing',
828
+ left: 100,
829
+ top: 50,
830
+ right: 200,
831
+ bottom: 150,
832
+ width: 100,
833
+ height: 100
834
+ },
835
+ {
836
+ uuid: 'dropped',
837
+ left: 100,
838
+ top: 0,
839
+ right: 200,
840
+ bottom: 100,
841
+ width: 100,
842
+ height: 100
843
+ }
844
+ ];
845
+ const positions = calculateReflowPositions(['dropped'], allBounds);
846
+ // Sacred dropped above existing, so it keeps priority
847
+ expect(positions.has('dropped')).to.be.false;
848
+ expect(positions.has('existing')).to.be.true;
849
+ });
850
+ it('sacred keeps position when dropped at same top as existing node', () => {
851
+ // Both at top=0 - sacred keeps priority since it's not below existing
852
+ const allBounds = [
853
+ {
854
+ uuid: 'existing',
855
+ left: 100,
856
+ top: 0,
857
+ right: 200,
858
+ bottom: 100,
859
+ width: 100,
860
+ height: 100
861
+ },
862
+ {
863
+ uuid: 'dropped',
864
+ left: 100,
865
+ top: 0,
866
+ right: 200,
867
+ bottom: 100,
868
+ width: 100,
869
+ height: 100
870
+ }
871
+ ];
872
+ const positions = calculateReflowPositions(['dropped'], allBounds);
873
+ // Sacred at same top keeps priority - existing node moves
874
+ expect(positions.has('dropped')).to.be.false;
875
+ expect(positions.has('existing')).to.be.true;
876
+ });
622
877
  });
623
878
  describe('edge cases', () => {
624
879
  it('handles empty allBounds array', () => {
@@ -1 +1 @@
1
- {"version":3,"file":"temba-flow-collision.test.js","sourceRoot":"","sources":["../../test/temba-flow-collision.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport {\n getNodeBounds,\n nodesOverlap,\n detectCollisions,\n calculateReflowPositions,\n NodeBounds\n} from '../src/flow/utils';\n\ndescribe('Collision Detection Utilities', () => {\n describe('getNodeBounds', () => {\n it('returns null when element not found', () => {\n const position = { left: 100, top: 200 };\n const bounds = getNodeBounds('nonexistent-uuid', position);\n expect(bounds).to.be.null;\n });\n\n it('calculates bounds correctly from element', () => {\n // Create a mock element\n const mockElement = document.createElement('div');\n mockElement.id = 'test-node';\n mockElement.style.width = '200px';\n mockElement.style.height = '150px';\n document.body.appendChild(mockElement);\n\n const position = { left: 100, top: 200 };\n const bounds = getNodeBounds('test-node', position, mockElement);\n\n expect(bounds).to.not.be.null;\n expect(bounds!.uuid).to.equal('test-node');\n expect(bounds!.left).to.equal(100);\n expect(bounds!.top).to.equal(200);\n expect(bounds!.right).to.equal(300); // left + width\n expect(bounds!.bottom).to.equal(350); // top + height\n expect(bounds!.width).to.equal(200);\n expect(bounds!.height).to.equal(150);\n\n document.body.removeChild(mockElement);\n });\n });\n\n describe('nodesOverlap', () => {\n it('detects overlapping nodes horizontally and vertically', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.true;\n });\n\n it('detects non-overlapping nodes to the right', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 300,\n top: 100,\n right: 400,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.false;\n });\n\n it('detects non-overlapping nodes below', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 100,\n top: 300,\n right: 200,\n bottom: 400,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.false;\n });\n\n it('detects edge touching as overlapping (within buffer)', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 200,\n top: 100,\n right: 300,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n // edges touching should be considered overlapping due to buffer\n expect(nodesOverlap(bounds1, bounds2)).to.be.true;\n });\n\n it('detects partial vertical overlap', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 150,\n top: 50,\n right: 250,\n bottom: 150,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.true;\n });\n });\n\n describe('detectCollisions', () => {\n it('returns empty array when no collisions', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n {\n uuid: 'node1',\n left: 300,\n top: 100,\n right: 400,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 100,\n top: 300,\n right: 200,\n bottom: 400,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(0);\n });\n\n it('detects single collision', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n {\n uuid: 'node1',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 300,\n top: 300,\n right: 400,\n bottom: 400,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(1);\n expect(collisions[0].uuid).to.equal('node1');\n });\n\n it('detects multiple collisions', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n {\n uuid: 'node1',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 50,\n top: 50,\n right: 150,\n bottom: 150,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(2);\n });\n\n it('excludes target node from collisions', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n targetBounds,\n {\n uuid: 'node1',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(1);\n expect(collisions[0].uuid).to.equal('node1');\n });\n });\n\n describe('calculateReflowPositions', () => {\n it('returns empty map when no collisions', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node1',\n left: 300,\n top: 100,\n right: 400,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['moved'], allBounds);\n expect(positions.size).to.equal(0);\n });\n\n it('moves colliding node out of the way', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node1',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['moved'], allBounds);\n\n expect(positions.size).to.equal(1);\n expect(positions.has('node1')).to.be.true;\n expect(positions.has('moved')).to.be.false;\n });\n\n it('sacred node never appears in returned positions', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'dropped',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'existing',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['dropped'], allBounds);\n\n expect(positions.has('dropped')).to.be.false;\n expect(positions.has('existing')).to.be.true;\n });\n\n it('prefers least-displacement direction', () => {\n // Sacred at (100,100)-(200,200), collider at (180,100)-(280,200)\n // Right requires only 60px displacement, down requires 140px\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 180,\n top: 100,\n right: 280,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should move right (shorter) rather than down (longer)\n expect(newPos.left).to.be.greaterThan(200);\n expect(newPos.top).to.equal(100); // vertical position unchanged\n });\n\n it('prefers up when it is the shortest move', () => {\n // Sacred at (100,200)-(200,300), collider at (100,180)-(200,280)\n // Up: newTop=snapToGrid(200-100-30)=snapToGrid(70)=80, distance=100\n // Down: newTop=snapToGrid(300+30)=340, distance=160\n // Right: newLeft=snapToGrid(200+30)=240, distance=140\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 200,\n right: 200,\n bottom: 300,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 180,\n right: 200,\n bottom: 280,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should move up (shortest displacement)\n expect(newPos.top).to.be.lessThan(200);\n expect(newPos.left).to.equal(100); // horizontal position unchanged\n });\n\n it('prefers direction with fewer cascading collisions', () => {\n // Sacred at (100,100)-(200,200), collider at (100,150)-(200,250)\n // A node sits below at (100,280)-(200,380) blocking the downward path\n // Down causes cascade, right does not\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'blocker',\n left: 100,\n top: 280,\n right: 200,\n bottom: 380,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n // Should avoid moving down (would cascade into blocker)\n // blocker should not need to move\n expect(positions.has('blocker')).to.be.false;\n });\n\n it('resolves cascading collisions', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node1',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 100,\n top: 200,\n right: 200,\n bottom: 300,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['moved'], allBounds);\n\n // At least one node should be repositioned\n expect(positions.size).to.be.greaterThan(0);\n\n // No node should overlap with the sacred node or each other after reflow\n // (verified by the algorithm's correctness guarantee)\n });\n\n it('handles multiple sacred nodes', () => {\n // Two sacred nodes with a non-sacred node overlapping one\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'sacred2',\n left: 100,\n top: 400,\n right: 200,\n bottom: 500,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(\n ['sacred1', 'sacred2'],\n allBounds\n );\n\n // Sacred nodes should never be moved\n expect(positions.has('sacred1')).to.be.false;\n expect(positions.has('sacred2')).to.be.false;\n // Collider should be moved\n expect(positions.has('collider')).to.be.true;\n });\n\n it('does not move a node into another sacred node', () => {\n // Two sacred nodes close together with a collider between them\n // Moving right would overlap sacred2, so it should choose another direction\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'sacred2',\n left: 240,\n top: 100,\n right: 340,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 150,\n top: 100,\n right: 250,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(\n ['sacred1', 'sacred2'],\n allBounds\n );\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should not overlap either sacred node after reflow\n const newBounds: NodeBounds = {\n uuid: 'collider',\n left: newPos.left,\n top: newPos.top,\n right: newPos.left + 100,\n bottom: newPos.top + 100,\n width: 100,\n height: 100\n };\n expect(nodesOverlap(newBounds, allBounds[0])).to.be.false;\n expect(nodesOverlap(newBounds, allBounds[1])).to.be.false;\n });\n\n it('snaps reflow positions to grid', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Both coordinates should be multiples of 20 (grid size)\n expect(newPos.left % 20).to.equal(0);\n expect(newPos.top % 20).to.equal(0);\n });\n\n it('clamps positions to zero (no negative coordinates)', () => {\n // Sacred node near top-left, collider above it\n // Moving up would go negative, so it should fall back\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 0,\n top: 0,\n right: 200,\n bottom: 200,\n width: 200,\n height: 200\n },\n {\n uuid: 'collider',\n left: 0,\n top: 100,\n right: 200,\n bottom: 200,\n width: 200,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n expect(newPos.left).to.be.at.least(0);\n expect(newPos.top).to.be.at.least(0);\n });\n });\n\n describe('edge cases', () => {\n it('handles empty allBounds array', () => {\n const positions = calculateReflowPositions(['moved'], []);\n expect(positions.size).to.equal(0);\n });\n\n it('handles single node (no other nodes to collide with)', () => {\n const movedBounds: NodeBounds = {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const positions = calculateReflowPositions(['moved'], [movedBounds]);\n expect(positions.size).to.equal(0);\n });\n\n it('prevents infinite loops with complex collisions', () => {\n const movedBounds: NodeBounds = {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n // Create a complex scenario with many overlapping nodes\n const allBounds: NodeBounds[] = [movedBounds];\n\n for (let i = 0; i < 20; i++) {\n allBounds.push({\n uuid: `node${i}`,\n left: 100 + i * 10,\n top: 100 + i * 10,\n right: 200 + i * 10,\n bottom: 200 + i * 10,\n width: 100,\n height: 100\n });\n }\n\n // Should complete without hanging\n const positions = calculateReflowPositions(['moved'], allBounds);\n\n // Should have resolved some collisions\n expect(positions.size).to.be.greaterThan(0);\n });\n });\n});\n"]}
1
+ {"version":3,"file":"temba-flow-collision.test.js","sourceRoot":"","sources":["../../test/temba-flow-collision.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing';\nimport {\n getNodeBounds,\n nodesOverlap,\n detectCollisions,\n calculateReflowPositions,\n NodeBounds\n} from '../src/flow/utils';\n\ndescribe('Collision Detection Utilities', () => {\n describe('getNodeBounds', () => {\n it('returns null when element not found', () => {\n const position = { left: 100, top: 200 };\n const bounds = getNodeBounds('nonexistent-uuid', position);\n expect(bounds).to.be.null;\n });\n\n it('calculates bounds correctly from element', () => {\n // Create a mock element\n const mockElement = document.createElement('div');\n mockElement.id = 'test-node';\n mockElement.style.width = '200px';\n mockElement.style.height = '150px';\n document.body.appendChild(mockElement);\n\n const position = { left: 100, top: 200 };\n const bounds = getNodeBounds('test-node', position, mockElement);\n\n expect(bounds).to.not.be.null;\n expect(bounds!.uuid).to.equal('test-node');\n expect(bounds!.left).to.equal(100);\n expect(bounds!.top).to.equal(200);\n expect(bounds!.right).to.equal(300); // left + width\n expect(bounds!.bottom).to.equal(350); // top + height\n expect(bounds!.width).to.equal(200);\n expect(bounds!.height).to.equal(150);\n\n document.body.removeChild(mockElement);\n });\n });\n\n describe('nodesOverlap', () => {\n it('detects overlapping nodes horizontally and vertically', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.true;\n });\n\n it('detects non-overlapping nodes to the right', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 300,\n top: 100,\n right: 400,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.false;\n });\n\n it('detects non-overlapping nodes below', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 100,\n top: 300,\n right: 200,\n bottom: 400,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.false;\n });\n\n it('detects edge touching as overlapping (within buffer)', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 200,\n top: 100,\n right: 300,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n // edges touching should be considered overlapping due to buffer\n expect(nodesOverlap(bounds1, bounds2)).to.be.true;\n });\n\n it('detects partial vertical overlap', () => {\n const bounds1: NodeBounds = {\n uuid: 'node1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const bounds2: NodeBounds = {\n uuid: 'node2',\n left: 150,\n top: 50,\n right: 250,\n bottom: 150,\n width: 100,\n height: 100\n };\n\n expect(nodesOverlap(bounds1, bounds2)).to.be.true;\n });\n });\n\n describe('detectCollisions', () => {\n it('returns empty array when no collisions', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n {\n uuid: 'node1',\n left: 300,\n top: 100,\n right: 400,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 100,\n top: 300,\n right: 200,\n bottom: 400,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(0);\n });\n\n it('detects single collision', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n {\n uuid: 'node1',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 300,\n top: 300,\n right: 400,\n bottom: 400,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(1);\n expect(collisions[0].uuid).to.equal('node1');\n });\n\n it('detects multiple collisions', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n {\n uuid: 'node1',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 50,\n top: 50,\n right: 150,\n bottom: 150,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(2);\n });\n\n it('excludes target node from collisions', () => {\n const targetBounds: NodeBounds = {\n uuid: 'target',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const allBounds: NodeBounds[] = [\n targetBounds,\n {\n uuid: 'node1',\n left: 150,\n top: 150,\n right: 250,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const collisions = detectCollisions(targetBounds, allBounds);\n expect(collisions).to.have.length(1);\n expect(collisions[0].uuid).to.equal('node1');\n });\n });\n\n describe('calculateReflowPositions', () => {\n it('returns empty map when no collisions', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node1',\n left: 300,\n top: 100,\n right: 400,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['moved'], allBounds);\n expect(positions.size).to.equal(0);\n });\n\n it('moves colliding node out of the way', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node1',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['moved'], allBounds);\n\n expect(positions.size).to.equal(1);\n expect(positions.has('node1')).to.be.true;\n expect(positions.has('moved')).to.be.false;\n });\n\n it('sacred node never appears in returned positions', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'dropped',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'existing',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['dropped'], allBounds);\n\n expect(positions.has('dropped')).to.be.false;\n expect(positions.has('existing')).to.be.true;\n });\n\n it('prefers least-displacement direction', () => {\n // Sacred at (100,100)-(200,200), collider at (180,100)-(280,200)\n // Right requires only 60px displacement, down requires 140px\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 180,\n top: 100,\n right: 280,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should move right (shorter) rather than down (longer)\n expect(newPos.left).to.be.greaterThan(200);\n expect(newPos.top).to.equal(100); // vertical position unchanged\n });\n\n it('prefers up when it is the shortest move', () => {\n // Sacred at (100,200)-(200,300), collider at (100,180)-(200,280)\n // Up: newTop=snapToGrid(200-100-30)=snapToGrid(70)=80, distance=100\n // Down: newTop=snapToGrid(300+30)=340, distance=160\n // Right: newLeft=snapToGrid(200+30)=240, distance=140\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 200,\n right: 200,\n bottom: 300,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 180,\n right: 200,\n bottom: 280,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should move up (shortest displacement)\n expect(newPos.top).to.be.lessThan(200);\n expect(newPos.left).to.equal(100); // horizontal position unchanged\n });\n\n it('prefers axis-matching direction even with a cascade', () => {\n // Sacred at (100,100)-(200,200), collider at (100,150)-(200,250)\n // Overlap is 100w x 50h (wide) = vertical collision = prefer down\n // A blocker sits below at (100,280)-(200,380), so down causes a cascade\n // But axis bias still prefers down over moving right\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'blocker',\n left: 100,\n top: 280,\n right: 200,\n bottom: 380,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Axis bias prefers down (vertical) even though it cascades into blocker\n expect(newPos.top).to.be.greaterThan(200);\n expect(newPos.left).to.equal(100); // horizontal position unchanged\n });\n\n it('resolves cascading collisions', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'node1',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n },\n {\n uuid: 'node2',\n left: 100,\n top: 200,\n right: 200,\n bottom: 300,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['moved'], allBounds);\n\n // At least one node should be repositioned\n expect(positions.size).to.be.greaterThan(0);\n\n // No node should overlap with the sacred node or each other after reflow\n // (verified by the algorithm's correctness guarantee)\n });\n\n it('handles multiple sacred nodes', () => {\n // Two sacred nodes with a non-sacred node overlapping one\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'sacred2',\n left: 100,\n top: 400,\n right: 200,\n bottom: 500,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(\n ['sacred1', 'sacred2'],\n allBounds\n );\n\n // Sacred nodes should never be moved\n expect(positions.has('sacred1')).to.be.false;\n expect(positions.has('sacred2')).to.be.false;\n // Collider should be moved\n expect(positions.has('collider')).to.be.true;\n });\n\n it('does not move a node into another sacred node', () => {\n // Two sacred nodes close together with a collider between them\n // Moving right would overlap sacred2, so it should choose another direction\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred1',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'sacred2',\n left: 240,\n top: 100,\n right: 340,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 150,\n top: 100,\n right: 250,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(\n ['sacred1', 'sacred2'],\n allBounds\n );\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should not overlap either sacred node after reflow\n const newBounds: NodeBounds = {\n uuid: 'collider',\n left: newPos.left,\n top: newPos.top,\n right: newPos.left + 100,\n bottom: newPos.top + 100,\n width: 100,\n height: 100\n };\n expect(nodesOverlap(newBounds, allBounds[0])).to.be.false;\n expect(nodesOverlap(newBounds, allBounds[1])).to.be.false;\n });\n\n it('snaps reflow positions to grid', () => {\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 150,\n right: 200,\n bottom: 250,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Both coordinates should be multiples of 20 (grid size)\n expect(newPos.left % 20).to.equal(0);\n expect(newPos.top % 20).to.equal(0);\n });\n\n it('clamps positions to zero (no negative coordinates)', () => {\n // Sacred node near top-left, collider above it\n // Moving up would go negative, so it should fall back\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 0,\n top: 0,\n right: 200,\n bottom: 200,\n width: 200,\n height: 200\n },\n {\n uuid: 'collider',\n left: 0,\n top: 100,\n right: 200,\n bottom: 200,\n width: 200,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n expect(newPos.left).to.be.at.least(0);\n expect(newPos.top).to.be.at.least(0);\n });\n\n it('does not move a lower node above the sacred node', () => {\n // Collider is below sacred (collider.top > sacred.top)\n // Up should be filtered, so collider goes down or to the side\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 180,\n right: 200,\n bottom: 280,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should NOT move above the sacred node's top\n expect(newPos.top).to.be.at.least(100);\n });\n\n it('does not move a right-of node left of the sacred node', () => {\n // Collider is to the right of sacred (collider.left > sacred.left)\n // Left should be filtered\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 180,\n top: 100,\n right: 280,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should NOT move left of the sacred node's left\n expect(newPos.left).to.be.at.least(100);\n });\n\n it('prefers vertical for wide overlap (vertical collision)', () => {\n // Nodes stacked: same horizontal position, slight vertical overlap\n // Overlap: 100w x 30h (wider than tall) = vertical collision = prefer up/down\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 170,\n right: 200,\n bottom: 270,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should move vertically (down since collider is below)\n expect(newPos.top).to.be.greaterThan(200);\n expect(newPos.left).to.equal(100); // horizontal position unchanged\n });\n\n it('prefers horizontal for tall overlap (horizontal collision)', () => {\n // Nodes side-by-side: same vertical position, slight horizontal overlap\n // Overlap: 30w x 100h (taller than wide) = horizontal collision = prefer left/right\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 170,\n top: 100,\n right: 270,\n bottom: 200,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should move horizontally (right since collider is right of sacred)\n expect(newPos.left).to.be.greaterThan(200);\n expect(newPos.top).to.equal(100); // vertical position unchanged\n });\n\n it('axis bias tolerates a few cascading collisions', () => {\n // Sacred at (100,100)-(200,200), collider at (100,170)-(200,270)\n // Overlap: 100w x 30h = vertical collision = prefer down\n // Two blockers below: moving down causes 2 cascades\n // Moving right causes 0 cascades but is axis-mismatched\n // Axis bias should still prefer down with 2 cascades\n const allBounds: NodeBounds[] = [\n {\n uuid: 'sacred',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n },\n {\n uuid: 'collider',\n left: 100,\n top: 170,\n right: 200,\n bottom: 270,\n width: 100,\n height: 100\n },\n {\n uuid: 'blocker1',\n left: 100,\n top: 260,\n right: 200,\n bottom: 360,\n width: 100,\n height: 100\n },\n {\n uuid: 'blocker2',\n left: 100,\n top: 350,\n right: 200,\n bottom: 450,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['sacred'], allBounds);\n\n expect(positions.has('collider')).to.be.true;\n const newPos = positions.get('collider')!;\n // Should still prefer down (axis match) despite 2 cascades\n expect(newPos.top).to.be.greaterThan(200);\n expect(newPos.left).to.equal(100);\n });\n\n it('sacred node yields to existing top node when dropped below its top', () => {\n // Existing node at top of canvas, sacred dropped overlapping from below\n const allBounds: NodeBounds[] = [\n {\n uuid: 'existing',\n left: 100,\n top: 0,\n right: 200,\n bottom: 100,\n width: 100,\n height: 100\n },\n {\n uuid: 'dropped',\n left: 100,\n top: 50,\n right: 200,\n bottom: 150,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['dropped'], allBounds);\n\n // Sacred (dropped) should yield since it didn't drop above existing\n // and existing has no room to move up\n expect(positions.has('dropped')).to.be.true;\n expect(positions.has('existing')).to.be.false;\n\n const newPos = positions.get('dropped')!;\n expect(newPos.top).to.be.greaterThanOrEqual(100); // moved below existing\n });\n\n it('sacred keeps position when dropped above existing node', () => {\n // Sacred node dropped above existing node - sacred gets priority\n const allBounds: NodeBounds[] = [\n {\n uuid: 'existing',\n left: 100,\n top: 50,\n right: 200,\n bottom: 150,\n width: 100,\n height: 100\n },\n {\n uuid: 'dropped',\n left: 100,\n top: 0,\n right: 200,\n bottom: 100,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['dropped'], allBounds);\n\n // Sacred dropped above existing, so it keeps priority\n expect(positions.has('dropped')).to.be.false;\n expect(positions.has('existing')).to.be.true;\n });\n\n it('sacred keeps position when dropped at same top as existing node', () => {\n // Both at top=0 - sacred keeps priority since it's not below existing\n const allBounds: NodeBounds[] = [\n {\n uuid: 'existing',\n left: 100,\n top: 0,\n right: 200,\n bottom: 100,\n width: 100,\n height: 100\n },\n {\n uuid: 'dropped',\n left: 100,\n top: 0,\n right: 200,\n bottom: 100,\n width: 100,\n height: 100\n }\n ];\n\n const positions = calculateReflowPositions(['dropped'], allBounds);\n\n // Sacred at same top keeps priority - existing node moves\n expect(positions.has('dropped')).to.be.false;\n expect(positions.has('existing')).to.be.true;\n });\n });\n\n describe('edge cases', () => {\n it('handles empty allBounds array', () => {\n const positions = calculateReflowPositions(['moved'], []);\n expect(positions.size).to.equal(0);\n });\n\n it('handles single node (no other nodes to collide with)', () => {\n const movedBounds: NodeBounds = {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n const positions = calculateReflowPositions(['moved'], [movedBounds]);\n expect(positions.size).to.equal(0);\n });\n\n it('prevents infinite loops with complex collisions', () => {\n const movedBounds: NodeBounds = {\n uuid: 'moved',\n left: 100,\n top: 100,\n right: 200,\n bottom: 200,\n width: 100,\n height: 100\n };\n\n // Create a complex scenario with many overlapping nodes\n const allBounds: NodeBounds[] = [movedBounds];\n\n for (let i = 0; i < 20; i++) {\n allBounds.push({\n uuid: `node${i}`,\n left: 100 + i * 10,\n top: 100 + i * 10,\n right: 200 + i * 10,\n bottom: 200 + i * 10,\n width: 100,\n height: 100\n });\n }\n\n // Should complete without hanging\n const positions = calculateReflowPositions(['moved'], allBounds);\n\n // Should have resolved some collisions\n expect(positions.size).to.be.greaterThan(0);\n });\n });\n});\n"]}