@nyaruka/temba-components 0.131.1 → 0.131.3

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 (485) hide show
  1. package/.github/workflows/publish.yml +4 -1
  2. package/CHANGELOG.md +75 -1
  3. package/demo/components/floating-tabs/example.html +400 -0
  4. package/demo/components/flow/index.html +1 -1
  5. package/demo/data/flows/food-order.json +2 -2
  6. package/demo/data/flows/sample-flow.json +113 -125
  7. package/demo/data/flows/voicemail.json +613 -0
  8. package/demo/index.html +6 -0
  9. package/dist/locales/es.js +5 -5
  10. package/dist/locales/es.js.map +1 -1
  11. package/dist/locales/fr.js +5 -5
  12. package/dist/locales/fr.js.map +1 -1
  13. package/dist/locales/locale-codes.js +11 -2
  14. package/dist/locales/locale-codes.js.map +1 -1
  15. package/dist/locales/pt.js +5 -5
  16. package/dist/locales/pt.js.map +1 -1
  17. package/dist/static/svg/index.svg +1 -1
  18. package/dist/temba-components.js +1773 -662
  19. package/dist/temba-components.js.map +1 -1
  20. package/out-tsc/src/Icons.js +4 -1
  21. package/out-tsc/src/Icons.js.map +1 -1
  22. package/out-tsc/src/display/FloatingTab.js +167 -0
  23. package/out-tsc/src/display/FloatingTab.js.map +1 -0
  24. package/out-tsc/src/display/ProgressBar.js +22 -2
  25. package/out-tsc/src/display/ProgressBar.js.map +1 -1
  26. package/out-tsc/src/events.js.map +1 -1
  27. package/out-tsc/src/flow/CanvasMenu.js +200 -0
  28. package/out-tsc/src/flow/CanvasMenu.js.map +1 -0
  29. package/out-tsc/src/flow/CanvasNode.js +489 -47
  30. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  31. package/out-tsc/src/flow/Editor.js +1417 -67
  32. package/out-tsc/src/flow/Editor.js.map +1 -1
  33. package/out-tsc/src/flow/NodeEditor.js +479 -112
  34. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  35. package/out-tsc/src/flow/NodeTypeSelector.js +540 -0
  36. package/out-tsc/src/flow/NodeTypeSelector.js.map +1 -0
  37. package/out-tsc/src/flow/StickyNote.js +12 -3
  38. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  39. package/out-tsc/src/flow/actions/add_contact_groups.js +4 -3
  40. package/out-tsc/src/flow/actions/add_contact_groups.js.map +1 -1
  41. package/out-tsc/src/flow/actions/add_contact_urn.js +63 -4
  42. package/out-tsc/src/flow/actions/add_contact_urn.js.map +1 -1
  43. package/out-tsc/src/flow/actions/add_input_labels.js +4 -3
  44. package/out-tsc/src/flow/actions/add_input_labels.js.map +1 -1
  45. package/out-tsc/src/flow/actions/play_audio.js +3 -2
  46. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  47. package/out-tsc/src/flow/actions/remove_contact_groups.js +7 -5
  48. package/out-tsc/src/flow/actions/remove_contact_groups.js.map +1 -1
  49. package/out-tsc/src/flow/actions/request_optin.js +3 -2
  50. package/out-tsc/src/flow/actions/request_optin.js.map +1 -1
  51. package/out-tsc/src/flow/actions/say_msg.js +3 -2
  52. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  53. package/out-tsc/src/flow/actions/send_broadcast.js +77 -23
  54. package/out-tsc/src/flow/actions/send_broadcast.js.map +1 -1
  55. package/out-tsc/src/flow/actions/send_email.js +5 -5
  56. package/out-tsc/src/flow/actions/send_email.js.map +1 -1
  57. package/out-tsc/src/flow/actions/send_msg.js +101 -21
  58. package/out-tsc/src/flow/actions/send_msg.js.map +1 -1
  59. package/out-tsc/src/flow/actions/set_contact_channel.js +6 -9
  60. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  61. package/out-tsc/src/flow/actions/set_contact_field.js +20 -20
  62. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  63. package/out-tsc/src/flow/actions/set_contact_language.js +3 -2
  64. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  65. package/out-tsc/src/flow/actions/set_contact_name.js +3 -12
  66. package/out-tsc/src/flow/actions/set_contact_name.js.map +1 -1
  67. package/out-tsc/src/flow/actions/set_contact_status.js +3 -2
  68. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  69. package/out-tsc/src/flow/actions/set_run_result.js +4 -3
  70. package/out-tsc/src/flow/actions/set_run_result.js.map +1 -1
  71. package/out-tsc/src/flow/actions/start_session.js +181 -6
  72. package/out-tsc/src/flow/actions/start_session.js.map +1 -1
  73. package/out-tsc/src/flow/config.js +11 -23
  74. package/out-tsc/src/flow/config.js.map +1 -1
  75. package/out-tsc/src/flow/currencies.js +45 -0
  76. package/out-tsc/src/flow/currencies.js.map +1 -0
  77. package/out-tsc/src/flow/nodes/shared-rules.js +257 -0
  78. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -0
  79. package/out-tsc/src/flow/nodes/shared.js +71 -0
  80. package/out-tsc/src/flow/nodes/shared.js.map +1 -0
  81. package/out-tsc/src/flow/nodes/split_by_airtime.js +211 -5
  82. package/out-tsc/src/flow/nodes/split_by_airtime.js.map +1 -1
  83. package/out-tsc/src/flow/nodes/split_by_contact_field.js +152 -3
  84. package/out-tsc/src/flow/nodes/split_by_contact_field.js.map +1 -1
  85. package/out-tsc/src/flow/nodes/split_by_expression.js +73 -2
  86. package/out-tsc/src/flow/nodes/split_by_expression.js.map +1 -1
  87. package/out-tsc/src/flow/nodes/split_by_groups.js +18 -10
  88. package/out-tsc/src/flow/nodes/split_by_groups.js.map +1 -1
  89. package/out-tsc/src/flow/nodes/split_by_intent.js +8 -0
  90. package/out-tsc/src/flow/nodes/split_by_intent.js.map +1 -0
  91. package/out-tsc/src/flow/nodes/split_by_llm.js +11 -3
  92. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -1
  93. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +10 -3
  94. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  95. package/out-tsc/src/flow/nodes/split_by_random.js +10 -4
  96. package/out-tsc/src/flow/nodes/split_by_random.js.map +1 -1
  97. package/out-tsc/src/flow/nodes/split_by_resthook.js +113 -0
  98. package/out-tsc/src/flow/nodes/split_by_resthook.js.map +1 -0
  99. package/out-tsc/src/flow/nodes/split_by_run_result.js +211 -3
  100. package/out-tsc/src/flow/nodes/split_by_run_result.js.map +1 -1
  101. package/out-tsc/src/flow/nodes/split_by_scheme.js +158 -2
  102. package/out-tsc/src/flow/nodes/split_by_scheme.js.map +1 -1
  103. package/out-tsc/src/flow/nodes/split_by_subflow.js +13 -5
  104. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  105. package/out-tsc/src/flow/nodes/split_by_ticket.js +10 -3
  106. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  107. package/out-tsc/src/flow/nodes/split_by_webhook.js +10 -3
  108. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  109. package/out-tsc/src/flow/nodes/wait_for_digits.js +3 -2
  110. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  111. package/out-tsc/src/flow/nodes/wait_for_menu.js +3 -2
  112. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  113. package/out-tsc/src/flow/nodes/wait_for_response.js +38 -568
  114. package/out-tsc/src/flow/nodes/wait_for_response.js.map +1 -1
  115. package/out-tsc/src/flow/types.js +86 -12
  116. package/out-tsc/src/flow/types.js.map +1 -1
  117. package/out-tsc/src/flow/utils.js +101 -14
  118. package/out-tsc/src/flow/utils.js.map +1 -1
  119. package/out-tsc/src/form/FieldRenderer.js +2 -4
  120. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  121. package/out-tsc/src/interfaces.js +3 -0
  122. package/out-tsc/src/interfaces.js.map +1 -1
  123. package/out-tsc/src/layout/FloatingWindow.js +346 -0
  124. package/out-tsc/src/layout/FloatingWindow.js.map +1 -0
  125. package/out-tsc/src/list/SortableList.js +98 -33
  126. package/out-tsc/src/list/SortableList.js.map +1 -1
  127. package/out-tsc/src/live/ContactChat.js +6 -25
  128. package/out-tsc/src/live/ContactChat.js.map +1 -1
  129. package/out-tsc/src/locales/es.js +5 -5
  130. package/out-tsc/src/locales/es.js.map +1 -1
  131. package/out-tsc/src/locales/fr.js +5 -5
  132. package/out-tsc/src/locales/fr.js.map +1 -1
  133. package/out-tsc/src/locales/locale-codes.js +11 -2
  134. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  135. package/out-tsc/src/locales/pt.js +5 -5
  136. package/out-tsc/src/locales/pt.js.map +1 -1
  137. package/out-tsc/src/store/AppState.js +120 -0
  138. package/out-tsc/src/store/AppState.js.map +1 -1
  139. package/out-tsc/src/utils.js +254 -13
  140. package/out-tsc/src/utils.js.map +1 -1
  141. package/out-tsc/temba-modules.js +8 -0
  142. package/out-tsc/temba-modules.js.map +1 -1
  143. package/out-tsc/test/ActionHelper.js +3 -3
  144. package/out-tsc/test/ActionHelper.js.map +1 -1
  145. package/out-tsc/test/NodeHelper.js +6 -3
  146. package/out-tsc/test/NodeHelper.js.map +1 -1
  147. package/out-tsc/test/actions/add_contact_urn.test.js +202 -0
  148. package/out-tsc/test/actions/add_contact_urn.test.js.map +1 -0
  149. package/out-tsc/test/actions/send_broadcast.test.js +148 -0
  150. package/out-tsc/test/actions/send_broadcast.test.js.map +1 -0
  151. package/out-tsc/test/actions/send_email.test.js +17 -23
  152. package/out-tsc/test/actions/send_email.test.js.map +1 -1
  153. package/out-tsc/test/actions/send_msg.test.js +33 -15
  154. package/out-tsc/test/actions/send_msg.test.js.map +1 -1
  155. package/out-tsc/test/actions/start_session.test.js +116 -0
  156. package/out-tsc/test/actions/start_session.test.js.map +1 -0
  157. package/out-tsc/test/nodes/split_by_airtime.test.js +604 -0
  158. package/out-tsc/test/nodes/split_by_airtime.test.js.map +1 -0
  159. package/out-tsc/test/nodes/split_by_contact_field.test.js +387 -0
  160. package/out-tsc/test/nodes/split_by_contact_field.test.js.map +1 -0
  161. package/out-tsc/test/nodes/split_by_expression.test.js +614 -0
  162. package/out-tsc/test/nodes/split_by_expression.test.js.map +1 -0
  163. package/out-tsc/test/nodes/split_by_random.test.js +3 -3
  164. package/out-tsc/test/nodes/split_by_random.test.js.map +1 -1
  165. package/out-tsc/test/nodes/split_by_resthook.test.js +337 -0
  166. package/out-tsc/test/nodes/split_by_resthook.test.js.map +1 -0
  167. package/out-tsc/test/nodes/split_by_run_result.test.js +920 -0
  168. package/out-tsc/test/nodes/split_by_run_result.test.js.map +1 -0
  169. package/out-tsc/test/nodes/split_by_scheme.test.js +399 -0
  170. package/out-tsc/test/nodes/split_by_scheme.test.js.map +1 -0
  171. package/out-tsc/test/nodes/split_by_subflow.test.js +333 -0
  172. package/out-tsc/test/nodes/split_by_subflow.test.js.map +1 -0
  173. package/out-tsc/test/nodes/wait_for_digits.test.js +2 -2
  174. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  175. package/out-tsc/test/nodes/wait_for_response.test.js +2 -1
  176. package/out-tsc/test/nodes/wait_for_response.test.js.map +1 -1
  177. package/out-tsc/test/temba-action-drag-between-nodes.test.js +252 -0
  178. package/out-tsc/test/temba-action-drag-between-nodes.test.js.map +1 -0
  179. package/out-tsc/test/temba-canvas-menu.test.js +122 -0
  180. package/out-tsc/test/temba-canvas-menu.test.js.map +1 -0
  181. package/out-tsc/test/temba-floating-tab.test.js +91 -0
  182. package/out-tsc/test/temba-floating-tab.test.js.map +1 -0
  183. package/out-tsc/test/temba-floating-window.test.js +301 -0
  184. package/out-tsc/test/temba-floating-window.test.js.map +1 -0
  185. package/out-tsc/test/temba-flow-editor-node.test.js +202 -2
  186. package/out-tsc/test/temba-flow-editor-node.test.js.map +1 -1
  187. package/out-tsc/test/temba-flow-editor.test.js +7 -8
  188. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  189. package/out-tsc/test/temba-localization.test.js +471 -0
  190. package/out-tsc/test/temba-localization.test.js.map +1 -0
  191. package/out-tsc/test/temba-node-editor.test.js +3 -1
  192. package/out-tsc/test/temba-node-editor.test.js.map +1 -1
  193. package/out-tsc/test/temba-node-type-selector.test.js +265 -0
  194. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -0
  195. package/out-tsc/test/temba-omnibox.test.js +2 -1
  196. package/out-tsc/test/temba-omnibox.test.js.map +1 -1
  197. package/out-tsc/test/temba-sortable-list.test.js +51 -0
  198. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  199. package/out-tsc/test/temba-utils-index.test.js +1 -27
  200. package/out-tsc/test/temba-utils-index.test.js.map +1 -1
  201. package/out-tsc/test/utils.test.js +20 -0
  202. package/out-tsc/test/utils.test.js.map +1 -1
  203. package/package.json +2 -1
  204. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  205. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  206. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  207. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  208. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  209. package/screenshots/truth/actions/add_contact_groups/render/descriptive-group-names.png +0 -0
  210. package/screenshots/truth/actions/add_contact_groups/render/long-group-names.png +0 -0
  211. package/screenshots/truth/actions/add_contact_groups/render/many-groups.png +0 -0
  212. package/screenshots/truth/actions/add_contact_groups/render/multiple-groups.png +0 -0
  213. package/screenshots/truth/actions/add_contact_groups/render/single-group.png +0 -0
  214. package/screenshots/truth/actions/add_contact_urn/editor/expression-facebook.png +0 -0
  215. package/screenshots/truth/actions/add_contact_urn/editor/expression-phone.png +0 -0
  216. package/screenshots/truth/actions/add_contact_urn/editor/facebook-id.png +0 -0
  217. package/screenshots/truth/actions/add_contact_urn/editor/instagram-handle.png +0 -0
  218. package/screenshots/truth/actions/add_contact_urn/editor/line-id.png +0 -0
  219. package/screenshots/truth/actions/add_contact_urn/editor/phone-number.png +0 -0
  220. package/screenshots/truth/actions/add_contact_urn/editor/telegram-id.png +0 -0
  221. package/screenshots/truth/actions/add_contact_urn/editor/viber-id.png +0 -0
  222. package/screenshots/truth/actions/add_contact_urn/editor/wechat-id.png +0 -0
  223. package/screenshots/truth/actions/add_contact_urn/editor/whatsapp.png +0 -0
  224. package/screenshots/truth/actions/add_contact_urn/render/expression-facebook.png +0 -0
  225. package/screenshots/truth/actions/add_contact_urn/render/expression-phone.png +0 -0
  226. package/screenshots/truth/actions/add_contact_urn/render/facebook-id.png +0 -0
  227. package/screenshots/truth/actions/add_contact_urn/render/instagram-handle.png +0 -0
  228. package/screenshots/truth/actions/add_contact_urn/render/line-id.png +0 -0
  229. package/screenshots/truth/actions/add_contact_urn/render/phone-number.png +0 -0
  230. package/screenshots/truth/actions/add_contact_urn/render/telegram-id.png +0 -0
  231. package/screenshots/truth/actions/add_contact_urn/render/viber-id.png +0 -0
  232. package/screenshots/truth/actions/add_contact_urn/render/wechat-id.png +0 -0
  233. package/screenshots/truth/actions/add_contact_urn/render/whatsapp.png +0 -0
  234. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  235. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  236. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  237. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  238. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  239. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  240. package/screenshots/truth/actions/remove_contact_groups/render/cleanup-groups.png +0 -0
  241. package/screenshots/truth/actions/remove_contact_groups/render/long-descriptive-group-names.png +0 -0
  242. package/screenshots/truth/actions/remove_contact_groups/render/many-groups.png +0 -0
  243. package/screenshots/truth/actions/remove_contact_groups/render/multiple-groups.png +0 -0
  244. package/screenshots/truth/actions/remove_contact_groups/render/remove-from-all-groups.png +0 -0
  245. package/screenshots/truth/actions/remove_contact_groups/render/single-group.png +0 -0
  246. package/screenshots/truth/actions/send_broadcast/editor/contacts-only.png +0 -0
  247. package/screenshots/truth/actions/send_broadcast/editor/groups-and-contacts.png +0 -0
  248. package/screenshots/truth/actions/send_broadcast/editor/groups-only.png +0 -0
  249. package/screenshots/truth/actions/send_broadcast/editor/many-groups.png +0 -0
  250. package/screenshots/truth/actions/send_broadcast/editor/multiline-text.png +0 -0
  251. package/screenshots/truth/actions/send_broadcast/editor/with-attachments.png +0 -0
  252. package/screenshots/truth/actions/send_broadcast/render/contacts-only.png +0 -0
  253. package/screenshots/truth/actions/send_broadcast/render/groups-and-contacts.png +0 -0
  254. package/screenshots/truth/actions/send_broadcast/render/groups-only.png +0 -0
  255. package/screenshots/truth/actions/send_broadcast/render/many-groups.png +0 -0
  256. package/screenshots/truth/actions/send_broadcast/render/multiline-text.png +0 -0
  257. package/screenshots/truth/actions/send_broadcast/render/with-attachments.png +0 -0
  258. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  259. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  260. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  261. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  262. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  263. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  264. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  265. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  266. package/screenshots/truth/actions/send_email/render/complex-business-email.png +0 -0
  267. package/screenshots/truth/actions/send_email/render/empty-body.png +0 -0
  268. package/screenshots/truth/actions/send_email/render/empty-subject.png +0 -0
  269. package/screenshots/truth/actions/send_email/render/long-subject.png +0 -0
  270. package/screenshots/truth/actions/send_email/render/multiline-body.png +0 -0
  271. package/screenshots/truth/actions/send_email/render/multiple-recipients.png +0 -0
  272. package/screenshots/truth/actions/send_email/render/simple-email.png +0 -0
  273. package/screenshots/truth/actions/send_email/render/with-expressions.png +0 -0
  274. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  275. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  276. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  277. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  278. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  279. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  280. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  281. package/screenshots/truth/actions/send_msg/render/long-quick-replies.png +0 -0
  282. package/screenshots/truth/actions/send_msg/render/multiline-text-with-replies.png +0 -0
  283. package/screenshots/truth/actions/send_msg/render/simple-text.png +0 -0
  284. package/screenshots/truth/actions/send_msg/render/text-with-linebreaks.png +0 -0
  285. package/screenshots/truth/actions/send_msg/render/text-with-many-quick-replies.png +0 -0
  286. package/screenshots/truth/actions/send_msg/render/text-with-quick-replies.png +0 -0
  287. package/screenshots/truth/actions/send_msg/render/text-without-quick-replies.png +0 -0
  288. package/screenshots/truth/actions/start_session/editor/contact-query.png +0 -0
  289. package/screenshots/truth/actions/start_session/editor/contacts-only.png +0 -0
  290. package/screenshots/truth/actions/start_session/editor/create-contact.png +0 -0
  291. package/screenshots/truth/actions/start_session/editor/groups-and-contacts.png +0 -0
  292. package/screenshots/truth/actions/start_session/editor/groups-only.png +0 -0
  293. package/screenshots/truth/actions/start_session/editor/many-recipients.png +0 -0
  294. package/screenshots/truth/actions/start_session/render/contact-query.png +0 -0
  295. package/screenshots/truth/actions/start_session/render/contacts-only.png +0 -0
  296. package/screenshots/truth/actions/start_session/render/create-contact.png +0 -0
  297. package/screenshots/truth/actions/start_session/render/groups-and-contacts.png +0 -0
  298. package/screenshots/truth/actions/start_session/render/groups-only.png +0 -0
  299. package/screenshots/truth/actions/start_session/render/many-recipients.png +0 -0
  300. package/screenshots/truth/canvas-menu/open.png +0 -0
  301. package/screenshots/truth/editor/router.png +0 -0
  302. package/screenshots/truth/editor/wait.png +0 -0
  303. package/screenshots/truth/floating-tab/default.png +0 -0
  304. package/screenshots/truth/floating-tab/gray.png +0 -0
  305. package/screenshots/truth/floating-tab/green.png +0 -0
  306. package/screenshots/truth/floating-tab/hidden.png +0 -0
  307. package/screenshots/truth/floating-tab/hover.png +0 -0
  308. package/screenshots/truth/floating-tab/purple.png +0 -0
  309. package/screenshots/truth/floating-window/chromeless.png +0 -0
  310. package/screenshots/truth/floating-window/custom-size.png +0 -0
  311. package/screenshots/truth/floating-window/default.png +0 -0
  312. package/screenshots/truth/floating-window/with-header.png +0 -0
  313. package/screenshots/truth/list/fields-dragging.png +0 -0
  314. package/screenshots/truth/list/sortable-dragging.png +0 -0
  315. package/screenshots/truth/node-type-selector/action-mode.png +0 -0
  316. package/screenshots/truth/node-type-selector/split-mode.png +0 -0
  317. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  318. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  319. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  320. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  321. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  322. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  323. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  324. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  325. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  326. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  327. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  328. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  329. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  330. package/screenshots/truth/nodes/split_by_llm_categorize/render/basic-categorization.png +0 -0
  331. package/screenshots/truth/nodes/split_by_llm_categorize/render/custom-input-and-result-name.png +0 -0
  332. package/screenshots/truth/nodes/split_by_llm_categorize/render/feedback-categorization.png +0 -0
  333. package/screenshots/truth/nodes/split_by_llm_categorize/render/many-categories.png +0 -0
  334. package/screenshots/truth/nodes/split_by_llm_categorize/render/minimal-categories.png +0 -0
  335. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  336. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  337. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  338. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  339. package/screenshots/truth/nodes/split_by_random/render/ab-test-multiple-variants.png +0 -0
  340. package/screenshots/truth/nodes/split_by_random/render/sampling-split.png +0 -0
  341. package/screenshots/truth/nodes/split_by_random/render/three-way-split.png +0 -0
  342. package/screenshots/truth/nodes/split_by_random/render/two-bucket-split.png +0 -0
  343. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  344. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  345. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  346. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  347. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  348. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  349. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  350. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
  351. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  352. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  353. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  354. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  355. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  356. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  357. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  358. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  359. package/src/Icons.ts +4 -1
  360. package/src/display/FloatingTab.ts +174 -0
  361. package/src/display/ProgressBar.ts +22 -2
  362. package/src/events.ts +2 -8
  363. package/src/flow/CanvasMenu.ts +217 -0
  364. package/src/flow/CanvasNode.ts +596 -40
  365. package/src/flow/Editor.ts +1721 -45
  366. package/src/flow/NodeEditor.ts +621 -144
  367. package/src/flow/NodeTypeSelector.ts +636 -0
  368. package/src/flow/StickyNote.ts +12 -3
  369. package/src/flow/actions/add_contact_groups.ts +5 -4
  370. package/src/flow/actions/add_contact_urn.ts +78 -4
  371. package/src/flow/actions/add_input_labels.ts +5 -4
  372. package/src/flow/actions/play_audio.ts +3 -2
  373. package/src/flow/actions/remove_contact_groups.ts +16 -6
  374. package/src/flow/actions/request_optin.ts +3 -2
  375. package/src/flow/actions/say_msg.ts +3 -2
  376. package/src/flow/actions/send_broadcast.ts +86 -23
  377. package/src/flow/actions/send_email.ts +12 -6
  378. package/src/flow/actions/send_msg.ts +155 -34
  379. package/src/flow/actions/set_contact_channel.ts +6 -11
  380. package/src/flow/actions/set_contact_field.ts +21 -25
  381. package/src/flow/actions/set_contact_language.ts +11 -4
  382. package/src/flow/actions/set_contact_name.ts +4 -15
  383. package/src/flow/actions/set_contact_status.ts +4 -3
  384. package/src/flow/actions/set_run_result.ts +5 -4
  385. package/src/flow/actions/start_session.ts +210 -6
  386. package/src/flow/config.ts +11 -23
  387. package/src/flow/currencies.ts +51 -0
  388. package/src/flow/nodes/shared-rules.ts +301 -0
  389. package/src/flow/nodes/shared.ts +87 -0
  390. package/src/flow/nodes/split_by_airtime.ts +255 -5
  391. package/src/flow/nodes/split_by_contact_field.ts +195 -3
  392. package/src/flow/nodes/split_by_expression.ts +104 -2
  393. package/src/flow/nodes/split_by_groups.ts +26 -11
  394. package/src/flow/nodes/split_by_intent.ts +8 -0
  395. package/src/flow/nodes/split_by_llm.ts +22 -4
  396. package/src/flow/nodes/split_by_llm_categorize.ts +22 -5
  397. package/src/flow/nodes/split_by_random.ts +16 -6
  398. package/src/flow/nodes/split_by_resthook.ts +140 -0
  399. package/src/flow/nodes/split_by_run_result.ts +259 -3
  400. package/src/flow/nodes/split_by_scheme.ts +202 -2
  401. package/src/flow/nodes/split_by_subflow.ts +17 -5
  402. package/src/flow/nodes/split_by_ticket.ts +15 -4
  403. package/src/flow/nodes/split_by_webhook.ts +17 -6
  404. package/src/flow/nodes/wait_for_digits.ts +3 -2
  405. package/src/flow/nodes/wait_for_menu.ts +3 -2
  406. package/src/flow/nodes/wait_for_response.ts +59 -680
  407. package/src/flow/types.ts +156 -23
  408. package/src/flow/utils.ts +108 -14
  409. package/src/form/FieldRenderer.ts +2 -4
  410. package/src/interfaces.ts +3 -0
  411. package/src/layout/FloatingWindow.ts +386 -0
  412. package/src/list/SortableList.ts +109 -34
  413. package/src/live/ContactChat.ts +7 -25
  414. package/src/locales/es.ts +18 -13
  415. package/src/locales/fr.ts +18 -13
  416. package/src/locales/locale-codes.ts +11 -2
  417. package/src/locales/pt.ts +18 -13
  418. package/src/store/AppState.ts +173 -0
  419. package/src/store/flow-definition.d.ts +2 -5
  420. package/src/utils.ts +332 -12
  421. package/static/api/channels.json +46 -0
  422. package/static/api/llms.json +18 -0
  423. package/static/api/resthooks.json +31 -0
  424. package/static/svg/index.svg +1 -1
  425. package/static/svg/work/traced/lightning-02.svg +1 -0
  426. package/static/svg/work/used/lightning-02.svg +3 -0
  427. package/temba-modules.ts +8 -0
  428. package/test/ActionHelper.ts +3 -3
  429. package/test/NodeHelper.ts +6 -3
  430. package/test/actions/add_contact_urn.test.ts +287 -0
  431. package/test/actions/send_broadcast.test.ts +190 -0
  432. package/test/actions/send_email.test.ts +17 -23
  433. package/test/actions/send_msg.test.ts +39 -15
  434. package/test/actions/start_session.test.ts +151 -0
  435. package/test/nodes/split_by_airtime.test.ts +673 -0
  436. package/test/nodes/split_by_contact_field.test.ts +451 -0
  437. package/test/nodes/split_by_expression.test.ts +751 -0
  438. package/test/nodes/split_by_random.test.ts +3 -3
  439. package/test/nodes/split_by_resthook.test.ts +398 -0
  440. package/test/nodes/split_by_run_result.test.ts +1109 -0
  441. package/test/nodes/split_by_scheme.test.ts +486 -0
  442. package/test/nodes/split_by_subflow.test.ts +381 -0
  443. package/test/nodes/wait_for_digits.test.ts +2 -2
  444. package/test/nodes/wait_for_response.test.ts +2 -1
  445. package/test/temba-action-drag-between-nodes.test.ts +301 -0
  446. package/test/temba-canvas-menu.test.ts +156 -0
  447. package/test/temba-floating-tab.test.ts +110 -0
  448. package/test/temba-floating-window.test.ts +477 -0
  449. package/test/temba-flow-editor-node.test.ts +246 -2
  450. package/test/temba-flow-editor.test.ts +7 -8
  451. package/test/temba-localization.test.ts +611 -0
  452. package/test/temba-node-editor.test.ts +3 -1
  453. package/test/temba-node-type-selector.test.ts +355 -0
  454. package/test/temba-omnibox.test.ts +2 -1
  455. package/test/temba-sortable-list.test.ts +69 -0
  456. package/test/temba-utils-index.test.ts +0 -35
  457. package/test/utils.test.ts +22 -0
  458. package/test-assets/contacts/history.json +14 -21
  459. package/test-assets/select/llms.json +2 -2
  460. package/web-dev-server.config.mjs +49 -1
  461. package/web-test-runner.config.mjs +0 -1
  462. package/out-tsc/src/flow/actions/call_classifier.js +0 -11
  463. package/out-tsc/src/flow/actions/call_classifier.js.map +0 -1
  464. package/out-tsc/src/flow/actions/call_resthook.js +0 -11
  465. package/out-tsc/src/flow/actions/call_resthook.js.map +0 -1
  466. package/out-tsc/src/flow/actions/split_by_expression_example.js +0 -77
  467. package/out-tsc/src/flow/actions/split_by_expression_example.js.map +0 -1
  468. package/out-tsc/src/flow/actions/transfer_airtime.js +0 -11
  469. package/out-tsc/src/flow/actions/transfer_airtime.js.map +0 -1
  470. package/out-tsc/src/flow/nodes/wait_for_audio.js +0 -7
  471. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +0 -1
  472. package/out-tsc/src/flow/nodes/wait_for_image.js +0 -7
  473. package/out-tsc/src/flow/nodes/wait_for_image.js.map +0 -1
  474. package/out-tsc/src/flow/nodes/wait_for_location.js +0 -7
  475. package/out-tsc/src/flow/nodes/wait_for_location.js.map +0 -1
  476. package/out-tsc/src/flow/nodes/wait_for_video.js +0 -7
  477. package/out-tsc/src/flow/nodes/wait_for_video.js.map +0 -1
  478. package/src/flow/actions/call_classifier.ts +0 -12
  479. package/src/flow/actions/call_resthook.ts +0 -12
  480. package/src/flow/actions/split_by_expression_example.ts +0 -88
  481. package/src/flow/actions/transfer_airtime.ts +0 -12
  482. package/src/flow/nodes/wait_for_audio.ts +0 -7
  483. package/src/flow/nodes/wait_for_image.ts +0 -7
  484. package/src/flow/nodes/wait_for_location.ts +0 -7
  485. package/src/flow/nodes/wait_for_video.ts +0 -7
