@nyaruka/temba-components 0.131.0 → 0.131.2

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 (430) hide show
  1. package/.github/workflows/publish.yml +4 -1
  2. package/CHANGELOG.md +67 -1
  3. package/demo/data/flows/food-order.json +2 -2
  4. package/demo/data/flows/sample-flow.json +74 -125
  5. package/dist/static/svg/index.svg +1 -1
  6. package/dist/temba-components.js +1156 -619
  7. package/dist/temba-components.js.map +1 -1
  8. package/out-tsc/src/Icons.js +4 -1
  9. package/out-tsc/src/Icons.js.map +1 -1
  10. package/out-tsc/src/events.js.map +1 -1
  11. package/out-tsc/src/flow/CanvasMenu.js +200 -0
  12. package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
  13. package/out-tsc/src/flow/CanvasNode.js +327 -19
  14. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  15. package/out-tsc/src/flow/Editor.js +562 -66
  16. package/out-tsc/src/flow/Editor.js.map +1 -1
  17. package/out-tsc/src/flow/NodeEditor.js +240 -93
  18. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  19. package/out-tsc/src/flow/NodeTypeSelector.js +499 -0
  20. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
  21. package/out-tsc/src/flow/actions/add_contact_groups.js +3 -3
  22. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  23. package/out-tsc/src/flow/actions/add_contact_urn.js +62 -4
  24. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  25. package/out-tsc/src/flow/actions/add_input_labels.js +3 -3
  26. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  27. package/out-tsc/src/flow/actions/play_audio.js +2 -2
  28. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  29. package/out-tsc/src/flow/actions/remove_contact_groups.js +6 -5
  30. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  31. package/out-tsc/src/flow/actions/request_optin.js +2 -2
  32. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  33. package/out-tsc/src/flow/actions/say_msg.js +2 -2
  34. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  35. package/out-tsc/src/flow/actions/send_broadcast.js +76 -23
  36. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  37. package/out-tsc/src/flow/actions/send_email.js +4 -5
  38. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  39. package/out-tsc/src/flow/actions/send_msg.js +9 -19
  40. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  41. package/out-tsc/src/flow/actions/set_contact_channel.js +5 -9
  42. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  43. package/out-tsc/src/flow/actions/set_contact_field.js +19 -20
  44. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  45. package/out-tsc/src/flow/actions/set_contact_language.js +2 -2
  46. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  47. package/out-tsc/src/flow/actions/set_contact_name.js +2 -12
  48. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  49. package/out-tsc/src/flow/actions/set_contact_status.js +2 -2
  50. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  51. package/out-tsc/src/flow/actions/set_run_result.js +3 -3
  52. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  53. package/out-tsc/src/flow/actions/start_session.js +180 -6
  54. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  55. package/out-tsc/src/flow/config.js +11 -15
  56. package/out-tsc/src/flow/config.js.map +1 -1
  57. package/out-tsc/src/flow/currencies.js +45 -0
  58. package/out-tsc/src/flow/currencies.js.map +1 -0
  59. package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
  60. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
  61. package/out-tsc/src/flow/nodes/shared.js +17 -0
  62. package/out-tsc/src/flow/nodes/shared.js.map +1 -0
  63. package/out-tsc/src/flow/nodes/split_by_airtime.js +205 -5
  64. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  65. package/out-tsc/src/flow/nodes/split_by_contact_field.js +147 -3
  66. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  67. package/out-tsc/src/flow/nodes/split_by_expression.js +68 -2
  68. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  69. package/out-tsc/src/flow/nodes/split_by_groups.js +12 -9
  70. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  71. package/out-tsc/src/flow/nodes/split_by_intent.js +7 -0
  72. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
  73. package/out-tsc/src/flow/nodes/split_by_llm.js +3 -2
  74. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  75. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +2 -2
  76. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  77. package/out-tsc/src/flow/nodes/split_by_random.js +3 -3
  78. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  79. package/out-tsc/src/flow/nodes/split_by_resthook.js +108 -0
  80. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
  81. package/out-tsc/src/flow/nodes/split_by_run_result.js +206 -3
  82. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  83. package/out-tsc/src/flow/nodes/split_by_scheme.js +153 -2
  84. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  85. package/out-tsc/src/flow/nodes/split_by_subflow.js +6 -4
  86. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  87. package/out-tsc/src/flow/nodes/split_by_ticket.js +3 -2
  88. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  89. package/out-tsc/src/flow/nodes/split_by_webhook.js +3 -2
  90. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  91. package/out-tsc/src/flow/nodes/wait_for_audio.js +2 -2
  92. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -1
  93. package/out-tsc/src/flow/nodes/wait_for_digits.js +2 -2
  94. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  95. package/out-tsc/src/flow/nodes/wait_for_image.js +2 -2
  96. package/out-tsc/src/flow/nodes/wait_for_image.js.map +1 -1
  97. package/out-tsc/src/flow/nodes/wait_for_location.js +2 -2
  98. package/out-tsc/src/flow/nodes/wait_for_location.js.map +1 -1
  99. package/out-tsc/src/flow/nodes/wait_for_menu.js +2 -2
  100. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  101. package/out-tsc/src/flow/nodes/wait_for_response.js +32 -567
  102. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  103. package/out-tsc/src/flow/nodes/wait_for_video.js +2 -2
  104. package/out-tsc/src/flow/nodes/wait_for_video.js.map +1 -1
  105. package/out-tsc/src/flow/types.js +71 -12
  106. package/out-tsc/src/flow/types.js.map +1 -1
  107. package/out-tsc/src/flow/utils.js +101 -14
  108. package/out-tsc/src/flow/utils.js.map +1 -1
  109. package/out-tsc/src/form/ContactSearch.js +1 -1
  110. package/out-tsc/src/form/ContactSearch.js.map +1 -1
  111. package/out-tsc/src/form/FieldRenderer.js +2 -4
  112. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  113. package/out-tsc/src/interfaces.js +3 -0
  114. package/out-tsc/src/interfaces.js.map +1 -1
  115. package/out-tsc/src/list/SortableList.js +98 -33
  116. package/out-tsc/src/list/SortableList.js.map +1 -1
  117. package/out-tsc/src/live/ContactChat.js +15 -18
  118. package/out-tsc/src/live/ContactChat.js.map +1 -1
  119. package/out-tsc/src/store/AppState.js +53 -0
  120. package/out-tsc/src/store/AppState.js.map +1 -1
  121. package/out-tsc/src/utils.js +254 -13
  122. package/out-tsc/src/utils.js.map +1 -1
  123. package/out-tsc/temba-modules.js +4 -0
  124. package/out-tsc/temba-modules.js.map +1 -1
  125. package/out-tsc/test/ActionHelper.js +3 -3
  126. package/out-tsc/test/ActionHelper.js.map +1 -1
  127. package/out-tsc/test/NodeHelper.js +6 -3
  128. package/out-tsc/test/NodeHelper.js.map +1 -1
  129. package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
  130. package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
  131. package/out-tsc/test/actions/send_broadcast.test.js +148 -0
  132. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
  133. package/out-tsc/test/actions/send_email.test.js +17 -23
  134. package/out-tsc/test/actions/send_email.test.js.map +1 -1
  135. package/out-tsc/test/actions/send_msg.test.js +33 -15
  136. package/out-tsc/test/actions/send_msg.test.js.map +1 -1
  137. package/out-tsc/test/actions/start_session.test.js +116 -0
  138. package/out-tsc/test/actions/start_session.test.js.map +1 -0
  139. package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
  140. package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
  141. package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
  142. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
  143. package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
  144. package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
  145. package/out-tsc/test/nodes/split_by_random.test.js +3 -3
  146. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  147. package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
  148. package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
  149. package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
  150. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
  151. package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
  152. package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
  153. package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
  154. package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
  155. package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
  156. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  157. package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
  158. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  159. package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
  160. package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
  161. package/out-tsc/test/temba-canvas-menu.test.js +122 -0
  162. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
  163. package/out-tsc/test/temba-flow-editor-node.test.js +85 -2
  164. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  165. package/out-tsc/test/temba-flow-editor.test.js +7 -8
  166. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  167. package/out-tsc/test/temba-node-editor.test.js +3 -1
  168. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  169. package/out-tsc/test/temba-node-type-selector.test.js +115 -0
  170. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
  171. package/out-tsc/test/temba-omnibox.test.js +2 -1
  172. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  173. package/out-tsc/test/temba-sortable-list.test.js +51 -0
  174. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  175. package/out-tsc/test/temba-utils-index.test.js +1 -27
  176. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  177. package/out-tsc/test/utils.test.js +2 -0
  178. package/out-tsc/test/utils.test.js.map +1 -1
  179. package/package.json +2 -1
  180. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  181. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  182. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  183. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  184. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  185. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  186. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  187. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  188. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  189. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  190. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  191. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  192. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  193. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  194. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  195. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  196. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  197. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  198. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  199. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  200. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  201. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  202. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  203. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  204. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  205. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  206. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  207. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  208. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  209. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  210. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  211. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  212. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  213. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  214. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  215. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  216. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  217. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  218. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  219. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  220. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  221. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  222. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  223. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  224. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  225. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  226. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  227. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  228. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  229. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  230. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  231. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  232. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  233. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  234. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  235. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  236. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  237. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  238. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  239. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  240. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  241. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  242. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  243. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  244. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  245. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  246. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  247. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  248. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  249. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  250. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  251. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  252. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  253. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  254. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  255. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  256. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  257. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  258. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  259. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  260. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  261. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  262. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  263. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  264. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  265. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  266. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  267. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  268. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  269. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  270. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  271. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  272. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  273. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  274. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  275. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  276. package/screenshots/truth/canvas-menu/open.png +0 -0
  277. package/screenshots/truth/editor/router.png +0 -0
  278. package/screenshots/truth/editor/wait.png +0 -0
  279. package/screenshots/truth/list/fields-dragging.png +0 -0
  280. package/screenshots/truth/list/sortable-dragging.png +0 -0
  281. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  282. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  283. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  284. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  285. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  286. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  287. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  288. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  289. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  290. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  291. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  292. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  293. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  294. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  295. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  296. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  297. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  298. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  299. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  300. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  301. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  302. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  303. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  304. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  305. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  306. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  307. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  308. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  309. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  310. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  311. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  312. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  313. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  314. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  315. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  316. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  317. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  318. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  319. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  320. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  321. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  322. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  323. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  324. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  325. package/src/Icons.ts +4 -1
  326. package/src/events.ts +2 -6
  327. package/src/flow/CanvasMenu.ts +217 -0
  328. package/src/flow/CanvasNode.ts +408 -10
  329. package/src/flow/Editor.ts +683 -44
  330. package/src/flow/NodeEditor.ts +304 -125
  331. package/src/flow/NodeTypeSelector.ts +592 -0
  332. package/src/flow/actions/add_contact_groups.ts +4 -4
  333. package/src/flow/actions/add_contact_urn.ts +76 -4
  334. package/src/flow/actions/add_input_labels.ts +4 -4
  335. package/src/flow/actions/play_audio.ts +2 -2
  336. package/src/flow/actions/remove_contact_groups.ts +14 -6
  337. package/src/flow/actions/request_optin.ts +2 -2
  338. package/src/flow/actions/say_msg.ts +2 -2
  339. package/src/flow/actions/send_broadcast.ts +85 -23
  340. package/src/flow/actions/send_email.ts +10 -6
  341. package/src/flow/actions/send_msg.ts +22 -32
  342. package/src/flow/actions/set_contact_channel.ts +5 -11
  343. package/src/flow/actions/set_contact_field.ts +20 -25
  344. package/src/flow/actions/set_contact_language.ts +9 -4
  345. package/src/flow/actions/set_contact_name.ts +3 -15
  346. package/src/flow/actions/set_contact_status.ts +3 -3
  347. package/src/flow/actions/set_run_result.ts +4 -4
  348. package/src/flow/actions/start_session.ts +208 -6
  349. package/src/flow/config.ts +13 -15
  350. package/src/flow/currencies.ts +51 -0
  351. package/src/flow/nodes/shared-rules.ts +301 -0
  352. package/src/flow/nodes/shared.ts +18 -0
  353. package/src/flow/nodes/split_by_airtime.ts +238 -5
  354. package/src/flow/nodes/split_by_contact_field.ts +185 -3
  355. package/src/flow/nodes/split_by_expression.ts +94 -2
  356. package/src/flow/nodes/split_by_groups.ts +15 -10
  357. package/src/flow/nodes/split_by_intent.ts +7 -0
  358. package/src/flow/nodes/split_by_llm.ts +4 -3
  359. package/src/flow/nodes/split_by_llm_categorize.ts +4 -4
  360. package/src/flow/nodes/split_by_random.ts +5 -5
  361. package/src/flow/nodes/split_by_resthook.ts +130 -0
  362. package/src/flow/nodes/split_by_run_result.ts +249 -3
  363. package/src/flow/nodes/split_by_scheme.ts +192 -2
  364. package/src/flow/nodes/split_by_subflow.ts +6 -4
  365. package/src/flow/nodes/split_by_ticket.ts +4 -3
  366. package/src/flow/nodes/split_by_webhook.ts +6 -5
  367. package/src/flow/nodes/wait_for_audio.ts +2 -2
  368. package/src/flow/nodes/wait_for_digits.ts +2 -2
  369. package/src/flow/nodes/wait_for_image.ts +2 -2
  370. package/src/flow/nodes/wait_for_location.ts +2 -2
  371. package/src/flow/nodes/wait_for_menu.ts +2 -2
  372. package/src/flow/nodes/wait_for_response.ts +48 -679
  373. package/src/flow/nodes/wait_for_video.ts +2 -2
  374. package/src/flow/types.ts +109 -23
  375. package/src/flow/utils.ts +108 -14
  376. package/src/form/ContactSearch.ts +1 -1
  377. package/src/form/FieldRenderer.ts +2 -4
  378. package/src/interfaces.ts +3 -0
  379. package/src/list/SortableList.ts +109 -34
  380. package/src/live/ContactChat.ts +15 -18
  381. package/src/store/AppState.ts +69 -0
  382. package/src/store/flow-definition.d.ts +2 -5
  383. package/src/utils.ts +332 -12
  384. package/static/api/channels.json +46 -0
  385. package/static/api/resthooks.json +31 -0
  386. package/static/svg/index.svg +1 -1
  387. package/static/svg/work/traced/lightning-02.svg +1 -0
  388. package/static/svg/work/used/lightning-02.svg +3 -0
  389. package/temba-modules.ts +4 -0
  390. package/test/ActionHelper.ts +3 -3
  391. package/test/NodeHelper.ts +6 -3
  392. package/test/actions/add_contact_urn.test.ts +287 -0
  393. package/test/actions/send_broadcast.test.ts +190 -0
  394. package/test/actions/send_email.test.ts +17 -23
  395. package/test/actions/send_msg.test.ts +39 -15
  396. package/test/actions/start_session.test.ts +151 -0
  397. package/test/nodes/split_by_airtime.test.ts +673 -0
  398. package/test/nodes/split_by_contact_field.test.ts +451 -0
  399. package/test/nodes/split_by_expression.test.ts +751 -0
  400. package/test/nodes/split_by_random.test.ts +3 -3
  401. package/test/nodes/split_by_resthook.test.ts +398 -0
  402. package/test/nodes/split_by_run_result.test.ts +1109 -0
  403. package/test/nodes/split_by_scheme.test.ts +486 -0
  404. package/test/nodes/split_by_subflow.test.ts +381 -0
  405. package/test/nodes/wait_for_digits.test.ts +2 -2
  406. package/test/nodes/wait_for_response.test.ts +2 -1
  407. package/test/temba-action-drag-between-nodes.test.ts +301 -0
  408. package/test/temba-canvas-menu.test.ts +156 -0
  409. package/test/temba-flow-editor-node.test.ts +102 -2
  410. package/test/temba-flow-editor.test.ts +7 -8
  411. package/test/temba-node-editor.test.ts +3 -1
  412. package/test/temba-node-type-selector.test.ts +152 -0
  413. package/test/temba-omnibox.test.ts +2 -1
  414. package/test/temba-sortable-list.test.ts +69 -0
  415. package/test/temba-utils-index.test.ts +0 -35
  416. package/test/utils.test.ts +2 -0
  417. package/test-assets/contacts/history.json +14 -20
  418. package/web-dev-server.config.mjs +3 -1
  419. package/out-tsc/src/flow/actions/call_classifier.js +0 -11
  420. package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
  421. package/out-tsc/src/flow/actions/call_resthook.js +0 -11
  422. package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
  423. package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
  424. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
  425. package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
  426. package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
  427. package/src/flow/actions/call_classifier.ts +0 -12
  428. package/src/flow/actions/call_resthook.ts +0 -12
  429. package/src/flow/actions/split_by_expression_example.ts +0 -88
  430. package/src/flow/actions/transfer_airtime.ts +0 -12
