@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
@@ -3,6 +3,7 @@ import { html, css } from 'lit';
3
3
  import { property, state } from 'lit/decorators.js';
4
4
  import { RapidElement } from '../RapidElement';
5
5
  import { NODE_CONFIG, ACTION_CONFIG } from './config';
6
+ import { ACTION_GROUP_METADATA, SPLIT_GROUP_METADATA } from './types';
6
7
  import { CustomEventType } from '../interfaces';
7
8
  import { generateUUID } from '../utils';
8
9
  import { FieldRenderer } from '../form/FieldRenderer';
@@ -16,6 +17,7 @@ export class NodeEditor extends RapidElement {
16
17
  this.errors = {};
17
18
  this.groupCollapseState = {};
18
19
  this.groupHoverState = {};
20
+ this.revealedOptionalFields = new Set();
19
21
  }
20
22
  static get styles() {
21
23
  return css `
@@ -25,7 +27,6 @@ export class NodeEditor extends RapidElement {
25
27
  flex-direction: column;
26
28
  gap: 15px;
27
29
  min-width: 400px;
28
- padding-bottom: 40px;
29
30
 
30
31
  --color-bubble-bg: rgba(var(--primary-rgb), 0.7);
31
32
  --color-bubble-border: rgba(0, 0, 0, 0.2);
@@ -88,6 +89,28 @@ export class NodeEditor extends RapidElement {
88
89
  align-items: center;
89
90
  }
90
91
 
92
+ .form-row-wrapper {
93
+ display: flex;
94
+ flex-direction: column;
95
+ }
96
+
97
+ .form-row-label {
98
+ margin-bottom: 5px;
99
+ margin-left: 4px;
100
+ display: block;
101
+ font-weight: 400;
102
+ font-size: var(--label-size);
103
+ letter-spacing: 0.05em;
104
+ line-height: normal;
105
+ color: var(--color-label, #777);
106
+ }
107
+
108
+ .form-row-help {
109
+ font-size: 12px;
110
+ color: #666;
111
+ margin-top: 6px;
112
+ }
113
+
91
114
  .form-group {
92
115
  border: 1px solid #e0e0e0;
93
116
  border-radius: 6px;
@@ -276,6 +299,21 @@ export class NodeEditor extends RapidElement {
276
299
  .gutter-fields temba-select {
277
300
  min-width: 120px;
278
301
  }
302
+
303
+ .optional-field-link {
304
+ margin: 10px 0;
305
+ }
306
+
307
+ .optional-field-link a {
308
+ color: var(--color-link-primary, #0066cc);
309
+ text-decoration: none;
310
+ font-size: 13px;
311
+ cursor: pointer;
312
+ }
313
+
314
+ .optional-field-link a:hover {
315
+ text-decoration: underline;
316
+ }
279
317
  `;
280
318
  }
