@nyaruka/temba-components 0.129.9 → 0.129.11

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 (323) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/demo/data/flows/sample-flow.json +96 -56
  3. package/demo/test-colorpicker.html +30 -0
  4. package/dist/temba-components.js +896 -934
  5. package/dist/temba-components.js.map +1 -1
  6. package/out-tsc/src/events.js.map +1 -1
  7. package/out-tsc/src/flow/Editor.js +9 -6
  8. package/out-tsc/src/flow/Editor.js.map +1 -1
  9. package/out-tsc/src/flow/actions/set_contact_channel.js +1 -1
  10. package/out-tsc/src/flow/actions/set_contact_channel.js.map +1 -1
  11. package/out-tsc/src/flow/actions/set_contact_field.js +1 -1
  12. package/out-tsc/src/flow/actions/set_contact_field.js.map +1 -1
  13. package/out-tsc/src/flow/actions/set_contact_language.js +1 -1
  14. package/out-tsc/src/flow/actions/set_contact_language.js.map +1 -1
  15. package/out-tsc/src/flow/actions/set_contact_status.js +1 -1
  16. package/out-tsc/src/flow/actions/set_contact_status.js.map +1 -1
  17. package/out-tsc/src/flow/config.js +2 -8
  18. package/out-tsc/src/flow/config.js.map +1 -1
  19. package/out-tsc/src/flow/nodes/split_by_llm.js +101 -0
  20. package/out-tsc/src/flow/nodes/split_by_llm.js.map +1 -0
  21. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js +4 -89
  22. package/out-tsc/src/flow/nodes/split_by_llm_categorize.js.map +1 -1
  23. package/out-tsc/src/flow/nodes/split_by_subflow.js +123 -3
  24. package/out-tsc/src/flow/nodes/split_by_subflow.js.map +1 -1
  25. package/out-tsc/src/flow/nodes/split_by_ticket.js +115 -13
  26. package/out-tsc/src/flow/nodes/split_by_ticket.js.map +1 -1
  27. package/out-tsc/src/flow/nodes/split_by_webhook.js +160 -12
  28. package/out-tsc/src/flow/nodes/split_by_webhook.js.map +1 -1
  29. package/out-tsc/src/form/ArrayEditor.js +45 -56
  30. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  31. package/out-tsc/src/form/BaseListEditor.js +4 -3
  32. package/out-tsc/src/form/BaseListEditor.js.map +1 -1
  33. package/out-tsc/src/form/Checkbox.js +77 -24
  34. package/out-tsc/src/form/Checkbox.js.map +1 -1
  35. package/out-tsc/src/form/ColorPicker.js +28 -40
  36. package/out-tsc/src/form/ColorPicker.js.map +1 -1
  37. package/out-tsc/src/form/Completion.js +44 -53
  38. package/out-tsc/src/form/Completion.js.map +1 -1
  39. package/out-tsc/src/form/Compose.js +7 -8
  40. package/out-tsc/src/form/Compose.js.map +1 -1
  41. package/out-tsc/src/form/ContactSearch.js +3 -4
  42. package/out-tsc/src/form/ContactSearch.js.map +1 -1
  43. package/out-tsc/src/form/DatePicker.js +29 -36
  44. package/out-tsc/src/form/DatePicker.js.map +1 -1
  45. package/out-tsc/src/form/{FormField.js → FieldElement.js} +78 -50
  46. package/out-tsc/src/form/FieldElement.js.map +1 -0
  47. package/out-tsc/src/form/FieldRenderer.js +2 -1
  48. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  49. package/out-tsc/src/form/ImagePicker.js +122 -126
  50. package/out-tsc/src/form/ImagePicker.js.map +1 -1
  51. package/out-tsc/src/form/KeyValueEditor.js +41 -37
  52. package/out-tsc/src/form/KeyValueEditor.js.map +1 -1
  53. package/out-tsc/src/form/MessageEditor.js +55 -63
  54. package/out-tsc/src/form/MessageEditor.js.map +1 -1
  55. package/out-tsc/src/form/TembaSlider.js +3 -3
  56. package/out-tsc/src/form/TembaSlider.js.map +1 -1
  57. package/out-tsc/src/form/TemplateEditor.js +3 -3
  58. package/out-tsc/src/form/TemplateEditor.js.map +1 -1
  59. package/out-tsc/src/form/TextInput.js +22 -26
  60. package/out-tsc/src/form/TextInput.js.map +1 -1
  61. package/out-tsc/src/form/select/Select.js +9 -15
  62. package/out-tsc/src/form/select/Select.js.map +1 -1
  63. package/out-tsc/src/form/select/UserSelect.js +8 -9
  64. package/out-tsc/src/form/select/UserSelect.js.map +1 -1
  65. package/out-tsc/src/form/select/WorkspaceSelect.js +7 -8
  66. package/out-tsc/src/form/select/WorkspaceSelect.js.map +1 -1
  67. package/out-tsc/src/live/ContactChat.js +73 -99
  68. package/out-tsc/src/live/ContactChat.js.map +1 -1
  69. package/out-tsc/src/live/ContactFieldEditor.js.map +1 -1
  70. package/out-tsc/src/utils.js +115 -0
  71. package/out-tsc/src/utils.js.map +1 -1
  72. package/out-tsc/temba-modules.js +3 -2
  73. package/out-tsc/temba-modules.js.map +1 -1
  74. package/out-tsc/test/nodes/split_by_llm.test.js +174 -0
  75. package/out-tsc/test/nodes/split_by_llm.test.js.map +1 -0
  76. package/out-tsc/test/temba-checkbox.test.js +16 -0
  77. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  78. package/out-tsc/test/temba-integration-markdown.test.js +2 -4
  79. package/out-tsc/test/temba-integration-markdown.test.js.map +1 -1
  80. package/out-tsc/test/temba-slider.test.js +0 -1
  81. package/out-tsc/test/temba-slider.test.js.map +1 -1
  82. package/package.json +1 -1
  83. package/screenshots/truth/actions/add_contact_groups/editor/descriptive-group-names.png +0 -0
  84. package/screenshots/truth/actions/add_contact_groups/editor/long-group-names.png +0 -0
  85. package/screenshots/truth/actions/add_contact_groups/editor/many-groups.png +0 -0
  86. package/screenshots/truth/actions/add_contact_groups/editor/multiple-groups.png +0 -0
  87. package/screenshots/truth/actions/add_contact_groups/editor/single-group.png +0 -0
  88. package/screenshots/truth/actions/call_llm/editor/information-extraction.png +0 -0
  89. package/screenshots/truth/actions/call_llm/editor/sentiment-analysis.png +0 -0
  90. package/screenshots/truth/actions/call_llm/editor/summarization.png +0 -0
  91. package/screenshots/truth/actions/call_llm/editor/translation-task.png +0 -0
  92. package/screenshots/truth/actions/remove_contact_groups/editor/cleanup-groups.png +0 -0
  93. package/screenshots/truth/actions/remove_contact_groups/editor/long-descriptive-group-names.png +0 -0
  94. package/screenshots/truth/actions/remove_contact_groups/editor/many-groups.png +0 -0
  95. package/screenshots/truth/actions/remove_contact_groups/editor/multiple-groups.png +0 -0
  96. package/screenshots/truth/actions/remove_contact_groups/editor/remove-from-all-groups.png +0 -0
  97. package/screenshots/truth/actions/remove_contact_groups/editor/single-group.png +0 -0
  98. package/screenshots/truth/actions/send_email/editor/complex-business-email.png +0 -0
  99. package/screenshots/truth/actions/send_email/editor/empty-body.png +0 -0
  100. package/screenshots/truth/actions/send_email/editor/empty-subject.png +0 -0
  101. package/screenshots/truth/actions/send_email/editor/long-subject.png +0 -0
  102. package/screenshots/truth/actions/send_email/editor/multiline-body.png +0 -0
  103. package/screenshots/truth/actions/send_email/editor/multiple-recipients.png +0 -0
  104. package/screenshots/truth/actions/send_email/editor/simple-email.png +0 -0
  105. package/screenshots/truth/actions/send_email/editor/with-expressions.png +0 -0
  106. package/screenshots/truth/actions/send_msg/editor/long-quick-replies.png +0 -0
  107. package/screenshots/truth/actions/send_msg/editor/multiline-text-with-replies.png +0 -0
  108. package/screenshots/truth/actions/send_msg/editor/simple-text.png +0 -0
  109. package/screenshots/truth/actions/send_msg/editor/text-with-linebreaks.png +0 -0
  110. package/screenshots/truth/actions/send_msg/editor/text-with-many-quick-replies.png +0 -0
  111. package/screenshots/truth/actions/send_msg/editor/text-with-quick-replies.png +0 -0
  112. package/screenshots/truth/actions/send_msg/editor/text-without-quick-replies.png +0 -0
  113. package/screenshots/truth/checkbox/checkbox-label-background-hover.png +0 -0
  114. package/screenshots/truth/checkbox/checkbox-no-label-no-background-hover.png +0 -0
  115. package/screenshots/truth/checkbox/checkbox-whitespace-label-no-background-hover.png +0 -0
  116. package/screenshots/truth/checkbox/checkbox-with-help-text.png +0 -0
  117. package/screenshots/truth/checkbox/checked.png +0 -0
  118. package/screenshots/truth/checkbox/default.png +0 -0
  119. package/screenshots/truth/colorpicker/default.png +0 -0
  120. package/screenshots/truth/colorpicker/focused.png +0 -0
  121. package/screenshots/truth/colorpicker/initialized.png +0 -0
  122. package/screenshots/truth/colorpicker/selected.png +0 -0
  123. package/screenshots/truth/compose/attachments-tab.png +0 -0
  124. package/screenshots/truth/compose/attachments-with-files.png +0 -0
  125. package/screenshots/truth/compose/intial-text.png +0 -0
  126. package/screenshots/truth/compose/no-counter.png +0 -0
  127. package/screenshots/truth/compose/wraps-text-and-spaces.png +0 -0
  128. package/screenshots/truth/compose/wraps-text-and-url.png +0 -0
  129. package/screenshots/truth/compose/wraps-text-no-spaces.png +0 -0
  130. package/screenshots/truth/contacts/chat-failure.png +0 -0
  131. package/screenshots/truth/contacts/chat-for-active-contact.png +0 -0
  132. package/screenshots/truth/contacts/chat-sends-attachments-only.png +0 -0
  133. package/screenshots/truth/contacts/chat-sends-text-and-attachments.png +0 -0
  134. package/screenshots/truth/contacts/chat-sends-text-only.png +0 -0
  135. package/screenshots/truth/counter/summary.png +0 -0
  136. package/screenshots/truth/counter/text.png +0 -0
  137. package/screenshots/truth/counter/unicode-variables.png +0 -0
  138. package/screenshots/truth/counter/unicode.png +0 -0
  139. package/screenshots/truth/counter/variable.png +0 -0
  140. package/screenshots/truth/datepicker/date-truncated-time.png +0 -0
  141. package/screenshots/truth/datepicker/date.png +0 -0
  142. package/screenshots/truth/datepicker/initial-timezone.png +0 -0
  143. package/screenshots/truth/datepicker/range-picker-editing-start.png +0 -0
  144. package/screenshots/truth/datepicker/updated-keyboard-date.png +0 -0
  145. package/screenshots/truth/dialog/focused.png +0 -0
  146. package/screenshots/truth/dropdown/right-edge-collision.png +0 -0
  147. package/screenshots/truth/editor/router.png +0 -0
  148. package/screenshots/truth/editor/send_msg.png +0 -0
  149. package/screenshots/truth/editor/set_contact_language.png +0 -0
  150. package/screenshots/truth/editor/set_contact_name.png +0 -0
  151. package/screenshots/truth/editor/set_run_result.png +0 -0
  152. package/screenshots/truth/editor/wait.png +0 -0
  153. package/screenshots/truth/field-renderer/checkbox-checked.png +0 -0
  154. package/screenshots/truth/field-renderer/checkbox-unchecked.png +0 -0
  155. package/screenshots/truth/field-renderer/checkbox-with-errors.png +0 -0
  156. package/screenshots/truth/field-renderer/context-comparison.png +0 -0
  157. package/screenshots/truth/field-renderer/key-value-with-label.png +0 -0
  158. package/screenshots/truth/field-renderer/message-editor-with-label.png +0 -0
  159. package/screenshots/truth/field-renderer/select-multi.png +0 -0
  160. package/screenshots/truth/field-renderer/select-no-label.png +0 -0
  161. package/screenshots/truth/field-renderer/select-with-label.png +0 -0
  162. package/screenshots/truth/field-renderer/text-evaluated.png +0 -0
  163. package/screenshots/truth/field-renderer/text-no-label.png +0 -0
  164. package/screenshots/truth/field-renderer/text-with-errors.png +0 -0
  165. package/screenshots/truth/field-renderer/text-with-label.png +0 -0
  166. package/screenshots/truth/field-renderer/textarea-evaluated.png +0 -0
  167. package/screenshots/truth/field-renderer/textarea-with-label.png +0 -0
  168. package/screenshots/truth/integration/checkbox-markdown-errors.png +0 -0
  169. package/screenshots/truth/list/fields-dragging.png +0 -0
  170. package/screenshots/truth/list/fields-filtered.png +0 -0
  171. package/screenshots/truth/list/fields-hovered.png +0 -0
  172. package/screenshots/truth/list/fields.png +0 -0
  173. package/screenshots/truth/list/items-selected.png +0 -0
  174. package/screenshots/truth/list/items-updated.png +0 -0
  175. package/screenshots/truth/list/items.png +0 -0
  176. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  177. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  178. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  179. package/screenshots/truth/menu/menu-submenu.png +0 -0
  180. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  181. package/screenshots/truth/menu/menu-tasks.png +0 -0
  182. package/screenshots/truth/message-editor/autogrow-initial-content.png +0 -0
  183. package/screenshots/truth/message-editor/default.png +0 -0
  184. package/screenshots/truth/message-editor/drag-highlight.png +0 -0
  185. package/screenshots/truth/message-editor/filtered-attachments.png +0 -0
  186. package/screenshots/truth/message-editor/with-completion.png +0 -0
  187. package/screenshots/truth/message-editor/with-properties.png +0 -0
  188. package/screenshots/truth/modax/form.png +0 -0
  189. package/screenshots/truth/modax/simple.png +0 -0
  190. package/screenshots/truth/nodes/split_by_llm/editor/information-extraction.png +0 -0
  191. package/screenshots/truth/nodes/split_by_llm/editor/sentiment-analysis.png +0 -0
  192. package/screenshots/truth/nodes/split_by_llm/editor/summarization.png +0 -0
  193. package/screenshots/truth/nodes/split_by_llm/editor/translation-task.png +0 -0
  194. package/screenshots/truth/nodes/split_by_llm/render/information-extraction.png +0 -0
  195. package/screenshots/truth/nodes/split_by_llm/render/sentiment-analysis.png +0 -0
  196. package/screenshots/truth/nodes/split_by_llm/render/summarization.png +0 -0
  197. package/screenshots/truth/nodes/split_by_llm/render/translation-task.png +0 -0
  198. package/screenshots/truth/nodes/split_by_llm_categorize/editor/basic-categorization.png +0 -0
  199. package/screenshots/truth/nodes/split_by_llm_categorize/editor/custom-input-and-result-name.png +0 -0
  200. package/screenshots/truth/nodes/split_by_llm_categorize/editor/feedback-categorization.png +0 -0
  201. package/screenshots/truth/nodes/split_by_llm_categorize/editor/many-categories.png +0 -0
  202. package/screenshots/truth/nodes/split_by_llm_categorize/editor/minimal-categories.png +0 -0
  203. package/screenshots/truth/nodes/split_by_random/editor/ab-test-multiple-variants.png +0 -0
  204. package/screenshots/truth/nodes/split_by_random/editor/sampling-split.png +0 -0
  205. package/screenshots/truth/nodes/split_by_random/editor/three-way-split.png +0 -0
  206. package/screenshots/truth/nodes/split_by_random/editor/two-bucket-split.png +0 -0
  207. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  208. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  209. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  210. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  211. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  212. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  213. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  214. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  215. package/screenshots/truth/omnibox/selected.png +0 -0
  216. package/screenshots/truth/options/block.png +0 -0
  217. package/screenshots/truth/run-list/basic.png +0 -0
  218. package/screenshots/truth/select/disabled-multi-selection.png +0 -0
  219. package/screenshots/truth/select/disabled-selection.png +0 -0
  220. package/screenshots/truth/select/disabled.png +0 -0
  221. package/screenshots/truth/select/embedded.png +0 -0
  222. package/screenshots/truth/select/empty-options.png +0 -0
  223. package/screenshots/truth/select/expression-selected.png +0 -0
  224. package/screenshots/truth/select/expressions.png +0 -0
  225. package/screenshots/truth/select/functions.png +0 -0
  226. package/screenshots/truth/select/local-options.png +0 -0
  227. package/screenshots/truth/select/multi-with-endpoint.png +0 -0
  228. package/screenshots/truth/select/multiple-initial-values.png +0 -0
  229. package/screenshots/truth/select/remote-options.png +0 -0
  230. package/screenshots/truth/select/search-enabled.png +0 -0
  231. package/screenshots/truth/select/search-multi-no-matches.png +0 -0
  232. package/screenshots/truth/select/search-selected-focus.png +0 -0
  233. package/screenshots/truth/select/search-selected.png +0 -0
  234. package/screenshots/truth/select/search-with-selected.png +0 -0
  235. package/screenshots/truth/select/searching.png +0 -0
  236. package/screenshots/truth/select/selected-multi-maxitems-reached.png +0 -0
  237. package/screenshots/truth/select/selected-multi.png +0 -0
  238. package/screenshots/truth/select/selected-single.png +0 -0
  239. package/screenshots/truth/select/selection-clearable.png +0 -0
  240. package/screenshots/truth/select/static-initial-value.png +0 -0
  241. package/screenshots/truth/select/static-initial-via-selected.png +0 -0
  242. package/screenshots/truth/select/truncated-selection.png +0 -0
  243. package/screenshots/truth/select/with-placeholder.png +0 -0
  244. package/screenshots/truth/select/without-placeholder.png +0 -0
  245. package/screenshots/truth/slider/update-slider-on-circle-dragged.png +0 -0
  246. package/screenshots/truth/templates/default.png +0 -0
  247. package/screenshots/truth/templates/unapproved.png +0 -0
  248. package/screenshots/truth/textinput/autogrow-initial.png +0 -0
  249. package/screenshots/truth/textinput/input-disabled.png +0 -0
  250. package/screenshots/truth/textinput/input-focused.png +0 -0
  251. package/screenshots/truth/textinput/input-form.png +0 -0
  252. package/screenshots/truth/textinput/input-inserted.png +0 -0
  253. package/screenshots/truth/textinput/input-placeholder.png +0 -0
  254. package/screenshots/truth/textinput/input-updated.png +0 -0
  255. package/screenshots/truth/textinput/input.png +0 -0
  256. package/screenshots/truth/textinput/textarea-focused.png +0 -0
  257. package/screenshots/truth/textinput/textarea.png +0 -0
  258. package/src/events.ts +9 -8
  259. package/src/flow/Editor.ts +6 -3
  260. package/src/flow/actions/set_contact_channel.ts +1 -1
  261. package/src/flow/actions/set_contact_field.ts +1 -1
  262. package/src/flow/actions/set_contact_language.ts +1 -1
  263. package/src/flow/actions/set_contact_status.ts +1 -1
  264. package/src/flow/config.ts +2 -8
  265. package/src/flow/nodes/split_by_llm.ts +119 -0
  266. package/src/flow/nodes/split_by_llm_categorize.ts +13 -116
  267. package/src/flow/nodes/split_by_subflow.ts +153 -3
  268. package/src/flow/nodes/split_by_ticket.ts +135 -12
  269. package/src/flow/nodes/split_by_webhook.ts +187 -12
  270. package/src/form/ArrayEditor.ts +45 -57
  271. package/src/form/BaseListEditor.ts +4 -4
  272. package/src/form/Checkbox.ts +81 -24
  273. package/src/form/ColorPicker.ts +31 -43
  274. package/src/form/Completion.ts +49 -56
  275. package/src/form/Compose.ts +8 -8
  276. package/src/form/ContactSearch.ts +3 -4
  277. package/src/form/DatePicker.ts +32 -38
  278. package/src/form/{FormField.ts → FieldElement.ts} +105 -52
  279. package/src/form/FieldRenderer.ts +2 -1
  280. package/src/form/ImagePicker.ts +107 -110
  281. package/src/form/KeyValueEditor.ts +43 -39
  282. package/src/form/MessageEditor.ts +61 -67
  283. package/src/form/TembaSlider.ts +3 -3
  284. package/src/form/TemplateEditor.ts +3 -3
  285. package/src/form/TextInput.ts +25 -28
  286. package/src/form/select/Select.ts +12 -17
  287. package/src/form/select/UserSelect.ts +10 -11
  288. package/src/form/select/WorkspaceSelect.ts +9 -10
  289. package/src/live/ContactChat.ts +81 -92
  290. package/src/live/ContactFieldEditor.ts +2 -2
  291. package/src/store/flow-definition.d.ts +2 -1
  292. package/src/utils.ts +192 -0
  293. package/temba-modules.ts +3 -2
  294. package/test/nodes/split_by_llm.test.ts +232 -0
  295. package/test/temba-checkbox.test.ts +26 -0
  296. package/test/temba-integration-markdown.test.ts +2 -4
  297. package/test/temba-slider.test.ts +0 -1
  298. package/test-assets/contacts/history.json +7 -20
  299. package/test-assets/style.css +36 -234
  300. package/web-dev-server.config.mjs +26 -0
  301. package/web-test-runner.config.mjs +1 -1
  302. package/out-tsc/src/flow/actions/call_llm.js +0 -64
  303. package/out-tsc/src/flow/actions/call_llm.js.map +0 -1
  304. package/out-tsc/src/flow/actions/call_webhook.js +0 -131
  305. package/out-tsc/src/flow/actions/call_webhook.js.map +0 -1
  306. package/out-tsc/src/flow/actions/enter_flow.js +0 -14
  307. package/out-tsc/src/flow/actions/enter_flow.js.map +0 -1
  308. package/out-tsc/src/flow/actions/open_ticket.js +0 -73
  309. package/out-tsc/src/flow/actions/open_ticket.js.map +0 -1
  310. package/out-tsc/src/form/FormElement.js +0 -67
  311. package/out-tsc/src/form/FormElement.js.map +0 -1
  312. package/out-tsc/src/form/FormField.js.map +0 -1
  313. package/out-tsc/test/actions/call_llm.test.js +0 -103
  314. package/out-tsc/test/actions/call_llm.test.js.map +0 -1
  315. package/out-tsc/test/temba-formfield.test.js +0 -94
  316. package/out-tsc/test/temba-formfield.test.js.map +0 -1
  317. package/src/flow/actions/call_llm.ts +0 -66
  318. package/src/flow/actions/call_webhook.ts +0 -143
  319. package/src/flow/actions/enter_flow.ts +0 -15
  320. package/src/flow/actions/open_ticket.ts +0 -83
  321. package/src/form/FormElement.ts +0 -69
  322. package/test/actions/call_llm.test.ts +0 -137
  323. package/test/temba-formfield.test.ts +0 -121
