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

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 +2372 -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 +1193 -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 +95 -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
package/dist/index.d.ts CHANGED
@@ -1,15 +1,8 @@
1
1
  import * as react from 'react';
2
- import react__default, { ReactNode, FormEvent } from 'react';
3
- import { M as MessagePayload, k as ConversationsFeatures, i as ConversationSummary, f as ConversationChannel, j as ConversationsApi, S as SSEProvider, g as ConversationDocument } from './types-C0PtaO7S.js';
4
- export { C as CHANNEL_CAPABILITIES, a as CHANNEL_FILTER_ALL, b as CONVERSATION_CHANNEL, c as ChannelCapabilities, d as ChannelFilter, e as ChannelFilterOption, h as ConversationEventSource, l as ConversationsTheme, m as ConversationsUIConfig, D as DEFAULT_CONVERSATION_CHANNEL, F as FormatContactHandleParams, H as HANDLE_KIND, n as HandleKind, R as REOPEN_MECHANISM, o as ReopenMechanism, p as capabilitiesOf, q as channelFiltersFor, r as contactFlag, s as formatContactHandle } from './types-C0PtaO7S.js';
5
-
6
- type ResolveMediaUrl = (message: MessagePayload) => Promise<string | null>;
7
- interface MediaRendererProps {
8
- message: MessagePayload;
9
- onLightbox: (src: string) => void;
10
- onResolveUrl?: ResolveMediaUrl;
11
- }
12
- declare function MediaRenderer({ message, onLightbox, onResolveUrl }: MediaRendererProps): react.JSX.Element | null;
2
+ import react__default, { ReactNode, CSSProperties, UIEvent, FormEvent, RefObject } from 'react';
3
+ import { U as MessagePayload, a3 as ResolveMediaUrl, P as InteractiveSelection, V as MessageTranscription, N as InteractivePayload, x as ConversationsFeatures, u as ConversationSummary, j as ConversationChannel, Q as ListConversationsParams, w as ConversationsApi, a5 as SSEProvider, aa as TranscriptionMode, R as ListDocumentsParams, k as ConversationDocument, v as ConversationTemplate, f as ChannelFilter, g as ChannelFilterOption, q as ConversationSimulatorClient, s as ConversationSimulatorPanelLabels, Z as PreviewUploadedMedia } from './ConversationSimulatorPanel--5fIzXWY.js';
4
+ export { A as AudioRecorderButton, a as AudioRecorderButtonLabels, b as AudioRecorderButtonProps, C as CHANNEL_CAPABILITIES, c as CHANNEL_FILTER_ALL, d as CONVERSATION_CHANNEL, e as ChannelCapabilities, h as CompanyDocument, i as CompanyDocumentPage, l as ConversationDocumentPage, m as ConversationEventSource, n as ConversationPage, y as ConversationsTheme, z as ConversationsUIConfig, E as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, F as DEFAULT_CONVERSATION_CHANNEL, H as DEFAULT_MAX_RECORDING_MILLISECONDS, J as FormatContactHandleParams, K as HANDLE_KIND, L as HandleKind, M as InteractiveOption, O as InteractiveSection, S as MediaRenderer, T as MediaRendererProps, a1 as REOPEN_MECHANISM, a2 as ReopenMechanism, a7 as SendSimulatorMediaParams, a8 as SimulatorMediaKind, ab as TranscriptionStatus, ae as capabilitiesOf, af as channelFiltersFor, ag as contactFlag, ak as formatContactHandle } from './ConversationSimulatorPanel--5fIzXWY.js';
5
+ import '@adatechnology/meta-whatsapp-contracts/testing';
13
6
 