281
319
  connectedCallback() {
@@ -326,7 +364,7 @@ export class NodeEditor extends RapidElement {
326
364
  // Node editing mode - use node config
327
365
  const nodeConfig = this.getNodeConfig();
328
366
  if (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.toFormData) {
329
- this.formData = nodeConfig.toFormData(this.node);
367
+ this.formData = nodeConfig.toFormData(this.node, this.nodeUI);
330
368
  }
331
369
  else {
332
370
  this.formData = { ...this.node };
@@ -433,8 +471,12 @@ export class NodeEditor extends RapidElement {
433
471
  return this.nodeUI.type ? NODE_CONFIG[this.nodeUI.type] : null;
434
472
  }
435
473
  getHeaderColor() {
474
+ var _a, _b;
436
475
  const config = this.getConfig();
437
- return (config === null || config === void 0 ? void 0 : config.color) || '#666666';
476
+ return (config === null || config === void 0 ? void 0 : config.group)
477
+ ? ((_a = ACTION_GROUP_METADATA[config.group]) === null || _a === void 0 ? void 0 : _a.color) ||
478
+ ((_b = SPLIT_GROUP_METADATA[config.group]) === null || _b === void 0 ? void 0 : _b.color)
479
+ : '#aaaaaa';
438
480
  }
439
481
  handleDialogButtonClick(event) {
440
482
  const button = event.detail.button;
@@ -462,8 +504,14 @@ export class NodeEditor extends RapidElement {
462
504
  if (this.node && this.node.router) {
463
505
  // Node editing mode with router - use formDataToNode
464
506
  const updatedNode = this.formDataToNode(processedFormData);
507
+ // Generate UI config if the node config provides a toUIConfig function
508
+ const nodeConfig = this.getNodeConfig();
509
+ const uiConfig = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.toUIConfig)
510
+ ? nodeConfig.toUIConfig(processedFormData)
511
+ : undefined;
465
512
  this.fireCustomEvent(CustomEventType.NodeSaved, {
466
- node: updatedNode
513
+ node: updatedNode,
514
+ uiConfig
467
515
  });
468
516
  }
469
517
  else if (this.action) {
@@ -476,8 +524,14 @@ export class NodeEditor extends RapidElement {
476
524
  else if (this.node) {
477
525
  // Node editing mode without router
478
526
  const updatedNode = this.formDataToNode(processedFormData);
527
+ // Generate UI config if the node config provides a toUIConfig function
528
+ const nodeConfig = this.getNodeConfig();
529
+ const uiConfig = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.toUIConfig)
530
+ ? nodeConfig.toUIConfig(processedFormData)
531
+ : undefined;
479
532
  this.fireCustomEvent(CustomEventType.NodeSaved, {
480
- node: updatedNode
533
+ node: updatedNode,
534
+ uiConfig
481
535
  });
482
536
  }
483
537
  }
@@ -642,8 +696,14 @@ export class NodeEditor extends RapidElement {
642
696
  if (!this.node)
643
697
  throw new Error('No node to update');
644
698
  let updatedNode = { ...this.node };
699
+ // Check if node config has fromFormData - if so, it handles the entire transformation
700
+ const nodeConfig = this.getNodeConfig();
701
+ const nodeHasFromFormData = (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.fromFormData) !== undefined;
645
702
  // Handle actions using action config transformations if available
646
- if (this.node.actions && this.node.actions.length > 0) {
703
+ // Skip this if the node has its own fromFormData (which handles actions itself)
704
+ if (!nodeHasFromFormData &&
705
+ this.node.actions &&
706
+ this.node.actions.length > 0) {
647
707
  updatedNode.actions = this.node.actions.map((action) => {
648
708
  // If we're editing a specific action, only transform that one
649
709
  if (this.action && action.uuid === this.action.uuid) {
@@ -664,53 +724,83 @@ export class NodeEditor extends RapidElement {
664
724
  });
665
725
  }
666
726
  // Handle router configuration using node config
667
- if (this.node.router) {
668
- const nodeConfig = this.getNodeConfig();
669
- if (nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.fromFormData) {
670
- // Use node-specific form data transformation
671
- updatedNode = nodeConfig.fromFormData(formData, updatedNode);
727
+ if (nodeHasFromFormData) {
728
+ // Use node-specific form data transformation
729
+ // When a node has fromFormData, it's responsible for creating the entire
730
+ // node structure including actions and router (regardless of whether router exists yet)
731
+ updatedNode = nodeConfig.fromFormData(formData, updatedNode);
732
+ }
733
+ else if (this.node.router) {
734
+ // Default router handling when no nodeConfig.fromFormData
735
+ updatedNode.router = { ...this.node.router };
736
+ // Apply form data to router fields if they exist
737
+ if (formData.result_name !== undefined) {
738
+ updatedNode.router.result_name = formData.result_name;
672
739
  }
673
- else {
674
- // Default router handling
675
- updatedNode.router = { ...this.node.router };
676
- // Apply form data to router fields if they exist
677
- if (formData.result_name !== undefined) {
678
- updatedNode.router.result_name = formData.result_name;
679
- }
680
- // Handle preconfigured rules from node config
681
- if ((_a = nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.router) === null || _a === void 0 ? void 0 : _a.rules) {
682
- // Build a complete new set of categories and exits based on node config
683
- const existingCategories = updatedNode.router.categories || [];
684
- const existingExits = updatedNode.exits || [];
685
- const newCategories = [];
686
- const newExits = [];
687
- // Group rules by category name to handle multiple rules pointing to the same category
688
- const categoryNameToRules = new Map();
689
- nodeConfig.router.rules.forEach((rule) => {
690
- if (!categoryNameToRules.has(rule.categoryName)) {
691
- categoryNameToRules.set(rule.categoryName, []);
740
+ // Handle preconfigured rules from node config
741
+ if ((_a = nodeConfig === null || nodeConfig === void 0 ? void 0 : nodeConfig.router) === null || _a === void 0 ? void 0 : _a.rules) {
742
+ // Build a complete new set of categories and exits based on node config
743
+ const existingCategories = updatedNode.router.categories || [];
744
+ const existingExits = updatedNode.exits || [];
745
+ const newCategories = [];
746
+ const newExits = [];
747
+ // Group rules by category name to handle multiple rules pointing to the same category
748
+ const categoryNameToRules = new Map();
749
+ nodeConfig.router.rules.forEach((rule) => {
750
+ if (!categoryNameToRules.has(rule.categoryName)) {
751
+ categoryNameToRules.set(rule.categoryName, []);
752
+ }
753
+ categoryNameToRules.get(rule.categoryName).push(rule);
754
+ });
755
+ // Create categories for all unique category names
756
+ categoryNameToRules.forEach((rules, categoryName) => {
757
+ // Check if category already exists to preserve its UUID and exit_uuid
758
+ const existingCategory = existingCategories.find((cat) => cat.name === categoryName);
759
+ if (existingCategory) {
760
+ // Preserve existing category and its associated exit
761
+ newCategories.push(existingCategory);
762
+ const associatedExit = existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid);
763
+ if (associatedExit) {
764
+ newExits.push(associatedExit);
692
765
  }
693
- categoryNameToRules.get(rule.categoryName).push(rule);
694
- });
695
- // Create categories for all unique category names
696
- categoryNameToRules.forEach((rules, categoryName) => {
697
- // Check if category already exists to preserve its UUID and exit_uuid
698
- const existingCategory = existingCategories.find((cat) => cat.name === categoryName);
699
- if (existingCategory) {
700
- // Preserve existing category and its associated exit
701
- newCategories.push(existingCategory);
702
- const associatedExit = existingExits.find((exit) => exit.uuid === existingCategory.exit_uuid);
766
+ }
767
+ else {
768
+ // Create new category and exit
769
+ const categoryUuid = generateUUID();
770
+ const exitUuid = generateUUID();
771
+ newCategories.push({
772
+ uuid: categoryUuid,
773
+ name: categoryName,
774
+ exit_uuid: exitUuid
775
+ });
776
+ newExits.push({
777
+ uuid: exitUuid,
778
+ destination_uuid: null
779
+ });
780
+ }
781
+ });
782
+ // Add default category if specified
783
+ if (nodeConfig.router.defaultCategory) {
784
+ // Check if default category already exists in our new list
785
+ const existingDefault = newCategories.find((cat) => cat.name === nodeConfig.router.defaultCategory);
786
+ if (!existingDefault) {
787
+ // Check if it exists in the original categories
788
+ const originalDefault = existingCategories.find((cat) => cat.name === nodeConfig.router.defaultCategory);
789
+ if (originalDefault) {
790
+ // Preserve existing default category and its exit
791
+ newCategories.push(originalDefault);
792
+ const associatedExit = existingExits.find((exit) => exit.uuid === originalDefault.exit_uuid);
703
793
  if (associatedExit) {
704
794
  newExits.push(associatedExit);
705
795
  }
706
796
  }
707
797
  else {
708
- // Create new category and exit
798
+ // Create new default category and exit
709
799
  const categoryUuid = generateUUID();
710
800
  const exitUuid = generateUUID();
711
801
  newCategories.push({
712
802
  uuid: categoryUuid,
713
- name: categoryName,
803
+ name: nodeConfig.router.defaultCategory,
714
804
  exit_uuid: exitUuid
715
805
  });
716
806
  newExits.push({
@@ -718,42 +808,11 @@ export class NodeEditor extends RapidElement {
718
808
  destination_uuid: null
719
809
  });
720
810
  }
721
- });
722
- // Add default category if specified
723
- if (nodeConfig.router.defaultCategory) {
724
- // Check if default category already exists in our new list
725
- const existingDefault = newCategories.find((cat) => cat.name === nodeConfig.router.defaultCategory);
726
- if (!existingDefault) {
727
- // Check if it exists in the original categories
728
- const originalDefault = existingCategories.find((cat) => cat.name === nodeConfig.router.defaultCategory);
729
- if (originalDefault) {
730
- // Preserve existing default category and its exit
731
- newCategories.push(originalDefault);
732
- const associatedExit = existingExits.find((exit) => exit.uuid === originalDefault.exit_uuid);
733
- if (associatedExit) {
734
- newExits.push(associatedExit);
735
- }
736
- }
737
- else {
738
- // Create new default category and exit
739
- const categoryUuid = generateUUID();
740
- const exitUuid = generateUUID();
741
- newCategories.push({
742
- uuid: categoryUuid,
743
- name: nodeConfig.router.defaultCategory,
744
- exit_uuid: exitUuid
745
- });
746
- newExits.push({
747
- uuid: exitUuid,
748
- destination_uuid: null
749
- });
750
- }
751
- }
752
811
  }
753
- // Replace the entire categories and exits lists with our complete new sets
754
- updatedNode.router.categories = newCategories;
755
- updatedNode.exits = newExits;
756
812
  }
813
+ // Replace the entire categories and exits lists with our complete new sets
814
+ updatedNode.router.categories = newCategories;
815
+ updatedNode.exits = newExits;
757
816
  }
758
817
  }
759
818
  else {
@@ -862,21 +921,28 @@ export class NodeEditor extends RapidElement {
862
921
  }
863
922
  });
864
923
  }
865
- renderNewField(fieldName, config, value) {
924
+ /**
925
+ * Helper method to check if a field is visible based on its conditions
926
+ */
927
+ isFieldVisible(fieldName, config) {
866
928
  var _a;
867
- // Check visibility condition
868
929
  if ((_a = config.conditions) === null || _a === void 0 ? void 0 : _a.visible) {
869
930
  try {
870
- const isVisible = config.conditions.visible(this.formData);
871
- if (!isVisible) {
872
- return html ``;
873
- }
931
+ return config.conditions.visible(this.formData);
874
932
  }
875
933
  catch (error) {
876
934
  console.error(`Error checking visibility for ${fieldName}:`, error);
877
935
  // If there's an error, show the field by default
936
+ return true;
878
937
  }
879
938
  }
939
+ return true;
940
+ }
941
+ renderNewField(fieldName, config, value) {
942
+ // Check visibility condition
943
+ if (!this.isFieldVisible(fieldName, config)) {
944
+ return html ``;
945
+ }
880
946
  const errors = this.errors[fieldName] ? [this.errors[fieldName]] : [];
881
947
  // Build container style with maxWidth if specified
882
948
  const containerStyle = config.maxWidth
@@ -889,6 +955,35 @@ export class NodeEditor extends RapidElement {
889
955
  }
890
956
  return fieldContent;
891
957
  }
958
+ renderOptionalField(fieldName, config, value) {
959
+ // If the field has a value or has been revealed, show it
960
+ const hasValue = value && value.toString().trim() !== '';
961
+ const isRevealed = this.revealedOptionalFields.has(fieldName);
962
+ if (hasValue || isRevealed) {
963
+ // Render the field normally
964
+ return this.renderNewField(fieldName, config, value);
965
+ }
966
+ // Show the "Save as..." link
967
+ return html `
968
+ <div class="optional-field-link">
969
+ <a
970
+ href="#"
971
+ @click="${(e) => {
972
+ e.preventDefault();
973
+ this.revealOptionalField(fieldName);
974
+ }}"
975
+ >
976
+ ${config.optionalLink}
977
+ </a>
978
+ </div>
979
+ `;
980
+ }
981
+ revealOptionalField(fieldName) {
982
+ this.revealedOptionalFields = new Set([
983
+ ...this.revealedOptionalFields,
984
+ fieldName
985
+ ]);
986
+ }
892
987
  renderFieldContent(fieldName, config, value, errors) {
893
988
  // Use FieldRenderer for consistent field rendering
894
989
  return FieldRenderer.renderField(fieldName, config, value, {
@@ -977,7 +1072,12 @@ export class NodeEditor extends RapidElement {
977
1072
  case 'field':
978
1073
  if (config.form[item.field] && !renderedFields.has(item.field)) {
979
1074
  renderedFields.add(item.field);
980
- return this.renderNewField(item.field, config.form[item.field], this.formData[item.field]);
1075
+ const fieldConfig = config.form[item.field];
1076
+ // Handle optional link fields
1077
+ if (fieldConfig.optionalLink) {
1078
+ return this.renderOptionalField(item.field, fieldConfig, this.formData[item.field]);
1079
+ }
1080
+ return this.renderNewField(item.field, fieldConfig, this.formData[item.field]);
981
1081
  }
982
1082
  return html ``;
983
1083
  case 'row':
@@ -989,24 +1089,68 @@ export class NodeEditor extends RapidElement {
989
1089
  }
990
1090
  }
991
1091
  renderRow(rowConfig, config, renderedFields) {
992
- const { items, gap = '1rem' } = rowConfig;
1092
+ const { items, gap = '1rem', label, helpText } = rowConfig;
993
1093
  // Collect all fields from this row for width calculations
994
1094
  const fieldsInRow = this.collectFieldsFromItems(items);
995
1095
  const validFields = fieldsInRow.filter((fieldName) => { var _a; return (_a = config.form) === null || _a === void 0 ? void 0 : _a[fieldName]; });
996
- if (validFields.length === 0) {
1096
+ // Filter for visible fields only to handle conditional visibility
1097
+ const visibleFields = validFields.filter((fieldName) => {
1098
+ const fieldConfig = config.form[fieldName];
1099
+ return this.isFieldVisible(fieldName, fieldConfig);
1100
+ });
1101
+ if (visibleFields.length === 0) {
997
1102
  return html ``;
998
1103
  }
999
- // Calculate grid template columns based on field maxWidth constraints
1000
- const columns = validFields.map((fieldName) => {
1104
+ // Build a map of field flex styles
1105
+ // Fields with maxWidth get flex: 0 0 {maxWidth} (fixed)
1106
+ // Fields without maxWidth get flex: 1 1 0 (grow to fill space)
1107
+ const fieldFlexStyles = new Map();
1108
+ visibleFields.forEach((fieldName) => {
1001
1109
  const fieldConfig = config.form[fieldName];
1002
- return fieldConfig.maxWidth || '1fr';
1110
+ if (fieldConfig.maxWidth) {
1111
+ // Fixed width field: no grow, no shrink, basis = maxWidth
1112
+ fieldFlexStyles.set(fieldName, `flex: 0 0 ${fieldConfig.maxWidth};`);
1113
+ }
1114
+ else {
1115
+ // Flexible field: grow to fill remaining space
1116
+ fieldFlexStyles.set(fieldName, `flex: 1 1 0;`);
1117
+ }
1003
1118
  });
1119
+ const rowContent = html `
1120
+ <div class="form-row" style="display: flex; gap: ${gap};">
1121
+ ${items.map((item) => {
1122
+ // Get the field name from the item
1123
+ const fieldName = typeof item === 'string'
1124
+ ? item
1125
+ : item.type === 'field'
1126
+ ? item.field
1127
+ : null;
1128
+ // Get flex style for this field if it's a visible field
1129
+ const flexStyle = fieldName && fieldFlexStyles.has(fieldName)
1130
+ ? fieldFlexStyles.get(fieldName)
1131
+ : '';
1132
+ const itemContent = this.renderLayoutItem(item, config, renderedFields);
1133
+ // Wrap in a div with flex style if we have a flex style
1134
+ return flexStyle
1135
+ ? html `<div style="${flexStyle}">${itemContent}</div>`
1136
+ : itemContent;
1137
+ })}
1138
+ </div>
1139
+ `;
1140
+ // If no label or helpText, return just the row content
1141
+ if (!label && !helpText) {
1142
+ return rowContent;
1143
+ }
1144
+ // Otherwise, wrap with label on top, content, then helpText below (matching field pattern)
1004
1145
  return html `
1005
- <div
1006
- class="form-row"
1007
- style="display: grid; grid-template-columns: ${columns.join(' ')}; gap: ${gap};"
1008
- >
1009
- ${items.map((item) => this.renderLayoutItem(item, config, renderedFields))}
1146
+ <div class="form-row-wrapper">
1147
+ ${label ? html `<label class="form-row-label">${label}</label>` : ''}
1148
+ ${rowContent}
1149
+ ${helpText
1150
+ ? html `<div class="form-row-help">
1151
+ ${renderMarkdownInline(helpText)}
1152
+ </div>`
1153
+ : ''}
1010
1154
  </div>
1011
1155
  `;
1012
1156
  }
@@ -1351,4 +1495,7 @@ __decorate([
1351
1495
  __decorate([
1352
1496
  state()
1353
1497
  ], NodeEditor.prototype, "groupHoverState", void 0);
1498
+ __decorate([
1499
+ state()
1500
+ ], NodeEditor.prototype, "revealedOptionalFields", void 0);
1354
1501
  //# sourceMappingURL=NodeEditor.js.map