@adatechnology/conversations-ui 0.0.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 (65) hide show
  1. package/dist/channel/index.d.ts +2 -0
  2. package/dist/channel/index.js +0 -0
  3. package/dist/chunk-ZDURDZTM.js +199 -0
  4. package/dist/flows/index.d.ts +246 -0
  5. package/dist/flows/index.js +1110 -0
  6. package/dist/index.d.ts +542 -0
  7. package/dist/index.js +2077 -0
  8. package/dist/styles.css +11 -0
  9. package/dist/styles.d.ts +2 -0
  10. package/package.json +52 -0
  11. package/src/AudioPlayer.tsx +103 -0
  12. package/src/Avatar.tsx +63 -0
  13. package/src/ConversationListItem.tsx +147 -0
  14. package/src/ConversationLocalesProvider.tsx +76 -0
  15. package/src/DateDivider.tsx +32 -0
  16. package/src/EmojiPicker.tsx +77 -0
  17. package/src/FileIcon.tsx +33 -0
  18. package/src/Lightbox.tsx +17 -0
  19. package/src/MediaRenderer.tsx +158 -0
  20. package/src/MessageBubble.tsx +129 -0
  21. package/src/MessageComposer.tsx +211 -0
  22. package/src/MessageTail.tsx +18 -0
  23. package/src/MessageText.tsx +42 -0
  24. package/src/MessageTimestamp.tsx +22 -0
  25. package/src/SimpleEmojiPicker.tsx +72 -0
  26. package/src/StatusTicks.tsx +39 -0
  27. package/src/Toast.tsx +140 -0
  28. package/src/Wallpaper.tsx +13 -0
  29. package/src/WhatsAppMessageEditor.tsx +142 -0
  30. package/src/channel/index.ts +1 -0
  31. package/src/conversations/index.ts +1 -0
  32. package/src/flows/FlowGroupFrame.tsx +21 -0
  33. package/src/flows/FlowGroupHeader.tsx +31 -0
  34. package/src/flows/FlowMapCanvas.tsx +76 -0
  35. package/src/flows/FlowMapNode.tsx +39 -0
  36. package/src/flows/FlowNodeCard.tsx +150 -0
  37. package/src/flows/FlowNodePanel.tsx +356 -0
  38. package/src/flows/FlowPalette.tsx +137 -0
  39. package/src/flows/FlowPortalNode.tsx +30 -0
  40. package/src/flows/FlowWhatsAppPreview.tsx +67 -0
  41. package/src/flows/flowGraph.ts +391 -0
  42. package/src/flows/index.ts +55 -0
  43. package/src/flows/labels.ts +187 -0
  44. package/src/hooks/useAsyncResource.ts +38 -0
  45. package/src/hooks/useConversationContext.ts +23 -0
  46. package/src/hooks/useConversationDocuments.ts +33 -0
  47. package/src/hooks/useConversationList.ts +32 -0
  48. package/src/hooks/useConversationMessages.ts +64 -0
  49. package/src/hooks/useConversationRealtime.ts +50 -0
  50. package/src/index.ts +87 -0
  51. package/src/lib/format.ts +32 -0
  52. package/src/lib/phone.ts +26 -0
  53. package/src/lib/whatsapp-formatting.tsx +215 -0
  54. package/src/providers/ConversationsProvider.tsx +29 -0
  55. package/src/providers/types.ts +54 -0
  56. package/src/settings/TopicsForm.tsx +109 -0
  57. package/src/settings/WelcomeFarewellForm.tsx +118 -0
  58. package/src/settings/WhatsAppCreateTemplateForm.tsx +309 -0
  59. package/src/settings/WhatsAppTemplateSettingsForm.tsx +264 -0
  60. package/src/styles.css +21 -0
  61. package/src/theme.ts +30 -0
  62. package/src/types.ts +44 -0
  63. package/src/useDarkMode.ts +48 -0
  64. package/src/useWaitingNotifications.ts +64 -0
  65. package/tsconfig.json +15 -0
