@adatechnology/conversations-ui 0.1.0-rc.4 → 0.1.0-rc.41

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 (149) hide show
  1. package/dist/ConversationSimulatorPanel--5fIzXWY.d.ts +804 -0
  2. package/dist/chunk-BJNRLLDO.js +2708 -0
  3. package/dist/chunk-DKPXKQGC.js +110 -0
  4. package/dist/{chunk-OGRRHQQW.js → chunk-WCBDXZ3X.js} +68 -4
  5. package/dist/flows/index.d.ts +422 -5
  6. package/dist/flows/index.js +2389 -678
  7. package/dist/index.d.ts +1171 -42
  8. package/dist/index.js +3755 -843
  9. package/dist/preview/index.d.ts +157 -42
  10. package/dist/preview/index.js +772 -191
  11. package/dist/styles.css +893 -0
  12. package/package.json +9 -8
  13. package/src/AudioPlayer.tsx +8 -0
  14. package/src/AudioRecorderButton.test.tsx +30 -0
  15. package/src/AudioRecorderButton.tsx +248 -0
  16. package/src/AudioTranscription.test.tsx +115 -0
  17. package/src/AudioTranscription.tsx +252 -0
  18. package/src/Avatar.tsx +14 -3
  19. package/src/ConversationContextPanel.tsx +218 -44
  20. package/src/ConversationDocumentsPanel.tsx +347 -24
  21. package/src/ConversationHeader.test.tsx +66 -0
  22. package/src/ConversationHeader.tsx +163 -45
  23. package/src/ConversationListItem.tsx +19 -2
  24. package/src/ConversationLocalesProvider.tsx +28 -0
  25. package/src/ConversationRow.tsx +31 -7
  26. package/src/DarkModeToggle.test.tsx +76 -0
  27. package/src/DarkModeToggle.tsx +92 -0
  28. package/src/DocumentsLibrary.tsx +382 -0
  29. package/src/EmojiPicker.tsx +70 -55
  30. package/src/FileIcon.test.ts +83 -0
  31. package/src/FileIcon.tsx +88 -11
  32. package/src/InteractiveMessage.test.tsx +41 -0
  33. package/src/InteractiveMessage.tsx +146 -0
  34. package/src/Lightbox.tsx +18 -3
  35. package/src/MediaRenderer.tsx +92 -16
  36. package/src/MessageBubble.test.tsx +41 -0
  37. package/src/MessageBubble.tsx +75 -6
  38. package/src/MessageComposer.test.tsx +35 -0
  39. package/src/MessageComposer.tsx +155 -19
  40. package/src/RichMessageComposer.test.tsx +113 -0
  41. package/src/RichMessageComposer.tsx +551 -0
  42. package/src/SimpleEmojiPicker.tsx +5 -3
  43. package/src/StatusTicks.tsx +1 -1
  44. package/src/Toast.tsx +4 -0
  45. package/src/Tooltip.test.ts +42 -0
  46. package/src/Tooltip.tsx +167 -0
  47. package/src/Wallpaper.test.tsx +21 -0
  48. package/src/Wallpaper.tsx +67 -7
  49. package/src/WhatsAppMessageEditor.tsx +34 -7
  50. package/src/WindowExpiredNotice.tsx +12 -4
  51. package/src/audioRecorderFormat.test.ts +67 -0
  52. package/src/buildOutput.test.ts +79 -0
  53. package/src/composer.constant.ts +33 -0
  54. package/src/conversationTranscript.test.ts +57 -0
  55. package/src/conversationTranscript.ts +29 -4
  56. package/src/conversationWindow.ts +7 -5
  57. package/src/documentTypeLabel.test.ts +57 -0
  58. package/src/documents/DocumentsWorkspace.tsx +550 -0
  59. package/src/documents/index.ts +8 -0
  60. package/src/documents/labels.ts +92 -0
  61. package/src/emojiCatalog.test.ts +35 -0
  62. package/src/emojiCatalog.ts +189 -0
  63. package/src/flows/FlowConnectionEdge.tsx +104 -0
  64. package/src/flows/FlowGroupHeader.tsx +12 -2
  65. package/src/flows/FlowLegend.tsx +125 -0
  66. package/src/flows/FlowMapCanvas.tsx +15 -12
  67. package/src/flows/FlowMapNode.tsx +4 -1
  68. package/src/flows/FlowNodeCard.tsx +219 -34
  69. package/src/flows/FlowNodePanel.tsx +153 -39
  70. package/src/flows/FlowPalette.tsx +106 -69
  71. package/src/flows/FlowPortalNode.tsx +1 -1
  72. package/src/flows/FlowWhatsAppPreview.tsx +14 -3
  73. package/src/flows/FlowsWorkspace.tsx +1219 -0
  74. package/src/flows/flowCanvasModel.test.ts +456 -0
  75. package/src/flows/flowCanvasModel.ts +378 -0
  76. package/src/flows/flowEditorOps.test.ts +276 -0
  77. package/src/flows/flowEditorOps.ts +202 -0
  78. package/src/flows/flowGraph.ts +78 -53
  79. package/src/flows/index.ts +51 -2
  80. package/src/flows/labels.ts +180 -0
  81. package/src/flows/workspaceContract.test.ts +126 -0
  82. package/src/hooks/useContainerWidth.ts +35 -0
  83. package/src/hooks/useConversationActions.ts +56 -0
  84. package/src/hooks/useConversationDocuments.ts +11 -7
  85. package/src/hooks/useConversationList.ts +15 -9
  86. package/src/hooks/useConversationMessages.ts +2 -2
  87. package/src/hooks/useConversationRealtime.ts +10 -8
  88. package/src/hooks/useScrollToLatestMessage.ts +127 -0
  89. package/src/hooks/useUrlFilterState.ts +107 -0
  90. package/src/icon.constant.ts +12 -0
  91. package/src/index.ts +129 -13
  92. package/src/lib/cn.test.ts +29 -0
  93. package/src/lib/composer-formatting.test.ts +78 -0
  94. package/src/lib/composer-formatting.ts +145 -0
  95. package/src/lib/createMediaUrlResolver.ts +33 -0
  96. package/src/lib/paginated.test.ts +33 -0
  97. package/src/lib/paginated.ts +26 -0
  98. package/src/lib/whatsapp-formatting.test.tsx +37 -0
  99. package/src/lib/whatsapp-formatting.tsx +28 -3
  100. package/src/listing/index.tsx +202 -0
  101. package/src/pagination.constant.ts +10 -0
  102. package/src/preview/ConversationPreview.tsx +225 -17
  103. package/src/preview/ConversationSimulatorClient.ts +143 -0
  104. package/src/preview/ConversationSimulatorPanel.test.tsx +55 -0
  105. package/src/preview/ConversationSimulatorPanel.tsx +131 -0
  106. package/src/preview/MediaTypesPreview.tsx +87 -0
  107. package/src/preview/conversationPreviewFailures.test.ts +64 -0
  108. package/src/preview/createMockConversationsApi.ts +175 -15
  109. package/src/preview/createPreviewBridgeClient.test.ts +92 -0
  110. package/src/preview/createPreviewBridgeClient.ts +124 -0
  111. package/src/preview/createPreviewMediaUploader.ts +82 -0
  112. package/src/preview/createPreviewWebhookClient.test.ts +96 -0
  113. package/src/preview/createPreviewWebhookClient.ts +127 -4
  114. package/src/preview/index.ts +51 -3
  115. package/src/preview/mediaTypeOf.test.ts +15 -0
  116. package/src/preview/mockDocumentsSearch.test.ts +57 -0
  117. package/src/preview/preview.test.ts +5 -3
  118. package/src/preview/previewFileSamples.test.ts +151 -0
  119. package/src/preview/previewFileSamples.ts +74 -0
  120. package/src/preview/previewFixtures.ts +288 -1
  121. package/src/preview/previewMediaSource.test.ts +62 -0
  122. package/src/preview/previewMediaSource.ts +91 -0
  123. package/src/preview/previewMediaUploader.test.ts +61 -0
  124. package/src/providers/ConversationsProvider.tsx +8 -6
  125. package/src/providers/types.ts +185 -10
  126. package/src/quickReply.test.ts +58 -0
  127. package/src/settings/MessagesWorkspace.tsx +571 -0
  128. package/src/settings/TopicsForm.tsx +2 -0
  129. package/src/settings/TranscriptionSettingsForm.test.tsx +81 -0
  130. package/src/settings/TranscriptionSettingsForm.tsx +190 -0
  131. package/src/settings/WelcomeFarewellForm.tsx +1 -0
  132. package/src/settings/WhatsAppCreateTemplateForm.tsx +4 -1
  133. package/src/settings/WhatsAppTemplateSettingsForm.tsx +5 -2
  134. package/src/settings/WhatsAppTemplatesSettings.test.tsx +61 -0
  135. package/src/settings/WhatsAppTemplatesSettings.tsx +22 -2
  136. package/src/styles.css +858 -0
  137. package/src/types.ts +64 -1
  138. package/src/useWaitingNotifications.ts +74 -29
  139. package/src/workspace/BulkTemplateModal.tsx +132 -0
  140. package/src/workspace/ConversationPane.tsx +432 -0
  141. package/src/workspace/ConversationsInboxList.tsx +194 -0
  142. package/src/workspace/ConversationsWorkspace.tsx +423 -0
  143. package/src/workspace/index.ts +17 -0
  144. package/src/workspace/labels.test.ts +17 -0
  145. package/src/workspace/labels.ts +85 -0
  146. package/src/workspace/useConversationsInbox.ts +332 -0
  147. package/dist/chunk-4R6Y43DQ.js +0 -726
  148. package/dist/chunk-NV2RZ5KT.js +0 -56
  149. package/dist/types-C0PtaO7S.d.ts +0 -207
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
- import { NodeProps } from '@xyflow/react';
2
+ import { ReactNode } from 'react';
3
+ import { NodeProps, EdgeProps } from '@xyflow/react';
3
4
  import { LucideIcon } from 'lucide-react';