@@ -0,0 +1,232 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { split_by_llm } from '../../src/flow/nodes/split_by_llm';
3
+ import { Node, CallLLM } from '../../src/store/flow-definition';
4
+ import { NodeTest } from '../NodeHelper';
5
+
6
+ /**
7
+ * Test suite for the split_by_llm node configuration.
8
+ */
9
+ describe('split_by_llm node config', () => {
10
+ const helper = new NodeTest(split_by_llm, 'split_by_llm');
11
+
12
+ describe('basic properties', () => {
13
+ helper.testBasicProperties();
14
+
15
+ it('has correct name', () => {
16
+ expect(split_by_llm.name).to.equal('Call AI');
17
+ });
18
+
19
+ it('has form configuration', () => {
20
+ expect(split_by_llm.form).to.exist;
21
+ expect(split_by_llm.form.llm).to.exist;
22
+ expect(split_by_llm.form.instructions).to.exist;
23
+ expect(split_by_llm.form.input).to.exist;
24
+ });
25
+
26
+ it('has layout configuration', () => {
27
+ expect(split_by_llm.layout).to.exist;
28
+ expect(split_by_llm.layout).to.deep.equal([
29
+ 'llm',
30
+ 'input',
31
+ 'instructions'
32
+ ]);
33
+ });
34
+ });
35
+
36
+ describe('toFormData', () => {
37
+ it('extracts data from node with call_llm action', () => {
38
+ const node: Node = {
39
+ uuid: 'test-node',
40
+ actions: [
41
+ {
42
+ uuid: 'test-action',
43
+ type: 'call_llm',
44
+ llm: { uuid: 'gpt-4', name: 'GPT 4.1' },
45
+ instructions: 'Translate to French',
46
+ input: '@input',
47
+ output_local: '_llm_output'
48
+ } as CallLLM
49
+ ],
50
+ exits: []
51
+ };
52
+
53
+ const formData = split_by_llm.toFormData(node);
54
+
55
+ expect(formData.uuid).to.equal('test-node');
56
+ expect(formData.llm).to.deep.equal([{ value: 'gpt-4', name: 'GPT 4.1' }]);
57
+ expect(formData.input).to.equal('@input');
58
+ expect(formData.instructions).to.equal('Translate to French');
59
+ });
60
+
61
+ it('handles empty node', () => {
62
+ const node: Node = {
63
+ uuid: 'test-node',
64
+ actions: [],
65
+ exits: []
66
+ };
67
+
68
+ const formData = split_by_llm.toFormData(node);
69
+
70
+ expect(formData.uuid).to.equal('test-node');
71
+ expect(formData.llm).to.deep.equal([]);
72
+ expect(formData.input).to.equal('@input');
73
+ expect(formData.instructions).to.equal('');
74
+ });
75
+ });
76
+
77
+ describe('fromFormData', () => {
78
+ it('creates node with call_llm action and router', () => {
79
+ const formData = {
80
+ uuid: 'test-node',
81
+ llm: [{ value: 'gpt-4', name: 'GPT 4.1' }],
82
+ input: '@input',
83
+ instructions: 'Translate to French'
84
+ };
85
+
86
+ const originalNode: Node = {
87
+ uuid: 'test-node',
88
+ actions: [],
89
+ exits: []
90
+ };
91
+
92
+ const node = split_by_llm.fromFormData(formData, originalNode);
93
+
94
+ expect(node.uuid).to.equal('test-node');
95
+ expect(node.actions).to.have.length(1);
96
+ expect(node.actions[0].type).to.equal('call_llm');
97
+ expect((node.actions[0] as CallLLM).llm).to.deep.equal({
98
+ uuid: 'gpt-4',
99
+ name: 'GPT 4.1'
100
+ });
101
+ expect((node.actions[0] as CallLLM).instructions).to.equal(
102
+ 'Translate to French'
103
+ );
104
+ expect((node.actions[0] as CallLLM).input).to.equal('@input');
105
+ expect((node.actions[0] as CallLLM).output_local).to.equal('_llm_output');
106
+
107
+ expect(node.router).to.exist;
108
+ expect(node.router.type).to.equal('switch');
109
+ expect(node.router.operand).to.equal('@locals._llm_output');
110
+ expect(node.router.categories).to.have.length(2);
111
+ expect(node.router.categories[0].name).to.equal('Success');
112
+ expect(node.router.categories[1].name).to.equal('Failure');
113
+
114
+ expect(node.exits).to.have.length(2);
115
+ });
116
+
117
+ it('handles empty form data', () => {
118
+ const formData = {
119
+ uuid: 'test-node',
120
+ llm: [],
121
+ input: '',
122
+ instructions: ''
123
+ };
124
+
125
+ const originalNode: Node = {
126
+ uuid: 'test-node',
127
+ actions: [],
128
+ exits: []
129
+ };
130
+
131
+ const node = split_by_llm.fromFormData(formData, originalNode);
132
+
133
+ const action = node.actions[0] as CallLLM;
134
+ expect(action.llm).to.deep.equal({ uuid: '', name: '' });
135
+ expect(action.instructions).to.equal('');
136
+ expect(action.input).to.equal('@input');
137
+ });
138
+ });
139
+
140
+ describe('node scenarios', () => {
141
+ const createTestNode = (
142
+ llm: any,
143
+ instructions: string,
144
+ input: string = '@input'
145
+ ): Node => ({
146
+ uuid: 'test-node',
147
+ actions: [
148
+ {
149
+ uuid: 'test-action',
150
+ type: 'call_llm',
151
+ llm,
152
+ instructions,
153
+ input,
154
+ output_local: '_llm_output'
155
+ } as CallLLM
156
+ ],
157
+ router: {
158
+ type: 'switch',
159
+ operand: '@locals._llm_output',
160
+ categories: [
161
+ {
162
+ uuid: 'success-category',
163
+ name: 'Success',
164
+ exit_uuid: 'success-exit'
165
+ },
166
+ {
167
+ uuid: 'failure-category',
168
+ name: 'Failure',
169
+ exit_uuid: 'failure-exit'
170
+ }
171
+ ],
172
+ default_category_uuid: 'failure-category',
173
+ cases: [
174
+ {
175
+ uuid: 'success-case',
176
+ type: 'has_text',
177
+ arguments: [],
178
+ category_uuid: 'success-category'
179
+ }
180
+ ]
181
+ },
182
+ exits: [
183
+ {
184
+ uuid: 'success-exit',
185
+ destination_uuid: null
186
+ },
187
+ {
188
+ uuid: 'failure-exit',
189
+ destination_uuid: null
190
+ }
191
+ ]
192
+ });
193
+
194
+ const nodeUI = {
195
+ type: 'split_by_llm',
196
+ position: { left: 50, top: 50 }
197
+ };
198
+
199
+ helper.testNode(
200
+ createTestNode({ uuid: 'gpt-4', name: 'GPT 4.1' }, 'Translate to French'),
201
+ nodeUI,
202
+ 'translation-task'
203
+ );
204
+
205
+ helper.testNode(
206
+ createTestNode(
207
+ { uuid: 'gpt-5', name: 'GPT 5' },
208
+ 'Analyze the sentiment of the following message and classify it as positive, negative, or neutral. Provide a brief explanation for your classification.'
209
+ ),
210
+ nodeUI,
211
+ 'sentiment-analysis'
212
+ );
213
+
214
+ helper.testNode(
215
+ createTestNode(
216
+ { uuid: 'gpt-4', name: 'GPT 4.1' },
217
+ 'Summarize the key points from the conversation above in bullet format.'
218
+ ),
219
+ nodeUI,
220
+ 'summarization'
221
+ );
222
+
223
+ helper.testNode(
224
+ createTestNode(
225
+ { uuid: 'gpt-5', name: 'GPT 5' },
226
+ 'Extract any contact information (phone numbers, email addresses) from the text and format them as a JSON object.'
227
+ ),
228
+ nodeUI,
229
+ 'information-extraction'
230
+ );
231
+ });
232
+ });
@@ -165,4 +165,30 @@ describe('temba-checkbox', () => {
165
165
  data = new FormData(form);
166
166
  expect(data.get('my-cb')).to.equal('5');
167
167
  });