@@ -0,0 +1,2 @@
1
+
2
+ export { }
File without changes
@@ -0,0 +1,199 @@
1
+ // src/lib/whatsapp-formatting.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ var MONOSPACE_REGEX = /```([\s\S]*?)```/g;
4
+ function tokenize(text) {
5
+ const tokens = [];
6
+ let remaining = text;
7
+ const codeBlocks = [];
8
+ remaining = remaining.replace(MONOSPACE_REGEX, (_match, content, offset) => {
9
+ codeBlocks.push({ index: offset, content });
10
+ return "\0".repeat(_match.length);
11
+ });
12
+ let codeBlockIndex = 0;
13
+ let i = 0;
14
+ let buffer = "";
15
+ while (i < remaining.length) {
16
+ if (remaining[i] === "\0") {
17
+ if (buffer) {
18
+ tokens.push(...parseInlineTokens(buffer));
19
+ buffer = "";
20
+ }
21
+ const cb = codeBlocks[codeBlockIndex++];
22
+ tokens.push({ type: "codeblock", content: cb.content });
23
+ const skip = "```" + cb.content + "```";
24
+ i += skip.length;
25
+ continue;
26
+ }
27
+ const inlineMatch = remaining.slice(i).match(/^`([^`]+)`/);
28
+ if (inlineMatch && inlineMatch.index === 0) {
29
+ if (buffer) {
30
+ tokens.push(...parseInlineTokens(buffer));
31
+ buffer = "";
32
+ }
33
+ tokens.push({ type: "monospace", content: inlineMatch[1] });
34
+ i += inlineMatch[0].length;
35
+ continue;
36
+ }
37
+ buffer += remaining[i];
38
+ i++;
39
+ }
40
+ if (buffer) {
41
+ tokens.push(...parseInlineTokens(buffer));
42
+ }
43
+ return tokens;
44
+ }
45
+ function parseInlineTokens(text) {
46
+ const result = [];
47
+ let remaining = text;
48
+ while (remaining.length > 0) {
49
+ const boldMatch = remaining.match(/^\*([^*]+)\*/);
50
+ if (boldMatch) {
51
+ result.push({ type: "bold", content: boldMatch[1] });
52
+ remaining = remaining.slice(boldMatch[0].length);
53
+ continue;
54
+ }
55
+ const italicMatch = remaining.match(/^_([^_]+)_/);
56
+ if (italicMatch) {
57
+ result.push({ type: "italic", content: italicMatch[1] });
58
+ remaining = remaining.slice(italicMatch[0].length);
59
+ continue;
60
+ }
61
+ const strikeMatch = remaining.match(/^~([^~]+)~/);
62
+ if (strikeMatch) {
63
+ result.push({ type: "strikethrough", content: strikeMatch[1] });
64
+ remaining = remaining.slice(strikeMatch[0].length);
65
+ continue;
66
+ }
67
+ const nextSpecial = remaining.search(/[*_~`]/);
68
+ if (nextSpecial === -1) {
69
+ if (remaining) result.push({ type: "text", content: remaining });
70
+ break;
71
+ }
72
+ if (nextSpecial > 0) {
73
+ result.push({ type: "text", content: remaining.slice(0, nextSpecial) });
74
+ }
75
+ remaining = remaining.slice(nextSpecial);
76
+ }
77
+ return result;
78
+ }
79
+ function parseWhatsAppFormatting(text) {
80
+ const tokens = tokenize(text);
81
+ return tokens.map((token, index) => {
82
+ switch (token.type) {
83
+ case "bold":
84
+ return /* @__PURE__ */ jsx("strong", { children: token.content }, index);
85
+ case "italic":
86
+ return /* @__PURE__ */ jsx("em", { children: token.content }, index);
87
+ case "strikethrough":
88
+ return /* @__PURE__ */ jsx("del", { children: token.content }, index);
89
+ case "monospace":
90
+ return /* @__PURE__ */ jsx("code", { className: "bg-gray-100 px-1 py-0.5 rounded text-sm", children: token.content }, index);
91
+ case "codeblock":
92
+ return /* @__PURE__ */ jsx("pre", { className: "bg-gray-100 p-2 rounded text-sm overflow-x-auto my-1", children: /* @__PURE__ */ jsx("code", { children: token.content }) }, index);
93
+ default:
94
+ return /* @__PURE__ */ jsx("span", { children: token.content }, index);
95
+ }
96
+ });
97
+ }
98
+ function escapeHtml(text) {
99
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
100
+ }
101
+ function unescapeHtml(text) {
102
+ return text.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&nbsp;/g, " ");
103
+ }
104
+ var CODE_TOKEN_MARK = String.fromCharCode(57344);
105
+ var CODE_TOKEN_REGEX = new RegExp(`${CODE_TOKEN_MARK}(\\d+)${CODE_TOKEN_MARK}`, "g");
106
+ function waToHTML(text) {
107
+ if (!text) return "";
108
+ const codeTokens = [];
109
+ let working = text.replace(/```([\s\S]*?)```/g, (_match, content) => {
110
+ codeTokens.push({ type: "block", content });
111
+ return `${CODE_TOKEN_MARK}${codeTokens.length - 1}${CODE_TOKEN_MARK}`;
112
+ });
113
+ working = working.replace(/`([^`\n]+)`/g, (_match, content) => {
114
+ codeTokens.push({ type: "inline", content });
115
+ return `${CODE_TOKEN_MARK}${codeTokens.length - 1}${CODE_TOKEN_MARK}`;
116
+ });
117
+ let html = escapeHtml(working);
118
+ html = html.replace(/\*([^*\n]+)\*/g, "<strong>$1</strong>");
119
+ html = html.replace(/_([^_\n]+)_/g, "<em>$1</em>");
120
+ html = html.replace(/~([^~\n]+)~/g, "<del>$1</del>");
121
+ html = html.replace(/\n/g, "<br>");
122
+ html = html.replace(CODE_TOKEN_REGEX, (_match, indexStr) => {
123
+ const token = codeTokens[Number(indexStr)];
124
+ const escaped = escapeHtml(token.content);
125
+ if (token.type === "block") {
126
+ return `<code data-wa="block" class="block bg-black/5 dark:bg-white/10 rounded px-1.5 py-0.5 font-mono text-sm whitespace-pre-wrap">${escaped.replace(/\n/g, "<br>")}</code>`;
127
+ }
128
+ return `<code data-wa="inline" class="bg-black/5 dark:bg-white/10 rounded px-0.5 font-mono text-sm">${escaped}</code>`;
129
+ });
130
+ return html;
131
+ }
132
+ function htmlToWA(html) {
133
+ if (!html) return "";
134
+ let text = html;
135
+ text = text.replace(/<code data-wa="block"[^>]*>([\s\S]*?)<\/code>/gi, (_match, inner) => `\`\`\`${unescapeHtml(inner.replace(/<br\s*\/?>/gi, "\n"))}\`\`\``);
136
+ text = text.replace(/<code data-wa="inline"[^>]*>([\s\S]*?)<\/code>/gi, (_match, inner) => `\`${unescapeHtml(inner)}\``);
137
+ text = text.replace(/<br\s*\/?>/gi, "\n").replace(/<div>/gi, "\n").replace(/<\/div>/gi, "").replace(/<\/p>/gi, "\n").replace(/<p[^>]*>/gi, "");
138
+ text = text.replace(/<strong>(.*?)<\/strong>/gi, "*$1*");
139
+ text = text.replace(/<b>(.*?)<\/b>/gi, "*$1*");
140
+ text = text.replace(/<em>(.*?)<\/em>/gi, "_$1_");
141
+ text = text.replace(/<i>(.*?)<\/i>/gi, "_$1_");
142
+ text = text.replace(/<del>(.*?)<\/del>/gi, "~$1~");
143
+ text = text.replace(/<s>(.*?)<\/s>/gi, "~$1~");
144
+ text = text.replace(/<code[^>]*>(.*?)<\/code>/gi, "`$1`");
145
+ text = text.replace(/<[^>]+>/g, "");
146
+ text = unescapeHtml(text);
147
+ return text.trim();
148
+ }
149
+ function waToHTMLInline(text) {
150
+ if (!text) return "";
151
+ return escapeHtml(text).replace(/\*([^*\n]+)\*/g, "<strong>$1</strong>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~([^~\n]+)~/g, "<del>$1</del>").replace(/`([^`\n]+)`/g, "<code>$1</code>");
152
+ }
153
+
154
+ // src/useDarkMode.ts
155
+ import { useState, useEffect, useCallback } from "react";
156
+ var STORAGE_KEY = "conversations-ui-dark-mode";
157
+ function getInitialDark() {
158
+ if (typeof window === "undefined") return false;
159
+ const stored = localStorage.getItem(STORAGE_KEY);
160
+ if (stored !== null) {
161
+ return stored === "true";
162
+ }
163
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
164
+ }
165
+ function useDarkMode() {
166
+ const [isDark, setIsDark] = useState(getInitialDark);
167
+ useEffect(() => {
168
+ const root = document.documentElement;
169
+ if (isDark) {
170
+ root.classList.add("dark");
171
+ } else {
172
+ root.classList.remove("dark");
173
+ }
174
+ localStorage.setItem(STORAGE_KEY, String(isDark));
175
+ }, [isDark]);
176
+ useEffect(() => {
177
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
178
+ const handleChange = (event) => {
179
+ const stored = localStorage.getItem(STORAGE_KEY);
180
+ if (stored === null) {
181
+ setIsDark(event.matches);
182
+ }
183
+ };
184
+ mediaQuery.addEventListener("change", handleChange);
185
+ return () => mediaQuery.removeEventListener("change", handleChange);
186
+ }, []);
187
+ const toggle = useCallback(() => {
188
+ setIsDark((prev) => !prev);
189
+ }, []);
190
+ return { isDark, toggle };
191
+ }
192
+
193
+ export {
194
+ parseWhatsAppFormatting,
195
+ waToHTML,
196
+ htmlToWA,
197
+ waToHTMLInline,
198
+ useDarkMode
199
+ };
@@ -0,0 +1,246 @@
1
+ import * as react from 'react';
2
+ import { NodeProps } from '@xyflow/react';
3
+ import { LucideIcon } from 'lucide-react';
4
+ import { FlowConditionOperator, FlowGraphData, FlowNodeData, FlowNodeType, FlowQuestionType, FlowActionKind } from '@adatechnology/meta-whatsapp-contracts';
5
+ export { FlowActionKind, FlowConditionOperator, FlowGraphData, FlowNodeData, FlowNodeNext, FlowNodeType, FlowQuestionType } from '@adatechnology/meta-whatsapp-contracts';
6
+
7
+ declare const CONDITION_OPERATORS: FlowConditionOperator[];
8
+ declare const BUILT_IN_ACTION_KINDS: {
9
+ readonly HANDOFF: "handoff";
10
+ readonly RATE_LIMITED_HANDOFF: "rate_limited_handoff";
11
+ readonly SEND_PRODUCT_LIST: "send_product_list";
12
+ };
13
+ declare const CROSS_FLOW_PREFIX = "flow:";
14
+ declare const isCrossFlowTarget: (target: string) => boolean;
15
+ declare const crossFlowKey: (target: string) => string;
16
+ declare const NODE_CARD_WIDTH = 240;
17
+ declare function estimateNodeHeight(node: FlowNodeData): number;
18
+ declare const WHATSAPP_LIMITS: {
19
+ readonly MAX_BUTTONS: 3;
20
+ readonly MAX_LIST_ROWS: 10;
21
+ readonly BUTTON_TITLE_MAX: 20;
22
+ readonly LIST_ROW_TITLE_MAX: 24;
23
+ readonly BODY_MAX: 1024;
24
+ };
25
+ declare function rendersAsButtons(options: [string, string][] | undefined): boolean;
26
+ declare function targetsOf(node: FlowNodeData): {
27
+ target: string;
28
+ optionId?: string;
29
+ isDefault?: boolean;
30
+ }[];
31
+ type GraphIssue = {
32
+ severity: 'error' | 'warning';
33
+ nodeId?: string;
34
+ message: string;
35
+ };
36
+ declare function validateGraph(graph: FlowGraphData, issueText: {
37
+ noStart: string;
38
+ brokenRef: (from: string, to: string) => string;
39
+ choiceWithoutOptions: (id: string) => string;
40
+ duplicatedOptionId: (id: string, optionId: string) => string;
41
+ optionWithoutTarget: (id: string, optionLabel: string) => string;
42
+ tooManyOptions: (id: string, count: number) => string;
43
+ buttonTitleTooLong: (id: string, label: string) => string;
44
+ listTitleTooLong: (id: string, label: string) => string;
45
+ bodyTooLong: (id: string) => string;
46
+ unreachable: (id: string) => string;
47
+ deadEndQuestion: (id: string) => string;
48
+ conditionIncomplete: (id: string) => string;
49
+ conditionBranchMissing: (id: string, branch: string) => string;
50
+ }): GraphIssue[];
51
+ declare function computeAutoLayout(graph: FlowGraphData): Record<string, {
52
+ x: number;
53
+ y: number;
54
+ }>;
55
+ declare function crossFlowTargetsOf(graph: FlowGraphData): string[];
56
+ declare function computeFlowMapLayout(graphs: Record<string, FlowGraphData>, rootKey: string): Record<string, {
57
+ x: number;
58
+ y: number;
59
+ }>;
60
+ type CollectionChain = {
61
+ nodeIds: string[];
62
+ actionNodeId: string;
63
+ };
64
+ declare function findCollectionChains(graph: FlowGraphData): CollectionChain[];
65
+ declare function slugifyNodeId(label: string, existing: Set<string>): string;
66
+
67
+ interface FlowEditorLabels {
68
+ legend: Record<FlowNodeType, string>;
69
+ startNodeTooltip: string;
70
+ liveCountTooltip: (count: number) => string;
71
+ edgeFallbackLabel: string;
72
+ actionKindLabels: Record<string, string>;
73
+ conditionOperatorLabels: Record<FlowConditionOperator, string>;
74
+ questionTypeLabels: Record<FlowQuestionType, string>;
75
+ nodePanel: {
76
+ title: string;
77
+ contextKey: string;
78
+ questionType: string;
79
+ question: string;
80
+ options: string;
81
+ addOption: string;
82
+ optionId: string;
83
+ optionLabel: string;
84
+ next: string;
85
+ nextHint: string;
86
+ nextRowLabel: string;
87
+ otherFlowsGroup: string;
88
+ nextByAnswer: (id: string) => string;
89
+ nextDefault: string;
90
+ save: string;
91
+ cancel: string;
92
+ fixedLogicNotice: string;
93
+ actionNotice: string;
94
+ preview: string;
95
+ previewPlaceholder: string;
96
+ previewEmptyBody: string;
97
+ previewEmptyOption: string;
98
+ previewListButton: string;
99
+ previewModeButtons: string;
100
+ previewModeList: string;
101
+ delete: string;
102
+ deleteConfirm: string;
103
+ directMessage: string;
104
+ fallbackMessage: string;
105
+ conditionNotice: string;
106
+ conditionVariable: string;
107
+ conditionOperator: string;
108
+ conditionValue: string;
109
+ conditionTrue: string;
110
+ conditionFalse: string;
111
+ conditionVariableMissing: string;
112
+ };
113
+ palette: {
114
+ title: string;
115
+ question: string;
116
+ decision: string;
117
+ condition: string;
118
+ conditionHint: string;
119
+ action: string;
120
+ };
121
+ flowMap: {
122
+ nodeCount: (count: number) => string;
123
+ openFlow: string;
124
+ };
125
+ flowGroup: {
126
+ focus: string;
127
+ close: string;
128
+ };
129
+ crossFlowPortal: {
130
+ tooltip: string;
131
+ goesTo: (label: string) => string;
132
+ };
133
+ }
134
+ declare const DEFAULT_FLOW_EDITOR_LABELS: FlowEditorLabels;
135
+ declare function mergeFlowEditorLabels(override?: Partial<FlowEditorLabels>): FlowEditorLabels;
136
+
137
+ declare function nodeLabel(node: FlowNodeData | undefined, labels: FlowEditorLabels): string;
138
+ type FlowNodeCardData = {
139
+ node: FlowNodeData;
140
+ liveCount: number;
141
+ isStart: boolean;
142
+ isSelected: boolean;
143
+ issues: GraphIssue[];
144
+ labels: FlowEditorLabels;
145
+ actionKindIcons?: Record<string, LucideIcon>;
146
+ onSelect: (id: string) => void;
147
+ };
148
+ declare function FlowNodeCard({ data }: NodeProps): react.JSX.Element;
149
+ declare const flowNodeTypes: {
150
+ flowNode: typeof FlowNodeCard;
151
+ };
152
+
153
+ type FlowMapNodeData = {
154
+ label: string;
155
+ nodeCount: number;
156
+ isRoot: boolean;
157
+ labels: FlowEditorLabels;
158
+ onOpen: () => void;
159
+ };
160
+ declare function FlowMapNode({ data }: NodeProps): react.JSX.Element;
161
+ declare const flowMapNodeTypes: {
162
+ flowMapNode: typeof FlowMapNode;
163
+ };
164
+
165
+ interface FlowMapCanvasProps {
166
+ graphs: Record<string, FlowGraphData>;
167
+ rootKey: string;
168
+ onOpenFlow: (key: string) => void;
169
+ labels?: Partial<FlowEditorLabels>;
170
+ }
171
+ declare function FlowMapCanvas({ graphs, rootKey, onOpenFlow, labels: labelsOverride }: FlowMapCanvasProps): react.JSX.Element;
172
+
173
+ type FlowGroupFrameData = {
174
+ label: string;
175
+ };
176
+ declare function FlowGroupFrame({ data }: NodeProps): react.JSX.Element;
177
+ declare const flowGroupFrameNodeTypes: {
178
+ flowGroupFrame: typeof FlowGroupFrame;
179
+ };
180
+
181
+ type FlowGroupHeaderData = {
182
+ label: string;
183
+ labels?: FlowEditorLabels;
184
+ onFocus: () => void;
185
+ onClose: () => void;
186
+ };
187
+ declare function FlowGroupHeader({ data }: NodeProps): react.JSX.Element;
188
+ declare const flowGroupHeaderNodeTypes: {
189
+ flowGroupHeader: typeof FlowGroupHeader;
190
+ };
191
+
192
+ type FlowPortalNodeData = {
193
+ label: string;
194
+ labels?: FlowEditorLabels;
195
+ onNavigate: () => void;
196
+ };
197
+ declare function FlowPortalNode({ data }: NodeProps): react.JSX.Element;
198
+ declare const flowPortalNodeTypes: {
199
+ flowPortal: typeof FlowPortalNode;
200
+ };
201
+
202
+ type NewNodeSpec = {
203
+ kind: 'question';
204
+ questionType: FlowQuestionType;
205
+ } | {
206
+ kind: 'decision';
207
+ } | {
208
+ kind: 'condition';
209
+ } | {
210
+ kind: 'action';
211
+ actionKind: FlowActionKind;
212
+ };
213
+ interface FlowPaletteActionOption {
214
+ actionKind: FlowActionKind;
215
+ label: string;
216
+ }
217
+ interface FlowPaletteProps {
218
+ onAdd: (spec: NewNodeSpec) => void;
219
+ labels?: Partial<FlowEditorLabels>;
220
+ actionOptions?: FlowPaletteActionOption[];
221
+ }
222
+ declare function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }: FlowPaletteProps): react.JSX.Element;
223
+
224
+ interface FlowNodePanelProps {
225
+ graph: FlowGraphData;
226
+ node: FlowNodeData;
227
+ issues: GraphIssue[];
228
+ otherFlows: {
229
+ key: string;
230
+ label: string;
231
+ }[];
232
+ onClose: () => void;
233
+ onChange: (updated: FlowNodeData) => void;
234
+ onDelete: (nodeId: string) => void;
235
+ labels?: Partial<FlowEditorLabels>;
236
+ }
237
+ declare function FlowNodePanel({ graph, node, issues, otherFlows, onClose, onChange, onDelete, labels: labelsOverride, }: FlowNodePanelProps): react.JSX.Element;
238
+
239
+ interface FlowWhatsAppPreviewProps {
240
+ body: string;
241
+ options?: [string, string][];
242
+ labels?: FlowEditorLabels['nodePanel'];
243
+ }
244
+ declare function FlowWhatsAppPreview({ body, options, labels }: FlowWhatsAppPreviewProps): react.JSX.Element;
245
+
246
+ export { BUILT_IN_ACTION_KINDS, CONDITION_OPERATORS, CROSS_FLOW_PREFIX, type CollectionChain, DEFAULT_FLOW_EDITOR_LABELS, type FlowEditorLabels, FlowGroupFrame, type FlowGroupFrameData, FlowGroupHeader, type FlowGroupHeaderData, FlowMapCanvas, type FlowMapCanvasProps, FlowMapNode, type FlowMapNodeData, FlowNodeCard, type FlowNodeCardData, FlowNodePanel, type FlowNodePanelProps, FlowPalette, type FlowPaletteActionOption, type FlowPaletteProps, FlowPortalNode, type FlowPortalNodeData, FlowWhatsAppPreview, type FlowWhatsAppPreviewProps, type GraphIssue, NODE_CARD_WIDTH, type NewNodeSpec, WHATSAPP_LIMITS, computeAutoLayout, computeFlowMapLayout, crossFlowKey, crossFlowTargetsOf, estimateNodeHeight, findCollectionChains, flowGroupFrameNodeTypes, flowGroupHeaderNodeTypes, flowMapNodeTypes, flowNodeTypes, flowPortalNodeTypes, isCrossFlowTarget, mergeFlowEditorLabels, nodeLabel, rendersAsButtons, slugifyNodeId, targetsOf, validateGraph };