@@ -2,6 +2,7 @@ import { __decorate } from "tslib";
2
2
  import { css, html } from 'lit';
3
3
  import { repeat } from 'lit/directives/repeat.js';
4
4
  import { ACTION_CONFIG, NODE_CONFIG } from './config';
5
+ import { ACTION_GROUP_METADATA, SPLIT_GROUP_METADATA } from './types';
5
6
  import { property } from 'lit/decorators.js';
6
7
  import { RapidElement } from '../RapidElement';
7
8
  import { getClasses } from '../utils';
@@ -126,7 +127,7 @@ export class CanvasNode extends RapidElement {
126
127
  pointer-events: auto; /* Ensure drag handle can receive events */
127
128
  }
128
129
  .title-spacer {
129
- width: 2em;
130
+ width: 1.8em;
130
131
 
131
132
  }
132
133
 
@@ -305,6 +306,36 @@ export class CanvasNode extends RapidElement {
305
306
  border-top-left-radius: var(--curvature);
306
307
  border-top-right-radius: var(--curvature);
307
308
  }
309
+
310
+ /* Add action button */
311
+ .add-action-button {
312
+ position: absolute;
313
+ bottom: 0.5em;
314
+ right: 0.5em;
315
+ width: 1.5em;
316
+ height: 1.5em;
317
+ border-radius: 50%;
318
+ background: var(--color-primary, #3b82f6);
319
+ color: white;
320
+ display: flex;
321
+ align-items: center;
322
+ justify-content: center;
323
+ cursor: pointer;
324
+ opacity: 0;
325
+ transition: opacity 200ms ease-in-out;
326
+ z-index: 10;
327
+ pointer-events: auto;
328
+ font-size: 0.9em;
329
+ }
330
+
331
+ .node.execute-actions:hover .add-action-button {
332
+ opacity: 0.8;
333
+ }
334
+
335
+ .add-action-button:hover {
336
+ opacity: 1 !important;
337
+ transform: scale(1.1);
338
+ }
308
339
  }`;
309
340
  }
310
341
  constructor() {
@@ -323,10 +354,34 @@ export class CanvasNode extends RapidElement {
323
354
  // Track node click state to distinguish from drag
324
355
  this.nodeClickStartPos = null;
325
356
  this.pendingNodeClick = null;
357
+ // Track the height of the action being dragged (captured at drag start)
358
+ this.draggedActionHeight = 0;
359
+ // Track external action drag (action being dragged from another node)
360
+ this.externalDragInfo = null;
326
361
  this.handleActionOrderChanged = this.handleActionOrderChanged.bind(this);
362
+ this.handleActionDragStart = this.handleActionDragStart.bind(this);
363
+ this.handleActionDragExternal = this.handleActionDragExternal.bind(this);
364
+ this.handleActionDragInternal = this.handleActionDragInternal.bind(this);
365
+ this.handleActionDragStop = this.handleActionDragStop.bind(this);
366
+ this.handleExternalActionDragOver =
367
+ this.handleExternalActionDragOver.bind(this);
368
+ this.handleExternalActionDrop = this.handleExternalActionDrop.bind(this);
369
+ this.handleExternalActionDragLeave =
370
+ this.handleExternalActionDragLeave.bind(this);
371
+ this.handleActionShowGhost = this.handleActionShowGhost.bind(this);
372
+ this.handleActionHideGhost = this.handleActionHideGhost.bind(this);
373
+ }
374
+ connectedCallback() {
375
+ super.connectedCallback();
376
+ // Listen for external action drag events from Editor
377
+ this.addEventListener('action-drag-over', this.handleExternalActionDragOver);
378
+ this.addEventListener('action-drop', this.handleExternalActionDrop);
379
+ this.addEventListener('action-drag-leave', this.handleExternalActionDragLeave);
380
+ this.addEventListener('action-show-ghost', this.handleActionShowGhost);
381
+ this.addEventListener('action-hide-ghost', this.handleActionHideGhost);
327
382
  }
328
383
  updated(changes) {
329
- var _a;
384
+ var _b;
330
385
  super.updated(changes);
331
386
  if (changes.has('node')) {
332
387
  // Only proceed if plumber is available (for tests that don't set it up)
@@ -348,13 +403,19 @@ export class CanvasNode extends RapidElement {
348
403
  const ele = this.parentElement;
349
404
  if (ele) {
350
405
  const rect = ele.getBoundingClientRect();
351
- (_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
406
+ (_b = getStore()) === null || _b === void 0 ? void 0 : _b.getState().expandCanvas(this.ui.position.left + rect.width, this.ui.position.top + rect.height);
352
407
  }
353
408
  }
354
409
  }
355
410
  disconnectedCallback() {
356
411
  // Remove the event listener when the component is removed
357
412
  super.disconnectedCallback();
413
+ // Remove external drag event listeners
414
+ this.removeEventListener('action-drag-over', this.handleExternalActionDragOver);
415
+ this.removeEventListener('action-drop', this.handleExternalActionDrop);
416
+ this.removeEventListener('action-drag-leave', this.handleExternalActionDragLeave);
417
+ this.removeEventListener('action-show-ghost', this.handleActionShowGhost);
418
+ this.removeEventListener('action-hide-ghost', this.handleActionHideGhost);
358
419
  // Clear any pending exit removal timeouts
359
420
  this.exitRemovalTimeouts.forEach((timeoutId) => {
360
421
  clearTimeout(timeoutId);
@@ -401,7 +462,7 @@ export class CanvasNode extends RapidElement {
401
462
  this.exitRemovalTimeouts.set(exitId, timeoutId);
402
463
  }
403
464
  disconnectExit(exit) {
404
- var _a;
465
+ var _b;
405
466
  const exitId = exit.uuid;
406
467
  // Clear the UI state
407
468
  this.exitRemovingState.delete(exitId);
@@ -420,7 +481,7 @@ export class CanvasNode extends RapidElement {
420
481
  const updatedExits = this.node.exits.map((e) => e.uuid === exitId ? updatedExit : e);
421
482
  // Update the node
422
483
  const updatedNode = { ...this.node, exits: updatedExits };
423
- (_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().updateNode(this.node.uuid, updatedNode);
484
+ (_b = getStore()) === null || _b === void 0 ? void 0 : _b.getState().updateNode(this.node.uuid, updatedNode);
424
485
  // Request update to reflect changes
425
486
  this.requestUpdate();
426
487
  }
@@ -450,7 +511,7 @@ export class CanvasNode extends RapidElement {
450
511
  }
451
512
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
452
513
  removeAction(action, _index) {
453
- var _a;
514
+ var _b;
454
515
  const actionId = action.uuid;
455
516
  // Clear the UI state
456
517
  this.actionRemovingState.delete(actionId);
@@ -470,7 +531,7 @@ export class CanvasNode extends RapidElement {
470
531
  else {
471
532
  // Update the node with remaining actions
472
533
  const updatedNode = { ...this.node, actions: updatedActions };
473
- (_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().updateNode(this.node.uuid, updatedNode);
534
+ (_b = getStore()) === null || _b === void 0 ? void 0 : _b.getState().updateNode(this.node.uuid, updatedNode);
474
535
  // Request update to reflect changes
475
536
  this.requestUpdate();
476
537
  }
@@ -514,7 +575,7 @@ export class CanvasNode extends RapidElement {
514
575
  });
515
576
  }
516
577
  handleActionOrderChanged(event) {
517
- var _a;
578
+ var _b;
518
579
  const [fromIdx, toIdx] = event.detail.swap;
519
580
  // swap our actions
520
581
  const newActions = [...this.node.actions];
@@ -524,7 +585,73 @@ export class CanvasNode extends RapidElement {
524
585
  // since the editor will update us from it's definition subscription
525
586
  // but it makes testing a lot easier
526
587
  this.node = { ...this.node, actions: newActions };
527
- (_a = getStore()) === null || _a === void 0 ? void 0 : _a.getState().updateNode(this.node.uuid, { ...this.node, actions: newActions });
588
+ (_b = getStore()) === null || _b === void 0 ? void 0 : _b.getState().updateNode(this.node.uuid, { ...this.node, actions: newActions });
589
+ }
590
+ handleActionDragStart(event) {
591
+ // Capture the height of the action being dragged
592
+ const actionId = event.detail.id;
593
+ const actionElement = this.querySelector(`#${actionId}`);
594
+ if (actionElement) {
595
+ const rect = actionElement.getBoundingClientRect();
596
+ this.draggedActionHeight = rect.height;
597
+ }
598
+ else {
599
+ // Fallback to a reasonable default
600
+ this.draggedActionHeight = 60;
601
+ }
602
+ }
603
+ handleActionDragExternal(event) {
604
+ // stop propagation of the original event from SortableList
605
+ event.stopPropagation();
606
+ // get the action being dragged
607
+ const actionId = event.detail.id;
608
+ const splitId = actionId.split('-');
609
+ if (splitId.length < 2 || isNaN(parseInt(splitId[1], 10))) {
610
+ // invalid format, do not proceed
611
+ return;
612
+ }
613
+ const actionIndex = parseInt(splitId[1], 10);
614
+ const action = this.node.actions[actionIndex];
615
+ // fire event to editor to show canvas drop preview, including the captured height
616
+ this.fireCustomEvent(CustomEventType.DragExternal, {
617
+ action,
618
+ nodeUuid: this.node.uuid,
619
+ actionIndex,
620
+ mouseX: event.detail.mouseX,
621
+ mouseY: event.detail.mouseY,
622
+ actionHeight: this.draggedActionHeight
623
+ });
624
+ }
625
+ handleActionDragInternal(_event) {
626
+ // stop propagation of the original event from SortableList
627
+ _event.stopPropagation();
628
+ // fire event to editor to hide canvas drop preview
629
+ this.fireCustomEvent(CustomEventType.DragInternal, {});
630
+ }
631
+ handleActionDragStop(event) {
632
+ const isExternal = event.detail.isExternal;
633
+ if (isExternal) {
634
+ // stop propagation of the original event from SortableList
635
+ event.stopPropagation();
636
+ // get the action being dragged
637
+ const actionId = event.detail.id;
638
+ const split = actionId.split('-');
639
+ if (split.length < 2 || isNaN(Number(split[1]))) {
640
+ // invalid actionId format, do not proceed
641
+ return;
642
+ }
643
+ const actionIndex = parseInt(split[1], 10);
644
+ const action = this.node.actions[actionIndex];
645
+ // fire event to editor to create new node
646
+ this.fireCustomEvent(CustomEventType.DragStop, {
647
+ action,
648
+ nodeUuid: this.node.uuid,
649
+ actionIndex,
650
+ isExternal: true,
651
+ mouseX: event.detail.mouseX,
652
+ mouseY: event.detail.mouseY
653
+ });
654
+ }
528
655
  }