168
+
169
+ it('aligns help text with label when both are present', async () => {
170
+ const el: Checkbox = await fixture(html`
171
+ <temba-checkbox
172
+ label="Checkbox with help"
173
+ help_text="This help text should align with the label text"
174
+ >
175
+ </temba-checkbox>
176
+ `);
177
+
178
+ expect(el.label).to.equal('Checkbox with help');
179
+ expect(el.helpText).to.equal(
180
+ 'This help text should align with the label text'
181
+ );
182
+
183
+ // Verify help text element exists and has proper alignment styles
184
+ const helpTextEl = el.shadowRoot.querySelector(
185
+ '.checkbox-help-text'
186
+ ) as HTMLElement;
187
+ expect(helpTextEl).to.not.be.null;
188
+ expect(helpTextEl.textContent.trim()).to.equal(
189
+ 'This help text should align with the label text'
190
+ );
191
+
192
+ await assertScreenshot('checkbox/checkbox-with-help-text', getClip(el));
193
+ });
168
194
  });
@@ -16,10 +16,8 @@ describe('FormElement markdown integration', () => {
16
16
 
17
17
  await checkbox.updateComplete;
18
18
 
19
- // Check that errors are rendered with markdown
20
- const errorElements = checkbox.shadowRoot
21
- .querySelectorAll('temba-field')[0]
22
- .shadowRoot.querySelectorAll('.alert-error');
19
+ // Check that errors are rendered with markdown directly in checkbox shadow root
20
+ const errorElements = checkbox.shadowRoot.querySelectorAll('.alert-error');
23
21
  expect(errorElements.length).to.equal(2);
24
22
 
25
23
  // First error should have bold text and link
@@ -15,7 +15,6 @@ describe('temba-slider', () => {
15
15
  <temba-slider label="My Slider"></temba-slider>
16
16
  `);
17
17
 
18
- expect(slider.label).to.equal('My Slider');
19
18
  await assertScreenshot('slider/default', getClip(slider));
20
19
  });
21
20
 
@@ -62,13 +62,14 @@
62
62
  "logs_url": null
63
63
  },
64
64
  {
65
- "type": "flow_exited",
66
- "created_on": "2021-03-30T22:20:35.573809+00:00",
65
+ "type": "run_ended",
66
+ "created_on": "2021-03-30T22:20:26.704467+00:00",
67
+ "run_uuid": "0198c846-da2b-799f-8af9-aaf8857f947f",
67
68
  "flow": {
68
69
  "uuid": "d076d716-8071-417e-b188-d9746db223a1",
69
70
  "name": "Favorites"
70
71
  },
71
- "status": "C"
72
+ "status": "completed"
72
73
  },
73
74
  {
74
75
  "uuid": "01988a70-3e8f-7890-b1d2-7418db7a575e",
@@ -209,13 +210,9 @@
209
210
  "logs_url": "/channels/channellog/read/1472/"
210
211
  },
211
212
  {
212
- "type": "flow_entered",
213
- "created_on": "2021-03-30T22:20:26.704467+00:00",
214
- "flow": {
215
- "uuid": "d076d716-8071-417e-b188-d9746db223a1",
216
- "name": "Favorites"
217
- },
218
- "logs_url": null
213
+ "uuid": "0198e7b8-fe00-76e6-91e4-1253dd9a6230",
214
+ "type": "call_missed",
215
+ "created_on": "2021-03-30T22:20:26.704467+00:00"
219
216
  },
220
217
  {
221
218
  "type": "run_started",
@@ -226,16 +223,6 @@
226
223
  "name": "Favorites"
227
224
  }
228
225
  },
229
- {
230
- "type": "run_ended",
231
- "created_on": "2021-03-30T22:20:26.704467+00:00",
232
- "run_uuid": "0198c846-da2b-799f-8af9-aaf8857f947f",
233
- "flow": {
234
- "uuid": "d076d716-8071-417e-b188-d9746db223a1",
235
- "name": "Favorites"
236
- },
237
- "status": "completed"
238
- },
239
226
  {
240
227
  "uuid": "01988a77-979e-7768-a940-8d9c348e24fe",
241
228
  "type": "msg_received",
@@ -1,236 +1,3 @@
1
- #mocha {
2
- display: none;
3
- }
4
-
5
- temba-dialog,
6
- temba-modax,
7
- temba-contact-history,
8
- temba-menu,
9
- temba-select {
10
- --transition-speed: 0ms !important;
11
- }
12
-
13
- body {
14
- padding: 20px;
15
- }
16
-
17
- html input {
18
-
19
- caret-color: transparent;
20
- }
21
-
22
- temba-input,
23
- temba-modax,
24
- temba-dialog {
25
- caret-color: transparent;
26
- }
27
-
28
- /* Disable CSS animations for deterministic screenshots */
29
- *,
30
- *::before,
31
- *::after {
32
- animation-duration: 0s !important;
33
- animation-delay: 0s !important;
34
- transition-duration: 0s !important;
35
- transition-delay: 0s !important;
36
- animation-iteration-count: 1 !important;
37
- }
38
-
39
- /* Override CSS custom properties for animation control in tests */
40
- :root {
41
- --test-animation-duration: 0s !important;
42
- --test-animation-play-state: paused !important;
43
- }
44
-
45
- /* Force disable spin animations by overriding keyframes - this pierces shadow DOM */
46
- @keyframes spin {
47
- from { transform: rotate(0deg); }
48
- to { transform: rotate(0deg); }
49
- }
50
-
51
- html {
52
- --transition-speed: 0ms !important;
53
- --input-caret: transparent !important;
54
- --font-family: 'Roboto';
55
- --primary-rgb: 35, 135, 202;
56
- --secondary-rgb: 140, 51, 140;
57
- --tertiary-rgb: 135, 202, 35;
58
-
59
- --focus-rgb: 82, 168, 236;
60
- --error-rgb: 255, 99, 71;
61
- --success-rgb: 102, 186, 104;
62
-
63
- --color-label: #333;
64
-
65
- --selection-light-rgb: 240, 240, 240;
66
- --selection-dark-rgb: 180, 180, 180;
67
-
68
- --select-input-height: inherit;
69
-
70
- --curvature: 6px;
71
- --curvature-widget: 6px;
72
- --color-focus: #a4cafe;
73
- --color-widget-bg: #fff;
74
- --color-widget-bg-focused: #fff;
75
- --color-widget-border: rgb(225, 225, 225);
76
-
77
- /* primary colors, should be dark */
78
- --color-selection: #f0f6ff;
79
-
80
- --widget-box-shadow-focused: 0 0 0 3px rgba(164, 202, 254, .45);
81
- --widget-box-shadow-focused-error: 0 0 0 3px rgba(var(--error-rgb), 0.3);
82
-
83
- --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
84
-
85
- /* page text, borders, widgets */
86
- --color-text: #555;
87
- --color-widget-text: #555;
88
- --color-borders: rgba(0, 0, 0, 0.07);
89
- --color-placeholder: #ccc;
90
-
91
- /* light colors, panel backgrounds, selection, etc */
92
- --color-primary-light: #eee;
93
- --color-secondary-light: #ccc;
94
-
95
- /* dark colors, nav bar, buttons, etc */
96
- --color-primary-dark: rgb(var(--primary-rgb));
97
- --color-secondary-dark: rgb(var(--secondary-rgb));
98
-
99
- /* light text goes over dark, dark over lights */
100
- --color-text-light: rgba(255, 255, 255, 1);
101
- --color-text-dark: rgba(0, 0, 0, 0.5);
102
- --color-text-dark-secondary: rgba(0, 0, 0, 0.25);
103
- --color-text-help: rgba(0, 0, 0, 0.35);
104
-
105
- --color-tertiary: rgb(var(--tertiary-rgb));
106
-
107
- /* solid overlays with text */
108
- --color-overlay-dark: rgba(0, 0, 0, 0.2);
109
- --color-overlay-dark-text: rgba(255, 255, 255, 0.9);
110
- --color-overlay-light: rgba(0, 0, 0, 0.05);
111
- --color-overlay-light-text: rgba(0, 0, 0, 0.6);
112
-
113
- /* links, buttons, and label badges */
114
- --color-link-primary: rgba(var(--primary-rgb), 0.8);
115
- --color-link-primary-hover: rgba(var(--primary-rgb), 0.9);
116
- --color-link-secondary: rgba(var(--secondary-rgb), 0.8);
117
- --color-link-secondary-hover: rgba(var(--secondary-rgb), 0.9);
118
- --color-button-primary: var(--color-primary-dark);
119
- --color-button-primary-text: var(--color-text-light);
120
- --color-button-light: rgb(246, 248, 250);
121
- --color-button-light-text: rgb(36, 41, 47);
122
- --color-button-secondary: var(--color-secondary-light);
123
- --color-button-secondary-text: var(--color-text-dark);
124
-
125
- --color-button-destructive: rgb(var(--error-rgb));
126
- --color-button-destructive-text: var(--color-text-light);
127
-
128
- --color-button-attention: #2ecc71;
129
-
130
- --color-label-primary: var(--color-primary-dark);
131
- --color-label-primary-text: var(--color-text-light);
132
- --color-label-secondary: rgba(0, 0, 0, 0.2);
133
- --color-label-secondary-text: rgba(255, 255, 255, 0.9);
134
-
135
- --color-nav-unselected: #fff;
136
- --color-nav-selected-bg: #fff;
137
- --color-nav-selected-text: var(--color-primary-dark);
138
- --color-info: rgb(238, 246, 255);
139
- --color-info-border: rgb(74, 163, 223);
140
-
141
- --color-warning: rgb(255, 253, 218);
142
- --color-warning-border: rgb(251, 246, 176);
143
- --color-error: rgb(var(--error-rgb));
144
- --font-size: 14px;
145
- --button-font-size: 1.125rem;
146
-
147
- --header-bg: var(--color-primary-dark);
148
- --header-text: var(--color-text-light);
149
-
150
- --temba-textinput-padding: 9px;
151
- --temba-textinput-font-size: 13px;
152
-
153
- --options-block-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.03);
154
- --options-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
155
-
156
- font-size: var(--font-size);
157
- font-weight: 400;
158
- font-family: var(--font-family);
159
- --dropdown-shadow: rgb(0 0 0 / 30%) 0px 0px 60px, rgb(0 0 0 / 12%) 0px 6px 12px;
160
-
161
- --icon-color: var(--text-color);
162
- --icon-color-hover: var(--icon-color);
163
- --event-padding: 0.5em 1em;
164
- --temba-select-selected-padding: 9px;
165
- --temba-select-selected-line-height: 16px;
166
- --temba-select-selected-font-size: 13px;
167
-
168
- --help-text-margin-top: 0px;
169
- --help-text-margin-left: 0.3em;
170
- --help-text-size: 0.85em;
171
- --color-text-help: rgb(120, 120, 120);
172
- --label-size: 14px;
173
-
174
- --button-y: 6px;
175
- --button-x: 14px;
176
-
177
- --menu-padding: 1em;
178
- --color-automated: rgb(78, 205, 106);
179
- }
180
-
181
- temba-button {
182
- --button-bg: var(--color-primary-dark);
183
- --button-text: var(--color-text-light);
184
- --button-border: none;
185
- --button-shadow: var(--widget-box-shadow);
186
- }
187
-
188
- temba-button:hover {
189
- --button-bg-img: linear-gradient(to bottom, rgba(var(--primary-rgb), .1), transparent, transparent);
190
- }
191
-
192
- temba-button.active {
193
- --button-bg-img: linear-gradient(to bottom, transparent, rgba(0, 0, 0, .05));
194
- }
195
-
196
- temba-button.light {
197
- --button-bg: #fff;
198
- --button-text: #999;
199
- --button-border: none;
200
- --button-shadow: var(--widget-box-shadow);
201
-
202
- }
203
-
204
- temba-button.light:hover {
205
- --button-bg-img: linear-gradient(to bottom, transparent, rgba(0, 0, 0, .001));
206
- }
207
-
208
- temba-button.light.active {
209
- --button-bg-img: linear-gradient(to bottom, transparent, rgba(0, 0, 0, .02));
210
- }
211
-
212
- temba-contact-history {
213
- min-height: 0;
214
- display: flex;
215
- flex-grow: 1;
216
- flex-direction: column;
217
- }
218
-
219
- temba-select:focus {
220
- outline: none;
221
- box-shadow: none;
222
- }
223
-
224
- temba-datepicker {
225
- margin: 1em;
226
- }
227
-
228
- .flatpickr-calendar {
229
- margin-top: 28px;
230
- margin-bottom: 28px;
231
- margin-left: -13px;
232
- }
233
-
234
1
  @font-face {
235
2
  font-family: 'Roboto Mono';
236
3
  font-style: normal;
@@ -288,4 +55,39 @@ temba-datepicker {
288
55
  /* Chrome 26+, Opera 23+, Firefox 39+ */
289
56
  url('./fonts/roboto-v20-latin-500.woff') format('woff');
290
57
  /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
291
- }
58
+ }
59
+
60
+ /* Force disable spin animations by overriding keyframes - this pierces shadow DOM */
61
+ @keyframes spin {
62
+ from { transform: rotate(0deg); }
63
+ to { transform: rotate(0deg); }
64
+ }
65
+
66
+ #mocha {
67
+ display: none;
68
+ }
69
+
70
+ /* Disable transitions and carets for deterministic screenshots */
71
+ *,
72
+ *::before,
73
+ *::after {
74
+ animation-duration: 0s !important;
75
+ animation-delay: 0s !important;
76
+ transition-duration: 0s !important;
77
+ transition-delay: 0s !important;
78
+ animation-iteration-count: 1 !important;
79
+ --transition-speed: 0ms !important;
80
+ --test-animation-duration: 0s !important;
81
+ --test-animation-play-state: paused !important;
82
+ caret-color: transparent;
83
+ }
84
+
85
+ /**
86
+ Without this body styling our screenshots don't have the proper dpi,
87
+ perhaps triggers a different rendering path for puppeteer? The styling
88
+ appears like it could be anything.
89
+ Unsure.
90
+ **/
91
+ body {
92
+ padding: 20px;
93
+ }
@@ -56,6 +56,32 @@ export default {
56
56
  context.body = fs.readFileSync(path.resolve(staticFile), 'utf-8');
57
57
  return;
58
58
  }
59
+
60
+ // Handle dynamic flows endpoint
61
+ if (basePath === '/api/v2/flows.json') {
62
+ const flowsDir = path.resolve('./demo/data/flows');
63
+
64
+ if (fs.existsSync(flowsDir)) {
65
+ const files = fs.readdirSync(flowsDir).filter(file => file.endsWith('.json'));
66
+ const flows = files.map(file => {
67
+ try {
68
+ const filePath = path.join(flowsDir, file);
69
+ const flowData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
70
+ return {
71
+ uuid: flowData.definition.uuid,
72
+ name: flowData.definition.name
73
+ };
74
+ } catch (e) {
75
+ console.warn(`Error reading flow file ${file}:`, e.message);
76
+ return null;
77
+ }
78
+ }).filter(Boolean);
79
+
80
+ context.contentType = 'application/json';
81
+ context.body = JSON.stringify({ results: flows });
82
+ return;
83
+ }
84
+ }
59
85
  }
60
86
 
61
87
  // Handle minio file uploads for media
@@ -329,7 +329,7 @@ export default {
329
329
  return {
330
330
  body: context.body.replace(
331
331
  /<head>/,
332
- `<head><link rel="stylesheet" href="/test-assets/style.css">`
332
+ `<head><link rel="stylesheet" href="/test-assets/temba-components.css"/><link rel="stylesheet" href="/test-assets/style.css"/>`
333
333
  ),
334
334
  };
335
335
  }