@@ -1,6 +1,8 @@
1
- import { COLORS } from '../types';
2
- import { generateUUID } from '../../utils';
1
+ import { SPLIT_GROUPS, FlowTypes } from '../types';
2
+ import { generateUUID, createRulesRouter } from '../../utils';
3
3
  import { getWaitForResponseOperators, operatorsToSelectOptions, getOperatorConfig } from '../operators';
4
+ import { resultNameField, categoriesToLocalizationFormData, localizationFormDataToCategories } from './shared';
5
+ import { createRulesArrayConfig, extractUserRules, casesToFormRules } from './shared-rules';
4
6
  const TIMEOUT_OPTIONS = [
5
7
  { value: '60', name: '1 minute' },
6
8
  { value: '120', name: '2 minutes' },
@@ -21,458 +23,32 @@ const TIMEOUT_OPTIONS = [
21
23
  { value: '259200', name: '3 days' },
22
24
  { value: '604800', name: '1 week' }
23
25
  ];
24
- // Helper function to check if a category is a system category
25
- const isSystemCategory = (categoryName) => {
26
- return ['No Response', 'Other', 'All Responses', 'Timeout'].includes(categoryName);
27
- };
28
- // Helper function to check if a UUID belongs to a system category
29
- const isSystemCategoryUuid = (uuid, categories) => {
30
- const category = categories.find((cat) => cat.uuid === uuid);
31
- return category ? isSystemCategory(category.name) : false;
32
- };
33
- // Helper function to generate default category name based on operator and operands
34
- const generateDefaultCategoryName = (operator, value1, value2) => {
35
- const operatorConfig = getOperatorConfig(operator);
36
- if (!operatorConfig)
37
- return '';
38
- // Fixed category names (no operands)
39
- if (operatorConfig.operands === 0) {
40
- return operatorConfig.categoryName || '';
41
- }
42
- // Dynamic category names based on operands
43
- const cleanValue1 = (value1 || '').trim();
44
- const cleanValue2 = (value2 || '').trim();
45
- // Helper to capitalize first letter
46
- const capitalize = (str) => {
47
- if (!str)
48
- return '';
49
- return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
50
- };
51
- // Handle different operator types
52
- switch (operator) {
53
- // Word/phrase operators - capitalize first letter of value
54
- case 'has_any_word':
55
- case 'has_all_words':
56
- case 'has_phrase':
57
- case 'has_only_phrase':
58
- case 'has_beginning':
59
- return cleanValue1 ? capitalize(cleanValue1) : '';
60
- // Pattern operators - show as-is
61
- case 'has_pattern':
62
- return cleanValue1;
63
- // Number comparison operators - include symbol
64
- case 'has_number_eq':
65
- return cleanValue1 ? `= ${cleanValue1}` : '';
66
- case 'has_number_lt':
67
- return cleanValue1 ? `< ${cleanValue1}` : '';
68
- case 'has_number_lte':
69
- return cleanValue1 ? `≤ ${cleanValue1}` : '';
70
- case 'has_number_gt':
71
- return cleanValue1 ? `> ${cleanValue1}` : '';
72
- case 'has_number_gte':
73
- return cleanValue1 ? `≥ ${cleanValue1}` : '';
74
- // Number between - range format
75
- case 'has_number_between':
76
- if (cleanValue1 && cleanValue2) {
77
- return `${cleanValue1} - ${cleanValue2}`;
78
- }
79
- return '';
80
- // Date operators - format with relative expressions
81
- case 'has_date_lt':
82
- case 'has_date_lte':
83
- if (cleanValue1) {
84
- // Parse relative date expression (e.g., "today + 5" or "today - 3")
85
- const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
86
- if (match) {
87
- const [, base, operator, days] = match;
88
- const dayWord = days === '1' ? 'day' : 'days';
89
- return `Before ${base} ${operator} ${days} ${dayWord}`;
90
- }
91
- // Fallback for other date formats
92
- return `Before ${cleanValue1}`;
93
- }
94
- return '';
95
- case 'has_date_gt':
96
- case 'has_date_gte':
97
- if (cleanValue1) {
98
- // Parse relative date expression
99
- const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
100
- if (match) {
101
- const [, base, operator, days] = match;
102
- const dayWord = days === '1' ? 'day' : 'days';
103
- return `After ${base} ${operator} ${days} ${dayWord}`;
104
- }
105
- // Fallback for other date formats
106
- return `After ${cleanValue1}`;
107
- }
108
- return '';
109
- case 'has_date_eq':
110
- if (cleanValue1) {
111
- // Parse relative date expression
112
- const match = cleanValue1.match(/^(today)\s*([+-])\s*(\d+)$/i);
113
- if (match) {
114
- const [, base, operator, days] = match;
115
- const dayWord = days === '1' ? 'day' : 'days';
116
- return `${base} ${operator} ${days} ${dayWord}`;
117
- }
118
- return cleanValue1;
119
- }
120
- return '';
121
- default:
122
- // Fallback - capitalize first value
123
- return cleanValue1 ? capitalize(cleanValue1) : '';
124
- }
125
- };
126
26
  // Helper function to create a wait_for_response router with user rules
27
+ // This is a thin wrapper around createRulesRouter that adds the No Response category for timeouts
127
28
  const createWaitForResponseRouter = (userRules, existingCategories = [], existingExits = [], existingCases = []) => {
128
- const categories = [];
129
- const exits = [];
130
- const cases = [];
131
- // Filter existing categories to get only user-defined rules (exclude system categories)
132
- const existingUserCategories = existingCategories.filter((cat) => !isSystemCategory(cat.name));
133
- // Track categories as we create them (case-insensitive lookup)
134
- const createdCategories = new Map();
135
- // Process rules in their original order to preserve rule order
136
- userRules.forEach((rule, ruleIndex) => {
137
- const categoryKey = rule.category.trim().toLowerCase();
138
- const categoryName = rule.category.trim(); // Use original casing
139
- let categoryInfo = createdCategories.get(categoryKey);
140
- if (!categoryInfo) {
141
- // First time seeing this category - create it
142
- // Smart category matching: try by name first, then fall back to position
143
- let existingCategory = existingUserCategories.find((cat) => cat.name.toLowerCase() === categoryKey);
144
- // If no match by name, try by position (for category rename scenarios)
145
- const categoryCreationOrder = Array.from(createdCategories.keys()).length;
146
- if (!existingCategory &&
147
- categoryCreationOrder < existingUserCategories.length) {
148
- const candidateCategory = existingUserCategories[categoryCreationOrder];
149
- // Double-check that this candidate is not a system category UUID
150
- if (candidateCategory &&
151
- !isSystemCategoryUuid(candidateCategory.uuid, existingCategories)) {
152
- existingCategory = candidateCategory;
153
- }
154
- }
155
- const existingExit = existingCategory
156
- ? existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid)
157
- : null;
158
- // Generate UUIDs, ensuring we don't reuse system category UUIDs
159
- let exitUuid = (existingExit === null || existingExit === void 0 ? void 0 : existingExit.uuid) || generateUUID();
160
- let categoryUuid = (existingCategory === null || existingCategory === void 0 ? void 0 : existingCategory.uuid) || generateUUID();
161
- // Additional safety check: if somehow we got a system category UUID, generate new ones
162
- if (isSystemCategoryUuid(categoryUuid, existingCategories)) {
163
- categoryUuid = generateUUID();
164
- exitUuid = generateUUID();
165
- }
166
- categoryInfo = {
167
- uuid: categoryUuid,
168
- name: categoryName,
169
- exit_uuid: exitUuid
170
- };
171
- createdCategories.set(categoryKey, categoryInfo);
172
- // Add category and exit
173
- categories.push({
174
- uuid: categoryUuid,
175
- name: categoryName,
176
- exit_uuid: exitUuid
177
- });
178
- exits.push({
179
- uuid: exitUuid,
180
- destination_uuid: (existingExit === null || existingExit === void 0 ? void 0 : existingExit.destination_uuid) || null
181
- });
182
- }
183
- // Create case for this rule
184
- let existingCase = existingCases[ruleIndex];
185
- // If we can't find by position, try to find by matching rule content
186
- if (!existingCase && existingCases.length > 0) {
187
- existingCase = existingCases.find((case_) => {
188
- // Find the category for this case
189
- const caseCategory = existingCategories.find((cat) => cat.uuid === case_.category_uuid);
190
- // Match by operator type and category name
191
- return (case_.type === rule.operator &&
192
- (caseCategory === null || caseCategory === void 0 ? void 0 : caseCategory.name.toLowerCase()) === categoryKey);
193
- });
194
- }
195
- const caseUuid = (existingCase === null || existingCase === void 0 ? void 0 : existingCase.uuid) || generateUUID();
196
- // Parse rule value based on operator configuration
197
- const operatorConfig = getOperatorConfig(rule.operator);
198
- let arguments_ = [];
199
- if (operatorConfig) {
200
- if (operatorConfig.operands === 0) {
201
- // No operands needed
202
- arguments_ = [];
203
- }
204
- else if (operatorConfig.operands === 2) {
205
- // Split value for two operands (e.g., "1 10" for between)
206
- arguments_ = rule.value.split(' ').filter((arg) => arg.trim());
207
- }
208
- else {
209
- // Single operand - but split words for operators that expect multiple words
210
- if (rule.value && rule.value.trim()) {
211
- // Split on spaces and filter out empty strings
212
- arguments_ = rule.value
213
- .trim()
214
- .split(/\s+/)
215
- .filter((arg) => arg.length > 0);
216
- }
217
- else {
218
- arguments_ = [];
219
- }
220
- }
221
- }
222
- else {
223
- // Fallback for unknown operators - split on spaces if value exists
224
- if (rule.value && rule.value.trim()) {
225
- arguments_ = rule.value
226
- .trim()
227
- .split(/\s+/)
228
- .filter((arg) => arg.length > 0);
229
- }
230
- else {
231
- arguments_ = [];
232
- }
233
- }
234
- cases.push({
235
- uuid: caseUuid,
236
- type: rule.operator,
237
- arguments: arguments_,
238
- category_uuid: categoryInfo.uuid
239
- });
240
- });
241
- // Add default category (always present)
242
- // Name is "Other" if there are user rules, "All Responses" if there are no user rules
243
- const defaultCategoryName = userRules.length > 0 ? 'Other' : 'All Responses';
244
- // Try to find existing default category by name (prefer exact match)
245
- let existingDefaultCategory = existingCategories.find((cat) => cat.name === defaultCategoryName);
246
- // If no exact match, try to find the other possible default category name
247
- if (!existingDefaultCategory) {
248
- const alternateName = userRules.length > 0 ? 'All Responses' : 'Other';
249
- existingDefaultCategory = existingCategories.find((cat) => cat.name === alternateName);
250
- }
251
- const existingDefaultExit = existingDefaultCategory
252
- ? existingExits.find((exit) => exit.uuid === existingDefaultCategory.exit_uuid)
253
- : null;
254
- const defaultExitUuid = (existingDefaultExit === null || existingDefaultExit === void 0 ? void 0 : existingDefaultExit.uuid) || generateUUID();
255
- const defaultCategoryUuid = (existingDefaultCategory === null || existingDefaultCategory === void 0 ? void 0 : existingDefaultCategory.uuid) || generateUUID();
256
- categories.push({
257
- uuid: defaultCategoryUuid,
258
- name: defaultCategoryName,
259
- exit_uuid: defaultExitUuid
260
- });
261
- exits.push({
262
- uuid: defaultExitUuid,
263
- destination_uuid: (existingDefaultExit === null || existingDefaultExit === void 0 ? void 0 : existingDefaultExit.destination_uuid) || null
264
- });
29
+ const { router, exits } = createRulesRouter('@input.text', userRules, getOperatorConfig, existingCategories, existingExits, existingCases);
265
30
  // Add "No Response" category last (if it exists in the original)
266
31
  const existingNoResponseCategory = existingCategories.find((cat) => cat.name === 'No Response' || cat.name === 'Timeout');
267
32
  if (existingNoResponseCategory) {
268
33
  const existingNoResponseExit = existingExits.find((exit) => exit.uuid === existingNoResponseCategory.exit_uuid);
269
34
  if (existingNoResponseExit) {
270
- categories.push(existingNoResponseCategory);
271
- exits.push(existingNoResponseExit);
35
+ router.categories.push(existingNoResponseCategory);
36
+ exits.push({
37
+ uuid: existingNoResponseExit.uuid,
38
+ destination_uuid: existingNoResponseExit.destination_uuid || null
39
+ });
272
40
  }
273
41
  }
274
- // Find the default category (either "Other" or "All Responses")
275
- const defaultCategory = categories.find((cat) => cat.name === 'Other' || cat.name === 'All Responses');
276
- return {
277
- router: {
278
- type: 'switch',
279
- categories: categories,
280
- default_category_uuid: defaultCategory === null || defaultCategory === void 0 ? void 0 : defaultCategory.uuid,
281
- operand: '@input.text',
282
- cases: cases
283
- },
284
- exits: exits
285
- };
42
+ return { router, exits };
286
43
  };