529
656
  handleActionMouseDown(event, action) {
530
657
  // Don't handle clicks on the remove button, drag handle, or when action is in removing state
@@ -690,12 +817,138 @@ export class CanvasNode extends RapidElement {
690
817
  this.nodeClickStartPos = null;
691
818
  this.pendingNodeClick = null;
692
819
  }
820
+ handleAddActionClick(event) {
821
+ event.preventDefault();
822
+ event.stopPropagation();
823
+ // Fire event to request adding a new action to this node
824
+ this.fireCustomEvent(CustomEventType.AddActionRequested, {
825
+ nodeUuid: this.node.uuid
826
+ });
827
+ }
828
+ calculateDropIndex(mouseY) {
829
+ var _b, _c;
830
+ // Get the sortable list element
831
+ const sortableList = this.querySelector('temba-sortable-list');
832
+ if (!sortableList || !this.node.actions)
833
+ return (_c = (_b = this.node.actions) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
834
+ // Get all action elements
835
+ const actionElements = Array.from(sortableList.querySelectorAll('.action.sortable'));
836
+ if (actionElements.length === 0) {
837
+ return 0;
838
+ }
839
+ // Find where to insert based on mouse Y position
840
+ for (let i = 0; i < actionElements.length; i++) {
841
+ const actionElement = actionElements[i];
842
+ const rect = actionElement.getBoundingClientRect();
843
+ const centerY = rect.top + rect.height / 2;
844
+ if (mouseY < centerY) {
845
+ return i;
846
+ }
847
+ }
848
+ // If past all elements, insert at the end
849
+ return actionElements.length;
850
+ }
851
+ handleExternalActionDragOver(event) {
852
+ // Only handle if this is an execute_actions node
853
+ if (this.ui.type !== 'execute_actions')
854
+ return;
855
+ const { action, sourceNodeUuid, actionIndex, mouseY, actionHeight } = event.detail;
856
+ // Don't accept drops from the same node
857
+ if (sourceNodeUuid === this.node.uuid)
858
+ return;
859
+ // Calculate where to drop
860
+ const dropIndex = this.calculateDropIndex(mouseY);
861
+ // Store the drag info
862
+ this.externalDragInfo = {
863
+ action,
864
+ sourceNodeUuid,
865
+ actionIndex,
866
+ dropIndex,
867
+ actionHeight: actionHeight || 60 // fallback to 60px if not provided
868
+ };
869
+ // Request update to show placeholder
870
+ this.requestUpdate();
871
+ }
872
+ handleExternalActionDragLeave(_event) {
873
+ // Clear external drag state when drag leaves this node
874
+ this.externalDragInfo = null;
875
+ this.requestUpdate();
876
+ }
877
+ handleActionShowGhost(_event) {
878
+ // Show the ghost element in the sortable list
879
+ const sortableList = this.querySelector('temba-sortable-list');
880
+ if (sortableList) {
881
+ const ghostElement = document.querySelector('.ghost');
882
+ if (ghostElement) {
883
+ ghostElement.style.display = 'block';
884
+ }
885
+ }
886
+ }
887
+ handleActionHideGhost(_event) {
888
+ // Hide the ghost element in the sortable list
889
+ const sortableList = this.querySelector('temba-sortable-list');
890
+ if (sortableList) {
891
+ const ghostElement = document.querySelector('.ghost');
892
+ if (ghostElement) {
893
+ ghostElement.style.display = 'none';
894
+ }
895
+ }
896
+ }
897
+ handleExternalActionDrop(event) {
898
+ var _b, _c, _d, _e, _f, _g;
899
+ // Only handle if this is an execute_actions node
900
+ if (this.ui.type !== 'execute_actions')
901
+ return;
902
+ const { action, sourceNodeUuid, actionIndex } = event.detail;
903
+ // Don't accept drops from the same node
904
+ if (sourceNodeUuid === this.node.uuid)
905
+ return;
906
+ // Get the drop index from our tracking state
907
+ const dropIndex = (_e = (_c = (_b = this.externalDragInfo) === null || _b === void 0 ? void 0 : _b.dropIndex) !== null && _c !== void 0 ? _c : (_d = this.node.actions) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0;
908
+ // Clear external drag state
909
+ this.externalDragInfo = null;
910
+ // Remove the action from the source node
911
+ const store = getStore();
912
+ if (!store)
913
+ return;
914
+ const flowDefinition = store.getState().flowDefinition;
915
+ if (!flowDefinition)
916
+ return;
917
+ const sourceNode = flowDefinition.nodes.find((n) => n.uuid === sourceNodeUuid);
918
+ if (sourceNode) {
919
+ const updatedSourceActions = sourceNode.actions.filter((_a, idx) => idx !== actionIndex);
920
+ // If source node has no actions left, remove it
921
+ if (updatedSourceActions.length === 0) {
922
+ this.fireCustomEvent(CustomEventType.NodeDeleted, {
923
+ uuid: sourceNodeUuid
924
+ });
925
+ }
926
+ else {
927
+ // Update source node
928
+ const updatedSourceNode = {
929
+ ...sourceNode,
930
+ actions: updatedSourceActions
931
+ };
932
+ (_f = getStore()) === null || _f === void 0 ? void 0 : _f.getState().updateNode(sourceNodeUuid, updatedSourceNode);
933
+ }
934
+ }
935
+ // Add the action to this node at the calculated position
936
+ const newActions = [...this.node.actions];
937
+ newActions.splice(dropIndex, 0, action);
938
+ const updatedNode = { ...this.node, actions: newActions };
939
+ (_g = getStore()) === null || _g === void 0 ? void 0 : _g.getState().updateNode(this.node.uuid, updatedNode);
940
+ // Request update
941
+ this.requestUpdate();
942
+ }
693
943
  renderTitle(config, action, index, isRemoving = false) {
694
- var _a, _b;
695
- return html `<div class="cn-title" style="background:${config.color}">
696
- ${((_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.length) > 1
944
+ var _b, _c, _d;
945
+ const color = config.group
946
+ ? (_b = ACTION_GROUP_METADATA[config.group]) === null || _b === void 0 ? void 0 : _b.color
947
+ : '#aaaaaa';
948
+ return html `<div class="cn-title" style="background:${color}">
949
+ ${((_d = (_c = this.node) === null || _c === void 0 ? void 0 : _c.actions) === null || _d === void 0 ? void 0 : _d.length) > 1
697
950
  ? html `<temba-icon class="drag-handle" name="sort"></temba-icon>`
698
- : null}
951
+ : html `<div class="title-spacer"></div>`}
699
952
 
700
953
  <div class="name">${isRemoving ? 'Remove?' : config.name}</div>
701
954
  <div
@@ -707,13 +960,25 @@ export class CanvasNode extends RapidElement {
707
960
  </div>
708
961
  </div>`;
709
962
  }
710
- renderNodeTitle(config, isRemoving = false) {
963
+ renderNodeTitle(config, node, ui, isRemoving = false) {
964
+ var _b, _c;
965
+ // Get color from the appropriate metadata (either ACTION or SPLIT)
966
+ const color = config.group
967
+ ? ((_b = ACTION_GROUP_METADATA[config.group]) === null || _b === void 0 ? void 0 : _b.color) ||
968
+ ((_c = SPLIT_GROUP_METADATA[config.group]) === null || _c === void 0 ? void 0 : _c.color)
969
+ : '#aaaaaa';
711
970
  return html `<div
712
971
  class="cn-title ${isRemoving ? 'removing' : ''}"
713
- style="background:${config.color}"
972
+ style="background:${color}"
714
973
  >
715
974
  <div class="title-spacer"></div>
716
- <div class="name">${isRemoving ? 'Remove?' : config.name}</div>
975
+ <div class="name">
976
+ ${isRemoving
977
+ ? 'Remove?'
978
+ : config.renderTitle
979
+ ? config.renderTitle(node, ui)
980
+ : html `${config.name}`}
981
+ </div>
717
982
  <div
718
983
  class="remove-button"
719
984
  @click=${(e) => this.handleNodeRemoveClick(e)}
@@ -723,6 +988,14 @@ export class CanvasNode extends RapidElement {
723
988
  </div>
724
989
  </div>`;
725
990
  }
991
+ renderDropPlaceholder() {
992
+ var _b;
993
+ const height = ((_b = this.externalDragInfo) === null || _b === void 0 ? void 0 : _b.actionHeight) || 60;
994
+ return html `<div
995
+ class="action sortable drop-placeholder"
996
+ style="height: ${height}px; background: #f3f4f6; border: 2px dashed #d1d5db; border-radius: var(--curvature);"
997
+ ></div>`;
998
+ }
726
999
  renderAction(node, action, index) {
727
1000
  const config = ACTION_CONFIG[action.type];
728
1001
  const isRemoving = this.actionRemovingState.has(action.uuid);
@@ -760,6 +1033,25 @@ export class CanvasNode extends RapidElement {
760
1033
  ${action.type}
761
1034
  </div>`;
762
1035
  }
1036
+ renderActionsWithPlaceholder() {
1037
+ if (!this.externalDragInfo) {
1038
+ // No external drag, render normally
1039
+ return this.node.actions.map((action, index) => this.renderAction(this.node, action, index));
1040
+ }
1041
+ // Insert placeholder at the drop index
1042
+ const result = [];
1043
+ for (let i = 0; i < this.node.actions.length; i++) {
1044
+ if (i === this.externalDragInfo.dropIndex) {
1045
+ result.push(this.renderDropPlaceholder());
1046
+ }
1047
+ result.push(this.renderAction(this.node, this.node.actions[i], i));
1048
+ }
1049
+ // If dropping at the end, add placeholder after all actions
1050
+ if (this.externalDragInfo.dropIndex >= this.node.actions.length) {
1051
+ result.push(this.renderDropPlaceholder());
1052
+ }
1053
+ return result;
1054
+ }
763
1055
  renderRouter(router, ui) {
764
1056
  const nodeConfig = NODE_CONFIG[ui.type];
765
1057
  if (nodeConfig) {
@@ -830,17 +1122,24 @@ export class CanvasNode extends RapidElement {
830
1122
  @mouseup=${(e) => this.handleNodeMouseUp(e)}
831
1123
  style="cursor: pointer;"
832
1124
  >
833
- ${this.renderNodeTitle(nodeConfig, this.actionRemovingState.has(this.node.uuid))}
834
- ${nodeConfig.render ? nodeConfig.render(this.node) : null}
1125
+ ${this.renderNodeTitle(nodeConfig, this.node, this.ui, this.actionRemovingState.has(this.node.uuid))}
1126
+ ${nodeConfig.render
1127
+ ? nodeConfig.render(this.node, this.ui)
1128
+ : null}
835
1129
  </div>
836
1130
  </div>`
837
1131
  : this.node.actions.length > 0
838
1132
  ? this.ui.type === 'execute_actions'
839
1133
  ? html `<temba-sortable-list
840
1134
  dragHandle="drag-handle"
1135
+ externalDrag
841
1136
  @temba-order-changed="${this.handleActionOrderChanged}"
1137
+ @temba-drag-start="${this.handleActionDragStart}"
1138
+ @temba-drag-external="${this.handleActionDragExternal}"
1139
+ @temba-drag-internal="${this.handleActionDragInternal}"
1140
+ @temba-drag-stop="${this.handleActionDragStop}"
842
1141
  >
843
- ${this.node.actions.map((action, index) => this.renderAction(this.node, action, index))}
1142
+ ${this.renderActionsWithPlaceholder()}
844
1143
  </temba-sortable-list>`
845
1144
  : html `${this.node.actions.map((action, index) => this.renderAction(this.node, action, index))}`
846
1145
  : ''}
@@ -850,6 +1149,15 @@ export class CanvasNode extends RapidElement {
850
1149
  : html `<div class="action-exits">
851
1150
  ${repeat(this.node.exits, (exit) => exit.uuid, (exit) => this.renderExit(exit))}
852
1151
  </div>`}
1152
+ ${this.ui.type === 'execute_actions'
1153
+ ? html `<div
1154
+ class="add-action-button"
1155
+ @click=${(e) => this.handleAddActionClick(e)}
1156
+ title="Add action"
1157
+ >
1158
+ <temba-icon name="add"></temba-icon>
1159
+ </div>`
1160
+ : ''}
853
1161
  </div>
854
1162
  `;
855
1163
  }