4
5
  import { FlowConditionOperator, FlowGraphData, FlowNodeData, FlowNodeType, FlowQuestionType, FlowActionKind } from '@adatechnology/meta-whatsapp-contracts';
5
6
  export { FlowActionKind, FlowConditionOperator, FlowGraphData, FlowNodeData, FlowNodeNext, FlowNodeType, FlowQuestionType } from '@adatechnology/meta-whatsapp-contracts';
@@ -9,7 +10,17 @@ declare const BUILT_IN_ACTION_KINDS: {
9
10
  readonly HANDOFF: "handoff";
10
11
  readonly RATE_LIMITED_HANDOFF: "rate_limited_handoff";
11
12
  readonly SEND_PRODUCT_LIST: "send_product_list";
13
+ readonly SEND_MEDIA: "send_media";
12
14
  };
15
+ /**
16
+ * Ações depois das quais a conversa CONTINUA no grafo, em vez de terminar.
17
+ *
18
+ * A distinção não é estética: o motor do bot só anda para o `next` depois de `send_media` — as
19
+ * demais ações (handoff, encerrar, simular, catálogo) encerram o passo ali. Dar saída a uma ação
20
+ * que o motor não atravessa desenharia um fio que o bot ignora em produção, deixando a conversa
21
+ * parada sem ninguém entender por quê — o mesmo erro que `resolveConnection` recusa cometer.
22
+ */
23
+ declare const PASS_THROUGH_ACTION_KINDS: readonly string[];
13
24
  declare const CROSS_FLOW_PREFIX = "flow:";
