@nyaruka/temba-components 0.139.0 → 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.
- package/.github/workflows/cla.yml +1 -1
- package/.github/workflows/copilot-setup-steps.yml +6 -1
- package/CHANGELOG.md +17 -0
- package/demo/data/flows/sample-flow.json +24 -0
- package/dist/temba-components.js +562 -296
- package/dist/temba-components.js.map +1 -1
- package/out-tsc/src/display/Chat.js +10 -7
- package/out-tsc/src/display/Chat.js.map +1 -1
- package/out-tsc/src/display/Dropdown.js +3 -1
- package/out-tsc/src/display/Dropdown.js.map +1 -1
- package/out-tsc/src/display/FloatingTab.js +3 -3
- package/out-tsc/src/display/FloatingTab.js.map +1 -1
- package/out-tsc/src/display/Thumbnail.js +163 -5
- package/out-tsc/src/display/Thumbnail.js.map +1 -1
- package/out-tsc/src/flow/CanvasNode.js +64 -22
- package/out-tsc/src/flow/CanvasNode.js.map +1 -1
- package/out-tsc/src/flow/Editor.js +142 -8
- package/out-tsc/src/flow/Editor.js.map +1 -1
- package/out-tsc/src/flow/NodeEditor.js +118 -10
- package/out-tsc/src/flow/NodeEditor.js.map +1 -1
- package/out-tsc/src/flow/StickyNote.js +13 -4
- package/out-tsc/src/flow/StickyNote.js.map +1 -1
- package/out-tsc/src/flow/actions/audio-player.js +112 -0
- package/out-tsc/src/flow/actions/audio-player.js.map +1 -0
- package/out-tsc/src/flow/actions/enter_flow.js +43 -0
- package/out-tsc/src/flow/actions/enter_flow.js.map +1 -0
- package/out-tsc/src/flow/actions/play_audio.js +57 -4
- package/out-tsc/src/flow/actions/play_audio.js.map +1 -1
- package/out-tsc/src/flow/actions/say_msg.js +86 -3
- package/out-tsc/src/flow/actions/say_msg.js.map +1 -1
- package/out-tsc/src/flow/config.js +11 -3
- package/out-tsc/src/flow/config.js.map +1 -1
- package/out-tsc/src/flow/nodes/shared-rules.js +1 -1
- package/out-tsc/src/flow/nodes/shared-rules.js.map +1 -1
- package/out-tsc/src/flow/nodes/terminal.js +7 -0
- package/out-tsc/src/flow/nodes/terminal.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js +77 -0
- package/out-tsc/src/flow/nodes/wait_for_audio.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_dial.js +151 -0
- package/out-tsc/src/flow/nodes/wait_for_dial.js.map +1 -0
- package/out-tsc/src/flow/nodes/wait_for_digits.js +61 -1
- package/out-tsc/src/flow/nodes/wait_for_digits.js.map +1 -1
- package/out-tsc/src/flow/nodes/wait_for_menu.js +173 -2
- package/out-tsc/src/flow/nodes/wait_for_menu.js.map +1 -1
- package/out-tsc/src/flow/operators.js +21 -5
- package/out-tsc/src/flow/operators.js.map +1 -1
- package/out-tsc/src/flow/types.js.map +1 -1
- package/out-tsc/src/flow/utils.js +79 -3
- package/out-tsc/src/flow/utils.js.map +1 -1
- package/out-tsc/src/form/ArrayEditor.js +4 -2
- package/out-tsc/src/form/ArrayEditor.js.map +1 -1
- package/out-tsc/src/form/FieldRenderer.js +49 -0
- package/out-tsc/src/form/FieldRenderer.js.map +1 -1
- package/out-tsc/src/interfaces.js +1 -0
- package/out-tsc/src/interfaces.js.map +1 -1
- package/out-tsc/src/layout/Dialog.js +52 -7
- package/out-tsc/src/layout/Dialog.js.map +1 -1
- package/out-tsc/src/live/TembaChart.js.map +1 -1
- package/out-tsc/src/simulator/Simulator.js +10 -4
- package/out-tsc/src/simulator/Simulator.js.map +1 -1
- package/out-tsc/src/store/AppState.js +89 -3
- package/out-tsc/src/store/AppState.js.map +1 -1
- package/out-tsc/test/actions/play_audio.test.js +118 -0
- package/out-tsc/test/actions/play_audio.test.js.map +1 -0
- package/out-tsc/test/actions/say_msg.test.js +158 -0
- package/out-tsc/test/actions/say_msg.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_audio.test.js +156 -0
- package/out-tsc/test/nodes/wait_for_audio.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_dial.test.js +336 -0
- package/out-tsc/test/nodes/wait_for_dial.test.js.map +1 -0
- package/out-tsc/test/nodes/wait_for_digits.test.js +198 -84
- package/out-tsc/test/nodes/wait_for_digits.test.js.map +1 -1
- package/out-tsc/test/nodes/wait_for_menu.test.js +340 -0
- package/out-tsc/test/nodes/wait_for_menu.test.js.map +1 -0
- package/out-tsc/test/temba-flow-collision.test.js +261 -6
- package/out-tsc/test/temba-flow-collision.test.js.map +1 -1
- package/out-tsc/test/temba-node-type-selector.test.js +6 -6
- package/out-tsc/test/temba-node-type-selector.test.js.map +1 -1
- package/package.json +1 -1
- package/screenshots/truth/actions/play_audio/editor/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/editor/static-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/expression-url.png +0 -0
- package/screenshots/truth/actions/play_audio/render/static-url.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/editor/text-with-audio-url.png +0 -0
- package/screenshots/truth/actions/say_msg/render/multiline-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/simple-text.png +0 -0
- package/screenshots/truth/actions/say_msg/render/text-with-audio-url.png +0 -0
- package/screenshots/truth/editor/router.png +0 -0
- package/screenshots/truth/editor/wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_audio/editor/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_audio/render/basic-audio-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/editor/dial-with-limits.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/basic-dial.png +0 -0
- package/screenshots/truth/nodes/wait_for_dial/render/dial-with-limits.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/basic-digits-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/digits-with-rules.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/editor/menu-with-digits.png +0 -0
- package/screenshots/truth/nodes/wait_for_menu/render/menu-with-digits.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/editor/short-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/basic-wait.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/custom-result-name.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/no-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_response/render/short-timeout.png +0 -0
- package/src/display/Chat.ts +13 -7
- package/src/display/Dropdown.ts +3 -1
- package/src/display/FloatingTab.ts +3 -3
- package/src/display/Thumbnail.ts +162 -2
- package/src/flow/CanvasNode.ts +69 -23
- package/src/flow/Editor.ts +156 -13
- package/src/flow/NodeEditor.ts +137 -9
- package/src/flow/StickyNote.ts +14 -4
- package/src/flow/actions/audio-player.ts +127 -0
- package/src/flow/actions/enter_flow.ts +44 -0
- package/src/flow/actions/play_audio.ts +64 -5
- package/src/flow/actions/say_msg.ts +94 -4
- package/src/flow/config.ts +11 -3
- package/src/flow/nodes/shared-rules.ts +1 -1
- package/src/flow/nodes/terminal.ts +9 -0
- package/src/flow/nodes/wait_for_audio.ts +88 -0
- package/src/flow/nodes/wait_for_dial.ts +176 -0
- package/src/flow/nodes/wait_for_digits.ts +86 -2
- package/src/flow/nodes/wait_for_menu.ts +209 -3
- package/src/flow/operators.ts +23 -5
- package/src/flow/types.ts +23 -1
- package/src/flow/utils.ts +82 -3
- package/src/form/ArrayEditor.ts +4 -2
- package/src/form/FieldRenderer.ts +64 -1
- package/src/interfaces.ts +2 -1
- package/src/layout/Dialog.ts +53 -7
- package/src/live/TembaChart.ts +1 -1
- package/src/simulator/Simulator.ts +13 -4
- package/src/store/AppState.ts +105 -1
- package/src/store/flow-definition.d.ts +2 -0
- package/test/actions/play_audio.test.ts +155 -0
- package/test/actions/say_msg.test.ts +196 -0
- package/test/nodes/wait_for_audio.test.ts +182 -0
- package/test/nodes/wait_for_dial.test.ts +382 -0
- package/test/nodes/wait_for_digits.test.ts +233 -109
- package/test/nodes/wait_for_menu.test.ts +383 -0
- package/test/temba-flow-collision.test.ts +286 -6
- package/test/temba-node-type-selector.test.ts +6 -6
- package/screenshots/truth/nodes/wait_for_digits/editor/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/editor/verification-code.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/phone-number-collection.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/single-digit-with-timeout.png +0 -0
- package/screenshots/truth/nodes/wait_for_digits/render/verification-code.png +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StickyNote.js","sourceRoot":"","sources":["../../../src/flow/StickyNote.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { StickyNote as StickyNoteData } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\n\nexport class StickyNote extends RapidElement {\n @property({ type: String })\n public uuid: string;\n\n @property({ type: Object })\n public data: StickyNoteData;\n\n @property({ type: Boolean })\n private dragging = false;\n\n @property({ type: Boolean })\n public selected = false;\n\n @property({ type: Boolean })\n private colorPickerExpanded = false;\n\n @fromStore(zustand, (state: AppState) => state.isTranslating)\n private isTranslating!: boolean;\n\n static get styles() {\n return css`\n :host {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n --curvature: 8px;\n }\n\n .sticky-note {\n width: 182px;\n background-color: var(--sticky-color);\n border: 1px solid var(--sticky-border-color);\n border-radius: var(--curvature);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n sans-serif;\n font-size: 12px;\n overflow: hidden;\n transition: transform 0.1s ease, box-shadow 0.2s ease;\n color: var(--sticky-text-color);\n opacity: 0.85;\n }\n\n .sticky-note.dragging {\n opacity: 0.7;\n z-index: 1000;\n transform: rotate(0deg);\n box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:hover {\n transform: translateY(0px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n }\n\n /* Color themes */\n .sticky-note.yellow {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n }\n .sticky-note.blue {\n --sticky-color: #bfdbfe;\n --sticky-border-color: #3b82f6;\n --sticky-text-color: #1e3a8a;\n }\n .sticky-note.pink {\n --sticky-color: #fce7f3;\n --sticky-border-color: #ec4899;\n --sticky-text-color: #831843;\n }\n .sticky-note.green {\n --sticky-color: #d1fae5;\n --sticky-border-color: #10b981;\n --sticky-text-color: #064e3b;\n }\n .sticky-note.gray {\n --sticky-color: #f3f4f6;\n --sticky-border-color: #6b7280;\n --sticky-text-color: #374151;\n }\n\n /* Title and body containers */\n .sticky-title-container {\n position: relative;\n border-bottom: 1px solid var(--sticky-border-color);\n background-color: rgba(255, 255, 255, 0.5);\n display: flex;\n align-items: center;\n }\n .sticky-body-container {\n position: relative;\n }\n\n /* Editable fields */\n [contenteditable='true'] {\n margin: 2px;\n padding: 4px 8px;\n outline: none;\n border-radius: var(--curvature);\n transition: background 0.2s;\n }\n [contenteditable='true']:focus {\n background-color: rgba(255, 255, 255, 0.8);\n border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n outline-color: var(--sticky-border-color);\n }\n\n /* Title */\n .sticky-title {\n font-weight: 600;\n font-size: 13px;\n color: var(--sticky-text-color);\n min-height: 20px;\n line-height: 20px;\n border-top-left-radius: var(--curvature);\n border-top-right-radius: var(--curvature);\n flex-grow: 1;\n padding: 4px 8px !important;\n margin: 2px;\n padding-left: 8px;\n }\n .sticky-title:empty::before {\n content: 'Click to add title';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-title:focus {\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n }\n\n /* Body */\n .sticky-body {\n padding: 8px 10px;\n color: var(--sticky-text-color);\n line-height: 1.4;\n min-height: 48px;\n word-wrap: break-word;\n white-space: pre-wrap;\n margin: 2px;\n }\n .sticky-body:empty::before {\n content: 'Click to add note';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-body:focus {\n border-top-left-radius: 0px;\n border-top-right-radius: 0px;\n }\n\n /* Drag icon */\n .sticky-title-container > .drag-handle {\n --icon-color: var(--sticky-border-color);\n cursor: move;\n max-width: 20px;\n padding-left: 8px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .sticky-note:hover .drag-handle {\n }\n\n .sticky-note:focus-within .sticky-title-container > .drag-handle {\n }\n\n /* Focus/active states */\n .sticky-note:focus-within {\n box-shadow: 0 0 0 1px var(--sticky-border-color),\n 0 10px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:focus-within .drag-handle {\n max-width: 0px;\n padding-left: 0px;\n }\n\n /* Color picker */\n .color-picker {\n position: absolute;\n bottom: 4px;\n right: 4px;\n width: 8px;\n height: 8px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n\n border-radius: 3px;\n background-color: var(--sticky-color);\n cursor: pointer;\n transition: transform 0.2s ease;\n }\n\n .color-picker:hover {\n transform: scale(1.1);\n }\n\n .color-options {\n position: absolute;\n bottom: 0;\n right: 0;\n display: flex;\n gap: 4px;\n background-color: rgba(255, 255, 255, 0.9);\n border: 1px solid #ccc;\n border-radius: 6px;\n padding: 3px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform-origin: bottom right;\n transform: scale(0);\n opacity: 0;\n transition: transform 0.2s ease, opacity 0.2s ease;\n z-index: 1000;\n }\n\n .color-options.expanded {\n transform: scale(1);\n opacity: 1;\n }\n\n .color-option {\n width: 12px;\n height: 12px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n cursor: pointer;\n transition: transform 0.15s ease, border-color 0.15s ease;\n }\n\n .color-option:hover {\n transform: scale(1.1);\n border-color: rgba(0, 0, 0, 0.4);\n }\n\n .color-option.yellow {\n background-color: #fef08a;\n }\n\n .color-option.blue {\n background-color: #bfdbfe;\n }\n\n .color-option.pink {\n background-color: #fce7f3;\n }\n\n .color-option.green {\n background-color: #d1fae5;\n }\n\n .color-option.gray {\n background-color: #f3f4f6;\n }\n `;\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('data') || changes.has('uuid')) {\n this.updateCanvasSize();\n }\n }\n\n private updateCanvasSize(): void {\n if (!this.data) {\n return;\n }\n\n const element = this.querySelector('.sticky-note');\n if (element) {\n const rect = element.getBoundingClientRect();\n getStore()\n .getState()\n .expandCanvas(\n this.data.position.left + rect.width,\n this.data.position.top + rect.height\n );\n }\n }\n\n private handleTitleBlur(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n const newTitle = target.textContent || '';\n\n if (this.data && newTitle !== this.data.title) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n title: newTitle\n });\n }\n this.requestUpdate();\n }\n\n private handleBodyBlur(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n const newBody = target.textContent || '';\n\n if (this.data && newBody !== this.data.body) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n body: newBody\n });\n }\n this.requestUpdate();\n }\n\n private handleContentMouseDown(event: MouseEvent): void {\n // If this sticky note is selected, don't stop propagation\n // so that group dragging can work\n if (this.selected) {\n return;\n }\n // Otherwise, stop propagation to enable editing\n event.stopPropagation();\n }\n\n private handleKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleColorPickerMouseEnter(): void {\n this.colorPickerExpanded = true;\n }\n\n private handleColorPickerMouseLeave(): void {\n this.colorPickerExpanded = false;\n }\n\n private handleColorOptionClick(\n event: MouseEvent,\n color: 'yellow' | 'blue' | 'pink' | 'green' | 'gray'\n ): void {\n event.stopPropagation();\n\n if (this.data && color !== this.data.color) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n color: color\n });\n }\n\n this.colorPickerExpanded = false;\n this.requestUpdate();\n }\n\n public render(): TemplateResult {\n if (!this.data) {\n return html`<div class=\"sticky-note\" style=\"display: none;\"></div>`;\n }\n\n const style = `left: ${this.data.position.left}px; top: ${this.data.position.top}px;`;\n\n return html`\n <div\n class=\"sticky-note ${this.data.color} ${this.dragging\n ? 'dragging'\n : ''}\"\n style=\"${style}\"\n data-uuid=\"${this.uuid}\"\n >\n <div class=\"sticky-title-container\">\n <temba-icon name=\"drag\" class=\"drag-handle\"></temba-icon>\n <div\n class=\"sticky-title\"\n contenteditable=\"${!this.isTranslating}\"\n @blur=\"${this.handleTitleBlur}\"\n @keydown=\"${this.handleKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n .textContent=\"${this.data.title}\"\n ></div>\n </div>\n <div class=\"sticky-body-container\">\n <div\n class=\"sticky-body\"\n contenteditable=\"${!this.isTranslating}\"\n @blur=\"${this.handleBodyBlur}\"\n @keydown=\"${this.handleKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n .textContent=\"${this.data.body}\"\n ></div>\n ${!this.isTranslating\n ? html`<div class=\"edit-icon\" title=\"Edit note\"></div>`\n : ''}\n\n <!-- Color picker -->\n <div\n class=\"color-picker\"\n @mouseenter=\"${this.handleColorPickerMouseEnter}\"\n @mouseleave=\"${this.handleColorPickerMouseLeave}\"\n >\n <div\n class=\"color-options ${this.colorPickerExpanded\n ? 'expanded'\n : ''}\"\n >\n <div\n class=\"color-option yellow\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'yellow')}\"\n ></div>\n <div\n class=\"color-option blue\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'blue')}\"\n ></div>\n <div\n class=\"color-option pink\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'pink')}\"\n ></div>\n <div\n class=\"color-option green\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'green')}\"\n ></div>\n <div\n class=\"color-option gray\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'gray')}\"\n ></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"StickyNote.js","sourceRoot":"","sources":["../../../src/flow/StickyNote.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, PropertyValueMap, TemplateResult } from 'lit';\nimport { property } from 'lit/decorators.js';\nimport { RapidElement } from '../RapidElement';\nimport { StickyNote as StickyNoteData } from '../store/flow-definition';\nimport { getStore } from '../store/Store';\nimport { AppState, fromStore, zustand } from '../store/AppState';\n\nexport class StickyNote extends RapidElement {\n @property({ type: String })\n public uuid: string;\n\n @property({ type: Object })\n public data: StickyNoteData;\n\n @property({ type: Boolean })\n private dragging = false;\n\n @property({ type: Boolean })\n public selected = false;\n\n @property({ type: Boolean })\n private colorPickerExpanded = false;\n\n @fromStore(zustand, (state: AppState) => state.isTranslating)\n private isTranslating!: boolean;\n\n static get styles() {\n return css`\n :host {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n --curvature: 8px;\n }\n\n .sticky-note {\n width: 182px;\n background-color: var(--sticky-color);\n border: 1px solid var(--sticky-border-color);\n border-radius: var(--curvature);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n sans-serif;\n font-size: 12px;\n overflow: hidden;\n transition: transform 0.1s ease, box-shadow 0.2s ease;\n color: var(--sticky-text-color);\n opacity: 0.85;\n }\n\n .sticky-note.dragging {\n opacity: 0.7;\n z-index: 1000;\n transform: rotate(0deg);\n box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:hover {\n transform: translateY(0px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n }\n\n /* Color themes */\n .sticky-note.yellow {\n --sticky-color: #fef08a;\n --sticky-border-color: #facc15;\n --sticky-text-color: #451a03;\n }\n .sticky-note.blue {\n --sticky-color: #bfdbfe;\n --sticky-border-color: #3b82f6;\n --sticky-text-color: #1e3a8a;\n }\n .sticky-note.pink {\n --sticky-color: #fce7f3;\n --sticky-border-color: #ec4899;\n --sticky-text-color: #831843;\n }\n .sticky-note.green {\n --sticky-color: #d1fae5;\n --sticky-border-color: #10b981;\n --sticky-text-color: #064e3b;\n }\n .sticky-note.gray {\n --sticky-color: #f3f4f6;\n --sticky-border-color: #6b7280;\n --sticky-text-color: #374151;\n }\n\n /* Title and body containers */\n .sticky-title-container {\n position: relative;\n border-bottom: 1px solid var(--sticky-border-color);\n background-color: rgba(255, 255, 255, 0.5);\n display: flex;\n align-items: center;\n }\n .sticky-body-container {\n position: relative;\n }\n\n /* Editable fields */\n [contenteditable='true'] {\n margin: 2px;\n padding: 4px 8px;\n outline: none;\n border-radius: var(--curvature);\n transition: background 0.2s;\n }\n [contenteditable='true']:focus {\n background-color: rgba(255, 255, 255, 0.8);\n border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n outline-color: var(--sticky-border-color);\n }\n\n /* Title */\n .sticky-title {\n font-weight: 600;\n font-size: 13px;\n color: var(--sticky-text-color);\n min-height: 20px;\n line-height: 20px;\n border-top-left-radius: var(--curvature);\n border-top-right-radius: var(--curvature);\n flex-grow: 1;\n padding: 4px 8px !important;\n margin: 2px;\n padding-left: 8px;\n }\n .sticky-title:empty::before {\n content: 'Click to add title';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-title:focus {\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n }\n\n /* Body */\n .sticky-body {\n padding: 8px 10px;\n color: var(--sticky-text-color);\n line-height: 1.4;\n min-height: 48px;\n word-wrap: break-word;\n white-space: pre-wrap;\n margin: 2px;\n }\n .sticky-body:empty::before {\n content: 'Click to add note';\n opacity: 0.5;\n font-style: italic;\n }\n .sticky-body:focus {\n border-top-left-radius: 0px;\n border-top-right-radius: 0px;\n }\n\n /* Drag icon */\n .sticky-title-container > .drag-handle {\n --icon-color: var(--sticky-border-color);\n cursor: move;\n max-width: 20px;\n padding-left: 8px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .sticky-note:hover .drag-handle {\n }\n\n .sticky-note:focus-within .sticky-title-container > .drag-handle {\n }\n\n /* Focus/active states */\n .sticky-note:focus-within {\n box-shadow: 0 0 0 1px var(--sticky-border-color),\n 0 10px 20px rgba(0, 0, 0, 0.3);\n }\n\n .sticky-note:focus-within .drag-handle {\n max-width: 0px;\n padding-left: 0px;\n }\n\n /* Color picker */\n .color-picker {\n position: absolute;\n bottom: 4px;\n right: 4px;\n width: 8px;\n height: 8px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n\n border-radius: 3px;\n background-color: var(--sticky-color);\n cursor: pointer;\n transition: transform 0.2s ease;\n }\n\n .color-picker:hover {\n transform: scale(1.1);\n }\n\n .color-options {\n position: absolute;\n bottom: 0;\n right: 0;\n display: flex;\n gap: 4px;\n background-color: rgba(255, 255, 255, 0.9);\n border: 1px solid #ccc;\n border-radius: 6px;\n padding: 3px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform-origin: bottom right;\n transform: scale(0);\n opacity: 0;\n transition: transform 0.2s ease, opacity 0.2s ease;\n z-index: 1000;\n }\n\n .color-options.expanded {\n transform: scale(1);\n opacity: 1;\n }\n\n .color-option {\n width: 12px;\n height: 12px;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 3px;\n cursor: pointer;\n transition: transform 0.15s ease, border-color 0.15s ease;\n }\n\n .color-option:hover {\n transform: scale(1.1);\n border-color: rgba(0, 0, 0, 0.4);\n }\n\n .color-option.yellow {\n background-color: #fef08a;\n }\n\n .color-option.blue {\n background-color: #bfdbfe;\n }\n\n .color-option.pink {\n background-color: #fce7f3;\n }\n\n .color-option.green {\n background-color: #d1fae5;\n }\n\n .color-option.gray {\n background-color: #f3f4f6;\n }\n `;\n }\n\n protected updated(\n changes: PropertyValueMap<any> | Map<PropertyKey, unknown>\n ): void {\n super.updated(changes);\n if (changes.has('data') || changes.has('uuid')) {\n this.updateCanvasSize();\n }\n }\n\n private updateCanvasSize(): void {\n if (!this.data) {\n return;\n }\n\n const element = this.querySelector('.sticky-note');\n if (element) {\n const rect = element.getBoundingClientRect();\n getStore()\n .getState()\n .expandCanvas(\n this.data.position.left + rect.width,\n this.data.position.top + rect.height\n );\n }\n }\n\n private handleTitleBlur(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n const newTitle = target.textContent || '';\n\n if (this.data && newTitle !== this.data.title) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n title: newTitle\n });\n }\n this.requestUpdate();\n }\n\n private handleBodyBlur(event: FocusEvent): void {\n const target = event.target as HTMLElement;\n const newBody = target.innerText || '';\n\n if (this.data && newBody !== this.data.body) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n body: newBody\n });\n }\n this.requestUpdate();\n }\n\n private handleContentMouseDown(event: MouseEvent): void {\n // If this sticky note is selected, don't stop propagation\n // so that group dragging can work\n if (this.selected) {\n return;\n }\n // Otherwise, stop propagation to enable editing\n event.stopPropagation();\n }\n\n private handleTitleKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter') {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleBodyKeyDown(event: KeyboardEvent): void {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n (event.target as HTMLElement).blur();\n }\n if (event.key === 'Escape') {\n (event.target as HTMLElement).blur();\n }\n }\n\n private handleColorPickerMouseEnter(): void {\n this.colorPickerExpanded = true;\n }\n\n private handleColorPickerMouseLeave(): void {\n this.colorPickerExpanded = false;\n }\n\n private handleColorOptionClick(\n event: MouseEvent,\n color: 'yellow' | 'blue' | 'pink' | 'green' | 'gray'\n ): void {\n event.stopPropagation();\n\n if (this.data && color !== this.data.color) {\n getStore()\n .getState()\n .updateStickyNote(this.uuid, {\n ...this.data,\n color: color\n });\n }\n\n this.colorPickerExpanded = false;\n this.requestUpdate();\n }\n\n public render(): TemplateResult {\n if (!this.data) {\n return html`<div class=\"sticky-note\" style=\"display: none;\"></div>`;\n }\n\n const style = `left: ${this.data.position.left}px; top: ${this.data.position.top}px;`;\n\n return html`\n <div\n class=\"sticky-note ${this.data.color} ${this.dragging\n ? 'dragging'\n : ''}\"\n style=\"${style}\"\n data-uuid=\"${this.uuid}\"\n >\n <div class=\"sticky-title-container\">\n <temba-icon name=\"drag\" class=\"drag-handle\"></temba-icon>\n <div\n class=\"sticky-title\"\n contenteditable=\"${!this.isTranslating}\"\n @blur=\"${this.handleTitleBlur}\"\n @keydown=\"${this.handleTitleKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n .textContent=\"${this.data.title}\"\n ></div>\n </div>\n <div class=\"sticky-body-container\">\n <div\n class=\"sticky-body\"\n contenteditable=\"${!this.isTranslating}\"\n @blur=\"${this.handleBodyBlur}\"\n @keydown=\"${this.handleBodyKeyDown}\"\n @mousedown=\"${this.handleContentMouseDown}\"\n .textContent=\"${this.data.body}\"\n ></div>\n ${!this.isTranslating\n ? html`<div class=\"edit-icon\" title=\"Edit note\"></div>`\n : ''}\n\n <!-- Color picker -->\n <div\n class=\"color-picker\"\n @mouseenter=\"${this.handleColorPickerMouseEnter}\"\n @mouseleave=\"${this.handleColorPickerMouseLeave}\"\n >\n <div\n class=\"color-options ${this.colorPickerExpanded\n ? 'expanded'\n : ''}\"\n >\n <div\n class=\"color-option yellow\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'yellow')}\"\n ></div>\n <div\n class=\"color-option blue\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'blue')}\"\n ></div>\n <div\n class=\"color-option pink\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'pink')}\"\n ></div>\n <div\n class=\"color-option green\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'green')}\"\n ></div>\n <div\n class=\"color-option gray\"\n @click=\"${(e: MouseEvent) =>\n this.handleColorOptionClick(e, 'gray')}\"\n ></div>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n"]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { html } from 'lit-html';
|
|
2
|
+
// SVG paths for play and pause icons
|
|
3
|
+
const PLAY_SVG = html `<svg
|
|
4
|
+
viewBox="0 0 24 24"
|
|
5
|
+
width="16"
|
|
6
|
+
height="16"
|
|
7
|
+
fill="currentColor"
|
|
8
|
+
>
|
|
9
|
+
<polygon points="6,3 20,12 6,21" />
|
|
10
|
+
</svg>`;
|
|
11
|
+
// Track active audio so only one plays at a time
|
|
12
|
+
let activeAudio = null;
|
|
13
|
+
let activeContainer = null;
|
|
14
|
+
function stopActive() {
|
|
15
|
+
if (activeAudio) {
|
|
16
|
+
activeAudio.pause();
|
|
17
|
+
activeAudio.currentTime = 0;
|
|
18
|
+
if (activeContainer) {
|
|
19
|
+
resetPlayer(activeContainer);
|
|
20
|
+
}
|
|
21
|
+
activeAudio = null;
|
|
22
|
+
activeContainer = null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resetPlayer(container) {
|
|
26
|
+
const btn = container.querySelector('.audio-play-btn');
|
|
27
|
+
const progress = container.querySelector('.audio-progress');
|
|
28
|
+
if (btn)
|
|
29
|
+
btn.innerHTML =
|
|
30
|
+
'<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><polygon points="6,3 20,12 6,21"/></svg>';
|
|
31
|
+
if (progress)
|
|
32
|
+
progress.style.width = '0%';
|
|
33
|
+
}
|
|
34
|
+
function handlePlayClick(e) {
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
const container = e.currentTarget.closest('.audio-player');
|
|
38
|
+
if (!container)
|
|
39
|
+
return;
|
|
40
|
+
const url = container.dataset.url;
|
|
41
|
+
if (!url)
|
|
42
|
+
return;
|
|
43
|
+
const btn = container.querySelector('.audio-play-btn');
|
|
44
|
+
const progress = container.querySelector('.audio-progress');
|
|
45
|
+
// If this is already playing, pause it
|
|
46
|
+
if (activeAudio && activeContainer === container && !activeAudio.paused) {
|
|
47
|
+
activeAudio.pause();
|
|
48
|
+
btn.innerHTML =
|
|
49
|
+
'<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><polygon points="6,3 20,12 6,21"/></svg>';
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Stop any other playing audio
|
|
53
|
+
stopActive();
|
|
54
|
+
const audio = new Audio(url);
|
|
55
|
+
activeAudio = audio;
|
|
56
|
+
activeContainer = container;
|
|
57
|
+
btn.innerHTML =
|
|
58
|
+
'<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><rect x="5" y="3" width="4" height="18"/><rect x="15" y="3" width="4" height="18"/></svg>';
|
|
59
|
+
audio.addEventListener('timeupdate', () => {
|
|
60
|
+
if (audio.duration && progress) {
|
|
61
|
+
const pct = (audio.currentTime / audio.duration) * 100;
|
|
62
|
+
progress.style.width = `${pct}%`;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
audio.addEventListener('ended', () => {
|
|
66
|
+
resetPlayer(container);
|
|
67
|
+
activeAudio = null;
|
|
68
|
+
activeContainer = null;
|
|
69
|
+
});
|
|
70
|
+
audio.addEventListener('error', () => {
|
|
71
|
+
resetPlayer(container);
|
|
72
|
+
activeAudio = null;
|
|
73
|
+
activeContainer = null;
|
|
74
|
+
});
|
|
75
|
+
audio.play().catch(() => {
|
|
76
|
+
resetPlayer(container);
|
|
77
|
+
activeAudio = null;
|
|
78
|
+
activeContainer = null;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Renders an inline audio player with play/pause button and progress bar.
|
|
83
|
+
* Used on canvas nodes for play_audio and say_msg actions.
|
|
84
|
+
*/
|
|
85
|
+
export function renderAudioPlayer(audioUrl) {
|
|
86
|
+
return html `
|
|
87
|
+
<div
|
|
88
|
+
class="audio-player"
|
|
89
|
+
data-url="${audioUrl}"
|
|
90
|
+
style="display: flex; align-items: center; gap: 0.4em; cursor: default;"
|
|
91
|
+
@mousedown=${(e) => e.stopPropagation()}
|
|
92
|
+
@mouseup=${(e) => e.stopPropagation()}
|
|
93
|
+
>
|
|
94
|
+
<div
|
|
95
|
+
class="audio-play-btn"
|
|
96
|
+
@click=${handlePlayClick}
|
|
97
|
+
style="cursor: pointer; color: #666; display: flex; align-items: center; flex-shrink: 0;"
|
|
98
|
+
>
|
|
99
|
+
${PLAY_SVG}
|
|
100
|
+
</div>
|
|
101
|
+
<div
|
|
102
|
+
style="flex: 1; height: 4px; background: #e0e0e0; border-radius: 2px; overflow: hidden; min-width: 40px;"
|
|
103
|
+
>
|
|
104
|
+
<div
|
|
105
|
+
class="audio-progress"
|
|
106
|
+
style="width: 0%; height: 100%; background: var(--color-primary, #2387ca); border-radius: 2px; transition: width 0.2s linear;"
|
|
107
|
+
></div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=audio-player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-player.js","sourceRoot":"","sources":["../../../../src/flow/actions/audio-player.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, TemplateResult } from 'lit-html';\n\n// SVG paths for play and pause icons\nconst PLAY_SVG = html`<svg\n viewBox=\"0 0 24 24\"\n width=\"16\"\n height=\"16\"\n fill=\"currentColor\"\n>\n <polygon points=\"6,3 20,12 6,21\" />\n</svg>`;\n\n// Track active audio so only one plays at a time\nlet activeAudio: HTMLAudioElement | null = null;\nlet activeContainer: HTMLElement | null = null;\n\nfunction stopActive() {\n if (activeAudio) {\n activeAudio.pause();\n activeAudio.currentTime = 0;\n if (activeContainer) {\n resetPlayer(activeContainer);\n }\n activeAudio = null;\n activeContainer = null;\n }\n}\n\nfunction resetPlayer(container: HTMLElement) {\n const btn = container.querySelector('.audio-play-btn') as HTMLElement;\n const progress = container.querySelector('.audio-progress') as HTMLElement;\n if (btn)\n btn.innerHTML =\n '<svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><polygon points=\"6,3 20,12 6,21\"/></svg>';\n if (progress) progress.style.width = '0%';\n}\n\nfunction handlePlayClick(e: MouseEvent) {\n e.stopPropagation();\n e.preventDefault();\n\n const container = (e.currentTarget as HTMLElement).closest(\n '.audio-player'\n ) as HTMLElement;\n if (!container) return;\n\n const url = container.dataset.url;\n if (!url) return;\n\n const btn = container.querySelector('.audio-play-btn') as HTMLElement;\n const progress = container.querySelector('.audio-progress') as HTMLElement;\n\n // If this is already playing, pause it\n if (activeAudio && activeContainer === container && !activeAudio.paused) {\n activeAudio.pause();\n btn.innerHTML =\n '<svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><polygon points=\"6,3 20,12 6,21\"/></svg>';\n return;\n }\n\n // Stop any other playing audio\n stopActive();\n\n const audio = new Audio(url);\n activeAudio = audio;\n activeContainer = container;\n\n btn.innerHTML =\n '<svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\"><rect x=\"5\" y=\"3\" width=\"4\" height=\"18\"/><rect x=\"15\" y=\"3\" width=\"4\" height=\"18\"/></svg>';\n\n audio.addEventListener('timeupdate', () => {\n if (audio.duration && progress) {\n const pct = (audio.currentTime / audio.duration) * 100;\n progress.style.width = `${pct}%`;\n }\n });\n\n audio.addEventListener('ended', () => {\n resetPlayer(container);\n activeAudio = null;\n activeContainer = null;\n });\n\n audio.addEventListener('error', () => {\n resetPlayer(container);\n activeAudio = null;\n activeContainer = null;\n });\n\n audio.play().catch(() => {\n resetPlayer(container);\n activeAudio = null;\n activeContainer = null;\n });\n}\n\n/**\n * Renders an inline audio player with play/pause button and progress bar.\n * Used on canvas nodes for play_audio and say_msg actions.\n */\nexport function renderAudioPlayer(audioUrl: string): TemplateResult {\n return html`\n <div\n class=\"audio-player\"\n data-url=\"${audioUrl}\"\n style=\"display: flex; align-items: center; gap: 0.4em; cursor: default;\"\n @mousedown=${(e: MouseEvent) => e.stopPropagation()}\n @mouseup=${(e: MouseEvent) => e.stopPropagation()}\n >\n <div\n class=\"audio-play-btn\"\n @click=${handlePlayClick}\n style=\"cursor: pointer; color: #666; display: flex; align-items: center; flex-shrink: 0;\"\n >\n ${PLAY_SVG}\n </div>\n <div\n style=\"flex: 1; height: 4px; background: #e0e0e0; border-radius: 2px; overflow: hidden; min-width: 40px;\"\n >\n <div\n class=\"audio-progress\"\n style=\"width: 0%; height: 100%; background: var(--color-primary, #2387ca); border-radius: 2px; transition: width 0.2s linear;\"\n ></div>\n </div>\n </div>\n `;\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { html } from 'lit-html';
|
|
2
|
+
import { ACTION_GROUPS, FlowTypes } from '../types';
|
|
3
|
+
import { renderNamedObjects } from '../utils';
|
|
4
|
+
export const enter_flow = {
|
|
5
|
+
name: 'Enter a Flow',
|
|
6
|
+
group: ACTION_GROUPS.trigger,
|
|
7
|
+
hideFromActions: true,
|
|
8
|
+
flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],
|
|
9
|
+
render: (_node, action) => {
|
|
10
|
+
return html `${renderNamedObjects([action.flow], 'flow')}`;
|
|
11
|
+
},
|
|
12
|
+
toFormData: (action) => {
|
|
13
|
+
return {
|
|
14
|
+
uuid: action.uuid,
|
|
15
|
+
flow: action.flow ? [action.flow] : []
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
form: {
|
|
19
|
+
flow: {
|
|
20
|
+
type: 'select',
|
|
21
|
+
required: true,
|
|
22
|
+
placeholder: 'Select a flow...',
|
|
23
|
+
helpText: 'The contact will enter this flow and not return',
|
|
24
|
+
endpoint: '/api/v2/flows.json',
|
|
25
|
+
valueKey: 'uuid',
|
|
26
|
+
nameKey: 'name'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
layout: ['flow'],
|
|
30
|
+
fromFormData: (formData) => {
|
|
31
|
+
const selected = formData.flow[0];
|
|
32
|
+
return {
|
|
33
|
+
uuid: formData.uuid,
|
|
34
|
+
type: 'enter_flow',
|
|
35
|
+
terminal: true,
|
|
36
|
+
flow: {
|
|
37
|
+
uuid: selected.uuid || selected.value,
|
|
38
|
+
name: selected.name
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=enter_flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enter_flow.js","sourceRoot":"","sources":["../../../../src/flow/actions/enter_flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,CAAC,MAAM,UAAU,GAAiB;IACtC,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,aAAa,CAAC,OAAO;IAC5B,eAAe,EAAE,IAAI;IACrB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,UAAU,CAAC;IACrE,MAAM,EAAE,CAAC,KAAW,EAAE,MAAiB,EAAE,EAAE;QACzC,OAAO,IAAI,CAAA,GAAG,kBAAkB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5D,CAAC;IACD,UAAU,EAAE,CAAC,MAAiB,EAAE,EAAE;QAChC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;SACvC,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,kBAAkB;YAC/B,QAAQ,EAAE,iDAAiD;YAC3D,QAAQ,EAAE,oBAAoB;YAC9B,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,MAAM;SAChB;KACF;IACD,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,YAAY,EAAE,CAAC,QAAa,EAAa,EAAE;QACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK;gBACrC,IAAI,EAAE,QAAQ,CAAC,IAAI;aACpB;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FlowTypes } from '../types';\nimport { Node, EnterFlow } from '../../store/flow-definition';\nimport { renderNamedObjects } from '../utils';\n\nexport const enter_flow: ActionConfig = {\n name: 'Enter a Flow',\n group: ACTION_GROUPS.trigger,\n hideFromActions: true,\n flowTypes: [FlowTypes.VOICE, FlowTypes.MESSAGE, FlowTypes.BACKGROUND],\n render: (_node: Node, action: EnterFlow) => {\n return html`${renderNamedObjects([action.flow], 'flow')}`;\n },\n toFormData: (action: EnterFlow) => {\n return {\n uuid: action.uuid,\n flow: action.flow ? [action.flow] : []\n };\n },\n form: {\n flow: {\n type: 'select',\n required: true,\n placeholder: 'Select a flow...',\n helpText: 'The contact will enter this flow and not return',\n endpoint: '/api/v2/flows.json',\n valueKey: 'uuid',\n nameKey: 'name'\n }\n },\n layout: ['flow'],\n fromFormData: (formData: any): EnterFlow => {\n const selected = formData.flow[0];\n return {\n uuid: formData.uuid,\n type: 'enter_flow',\n terminal: true,\n flow: {\n uuid: selected.uuid || selected.value,\n name: selected.name\n }\n };\n }\n};\n"]}
|
|
@@ -1,12 +1,65 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
2
|
import { ACTION_GROUPS, FlowTypes } from '../types';
|
|
3
3
|
export const play_audio = {
|
|
4
|
-
name: 'Play
|
|
4
|
+
name: 'Play Recording',
|
|
5
5
|
group: ACTION_GROUPS.send,
|
|
6
6
|
flowTypes: [FlowTypes.VOICE],
|
|
7
|
-
render: (_node,
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
render: (_node, action) => {
|
|
8
|
+
return html `
|
|
9
|
+
<div style="display: flex; align-items: center; gap: 0.3em;">
|
|
10
|
+
<temba-icon name="recording" size="1"></temba-icon>
|
|
11
|
+
<div
|
|
12
|
+
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;"
|
|
13
|
+
title="${action.audio_url || ''}"
|
|
14
|
+
>
|
|
15
|
+
${action.audio_url || ''}
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
`;
|
|
19
|
+
},
|
|
20
|
+
form: {
|
|
21
|
+
audio_url: {
|
|
22
|
+
type: 'text',
|
|
23
|
+
label: 'Recording URL',
|
|
24
|
+
required: true,
|
|
25
|
+
evaluated: true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
layout: ['audio_url'],
|
|
29
|
+
toFormData: (action) => {
|
|
30
|
+
return {
|
|
31
|
+
uuid: action.uuid,
|
|
32
|
+
audio_url: action.audio_url || ''
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
fromFormData: (data) => {
|
|
36
|
+
return {
|
|
37
|
+
uuid: data.uuid,
|
|
38
|
+
type: 'play_audio',
|
|
39
|
+
audio_url: (data.audio_url || '').trim()
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
localizable: ['audio_url'],
|
|
43
|
+
toLocalizationFormData: (action, localization) => {
|
|
44
|
+
const formData = {
|
|
45
|
+
uuid: action.uuid
|
|
46
|
+
};
|
|
47
|
+
if (localization.audio_url && Array.isArray(localization.audio_url)) {
|
|
48
|
+
formData.audio_url = localization.audio_url[0] || '';
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
formData.audio_url = '';
|
|
52
|
+
}
|
|
53
|
+
return formData;
|
|
54
|
+
},
|
|
55
|
+
fromLocalizationFormData: (formData, action) => {
|
|
56
|
+
const localization = {};
|
|
57
|
+
if (formData.audio_url && formData.audio_url.trim() !== '') {
|
|
58
|
+
if (formData.audio_url !== action.audio_url) {
|
|
59
|
+
localization.audio_url = [formData.audio_url];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return localization;
|
|
10
63
|
}
|
|
11
64
|
};
|
|
12
65
|
//# sourceMappingURL=play_audio.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"play_audio.js","sourceRoot":"","sources":["../../../../src/flow/actions/play_audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,
|
|
1
|
+
{"version":3,"file":"play_audio.js","sourceRoot":"","sources":["../../../../src/flow/actions/play_audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,EAAY,SAAS,EAAE,MAAM,UAAU,CAAC;AAG5E,MAAM,CAAC,MAAM,UAAU,GAAiB;IACtC,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,aAAa,CAAC,IAAI;IACzB,SAAS,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;IAC5B,MAAM,EAAE,CAAC,KAAW,EAAE,MAAiB,EAAE,EAAE;QACzC,OAAO,IAAI,CAAA;;;;;mBAKI,MAAM,CAAC,SAAS,IAAI,EAAE;;YAE7B,MAAM,CAAC,SAAS,IAAI,EAAE;;;KAG7B,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,SAAS,EAAE;YACT,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,eAAe;YACtB,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;SAChB;KACF;IACD,MAAM,EAAE,CAAC,WAAW,CAAC;IACrB,UAAU,EAAE,CAAC,MAAiB,EAAE,EAAE;QAChC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;SAClC,CAAC;IACJ,CAAC;IACD,YAAY,EAAE,CAAC,IAAc,EAAE,EAAE;QAC/B,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;SAC5B,CAAC;IACjB,CAAC;IACD,WAAW,EAAE,CAAC,WAAW,CAAC;IAC1B,sBAAsB,EAAE,CACtB,MAAiB,EACjB,YAAiC,EACjC,EAAE;QACF,MAAM,QAAQ,GAAa;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI;SAClB,CAAC;QAEF,IAAI,YAAY,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YACpE,QAAQ,CAAC,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,SAAS,GAAG,EAAE,CAAC;QAC1B,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,wBAAwB,EAAE,CAAC,QAAkB,EAAE,MAAiB,EAAE,EAAE;QAClE,MAAM,YAAY,GAAwB,EAAE,CAAC;QAE7C,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC3D,IAAI,QAAQ,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;gBAC5C,YAAY,CAAC,SAAS,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;CACF,CAAC","sourcesContent":["import { html } from 'lit-html';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, PlayAudio } from '../../store/flow-definition';\n\nexport const play_audio: ActionConfig = {\n name: 'Play Recording',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE],\n render: (_node: Node, action: PlayAudio) => {\n return html`\n <div style=\"display: flex; align-items: center; gap: 0.3em;\">\n <temba-icon name=\"recording\" size=\"1\"></temba-icon>\n <div\n style=\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;\"\n title=\"${action.audio_url || ''}\"\n >\n ${action.audio_url || ''}\n </div>\n </div>\n `;\n },\n form: {\n audio_url: {\n type: 'text',\n label: 'Recording URL',\n required: true,\n evaluated: true\n }\n },\n layout: ['audio_url'],\n toFormData: (action: PlayAudio) => {\n return {\n uuid: action.uuid,\n audio_url: action.audio_url || ''\n };\n },\n fromFormData: (data: FormData) => {\n return {\n uuid: data.uuid,\n type: 'play_audio',\n audio_url: (data.audio_url || '').trim()\n } as PlayAudio;\n },\n localizable: ['audio_url'],\n toLocalizationFormData: (\n action: PlayAudio,\n localization: Record<string, any>\n ) => {\n const formData: FormData = {\n uuid: action.uuid\n };\n\n if (localization.audio_url && Array.isArray(localization.audio_url)) {\n formData.audio_url = localization.audio_url[0] || '';\n } else {\n formData.audio_url = '';\n }\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: PlayAudio) => {\n const localization: Record<string, any> = {};\n\n if (formData.audio_url && formData.audio_url.trim() !== '') {\n if (formData.audio_url !== action.audio_url) {\n localization.audio_url = [formData.audio_url];\n }\n }\n\n return localization;\n }\n};\n"]}
|
|
@@ -1,12 +1,95 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
+
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
|
2
3
|
import { ACTION_GROUPS, FlowTypes } from '../types';
|
|
4
|
+
import { renderAudioPlayer } from './audio-player';
|
|
3
5
|
export const say_msg = {
|
|
4
6
|
name: 'Say Message',
|
|
5
7
|
group: ACTION_GROUPS.send,
|
|
6
8
|
flowTypes: [FlowTypes.VOICE],
|
|
7
|
-
render: (_node,
|
|
8
|
-
|
|
9
|
-
return html
|
|
9
|
+
render: (_node, action) => {
|
|
10
|
+
const text = (action.text || '').replace(/\n/g, '<br>');
|
|
11
|
+
return html `
|
|
12
|
+
${unsafeHTML(text)}
|
|
13
|
+
${action.audio_url
|
|
14
|
+
? html `<div style="margin-top: 0.5em;">
|
|
15
|
+
${renderAudioPlayer(action.audio_url)}
|
|
16
|
+
</div>`
|
|
17
|
+
: null}
|
|
18
|
+
`;
|
|
19
|
+
},
|
|
20
|
+
form: {
|
|
21
|
+
text: {
|
|
22
|
+
type: 'textarea',
|
|
23
|
+
label: 'Message',
|
|
24
|
+
required: true,
|
|
25
|
+
evaluated: true,
|
|
26
|
+
placeholder: 'Enter message to speak...',
|
|
27
|
+
minHeight: 80
|
|
28
|
+
},
|
|
29
|
+
audio_url: {
|
|
30
|
+
type: 'media',
|
|
31
|
+
label: 'Recording',
|
|
32
|
+
required: false,
|
|
33
|
+
accept: 'audio/*',
|
|
34
|
+
optionalLink: 'Add a recording'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
layout: ['text', 'audio_url'],
|
|
38
|
+
toFormData: (action) => {
|
|
39
|
+
return {
|
|
40
|
+
uuid: action.uuid,
|
|
41
|
+
text: action.text || '',
|
|
42
|
+
audio_url: action.audio_url || ''
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
fromFormData: (data) => {
|
|
46
|
+
const result = {
|
|
47
|
+
uuid: data.uuid,
|
|
48
|
+
type: 'say_msg',
|
|
49
|
+
text: data.text || ''
|
|
50
|
+
};
|
|
51
|
+
if (data.audio_url && data.audio_url.trim() !== '') {
|
|
52
|
+
result.audio_url = data.audio_url.trim();
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
},
|
|
56
|
+
sanitize: (formData) => {
|
|
57
|
+
if (formData.text && typeof formData.text === 'string') {
|
|
58
|
+
formData.text = formData.text.trim();
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
localizable: ['text', 'audio_url'],
|
|
62
|
+
toLocalizationFormData: (action, localization) => {
|
|
63
|
+
const formData = {
|
|
64
|
+
uuid: action.uuid
|
|
65
|
+
};
|
|
66
|
+
if (localization.text && Array.isArray(localization.text)) {
|
|
67
|
+
formData.text = localization.text[0] || '';
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
formData.text = '';
|
|
71
|
+
}
|
|
72
|
+
if (localization.audio_url && Array.isArray(localization.audio_url)) {
|
|
73
|
+
formData.audio_url = localization.audio_url[0] || '';
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
formData.audio_url = '';
|
|
77
|
+
}
|
|
78
|
+
return formData;
|
|
79
|
+
},
|
|
80
|
+
fromLocalizationFormData: (formData, action) => {
|
|
81
|
+
const localization = {};
|
|
82
|
+
if (formData.text && formData.text.trim() !== '') {
|
|
83
|
+
if (formData.text !== action.text) {
|
|
84
|
+
localization.text = [formData.text];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (formData.audio_url && formData.audio_url.trim() !== '') {
|
|
88
|
+
if (formData.audio_url !== action.audio_url) {
|
|
89
|
+
localization.audio_url = [formData.audio_url];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return localization;
|
|
10
93
|
}
|
|
11
94
|
};
|
|
12
95
|
//# sourceMappingURL=say_msg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"say_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/say_msg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAgB,aAAa,
|
|
1
|
+
{"version":3,"file":"say_msg.js","sourceRoot":"","sources":["../../../../src/flow/actions/say_msg.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit-html';\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js';\nimport { ActionConfig, ACTION_GROUPS, FormData, FlowTypes } from '../types';\nimport { Node, SayMsg } from '../../store/flow-definition';\nimport { renderAudioPlayer } from './audio-player';\n\nexport const say_msg: ActionConfig = {\n name: 'Say Message',\n group: ACTION_GROUPS.send,\n flowTypes: [FlowTypes.VOICE],\n render: (_node: Node, action: SayMsg) => {\n const text = (action.text || '').replace(/\\n/g, '<br>');\n return html`\n ${unsafeHTML(text)}\n ${action.audio_url\n ? html`<div style=\"margin-top: 0.5em;\">\n ${renderAudioPlayer(action.audio_url)}\n </div>`\n : null}\n `;\n },\n form: {\n text: {\n type: 'textarea',\n label: 'Message',\n required: true,\n evaluated: true,\n placeholder: 'Enter message to speak...',\n minHeight: 80\n },\n audio_url: {\n type: 'media',\n label: 'Recording',\n required: false,\n accept: 'audio/*',\n optionalLink: 'Add a recording'\n }\n },\n layout: ['text', 'audio_url'],\n toFormData: (action: SayMsg) => {\n return {\n uuid: action.uuid,\n text: action.text || '',\n audio_url: action.audio_url || ''\n };\n },\n fromFormData: (data: FormData) => {\n const result: any = {\n uuid: data.uuid,\n type: 'say_msg',\n text: data.text || ''\n };\n if (data.audio_url && data.audio_url.trim() !== '') {\n result.audio_url = data.audio_url.trim();\n }\n return result as SayMsg;\n },\n sanitize: (formData: FormData): void => {\n if (formData.text && typeof formData.text === 'string') {\n formData.text = formData.text.trim();\n }\n },\n localizable: ['text', 'audio_url'],\n toLocalizationFormData: (\n action: SayMsg,\n localization: Record<string, any>\n ) => {\n const formData: FormData = {\n uuid: action.uuid\n };\n\n if (localization.text && Array.isArray(localization.text)) {\n formData.text = localization.text[0] || '';\n } else {\n formData.text = '';\n }\n\n if (localization.audio_url && Array.isArray(localization.audio_url)) {\n formData.audio_url = localization.audio_url[0] || '';\n } else {\n formData.audio_url = '';\n }\n\n return formData;\n },\n fromLocalizationFormData: (formData: FormData, action: SayMsg) => {\n const localization: Record<string, any> = {};\n\n if (formData.text && formData.text.trim() !== '') {\n if (formData.text !== action.text) {\n localization.text = [formData.text];\n }\n }\n\n if (formData.audio_url && formData.audio_url.trim() !== '') {\n if (formData.audio_url !== action.audio_url) {\n localization.audio_url = [formData.audio_url];\n }\n }\n\n return localization;\n }\n};\n"]}
|
|
@@ -18,6 +18,7 @@ import { remove_contact_groups } from './actions/remove_contact_groups';
|
|
|
18
18
|
import { request_optin } from './actions/request_optin';
|
|
19
19
|
import { say_msg } from './actions/say_msg';
|
|
20
20
|
import { play_audio } from './actions/play_audio';
|
|
21
|
+
import { enter_flow } from './actions/enter_flow';
|
|
21
22
|
// Import all node configurations
|
|
22
23
|
import { execute_actions } from './nodes/execute_actions';
|
|
23
24
|
import { split_by_airtime } from './nodes/split_by_airtime';
|
|
@@ -28,11 +29,14 @@ import { split_by_random } from './nodes/split_by_random';
|
|
|
28
29
|
import { split_by_run_result } from './nodes/split_by_run_result';
|
|
29
30
|
import { split_by_scheme } from './nodes/split_by_scheme';
|
|
30
31
|
import { split_by_subflow } from './nodes/split_by_subflow';
|
|
32
|
+
import { terminal } from './nodes/terminal';
|
|
31
33
|
import { split_by_ticket } from './nodes/split_by_ticket';
|
|
32
34
|
import { split_by_webhook } from './nodes/split_by_webhook';
|
|
33
35
|
import { split_by_resthook } from './nodes/split_by_resthook';
|
|
34
36
|
import { split_by_llm } from './nodes/split_by_llm';
|
|
35
37
|
import { split_by_llm_categorize } from './nodes/split_by_llm_categorize';
|
|
38
|
+
import { wait_for_audio } from './nodes/wait_for_audio';
|
|
39
|
+
import { wait_for_dial } from './nodes/wait_for_dial';
|
|
36
40
|
import { wait_for_digits } from './nodes/wait_for_digits';
|
|
37
41
|
import { wait_for_menu } from './nodes/wait_for_menu';
|
|
38
42
|
import { wait_for_response } from './nodes/wait_for_response';
|
|
@@ -53,7 +57,8 @@ export const ACTION_CONFIG = registerConfigWithAliases({
|
|
|
53
57
|
set_contact_status,
|
|
54
58
|
add_contact_urn,
|
|
55
59
|
add_input_labels,
|
|
56
|
-
request_optin
|
|
60
|
+
request_optin,
|
|
61
|
+
enter_flow
|
|
57
62
|
});
|
|
58
63
|
// Helper to register a config and its aliases
|
|
59
64
|
function registerConfigWithAliases(config) {
|
|
@@ -82,9 +87,12 @@ export const NODE_CONFIG = registerConfigWithAliases({
|
|
|
82
87
|
split_by_ticket,
|
|
83
88
|
split_by_webhook,
|
|
84
89
|
split_by_resthook,
|
|
85
|
-
wait_for_digits,
|
|
86
90
|
wait_for_menu,
|
|
91
|
+
wait_for_digits,
|
|
92
|
+
wait_for_audio,
|
|
93
|
+
wait_for_dial,
|
|
87
94
|
wait_for_response,
|
|
88
|
-
split_by_airtime
|
|
95
|
+
split_by_airtime,
|
|
96
|
+
terminal // Temporary: legacy support for terminal nodes (see AppState.ts)
|
|
89
97
|
});
|
|
90
98
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/flow/config.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,cAAc,SAAS,CAAC;AAGxB,mCAAmC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,iCAAiC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,CAAC,MAAM,aAAa,GAEtB,yBAAyB,CAAC;IAC5B,OAAO;IACP,UAAU;IACV,iBAAiB;IACjB,cAAc;IACd,cAAc;IACd,QAAQ;IACR,UAAU;IACV,aAAa;IACb,gBAAgB;IAChB,kBAAkB;IAClB,qBAAqB;IACrB,mBAAmB;IACnB,oBAAoB;IACpB,kBAAkB;IAClB,eAAe;IACf,gBAAgB;IAChB,aAAa;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/flow/config.ts"],"names":[],"mappings":"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","sourcesContent":["// Re-export all types and utilities\nexport * from './types';\nimport type { ActionConfig, NodeConfig } from './types';\n\n// Import all action configurations\nimport { add_input_labels } from './actions/add_input_labels';\nimport { add_contact_urn } from './actions/add_contact_urn';\nimport { set_contact_field } from './actions/set_contact_field';\nimport { set_contact_channel } from './actions/set_contact_channel';\nimport { set_contact_language } from './actions/set_contact_language';\nimport { set_contact_status } from './actions/set_contact_status';\nimport { send_broadcast } from './actions/send_broadcast';\nimport { set_run_result } from './actions/set_run_result';\nimport { send_msg } from './actions/send_msg';\nimport { send_email } from './actions/send_email';\nimport { start_session } from './actions/start_session';\nimport { set_contact_name } from './actions/set_contact_name';\nimport { add_contact_groups } from './actions/add_contact_groups';\nimport { remove_contact_groups } from './actions/remove_contact_groups';\nimport { request_optin } from './actions/request_optin';\nimport { say_msg } from './actions/say_msg';\nimport { play_audio } from './actions/play_audio';\nimport { enter_flow } from './actions/enter_flow';\n\n// Import all node configurations\nimport { execute_actions } from './nodes/execute_actions';\nimport { split_by_airtime } from './nodes/split_by_airtime';\nimport { split_by_contact_field } from './nodes/split_by_contact_field';\nimport { split_by_expression } from './nodes/split_by_expression';\nimport { split_by_groups } from './nodes/split_by_groups';\nimport { split_by_random } from './nodes/split_by_random';\nimport { split_by_run_result } from './nodes/split_by_run_result';\nimport { split_by_scheme } from './nodes/split_by_scheme';\nimport { split_by_subflow } from './nodes/split_by_subflow';\nimport { terminal } from './nodes/terminal';\nimport { split_by_ticket } from './nodes/split_by_ticket';\nimport { split_by_webhook } from './nodes/split_by_webhook';\nimport { split_by_resthook } from './nodes/split_by_resthook';\nimport { split_by_llm } from './nodes/split_by_llm';\nimport { split_by_llm_categorize } from './nodes/split_by_llm_categorize';\nimport { wait_for_audio } from './nodes/wait_for_audio';\nimport { wait_for_dial } from './nodes/wait_for_dial';\nimport { wait_for_digits } from './nodes/wait_for_digits';\nimport { wait_for_menu } from './nodes/wait_for_menu';\nimport { wait_for_response } from './nodes/wait_for_response';\n\nexport const ACTION_CONFIG: {\n [key: string]: ActionConfig;\n} = registerConfigWithAliases({\n say_msg,\n play_audio,\n set_contact_field,\n send_broadcast,\n set_run_result,\n send_msg,\n send_email,\n start_session,\n set_contact_name,\n add_contact_groups,\n remove_contact_groups,\n set_contact_channel,\n set_contact_language,\n set_contact_status,\n add_contact_urn,\n add_input_labels,\n request_optin,\n enter_flow\n});\n\n// Helper to register a config and its aliases\nfunction registerConfigWithAliases<T extends NodeConfig | ActionConfig>(\n config: Record<string, T>\n): Record<string, T> {\n const result = { ...config };\n\n // Register aliases for each config\n Object.values(config).forEach((cfg) => {\n if (cfg.aliases) {\n cfg.aliases.forEach((alias) => {\n result[alias] = cfg;\n });\n }\n });\n\n return result;\n}\n\nexport const NODE_CONFIG: {\n [key: string]: NodeConfig;\n} = registerConfigWithAliases({\n execute_actions,\n split_by_contact_field,\n split_by_expression,\n split_by_groups,\n split_by_llm,\n split_by_llm_categorize,\n split_by_random,\n split_by_run_result,\n split_by_scheme,\n split_by_subflow,\n split_by_ticket,\n split_by_webhook,\n split_by_resthook,\n wait_for_menu,\n wait_for_digits,\n wait_for_audio,\n wait_for_dial,\n wait_for_response,\n split_by_airtime,\n terminal // Temporary: legacy support for terminal nodes (see AppState.ts)\n});\n"]}
|