@nyaruka/temba-components 0.138.6 → 0.140.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/.github/workflows/cla.yml +1 -1
  2. package/.github/workflows/copilot-setup-steps.yml +6 -1
  3. package/CHANGELOG.md +26 -0
  4. package/demo/data/flows/sample-flow.json +24 -0
  5. package/dist/locales/es.js +5 -5
  6. package/dist/locales/es.js.map +1 -1
  7. package/dist/locales/fr.js +5 -5
  8. package/dist/locales/fr.js.map +1 -1
  9. package/dist/locales/locale-codes.js +2 -11
  10. package/dist/locales/locale-codes.js.map +1 -1
  11. package/dist/locales/pt.js +5 -5
  12. package/dist/locales/pt.js.map +1 -1
  13. package/dist/temba-components.js +1112 -882
  14. package/dist/temba-components.js.map +1 -1
  15. package/out-tsc/src/display/Chat.js +10 -7
  16. package/out-tsc/src/display/Chat.js.map +1 -1
  17. package/out-tsc/src/display/Dropdown.js +3 -1
  18. package/out-tsc/src/display/Dropdown.js.map +1 -1
  19. package/out-tsc/src/display/FloatingTab.js +25 -32
  20. package/out-tsc/src/display/FloatingTab.js.map +1 -1
  21. package/out-tsc/src/display/Thumbnail.js +163 -5
  22. package/out-tsc/src/display/Thumbnail.js.map +1 -1
  23. package/out-tsc/src/flow/CanvasMenu.js +5 -3
  24. package/out-tsc/src/flow/CanvasMenu.js.map +1 -1
  25. package/out-tsc/src/flow/CanvasNode.js +70 -29
  26. package/out-tsc/src/flow/CanvasNode.js.map +1 -1
  27. package/out-tsc/src/flow/Editor.js +290 -239
  28. package/out-tsc/src/flow/Editor.js.map +1 -1
  29. package/out-tsc/src/flow/NodeEditor.js +118 -10
  30. package/out-tsc/src/flow/NodeEditor.js.map +1 -1
  31. package/out-tsc/src/flow/Plumber.js +757 -403
  32. package/out-tsc/src/flow/Plumber.js.map +1 -1
  33. package/out-tsc/src/flow/StickyNote.js +13 -4
  34. package/out-tsc/src/flow/StickyNote.js.map +1 -1
  35. package/out-tsc/src/flow/actions/audio-player.js +112 -0
  36. package/out-tsc/src/flow/actions/audio-player.js.map +1 -0
  37. package/out-tsc/src/flow/actions/enter_flow.js +43 -0
  38. package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
  39. package/out-tsc/src/flow/actions/play_audio.js +57 -4
  40. package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
  41. package/out-tsc/src/flow/actions/say_msg.js +86 -3
  42. package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
  43. package/out-tsc/src/flow/config.js +11 -3
  44. package/out-tsc/src/flow/config.js.map +1 -1
  45. package/out-tsc/src/flow/nodes/shared-rules.js +1 -1
  46. package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -1
  47. package/out-tsc/src/flow/nodes/terminal.js +7 -0
  48. package/out-tsc/src/flow/nodes/terminal.js.map +1 -0
  49. package/out-tsc/src/flow/nodes/wait_for_audio.js +77 -0
  50. package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
  51. package/out-tsc/src/flow/nodes/wait_for_dial.js +151 -0
  52. package/out-tsc/src/flow/nodes/wait_for_dial.js.map +1 -0
  53. package/out-tsc/src/flow/nodes/wait_for_digits.js +61 -1
  54. package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
  55. package/out-tsc/src/flow/nodes/wait_for_menu.js +173 -2
  56. package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
  57. package/out-tsc/src/flow/operators.js +21 -5
  58. package/out-tsc/src/flow/operators.js.map +1 -1
  59. package/out-tsc/src/flow/types.js.map +1 -1
  60. package/out-tsc/src/flow/utils.js +213 -65
  61. package/out-tsc/src/flow/utils.js.map +1 -1
  62. package/out-tsc/src/form/ArrayEditor.js +4 -2
  63. package/out-tsc/src/form/ArrayEditor.js.map +1 -1
  64. package/out-tsc/src/form/FieldRenderer.js +49 -0
  65. package/out-tsc/src/form/FieldRenderer.js.map +1 -1
  66. package/out-tsc/src/interfaces.js +2 -0
  67. package/out-tsc/src/interfaces.js.map +1 -1
  68. package/out-tsc/src/layout/Dialog.js +52 -7
  69. package/out-tsc/src/layout/Dialog.js.map +1 -1
  70. package/out-tsc/src/list/TicketList.js +4 -1
  71. package/out-tsc/src/list/TicketList.js.map +1 -1
  72. package/out-tsc/src/live/TembaChart.js.map +1 -1
  73. package/out-tsc/src/locales/es.js +5 -5
  74. package/out-tsc/src/locales/es.js.map +1 -1
  75. package/out-tsc/src/locales/fr.js +5 -5
  76. package/out-tsc/src/locales/fr.js.map +1 -1
  77. package/out-tsc/src/locales/locale-codes.js +2 -11
  78. package/out-tsc/src/locales/locale-codes.js.map +1 -1
  79. package/out-tsc/src/locales/pt.js +5 -5
  80. package/out-tsc/src/locales/pt.js.map +1 -1
  81. package/out-tsc/src/simulator/Simulator.js +10 -3
  82. package/out-tsc/src/simulator/Simulator.js.map +1 -1
  83. package/out-tsc/src/store/AppState.js +89 -3
  84. package/out-tsc/src/store/AppState.js.map +1 -1
  85. package/out-tsc/test/actions/play_audio.test.js +118 -0
  86. package/out-tsc/test/actions/play_audio.test.js.map +1 -0
  87. package/out-tsc/test/actions/say_msg.test.js +158 -0
  88. package/out-tsc/test/actions/say_msg.test.js.map +1 -0
  89. package/out-tsc/test/nodes/wait_for_audio.test.js +156 -0
  90. package/out-tsc/test/nodes/wait_for_audio.test.js.map +1 -0
  91. package/out-tsc/test/nodes/wait_for_dial.test.js +336 -0
  92. package/out-tsc/test/nodes/wait_for_dial.test.js.map +1 -0
  93. package/out-tsc/test/nodes/wait_for_digits.test.js +198 -84
  94. package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
  95. package/out-tsc/test/nodes/wait_for_menu.test.js +340 -0
  96. package/out-tsc/test/nodes/wait_for_menu.test.js.map +1 -0
  97. package/out-tsc/test/temba-floating-tab.test.js +4 -6
  98. package/out-tsc/test/temba-floating-tab.test.js.map +1 -1
  99. package/out-tsc/test/temba-flow-collision.test.js +473 -220
  100. package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
  101. package/out-tsc/test/temba-flow-editor.test.js +0 -2
  102. package/out-tsc/test/temba-flow-editor.test.js.map +1 -1
  103. package/out-tsc/test/temba-flow-plumber-connections.test.js +83 -84
  104. package/out-tsc/test/temba-flow-plumber-connections.test.js.map +1 -1
  105. package/out-tsc/test/temba-flow-plumber.test.js +102 -93
  106. package/out-tsc/test/temba-flow-plumber.test.js.map +1 -1
  107. package/out-tsc/test/temba-node-type-selector.test.js +6 -6
  108. package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
  109. package/package.json +1 -1
  110. package/screenshots/truth/actions/play_audio/editor/expression-url.png +0 -0
  111. package/screenshots/truth/actions/play_audio/editor/static-url.png +0 -0
  112. package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
  113. package/screenshots/truth/actions/play_audio/render/static-url.png +0 -0
  114. package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
  115. package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
  116. package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
  117. package/screenshots/truth/actions/say_msg/render/multiline-text.png +0 -0
  118. package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
  119. package/screenshots/truth/actions/say_msg/render/text-with-audio-url.png +0 -0
  120. package/screenshots/truth/editor/router.png +0 -0
  121. package/screenshots/truth/editor/wait.png +0 -0
  122. package/screenshots/truth/nodes/wait_for_audio/editor/basic-audio-wait.png +0 -0
  123. package/screenshots/truth/nodes/wait_for_audio/render/basic-audio-wait.png +0 -0
  124. package/screenshots/truth/nodes/wait_for_dial/editor/basic-dial.png +0 -0
  125. package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
  126. package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
  127. package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.png +0 -0
  128. package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
  129. package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
  130. package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
  131. package/screenshots/truth/nodes/wait_for_digits/render/digits-with-rules.png +0 -0
  132. package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
  133. package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.png +0 -0
  134. package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
  135. package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
  136. package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
  137. package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
  138. package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
  139. package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
  140. package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
  141. package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
  142. package/src/display/Chat.ts +13 -7
  143. package/src/display/Dropdown.ts +3 -1
  144. package/src/display/FloatingTab.ts +24 -33
  145. package/src/display/Thumbnail.ts +162 -2
  146. package/src/flow/CanvasMenu.ts +8 -3
  147. package/src/flow/CanvasNode.ts +75 -30
  148. package/src/flow/Editor.ts +336 -288
  149. package/src/flow/NodeEditor.ts +137 -9
  150. package/src/flow/Plumber.ts +1011 -457
  151. package/src/flow/StickyNote.ts +14 -4
  152. package/src/flow/actions/audio-player.ts +127 -0
  153. package/src/flow/actions/enter_flow.ts +44 -0
  154. package/src/flow/actions/play_audio.ts +64 -5
  155. package/src/flow/actions/say_msg.ts +94 -4
  156. package/src/flow/config.ts +11 -3
  157. package/src/flow/nodes/shared-rules.ts +1 -1
  158. package/src/flow/nodes/terminal.ts +9 -0
  159. package/src/flow/nodes/wait_for_audio.ts +88 -0
  160. package/src/flow/nodes/wait_for_dial.ts +176 -0
  161. package/src/flow/nodes/wait_for_digits.ts +86 -2
  162. package/src/flow/nodes/wait_for_menu.ts +209 -3
  163. package/src/flow/operators.ts +23 -5
  164. package/src/flow/types.ts +23 -1
  165. package/src/flow/utils.ts +238 -81
  166. package/src/form/ArrayEditor.ts +4 -2
  167. package/src/form/FieldRenderer.ts +64 -1
  168. package/src/interfaces.ts +3 -1
  169. package/src/layout/Dialog.ts +53 -7
  170. package/src/list/TicketList.ts +4 -1
  171. package/src/live/TembaChart.ts +1 -1
  172. package/src/locales/es.ts +13 -18
  173. package/src/locales/fr.ts +13 -18
  174. package/src/locales/locale-codes.ts +2 -11
  175. package/src/locales/pt.ts +13 -18
  176. package/src/simulator/Simulator.ts +13 -3
  177. package/src/store/AppState.ts +105 -1
  178. package/src/store/flow-definition.d.ts +2 -0
  179. package/test/actions/play_audio.test.ts +155 -0
  180. package/test/actions/say_msg.test.ts +196 -0
  181. package/test/nodes/wait_for_audio.test.ts +182 -0
  182. package/test/nodes/wait_for_dial.test.ts +382 -0
  183. package/test/nodes/wait_for_digits.test.ts +233 -109
  184. package/test/nodes/wait_for_menu.test.ts +383 -0
  185. package/test/temba-floating-tab.test.ts +4 -6
  186. package/test/temba-flow-collision.test.ts +495 -293
  187. package/test/temba-flow-editor.test.ts +0 -2
  188. package/test/temba-flow-plumber-connections.test.ts +97 -97
  189. package/test/temba-flow-plumber.test.ts +116 -103
  190. package/test/temba-node-type-selector.test.ts +6 -6
  191. package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
  192. package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
  193. package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
  194. package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
  195. package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
  196. package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait_for_dial.js","sourceRoot":"","sources":["../../../../src/flow/nodes/wait_for_dial.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAwB,SAAS,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,eAAe,EACf,gCAAgC,EAChC,gCAAgC,EACjC,MAAM,UAAU,CAAC;AAElB,MAAM,eAAe,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AACpE,MAAM,UAAU,GAAG;IACjB,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE;IAC5E;QACE,IAAI,EAAE,eAAe;QACrB,SAAS,EAAE,CAAC,WAAW,CAAC;QACxB,YAAY,EAAE,WAAW;KAC1B;IACD,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE;CACrE,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAe;IACvC,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,YAAY,CAAC,IAAI;IACxB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;IAC5B,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,eAAe,EAAE,QAAQ;QACzB,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5B,IAAI,EAAE,CAAC,CAAC,IAAW;YACnB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,YAAY,EAAE,CAAC,CAAC,YAAY;SAC7B,CAAC,CAAC;KACJ;IACD,IAAI,EAAE;QACJ,KAAK,EAAE;YACL,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,cAAc;YACrB,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,4BAA4B;SAC1C;QACD,kBAAkB,EAAE;YAClB,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,sBAAsB;YAC7B,QAAQ,EAAE,KAAK;YACf,WAAW,EAAE,IAAI;SAClB;QACD,kBAAkB,EAAE;YAClB,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,sBAAsB;YAC7B,QAAQ,EAAE,KAAK;YACf,WAAW,EAAE,MAAM;SACpB;QACD,WAAW,EAAE,eAAe;KAC7B;IACD,MAAM,EAAE;QACN,OAAO;QACP;YACE,IAAI,EAAE,KAAK;YACX,KAAK,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;SACpD;QACD,aAAa;KACd;IACD,UAAU,EAAE,CAAC,IAAU,EAAE,EAAE;;QACzB,MAAM,IAAI,GAAG,MAAA,IAAI,CAAC,MAAM,0CAAE,IAAI,CAAC;QAC/B,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,KAAK,KAAI,EAAE;YACxB,kBAAkB,EAAE,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,kBAAkB;gBAC1C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC;gBACjC,CAAC,CAAC,EAAE;YACN,kBAAkB,EAAE,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,kBAAkB;gBAC1C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC;gBACjC,CAAC,CAAC,EAAE;YACN,WAAW,EAAE,CAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,WAAW,KAAI,EAAE;SAC5C,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAE,YAAkB,EAAQ,EAAE;;QAC7D,MAAM,kBAAkB,GAAG,CAAA,MAAA,YAAY,CAAC,MAAM,0CAAE,UAAU,KAAI,EAAE,CAAC;QACjE,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/C,MAAM,aAAa,GAAG,CAAA,MAAA,YAAY,CAAC,MAAM,0CAAE,KAAK,KAAI,EAAE,CAAC;QAEvD,MAAM,UAAU,GAAe,EAAE,CAAC;QAClC,MAAM,KAAK,GAAW,EAAE,CAAC;QACzB,MAAM,KAAK,GAAU,EAAE,CAAC;QAExB,mDAAmD;QACnD,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CACtC,CAAC,CAAW,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CACpC,CAAC;YAEF,IAAI,QAAQ,EAAE,CAAC;gBACb,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC1B,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CACrC,CAAC,CAAO,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,SAAS,CAC3C,CAAC;gBACF,KAAK,CAAC,IAAI,CACR,YAAY,IAAI;oBACd,IAAI,EAAE,QAAQ,CAAC,SAAS;oBACxB,gBAAgB,EAAE,IAAI;iBACvB,CACF,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,YAAY,EAAE;oBACpB,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,QAAQ;iBACpB,CAAC,CAAC;gBACH,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QAED,yCAAyC;QACzC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC1E,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAExB,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CACrC,CAAC,CAAM,EAAE,EAAE,WACT,OAAA,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,CAAA,MAAA,CAAC,CAAC,SAAS,0CAAG,CAAC,CAAC,MAAK,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA,EAAA,CACzE,CAAC;YAEF,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,IAAI,KAAI,YAAY,EAAE;gBAC1C,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,aAAa,EAAE,QAAQ,CAAC,IAAI;aAC7B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAEnE,oBAAoB;QACpB,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAQ;YACtB,IAAI,EAAE,MAAM;YACZ,KAAK;SACN,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACvC,UAAU,CAAC,kBAAkB,GAAG,SAAS,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACvC,UAAU,CAAC,kBAAkB,GAAG,SAAS,CAAC;QAC5C,CAAC;QAED,MAAM,MAAM,GAAQ;YAClB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,oCAAoC;YAC7C,qBAAqB,EAAE,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,IAAI;YAC3C,KAAK;YACL,UAAU;YACV,IAAI,EAAE,UAAU;SACjB,CAAC;QAEF,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/D,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACnD,CAAC;QAED,OAAO;YACL,GAAG,YAAY;YACf,MAAM;YACN,KAAK;SACN,CAAC;IACJ,CAAC;IACD,WAAW,EAAE,YAAY;IACzB,sBAAsB,EAAE,gCAAgC;IACxD,wBAAwB,EAAE,gCAAgC;CAC3D,CAAC","sourcesContent":["import { SPLIT_GROUPS, FormData, NodeConfig, FlowTypes } from '../types';\nimport { Node, Category, Exit } from '../../store/flow-definition';\nimport { generateUUID } from '../../utils';\nimport {\n resultNameField,\n categoriesToLocalizationFormData,\n localizationFormDataToCategories\n} from './shared';\n\nconst DIAL_CATEGORIES = ['Answered', 'No Answer', 'Busy', 'Failed'];\nconst DIAL_CASES = [\n { type: 'has_only_text', arguments: ['answered'], categoryName: 'Answered' },\n {\n type: 'has_only_text',\n arguments: ['no_answer'],\n categoryName: 'No Answer'\n },\n { type: 'has_only_text', arguments: ['busy'], categoryName: 'Busy' }\n];\n\nexport const wait_for_dial: NodeConfig = {\n type: 'wait_for_dial',\n name: 'Redirect Call',\n group: SPLIT_GROUPS.wait,\n flowTypes: [FlowTypes.VOICE],\n router: {\n type: 'switch',\n defaultCategory: 'Failed',\n rules: DIAL_CASES.map((c) => ({\n type: c.type as any,\n arguments: c.arguments,\n categoryName: c.categoryName\n }))\n },\n form: {\n phone: {\n type: 'text',\n label: 'Phone Number',\n required: true,\n evaluated: true,\n placeholder: 'Phone number or expression'\n },\n dial_limit_seconds: {\n type: 'text',\n label: 'Dial Limit (seconds)',\n required: false,\n placeholder: '60'\n },\n call_limit_seconds: {\n type: 'text',\n label: 'Call Limit (seconds)',\n required: false,\n placeholder: '7200'\n },\n result_name: resultNameField\n },\n layout: [\n 'phone',\n {\n type: 'row',\n items: ['dial_limit_seconds', 'call_limit_seconds']\n },\n 'result_name'\n ],\n toFormData: (node: Node) => {\n const wait = node.router?.wait;\n return {\n uuid: node.uuid,\n phone: wait?.phone || '',\n dial_limit_seconds: wait?.dial_limit_seconds\n ? String(wait.dial_limit_seconds)\n : '',\n call_limit_seconds: wait?.call_limit_seconds\n ? String(wait.call_limit_seconds)\n : '',\n result_name: node.router?.result_name || ''\n };\n },\n fromFormData: (formData: FormData, originalNode: Node): Node => {\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n const categories: Category[] = [];\n const exits: Exit[] = [];\n const cases: any[] = [];\n\n // Build categories and cases for each dial outcome\n for (const catName of DIAL_CATEGORIES) {\n const existing = existingCategories.find(\n (c: Category) => c.name === catName\n );\n\n if (existing) {\n categories.push(existing);\n const existingExit = existingExits.find(\n (e: Exit) => e.uuid === existing.exit_uuid\n );\n exits.push(\n existingExit || {\n uuid: existing.exit_uuid,\n destination_uuid: null\n }\n );\n } else {\n const exitUuid = generateUUID();\n categories.push({\n uuid: generateUUID(),\n name: catName,\n exit_uuid: exitUuid\n });\n exits.push({ uuid: exitUuid, destination_uuid: null });\n }\n }\n\n // Build cases for non-default categories\n for (const dialCase of DIAL_CASES) {\n const category = categories.find((c) => c.name === dialCase.categoryName);\n if (!category) continue;\n\n const existingCase = existingCases.find(\n (c: any) =>\n c.type === dialCase.type && c.arguments?.[0] === dialCase.arguments[0]\n );\n\n cases.push({\n uuid: existingCase?.uuid || generateUUID(),\n type: dialCase.type,\n arguments: dialCase.arguments,\n category_uuid: category.uuid\n });\n }\n\n const failedCategory = categories.find((c) => c.name === 'Failed');\n\n // Build wait config\n const phone = (formData.phone || '').trim();\n const dialLimit = parseInt(formData.dial_limit_seconds, 10);\n const callLimit = parseInt(formData.call_limit_seconds, 10);\n\n const waitConfig: any = {\n type: 'dial',\n phone\n };\n\n if (!isNaN(dialLimit) && dialLimit > 0) {\n waitConfig.dial_limit_seconds = dialLimit;\n }\n\n if (!isNaN(callLimit) && callLimit > 0) {\n waitConfig.call_limit_seconds = callLimit;\n }\n\n const router: any = {\n type: 'switch',\n operand: '@(default(resume.dial.status, \"\"))',\n default_category_uuid: failedCategory?.uuid,\n cases,\n categories,\n wait: waitConfig\n };\n\n if (formData.result_name && formData.result_name.trim() !== '') {\n router.result_name = formData.result_name.trim();\n }\n\n return {\n ...originalNode,\n router,\n exits\n };\n },\n localizable: 'categories',\n toLocalizationFormData: categoriesToLocalizationFormData,\n fromLocalizationFormData: localizationFormDataToCategories\n};\n"]}
@@ -1,8 +1,68 @@
1
1
  import { SPLIT_GROUPS, FlowTypes } from '../types';
2
+ import { createRulesRouter } from '../../utils';
3
+ import { getDigitOperators, operatorsToSelectOptions, getOperatorConfig } from '../operators';
4
+ import { resultNameField, categoriesToLocalizationFormData, localizationFormDataToCategories } from './shared';
5
+ import { createRulesArrayConfig, extractUserRules, casesToFormRules } from './shared-rules';
2
6
  export const wait_for_digits = {
3
7
  type: 'wait_for_digits',
4
8
  name: 'Wait for Digits',
5
9
  group: SPLIT_GROUPS.wait,
6
- flowTypes: [FlowTypes.VOICE]
10
+ flowTypes: [FlowTypes.VOICE],
11
+ dialogSize: 'large',
12
+ form: {
13
+ rules: createRulesArrayConfig(operatorsToSelectOptions(getDigitOperators())),
14
+ result_name: resultNameField
15
+ },
16
+ layout: [
17
+ {
18
+ type: 'text',
19
+ text: 'Rules match against all digits pressed by the caller followed by the # sign.'
20
+ },
21
+ 'rules',
22
+ 'result_name'
23
+ ],
24
+ validate: (_formData) => {
25
+ return {
26
+ valid: true,
27
+ errors: {}
28
+ };
29
+ },
30
+ toFormData: (node) => {
31
+ var _a;
32
+ const rules = casesToFormRules(node);
33
+ return {
34
+ uuid: node.uuid,
35
+ rules,
36
+ result_name: ((_a = node.router) === null || _a === void 0 ? void 0 : _a.result_name) || ''
37
+ };
38
+ },
39
+ fromFormData: (formData, originalNode) => {
40
+ var _a, _b;
41
+ const userRules = extractUserRules(formData);
42
+ const existingCategories = ((_a = originalNode.router) === null || _a === void 0 ? void 0 : _a.categories) || [];
43
+ const existingExits = originalNode.exits || [];
44
+ const existingCases = ((_b = originalNode.router) === null || _b === void 0 ? void 0 : _b.cases) || [];
45
+ const { router, exits } = createRulesRouter('@input.text', userRules, getOperatorConfig, existingCategories, existingExits, existingCases);
46
+ const finalRouter = {
47
+ ...router,
48
+ wait: {
49
+ type: 'msg',
50
+ hint: {
51
+ type: 'digits'
52
+ }
53
+ }
54
+ };
55
+ if (formData.result_name && formData.result_name.trim() !== '') {
56
+ finalRouter.result_name = formData.result_name.trim();
57
+ }
58
+ return {
59
+ ...originalNode,
60
+ router: finalRouter,
61
+ exits
62
+ };
63
+ },
64
+ localizable: 'categories',
65
+ toLocalizationFormData: categoriesToLocalizationFormData,
66
+ fromLocalizationFormData: localizationFormDataToCategories
7
67
  };
8
68
  //# sourceMappingURL=wait_for_digits.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wait_for_digits.js","sourceRoot":"","sources":["../../../../src/flow/nodes/wait_for_digits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/D,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,IAAI,EAAE,iBAAiB;IACvB,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,YAAY,CAAC,IAAI;IACxB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;CAC7B,CAAC","sourcesContent":["import { SPLIT_GROUPS, NodeConfig, FlowTypes } from '../types';\n\nexport const wait_for_digits: NodeConfig = {\n type: 'wait_for_digits',\n name: 'Wait for Digits',\n group: SPLIT_GROUPS.wait,\n flowTypes: [FlowTypes.VOICE]\n};\n"]}
1
+ {"version":3,"file":"wait_for_digits.js","sourceRoot":"","sources":["../../../../src/flow/nodes/wait_for_digits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAwB,SAAS,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,eAAe,EACf,gCAAgC,EAChC,gCAAgC,EACjC,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAExB,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,IAAI,EAAE,iBAAiB;IACvB,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,YAAY,CAAC,IAAI;IACxB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;IAC5B,UAAU,EAAE,OAAO;IACnB,IAAI,EAAE;QACJ,KAAK,EAAE,sBAAsB,CAC3B,wBAAwB,CAAC,iBAAiB,EAAE,CAAC,CAC9C;QACD,WAAW,EAAE,eAAe;KAC7B;IACD,MAAM,EAAE;QACN;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,8EAA8E;SACrF;QACD,OAAO;QACP,aAAa;KACd;IACD,QAAQ,EAAE,CAAC,SAAmB,EAAE,EAAE;QAChC,OAAO;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,EAAE;SACX,CAAC;IACJ,CAAC;IACD,UAAU,EAAE,CAAC,IAAU,EAAE,EAAE;;QACzB,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK;YACL,WAAW,EAAE,CAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,WAAW,KAAI,EAAE;SAC5C,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,QAAkB,EAAE,YAAkB,EAAQ,EAAE;;QAC7D,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,kBAAkB,GAAG,CAAA,MAAA,YAAY,CAAC,MAAM,0CAAE,UAAU,KAAI,EAAE,CAAC;QACjE,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/C,MAAM,aAAa,GAAG,CAAA,MAAA,YAAY,CAAC,MAAM,0CAAE,KAAK,KAAI,EAAE,CAAC;QAEvD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,iBAAiB,CACzC,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,aAAa,CACd,CAAC;QAEF,MAAM,WAAW,GAAQ;YACvB,GAAG,MAAM;YACT,IAAI,EAAE;gBACJ,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;iBACf;aACF;SACF,CAAC;QAEF,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/D,WAAW,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACxD,CAAC;QAED,OAAO;YACL,GAAG,YAAY;YACf,MAAM,EAAE,WAAW;YACnB,KAAK;SACN,CAAC;IACJ,CAAC;IACD,WAAW,EAAE,YAAY;IACzB,sBAAsB,EAAE,gCAAgC;IACxD,wBAAwB,EAAE,gCAAgC;CAC3D,CAAC","sourcesContent":["import { SPLIT_GROUPS, FormData, NodeConfig, FlowTypes } from '../types';\nimport { Node } from '../../store/flow-definition';\nimport { createRulesRouter } from '../../utils';\nimport {\n getDigitOperators,\n operatorsToSelectOptions,\n getOperatorConfig\n} from '../operators';\nimport {\n resultNameField,\n categoriesToLocalizationFormData,\n localizationFormDataToCategories\n} from './shared';\nimport {\n createRulesArrayConfig,\n extractUserRules,\n casesToFormRules\n} from './shared-rules';\n\nexport const wait_for_digits: NodeConfig = {\n type: 'wait_for_digits',\n name: 'Wait for Digits',\n group: SPLIT_GROUPS.wait,\n flowTypes: [FlowTypes.VOICE],\n dialogSize: 'large',\n form: {\n rules: createRulesArrayConfig(\n operatorsToSelectOptions(getDigitOperators())\n ),\n result_name: resultNameField\n },\n layout: [\n {\n type: 'text',\n text: 'Rules match against all digits pressed by the caller followed by the # sign.'\n },\n 'rules',\n 'result_name'\n ],\n validate: (_formData: FormData) => {\n return {\n valid: true,\n errors: {}\n };\n },\n toFormData: (node: Node) => {\n const rules = casesToFormRules(node);\n return {\n uuid: node.uuid,\n rules,\n result_name: node.router?.result_name || ''\n };\n },\n fromFormData: (formData: FormData, originalNode: Node): Node => {\n const userRules = extractUserRules(formData);\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n const { router, exits } = createRulesRouter(\n '@input.text',\n userRules,\n getOperatorConfig,\n existingCategories,\n existingExits,\n existingCases\n );\n\n const finalRouter: any = {\n ...router,\n wait: {\n type: 'msg',\n hint: {\n type: 'digits'\n }\n }\n };\n\n if (formData.result_name && formData.result_name.trim() !== '') {\n finalRouter.result_name = formData.result_name.trim();\n }\n\n return {\n ...originalNode,\n router: finalRouter,\n exits\n };\n },\n localizable: 'categories',\n toLocalizationFormData: categoriesToLocalizationFormData,\n fromLocalizationFormData: localizationFormDataToCategories\n};\n"]}
@@ -1,8 +1,179 @@
1
1
  import { SPLIT_GROUPS, FlowTypes } from '../types';
2
+ import { generateUUID } from '../../utils';
3
+ import { resultNameField, categoriesToLocalizationFormData, localizationFormDataToCategories } from './shared';
4
+ // Menu digits in display order: 1-9 then 0
5
+ const MENU_DIGITS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
6
+ function digitFieldKey(digit) {
7
+ return `digit_${digit}`;
8
+ }
2
9
  export const wait_for_menu = {
3
10
  type: 'wait_for_menu',
4
- name: 'Wait for Menu Selection',
11
+ name: 'Wait for Menu',
5
12
  group: SPLIT_GROUPS.wait,
6
- flowTypes: [FlowTypes.VOICE]
13
+ flowTypes: [FlowTypes.VOICE],
14
+ form: {
15
+ ...Object.fromEntries(MENU_DIGITS.map((digit) => [
16
+ digitFieldKey(digit),
17
+ {
18
+ type: 'text',
19
+ required: false,
20
+ placeholder: '',
21
+ flavor: 'xsmall'
22
+ }
23
+ ])),
24
+ result_name: resultNameField
25
+ },
26
+ layout: [
27
+ {
28
+ type: 'row',
29
+ items: ['digit_1', 'digit_2', 'digit_3'],
30
+ gap: '2rem',
31
+ marginBottom: '0.5rem',
32
+ inlineLabels: { digit_1: '1', digit_2: '2', digit_3: '3' }
33
+ },
34
+ {
35
+ type: 'row',
36
+ items: ['digit_4', 'digit_5', 'digit_6'],
37
+ gap: '2rem',
38
+ marginBottom: '0.5rem',
39
+ inlineLabels: { digit_4: '4', digit_5: '5', digit_6: '6' }
40
+ },
41
+ {
42
+ type: 'row',
43
+ items: ['digit_7', 'digit_8', 'digit_9'],
44
+ gap: '2rem',
45
+ marginBottom: '0.5rem',
46
+ inlineLabels: { digit_7: '7', digit_8: '8', digit_9: '9' }
47
+ },
48
+ {
49
+ type: 'row',
50
+ items: [
51
+ { type: 'spacer' },
52
+ 'digit_0',
53
+ { type: 'spacer' }
54
+ ],
55
+ gap: '2rem',
56
+ inlineLabels: { digit_0: '0' }
57
+ },
58
+ 'result_name'
59
+ ],
60
+ toFormData: (node) => {
61
+ var _a, _b, _c, _d;
62
+ const formData = {
63
+ uuid: node.uuid,
64
+ result_name: ((_a = node.router) === null || _a === void 0 ? void 0 : _a.result_name) || ''
65
+ };
66
+ // Initialize all digit fields as empty
67
+ for (const digit of MENU_DIGITS) {
68
+ formData[digitFieldKey(digit)] = '';
69
+ }
70
+ // Fill in category names from cases
71
+ if (((_b = node.router) === null || _b === void 0 ? void 0 : _b.cases) && ((_c = node.router) === null || _c === void 0 ? void 0 : _c.categories)) {
72
+ for (const case_ of node.router.cases) {
73
+ if (case_.type === 'has_number_eq' && ((_d = case_.arguments) === null || _d === void 0 ? void 0 : _d[0])) {
74
+ const digit = case_.arguments[0];
75
+ const category = node.router.categories.find((cat) => cat.uuid === case_.category_uuid);
76
+ if (category && MENU_DIGITS.includes(digit)) {
77
+ formData[digitFieldKey(digit)] = category.name;
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return formData;
83
+ },
84
+ fromFormData: (formData, originalNode) => {
85
+ var _a, _b;
86
+ const existingCategories = ((_a = originalNode.router) === null || _a === void 0 ? void 0 : _a.categories) || [];
87
+ const existingExits = originalNode.exits || [];
88
+ const existingCases = ((_b = originalNode.router) === null || _b === void 0 ? void 0 : _b.cases) || [];
89
+ const categories = [];
90
+ const exits = [];
91
+ const cases = [];
92
+ // Build categories and cases for each filled digit
93
+ for (const digit of MENU_DIGITS) {
94
+ const categoryName = (formData[digitFieldKey(digit)] || '').trim();
95
+ if (!categoryName)
96
+ continue;
97
+ // Check if a category with this name already exists in our new list
98
+ let category = categories.find((c) => c.name === categoryName);
99
+ if (!category) {
100
+ // Try to find existing category with same name to preserve UUIDs
101
+ const existingCat = existingCategories.find((c) => c.name === categoryName);
102
+ if (existingCat) {
103
+ category = existingCat;
104
+ const existingExit = existingExits.find((e) => e.uuid === existingCat.exit_uuid);
105
+ categories.push(category);
106
+ exits.push(existingExit || {
107
+ uuid: existingCat.exit_uuid,
108
+ destination_uuid: null
109
+ });
110
+ }
111
+ else {
112
+ const exitUuid = generateUUID();
113
+ category = {
114
+ uuid: generateUUID(),
115
+ name: categoryName,
116
+ exit_uuid: exitUuid
117
+ };
118
+ categories.push(category);
119
+ exits.push({ uuid: exitUuid, destination_uuid: null });
120
+ }
121
+ }
122
+ // Find existing case for this digit to preserve UUID
123
+ const existingCase = existingCases.find((c) => { var _a; return c.type === 'has_number_eq' && ((_a = c.arguments) === null || _a === void 0 ? void 0 : _a[0]) === digit; });
124
+ cases.push({
125
+ uuid: (existingCase === null || existingCase === void 0 ? void 0 : existingCase.uuid) || generateUUID(),
126
+ type: 'has_number_eq',
127
+ arguments: [digit],
128
+ category_uuid: category.uuid
129
+ });
130
+ }
131
+ // Add "Other" default category
132
+ const existingOther = existingCategories.find((c) => c.name === 'Other');
133
+ let otherCategory;
134
+ if (existingOther) {
135
+ otherCategory = existingOther;
136
+ const existingExit = existingExits.find((e) => e.uuid === existingOther.exit_uuid);
137
+ exits.push(existingExit || {
138
+ uuid: existingOther.exit_uuid,
139
+ destination_uuid: null
140
+ });
141
+ }
142
+ else {
143
+ const exitUuid = generateUUID();
144
+ otherCategory = {
145
+ uuid: generateUUID(),
146
+ name: 'Other',
147
+ exit_uuid: exitUuid
148
+ };
149
+ exits.push({ uuid: exitUuid, destination_uuid: null });
150
+ }
151
+ categories.push(otherCategory);
152
+ const router = {
153
+ type: 'switch',
154
+ operand: '@input.text',
155
+ default_category_uuid: otherCategory.uuid,
156
+ cases,
157
+ categories,
158
+ wait: {
159
+ type: 'msg',
160
+ hint: {
161
+ type: 'digits',
162
+ count: 1
163
+ }
164
+ }
165
+ };
166
+ if (formData.result_name && formData.result_name.trim() !== '') {
167
+ router.result_name = formData.result_name.trim();
168
+ }
169
+ return {
170
+ ...originalNode,
171
+ router,
172
+ exits
173
+ };
174
+ },
175
+ localizable: 'categories',
176
+ toLocalizationFormData: categoriesToLocalizationFormData,
177
+ fromLocalizationFormData: localizationFormDataToCategories
7
178
  };
8
179
  //# sourceMappingURL=wait_for_menu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wait_for_menu.js","sourceRoot":"","sources":["../../../../src/flow/nodes/wait_for_menu.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/D,MAAM,CAAC,MAAM,aAAa,GAAe;IACvC,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,yBAAyB;IAC/B,KAAK,EAAE,YAAY,CAAC,IAAI;IACxB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;CAC7B,CAAC","sourcesContent":["import { SPLIT_GROUPS, NodeConfig, FlowTypes } from '../types';\n\nexport const wait_for_menu: NodeConfig = {\n type: 'wait_for_menu',\n name: 'Wait for Menu Selection',\n group: SPLIT_GROUPS.wait,\n flowTypes: [FlowTypes.VOICE]\n};\n"]}
1
+ {"version":3,"file":"wait_for_menu.js","sourceRoot":"","sources":["../../../../src/flow/nodes/wait_for_menu.ts"],"names":[],"mappings":"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","sourcesContent":["import { SPLIT_GROUPS, FormData, NodeConfig, FlowTypes } from '../types';\nimport { Node, Category, Exit } from '../../store/flow-definition';\nimport { generateUUID } from '../../utils';\nimport {\n resultNameField,\n categoriesToLocalizationFormData,\n localizationFormDataToCategories\n} from './shared';\n\n// Menu digits in display order: 1-9 then 0\nconst MENU_DIGITS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];\n\nfunction digitFieldKey(digit: string): string {\n return `digit_${digit}`;\n}\n\nexport const wait_for_menu: NodeConfig = {\n type: 'wait_for_menu',\n name: 'Wait for Menu',\n group: SPLIT_GROUPS.wait,\n flowTypes: [FlowTypes.VOICE],\n form: {\n ...Object.fromEntries(\n MENU_DIGITS.map((digit) => [\n digitFieldKey(digit),\n {\n type: 'text' as const,\n required: false,\n placeholder: '',\n flavor: 'xsmall' as const\n }\n ])\n ),\n result_name: resultNameField\n },\n layout: [\n {\n type: 'row' as const,\n items: ['digit_1', 'digit_2', 'digit_3'],\n gap: '2rem',\n marginBottom: '0.5rem',\n inlineLabels: { digit_1: '1', digit_2: '2', digit_3: '3' }\n },\n {\n type: 'row' as const,\n items: ['digit_4', 'digit_5', 'digit_6'],\n gap: '2rem',\n marginBottom: '0.5rem',\n inlineLabels: { digit_4: '4', digit_5: '5', digit_6: '6' }\n },\n {\n type: 'row' as const,\n items: ['digit_7', 'digit_8', 'digit_9'],\n gap: '2rem',\n marginBottom: '0.5rem',\n inlineLabels: { digit_7: '7', digit_8: '8', digit_9: '9' }\n },\n {\n type: 'row' as const,\n items: [\n { type: 'spacer' as const },\n 'digit_0',\n { type: 'spacer' as const }\n ],\n gap: '2rem',\n inlineLabels: { digit_0: '0' }\n },\n 'result_name'\n ],\n toFormData: (node: Node) => {\n const formData: FormData = {\n uuid: node.uuid,\n result_name: node.router?.result_name || ''\n };\n\n // Initialize all digit fields as empty\n for (const digit of MENU_DIGITS) {\n formData[digitFieldKey(digit)] = '';\n }\n\n // Fill in category names from cases\n if (node.router?.cases && node.router?.categories) {\n for (const case_ of node.router.cases) {\n if (case_.type === 'has_number_eq' && case_.arguments?.[0]) {\n const digit = case_.arguments[0];\n const category = node.router.categories.find(\n (cat: Category) => cat.uuid === case_.category_uuid\n );\n if (category && MENU_DIGITS.includes(digit)) {\n formData[digitFieldKey(digit)] = category.name;\n }\n }\n }\n }\n\n return formData;\n },\n fromFormData: (formData: FormData, originalNode: Node): Node => {\n const existingCategories = originalNode.router?.categories || [];\n const existingExits = originalNode.exits || [];\n const existingCases = originalNode.router?.cases || [];\n\n const categories: Category[] = [];\n const exits: Exit[] = [];\n const cases: any[] = [];\n\n // Build categories and cases for each filled digit\n for (const digit of MENU_DIGITS) {\n const categoryName = (formData[digitFieldKey(digit)] || '').trim();\n if (!categoryName) continue;\n\n // Check if a category with this name already exists in our new list\n let category = categories.find((c) => c.name === categoryName);\n\n if (!category) {\n // Try to find existing category with same name to preserve UUIDs\n const existingCat = existingCategories.find(\n (c: Category) => c.name === categoryName\n );\n\n if (existingCat) {\n category = existingCat;\n const existingExit = existingExits.find(\n (e: Exit) => e.uuid === existingCat.exit_uuid\n );\n categories.push(category);\n exits.push(\n existingExit || {\n uuid: existingCat.exit_uuid,\n destination_uuid: null\n }\n );\n } else {\n const exitUuid = generateUUID();\n category = {\n uuid: generateUUID(),\n name: categoryName,\n exit_uuid: exitUuid\n };\n categories.push(category);\n exits.push({ uuid: exitUuid, destination_uuid: null });\n }\n }\n\n // Find existing case for this digit to preserve UUID\n const existingCase = existingCases.find(\n (c: any) => c.type === 'has_number_eq' && c.arguments?.[0] === digit\n );\n\n cases.push({\n uuid: existingCase?.uuid || generateUUID(),\n type: 'has_number_eq',\n arguments: [digit],\n category_uuid: category.uuid\n });\n }\n\n // Add \"Other\" default category\n const existingOther = existingCategories.find(\n (c: Category) => c.name === 'Other'\n );\n\n let otherCategory: Category;\n if (existingOther) {\n otherCategory = existingOther;\n const existingExit = existingExits.find(\n (e: Exit) => e.uuid === existingOther.exit_uuid\n );\n exits.push(\n existingExit || {\n uuid: existingOther.exit_uuid,\n destination_uuid: null\n }\n );\n } else {\n const exitUuid = generateUUID();\n otherCategory = {\n uuid: generateUUID(),\n name: 'Other',\n exit_uuid: exitUuid\n };\n exits.push({ uuid: exitUuid, destination_uuid: null });\n }\n categories.push(otherCategory);\n\n const router: any = {\n type: 'switch',\n operand: '@input.text',\n default_category_uuid: otherCategory.uuid,\n cases,\n categories,\n wait: {\n type: 'msg',\n hint: {\n type: 'digits',\n count: 1\n }\n }\n };\n\n if (formData.result_name && formData.result_name.trim() !== '') {\n router.result_name = formData.result_name.trim();\n }\n\n return {\n ...originalNode,\n router,\n exits\n };\n },\n localizable: 'categories',\n toLocalizationFormData: categoriesToLocalizationFormData,\n fromLocalizationFormData: localizationFormDataToCategories\n};\n"]}
@@ -34,11 +34,6 @@ export const OPERATORS = [
34
34
  operands: 0,
35
35
  categoryName: 'Has Text'
36
36
  },
37
- {
38
- type: 'has_pattern',
39
- name: 'matches regex',
40
- operands: 1
41
- },
42
37
  // Number operators
43
38
  {
44
39
  type: 'has_number',
@@ -165,6 +160,11 @@ export const OPERATORS = [
165
160
  operands: 0,
166
161
  categoryName: 'Not Empty',
167
162
  visibility: 'hidden'
163
+ },
164
+ {
165
+ type: 'has_pattern',
166
+ name: 'matches regex',
167
+ operands: 1
168
168
  }
169
169
  ];
170
170
  // Get operators suitable for wait_for_response rules
@@ -172,6 +172,22 @@ export const getWaitForResponseOperators = () => {
172
172
  return OPERATORS.filter((op) => op.visibility !== 'hidden' && !op.filter // For now, exclude location operators unless we support feature detection
173
173
  );
174
174
  };
175
+ // Number operator types used for digit-based routing
176
+ const DIGIT_OPERATOR_TYPES = new Set([
177
+ 'has_beginning',
178
+ 'has_number',
179
+ 'has_number_between',
180
+ 'has_number_lt',
181
+ 'has_number_lte',
182
+ 'has_number_eq',
183
+ 'has_number_gte',
184
+ 'has_number_gt',
185
+ 'has_pattern'
186
+ ]);
187
+ // Get operators suitable for wait_for_digits rules (number operators + regex)
188
+ export const getDigitOperators = () => {
189
+ return OPERATORS.filter((op) => DIGIT_OPERATOR_TYPES.has(op.type));
190
+ };
175
191
  // Get operator configuration by type
176
192
  export const getOperatorConfig = (type) => {
177
193
  return OPERATORS.find((op) => op.type === type);
@@ -1 +1 @@
1
- {"version":3,"file":"operators.js","sourceRoot":"","sources":["../../../src/flow/operators.ts"],"names":[],"mappings":"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","sourcesContent":["// Flow router operator configurations\n// These define the available operators for rule-based routing in flow nodes\n\nexport interface OperatorConfig {\n type: string;\n name: string;\n operands: number; // Number of operands required (0 = no input needed, 1 = single value, 2 = two values)\n categoryName?: string; // Default category name when operands is 0\n visibility?: 'hidden' | 'visible'; // Whether to show in UI\n filter?: string; // Feature filter requirement\n}\n\n// All available operators for flow routing\nexport const OPERATORS: OperatorConfig[] = [\n // Text operators\n {\n type: 'has_any_word',\n name: 'has any of the words',\n operands: 1\n },\n {\n type: 'has_all_words',\n name: 'has all of the words',\n operands: 1\n },\n {\n type: 'has_phrase',\n name: 'has the phrase',\n operands: 1\n },\n {\n type: 'has_only_phrase',\n name: 'has only the phrase',\n operands: 1\n },\n {\n type: 'has_beginning',\n name: 'starts with',\n operands: 1\n },\n {\n type: 'has_text',\n name: 'has some text',\n operands: 0,\n categoryName: 'Has Text'\n },\n {\n type: 'has_pattern',\n name: 'matches regex',\n operands: 1\n },\n\n // Number operators\n {\n type: 'has_number',\n name: 'has a number',\n operands: 0,\n categoryName: 'Has Number'\n },\n {\n type: 'has_number_between',\n name: 'has a number between',\n operands: 2\n },\n {\n type: 'has_number_lt',\n name: 'has a number below',\n operands: 1\n },\n {\n type: 'has_number_lte',\n name: 'has a number at or below',\n operands: 1\n },\n {\n type: 'has_number_eq',\n name: 'has a number equal to',\n operands: 1\n },\n {\n type: 'has_number_gte',\n name: 'has a number at or above',\n operands: 1\n },\n {\n type: 'has_number_gt',\n name: 'has a number above',\n operands: 1\n },\n\n // Date operators\n {\n type: 'has_date',\n name: 'has a date',\n operands: 0,\n categoryName: 'Has Date'\n },\n {\n type: 'has_date_lt',\n name: 'has a date before',\n operands: 1\n },\n {\n type: 'has_date_eq',\n name: 'has a date equal to',\n operands: 1\n },\n {\n type: 'has_date_gt',\n name: 'has a date after',\n operands: 1\n },\n {\n type: 'has_time',\n name: 'has a time',\n operands: 0,\n categoryName: 'Has Time'\n },\n\n // Contact data operators\n {\n type: 'has_phone',\n name: 'has a phone number',\n operands: 0,\n categoryName: 'Has Phone'\n },\n {\n type: 'has_email',\n name: 'has an email',\n operands: 0,\n categoryName: 'Has Email'\n },\n\n // Location operators (require location feature)\n {\n type: 'has_state',\n name: 'has state',\n operands: 0,\n categoryName: 'Has State',\n filter: 'HAS_LOCATIONS'\n },\n {\n type: 'has_district',\n name: 'has district',\n operands: 1,\n categoryName: 'Has District',\n filter: 'HAS_LOCATIONS'\n },\n {\n type: 'has_ward',\n name: 'has ward',\n operands: 2,\n categoryName: 'Has Ward',\n filter: 'HAS_LOCATIONS'\n },\n\n // Hidden/system operators\n {\n type: 'has_group',\n name: 'is in the group',\n operands: 1,\n visibility: 'hidden'\n },\n {\n type: 'has_category',\n name: 'has the category',\n operands: 0,\n visibility: 'hidden'\n },\n {\n type: 'has_error',\n name: 'has an error',\n operands: 0,\n categoryName: 'Has Error',\n visibility: 'hidden'\n },\n {\n type: 'has_value',\n name: 'is not empty',\n operands: 0,\n categoryName: 'Not Empty',\n visibility: 'hidden'\n }\n];\n\n// Get operators suitable for wait_for_response rules\nexport const getWaitForResponseOperators = (): OperatorConfig[] => {\n return OPERATORS.filter(\n (op) => op.visibility !== 'hidden' && !op.filter // For now, exclude location operators unless we support feature detection\n );\n};\n\n// Get operator configuration by type\nexport const getOperatorConfig = (type: string): OperatorConfig | undefined => {\n return OPERATORS.find((op) => op.type === type);\n};\n\n// Convert operators to select options\nexport const operatorsToSelectOptions = (operators: OperatorConfig[]) => {\n return operators.map((op) => ({\n value: op.type,\n name: op.name\n }));\n};\n\n// Create an operator object for select components\nexport const createOperatorOption = (\n type: string\n): { value: string; name: string } => {\n const config = getOperatorConfig(type);\n return {\n value: type,\n name: config ? config.name : type\n };\n};\n"]}
1
+ {"version":3,"file":"operators.js","sourceRoot":"","sources":["../../../src/flow/operators.ts"],"names":[],"mappings":"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","sourcesContent":["// Flow router operator configurations\n// These define the available operators for rule-based routing in flow nodes\n\nexport interface OperatorConfig {\n type: string;\n name: string;\n operands: number; // Number of operands required (0 = no input needed, 1 = single value, 2 = two values)\n categoryName?: string; // Default category name when operands is 0\n visibility?: 'hidden' | 'visible'; // Whether to show in UI\n filter?: string; // Feature filter requirement\n}\n\n// All available operators for flow routing\nexport const OPERATORS: OperatorConfig[] = [\n // Text operators\n {\n type: 'has_any_word',\n name: 'has any of the words',\n operands: 1\n },\n {\n type: 'has_all_words',\n name: 'has all of the words',\n operands: 1\n },\n {\n type: 'has_phrase',\n name: 'has the phrase',\n operands: 1\n },\n {\n type: 'has_only_phrase',\n name: 'has only the phrase',\n operands: 1\n },\n {\n type: 'has_beginning',\n name: 'starts with',\n operands: 1\n },\n {\n type: 'has_text',\n name: 'has some text',\n operands: 0,\n categoryName: 'Has Text'\n },\n\n // Number operators\n {\n type: 'has_number',\n name: 'has a number',\n operands: 0,\n categoryName: 'Has Number'\n },\n {\n type: 'has_number_between',\n name: 'has a number between',\n operands: 2\n },\n {\n type: 'has_number_lt',\n name: 'has a number below',\n operands: 1\n },\n {\n type: 'has_number_lte',\n name: 'has a number at or below',\n operands: 1\n },\n {\n type: 'has_number_eq',\n name: 'has a number equal to',\n operands: 1\n },\n {\n type: 'has_number_gte',\n name: 'has a number at or above',\n operands: 1\n },\n {\n type: 'has_number_gt',\n name: 'has a number above',\n operands: 1\n },\n\n // Date operators\n {\n type: 'has_date',\n name: 'has a date',\n operands: 0,\n categoryName: 'Has Date'\n },\n {\n type: 'has_date_lt',\n name: 'has a date before',\n operands: 1\n },\n {\n type: 'has_date_eq',\n name: 'has a date equal to',\n operands: 1\n },\n {\n type: 'has_date_gt',\n name: 'has a date after',\n operands: 1\n },\n {\n type: 'has_time',\n name: 'has a time',\n operands: 0,\n categoryName: 'Has Time'\n },\n\n // Contact data operators\n {\n type: 'has_phone',\n name: 'has a phone number',\n operands: 0,\n categoryName: 'Has Phone'\n },\n {\n type: 'has_email',\n name: 'has an email',\n operands: 0,\n categoryName: 'Has Email'\n },\n\n // Location operators (require location feature)\n {\n type: 'has_state',\n name: 'has state',\n operands: 0,\n categoryName: 'Has State',\n filter: 'HAS_LOCATIONS'\n },\n {\n type: 'has_district',\n name: 'has district',\n operands: 1,\n categoryName: 'Has District',\n filter: 'HAS_LOCATIONS'\n },\n {\n type: 'has_ward',\n name: 'has ward',\n operands: 2,\n categoryName: 'Has Ward',\n filter: 'HAS_LOCATIONS'\n },\n\n // Hidden/system operators\n {\n type: 'has_group',\n name: 'is in the group',\n operands: 1,\n visibility: 'hidden'\n },\n {\n type: 'has_category',\n name: 'has the category',\n operands: 0,\n visibility: 'hidden'\n },\n {\n type: 'has_error',\n name: 'has an error',\n operands: 0,\n categoryName: 'Has Error',\n visibility: 'hidden'\n },\n {\n type: 'has_value',\n name: 'is not empty',\n operands: 0,\n categoryName: 'Not Empty',\n visibility: 'hidden'\n },\n {\n type: 'has_pattern',\n name: 'matches regex',\n operands: 1\n }\n];\n\n// Get operators suitable for wait_for_response rules\nexport const getWaitForResponseOperators = (): OperatorConfig[] => {\n return OPERATORS.filter(\n (op) => op.visibility !== 'hidden' && !op.filter // For now, exclude location operators unless we support feature detection\n );\n};\n\n// Number operator types used for digit-based routing\nconst DIGIT_OPERATOR_TYPES = new Set([\n 'has_beginning',\n 'has_number',\n 'has_number_between',\n 'has_number_lt',\n 'has_number_lte',\n 'has_number_eq',\n 'has_number_gte',\n 'has_number_gt',\n 'has_pattern'\n]);\n\n// Get operators suitable for wait_for_digits rules (number operators + regex)\nexport const getDigitOperators = (): OperatorConfig[] => {\n return OPERATORS.filter((op) => DIGIT_OPERATOR_TYPES.has(op.type));\n};\n\n// Get operator configuration by type\nexport const getOperatorConfig = (type: string): OperatorConfig | undefined => {\n return OPERATORS.find((op) => op.type === type);\n};\n\n// Convert operators to select options\nexport const operatorsToSelectOptions = (operators: OperatorConfig[]) => {\n return operators.map((op) => ({\n value: op.type,\n name: op.name\n }));\n};\n\n// Create an operator object for select components\nexport const createOperatorOption = (\n type: string\n): { value: string; name: string } => {\n const config = getOperatorConfig(type);\n return {\n value: type,\n name: config ? config.name : type\n };\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/flow/types.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,YAAY;CAChB,CAAC;AAIX;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,EAAE,EAAE,IAAI;IACR,OAAO,EAAE,SAAS;CACV,CAAC;AA+QX;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;CACN,CAAC;AAeX;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAuC;IACvE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE;QACpB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,mDAAmD;KACjE;IACD,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE;QACxB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,yCAAyC;KACvD;IACD,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE;QACpB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,iCAAiC;KAC/C;IACD,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE;QACxB,KAAK,EAAE,WAAW;QAClB,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,iCAAiC;KAC/C;IACD,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE;QACzB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,qDAAqD;KACnE;IACD,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;QACvB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,qCAAqC;KACnD;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAsC;IACrE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;QACnB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,2CAA2C;KACzD;IACD,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;QACpB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,OAAO;QACd,WAAW,EAAE,oCAAoC;KAClD;CACF,CAAC","sourcesContent":["import { TemplateResult } from 'lit-html';\nimport { Action, Node, NodeUI } from '../store/flow-definition';\n\nexport interface ValidationResult {\n valid: boolean;\n errors: { [key: string]: string };\n}\n\n/**\n * Flow types - defines the type of flow being edited\n */\nexport const FlowTypes = {\n MESSAGE: 'message',\n VOICE: 'voice',\n BACKGROUND: 'background'\n} as const;\n\nexport type FlowType = (typeof FlowTypes)[keyof typeof FlowTypes];\n\n/**\n * Features - defines the features available in the account\n */\nexport const Features = {\n AI: 'ai',\n AIRTIME: 'airtime'\n} as const;\n\nexport type Feature = (typeof Features)[keyof typeof Features];\n\n// Component attribute interfaces - these define what's allowed for each component type\nexport interface TextInputAttributes {\n type?: 'text' | 'email' | 'number' | 'url' | 'tel';\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n submitOnEnter?: boolean;\n}\n\nexport interface CompletionAttributes {\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n expressions?: string;\n counter?: string;\n minHeight?: number;\n}\n\nexport interface SelectAttributes {\n placeholder?: string;\n multi?: boolean;\n searchable?: boolean;\n tags?: boolean;\n emails?: boolean;\n clearable?: boolean;\n endpoint?: string;\n valueKey?: string;\n nameKey?: string;\n queryParam?: string;\n maxItems?: number;\n maxItemsText?: string;\n expressions?: string;\n options?: Array<{ name: string; value: any }>;\n sorted?: boolean;\n allowCreate?: boolean;\n jsonValue?: boolean;\n spaceSelect?: boolean;\n infoText?: string;\n}\n\nexport interface CheckboxAttributes {\n label?: string;\n size?: number;\n disabled?: boolean;\n animateChange?: string;\n}\n\nexport interface SliderAttributes {\n min?: number;\n max?: number;\n range?: boolean;\n}\n\nexport interface FormData extends Record<string, any> {}\n\nexport interface FormConfig {\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[];\n gutter?: LayoutItem[];\n sanitize?: (formData: FormData) => void;\n validate?: (formData: FormData) => ValidationResult;\n}\n\nexport interface NodeConfig extends FormConfig {\n type: string;\n name?: string;\n aliases?: string[]; // alternate type names for backwards compatibility (won't show in selector)\n group?: ActionGroup | SplitGroup; // Nodes can use either when showAsAction is true\n dialogSize?: 'small' | 'medium' | 'large' | 'xlarge';\n action?: ActionConfig;\n showAsAction?: boolean; // if true, show in action dialog instead of splits (default: false - nodes show in splits)\n flowTypes?: FlowType[]; // which flow types this node is available for (defaults to all if not specified)\n features?: Feature[]; // which features are required for this node (all must be present)\n router?: {\n type: 'switch' | 'random';\n defaultCategory?: string;\n operand?: string;\n configurable?: boolean; // can the rules be configured in the UI\n rules?: {\n type:\n | 'has_number_between'\n | 'has_string'\n | 'has_value'\n | 'has_not_value'\n | 'has_text';\n arguments: string[];\n categoryName: string;\n }[];\n };\n\n toFormData?: (node: Node, nodeUI?: any) => FormData;\n fromFormData?: (formData: FormData, originalNode: Node) => Node;\n toUIConfig?: (formData: FormData) => Record<string, any>;\n render?: (node: Node, nodeUI?: any) => TemplateResult;\n renderTitle?: (node: Node, nodeUI?: NodeUI) => TemplateResult;\n\n // Localization support for router categories\n localizable?: 'categories'; // Only categories are localizable for routers\n toLocalizationFormData?: (\n node: Node,\n localization: Record<string, any>\n ) => FormData;\n fromLocalizationFormData?: (\n formData: FormData,\n node: Node\n ) => Record<string, any>;\n}\n\n// New field configuration system for generic form generation\nexport interface BaseFieldConfig {\n label?: string | ((formData: Record<string, any>) => string);\n required?: boolean;\n evaluated?: boolean;\n dependsOn?: string[];\n computeValue?: (\n values: Record<string, any>,\n currentValue: any,\n originalValues?: Record<string, any>\n ) => any;\n\n // Validation properties\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n helpText?: string;\n\n // Layout properties\n maxWidth?: string;\n width?: string;\n\n // Conditional rendering\n conditions?: {\n visible?: (formData: Record<string, any>) => boolean;\n disabled?: (formData: Record<string, any>) => boolean;\n };\n\n // Optional field with reveal link\n // When set, the field is hidden by default and a link with this text is shown\n // Clicking the link reveals the field permanently (can't be hidden again)\n optionalLink?: string;\n}\n\nexport interface TextFieldConfig extends BaseFieldConfig {\n type: 'text';\n placeholder?: string;\n flavor?: 'xsmall' | 'small' | 'large';\n}\n\nexport interface TextareaFieldConfig extends BaseFieldConfig {\n type: 'textarea';\n placeholder?: string;\n rows?: number;\n minHeight?: number;\n}\n\nexport interface SelectFieldConfig extends BaseFieldConfig {\n type: 'select';\n options?: string[] | { value: string; name: string }[];\n multi?: boolean;\n clearable?: boolean;\n searchable?: boolean;\n tags?: boolean;\n placeholder?: string;\n maxItems?: number;\n valueKey?: string;\n nameKey?: string;\n endpoint?: string;\n emails?: boolean;\n getName?: (item: any) => string;\n flavor?: 'xsmall' | 'small' | 'large';\n createArbitraryOption?: (input: string, options: any[]) => any;\n allowCreate?: boolean;\n getDynamicOptions?: () => Array<{ value: string; name: string }>;\n}\n\nexport interface KeyValueFieldConfig extends BaseFieldConfig {\n type: 'key-value';\n sortable?: boolean;\n keyPlaceholder?: string;\n valuePlaceholder?: string;\n minRows?: number;\n}\n\nexport interface ArrayFieldConfig extends BaseFieldConfig {\n type: 'array';\n itemConfig: Record<string, FieldConfig>;\n sortable?: boolean;\n minItems?: number;\n maxItems?: number;\n itemLabel?: string;\n maintainEmptyItem?: boolean;\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n isEmptyItem?: (item: any) => boolean;\n}\n\nexport interface CheckboxFieldConfig extends BaseFieldConfig {\n type: 'checkbox';\n size?: number;\n animateChange?: string;\n labelPadding?: string;\n}\n\nexport interface MessageEditorFieldConfig extends BaseFieldConfig {\n type: 'message-editor';\n placeholder?: string;\n minHeight?: number;\n maxAttachments?: number;\n accept?: string;\n endpoint?: string;\n counter?: string;\n gsm?: boolean;\n autogrow?: boolean;\n disableCompletion?: boolean;\n}\n\nexport type FieldConfig =\n | TextFieldConfig\n | TextareaFieldConfig\n | SelectFieldConfig\n | KeyValueFieldConfig\n | ArrayFieldConfig\n | CheckboxFieldConfig\n | MessageEditorFieldConfig;\n\n// Layout configurations for better form organization\n// Recursive layout system - any layout item can contain other layout items\n\nexport interface FieldItemConfig {\n type: 'field';\n field: string; // field name to render\n}\n\nexport interface RowLayoutConfig {\n type: 'row';\n items: LayoutItem[]; // can contain fields, groups, or other rows\n gap?: string; // CSS gap value, defaults to '1rem'\n label?: string; // optional label for the entire row\n helpText?: string; // optional help text for the entire row\n}\n\nexport interface GroupLayoutConfig {\n type: 'group';\n label: string;\n items: LayoutItem[]; // can contain fields, rows, or other groups\n collapsible?: boolean;\n collapsed?: boolean | ((formData: FormData) => boolean); // initial state if collapsible - can be a function\n helpText?: string;\n getGroupValueCount?: (formData: FormData) => number; // optional function to get count for bubble display\n}\n\nexport type LayoutItem =\n | FieldItemConfig\n | RowLayoutConfig\n | GroupLayoutConfig\n | string; // string is shorthand for field\n\n/**\n * Action group constants - single source of truth for action categorization\n * Use as const for compile-time type checking\n */\nexport const ACTION_GROUPS = {\n send: 'send',\n contacts: 'contacts',\n save: 'save',\n services: 'services',\n broadcast: 'broadcast',\n trigger: 'trigger'\n} as const;\n\n/**\n * Split group constants - single source of truth for split categorization\n * Use as const for compile-time type checking\n */\nexport const SPLIT_GROUPS = {\n wait: 'wait',\n split: 'split'\n} as const;\n\n// Extract types from const objects for compile-time checking\nexport type ActionGroup = (typeof ACTION_GROUPS)[keyof typeof ACTION_GROUPS];\nexport type SplitGroup = (typeof SPLIT_GROUPS)[keyof typeof SPLIT_GROUPS];\n\n/**\n * Metadata for group display\n */\nexport interface GroupMetadata {\n color: string;\n title: string;\n description: string;\n}\n\n/**\n * Action group metadata - defines display properties for each action group\n * Order in this object determines display order in action selector (top to bottom)\n */\nexport const ACTION_GROUP_METADATA: Record<ActionGroup, GroupMetadata> = {\n [ACTION_GROUPS.send]: {\n color: '#3498db',\n title: 'Send',\n description: 'Actions that send messages or content to contacts'\n },\n [ACTION_GROUPS.contacts]: {\n color: '#01c1af',\n title: 'Contact',\n description: 'Actions that update contact information'\n },\n [ACTION_GROUPS.save]: {\n color: '#1a777c',\n title: 'Save',\n description: 'Actions that save or store data'\n },\n [ACTION_GROUPS.services]: {\n color: '#f79035ff',\n title: 'Services',\n description: 'Call external services and APIs'\n },\n [ACTION_GROUPS.broadcast]: {\n color: '#8e5ea7',\n title: 'Other People',\n description: 'Actions that apply to others instead of the contact'\n },\n [ACTION_GROUPS.trigger]: {\n color: '#df419f',\n title: 'Trigger',\n description: 'Actions that trigger other behavior'\n }\n};\n\n/**\n * Split group metadata - defines display properties for each split group\n * Order in this object determines display order in split selector (top to bottom)\n */\nexport const SPLIT_GROUP_METADATA: Record<SplitGroup, GroupMetadata> = {\n [SPLIT_GROUPS.wait]: {\n color: '#4d7dad',\n title: 'Wait',\n description: 'Wait for user and split on their response'\n },\n [SPLIT_GROUPS.split]: {\n color: '#aaaaaa',\n title: 'Split',\n description: 'Split the flow based on conditions'\n }\n};\n\nexport interface ActionConfig extends FormConfig {\n name: string;\n aliases?: string[]; // alternate type names for backwards compatibility (won't show in selector)\n group: ActionGroup;\n dialogSize?: 'small' | 'medium' | 'large' | 'xlarge';\n evaluated?: string[];\n hideFromActions?: boolean; // if true, don't show in action dialog (default: false - actions show in actions)\n flowTypes?: FlowType[]; // which flow types this action is available for (defaults to all if not specified)\n features?: Feature[]; // which features are required for this action (all must be present)\n render?: (node: any, action: any) => TemplateResult;\n\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[]; // optional layout configuration - array of layout items\n gutter?: LayoutItem[]; // fields to render in the dialog gutter (left side of buttons)\n\n toFormData?: (action: Action) => FormData;\n fromFormData?: (formData: FormData) => Action;\n\n // Localization support\n localizable?: string[]; // array of field names that can be localized\n toLocalizationFormData?: (\n action: Action,\n localization: Record<string, any>\n ) => FormData;\n fromLocalizationFormData?: (\n formData: FormData,\n action: Action\n ) => Record<string, any>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/flow/types.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,UAAU,EAAE,YAAY;CAChB,CAAC;AAIX;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,EAAE,EAAE,IAAI;IACR,OAAO,EAAE,SAAS;CACV,CAAC;AAqSX;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;CACN,CAAC;AAeX;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAuC;IACvE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE;QACpB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,mDAAmD;KACjE;IACD,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE;QACxB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,yCAAyC;KACvD;IACD,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE;QACpB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,iCAAiC;KAC/C;IACD,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE;QACxB,KAAK,EAAE,WAAW;QAClB,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,iCAAiC;KAC/C;IACD,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE;QACzB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,qDAAqD;KACnE;IACD,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE;QACvB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,qCAAqC;KACnD;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAsC;IACrE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;QACnB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,2CAA2C;KACzD;IACD,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;QACpB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,OAAO;QACd,WAAW,EAAE,oCAAoC;KAClD;CACF,CAAC","sourcesContent":["import { TemplateResult } from 'lit-html';\nimport { Action, Node, NodeUI } from '../store/flow-definition';\n\nexport interface ValidationResult {\n valid: boolean;\n errors: { [key: string]: string };\n}\n\n/**\n * Flow types - defines the type of flow being edited\n */\nexport const FlowTypes = {\n MESSAGE: 'message',\n VOICE: 'voice',\n BACKGROUND: 'background'\n} as const;\n\nexport type FlowType = (typeof FlowTypes)[keyof typeof FlowTypes];\n\n/**\n * Features - defines the features available in the account\n */\nexport const Features = {\n AI: 'ai',\n AIRTIME: 'airtime'\n} as const;\n\nexport type Feature = (typeof Features)[keyof typeof Features];\n\n// Component attribute interfaces - these define what's allowed for each component type\nexport interface TextInputAttributes {\n type?: 'text' | 'email' | 'number' | 'url' | 'tel';\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n submitOnEnter?: boolean;\n}\n\nexport interface CompletionAttributes {\n placeholder?: string;\n clearable?: boolean;\n maxlength?: number;\n gsm?: boolean;\n autogrow?: boolean;\n textarea?: boolean;\n expressions?: string;\n counter?: string;\n minHeight?: number;\n}\n\nexport interface SelectAttributes {\n placeholder?: string;\n multi?: boolean;\n searchable?: boolean;\n tags?: boolean;\n emails?: boolean;\n clearable?: boolean;\n endpoint?: string;\n valueKey?: string;\n nameKey?: string;\n queryParam?: string;\n maxItems?: number;\n maxItemsText?: string;\n expressions?: string;\n options?: Array<{ name: string; value: any }>;\n sorted?: boolean;\n allowCreate?: boolean;\n jsonValue?: boolean;\n spaceSelect?: boolean;\n infoText?: string;\n}\n\nexport interface CheckboxAttributes {\n label?: string;\n size?: number;\n disabled?: boolean;\n animateChange?: string;\n}\n\nexport interface SliderAttributes {\n min?: number;\n max?: number;\n range?: boolean;\n}\n\nexport interface FormData extends Record<string, any> {}\n\nexport interface FormConfig {\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[];\n gutter?: LayoutItem[];\n sanitize?: (formData: FormData) => void;\n validate?: (formData: FormData) => ValidationResult;\n}\n\nexport interface NodeConfig extends FormConfig {\n type: string;\n name?: string;\n aliases?: string[]; // alternate type names for backwards compatibility (won't show in selector)\n group?: ActionGroup | SplitGroup; // Nodes can use either when showAsAction is true\n dialogSize?: 'small' | 'medium' | 'large' | 'xlarge';\n action?: ActionConfig;\n showAsAction?: boolean; // if true, show in action dialog instead of splits (default: false - nodes show in splits)\n flowTypes?: FlowType[]; // which flow types this node is available for (defaults to all if not specified)\n features?: Feature[]; // which features are required for this node (all must be present)\n router?: {\n type: 'switch' | 'random';\n defaultCategory?: string;\n operand?: string;\n configurable?: boolean; // can the rules be configured in the UI\n rules?: {\n type:\n | 'has_number_between'\n | 'has_number_eq'\n | 'has_only_text'\n | 'has_string'\n | 'has_value'\n | 'has_not_value'\n | 'has_text';\n arguments: string[];\n categoryName: string;\n }[];\n };\n\n toFormData?: (node: Node, nodeUI?: any) => FormData;\n fromFormData?: (formData: FormData, originalNode: Node) => Node;\n toUIConfig?: (formData: FormData) => Record<string, any>;\n render?: (node: Node, nodeUI?: any) => TemplateResult;\n renderTitle?: (node: Node, nodeUI?: NodeUI) => TemplateResult;\n\n // Localization support for router categories\n localizable?: 'categories'; // Only categories are localizable for routers\n toLocalizationFormData?: (\n node: Node,\n localization: Record<string, any>\n ) => FormData;\n fromLocalizationFormData?: (\n formData: FormData,\n node: Node\n ) => Record<string, any>;\n}\n\n// New field configuration system for generic form generation\nexport interface BaseFieldConfig {\n label?: string | ((formData: Record<string, any>) => string);\n required?: boolean;\n evaluated?: boolean;\n dependsOn?: string[];\n computeValue?: (\n values: Record<string, any>,\n currentValue: any,\n originalValues?: Record<string, any>\n ) => any;\n\n // Validation properties\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n helpText?: string;\n\n // Layout properties\n maxWidth?: string;\n width?: string;\n\n // Conditional rendering\n conditions?: {\n visible?: (formData: Record<string, any>) => boolean;\n disabled?: (formData: Record<string, any>) => boolean;\n };\n\n // Optional field with reveal link\n // When set, the field is hidden by default and a link with this text is shown\n // Clicking the link reveals the field permanently (can't be hidden again)\n optionalLink?: string;\n}\n\nexport interface TextFieldConfig extends BaseFieldConfig {\n type: 'text';\n placeholder?: string;\n flavor?: 'xsmall' | 'small' | 'large';\n}\n\nexport interface TextareaFieldConfig extends BaseFieldConfig {\n type: 'textarea';\n placeholder?: string;\n rows?: number;\n minHeight?: number;\n}\n\nexport interface SelectFieldConfig extends BaseFieldConfig {\n type: 'select';\n options?: string[] | { value: string; name: string }[];\n multi?: boolean;\n clearable?: boolean;\n searchable?: boolean;\n tags?: boolean;\n placeholder?: string;\n maxItems?: number;\n valueKey?: string;\n nameKey?: string;\n endpoint?: string;\n emails?: boolean;\n getName?: (item: any) => string;\n flavor?: 'xsmall' | 'small' | 'large';\n createArbitraryOption?: (input: string, options: any[]) => any;\n allowCreate?: boolean;\n getDynamicOptions?: () => Array<{ value: string; name: string }>;\n}\n\nexport interface KeyValueFieldConfig extends BaseFieldConfig {\n type: 'key-value';\n sortable?: boolean;\n keyPlaceholder?: string;\n valuePlaceholder?: string;\n minRows?: number;\n}\n\nexport interface ArrayFieldConfig extends BaseFieldConfig {\n type: 'array';\n itemConfig: Record<string, FieldConfig>;\n sortable?: boolean;\n minItems?: number;\n maxItems?: number;\n itemLabel?: string;\n maintainEmptyItem?: boolean;\n onItemChange?: (\n itemIndex: number,\n field: string,\n value: any,\n allItems: any[]\n ) => any[];\n isEmptyItem?: (item: any) => boolean;\n}\n\nexport interface CheckboxFieldConfig extends BaseFieldConfig {\n type: 'checkbox';\n size?: number;\n animateChange?: string;\n labelPadding?: string;\n}\n\nexport interface MessageEditorFieldConfig extends BaseFieldConfig {\n type: 'message-editor';\n placeholder?: string;\n minHeight?: number;\n maxAttachments?: number;\n accept?: string;\n endpoint?: string;\n counter?: string;\n gsm?: boolean;\n autogrow?: boolean;\n disableCompletion?: boolean;\n}\n\nexport interface MediaFieldConfig extends BaseFieldConfig {\n type: 'media';\n accept?: string; // MIME filter, e.g. 'audio/*'\n endpoint?: string; // upload endpoint, defaults to DEFAULT_MEDIA_ENDPOINT\n}\n\nexport type FieldConfig =\n | TextFieldConfig\n | TextareaFieldConfig\n | SelectFieldConfig\n | KeyValueFieldConfig\n | ArrayFieldConfig\n | CheckboxFieldConfig\n | MessageEditorFieldConfig\n | MediaFieldConfig;\n\n// Layout configurations for better form organization\n// Recursive layout system - any layout item can contain other layout items\n\nexport interface FieldItemConfig {\n type: 'field';\n field: string; // field name to render\n}\n\nexport interface RowLayoutConfig {\n type: 'row';\n items: LayoutItem[]; // can contain fields, groups, or other rows\n gap?: string; // CSS gap value, defaults to '1rem'\n label?: string; // optional label for the entire row\n helpText?: string; // optional help text for the entire row\n inlineLabels?: Record<string, string>; // map of field name to inline label text\n marginBottom?: string; // CSS margin-bottom for spacing below the row\n}\n\nexport interface GroupLayoutConfig {\n type: 'group';\n label: string;\n items: LayoutItem[]; // can contain fields, rows, or other groups\n collapsible?: boolean;\n collapsed?: boolean | ((formData: FormData) => boolean); // initial state if collapsible - can be a function\n helpText?: string;\n getGroupValueCount?: (formData: FormData) => number; // optional function to get count for bubble display\n}\n\nexport interface SpacerLayoutConfig {\n type: 'spacer';\n}\n\nexport interface TextLayoutConfig {\n type: 'text';\n text: string;\n}\n\nexport type LayoutItem =\n | FieldItemConfig\n | RowLayoutConfig\n | GroupLayoutConfig\n | SpacerLayoutConfig\n | TextLayoutConfig\n | string; // string is shorthand for field\n\n/**\n * Action group constants - single source of truth for action categorization\n * Use as const for compile-time type checking\n */\nexport const ACTION_GROUPS = {\n send: 'send',\n contacts: 'contacts',\n save: 'save',\n services: 'services',\n broadcast: 'broadcast',\n trigger: 'trigger'\n} as const;\n\n/**\n * Split group constants - single source of truth for split categorization\n * Use as const for compile-time type checking\n */\nexport const SPLIT_GROUPS = {\n wait: 'wait',\n split: 'split'\n} as const;\n\n// Extract types from const objects for compile-time checking\nexport type ActionGroup = (typeof ACTION_GROUPS)[keyof typeof ACTION_GROUPS];\nexport type SplitGroup = (typeof SPLIT_GROUPS)[keyof typeof SPLIT_GROUPS];\n\n/**\n * Metadata for group display\n */\nexport interface GroupMetadata {\n color: string;\n title: string;\n description: string;\n}\n\n/**\n * Action group metadata - defines display properties for each action group\n * Order in this object determines display order in action selector (top to bottom)\n */\nexport const ACTION_GROUP_METADATA: Record<ActionGroup, GroupMetadata> = {\n [ACTION_GROUPS.send]: {\n color: '#3498db',\n title: 'Send',\n description: 'Actions that send messages or content to contacts'\n },\n [ACTION_GROUPS.contacts]: {\n color: '#01c1af',\n title: 'Contact',\n description: 'Actions that update contact information'\n },\n [ACTION_GROUPS.save]: {\n color: '#1a777c',\n title: 'Save',\n description: 'Actions that save or store data'\n },\n [ACTION_GROUPS.services]: {\n color: '#f79035ff',\n title: 'Services',\n description: 'Call external services and APIs'\n },\n [ACTION_GROUPS.broadcast]: {\n color: '#8e5ea7',\n title: 'Other People',\n description: 'Actions that apply to others instead of the contact'\n },\n [ACTION_GROUPS.trigger]: {\n color: '#df419f',\n title: 'Trigger',\n description: 'Actions that trigger other behavior'\n }\n};\n\n/**\n * Split group metadata - defines display properties for each split group\n * Order in this object determines display order in split selector (top to bottom)\n */\nexport const SPLIT_GROUP_METADATA: Record<SplitGroup, GroupMetadata> = {\n [SPLIT_GROUPS.wait]: {\n color: '#4d7dad',\n title: 'Wait',\n description: 'Wait for user and split on their response'\n },\n [SPLIT_GROUPS.split]: {\n color: '#aaaaaa',\n title: 'Split',\n description: 'Split the flow based on conditions'\n }\n};\n\nexport interface ActionConfig extends FormConfig {\n name: string;\n aliases?: string[]; // alternate type names for backwards compatibility (won't show in selector)\n group: ActionGroup;\n dialogSize?: 'small' | 'medium' | 'large' | 'xlarge';\n evaluated?: string[];\n hideFromActions?: boolean; // if true, don't show in action dialog (default: false - actions show in actions)\n flowTypes?: FlowType[]; // which flow types this action is available for (defaults to all if not specified)\n features?: Feature[]; // which features are required for this action (all must be present)\n render?: (node: any, action: any) => TemplateResult;\n\n form?: Record<string, FieldConfig>;\n layout?: LayoutItem[]; // optional layout configuration - array of layout items\n gutter?: LayoutItem[]; // fields to render in the dialog gutter (left side of buttons)\n\n toFormData?: (action: Action) => FormData;\n fromFormData?: (formData: FormData) => Action;\n\n // Localization support\n localizable?: string[]; // array of field names that can be localized\n toLocalizationFormData?: (\n action: Action,\n localization: Record<string, any>\n ) => FormData;\n fromLocalizationFormData?: (\n formData: FormData,\n action: Action\n ) => Record<string, any>;\n}\n"]}