14
25
  declare const isCrossFlowTarget: (target: string) => boolean;
15
26
  declare const crossFlowKey: (target: string) => string;
@@ -48,6 +59,15 @@ declare function validateGraph(graph: FlowGraphData, issueText: {
48
59
  conditionIncomplete: (id: string) => string;
49
60
  conditionBranchMissing: (id: string, branch: string) => string;
50
61
  }): GraphIssue[];
62
+ /**
63
+ * Auto-layout em cascata: avança para a direita a cada passo do fluxo e desce a cada card.
64
+ *
65
+ * **Um card por linha, sempre.** Empilhar por camada — todos do mesmo nível na mesma coluna, todas
66
+ * as colunas começando na mesma altura — deixava os fios correndo na horizontal, e um fio horizontal
67
+ * passa por trás de qualquer card que esteja entre a origem e o destino. Descendo um degrau por
68
+ * card, toda ligação vira uma diagonal curta e visível, e o caminho da conversa se lê de cima para
69
+ * baixo enquanto avança da esquerda para a direita.
70
+ */
51
71
  declare function computeAutoLayout(graph: FlowGraphData): Record<string, {
52
72
  x: number;
53
73
  y: number;
@@ -62,11 +82,12 @@ type CollectionChain = {
62
82
  actionNodeId: string;
63
83
  };
64
84
  declare function findCollectionChains(graph: FlowGraphData): CollectionChain[];
65
- declare function slugifyNodeId(label: string, existing: Set<string>): string;
85
+ declare function slugifyNodeId(label: string, existing: ReadonlySet<string>): string;
66
86
 
67
87
  interface FlowEditorLabels {
68
88
  legend: Record<FlowNodeType, string>;
69
89
  startNodeTooltip: string;
90
+ detachedNodeTooltip: string;
70
91
  liveCountTooltip: (count: number) => string;
71
92
  edgeFallbackLabel: string;
72
93
  actionKindLabels: Record<string, string>;
@@ -75,10 +96,15 @@ interface FlowEditorLabels {
75
96
  nodePanel: {
76
97
  title: string;
77
98
  contextKey: string;
99
+ nodeName: string;
100
+ nodeNamePlaceholder: string;
101
+ nodeNameHint: string;
78
102
  questionType: string;
79
103
  question: string;
80
104
  options: string;
81
105
  addOption: string;
106
+ removeOption: string;
107
+ close: string;
82
108
  optionId: string;
83
109
  optionLabel: string;
84
110
  next: string;
@@ -109,6 +135,27 @@ interface FlowEditorLabels {
109
135
  conditionTrue: string;
110
136
  conditionFalse: string;
111
137
  conditionVariableMissing: string;
138
+ media: string;
139
+ mediaUnavailable: string;
140
+ };
141
+ quickAdd: {
142
+ fromHandle: string;
143
+ title: string;
144
+ disconnect: string;
145
+ };
146
+ /** Legenda do canvas: o que cada traço e cada contorno querem dizer. */
147
+ legendPanel: {
148
+ title: string;
149
+ nodes: string;
150
+ connections: string;
151
+ linear: string;
152
+ branch: string;
153
+ fallback: string;
154
+ crossFlow: string;
155
+ live: string;
156
+ selfLoop: string;
157
+ detached: string;
158
+ startNode: string;
112
159
  };
113
160
  palette: {
114
161
  title: string;
@@ -121,6 +168,8 @@ interface FlowEditorLabels {
121
168
  flowMap: {
122
169
  nodeCount: (count: number) => string;
123
170
  openFlow: string;
171
+ toggleToMap: string;
172
+ toggleToDetail: string;
124
173
  };
125
174
  flowGroup: {
126
175
  focus: string;
@@ -130,6 +179,66 @@ interface FlowEditorLabels {
130
179
  tooltip: string;
131
180
  goesTo: (label: string) => string;
132
181
  };
182
+ collectionChain: {
183
+ feeds: (label: string) => string;
184
+ };
185
+ /**
186
+ * Texto dos problemas encontrados por `validateGraph`. Vive aqui, e não no host, porque a tela
187
+ * composta é quem valida — deixar de fora obrigaria todo produto a repassar o mesmo mapa de
188
+ * funções só para a barra de erros aparecer.
189
+ */
190
+ validation: FlowValidationLabels;
191
+ /** Barra de cima, estados de carregamento e ações do editor inteiro. */
192
+ workspace: {
193
+ title: string;
194
+ subtitle: string;
195
+ loading: string;
196
+ loadError: string;
197
+ saveGraph: string;
198
+ saving: string;
199
+ saveSuccess: string;
200
+ saveError: string;
201
+ organize: string;
202
+ organizeTooltip: string;
203
+ discardChanges: string;
204
+ discardTooltip: string;
205
+ discardConfirm: string;
206
+ unsavedChangesConfirm: string;
207
+ };
208
+ flowManager: {
209
+ newFlow: string;
210
+ createTitle: string;
211
+ key: string;
212
+ keyHint: string;
213
+ keyInvalid: string;
214
+ label: string;
215
+ showInMenu: string;
216
+ menuOptionLabel: string;
217
+ create: string;
218
+ creating: string;
219
+ deleteFlow: string;
220
+ deleteConfirm: (label: string) => string;
221
+ createError: string;
222
+ deleteError: string;
223
+ };
224
+ }
225
+ interface FlowValidationLabels {
226
+ title: string;
227
+ errors: (count: number) => string;
228
+ warnings: (count: number) => string;
229
+ noStart: string;
230
+ brokenRef: (from: string, to: string) => string;
231
+ choiceWithoutOptions: (id: string) => string;
232
+ duplicatedOptionId: (id: string, optionId: string) => string;
233
+ optionWithoutTarget: (id: string, optionLabel: string) => string;
234
+ tooManyOptions: (id: string, count: number) => string;
235
+ buttonTitleTooLong: (id: string, label: string) => string;
236
+ listTitleTooLong: (id: string, label: string) => string;
237
+ bodyTooLong: (id: string) => string;
238
+ unreachable: (id: string) => string;
239
+ deadEndQuestion: (id: string) => string;
240
+ conditionIncomplete: (id: string) => string;
241
+ conditionBranchMissing: (id: string, branch: string) => string;
133
242
  }
134
243
  declare const DEFAULT_FLOW_EDITOR_LABELS: FlowEditorLabels;
135
244
  declare function mergeFlowEditorLabels(override?: Partial<FlowEditorLabels>): FlowEditorLabels;
@@ -140,10 +249,28 @@ type FlowNodeCardData = {
140
249
  liveCount: number;
141
250
  isStart: boolean;
142
251
  isSelected: boolean;
252
+ /**
253
+ * Nó sem nenhuma ligação de entrada — o bot nunca chega nele. Contorno tracejado e pulso: sem
254
+ * isso, desconectar um fio ou criar um card solto passa despercebido até o fluxo quebrar em
255
+ * produção.
256
+ */
257
+ isDetached?: boolean;
143
258
  issues: GraphIssue[];
144
259
  labels: FlowEditorLabels;
145
260
  actionKindIcons?: Record<string, LucideIcon>;
146
261
  onSelect: (id: string) => void;
262
+ /**
263
+ * Criar o próximo nó já ligado nesta saída. Capacidade por ausência: sem o callback, o "+" não
264
+ * aparece — puxar o fio à mão continua funcionando igual.
265
+ */
266
+ onQuickAdd?: ((params: {
267
+ nodeId: string;
268
+ handle: string;
269
+ anchor: {
270
+ x: number;
271
+ y: number;
272
+ };
273
+ }) => void) | undefined;
147
274
  };
148
275
  declare function FlowNodeCard({ data }: NodeProps): react.JSX.Element;
149
276
  declare const flowNodeTypes: {
@@ -219,8 +346,41 @@ interface FlowPaletteProps {
219
346
  labels?: Partial<FlowEditorLabels>;
220
347
  actionOptions?: FlowPaletteActionOption[];
221
348
  }
349
+ interface FlowPaletteMenuProps {
350
+ onSelect: (spec: NewNodeSpec) => void;
351
+ labels: FlowEditorLabels;
352
+ actionOptions?: FlowPaletteActionOption[];
353
+ }
354
+ /**
355
+ * A lista de tipos de nó, sem o botão que a abre.
356
+ *
357
+ * Separada porque o "+" do card abre exatamente esta lista: duplicar os itens era garantir que um
358
+ * tipo novo aparecesse num caminho e faltasse no outro.
359
+ */
360
+ declare function FlowPaletteMenu({ onSelect, labels, actionOptions }: FlowPaletteMenuProps): react.JSX.Element;
222
361
  declare function FlowPalette({ onAdd, labels: labelsOverride, actionOptions }: FlowPaletteProps): react.JSX.Element;
223
362
 
363
+ type FlowLegendEdgeSample = {
364
+ readonly color: string;
365
+ readonly dash?: string | undefined;
366
+ readonly label: string;
367
+ };
368
+ type FlowLegendProps = {
369
+ readonly labels: FlowEditorLabels;
370
+ readonly edgeSamples: readonly FlowLegendEdgeSample[];
371
+ /** Classe de cor por tipo de card — a mesma que o `FlowNodeCard` usa, para a chave bater. */
372
+ readonly nodeSwatches: readonly {
373
+ readonly type: FlowNodeType;
374
+ readonly className: string;
375
+ }[];
376
+ };
377
+ declare function FlowLegend({ labels, edgeSamples, nodeSwatches }: FlowLegendProps): react.JSX.Element;
378
+
379
+ declare function FlowConnectionEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, markerEnd, style, data, interactionWidth, }: EdgeProps): react.JSX.Element;
380
+ declare const flowEdgeTypes: {
381
+ flowConnection: typeof FlowConnectionEdge;
382
+ };
383
+
224
384
  interface FlowNodePanelProps {
225
385
  graph: FlowGraphData;
226
386
  node: FlowNodeData;
@@ -233,14 +393,271 @@ interface FlowNodePanelProps {
233
393
  onChange: (updated: FlowNodeData) => void;
234
394
  onDelete: (nodeId: string) => void;
235
395
  labels?: Partial<FlowEditorLabels>;
396
+ /**
397
+ * Seletor de arquivos do nó `send_media`, renderizado no lugar da mensagem direta.
398
+ *
399
+ * Slot, e não uma lista de arquivos por prop, porque a biblioteca é do host: upload, permissão e
400
+ * URL assinada são dele, e o painel não tem como buscar nada. Ausente, o nó continua editável —
401
+ * só não dá para anexar por aqui.
402
+ */
403
+ renderMediaPicker?: (node: FlowNodeData, graph: FlowGraphData) => ReactNode;
236
404
  }
237
- declare function FlowNodePanel({ graph, node, issues, otherFlows, onClose, onChange, onDelete, labels: labelsOverride, }: FlowNodePanelProps): react.JSX.Element;
405
+ declare function FlowNodePanel({ graph, node, issues, otherFlows, onClose, onChange, onDelete, labels: labelsOverride, renderMediaPicker, }: FlowNodePanelProps): react.JSX.Element;
238
406
 
239
407
  interface FlowWhatsAppPreviewProps {
240
408
  body: string;
241
409
  options?: [string, string][];
242
410
  labels?: FlowEditorLabels['nodePanel'];
243
411
  }
244
- declare function FlowWhatsAppPreview({ body, options, labels }: FlowWhatsAppPreviewProps): react.JSX.Element;
412
+ declare function FlowWhatsAppPreview({ body, options, labels, }: FlowWhatsAppPreviewProps): react.JSX.Element;
413
+
414
+ /**
415
+ * Derivações puras do canvas do editor: posições, arestas, contagem de conversas vivas e o nó que
416
+ * nasce de cada item da paleta.
417
+ *
418
+ * Viviam dentro do componente, e é ali que estava o risco. Uma aresta com destino errado não pinta
419
+ * errado — ela leva a conversa do cliente para o lugar errado, e o sintoma aparece longe de quem
420
+ * editou. Um card que o layout joga para fora da área visível se lê como "apaguei sem querer".
421
+ *
422
+ * A topologia sai daqui em forma neutra (`FlowEdgeSpec`) e o estilo fica no componente: o teste cobra
423
+ * o que importa e roda sem navegador nem `@xyflow/react`. Cor de traço ninguém quebra sem ver.
424
+ */
425
+
426
+ type FlowNodePosition = {
427
+ readonly x: number;
428
+ readonly y: number;
429
+ };
430
+ /** Onde cada conversa viva está parada agora — uma linha por sessão. */
431
+ type FlowLivePosition = {
432
+ readonly currentState: string;
433
+ readonly flow: string | null;
434
+ readonly nodeId: string | null;
435
+ readonly menuNodeId: string | null;
436
+ };
437
+ /**
438
+ * Quantas conversas estão paradas num nó — o formato que o servidor já agrega.
439
+ *
440
+ * `meta-whatsapp-module` responde `GROUP BY (flowKey, currentNodeId)`, e trazer uma linha por
441
+ * sessão só para o editor recontar em memória seria pior em toda base grande. Os dois formatos
442
+ * valem porque produto que já implementou o de sessão não deve quebrar numa release de correção.
443
+ */
444
+ type FlowLiveNodeCount = {
445
+ readonly flowKey: string;
446
+ readonly nodeId: string;
447
+ readonly count: number;
448
+ };
449
+ type FlowLivePositionInput = FlowLivePosition | FlowLiveNodeCount;
450
+ /**
451
+ * Pseudo-nó que representa um fluxo alvo ainda não mesclado no canvas.
452
+ *
453
+ * Um portal por (nó de origem, fluxo alvo): duas opções do mesmo nó indo para o mesmo fluxo
454
+ * compartilham o portal, senão o card ficaria cercado de caixas idênticas.
455
+ */
456
+ declare function portalNodeId(sourceNodeId: string, target: string): string;
457
+ declare function chainFrameNodeId(actionNodeId: string): string;
458
+ declare const GROUP_HEADER_NODE_ID = "__group_header__";
459
+ /**
460
+ * Quantas conversas estão paradas em cada nó de um fluxo.
461
+ *
462
+ * A raiz é caso à parte no formato por sessão: o servidor guarda o passo do menu em `menuNodeId`,
463
+ * num campo próprio, e uma conversa no menu não carrega `flow`. Ler `nodeId` ali daria contagem
464
+ * zero na tela mais visitada do editor. No formato agregado a chave do fluxo já vem na linha,
465
+ * então a raiz não tem exceção.
466
+ */
467
+ declare function countLiveByNode(params: {
468
+ readonly flowKey: string;
469
+ readonly rootFlowKey: string;
470
+ readonly positions: readonly FlowLivePositionInput[] | undefined;
471
+ }): Record<string, number>;
472
+ /**
473
+ * Um layout só para TODOS os nós de TODOS os fluxos abertos juntos, na mesma cascata do fluxo
474
+ * único: um passo para a direita a cada avanço da conversa, um degrau para baixo a cada card.
475
+ *
476
+ * Posicionar cada fluxo à parte e deslocar não resolve: nada impede dois fluxos de ocuparem o mesmo
477
+ * espaço, e a altura real de cada card é ignorada. Aqui a travessia roda sobre o grafo mesclado
478
+ * inteiro, com os saltos `flow:<key>` já resolvidos para o nó inicial do alvo.
479
+ */
480
+ declare function computeMergedLayout(params: {
481
+ readonly openKeys: readonly string[];
482
+ readonly graphs: Readonly<Record<string, FlowGraphData>>;
483
+ readonly primaryFlowKey: string;
484
+ }): Map<string, FlowNodePosition>;
485
+ /** Papel visual da aresta. O componente traduz em traço, cor e seta; o modelo só decide qual é. */
486
+ type FlowEdgeKind = 'linear' | 'branch' | 'fallback';
487
+ type FlowEdgeSpec = {
488
+ readonly id: string;
489
+ readonly source: string;
490
+ readonly target: string;
491
+ readonly sourceHandle?: string;
492
+ readonly kind: FlowEdgeKind;
493
+ /** Salto entre fluxos — tracejado, esteja o alvo mesclado ou num portal. */
494
+ readonly crossFlow: boolean;
495
+ readonly live: boolean;
496
+ };
497
+ /**
498
+ * Arestas de todos os fluxos abertos.
499
+ *
500
+ * A regra que não é óbvia: um salto `flow:<key>` vira ligação real até o nó inicial do alvo quando
501
+ * esse fluxo já está no canvas, e só cai no portal quando não está. Sem isso, abrir dois fluxos
502
+ * ligados mostrava uma caixa de portal entre dois cards que já estavam ali do lado.
503
+ */
504
+ declare function buildFlowEdges(params: {
505
+ readonly openKeys: readonly string[];
506
+ readonly graphs: Readonly<Record<string, FlowGraphData>>;
507
+ readonly rootFlowKey: string;
508
+ readonly livePositions?: readonly FlowLivePositionInput[] | undefined;
509
+ }): FlowEdgeSpec[];
510
+ /** Nós de um fluxo que ninguém aponta — o card ganha contorno tracejado para cobrar a ligação. */
511
+ declare function detachedNodeIds(graph: FlowGraphData): Set<string>;
512
+ /** Retângulo que envolve uma cadeia de coleta, em coordenadas do canvas. */
513
+ declare function chainFrameBounds(params: {
514
+ readonly nodeIds: readonly string[];
515
+ readonly graph: FlowGraphData;
516
+ readonly positionOf: (nodeId: string) => FlowNodePosition;
517
+ readonly padding: number;
518
+ }): {
519
+ x: number;
520
+ y: number;
521
+ width: number;
522
+ height: number;
523
+ };
524
+ /**
525
+ * O nó que nasce de cada item da paleta.
526
+ *
527
+ * `contextKey` igual ao id porque é o que o motor usa para guardar a resposta: deixá-lo vazio faria a
528
+ * pergunta ser feita e a resposta descartada, sem erro em lugar nenhum.
529
+ */
530
+ declare function newNodeFromSpec(spec: NewNodeSpec, existingIds: ReadonlySet<string>): FlowNodeData;
531
+
532
+ interface CreateFlowInput {
533
+ key: string;
534
+ label: string;
535
+ showInMenu: boolean;
536
+ /** Ausente quando `showInMenu` é falso — não há opção de menu para rotular. */
537
+ menuOptionLabel?: string;
538
+ }
539
+ /**
540
+ * Backend de fluxos do host. Funções cruas em vez de um cliente HTTP: o pacote roda em produtos com
541
+ * axios, fetch e react-query, e nenhum deles precisa entrar como dependência daqui.
542
+ */
543
+ interface FlowsWorkspaceApi {
544
+ getGraphs(): Promise<Record<string, FlowGraphData>>;
545
+ saveGraph(key: string, graph: FlowGraphData): Promise<void>;
546
+ /**
547
+ * Criar e excluir fluxo são **opcionais por capacidade**: produto cujos fluxos vêm de um seed
548
+ * versionado não expõe rota para isso, e a tela simplesmente não desenha os botões — em vez de
549
+ * oferecer uma ação que estoura no clique.
550
+ */
551
+ createFlow?(input: CreateFlowInput): Promise<void>;
552
+ deleteFlow?(key: string): Promise<void>;
553
+ /**
554
+ * Onde estão as conversas vivas. Ausente, os cards não pulsam e nada é consultado.
555
+ *
556
+ * Aceita a linha por sessão e a linha já agregada por nó — `meta-whatsapp-module` responde a
557
+ * segunda, e exigir a primeira deixava os cards parados em todo produto que usa o módulo.
558
+ */
559
+ getLivePositions?(): Promise<readonly FlowLivePositionInput[]>;
560
+ }
561
+ interface FlowsWorkspaceProps {
562
+ readonly api: FlowsWorkspaceApi;
563
+ /** Fluxo raiz — o que abre por padrão e o único que não pode ser excluído. */
564
+ readonly rootFlowKey?: string;
565
+ readonly labels?: Partial<FlowEditorLabels>;
566
+ /** Kinds de ação do produto oferecidos na paleta (`trigger_simulation`, `abrir_comanda`…). */
567
+ readonly actionOptions?: readonly FlowPaletteActionOption[];
568
+ /** Seletor de arquivos do nó `send_media` — a biblioteca é do host, então entra por slot. */
569
+ readonly renderMediaPicker?: (node: FlowNodeData, graph: FlowGraphData) => ReactNode;
570
+ /** Intervalo do polling de posições vivas. Só tem efeito com `getLivePositions`. */
571
+ readonly livePollIntervalMs?: number;
572
+ /**
573
+ * Título e subtítulo do editor. `false` deixa só a barra de ações.
574
+ *
575
+ * Produto cuja navegação já nomeia a tela mostrava o nome duas vezes, em dois tamanhos, porque a
576
+ * tipografia daqui é do pacote e a de lá é do host. Esconder é a única saída que não força o
577
+ * pacote a adivinhar a escala tipográfica de cada produto.
578
+ */
579
+ readonly showHeader?: boolean;
580
+ readonly className?: string;
581
+ }
582
+ /**
583
+ * Editor de fluxograma completo — barra de ações, abas de fluxo, paleta, canvas com fusão
584
+ * editável, painel de nó, mapa de fluxos e diálogos de criar/excluir.
585
+ *
586
+ * É a tela inteira, não as peças: cada produto que remontava esse grid à mão acabava com uma
587
+ * versão diferente do mesmo editor. Customização entra por `labels`, `actionOptions` e
588
+ * `renderMediaPicker` — nunca por cópia do arquivo.
589
+ */
590
+ declare function FlowsWorkspace({ api, rootFlowKey, labels: labelsOverride, actionOptions, renderMediaPicker, livePollIntervalMs, showHeader, className, }: FlowsWorkspaceProps): react.JSX.Element;
591
+
592
+ /**
593
+ * Copyright (c) 2026 Ada Technology. MIT License.
594
+ *
595
+ * Operações puras de grafo que o editor de fluxo precisa, e que viviam soltas dentro da página de
596
+ * 973 linhas do financiamento.
597
+ *
598
+ * Puras e separadas do hook de propósito: são a parte que dá para testar sem navegador, sem React e
599
+ * sem estado — e três delas (`removeNodeAndCleanRefs`, `resolveConnection`, `mergedFlowKeysFrom`)
600
+ * decidem o que acontece com o fluxo que alguém desenhou. Errar ali não dá erro; dá aresta apontando
601
+ * para nó que não existe mais, ou fluxo que some do canvas.
602
+ */
603
+
604
+ /**
605
+ * Id de nó no canvas mesclado: vários fluxos dividem o mesmo espaço, e `boas-vindas` pode existir em
606
+ * dois deles. Sem o prefixo, arrastar um card moveria o homônimo do outro fluxo.
607
+ */
608
+ declare function namespaceNodeId(flowKey: string, nodeId: string): string;
609
+ declare function parseNamespacedId(value: string): {
610
+ flowKey: string;
611
+ nodeId: string;
612
+ };
613
+ /**
614
+ * Apaga o nó E as referências a ele.
615
+ *
616
+ * A limpeza não é cortesia: uma aresta apontando para nó inexistente faz o motor do bot parar a
617
+ * conversa no meio, e o sintoma aparece para o cliente, não para quem editou.
618
+ */
619
+ declare function removeNodeAndCleanRefs(nodes: Readonly<Record<string, FlowNodeData>>, removedId: string): Record<string, FlowNodeData>;
620
+ /**
621
+ * O fecho transitivo dos fluxos alcançáveis a partir de um — é o conjunto que o canvas abre junto.
622
+ *
623
+ * BFS e não recursão: fluxo que referencia a si mesmo (menu que volta ao menu) é comum, e recursão
624
+ * ingênua estouraria a pilha no caso mais banal que existe.
625
+ */
626
+ declare function mergedFlowKeysFrom(rootKey: string, graphs: Readonly<Record<string, FlowGraphData>>): readonly string[];
627
+ type ConnectionRequest = {
628
+ readonly source: string;
629
+ readonly target: string;
630
+ readonly sourceHandle?: string | null | undefined;
631
+ };
632
+ type ResolvedConnection = {
633
+ readonly flowKey: string;
634
+ readonly nodeId: string;
635
+ readonly handle: string;
636
+ /** O que gravar no `next`: id de nó local, ou `flow:<key>` quando atravessa fluxo. */
637
+ readonly targetValue: string;
638
+ };
639
+ /**
640
+ * Traduz um arraste de aresta no valor que vai para o `next` — e recusa o que o motor do bot não
641
+ * sabe executar.
642
+ *
643
+ * A regra que não é óbvia: conectar num nó de OUTRO fluxo só funciona se for o nó inicial dele,
644
+ * porque o motor só sabe pular para o começo de um fluxo, não para um nó do meio. Conectar no meio
645
+ * devolve `undefined` — recusa silenciosa é melhor que gravar um salto que o bot vai ignorar em
646
+ * produção, deixando a conversa parada sem ninguém entender por quê.
647
+ */
648
+ declare function resolveConnection(params: {
649
+ readonly connection: ConnectionRequest;
650
+ readonly graphs: Readonly<Record<string, FlowGraphData>>;
651
+ }): ResolvedConnection | undefined;
652
+ /** Aplica a conexão resolvida no nó. `next` string para saída única, objeto para ramificação. */
653
+ declare function applyConnection(node: FlowNodeData, resolved: ResolvedConnection): FlowNodeData;
654
+ /**
655
+ * Um fluxo está sujo quando o rascunho difere do publicado.
656
+ *
657
+ * Comparação estrutural por JSON: é grosseira, e é suficiente porque o grafo é dado serializável sem
658
+ * ordem significativa de chave — o servidor devolve o que gravou. Comparar campo a campo daria a
659
+ * mesma resposta com mais código para errar.
660
+ */
661
+ declare function isGraphDirty(working: FlowGraphData | undefined, published: FlowGraphData | undefined): boolean;
245
662
 
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 };
663
+ export { BUILT_IN_ACTION_KINDS, CONDITION_OPERATORS, CROSS_FLOW_PREFIX, type CollectionChain, type ConnectionRequest, type CreateFlowInput, DEFAULT_FLOW_EDITOR_LABELS, FlowConnectionEdge, type FlowEdgeKind, type FlowEdgeSpec, type FlowEditorLabels, FlowGroupFrame, type FlowGroupFrameData, FlowGroupHeader, type FlowGroupHeaderData, FlowLegend, type FlowLiveNodeCount, type FlowLivePosition, type FlowLivePositionInput, FlowMapCanvas, type FlowMapCanvasProps, FlowMapNode, type FlowMapNodeData, FlowNodeCard, type FlowNodeCardData, FlowNodePanel, type FlowNodePanelProps, type FlowNodePosition, FlowPalette, type FlowPaletteActionOption, FlowPaletteMenu, type FlowPaletteProps, FlowPortalNode, type FlowPortalNodeData, type FlowValidationLabels, FlowWhatsAppPreview, type FlowWhatsAppPreviewProps, FlowsWorkspace, type FlowsWorkspaceApi, type FlowsWorkspaceProps, GROUP_HEADER_NODE_ID, type GraphIssue, NODE_CARD_WIDTH, type NewNodeSpec, PASS_THROUGH_ACTION_KINDS, type ResolvedConnection, WHATSAPP_LIMITS, applyConnection, buildFlowEdges, chainFrameBounds, chainFrameNodeId, computeAutoLayout, computeFlowMapLayout, computeMergedLayout, countLiveByNode, crossFlowKey, crossFlowTargetsOf, detachedNodeIds, estimateNodeHeight, findCollectionChains, flowEdgeTypes, flowGroupFrameNodeTypes, flowGroupHeaderNodeTypes, flowMapNodeTypes, flowNodeTypes, flowPortalNodeTypes, isCrossFlowTarget, isGraphDirty, mergeFlowEditorLabels, mergedFlowKeysFrom, namespaceNodeId, newNodeFromSpec, nodeLabel, parseNamespacedId, portalNodeId, removeNodeAndCleanRefs, rendersAsButtons, resolveConnection, slugifyNodeId, targetsOf, validateGraph };