14
7
  interface MessageBubbleProps {
15
8
  message: MessagePayload;
@@ -19,16 +12,56 @@ interface MessageBubbleProps {
19
12
  isSelecting?: boolean;
20
13
  isSelected?: boolean;
21
14
  onToggleSelect?: () => void;
15
+ /**
16
+ * Como buscar a mídia da mensagem. Ausente, o balão usa o `ConversationsApi` do
17
+ * `ConversationsProvider` — passe apenas para sobrescrever (cache próprio, CDN, proxy do host).
18
+ */
22
19
  onResolveMediaUrl?: ResolveMediaUrl;
20
+ /**
21
+ * Toque numa opção do menu interativo. Ausente, as opções aparecem desabilitadas — que é o certo
22
+ * no histórico da inbox: o operador vê o que foi oferecido, sem responder no lugar do cliente.
23
+ */
24
+ onInteractiveSelect?: (selection: InteractiveSelection) => void;
25
+ /**
26
+ * Pede a transcrição do áudio. Ausente, o balão usa `transcribeAudio` do `ConversationsApi` do
27
+ * contexto — passe apenas para sobrescrever. Sem nenhum dos dois, o bloco de transcrição só exibe
28
+ * o que já veio pronto do backend.
29
+ */
30
+ onTranscribeAudio?: (messageId: string) => Promise<MessageTranscription | void>;
23
31
  className?: string;
24
32
  }
25
- declare function MessageBubble({ message, isMine, senderName, isFirstInGroup, isSelecting, isSelected, onToggleSelect, onResolveMediaUrl, className, }: MessageBubbleProps): react.JSX.Element;
33
+ declare function MessageBubble({ message, isMine, senderName, isFirstInGroup, isSelecting, isSelected, onToggleSelect, onResolveMediaUrl, onInteractiveSelect, onTranscribeAudio, className, }: MessageBubbleProps): react.JSX.Element;
34
+
35
+ interface InteractiveMessageLabels {
36
+ /** Fallback do rótulo do botão que abre a lista, quando o payload não traz um. */
37
+ openList: string;
38
+ }
39
+ declare const DEFAULT_INTERACTIVE_MESSAGE_LABELS: InteractiveMessageLabels;
40
+ interface InteractiveMessageProps {
41
+ payload: InteractivePayload;
42
+ onSelect?: (selection: InteractiveSelection) => void;
43
+ labels?: Partial<InteractiveMessageLabels>;
44
+ className?: string;
45
+ }
46
+ declare function InteractiveMessage({ payload, onSelect, labels, className }: InteractiveMessageProps): react.JSX.Element;
26
47
 
27
48
  interface ConversationWallpaperProps {
28
49
  children?: ReactNode;
29
50
  className?: string;
51
+ /** Ajusta ou substitui o fundo padrão — para produto com identidade visual própria. */
52
+ style?: CSSProperties;
53
+ /**
54
+ * Rolagem da área de mensagens. Par do `ref`: é este elemento que rola, então é aqui que
55
+ * `useScrollToLatestMessage` observa a posição para saber se o operador está acompanhando o fim.
56
+ */
57
+ onScroll?: (event: UIEvent<HTMLDivElement>) => void;
30
58
  }
31
- declare function ConversationWallpaper({ children, className }: ConversationWallpaperProps): react.JSX.Element;
59
+ /**
60
+ * `forwardRef` porque quem controla a rolagem é de fora — o hook precisa do elemento para saltar
61
+ * até a última mensagem. `forwardRef` e não `ref` como prop: o pacote suporta React 18, onde
62
+ * ref-como-prop ainda não existe.
63
+ */
64
+ declare const ConversationWallpaper: react.ForwardRefExoticComponent<ConversationWallpaperProps & react.RefAttributes<HTMLDivElement>>;
32
65
 
33
66
  interface ConversationLocales {
34
67
  bubble: {
@@ -52,6 +85,20 @@ interface ConversationLocales {
52
85
  untitledDocument: string;
53
86
  downloadFile: string;
54
87
  };
88
+ transcription: {
89
+ label: string;
90
+ copy: string;
91
+ copied: string;
92
+ transcribe: string;
93
+ transcribing: string;
94
+ retry: string;
95
+ failed: string;
96
+ /** Áudio processado sem fala detectada — distinto de "não transcrito". */
97
+ empty: string;
98
+ unsupported: string;
99
+ showMore: string;
100
+ showLess: string;
101
+ };
55
102
  selection: {
56
103
  select: string;
57
104
  };
@@ -76,13 +123,97 @@ interface AudioPlayerProps {
76
123
  }
77
124
  declare function AudioPlayer({ src, isMine }: AudioPlayerProps): react.JSX.Element;
78
125
 
126
+ interface AudioTranscriptionProps {
127
+ transcription?: MessageTranscription | null;
128
+ /**
129
+ * Pede a transcrição ao backend. Ausente, o bloco nunca oferece o botão — um host em modo
130
+ * automático não tem rota para isso, e desenhar um botão que estoura no clique é pior do que não
131
+ * desenhar nada.
132
+ *
133
+ * O que for devolvido é exibido na hora. Sem isso o texto só apareceria no próximo refetch da
134
+ * lista, e transcrição não emite evento de tempo real: o operador clicaria, veria o spinner
135
+ * terminar e continuaria sem o texto na tela.
136
+ */
137
+ onTranscribe?: () => Promise<MessageTranscription | void>;
138
+ isMine?: boolean;
139
+ }
140
+ /**
141
+ * Transcrição sob a nota de voz, com botão de copiar.
142
+ *
143
+ * Copiar é o motivo de o bloco existir: o operador cola o pedido do cliente no sistema interno, no
144
+ * orçamento, na busca. Seleção manual de texto dentro de um balão de chat é exatamente onde o
145
+ * arrasto do mouse falha — pega o balão vizinho, o horário, o nome do remetente.
146
+ *
147
+ * `navigator.clipboard.writeText` com try/catch silencioso, mesmo padrão de `MessageText.tsx`: a API
148
+ * exige contexto seguro e permissão, e um host servindo em HTTP simples não a tem. Falhar sem alarme
149
+ * é o certo — o texto continua na tela para seleção manual.
150
+ */
151
+ declare function AudioTranscription({ transcription, onTranscribe, isMine }: AudioTranscriptionProps): react.JSX.Element | null;
152
+
153
+ interface EmojiPickerLabels {
154
+ search: string;
155
+ noResults: string;
156
+ }
157
+ declare const DEFAULT_EMOJI_PICKER_LABELS: EmojiPickerLabels;
79
158
  interface EmojiPickerProps {
80
159
  onSelect: (emoji: string) => void;
160
+ labels?: Partial<EmojiPickerLabels>;
81
161
  className?: string;
82
162
  }
83
- declare const EmojiPicker: ({ onSelect, className }: EmojiPickerProps) => react.JSX.Element;
163
+ declare const EmojiPicker: ({ onSelect, labels, className }: EmojiPickerProps) => react.JSX.Element;
84
164
 
165
+ /**
166
+ * Catálogo de emojis com palavras-chave em português, usado pela busca do seletor.
167
+ *
168
+ * Fica em módulo próprio (e não dentro do `EmojiPicker`) porque a busca precisa varrer TODAS as
169
+ * categorias, não só a aberta — o índice é montado uma vez, no carregamento, e não a cada tecla.
170
+ *
171
+ * As palavras-chave são de busca, não legendas: incluem sinônimos e formas sem acento, porque quem
172
+ * digita rápido escreve "coracao" e "polegar" com a mesma frequência que a forma correta.
173
+ */
174
+ type EmojiEntry = {
175
+ readonly emoji: string;
176
+ readonly keywords: readonly string[];
177
+ };
178
+ type EmojiCategory = {
179
+ readonly name: string;
180
+ readonly entries: readonly EmojiEntry[];
181
+ };
182
+ declare const EMOJI_CATEGORIES: readonly EmojiCategory[];
183
+ /**
184
+ * Busca por prefixo de palavra-chave, não por substring: "ca" traz "casa" e "cartão", mas "sa" não
185
+ * traz "casa" — casar no meio da palavra devolvia resultado que ninguém consegue explicar.
186
+ */
187
+ declare function searchEmojis(query: string): readonly EmojiEntry[];
188
+
189
+ /**
190
+ * Mensagem pronta que o atendente cola no campo com um clique.
191
+ *
192
+ * `text` aceita string com `{{variavel}}` ou função: a string cobre o caso comum (copy fixa com o
193
+ * nome do cliente no meio) sem o host escrever código, e a função cobre o que precisa de lógica —
194
+ * escolher texto por produto, pluralizar, formatar moeda.
195
+ */
196
+ interface QuickReply {
197
+ key: string;
198
+ /** O que aparece no chip, emoji incluso: `👋 Saudação`. */
199
+ label: string;
200
+ text: string | ((variables: Readonly<Record<string, string>>) => string);
201
+ }
202
+ /**
203
+ * Troca `{{nome}}` pelos valores passados. Variável ausente vira string vazia, e não o literal
204
+ * `{{nome}}`: mandar "Olá {{nome}}!" para o cliente é pior que mandar "Olá !".
205
+ */
206
+ declare function applyQuickReplyVariables(template: string, variables?: Readonly<Record<string, string>>): string;
207
+ declare function resolveQuickReply(quickReply: QuickReply, variables?: Readonly<Record<string, string>>): string;
208
+ interface MessageComposerLabels {
209
+ emoji: string;
210
+ attach: string;
211
+ send: string;
212
+ removeAttachment: string;
213
+ }
214
+ declare const DEFAULT_MESSAGE_COMPOSER_LABELS: MessageComposerLabels;
85
215
  interface MessageComposerProps {
216
+ labels?: Partial<MessageComposerLabels>;
86
217
  onSend: (text: string) => void;
87
218
  onAttach?: (file: File) => void;
88
219
  value?: string;
@@ -92,16 +223,130 @@ interface MessageComposerProps {
92
223
  maxLength?: number;
93
224
  disabled?: boolean;
94
225
  acceptedFileTypes?: string;
226
+ /**
227
+ * Ocupa o lugar do botão de enviar enquanto não há nada para enviar — é onde o WhatsApp põe o
228
+ * microfone. Fora do campo, o botão vira um bloco solto ao lado do pill e quebra a barra.
229
+ */
230
+ idleAction?: ReactNode;
231
+ /** Mensagens prontas exibidas acima do campo. Vazio ou ausente, a faixa não é renderizada. */
232
+ quickReplies?: readonly QuickReply[];
233
+ /** Valores para `{{variavel}}` — tipicamente nome do cliente, produto, protocolo. */
234
+ quickReplyVariables?: Readonly<Record<string, string>>;
95
235
  className?: string;
96
236
  classNames?: Partial<MessageComposerClassNames>;
97
237
  }
98
238
  interface MessageComposerClassNames {
99
239
  root: string;
240
+ quickReplies: string;
241
+ quickReply: string;
100
242
  field: string;
101
243
  }
102
- declare const MessageComposer: ({ onSend, onAttach, value: externalValue, onChange: externalOnChange, features, placeholder, maxLength, disabled, acceptedFileTypes, className, classNames, }: MessageComposerProps) => react.JSX.Element;
244
+ /**
245
+ * Exatamente o que a Meta aceita em mensagem de mídia — imagem, sticker, áudio, vídeo e a lista
246
+ * fechada de documentos. Oferecer no seletor um formato que o WhatsApp recusa (`.zip`, `.rtf`)
247
+ * empurra a falha para depois do envio, quando já não dá para explicar ao operador o que houve.
248
+ * Produto com regra própria passa `acceptedFileTypes`.
249
+ */
250
+ declare const DEFAULT_ACCEPTED_FILE_TYPES: string;
251
+ declare const MessageComposer: ({ onSend, onAttach, value: externalValue, onChange: externalOnChange, quickReplies, quickReplyVariables, features, placeholder, maxLength, disabled, acceptedFileTypes, idleAction, className, classNames, labels, }: MessageComposerProps) => react.JSX.Element;
252
+
253
+ /** Cada ação que a barra sabe oferecer. `toolbar` decide quais delas aparecem. */
254
+ declare const RICH_COMPOSER_ACTION: {
255
+ readonly BOLD: "bold";
256
+ readonly ITALIC: "italic";
257
+ readonly STRIKETHROUGH: "strikethrough";
258
+ readonly MONOSPACE: "monospace";
259
+ readonly EMOJI: "emoji";
260
+ readonly ATTACH: "attach";
261
+ readonly VARIABLES: "variables";
262
+ };
263
+ type RichComposerAction = (typeof RICH_COMPOSER_ACTION)[keyof typeof RICH_COMPOSER_ACTION];
264
+ /** Texto das dicas. O host troca só o que quiser; o resto fica no padrão. */
265
+ interface RichComposerTooltips {
266
+ bold: string;
267
+ italic: string;
268
+ strikethrough: string;
269
+ monospace: string;
270
+ emoji: string;
271
+ attach: string;
272
+ send: string;
273
+ variables: string;
274
+ /** Botão que abre a formatação recolhida, quando a barra está estreita demais para os quatro. */
275
+ formatting: string;
276
+ }
277
+ declare const DEFAULT_RICH_COMPOSER_TOOLTIPS: RichComposerTooltips;
278
+ /**
279
+ * Um valor que o operador pode inserir no texto sem digitar. `value` é o que entra no campo — pode
280
+ * ser o dado já resolvido ("Anderson") ou um marcador a resolver depois ("{{nome}}"): quem decide é
281
+ * o host, porque só ele sabe se a substituição acontece aqui ou no envio.
282
+ */
283
+ interface RichComposerVariable {
284
+ id: string;
285
+ label: string;
286
+ value: string;
287
+ /** Prévia do que será inserido, para o operador conferir antes de tocar. */
288
+ tooltip?: string;
289
+ }
290
+ /** Uma resposta rápida: o que o operador lê no chip e o que cai no campo ao tocar nele. */
291
+ interface RichComposerQuickReply {
292
+ id: string;
293
+ label: string;
294
+ text: string;
295
+ /** Dica ao passar o mouse. Sem isto, o chip não tem `title` — rótulo curto já se explica. */
296
+ tooltip?: string;
297
+ }
298
+ /** Handle imperativo: `contentEditable` controlado pelo React perde o cursor a cada tecla. */
299
+ interface RichMessageComposerHandle {
300
+ setContent: (text: string) => void;
301
+ clear: () => void;
302
+ focus: () => void;
303
+ }
304
+ interface RichMessageComposerProps {
305
+ /** Texto na notação do WhatsApp (`*negrito*`), não HTML. */
306
+ value: string;
307
+ onChange: (value: string) => void;
308
+ onSend: () => void;
309
+ onAttachFiles?: (files: FileList) => void;
310
+ quickReplies?: RichComposerQuickReply[];
311
+ /** Variáveis que o operador pode inserir no texto. Vazio ou ausente, o botão não aparece. */
312
+ variables?: RichComposerVariable[];
313
+ /**
314
+ * Quais ações aparecem. Ausente, todas aparecem — menos as que não têm como funcionar (anexo sem
315
+ * `onAttachFiles` some sozinho, porque botão que não faz nada é pior que botão nenhum).
316
+ */
317
+ toolbar?: Partial<Record<RichComposerAction, boolean>>;
318
+ tooltips?: Partial<RichComposerTooltips>;
319
+ placeholder?: string;
320
+ disabled?: boolean;
321
+ isSending?: boolean;
322
+ /** Ocupa o lugar do enviar enquanto não há nada para enviar — onde o WhatsApp põe o microfone. */
323
+ idleAction?: ReactNode;
324
+ /** Prévia dos anexos já escolhidos, desenhada pelo host acima da barra. */
325
+ attachmentsPreview?: ReactNode;
326
+ /**
327
+ * Há anexo na fila esperando envio. O campo não conhece a fila — é do host — e sem essa dica ele
328
+ * concluía que não havia nada a enviar: com uma imagem anexada e nenhum texto, o botão de enviar
329
+ * dava lugar ao `idleAction` e a imagem ficava presa.
330
+ */
331
+ hasQueuedAttachments?: boolean;
332
+ acceptedFileTypes?: string;
333
+ className?: string;
334
+ }
335
+ declare const RichMessageComposer: react.ForwardRefExoticComponent<RichMessageComposerProps & react.RefAttributes<RichMessageComposerHandle>>;
103
336
 
337
+ interface WhatsAppMessageEditorLabels {
338
+ bold: string;
339
+ /** Tooltip da negrito — traz a sintaxe do WhatsApp junto, por isso é separado do `aria-label`. */
340
+ boldHint: string;
341
+ italic: string;
342
+ italicHint: string;
343
+ strikethrough: string;
344
+ strikethroughHint: string;
345
+ insertPlaceholder: (token: string) => string;
346
+ }
347
+ declare const DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS: WhatsAppMessageEditorLabels;
104
348
  interface WhatsAppMessageEditorProps {
349
+ labels?: Partial<WhatsAppMessageEditorLabels>;
105
350
  value: string;
106
351
  onChange: (value: string) => void;
107
352
  placeholder?: string;
@@ -110,7 +355,7 @@ interface WhatsAppMessageEditorProps {
110
355
  previewLabel?: string;
111
356
  emptyPreviewText?: string;
112
357
  }
113
- declare function WhatsAppMessageEditor({ value, onChange, placeholder, placeholders, rows, previewLabel, emptyPreviewText, }: WhatsAppMessageEditorProps): react.JSX.Element;
358
+ declare function WhatsAppMessageEditor({ value, onChange, placeholder, placeholders, rows, previewLabel, emptyPreviewText, labels, }: WhatsAppMessageEditorProps): react.JSX.Element;
114
359
 
115
360
  interface SimpleEmojiPickerProps {
116
361
  onSelect: (emoji: string) => void;
@@ -131,16 +376,30 @@ interface DateDividerProps {
131
376
  }
132
377
  declare function DateDivider({ iso, className, classNames }: DateDividerProps): react.JSX.Element;
133
378
 
379
+ interface AvatarLabels {
380
+ /** Lido por leitor de tela quando não há nome nem imagem — a silhueta genérica. */
381
+ unnamedContact: string;
382
+ }
134
383
  interface AvatarProps {
135
384
  name?: string | null;
136
385
  avatarUrl?: string;
137
386
  size?: 'sm' | 'md' | 'lg';
138
387
  className?: string;
388
+ labels?: Partial<AvatarLabels>;
139
389
  }
140
- declare function Avatar({ name, avatarUrl, size, className }: AvatarProps): react.JSX.Element;
390
+ declare const DEFAULT_AVATAR_LABELS: AvatarLabels;
391
+ declare function Avatar({ name, avatarUrl, size, className, labels }: AvatarProps): react.JSX.Element;
141
392
 
393
+ interface ConversationListItemLabels {
394
+ /** Tooltip do ponto vermelho: a janela de atendimento de 24h já fechou. */
395
+ expiredWindow: string;
396
+ /** Tooltip do ponto laranja: a janela de atendimento está perto de fechar. */
397
+ warningWindow: string;
398
+ }
399
+ declare const DEFAULT_CONVERSATION_LIST_ITEM_LABELS: ConversationListItemLabels;
142
400
  interface ConversationListItemProps {
143
401
  conversation: ConversationSummary;
402
+ labels?: Partial<ConversationListItemLabels>;
144
403
  active?: boolean;
145
404
  selected?: boolean;
146
405
  onClick?: () => void;
@@ -157,7 +416,7 @@ interface ConversationListItemProps {
157
416
  */
158
417
  highlightActive?: boolean;
159
418
  }
160
- declare const ConversationListItem: ({ conversation, active, selected, onClick, onSelect, showDivider, highlightActive, }: ConversationListItemProps) => react.JSX.Element;
419
+ declare const ConversationListItem: ({ conversation, active, selected, onClick, onSelect, showDivider, highlightActive, labels, }: ConversationListItemProps) => react.JSX.Element;
161
420
 
162
421
  type ToastType = 'success' | 'error' | 'info';
163
422
  interface ToastContextValue {
@@ -171,18 +430,41 @@ declare function ToastProvider({ children }: {
171
430
  }): react.JSX.Element;
172
431
  declare function useToast(): ToastContextValue;
173
432
 
433
+ /**
434
+ * Camada única de dicas do pacote.
435
+ *
436
+ * O `title` nativo existia em todos os botões e mesmo assim a dica "não aparecia": o navegador
437
+ * espera de 1 a 2 segundos antes de desenhar, o balão sai fora do tema e some ao menor movimento do
438
+ * mouse. Aqui a dica é do produto — aparece em 120ms, com o mesmo contraste do resto da interface.
439
+ *
440
+ * Um só ouvinte no documento atende a interface inteira: cada botão declara `data-cv-tooltip` e não
441
+ * precisa de estado, ref ou wrapper próprio. O balão é impresso em `document.body` via portal
442
+ * porque `overflow` de container (a régua de respostas rápidas rola na horizontal) recortaria um
443
+ * balão desenhado dentro do próprio botão.
444
+ */
445
+ /** Atributo que marca um elemento como portador de dica. */
446
+ declare const TOOLTIP_ATTRIBUTE = "data-cv-tooltip";
447
+ declare function TooltipLayer(): react.ReactPortal | null;
448
+
174
449
  interface StatusTicksProps {
175
450
  status: string;
176
451
  title?: string;
177
452
  }
178
453
  declare function StatusTicks({ status, title }: StatusTicksProps): react.JSX.Element;
179
454
 
455
+ interface LightboxLabels {
456
+ /** Texto alternativo quando a imagem não tem legenda — sem ele o leitor de tela anuncia a URL. */
457
+ imageAlt: string;
458
+ close: string;
459
+ }
180
460
  interface LightboxProps {
181
461
  imageUrl: string;
182
462
  caption?: string;
183
463
  onClose: () => void;
464
+ labels?: Partial<LightboxLabels>;
184
465
  }
185
- declare function Lightbox({ imageUrl, caption, onClose }: LightboxProps): react.JSX.Element;
466
+ declare const DEFAULT_LIGHTBOX_LABELS: LightboxLabels;
467
+ declare function Lightbox({ imageUrl, caption, onClose, labels }: LightboxProps): react.JSX.Element;
186
468
 
187
469
  interface FileIconProps {
188
470
  filename?: string;
@@ -229,23 +511,23 @@ type ConversationWindow = (typeof CONVERSATION_WINDOW)[keyof typeof CONVERSATION
229
511
  declare const WINDOW_FILTERS: readonly [{
230
512
  readonly value: "all";
231
513
  readonly label: "Todas";
232
- readonly dotClass: "";
514
+ readonly tone: "";
233
515
  }, {
234
516
  readonly value: "fresh";
235
517
  readonly label: "<12h";
236
- readonly dotClass: "bg-green-500";
518
+ readonly tone: "fresh";
237
519
  }, {
238
520
  readonly value: "warning";
239
521
  readonly label: "12-21h";
240
- readonly dotClass: "bg-yellow-500";
522
+ readonly tone: "warning";
241
523
  }, {
242
524
  readonly value: "critical";
243
525
  readonly label: "21-24h";
244
- readonly dotClass: "bg-red-500";
526
+ readonly tone: "critical";
245
527
  }, {
246
528
  readonly value: "expired";
247
529
  readonly label: ">24h";
248
- readonly dotClass: "bg-gray-400";
530
+ readonly tone: "expired";
249
531
  }];
250
532
  type WindowOfParams = {
251
533
  readonly lastInboundAt: string | null;
@@ -314,6 +596,19 @@ interface ConversationHeaderClassNames {
314
596
  desktopActions: string;
315
597
  mobileMenu: string;
316
598
  }
599
+ /**
600
+ * Utilitário extra que o host pendura no cabeçalho — ícone no desktop, item de menu no celular,
601
+ * como os nativos. Entra por aqui, e não por um slot de ReactNode, porque é isso que preserva o
602
+ * comportamento responsivo: um nó solto viraria um quarto ícone em 375px, sem área de toque.
603
+ */
604
+ interface ConversationHeaderUtility {
605
+ key: string;
606
+ /** Ícone da biblioteca (lucide), no tamanho dos utilitários nativos: `<Play size={16} />`. */
607
+ icon: ReactNode;
608
+ label: string;
609
+ run: () => void;
610
+ active?: boolean;
611
+ }
317
612
  interface ConversationHeaderProps {
318
613
  conversation: ConversationSummary;
319
614
  busy?: boolean;
@@ -323,29 +618,46 @@ interface ConversationHeaderProps {
323
618
  onDownload?: () => void;
324
619
  onOpenDocuments?: () => void;
325
620
  documentsOpen?: boolean;
621
+ /** Ações do produto que não existem no contrato do pacote (ex.: ferramentas de dev). */
622
+ extraUtilities?: readonly ConversationHeaderUtility[];
326
623
  onBack?: () => void;
327
624
  labels?: Partial<ConversationHeaderLabels>;
328
625
  className?: string;
329
626
  classNames?: Partial<ConversationHeaderClassNames>;
330
627
  }
331
- declare function ConversationHeader({ conversation, busy, onTakeover, onReturnToBot, onFinish, onDownload, onOpenDocuments, documentsOpen, onBack, labels: labelsOverride, className, classNames, }: ConversationHeaderProps): react.JSX.Element;
628
+ declare function ConversationHeader({ conversation, busy, onTakeover, onReturnToBot, onFinish, onDownload, onOpenDocuments, documentsOpen, extraUtilities, onBack, labels: labelsOverride, className, classNames, }: ConversationHeaderProps): react.JSX.Element;
332
629
 
333
630
  /**
334
631
  * "Suas Seleções": o que o bot já coletou na conversa. Para o atendente que assume no meio, é a
335
- * diferença entre ler o transcript inteiro e ver o estado em duas linhas.
632
+ * diferença entre ler o transcript inteiro e ver o estado de relance.
336
633
  *
337
634
  * O pacote não interpreta o contexto — ele é `Record<string, unknown>` e cada produto nomeia as
338
635
  * próprias chaves. O host traduz para `entries`; aqui só se decide como mostrar.
636
+ *
637
+ * Desenho em paridade com financiamento-imobiliario-bot/apps/web/src/components/SelectionsSummary.tsx:
638
+ * card com gradiente, badge de contagem, pills de status no cabeçalho e grid de cards com borda
639
+ * colorida por estado. O que ficou diferente de lá, e por quê, está comentado no ponto.
339
640
  */
641
+ /**
642
+ * `completed` tem valor, `pending` ainda não foi coletado, `editing` é o cliente refazendo a
643
+ * resposta. O host só precisa mandar `status` para o terceiro caso — os dois primeiros saem do
644
+ * próprio `value`, e exigir o campo quebraria quem já usa o painel.
645
+ */
646
+ type ConversationContextStatus = 'completed' | 'pending' | 'editing';
340
647
  interface ConversationContextEntry {
341
648
  key: string;
342
649
  label: string;
343
650
  value?: string | undefined;
344
651
  icon?: string;
652
+ status?: ConversationContextStatus;
345
653
  }
346
654
  interface ConversationContextPanelLabels {
347
655
  title: string;
348
656
  empty: string;
657
+ collapse: string;
658
+ expand: string;
659
+ /** Placeholder do card ainda não coletado. */
660
+ notCollected: string;
349
661
  }
350
662
  declare const DEFAULT_CONVERSATION_CONTEXT_LABELS: ConversationContextPanelLabels;
351
663
  interface ConversationContextPanelClassNames {
@@ -356,11 +668,23 @@ interface ConversationContextPanelClassNames {
356
668
  }
357
669
  interface ConversationContextPanelProps {
358
670
  entries: readonly ConversationContextEntry[];
671
+ /**
672
+ * Estado inicial. Ausente, abre sozinho no desktop quando há algum dado preenchido.
673
+ *
674
+ * Existe porque "abre sozinho" nem sempre é o que o produto quer: com 1 de 6 campos preenchidos o
675
+ * painel ocupa altura mostrando quase só pendências, e empurra a conversa — que é o que se veio ver.
676
+ */
677
+ defaultOpen?: boolean;
678
+ /**
679
+ * Rótulo do fluxo em curso, exibido em azul ao lado do título — no financiamento é o produto
680
+ * escolhido ("Consórcio", "MCMV"). Ausente, o cabeçalho fica só com título e contagens.
681
+ */
682
+ flowLabel?: string;
359
683
  labels?: Partial<ConversationContextPanelLabels>;
360
684
  className?: string;
361
685
  classNames?: Partial<ConversationContextPanelClassNames>;
362
686
  }
363
- declare function ConversationContextPanel({ entries, labels: labelsOverride, className, classNames, }: ConversationContextPanelProps): react.JSX.Element;
687
+ declare function ConversationContextPanel({ entries, defaultOpen, flowLabel, labels: labelsOverride, className, classNames, }: ConversationContextPanelProps): react.JSX.Element;
364
688
 
365
689
  interface WindowExpiredNoticeLabels {
366
690
  title: string;
@@ -380,6 +704,217 @@ declare function WindowExpiredNotice({ onSendTemplate, disabled, labels: labelsO
380
704
  */
381
705
  declare function isWindowBlocking(window: ConversationWindow): boolean;
382
706
 
707
+ /**
708
+ * Biblioteca de arquivos de TODAS as conversas — a tela de Documentos do painel, fora do
709
+ * atendimento.
710
+ *
711
+ * Distinta do `ConversationDocumentsPanel`: aquele parte de uma conversa aberta e vive dentro dela;
712
+ * esta varre a empresa e por isso mostra de qual conversa cada arquivo veio, com o número
713
+ * clicável. Sem essa referência, uma lista global de anexos não responde nenhuma pergunta.
714
+ */
715
+ interface DocumentsLibraryLabels {
716
+ title: string;
717
+ searchPlaceholder: string;
718
+ empty: string;
719
+ noResults: string;
720
+ loading: string;
721
+ failure: string;
722
+ view: string;
723
+ download: string;
724
+ openConversation: string;
725
+ sourceFilterAll: string;
726
+ sourceFilterCustomer: string;
727
+ sourceFilterTeam: string;
728
+ sortMostRecent: string;
729
+ sortOldest: string;
730
+ clearFilters: string;
731
+ upload: string;
732
+ uploadError: string;
733
+ total: (count: number) => string;
734
+ page: (current: number, last: number) => string;
735
+ }
736
+ declare const DEFAULT_DOCUMENTS_LIBRARY_LABELS: DocumentsLibraryLabels;
737
+ interface DocumentsLibraryClassNames {
738
+ root: string;
739
+ title: string;
740
+ filters: string;
741
+ search: string;
742
+ sourceSelect: string;
743
+ sortButton: string;
744
+ clearButton: string;
745
+ status: string;
746
+ list: string;
747
+ item: string;
748
+ conversationLink: string;
749
+ filename: string;
750
+ meta: string;
751
+ pagination: string;
752
+ }
753
+ interface DocumentsLibraryProps {
754
+ /** Itens por página. O total vem do servidor. */
755
+ perPage?: number;
756
+ /** Abrir a conversa de origem. Ausente, o número aparece como texto e não como link. */
757
+ onOpenConversation?: (conversationId: string) => void;
758
+ labels?: Partial<DocumentsLibraryLabels>;
759
+ className?: string;
760
+ classNames?: Partial<DocumentsLibraryClassNames>;
761
+ }
762
+ declare function DocumentsLibrary({ perPage, onOpenConversation, labels: labelsOverride, className, classNames, }: DocumentsLibraryProps): react.JSX.Element | null;
763
+
764
+ type SortDirection = 'asc' | 'desc';
765
+ interface SortableHeadProps {
766
+ readonly label: string;
767
+ readonly field: string;
768
+ readonly activeField: string;
769
+ readonly direction: SortDirection;
770
+ readonly onSort: (field: string) => void;
771
+ readonly className?: string;
772
+ }
773
+ declare function SortableHead({ label, field, activeField, direction, onSort, className }: SortableHeadProps): react.JSX.Element;
774
+ interface FilterOption {
775
+ readonly value: string;
776
+ readonly label: string;
777
+ }
778
+ interface MultiSelectFilterProps {
779
+ readonly label: string;
780
+ readonly options: readonly FilterOption[];
781
+ readonly selected: readonly string[];
782
+ readonly onChange: (selected: readonly string[]) => void;
783
+ readonly className?: string;
784
+ }
785
+ declare function MultiSelectFilter({ label, options, selected, onChange, className }: MultiSelectFilterProps): react.JSX.Element;
786
+ interface BulkActionBarProps {
787
+ readonly selectedCount: number;
788
+ readonly selectedLabel: (count: number) => string;
789
+ readonly clearLabel: string;
790
+ readonly onClear: () => void;
791
+ readonly children?: ReactNode;
792
+ }
793
+ declare function BulkActionBar({ selectedCount, selectedLabel, clearLabel, onClear, children }: BulkActionBarProps): react.JSX.Element | null;
794
+ interface ListingPaginationProps {
795
+ readonly page: number;
796
+ readonly total: number;
797
+ readonly perPage: number;
798
+ readonly perPageOptions?: readonly number[];
799
+ readonly onPageChange: (page: number) => void;
800
+ readonly onPerPageChange?: (perPage: number) => void;
801
+ readonly labels: {
802
+ readonly show: string;
803
+ readonly perPage: string;
804
+ readonly total: (count: number) => string;
805
+ readonly page: (current: number, last: number) => string;
806
+ readonly previous: string;
807
+ readonly next: string;
808
+ };
809
+ }
810
+ declare function ListingPagination({ page, total, perPage, perPageOptions, onPageChange, onPerPageChange, labels, }: ListingPaginationProps): react.JSX.Element;
811
+
812
+ /**
813
+ * Vocabulário da biblioteca de arquivos. Sobrescrevível campo a campo: o produto troca "cliente"
814
+ * por "lead" ou o idioma inteiro sem manter uma cópia da tela para isso.
815
+ */
816
+ interface DocumentsWorkspaceLabels {
817
+ readonly title: string;
818
+ readonly subtitle: (total: number) => string;
819
+ readonly searchPlaceholder: string;
820
+ readonly empty: string;
821
+ readonly noResults: string;
822
+ readonly loading: string;
823
+ readonly failure: string;
824
+ readonly view: string;
825
+ readonly download: string;
826
+ readonly remove: string;
827
+ readonly removeConfirm: (filename: string) => string;
828
+ readonly openConversation: string;
829
+ readonly sourceFilter: string;
830
+ readonly categoryFilter: string;
831
+ readonly startDate: string;
832
+ readonly endDate: string;
833
+ readonly clearFilters: string;
834
+ readonly selectAllPage: string;
835
+ readonly selectRow: string;
836
+ readonly bulkSelected: (count: number) => string;
837
+ readonly bulkClear: string;
838
+ readonly bulkDownloadZip: string;
839
+ readonly bulkRemove: (count: number) => string;
840
+ readonly bulkRemoveConfirm: (count: number) => string;
841
+ readonly upload: string;
842
+ readonly uploadError: string;
843
+ readonly columnFilename: string;
844
+ readonly columnContact: string;
845
+ readonly columnType: string;
846
+ readonly columnSize: string;
847
+ readonly columnSource: string;
848
+ readonly columnDate: string;
849
+ readonly columnActions: string;
850
+ readonly sourceLabels: Readonly<Record<string, string>>;
851
+ readonly categoryLabels: Readonly<Record<string, string>>;
852
+ readonly show: string;
853
+ readonly perPage: string;
854
+ readonly total: (count: number) => string;
855
+ readonly page: (current: number, last: number) => string;
856
+ readonly previousPage: string;
857
+ readonly nextPage: string;
858
+ }
859
+ declare const DEFAULT_DOCUMENTS_WORKSPACE_LABELS: DocumentsWorkspaceLabels;
860
+
861
+ interface DocumentsWorkspaceClassNames {
862
+ root: string;
863
+ header: string;
864
+ filters: string;
865
+ table: string;
866
+ row: string;
867
+ status: string;
868
+ }
869
+ interface DocumentsWorkspaceProps {
870
+ readonly perPage?: number;
871
+ readonly perPageOptions?: readonly number[];
872
+ /** Abrir a conversa de origem. Ausente, o número aparece como texto e não como link. */
873
+ readonly onOpenConversation?: (conversationId: string) => void;
874
+ /** Origens filtráveis. Ausente, usa o vocabulário padrão (`customer`, `agent`, `bot`). */
875
+ readonly sources?: readonly FilterOption[];
876
+ /** Categorias de arquivo. Lista vazia esconde o filtro — nem todo host classifica anexo. */
877
+ readonly categories?: readonly FilterOption[];
878
+ /** Recorte por data. Desligado quando o backend não sabe filtrar por período. */
879
+ readonly dateFilter?: boolean;
880
+ /**
881
+ * Filtros do produto (cliente, unidade, campanha). Recebe os parâmetros extras atuais e devolve
882
+ * os controles; o que o produto guardar aqui viaja em `extra` para o `getAllDocuments`.
883
+ */
884
+ readonly renderFilters?: (context: DocumentsFiltersContext) => ReactNode;
885
+ readonly labels?: Partial<DocumentsWorkspaceLabels>;
886
+ readonly className?: string;
887
+ readonly classNames?: Partial<DocumentsWorkspaceClassNames>;
888
+ /** Espelhar filtros e paginação na URL. Desligado em preview, onde não há rota de verdade. */
889
+ readonly syncUrl?: boolean;
890
+ }
891
+ interface DocumentsFiltersContext {
892
+ readonly extra: Readonly<Record<string, string | number>>;
893
+ readonly setExtra: (next: Readonly<Record<string, string | number>>) => void;
894
+ }
895
+ declare function DocumentsWorkspace({ perPage: initialPerPage, perPageOptions, onOpenConversation, sources, categories, dateFilter, renderFilters, labels: labelsOverride, className, classNames, syncUrl, }: DocumentsWorkspaceProps): react.JSX.Element | null;
896
+
897
+ /**
898
+ * Estado de listagem espelhado na URL — ordenação, filtros e paginação (regra `web.md` §7).
899
+ *
900
+ * Escrito sobre `history.replaceState` e não sobre um router: o pacote roda em três produtos com
901
+ * routers diferentes, e exigir um deles arrastaria dependência de framework para dentro do módulo.
902
+ * `replaceState` também é o comportamento certo aqui — filtrar não é navegar, e cada tecla digitada
903
+ * na busca não deve virar uma entrada no botão "voltar".
904
+ */
905
+ /** `enabled: false` mantém o mesmo contrato de estado sem tocar na URL — para uso em preview e teste. */
906
+ interface UrlStateOptions {
907
+ readonly enabled?: boolean;
908
+ }
909
+ declare function useUrlStringState(key: string, initial: string, { enabled }?: UrlStateOptions): [string, (next: string) => void];
910
+ declare function useUrlNumberState(key: string, initial: number, { enabled }?: UrlStateOptions): [number, (next: number) => void];
911
+ declare function useUrlArrayState(key: string, { enabled }?: UrlStateOptions): [readonly string[], (next: readonly string[]) => void];
912
+ /**
913
+ * Espera o usuário parar de digitar antes de deixar o valor chegar à query. Sem isto, cada tecla
914
+ * na busca vira uma chamada de rede e uma reescrita de URL.
915
+ */
916
+ declare function useDebouncedValue<TValue>(value: TValue, delayMs?: number): TValue;
917
+
383
918
  /**
384
919
  * Arquivos trocados na conversa. Sai do transcript e vira lista própria porque anexo é o que o
385
920
  * atendente mais precisa reencontrar depois — rolar meses de mensagens para achar um comprovante é
@@ -388,29 +923,76 @@ declare function isWindowBlocking(window: ConversationWindow): boolean;
388
923
  * Usa `useConversationDocuments`, então funciona com qualquer `ConversationsApi`. Host sem
389
924
  * biblioteca de documentos cai no estado vazio, sem quebrar.
390
925
  */
926
+ /** Origens que o filtro oferece. `all` não é origem — é a ausência de filtro. */
927
+ declare const DOCUMENT_SOURCE_FILTER: {
928
+ readonly ALL: "all";
929
+ readonly CUSTOMER: "customer";
930
+ readonly TEAM: "team";
931
+ };
932
+ type DocumentSourceFilter = (typeof DOCUMENT_SOURCE_FILTER)[keyof typeof DOCUMENT_SOURCE_FILTER];
391
933
  interface ConversationDocumentsPanelLabels {
392
934
  toggle: string;
393
935
  title: string;
394
936
  searchPlaceholder: string;
395
937
  empty: string;
938
+ /** Distinto de `empty`: sem resultado POR CAUSA do filtro, e não conversa sem anexo nenhum. */
939
+ noResults: string;
396
940
  loading: string;
397
941
  failure: string;
398
942
  download: string;
943
+ view: string;
944
+ sourceFilterAll: string;
945
+ sourceFilterCustomer: string;
946
+ sourceFilterTeam: string;
947
+ sortMostRecent: string;
948
+ sortOldest: string;
949
+ clearFilters: string;
950
+ selectAll: string;
951
+ downloadSelected: (count: number) => string;
952
+ archiveFailed: string;
953
+ total: (count: number) => string;
954
+ page: (current: number, last: number) => string;
399
955
  }
400
956
  declare const DEFAULT_CONVERSATION_DOCUMENTS_LABELS: ConversationDocumentsPanelLabels;
957
+ /**
958
+ * Partes estilizáveis do painel, no mesmo contrato do `ConversationHeader`: `cn` funde por cima da
959
+ * base e conflito de utilitário (padding, tamanho de fonte, borda) fica com o valor do produto.
960
+ *
961
+ * `status` cobre carregando/erro/vazio de uma vez — são a mesma linha de texto auxiliar, e slots
962
+ * separados só multiplicariam chave para quem quer mudar a cor de aviso.
963
+ */
401
964
  interface ConversationDocumentsPanelClassNames {
402
965
  root: string;
403
966
  body: string;
967
+ title: string;
968
+ filters: string;
969
+ search: string;
970
+ sourceSelect: string;
971
+ sortButton: string;
972
+ clearButton: string;
973
+ status: string;
974
+ list: string;
975
+ item: string;
976
+ sourceBadge: string;
977
+ filename: string;
978
+ meta: string;
979
+ viewButton: string;
980
+ downloadButton: string;
981
+ pagination: string;
982
+ selectionBar: string;
983
+ checkbox: string;
404
984
  }
405
985
  interface ConversationDocumentsPanelProps {
406
986
  conversationId: string;
407
987
  /** Controlado de fora porque o gatilho vive no cabeçalho, junto das outras ações da conversa. */
408
988
  open: boolean;
989
+ /** Itens por página. O total vem do servidor; sem paginação no host, a barra não aparece. */
990
+ perPage?: number;
409
991
  labels?: Partial<ConversationDocumentsPanelLabels>;
410
992
  className?: string;
411
993
  classNames?: Partial<ConversationDocumentsPanelClassNames>;
412
994
  }
413
- declare function ConversationDocumentsPanel({ conversationId, open, labels: labelsOverride, className, classNames, }: ConversationDocumentsPanelProps): react.JSX.Element | null;
995
+ declare function ConversationDocumentsPanel({ conversationId, open, perPage, labels: labelsOverride, className, classNames, }: ConversationDocumentsPanelProps): react.JSX.Element | null;
414
996
 
415
997
  /**
416
998
  * Serialização do transcript para download. Fica no pacote porque o formato de um histórico de
@@ -451,6 +1033,49 @@ declare function useDarkMode(): {
451
1033
  toggle: () => void;
452
1034
  };
453
1035
 
1036
+ interface DarkModeToggleLabels {
1037
+ /** Ação de ir para o tema escuro — é o rótulo lido enquanto o tema claro está ativo. */
1038
+ toDark: string;
1039
+ toLight: string;
1040
+ }
1041
+ interface DarkModeToggleClassNames {
1042
+ /** O tamanho do ícone vem daqui quando o host usa a própria escala (`size-4`, `size-5`). */
1043
+ icon?: string;
1044
+ label?: string;
1045
+ }
1046
+ interface DarkModeToggleProps {
1047
+ isDark: boolean;
1048
+ onToggle: () => void;
1049
+ /** Rótulo ao lado do ícone. Só-ícone exige a área de toque mínima, que a classe `cv-touch` dá. */
1050
+ showLabel?: boolean;
1051
+ labels?: Partial<DarkModeToggleLabels>;
1052
+ /**
1053
+ * Substitui a aparência do botão. As classes passam por `tailwind-merge`, então utilitário do
1054
+ * host vence o do pacote em vez de somar: `px-4` descarta o `px-3` daqui.
1055
+ *
1056
+ * A exceção é utilitário de token do host (`rounded-panel`), que o merge não reconhece como
1057
+ * família e deixa passar junto com o `rounded-lg` do pacote — aí quem decide é a ordem no CSS
1058
+ * gerado, não a do atributo. Nesse caso use a forma arbitrária (`rounded-[var(--radius-panel)]`),
1059
+ * que o merge entende e substitui.
1060
+ */
1061
+ className?: string;
1062
+ classNames?: DarkModeToggleClassNames;
1063
+ }
1064
+ declare const DEFAULT_DARK_MODE_TOGGLE_LABELS: DarkModeToggleLabels;
1065
+ /**
1066
+ * Interruptor de tema, apresentacional: o estado vem do host, junto com `useDarkMode`.
1067
+ *
1068
+ * Ele não monta o hook de propósito. `useDarkMode` é o controlador — escreve a classe `dark` no
1069
+ * `<html>` e persiste a escolha —, e uma barra lateral que também existe como gaveta fica montada
1070
+ * duas vezes na mesma tela. Dois controladores guardam estados iniciais separados, e o segundo
1071
+ * botão passa a mostrar o ícone contrário ao tema que está no ar. Com o estado no host, o hook é
1072
+ * chamado uma vez acima dos dois e as duas cópias concordam.
1073
+ *
1074
+ * O rótulo nomeia o destino, não o estado atual: no claro se lê "Tema escuro", que é o que o clique
1075
+ * faz. `aria-pressed` diria o oposto na mesma frase, então fica de fora.
1076
+ */
1077
+ declare function DarkModeToggle({ isDark, onToggle, showLabel, labels, className, classNames, }: DarkModeToggleProps): react.JSX.Element;
1078
+
454
1079
  /**
455
1080
  * Detecta tela estreita para decisões que CSS não resolve — como abrir ou não um painel por padrão.
456
1081
  *
@@ -461,11 +1086,35 @@ declare function useDarkMode(): {
461
1086
  declare const NARROW_MAX_WIDTH_PX = 1023;
462
1087
  declare function useIsNarrow(): boolean;
463
1088
 
1089
+ interface UseWaitingNotificationsLabels {
1090
+ /** Título da notificação do sistema. Recebe a conversa para o host escolher nome × número. */
1091
+ title: (conversation: ConversationSummary) => string;
1092
+ body: (conversation: ConversationSummary) => string;
1093
+ }
1094
+ interface UseWaitingNotificationsParams {
1095
+ /**
1096
+ * Repassado cru ao `fetchConversations`. É o que permite filtrar não lidas **no servidor** em
1097
+ * vez de baixar a lista inteira e contar no cliente: um painel com milhares de conversas não
1098
+ * pode paginar 50 por vez atrás de quem tem `unread > 0`.
1099
+ */
1100
+ readonly params?: ListConversationsParams;
1101
+ readonly intervalMs?: number;
1102
+ /** Desliga o polling sem desmontar quem chama — útil com a aba em segundo plano. */
1103
+ readonly enabled?: boolean;
1104
+ readonly icon?: string;
1105
+ readonly labels?: Partial<UseWaitingNotificationsLabels>;
1106
+ }
464
1107
  interface UseWaitingNotificationsResult {
465
1108
  unreadCount: number;
466
1109
  conversations: ConversationSummary[];
1110
+ /**
1111
+ * Releitura sob demanda. Existe porque o polling é o piso, não o mecanismo: quem já recebe SSE
1112
+ * ou acabou de marcar tudo como lido sabe da mudança antes do próximo tick, e esperar 10s para
1113
+ * o contador acompanhar faz a interface parecer travada.
1114
+ */
1115
+ refresh: () => Promise<void>;
467
1116
  }
468
- declare function useWaitingNotifications(): UseWaitingNotificationsResult;
1117
+ declare function useWaitingNotifications(params?: UseWaitingNotificationsParams): UseWaitingNotificationsResult;
469
1118
 
470
1119
  interface ConversationsContextValue {
471
1120
  api: ConversationsApi;
@@ -547,6 +1196,7 @@ interface WhatsAppCreateTemplateFormLabels {
547
1196
  sectionDescription: string;
548
1197
  nameLabel: string;
549
1198
  nameHint: string;
1199
+ namePlaceholder: string;
550
1200
  categoryLabel: string;
551
1201
  languageLabel: string;
552
1202
  headerLabel: string;
@@ -612,6 +1262,47 @@ interface WelcomeFarewellFormProps {
612
1262
  }
613
1263
  declare function WelcomeFarewellForm({ welcomeMessage, onWelcomeMessageChange, farewellMessage, onFarewellMessageChange, onSave, saving, saveSuccess, welcomePlaceholders, farewellPlaceholders, labels: labelsOverride, }: WelcomeFarewellFormProps): react.JSX.Element;
614
1264
 
1265
+ interface TranscriptionSettingsFormLabels {
1266
+ sectionTitle: string;
1267
+ enabled: string;
1268
+ enabledHint: string;
1269
+ modeTitle: string;
1270
+ modeAuto: string;
1271
+ modeAutoHint: string;
1272
+ modeOnDemand: string;
1273
+ modeOnDemandHint: string;
1274
+ unavailable: string;
1275
+ save: string;
1276
+ saving: string;
1277
+ saveSuccess: string;
1278
+ }
1279
+ interface TranscriptionSettingsFormProps {
1280
+ enabled: boolean;
1281
+ onEnabledChange: (value: boolean) => void;
1282
+ mode: TranscriptionMode;
1283
+ onModeChange: (value: TranscriptionMode) => void;
1284
+ onSave: (event: FormEvent) => void;
1285
+ /**
1286
+ * A capacidade existe no servidor (engine e credencial configurados)?
1287
+ *
1288
+ * Distinto de `enabled`: isto é "o ambiente consegue", aquilo é "esta empresa quer". Sem a
1289
+ * distinção, um lojista ligaria o interruptor num ambiente sem engine e concluiria que o produto
1290
+ * está quebrado — o texto nunca apareceria e nada explicaria por quê.
1291
+ */
1292
+ isAvailable?: boolean;
1293
+ saving?: boolean;
1294
+ saveSuccess?: boolean;
1295
+ labels?: Partial<TranscriptionSettingsFormLabels>;
1296
+ }
1297
+ /**
1298
+ * Interruptor de transcrição por empresa, para a tela de configurações.
1299
+ *
1300
+ * Puramente apresentacional, como os outros forms daqui: quem persiste é o host. O pacote não sabe a
1301
+ * rota, e a política vive nas configurações do módulo (`settings.transcriptionEnabled`), não em
1302
+ * variável de ambiente — mudar de ideia sobre transcrever não deveria exigir deploy.
1303
+ */
1304
+ declare function TranscriptionSettingsForm({ enabled, onEnabledChange, mode, onModeChange, onSave, isAvailable, saving, saveSuccess, labels: labelsOverride, }: TranscriptionSettingsFormProps): react.JSX.Element;
1305
+
615
1306
  declare const TEMPLATE_SETTINGS_TAB: {
616
1307
  readonly SELECT: "select";
617
1308
  readonly CREATE: "create";
@@ -642,10 +1333,21 @@ interface WhatsAppTemplatesSettingsProps {
642
1333
  onSubmit: (event: FormEvent) => void;
643
1334
  submitting?: boolean;
644
1335
  result?: WhatsAppCreateTemplateResult | null;
1336
+ labels?: Partial<WhatsAppCreateTemplateFormLabels>;
1337
+ /** Nome exibido na prévia do template criado; sem isto o form usa seu próprio fallback. */
1338
+ previewCompanyName?: string;
1339
+ variableExamples?: readonly string[];
645
1340
  };
646
1341
  labels?: Partial<WhatsAppTemplatesSettingsLabels>;
1342
+ /** Vocabulário do formulário de seleção — separado de `labels`, que nomeia só as abas daqui. */
1343
+ settingsLabels?: Partial<WhatsAppTemplateSettingsFormLabels>;
1344
+ /**
1345
+ * Formulários extras de seleção (um por papel adicional de template, ex.: despedida), empilhados
1346
+ * abaixo do principal na mesma sub-aba. Ausente = só o papel principal, comportamento de sempre.
1347
+ */
1348
+ extraRoleForms?: ReactNode;
647
1349
  }
648
- declare function WhatsAppTemplatesSettings({ labels: labelsOverride, create, ...settingsProps }: WhatsAppTemplatesSettingsProps): react.JSX.Element;
1350
+ declare function WhatsAppTemplatesSettings({ labels: labelsOverride, settingsLabels, create, extraRoleForms, ...settingsProps }: WhatsAppTemplatesSettingsProps): react.JSX.Element;
649
1351
 
650
1352
  interface TopicItem {
651
1353
  key: string;
@@ -671,6 +1373,88 @@ interface TopicsFormProps {
671
1373
  }
672
1374
  declare function TopicsForm({ topics, onToggle, onMessageChange, onSave, saving, saveSuccess, labels: labelsOverride, }: TopicsFormProps): react.JSX.Element;
673
1375
 
1376
+ interface BotMessages {
1377
+ welcomeMessage: string;
1378
+ farewellMessage: string;
1379
+ }
1380
+ interface TemplateSettings {
1381
+ templateName: string;
1382
+ templateLanguage: string;
1383
+ variables: string[];
1384
+ }
1385
+ /**
1386
+ * Papel adicional de template (ex.: despedida) além do principal (`getTemplateSettings`/
1387
+ * `saveTemplateSettings`). Cada papel ganha seu próprio formulário empilhado na mesma aba, com
1388
+ * carregamento e salvamento independentes — é o que permite hosts com mais de um papel (o
1389
+ * financiamento tem boas-vindas + despedida) convergirem para este componente em vez de remontar
1390
+ * a tela à parte.
1391
+ */
1392
+ interface MessagesWorkspaceTemplateRole {
1393
+ readonly key: string;
1394
+ readonly labels: {
1395
+ sectionTitle: string;
1396
+ sectionDescription: string;
1397
+ };
1398
+ getSettings(): Promise<TemplateSettings>;
1399
+ saveSettings(settings: TemplateSettings): Promise<void>;
1400
+ }
1401
+ interface TranscriptionSettings {
1402
+ enabled: boolean;
1403
+ mode: TranscriptionMode;
1404
+ available: boolean;
1405
+ }
1406
+ interface MessagesWorkspaceApi {
1407
+ getMessages(): Promise<BotMessages>;
1408
+ saveMessages(messages: BotMessages): Promise<void>;
1409
+ /** Sem este par, a seção de tópicos não é desenhada. */
1410
+ getTopics?(): Promise<TopicItem[]>;
1411
+ saveTopics?(topics: TopicItem[]): Promise<void>;
1412
+ /** Sem este par, a aba de templates não é desenhada. */
1413
+ getTemplateSettings?(): Promise<TemplateSettings>;
1414
+ saveTemplateSettings?(settings: TemplateSettings): Promise<void>;
1415
+ /** Papéis adicionais de template (ex.: despedida) além do principal acima. */
1416
+ templateRoles?: MessagesWorkspaceTemplateRole[];
1417
+ /**
1418
+ * Lista de templates aprovados na Meta. Ausente, a aba ainda existe (dá para salvar o nome
1419
+ * escolhido), mas nasce sem opções e sem botão de recarregar.
1420
+ */
1421
+ listTemplates?(): Promise<WhatsAppTemplateSummary[]>;
1422
+ /** Sem isto, o submenu "Criar template" some. */
1423
+ createTemplate?(input: WhatsAppCreateTemplateState): Promise<WhatsAppCreateTemplateResult>;
1424
+ /** Sem este par, a aba de transcrição não é desenhada. */
1425
+ getTranscription?(): Promise<TranscriptionSettings>;
1426
+ saveTranscription?(settings: Omit<TranscriptionSettings, 'available'>): Promise<void>;
1427
+ }
1428
+ interface MessagesWorkspaceLabels {
1429
+ title: string;
1430
+ subtitle: string;
1431
+ loading: string;
1432
+ loadError: string;
1433
+ tabBot: string;
1434
+ tabTemplates: string;
1435
+ tabTranscription: string;
1436
+ welcomeFarewell: Partial<WelcomeFarewellFormLabels>;
1437
+ topics: Partial<TopicsFormLabels>;
1438
+ templates: Partial<WhatsAppTemplatesSettingsLabels>;
1439
+ templateSettings: Partial<WhatsAppTemplateSettingsFormLabels>;
1440
+ createTemplate: Partial<WhatsAppCreateTemplateFormLabels>;
1441
+ transcription: Partial<TranscriptionSettingsFormLabels>;
1442
+ }
1443
+ interface MessagesWorkspaceProps {
1444
+ readonly api: MessagesWorkspaceApi;
1445
+ readonly labels?: Partial<MessagesWorkspaceLabels>;
1446
+ readonly welcomePlaceholders?: readonly string[];
1447
+ readonly farewellPlaceholders?: readonly string[];
1448
+ readonly availableVariables?: WhatsAppTemplateVariableSuggestion[];
1449
+ /** Aviso do produto acima da aba de templates (ex.: rota da Graph API ainda não implementada). */
1450
+ readonly renderTemplatesNotice?: () => ReactNode;
1451
+ /** Repassados ao formulário de criação — mesma prévia que `WhatsAppCreateTemplateForm` já suporta. */
1452
+ readonly createTemplatePreviewCompanyName?: string;
1453
+ readonly createTemplateVariableExamples?: readonly string[];
1454
+ readonly className?: string;
1455
+ }
1456
+ declare function MessagesWorkspace({ api, labels: labelsOverride, welcomePlaceholders, farewellPlaceholders, availableVariables, renderTemplatesNotice, createTemplatePreviewCompanyName, createTemplateVariableExamples, className, }: MessagesWorkspaceProps): react.JSX.Element;
1457
+
674
1458
  interface UseConversationMessagesResult {
675
1459
  messages: MessagePayload[];
676
1460
  loading: boolean;
@@ -684,7 +1468,7 @@ interface UseConversationMessagesResult {
684
1468
  caption?: string;
685
1469
  }) => Promise<MessagePayload>;
686
1470
  sendTemplate: (data: {
687
- templateName: string;
1471
+ templateName?: string;
688
1472
  languageCode?: string;
689
1473
  bodyParams?: string[];
690
1474
  }) => Promise<void>;
@@ -695,14 +1479,28 @@ declare function useConversationMessages(conversationId: string, params?: {
695
1479
  before?: string;
696
1480
  }): UseConversationMessagesResult;
697
1481
 
698
- interface UseConversationListParams {
699
- page?: number;
700
- limit?: number;
701
- waitingHuman?: boolean;
702
- search?: string;
703
- }
1482
+ type UseScrollToLatestMessageParams = {
1483
+ /** Troca de conversa. Muda ⇒ salto instantâneo para o fim. */
1484
+ readonly conversationId: string | undefined;
1485
+ /** Quantidade de mensagens carregadas. Cresce ⇒ acompanha o fim, se o operador estiver lá. */
1486
+ readonly messageCount: number;
1487
+ };
1488
+ type UseScrollToLatestMessageResult = {
1489
+ /** Vai no elemento que rola — tipicamente o `ConversationWallpaper`. */
1490
+ readonly containerRef: RefObject<HTMLDivElement | null>;
1491
+ /** Ligue no `onScroll` do mesmo elemento: é o que detecta o operador lendo o histórico. */
1492
+ readonly handleScroll: (event: UIEvent<HTMLDivElement>) => void;
1493
+ /** `true` quando o operador rolou para trás — serve a um botão "ir para a última". */
1494
+ readonly isAwayFromBottom: boolean;
1495
+ readonly scrollToBottom: (behavior?: ScrollBehavior) => void;
1496
+ };
1497
+ declare function useScrollToLatestMessage({ conversationId, messageCount, }: UseScrollToLatestMessageParams): UseScrollToLatestMessageResult;
1498
+
1499
+ type UseConversationListParams = ListConversationsParams;
704
1500
  interface UseConversationListResult {
705
1501
  conversations: ConversationSummary[];
1502
+ /** Total no servidor. Cai para o tamanho da página quando a API devolve só o array. */
1503
+ total: number;
706
1504
  loading: boolean;
707
1505
  error: Error | undefined;
708
1506
  refetch: () => Promise<void>;
@@ -717,12 +1515,11 @@ interface UseConversationContextResult {
717
1515
  }
718
1516
  declare function useConversationContext(conversationId: string): UseConversationContextResult;
719
1517
 
720
- interface UseConversationDocumentsParams {
721
- search?: string;
722
- page?: number;
723
- }
1518
+ type UseConversationDocumentsParams = ListDocumentsParams;
724
1519
  interface UseConversationDocumentsResult {
725
1520
  documents: ConversationDocument[];
1521
+ /** Total no servidor. Cai para o tamanho da página quando a API devolve só o array. */
1522
+ total: number;
726
1523
  loading: boolean;
727
1524
  error: Error | undefined;
728
1525
  refetch: () => Promise<void>;
@@ -733,6 +1530,27 @@ type ConversationRealtimeHandler = (event: MessageEvent) => void;
733
1530
  declare function useConversationRealtime(conversationId: string | undefined, onEvent: ConversationRealtimeHandler): void;
734
1531
  declare function useGlobalRealtime(onEvent: ConversationRealtimeHandler): void;
735
1532
 
1533
+ interface UseConversationActionsResult {
1534
+ /** `undefined` quando a API do host não implementa a operação — a UI esconde a afordância. */
1535
+ takeover: (() => Promise<void>) | undefined;
1536
+ release: (() => Promise<void>) | undefined;
1537
+ finalize: (() => Promise<void>) | undefined;
1538
+ }
1539
+ /**
1540
+ * Ações de atendimento de UMA conversa, já ligadas ao id.
1541
+ *
1542
+ * Separado de `useConversationMessages` porque assumir e devolver conversa também acontece a
1543
+ * partir da lista, onde nenhuma thread está aberta — embutir nas mensagens obrigaria a carregar
1544
+ * a thread inteira só para desenhar um botão na linha.
1545
+ */
1546
+ declare function useConversationActions(conversationId: string): UseConversationActionsResult;
1547
+ interface UseInboxActionsResult {
1548
+ markAllRead: (() => Promise<void>) | undefined;
1549
+ listTemplates: (() => Promise<ConversationTemplate[]>) | undefined;
1550
+ }
1551
+ /** Ações que valem para a caixa inteira, sem conversa selecionada. */
1552
+ declare function useInboxActions(): UseInboxActionsResult;
1553
+
736
1554
  declare function parseWhatsAppFormatting(text: string): ReactNode[];
737
1555
  declare function waToHTML(text: string): string;
738
1556
  declare function htmlToWA(html: string): string;
@@ -742,6 +1560,8 @@ declare function formatPhone(number: string): string;
742
1560
  declare function phoneInitials(number: string): string;
743
1561
 
744
1562
  declare function formatTimestamp(timestamp: string): string;
1563
+ declare function formatDateTime(iso: string): string;
1564
+ declare function isSameDay(a: Date, b: Date): boolean;
745
1565
  declare function formatFileSize(bytes: number): string;
746
1566
 
747
1567
  interface AsyncResourceState<T> {
@@ -751,4 +1571,313 @@ interface AsyncResourceState<T> {
751
1571
  refetch: () => Promise<void>;
752
1572
  }
753
1573
 
754
- export { type AsyncResourceState, AudioPlayer, type AudioPlayerProps, Avatar, type AvatarProps, type BuildTranscriptTextParams, CHANNEL_BRAND_COLOR, CONVERSATION_WINDOW, ChannelIcon, type ChannelIconProps, ConversationChannel, type ConversationContextEntry, ConversationContextPanel, type ConversationContextPanelClassNames, type ConversationContextPanelLabels, type ConversationContextPanelProps, ConversationDocument, ConversationDocumentsPanel, type ConversationDocumentsPanelClassNames, type ConversationDocumentsPanelLabels, type ConversationDocumentsPanelProps, ConversationHeader, type ConversationHeaderClassNames, type ConversationHeaderLabels, type ConversationHeaderProps, ConversationListItem, type ConversationListItemProps, type ConversationLocales, ConversationLocalesProvider, type ConversationLocalesProviderProps, type ConversationRealtimeHandler, ConversationRow, type ConversationRowClassNames, type ConversationRowProps, ConversationSummary, ConversationWallpaper, type ConversationWallpaperProps, type ConversationWindow, ConversationsApi, ConversationsFeatures, ConversationsProvider, DEFAULT_CONVERSATION_CONTEXT_LABELS, DEFAULT_CONVERSATION_DOCUMENTS_LABELS, DEFAULT_CONVERSATION_HEADER_LABELS, DEFAULT_TEMPLATES_SETTINGS_LABELS, DEFAULT_WINDOW_EXPIRED_LABELS, DateDivider, type DateDividerClassNames, type DateDividerProps, EmojiPicker, type EmojiPickerProps, FileIcon, type FileIconProps, Lightbox, type LightboxProps, MediaRenderer, type MediaRendererProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerClassNames, type MessageComposerProps, MessagePayload, MessageTail, MessageText, type MessageTextProps, MessageTimestamp, NARROW_MAX_WIDTH_PX, type ResolveMediaUrl, SSEProvider, SimpleEmojiPicker, type SimpleEmojiPickerProps, StatusTicks, type StatusTicksProps, TEMPLATE_SETTINGS_TAB, type TemplateSettingsTab, ToastProvider, type TopicItem, TopicsForm, type TopicsFormLabels, type TopicsFormProps, type UseConversationContextResult, type UseConversationDocumentsParams, type UseConversationDocumentsResult, type UseConversationListParams, type UseConversationListResult, type UseConversationMessagesResult, WINDOW_FILTERS, WelcomeFarewellForm, type WelcomeFarewellFormLabels, type WelcomeFarewellFormProps, WhatsAppCreateTemplateForm, type WhatsAppCreateTemplateFormLabels, type WhatsAppCreateTemplateFormProps, type WhatsAppCreateTemplateResult, type WhatsAppCreateTemplateState, WhatsAppMessageEditor, type WhatsAppMessageEditorProps, type WhatsAppTemplateHeaderType, WhatsAppTemplateSettingsForm, type WhatsAppTemplateSettingsFormLabels, type WhatsAppTemplateSettingsFormProps, type WhatsAppTemplateSummary, type WhatsAppTemplateVariableSuggestion, WhatsAppTemplatesSettings, type WhatsAppTemplatesSettingsLabels, type WhatsAppTemplatesSettingsProps, WindowExpiredNotice, type WindowExpiredNoticeLabels, type WindowExpiredNoticeProps, type WindowOfParams, buildTranscriptFilename, buildTranscriptText, downloadTextFile, formatFileSize, formatPhone, formatStalledFor, formatTimestamp, htmlToWA, isWindowBlocking, parseWhatsAppFormatting, phoneInitials, toast, useConversationContext, useConversationDocuments, useConversationList, useConversationLocales, useConversationMessages, useConversationRealtime, useConversations, useDarkMode, useGlobalRealtime, useIsDarkTheme, useIsNarrow, useToast, useWaitingNotifications, waToHTML, waToHTMLInline, windowOf };
1574
+ /**
1575
+ * Resolve a mídia de uma mensagem numa URL exibível, usando só o `ConversationsApi`.
1576
+ *
1577
+ * Mora no pacote, e não em cada host, porque a regra não tem nada de específico de produto: é a
1578
+ * tradução de `uploadId`/`mediaId` pelos dois métodos que o próprio contrato já declara. Deixá-la no
1579
+ * host significava que todo projeto que adotasse o SDK reescreveria as mesmas oito linhas — e, na
1580
+ * prática, ninguém escrevia: o `MediaRenderer` só busca mídia pela porta `onResolveMediaUrl`, então
1581
+ * onde nada era injetado foto, vídeo e áudio ficavam no placeholder para sempre.
1582
+ */
1583
+
1584
+ declare function createMediaUrlResolver(api: Pick<ConversationsApi, 'getDocumentUrl' | 'getMediaProxyUrl'>): ResolveMediaUrl;
1585
+
1586
+ /**
1587
+ * Textos da inbox. Todos sobrescrevíveis: o produto troca vocabulário ("cliente" vira "lead") ou
1588
+ * o idioma inteiro sem tocar no layout — e sem manter uma cópia da tela para isso.
1589
+ */
1590
+ interface ConversationsWorkspaceLabels {
1591
+ readonly title: string;
1592
+ readonly conversations: string;
1593
+ readonly waiting: string;
1594
+ readonly unread: string;
1595
+ readonly waitingOnly: string;
1596
+ readonly markSelectedAsRead: string;
1597
+ readonly search: string;
1598
+ readonly windowLegend: string;
1599
+ readonly channelLegend: string;
1600
+ readonly selectAll: string;
1601
+ readonly emptyList: string;
1602
+ readonly emptyDetail: string;
1603
+ readonly bulkSelected: (count: number) => string;
1604
+ readonly bulkClear: string;
1605
+ readonly bulkFinalize: string;
1606
+ readonly bulkFinalizeConfirm: (count: number) => string;
1607
+ readonly bulkTemplate: string;
1608
+ readonly markAllAsRead: string;
1609
+ readonly templateModalTitle: string;
1610
+ readonly templateModalSearch: string;
1611
+ readonly templateModalEmpty: string;
1612
+ readonly templateModalCancel: string;
1613
+ readonly templateModalSending: string;
1614
+ readonly templateModalNoneExpired: string;
1615
+ readonly templateModalAvailable: (count: number) => string;
1616
+ readonly templateModalSend: (count: number) => string;
1617
+ readonly messagesSelected: (count: number) => string;
1618
+ readonly copySelected: string;
1619
+ readonly composerPlaceholder: string;
1620
+ readonly attachFailure: string;
1621
+ /** Tira um arquivo da fila antes de enviar. */
1622
+ readonly attachmentRemove: string;
1623
+ readonly recordFailure: string;
1624
+ readonly sendFailure: string;
1625
+ readonly takeoverToReply: string;
1626
+ readonly signIn: string;
1627
+ readonly pageOf: (page: number, pageCount: number) => string;
1628
+ readonly rangeOf: (first: number, last: number, total: number) => string;
1629
+ }
1630
+ declare const DEFAULT_CONVERSATIONS_WORKSPACE_LABELS: ConversationsWorkspaceLabels;
1631
+
1632
+ /**
1633
+ * Estado da inbox: filtros, paginação, seleção em massa e ações de atendimento.
1634
+ *
1635
+ * Nasceu igual em três produtos e divergiu em três — contadores diferentes, um marcava lida ao
1636
+ * abrir e outro não, um perdia a página ao trocar de filtro. É a lógica de operar uma inbox, não
1637
+ * regra de nenhum negócio, e por isso mora aqui.
1638
+ *
1639
+ * A paginação é do cliente porque nem todo backend pagina a listagem. Fatiar aqui mantém a tela
1640
+ * utilizável com centenas de conversas; com dezenas de milhares o gargalo volta e a resposta é
1641
+ * paginar no servidor.
1642
+ */
1643
+
1644
+ declare const CONVERSATIONS_PER_PAGE = 50;
1645
+ interface UseConversationsInboxParams {
1646
+ /** Recortes do produto repassados crus ao backend dele (carteira, campanha, tipo). */
1647
+ readonly filters?: Record<string, string | undefined>;
1648
+ readonly perPage?: number;
1649
+ /**
1650
+ * Marca como lida a conversa aberta. Ligado por padrão: badge subindo na conversa que o atendente
1651
+ * tem na frente é ruído que ele não tem como resolver.
1652
+ */
1653
+ readonly markReadOnOpen?: boolean;
1654
+ /**
1655
+ * Pede a página ao servidor em vez de fatiar o que veio. Necessário quando a base é grande — o
1656
+ * padrão traz tudo e pagina no cliente porque nem todo backend pagina a listagem.
1657
+ */
1658
+ readonly serverPaginated?: boolean;
1659
+ readonly describeFailure?: (error: unknown) => string;
1660
+ }
1661
+ interface UseConversationsInboxResult {
1662
+ readonly conversations: readonly ConversationSummary[];
1663
+ readonly pageConversations: readonly ConversationSummary[];
1664
+ readonly selectedConversation: ConversationSummary | undefined;
1665
+ readonly loading: boolean;
1666
+ readonly now: number;
1667
+ readonly totalCount: number;
1668
+ readonly unreadCount: number;
1669
+ readonly waitingCount: number;
1670
+ readonly filteredCount: number;
1671
+ /** Selecionadas fora da janela de 24h — as únicas que um template alcança. */
1672
+ readonly expiredSelectedCount: number;
1673
+ readonly page: number;
1674
+ readonly pageCount: number;
1675
+ readonly firstOnPage: number;
1676
+ readonly lastOnPage: number;
1677
+ readonly selectedId: string | undefined;
1678
+ readonly selectedIds: ReadonlySet<string>;
1679
+ readonly allOnPageSelected: boolean;
1680
+ readonly waitingOnly: boolean;
1681
+ readonly windowFilter: ConversationWindow;
1682
+ readonly channelFilter: ChannelFilter;
1683
+ readonly channelFilters: readonly ChannelFilterOption[];
1684
+ readonly search: string;
1685
+ readonly busy: boolean;
1686
+ /** Por que a lista está vazia, quando não é por não haver conversa. */
1687
+ readonly loadFailure: string | undefined;
1688
+ /** Ausentes quando o host não implementa a capacidade — a UI não desenha a afordância. */
1689
+ readonly canTakeover: boolean;
1690
+ readonly canFinalize: boolean;
1691
+ readonly canListTemplates: boolean;
1692
+ readonly canMarkAllRead: boolean;
1693
+ refetch(): Promise<void> | void;
1694
+ selectConversation(conversationId: string): void;
1695
+ clearSelection(): void;
1696
+ toggleSelected(conversationId: string): void;
1697
+ toggleSelectAllOnPage(): void;
1698
+ clearBulkSelection(): void;
1699
+ setWaitingOnly(value: boolean): void;
1700
+ setWindowFilter(value: ConversationWindow): void;
1701
+ setChannelFilter(value: ChannelFilter): void;
1702
+ setSearch(value: string): void;
1703
+ goToPage(value: number): void;
1704
+ markSelectedAsRead(): Promise<void>;
1705
+ markAllAsRead(): Promise<void>;
1706
+ takeover(conversationId: string): Promise<void>;
1707
+ releaseToBot(conversationId: string): Promise<void>;
1708
+ finalize(conversationId: string): Promise<void>;
1709
+ finalizeSelected(): Promise<void>;
1710
+ sendTemplateToSelected(templateName?: string): Promise<void>;
1711
+ }
1712
+ declare function useConversationsInbox(params?: UseConversationsInboxParams): UseConversationsInboxResult;
1713
+
1714
+ type SimulatorTransportParams = {
1715
+ readonly conversationId: string;
1716
+ readonly channel: ConversationChannel;
1717
+ /** Identificador do contato no canal: telefone no WhatsApp, id de sessão no chat do site. */
1718
+ readonly handle: string;
1719
+ };
1720
+ /**
1721
+ * Fábrica do transporte daquele canal.
1722
+ *
1723
+ * É o único ponto onde o host precisa saber de canal: o painel, a moldura e o comportamento são os
1724
+ * mesmos em todos. No WhatsApp devolve o cliente-ponte (assinatura no servidor do host); no chat do
1725
+ * site, o cliente das rotas do widget.
1726
+ */
1727
+ type SimulatorTransportFactory = (params: SimulatorTransportParams) => ConversationSimulatorClient;
1728
+ interface ConversationsWorkspaceSimulator {
1729
+ /**
1730
+ * Um transporte por canal — o workspace monta o painel com o da conversa selecionada.
1731
+ *
1732
+ * Canal sem transporte não desenha o botão: capacidade é opcional por ausência, e oferecer
1733
+ * "simular" numa conversa que não tem como receber a mensagem é um botão que falha ao ser tocado.
1734
+ */
1735
+ readonly transports?: Partial<Record<ConversationChannel, SimulatorTransportFactory>>;
1736
+ /**
1737
+ * Válvula de escape: o host desenha o painel inteiro. Tem precedência sobre `transports`.
1738
+ *
1739
+ * Era a única porta antes de `transports`, quando o simulador só falava WhatsApp e cada produto
1740
+ * remontava a moldura — o que fez duas telas da mesma casa divergirem. Continua aceito para não
1741
+ * quebrar quem já a usa.
1742
+ */
1743
+ render?(params: {
1744
+ conversationId: string;
1745
+ channel: ConversationChannel;
1746
+ close: () => void;
1747
+ }): ReactNode;
1748
+ /** Ausente = ligado. Serve para esconder fora de desenvolvimento sem condicionar o JSX. */
1749
+ readonly enabled?: boolean;
1750
+ /** Ícone da biblioteca (lucide) no utilitário do cabeçalho. Ausente, entra o frasco de teste. */
1751
+ readonly icon?: ReactNode;
1752
+ readonly label?: string;
1753
+ /** Vocabulário do painel. O que muda de canal (destino, placeholder) já vem resolvido. */
1754
+ readonly labels?: Partial<ConversationSimulatorPanelLabels>;
1755
+ /** Destino do upload no canal que entrega mídia por referência (o caminho da Meta). */
1756
+ readonly uploadMedia?: (file: File) => Promise<PreviewUploadedMedia>;
1757
+ /** Recarrega o transcript a cada N ms. Serve a host sem stream. */
1758
+ readonly pollIntervalMs?: number;
1759
+ }
1760
+ interface ConversationsWorkspaceProps {
1761
+ readonly labels?: Partial<ConversationsWorkspaceLabels>;
1762
+ readonly filters?: Record<string, string | undefined>;
1763
+ readonly perPage?: number;
1764
+ readonly markReadOnOpen?: boolean;
1765
+ /** Pede a página ao servidor em vez de fatiar no cliente. */
1766
+ readonly serverPaginated?: boolean;
1767
+ /** Conversa a abrir na montagem (deep link `?id=`). */
1768
+ readonly initialConversationId?: string | undefined;
1769
+ /** Idem, pelo telefone — é o que costuma vir no link de um alerta ou de um pedido. */
1770
+ readonly initialWhatsappNumber?: string | undefined;
1771
+ readonly simulator?: ConversationsWorkspaceSimulator;
1772
+ readonly quickReplies?: readonly QuickReply[];
1773
+ readonly quickReplyVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => Record<string, string>;
1774
+ /** Etapa do fluxo mostrada no painel de contexto. */
1775
+ readonly flowLabelOf?: (conversation: ConversationSummary) => string | undefined;
1776
+ /** Substitui o download local do transcript (ex.: exportação completa pela rota do servidor). */
1777
+ readonly onDownload?: (conversation: ConversationSummary) => void;
1778
+ /** Bloqueia o composer enquanto a conversa estiver com o bot. */
1779
+ readonly requireTakeoverToReply?: boolean;
1780
+ /** Deixa marcar mensagens no transcript e copiar o trecho. */
1781
+ readonly messageSelection?: boolean;
1782
+ /** Texto já no campo ao abrir a conversa (deep link que sugere a resposta). */
1783
+ readonly initialComposerText?: string | undefined;
1784
+ /** `rich` troca o campo simples pelo texto com a formatação do WhatsApp desenhada ao escrever. */
1785
+ readonly composer?: 'simple' | 'rich';
1786
+ /** Valores que o operador insere sem digitar. Só o composer `rich` os oferece. */
1787
+ readonly composerVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => readonly RichComposerVariable[];
1788
+ /** Fila de anexos com legenda, como no WhatsApp. Ausente, o clipe manda cada arquivo na hora. */
1789
+ readonly onSendAttachments?: (conversation: ConversationSummary, files: readonly File[], caption: string) => Promise<void>;
1790
+ /** Nota de voz. Ausente, o microfone não aparece. */
1791
+ readonly onRecordAudio?: (conversation: ConversationSummary, file: File) => Promise<void>;
1792
+ readonly contextEntriesOf?: (context: Record<string, unknown> | undefined) => readonly ConversationContextEntry[];
1793
+ readonly onAttach?: (conversation: ConversationSummary, file: File) => Promise<void>;
1794
+ readonly extraUtilitiesFor?: (conversation: ConversationSummary) => readonly ConversationHeaderUtility[];
1795
+ readonly renderFilters?: (inbox: UseConversationsInboxResult) => ReactNode;
1796
+ readonly renderBulkActions?: (inbox: UseConversationsInboxResult) => ReactNode;
1797
+ readonly renderRow?: (conversation: ConversationSummary) => ReactNode;
1798
+ readonly renderAboveTranscript?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => ReactNode;
1799
+ readonly renderHeaderActions?: (inbox: UseConversationsInboxResult) => ReactNode;
1800
+ readonly onSendTemplateToSelected?: (inbox: UseConversationsInboxResult) => void;
1801
+ /** Destino do link de reentrar no painel, mostrado junto do aviso de sessão expirada. */
1802
+ readonly signInHref?: string;
1803
+ readonly className?: string;
1804
+ }
1805
+ declare function ConversationsWorkspace({ labels: labelsOverride, filters, perPage, markReadOnOpen, serverPaginated, initialConversationId, initialWhatsappNumber, simulator, quickReplies, quickReplyVariablesFor, flowLabelOf, onDownload, requireTakeoverToReply, messageSelection, initialComposerText, composer, composerVariablesFor, onSendAttachments, onRecordAudio, contextEntriesOf, onAttach, extraUtilitiesFor, renderFilters, renderBulkActions, renderRow, renderAboveTranscript, renderHeaderActions, onSendTemplateToSelected, signInHref, className, }: ConversationsWorkspaceProps): react.JSX.Element;
1806
+
1807
+ interface ConversationPaneProps {
1808
+ readonly conversation: ConversationSummary;
1809
+ readonly now: number;
1810
+ readonly busy: boolean;
1811
+ readonly labels: ConversationsWorkspaceLabels;
1812
+ /** Devolve a promessa quando o host a tem: o envio de template espera a tomada antes de sair. */
1813
+ readonly onTakeover?: (() => void | Promise<void>) | undefined;
1814
+ readonly onReturnToBot?: (() => void) | undefined;
1815
+ readonly onFinish?: (() => void) | undefined;
1816
+ readonly onBack: () => void;
1817
+ readonly extraUtilities?: readonly ConversationHeaderUtility[];
1818
+ /** Traduz o contexto cru do produto nas linhas do painel. Ausente, o painel não aparece. */
1819
+ readonly contextEntriesOf?: (context: Record<string, unknown> | undefined) => readonly ConversationContextEntry[];
1820
+ readonly quickReplies?: readonly QuickReply[];
1821
+ /**
1822
+ * Recebe o contexto junto porque o dado que interessa à variável (o nome que o bot perguntou,
1823
+ * por exemplo) vive no contexto do fluxo, não no resumo da listagem.
1824
+ */
1825
+ readonly quickReplyVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => Record<string, string>;
1826
+ /** Etapa do fluxo ao lado do contexto (ex.: "Anotando o pedido"). */
1827
+ readonly flowLabel?: string | undefined;
1828
+ /**
1829
+ * Substitui o download local do transcript. Existe para o produto que exporta pela rota do
1830
+ * servidor, onde o arquivo sai completo em vez de só com o que a tela carregou.
1831
+ */
1832
+ readonly onDownload?: (() => void) | undefined;
1833
+ /**
1834
+ * Bloqueia o composer enquanto a conversa estiver com o bot. Ligado, responder sem assumir
1835
+ * atropelaria o fluxo automático no meio de uma pergunta.
1836
+ */
1837
+ readonly requireTakeoverToReply?: boolean;
1838
+ /**
1839
+ * Deixa marcar mensagens e copiar o trecho. É o que se faz para levar um pedaço da conversa a um
1840
+ * e-mail ou a um chamado, sem baixar o transcript inteiro.
1841
+ */
1842
+ readonly messageSelection?: boolean;
1843
+ /** Texto já no campo ao abrir (deep link que sugere a resposta). */
1844
+ readonly initialComposerText?: string | undefined;
1845
+ /**
1846
+ * Peça do produto entre o contexto e o transcript (ex.: ficha do lead, resumo do pedido). Recebe o
1847
+ * contexto do fluxo junto: é dele que sai o resumo, e sem isso o host teria de buscá-lo de novo.
1848
+ */
1849
+ readonly renderAboveTranscript?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => ReactNode;
1850
+ readonly onAttach?: ((file: File) => Promise<void>) | undefined;
1851
+ /**
1852
+ * `rich` troca o `textarea` pelo campo com a formatação do WhatsApp desenhada enquanto se escreve.
1853
+ * Vale onde o atendente manda texto longo e formatado; para responder "já vou ver" o campo simples
1854
+ * é menos coisa na tela.
1855
+ */
1856
+ readonly composer?: 'simple' | 'rich';
1857
+ /** Valores que o operador insere sem digitar. Só o composer `rich` os oferece. */
1858
+ readonly composerVariablesFor?: (conversation: ConversationSummary, context: Record<string, unknown> | undefined) => readonly RichComposerVariable[];
1859
+ /**
1860
+ * Fila de anexos com legenda, como no WhatsApp: os arquivos escolhidos ficam visíveis acima da
1861
+ * barra e saem junto com o texto escrito. Ausente, o clipe manda cada arquivo na hora — o que
1862
+ * perde a legenda, e era a diferença entre as telas.
1863
+ */
1864
+ readonly onSendAttachments?: (files: readonly File[], caption: string) => Promise<void>;
1865
+ /** Grava e envia nota de voz. Ausente, o microfone não aparece. */
1866
+ readonly onRecordAudio?: (file: File) => Promise<void>;
1867
+ }
1868
+ declare function ConversationPane({ conversation, now, busy, labels, onTakeover, onReturnToBot, onFinish, onBack, extraUtilities, contextEntriesOf, quickReplies, quickReplyVariablesFor, flowLabel, onDownload, requireTakeoverToReply, messageSelection, initialComposerText, renderAboveTranscript, onAttach, composer, composerVariablesFor, onSendAttachments, onRecordAudio, }: ConversationPaneProps): react.JSX.Element;
1869
+
1870
+ interface ConversationsInboxListProps {
1871
+ readonly inbox: UseConversationsInboxResult;
1872
+ readonly labels: ConversationsWorkspaceLabels;
1873
+ readonly className?: string;
1874
+ /** Barra extra do produto acima da lista (ex.: filtro de carteira, seletor de campanha). */
1875
+ readonly renderFilters?: (inbox: UseConversationsInboxResult) => ReactNode;
1876
+ /** Ações em lote do produto, ao lado de finalizar e template. */
1877
+ readonly renderBulkActions?: (inbox: UseConversationsInboxResult) => ReactNode;
1878
+ readonly renderRow?: (conversation: ConversationSummary) => ReactNode;
1879
+ readonly onSendTemplateToSelected?: () => void;
1880
+ }
1881
+ declare function ConversationsInboxList({ inbox, labels, className, renderFilters, renderBulkActions, renderRow, onSendTemplateToSelected, }: ConversationsInboxListProps): react.JSX.Element;
1882
+
1883
+ export { type AsyncResourceState, AudioPlayer, type AudioPlayerProps, AudioTranscription, type AudioTranscriptionProps, Avatar, type AvatarLabels, type AvatarProps, type BotMessages, type BuildTranscriptTextParams, BulkActionBar, type BulkActionBarProps, CHANNEL_BRAND_COLOR, CONVERSATIONS_PER_PAGE, CONVERSATION_WINDOW, ChannelFilter, ChannelFilterOption, ChannelIcon, type ChannelIconProps, ConversationChannel, type ConversationContextEntry, ConversationContextPanel, type ConversationContextPanelClassNames, type ConversationContextPanelLabels, type ConversationContextPanelProps, type ConversationContextStatus, ConversationDocument, ConversationDocumentsPanel, type ConversationDocumentsPanelClassNames, type ConversationDocumentsPanelLabels, type ConversationDocumentsPanelProps, ConversationHeader, type ConversationHeaderClassNames, type ConversationHeaderLabels, type ConversationHeaderProps, type ConversationHeaderUtility, ConversationListItem, type ConversationListItemLabels, type ConversationListItemProps, type ConversationLocales, ConversationLocalesProvider, type ConversationLocalesProviderProps, ConversationPane, type ConversationPaneProps, type ConversationRealtimeHandler, ConversationRow, type ConversationRowClassNames, type ConversationRowProps, ConversationSimulatorClient, ConversationSummary, ConversationTemplate, ConversationWallpaper, type ConversationWallpaperProps, type ConversationWindow, ConversationsApi, ConversationsFeatures, ConversationsInboxList, type ConversationsInboxListProps, ConversationsProvider, ConversationsWorkspace, type ConversationsWorkspaceLabels, type ConversationsWorkspaceProps, type ConversationsWorkspaceSimulator, DEFAULT_ACCEPTED_FILE_TYPES, DEFAULT_AVATAR_LABELS, DEFAULT_CONVERSATIONS_WORKSPACE_LABELS, DEFAULT_CONVERSATION_CONTEXT_LABELS, DEFAULT_CONVERSATION_DOCUMENTS_LABELS, DEFAULT_CONVERSATION_HEADER_LABELS, DEFAULT_CONVERSATION_LIST_ITEM_LABELS, DEFAULT_DARK_MODE_TOGGLE_LABELS, DEFAULT_DOCUMENTS_LIBRARY_LABELS, DEFAULT_DOCUMENTS_WORKSPACE_LABELS, DEFAULT_EMOJI_PICKER_LABELS, DEFAULT_INTERACTIVE_MESSAGE_LABELS, DEFAULT_LIGHTBOX_LABELS, DEFAULT_MESSAGE_COMPOSER_LABELS, DEFAULT_RICH_COMPOSER_TOOLTIPS, DEFAULT_TEMPLATES_SETTINGS_LABELS, DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS, DEFAULT_WINDOW_EXPIRED_LABELS, DOCUMENT_SOURCE_FILTER, DarkModeToggle, type DarkModeToggleLabels, type DarkModeToggleProps, DateDivider, type DateDividerClassNames, type DateDividerProps, type DocumentSourceFilter, type DocumentsFiltersContext, DocumentsLibrary, type DocumentsLibraryClassNames, type DocumentsLibraryLabels, type DocumentsLibraryProps, DocumentsWorkspace, type DocumentsWorkspaceClassNames, type DocumentsWorkspaceLabels, type DocumentsWorkspaceProps, EMOJI_CATEGORIES, type EmojiCategory, type EmojiEntry, EmojiPicker, type EmojiPickerLabels, type EmojiPickerProps, FileIcon, type FileIconProps, type FilterOption, InteractiveMessage, type InteractiveMessageLabels, type InteractiveMessageProps, InteractivePayload, InteractiveSelection, Lightbox, type LightboxLabels, type LightboxProps, ListConversationsParams, ListDocumentsParams, ListingPagination, type ListingPaginationProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerClassNames, type MessageComposerLabels, type MessageComposerProps, MessagePayload, MessageTail, MessageText, type MessageTextProps, MessageTimestamp, MessageTranscription, MessagesWorkspace, type MessagesWorkspaceApi, type MessagesWorkspaceLabels, type MessagesWorkspaceProps, type MessagesWorkspaceTemplateRole, MultiSelectFilter, type MultiSelectFilterProps, NARROW_MAX_WIDTH_PX, type QuickReply, RICH_COMPOSER_ACTION, ResolveMediaUrl, type RichComposerAction, type RichComposerQuickReply, type RichComposerTooltips, type RichComposerVariable, RichMessageComposer, type RichMessageComposerHandle, type RichMessageComposerProps, SSEProvider, SimpleEmojiPicker, type SimpleEmojiPickerProps, type SimulatorTransportFactory, type SimulatorTransportParams, type SortDirection, SortableHead, type SortableHeadProps, StatusTicks, type StatusTicksProps, TEMPLATE_SETTINGS_TAB, TOOLTIP_ATTRIBUTE, type TemplateSettings, type TemplateSettingsTab, ToastProvider, TooltipLayer, type TopicItem, TopicsForm, type TopicsFormLabels, type TopicsFormProps, TranscriptionMode, type TranscriptionSettings, TranscriptionSettingsForm, type TranscriptionSettingsFormLabels, type TranscriptionSettingsFormProps, type UrlStateOptions, type UseConversationActionsResult, type UseConversationContextResult, type UseConversationDocumentsParams, type UseConversationDocumentsResult, type UseConversationListParams, type UseConversationListResult, type UseConversationMessagesResult, type UseConversationsInboxParams, type UseConversationsInboxResult, type UseInboxActionsResult, type UseScrollToLatestMessageParams, type UseScrollToLatestMessageResult, type UseWaitingNotificationsLabels, type UseWaitingNotificationsParams, type UseWaitingNotificationsResult, WINDOW_FILTERS, WelcomeFarewellForm, type WelcomeFarewellFormLabels, type WelcomeFarewellFormProps, WhatsAppCreateTemplateForm, type WhatsAppCreateTemplateFormLabels, type WhatsAppCreateTemplateFormProps, type WhatsAppCreateTemplateResult, type WhatsAppCreateTemplateState, WhatsAppMessageEditor, type WhatsAppMessageEditorLabels, type WhatsAppMessageEditorProps, type WhatsAppTemplateHeaderType, WhatsAppTemplateSettingsForm, type WhatsAppTemplateSettingsFormLabels, type WhatsAppTemplateSettingsFormProps, type WhatsAppTemplateSummary, type WhatsAppTemplateVariableSuggestion, WhatsAppTemplatesSettings, type WhatsAppTemplatesSettingsLabels, type WhatsAppTemplatesSettingsProps, WindowExpiredNotice, type WindowExpiredNoticeLabels, type WindowExpiredNoticeProps, type WindowOfParams, applyQuickReplyVariables, buildTranscriptFilename, buildTranscriptText, createMediaUrlResolver, downloadTextFile, formatDateTime, formatFileSize, formatPhone, formatStalledFor, formatTimestamp, htmlToWA, isSameDay, isWindowBlocking, parseWhatsAppFormatting, phoneInitials, resolveQuickReply, searchEmojis, toast, useConversationActions, useConversationContext, useConversationDocuments, useConversationList, useConversationLocales, useConversationMessages, useConversationRealtime, useConversations, useConversationsInbox, useDarkMode, useDebouncedValue, useGlobalRealtime, useInboxActions, useIsDarkTheme, useIsNarrow, useScrollToLatestMessage, useToast, useUrlArrayState, useUrlNumberState, useUrlStringState, useWaitingNotifications, waToHTML, waToHTMLInline, windowOf };