287
44
  export const wait_for_response = {
288
45
  type: 'wait_for_response',
289
46
  name: 'Wait for Response',
290
- color: COLORS.wait,
47
+ group: SPLIT_GROUPS.wait,
48
+ flowTypes: [FlowTypes.MESSAGE],
291
49
  dialogSize: 'large',
292
50
  form: {
293
- rules: {
294
- type: 'array',
295
- helpText: 'Define rules to categorize responses',
296
- itemLabel: 'Rule',
297
- minItems: 0,
298
- maxItems: 100,
299
- sortable: true,
300
- maintainEmptyItem: true, // Explicitly enable empty item maintenance
301
- isEmptyItem: (item) => {
302
- // Helper function to get operator value from various formats
303
- const getOperatorValue = (operator) => {
304
- if (typeof operator === 'string') {
305
- return operator.trim();
306
- }
307
- else if (Array.isArray(operator) && operator.length > 0) {
308
- // Handle array format: [{value: "has_any_word", name: "..."}]
309
- const firstOperator = operator[0];
310
- if (firstOperator &&
311
- typeof firstOperator === 'object' &&
312
- firstOperator.value) {
313
- return firstOperator.value.trim();
314
- }
315
- }
316
- else if (operator &&
317
- typeof operator === 'object' &&
318
- operator.value) {
319
- // Handle object format: {value: "has_any_word", name: "..."}
320
- return operator.value.trim();
321
- }
322
- return '';
323
- };
324
- // Check if operator and category are provided
325
- const operatorValue = getOperatorValue(item.operator);
326
- if (!operatorValue || !item.category || item.category.trim() === '') {
327
- return true;
328
- }
329
- // Check if value is required based on operator configuration
330
- const operatorConfig = getOperatorConfig(operatorValue);
331
- if (operatorConfig && operatorConfig.operands === 1) {
332
- // value1 is required for this operator
333
- return !item.value1 || item.value1.trim() === '';
334
- }
335
- else if (operatorConfig && operatorConfig.operands === 2) {
336
- // Both value1 and value2 are required for this operator
337
- return (!item.value1 ||
338
- item.value1.trim() === '' ||
339
- !item.value2 ||
340
- item.value2.trim() === '');
341
- }
342
- // No value required for this operator
343
- return false;
344
- },
345
- onItemChange: (itemIndex, field, value, allItems) => {
346
- const updatedItems = [...allItems];
347
- const item = { ...updatedItems[itemIndex] };
348
- // Helper to get operator value from various formats
349
- const getOperatorValue = (operator) => {
350
- if (typeof operator === 'string') {
351
- return operator.trim();
352
- }
353
- else if (Array.isArray(operator) && operator.length > 0) {
354
- const firstOperator = operator[0];
355
- if (firstOperator &&
356
- typeof firstOperator === 'object' &&
357
- firstOperator.value) {
358
- return firstOperator.value.trim();
359
- }
360
- }
361
- else if (operator &&
362
- typeof operator === 'object' &&
363
- operator.value) {
364
- return operator.value.trim();
365
- }
366
- return '';
367
- };
368
- // Update the changed field
369
- item[field] = value;
370
- // Get operator values (before and after the change)
371
- const oldItem = allItems[itemIndex] || {};
372
- const oldOperatorValue = field === 'operator'
373
- ? getOperatorValue(oldItem.operator)
374
- : getOperatorValue(item.operator);
375
- const newOperatorValue = getOperatorValue(item.operator);
376
- // Calculate what the default category name should be before the change
377
- const oldDefaultCategory = generateDefaultCategoryName(oldOperatorValue, field === 'value1' ? oldItem.value1 : item.value1, field === 'value2' ? oldItem.value2 : item.value2);
378
- // Calculate what the new default category name should be after the change
379
- const newDefaultCategory = generateDefaultCategoryName(newOperatorValue, item.value1, item.value2);
380
- // Determine if we should auto-update the category
381
- const shouldUpdateCategory =
382
- // Category is empty
383
- !item.category ||
384
- item.category.trim() === '' ||
385
- // Category matches the old default (user hasn't customized it)
386
- item.category === oldDefaultCategory;
387
- // Auto-populate or update category if conditions are met
388
- if (shouldUpdateCategory && newDefaultCategory) {
389
- item.category = newDefaultCategory;
390
- }
391
- updatedItems[itemIndex] = item;
392
- return updatedItems;
393
- },
394
- itemConfig: {
395
- operator: {
396
- type: 'select',
397
- required: true,
398
- multi: false, // Explicitly set as single-select
399
- options: operatorsToSelectOptions(getWaitForResponseOperators()),
400
- flavor: 'xsmall',
401
- width: '200px'
402
- },
403
- value1: {
404
- type: 'text',
405
- flavor: 'xsmall',
406
- conditions: {
407
- visible: (formData) => {
408
- // Helper function to get operator value from various formats
409
- const getOperatorValue = (operator) => {
410
- if (typeof operator === 'string') {
411
- return operator.trim();
412
- }
413
- else if (Array.isArray(operator) && operator.length > 0) {
414
- const firstOperator = operator[0];
415
- if (firstOperator &&
416
- typeof firstOperator === 'object' &&
417
- firstOperator.value) {
418
- return firstOperator.value.trim();
419
- }
420
- }
421
- else if (operator &&
422
- typeof operator === 'object' &&
423
- operator.value) {
424
- return operator.value.trim();
425
- }
426
- return '';
427
- };
428
- // Show value1 field for operators that require 1 or 2 operands
429
- const operatorValue = getOperatorValue(formData.operator);
430
- const operatorConfig = getOperatorConfig(operatorValue);
431
- return operatorConfig ? operatorConfig.operands >= 1 : true;
432
- }
433
- }
434
- },
435
- value2: {
436
- type: 'text',
437
- flavor: 'xsmall',
438
- conditions: {
439
- visible: (formData) => {
440
- // Helper function to get operator value from various formats
441
- const getOperatorValue = (operator) => {
442
- if (typeof operator === 'string') {
443
- return operator.trim();
444
- }
445
- else if (Array.isArray(operator) && operator.length > 0) {
446
- const firstOperator = operator[0];
447
- if (firstOperator &&
448
- typeof firstOperator === 'object' &&
449
- firstOperator.value) {
450
- return firstOperator.value.trim();
451
- }
452
- }
453
- else if (operator &&
454
- typeof operator === 'object' &&
455
- operator.value) {
456
- return operator.value.trim();
457
- }
458
- return '';
459
- };
460
- // Show value2 field only if operator requires exactly 2 operands
461
- const operatorValue = getOperatorValue(formData.operator);
462
- const operatorConfig = getOperatorConfig(operatorValue);
463
- return operatorConfig ? operatorConfig.operands === 2 : false;
464
- }
465
- }
466
- },
467
- category: {
468
- type: 'text',
469
- placeholder: 'Category',
470
- required: true,
471
- maxWidth: '120px',
472
- flavor: 'xsmall'
473
- }
474
- }
475
- },
51
+ rules: createRulesArrayConfig(operatorsToSelectOptions(getWaitForResponseOperators()), 'Define rules to categorize responses'),
476
52
  timeout_enabled: {
477
53
  type: 'checkbox',
478
54
  label: (formData) => {
@@ -486,7 +62,7 @@ export const wait_for_response = {
486
62
  type: 'select',
487
63
  placeholder: '5 minutes',
488
64
  multi: false,
489
- maxWidth: '150px',
65
+ maxWidth: '100px',
490
66
  flavor: 'xsmall',
491
67
  options: TIMEOUT_OPTIONS,
492
68
  conditions: {
@@ -495,12 +71,7 @@ export const wait_for_response = {
495
71
  }
496
72
  }
497
73
  },
498
- result_name: {
499
- type: 'text',
500
- label: 'Result Name',
501
- helpText: 'The name to save the response as',
502
- placeholder: 'response'
503
- }
74
+ result_name: resultNameField
504
75
  },
505
76
  layout: ['rules', 'result_name'],
506
77
  gutter: [
@@ -520,53 +91,11 @@ export const wait_for_response = {
520
91
  };
521
92
  },
522
93
  toFormData: (node) => {
523
- var _a, _b, _c, _d, _e, _f;
524
- // Extract rules from router cases
525
- const rules = [];
526
- if (((_a = node.router) === null || _a === void 0 ? void 0 : _a.cases) && ((_b = node.router) === null || _b === void 0 ? void 0 : _b.categories)) {
527
- node.router.cases.forEach((case_) => {
528
- // Find the category for this case
529
- const category = node.router.categories.find((cat) => cat.uuid === case_.category_uuid);
530
- // Skip system categories
531
- if (category && !isSystemCategory(category.name)) {
532
- // Handle different operator types
533
- const operatorConfig = getOperatorConfig(case_.type);
534
- const operatorDisplayName = operatorConfig
535
- ? operatorConfig.name
536
- : case_.type;
537
- let value1 = '';
538
- let value2 = '';
539
- if (operatorConfig && operatorConfig.operands === 0) {
540
- // No value needed for operators like has_text, has_number
541
- value1 = '';
542
- value2 = '';
543
- }
544
- else if (operatorConfig && operatorConfig.operands === 1) {
545
- // Single value for operators like has_number_lt - use value1
546
- value1 = case_.arguments.join(' ');
547
- value2 = '';
548
- }
549
- else if (operatorConfig && operatorConfig.operands === 2) {
550
- // Two separate values for operators like has_number_between
551
- value1 = case_.arguments[0] || '';
552
- value2 = case_.arguments[1] || '';
553
- }
554
- else {
555
- // Fallback: use first argument for unknown operators
556
- value1 = case_.arguments.join(' ');
557
- value2 = '';
558
- }
559
- rules.push({
560
- operator: { value: case_.type, name: operatorDisplayName },
561
- value1: value1,
562
- value2: value2,
563
- category: category.name
564
- });
565
- }
566
- });
567
- }
94
+ var _a, _b, _c, _d;
95
+ // Extract rules from router cases using shared function
96
+ const rules = casesToFormRules(node);
568
97
  // Extract timeout configuration
569
- const timeoutSeconds = (_e = (_d = (_c = node.router) === null || _c === void 0 ? void 0 : _c.wait) === null || _d === void 0 ? void 0 : _d.timeout) === null || _e === void 0 ? void 0 : _e.seconds;
98
+ const timeoutSeconds = (_c = (_b = (_a = node.router) === null || _a === void 0 ? void 0 : _a.wait) === null || _b === void 0 ? void 0 : _b.timeout) === null || _c === void 0 ? void 0 : _c.seconds;
570
99
  let timeoutOption = TIMEOUT_OPTIONS.find((opt) => opt.value === String(timeoutSeconds));
571
100
  if (!timeoutOption) {
572
101
  timeoutOption = { value: '300', name: '5 minutes' };
@@ -576,82 +105,13 @@ export const wait_for_response = {
576
105
  rules: rules,
577
106
  timeout_enabled: !!timeoutSeconds,
578
107
  timeout_duration: timeoutOption,
579
- result_name: ((_f = node.router) === null || _f === void 0 ? void 0 : _f.result_name) || 'response'
108
+ result_name: ((_d = node.router) === null || _d === void 0 ? void 0 : _d.result_name) || ''
580
109
  };
581
110
  },
582
111
  fromFormData: (formData, originalNode) => {
583
112
  var _a, _b, _c, _d, _e, _f, _g, _h;
584
- // Helper function to get operator value from various formats
585
- const getOperatorValue = (operator) => {
586
- if (typeof operator === 'string') {
587
- return operator.trim();
588
- }
589
- else if (Array.isArray(operator) && operator.length > 0) {
590
- // Handle array format: [{value: "has_any_word", name: "..."}]
591
- const firstOperator = operator[0];
592
- if (firstOperator &&
593
- typeof firstOperator === 'object' &&
594
- firstOperator.value) {
595
- return firstOperator.value.trim();
596
- }
597
- }
598
- else if (operator && typeof operator === 'object' && operator.value) {
599
- // Handle object format: {value: "has_any_word", name: "..."}
600
- return operator.value.trim();
601
- }
602
- return '';
603
- };
604
- // Get user rules
605
- const userRules = (formData.rules || [])
606
- .filter((rule) => {
607
- // Always need operator and category
608
- const operatorValue = getOperatorValue(rule === null || rule === void 0 ? void 0 : rule.operator);
609
- if (!operatorValue ||
610
- !(rule === null || rule === void 0 ? void 0 : rule.category) ||
611
- operatorValue === '' ||
612
- rule.category.trim() === '') {
613
- return false;
614
- }
615
- // Check if value is required based on operator
616
- const operatorConfig = getOperatorConfig(operatorValue);
617
- if (operatorConfig && operatorConfig.operands === 1) {
618
- // value1 is required for this operator
619
- return (rule === null || rule === void 0 ? void 0 : rule.value1) && rule.value1.trim() !== '';
620
- }
621
- else if (operatorConfig && operatorConfig.operands === 2) {
622
- // Both value1 and value2 are required for this operator
623
- return ((rule === null || rule === void 0 ? void 0 : rule.value1) &&
624
- rule.value1.trim() !== '' &&
625
- (rule === null || rule === void 0 ? void 0 : rule.value2) &&
626
- rule.value2.trim() !== '');
627
- }
628
- // No value required for this operator
629
- return true;
630
- })
631
- .map((rule) => {
632
- const operatorValue = getOperatorValue(rule.operator);
633
- const operatorConfig = getOperatorConfig(operatorValue);
634
- let value = '';
635
- if (operatorConfig && operatorConfig.operands === 1) {
636
- // Single value from value1
637
- value = rule.value1 ? rule.value1.trim() : '';
638
- }
639
- else if (operatorConfig && operatorConfig.operands === 2) {
640
- // Two values - combine them with space
641
- const val1 = rule.value1 ? rule.value1.trim() : '';
642
- const val2 = rule.value2 ? rule.value2.trim() : '';
643
- value = `${val1} ${val2}`.trim();
644
- }
645
- else {
646
- // No value needed for 0-operand operators
647
- value = '';
648
- }
649
- return {
650
- operator: operatorValue,
651
- value: value,
652
- category: rule.category.trim()
653
- };
654
- });
113
+ // Get user rules using shared extraction function
114
+ const userRules = extractUserRules(formData);
655
115
  // If no user rules, clear cases but preserve other router config
656
116
  if (userRules.length === 0) {
657
117
  // Get existing router data for preservation
@@ -691,9 +151,12 @@ export const wait_for_response = {
691
151
  );
692
152
  const router = {
693
153
  ...noRulesRouter,
694
- result_name: formData.result_name || 'response',
695
154
  cases: [] // Clear all cases when no rules
696
155
  };
156
+ // Only set result_name if provided
157
+ if (formData.result_name && formData.result_name.trim() !== '') {
158
+ router.result_name = formData.result_name.trim();
159
+ }
697
160
  // Build wait configuration based on form data
698
161
  const waitConfig = {
699
162
  type: 'msg'
@@ -752,9 +215,12 @@ export const wait_for_response = {
752
215
  const { router, exits } = createWaitForResponseRouter(userRules, existingCategories, existingExits, existingCases);
753
216
  // Build final router with wait configuration and result_name
754
217
  const finalRouter = {
755
- ...router,
756
- result_name: formData.result_name || 'response'
218
+ ...router
757
219
  };
220
+ // Only set result_name if provided
221
+ if (formData.result_name && formData.result_name.trim() !== '') {
222
+ finalRouter.result_name = formData.result_name.trim();
223
+ }
758
224
  // Build wait configuration based on form data
759
225
  const waitConfig = {
760
226
  type: 'msg'
@@ -826,6 +292,10 @@ export const wait_for_response = {
826
292
  router: finalRouter,
827
293
  exits: exits
828
294
  };
829
- }
295
+ },
296
+ // Localization support for categories
297
+ localizable: 'categories',
298
+ toLocalizationFormData: categoriesToLocalizationFormData,
299
+ fromLocalizationFormData: localizationFormDataToCategories
830
300
  };
831
301
  //# sourceMappingURL=wait_for_response.js.map