@adatechnology/conversations-ui 0.1.0-rc.5 → 0.1.0-rc.6

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.
@@ -1,4 +1,4 @@
1
- import { M as MessagePayload, k as ConversationSummary, i as ConversationEventSource, m as ConversationsApi, L as ListConversationsParams, j as ConversationPage, S as SSEProvider, g as ConversationDocument } from '../types-C2Yexi8A.js';
1
+ import { v as MessagePayload, m as ConversationSummary, k as ConversationEventSource, o as ConversationsApi, L as ListConversationsParams, l as ConversationPage, S as SSEProvider, i as ConversationDocument, x as ResolveMediaUrl } from '../types-CeixG2Z9.js';
2
2
  import * as react from 'react';
3
3
  import { InteractiveReplyOption } from '@adatechnology/meta-whatsapp-contracts/testing';
4
4
 
@@ -109,11 +109,6 @@ declare function createMockSSEProvider(params: CreateMockSSEProviderParams): SSE
109
109
 
110
110
  declare const PREVIEW_CONVERSATIONS: readonly ConversationSummary[];
111
111
  declare const PREVIEW_MESSAGES: Readonly<Record<string, readonly MessagePayload[]>>;
112
- /**
113
- * A biblioteca de arquivos da conversa, como o backend a devolveria. Deriva das mensagens de
114
- * documento acima em vez de repetir os dados: fixture duplicada divergiria na primeira edição, e o
115
- * painel passaria a mostrar arquivo que a thread não tem.
116
- */
117
112
  declare const PREVIEW_DOCUMENTS: Readonly<Record<string, readonly ConversationDocument[]>>;
118
113
 
119
114
  /**
@@ -183,4 +178,84 @@ type StartPreviewScriptParams = {
183
178
  };
184
179
  declare function startPreviewScript(params: StartPreviewScriptParams): () => void;
185
180
 
186
- export { type AppendMessageParams, ConversationPreview, type ConversationPreviewProps, type CreateMockConversationsApiParams, type CreateMockSSEProviderParams, type CreatePreviewStoreParams, type CreatePreviewWebhookClientParams, DEFAULT_PREVIEW_SCRIPT, GLOBAL_CHANNEL, type ListConversationsFilters, type MockEventSource, PREVIEW_CONVERSATIONS, PREVIEW_DOCUMENTS, PREVIEW_MESSAGES, type PreviewEmission, PreviewInProductionError, type PreviewScriptStep, type PreviewStore, type PreviewStoreListener, type PreviewWebhookClient, PreviewWebhookRejectedError, type SetModeParams, type StartPreviewScriptParams, assertPreviewEnvironment, conversationChannel, createMockConversationsApi, createMockEventSource, createMockSSEProvider, createPreviewStore, createPreviewWebhookClient, startPreviewScript };
181
+ /**
182
+ * Bytes de verdade para o preview abrir.
183
+ *
184
+ * O mock antes devolvia a MESMA imagem PNG para qualquer documento, então clicar em "visualizar" num
185
+ * PDF entregava um PNG rotulado `application/pdf` e o leitor dizia que o arquivo era inválido. Cada
186
+ * amostra aqui é um arquivo mínimo porém legítimo do seu tipo: as de mídia foram geradas com ffmpeg
187
+ * e conferidas com ffprobe, o PDF tem xref/startxref/%%EOF, e os pacotes Office são zips com as
188
+ * partes obrigatórias (`[Content_Types].xml`, `_rels/.rels`, o documento).
189
+ *
190
+ * Não há amostra de `audio/amr`, `video/3gp` nem dos formatos binários legados do Office
191
+ * (`application/msword`, `application/vnd.ms-excel`, `application/vnd.ms-powerpoint`): não há
192
+ * encoder para eles nesta máquina e forjar OLE2 à mão daria arquivo corrompido — exatamente o que
193
+ * este módulo existe para evitar. Esses caem no texto explicativo de `resolvePreviewFileSample`,
194
+ * que abre sem erro e diz o que é.
195
+ *
196
+ * GERADO — não editar à mão.
197
+ */
198
+ declare const PREVIEW_FILE_SAMPLES: Readonly<Record<string, string>>;
199
+ /**
200
+ * A data URL que o preview abre para um documento.
201
+ *
202
+ * Ordem: tipo exato → família (`audio/amr` toca como mp3, `video/3gp` como mp4) → nota em texto.
203
+ * A nota fecha o caso dos tipos sem amostra: abre num leitor de texto e explica, em vez de baixar
204
+ * um binário quebrado com extensão de Word.
205
+ */
206
+ declare function resolvePreviewFileSample(mimeType: string | undefined, filename?: string): string;
207
+
208
+ /**
209
+ * Como o preview entrega mídia para a UI abrir.
210
+ *
211
+ * Separado de `previewFileSamples` (que é gerado) porque aqui está a parte que depende do navegador:
212
+ * **blob URL, não data URL**. O Chrome bloqueia navegação de topo para `data:` desde a v60, então
213
+ * `window.open(dataUrl)` — que é o que os botões "visualizar" e "baixar" fazem — abre aba em branco
214
+ * mesmo com bytes perfeitamente válidos. Em `src` de `<img>`/`<video>` a data URL funcionaria; no
215
+ * `window.open` não, e o mesmo `getDocumentUrl` alimenta os dois.
216
+ */
217
+
218
+ /** URL que o preview pode abrir em aba nova. Cai na data URL fora do navegador (teste, SSR). */
219
+ declare function previewFileUrl(mimeType: string | undefined, filename?: string): string;
220
+ /**
221
+ * A mesma amostra em base64 cru, como o proxy de mídia do backend a devolveria.
222
+ *
223
+ * Existe porque `getMediaProxyUrl` é o caminho da mídia AINDA NÃO ingerida (só existe o id na Meta),
224
+ * e o contrato pede `{ mimeType, data }` — não URL. Sem isto o mock devolvia o PNG 1x1 para
225
+ * qualquer id, e vídeo e áudio da thread apareciam quebrados apesar de haver amostra válida.
226
+ */
227
+ declare function previewFileBase64(mimeType: string | undefined, filename?: string): {
228
+ mimeType: string;
229
+ data: string;
230
+ };
231
+ /**
232
+ * O `onResolveMediaUrl` que o `MessageBubble` espera.
233
+ *
234
+ * Sem ele, foto, vídeo e áudio da thread ficam parados no placeholder para sempre — o
235
+ * `MediaRenderer` só resolve `uploadId`/`mediaId` por esta porta, de propósito, para o pacote nunca
236
+ * chamar endpoint fixo. O preview não tinha resolvedor nenhum, então nenhuma mídia carregava.
237
+ */
238
+ declare function createPreviewMediaResolver(): ResolveMediaUrl;
239
+
240
+ /**
241
+ * Bancada de teste manual de mídia — montável em uma linha por qualquer projeto que adote o SDK.
242
+ *
243
+ * Existe porque o defeito que ela pega não é pegável por teste automatizado: "o PDF abre?" depende
244
+ * do leitor do navegador, "o vídeo toca?" do decodificador, e "a aba abre?" da política do Chrome
245
+ * sobre `data:` URL. Teste unitário confere bytes; só o olho confere que o arquivo abre. Sem uma
246
+ * superfície pronta no pacote, cada projeto teria de montar a sua — e, na prática, nenhum montava.
247
+ *
248
+ * Traz o próprio store, o próprio mock e o próprio provider: o host não injeta nada. E não passa
249
+ * `onResolveMediaUrl` em lugar nenhum de propósito — é o `MessageBubble` resolvendo mídia pelo
250
+ * `ConversationsApi` do contexto, então se essa resolução automática quebrar, esta tela mostra.
251
+ */
252
+ /** A conversa do fixture que carrega uma mensagem de cada tipo aceito. */
253
+ declare const MEDIA_TYPES_CONVERSATION_ID = "5511944443333";
254
+ type MediaTypesPreviewProps = {
255
+ /** Outra conversa do fixture, se o projeto tiver acrescentado a sua. */
256
+ conversationId?: string;
257
+ className?: string;
258
+ };
259
+ declare function MediaTypesPreview({ conversationId, className, }: MediaTypesPreviewProps): react.JSX.Element;
260
+
261
+ export { type AppendMessageParams, ConversationPreview, type ConversationPreviewProps, type CreateMockConversationsApiParams, type CreateMockSSEProviderParams, type CreatePreviewStoreParams, type CreatePreviewWebhookClientParams, DEFAULT_PREVIEW_SCRIPT, GLOBAL_CHANNEL, type ListConversationsFilters, MEDIA_TYPES_CONVERSATION_ID, MediaTypesPreview, type MediaTypesPreviewProps, type MockEventSource, PREVIEW_CONVERSATIONS, PREVIEW_DOCUMENTS, PREVIEW_FILE_SAMPLES, PREVIEW_MESSAGES, type PreviewEmission, PreviewInProductionError, type PreviewScriptStep, type PreviewStore, type PreviewStoreListener, type PreviewWebhookClient, PreviewWebhookRejectedError, type SetModeParams, type StartPreviewScriptParams, assertPreviewEnvironment, conversationChannel, createMockConversationsApi, createMockEventSource, createMockSSEProvider, createPreviewMediaResolver, createPreviewStore, createPreviewWebhookClient, previewFileBase64, previewFileUrl, resolvePreviewFileSample, startPreviewScript };
@@ -1,9 +1,12 @@
1
1
  import {
2
+ ConversationDocumentsPanel,
2
3
  ConversationWallpaper,
4
+ ConversationsProvider,
3
5
  DateDivider,
6
+ DocumentsLibrary,
4
7
  MessageBubble,
5
8
  MessageComposer
6
- } from "../chunk-YWITIIHD.js";
9
+ } from "../chunk-LITPZCWW.js";
7
10
  import "../chunk-OGRRHQQW.js";
8
11
 
9
12
  // src/preview/previewStore.ts
@@ -399,8 +402,7 @@ var PREVIEW_MESSAGES = {
399
402
  timestamp: at("15:09:00")
400
403
  },
401
404
  {
402
- // Extensão fora do EXTENSION_STYLE: garante que o ícone genérico cinza também apareça.
403
- id: "fixture-doc-generic",
405
+ id: "fixture-doc-txt",
404
406
  type: "document",
405
407
  uploadId: "preview/documentos/lista-compras.txt",
406
408
  filename: "lista-compras.txt",
@@ -409,13 +411,140 @@ var PREVIEW_MESSAGES = {
409
411
  direction: "inbound",
410
412
  sender: "customer",
411
413
  timestamp: at("15:10:00")
414
+ },
415
+ {
416
+ id: "fixture-doc-csv",
417
+ type: "document",
418
+ uploadId: "preview/documentos/planilha-do-pedido.csv",
419
+ filename: "planilha-do-pedido.csv",
420
+ mimeType: "text/csv",
421
+ sizeBytes: 2048,
422
+ direction: "inbound",
423
+ sender: "customer",
424
+ timestamp: at("15:11:00")
425
+ },
426
+ {
427
+ id: "fixture-doc-pptx",
428
+ type: "document",
429
+ uploadId: "preview/documentos/apresentacao.pptx",
430
+ filename: "apresentacao.pptx",
431
+ mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
432
+ sizeBytes: 512e3,
433
+ direction: "inbound",
434
+ sender: "customer",
435
+ timestamp: at("15:12:00")
436
+ },
437
+ {
438
+ id: "fixture-doc-ppt",
439
+ type: "document",
440
+ uploadId: "preview/documentos/antiga.ppt",
441
+ filename: "antiga.ppt",
442
+ mimeType: "application/vnd.ms-powerpoint",
443
+ sizeBytes: 348160,
444
+ direction: "inbound",
445
+ sender: "customer",
446
+ timestamp: at("15:13:00")
447
+ },
448
+ // Mídia enviada COMO arquivo: aí a Meta manda o nome, então é onde a extensão de música e de
449
+ // vídeo aparece na lista. Mandada como `image`/`audio`/`video` (acima e abaixo) ela chega sem
450
+ // nome nenhum, e o rótulo vira o id da mídia — como em produção.
451
+ {
452
+ id: "fixture-doc-mp3",
453
+ type: "document",
454
+ uploadId: "preview/documentos/musica-do-anuncio.mp3",
455
+ filename: "musica-do-anuncio.mp3",
456
+ mimeType: "audio/mpeg",
457
+ sizeBytes: 3145728,
458
+ direction: "inbound",
459
+ sender: "customer",
460
+ timestamp: at("15:14:00")
461
+ },
462
+ {
463
+ id: "fixture-doc-m4a",
464
+ type: "document",
465
+ uploadId: "preview/documentos/recado.m4a",
466
+ filename: "recado.m4a",
467
+ mimeType: "audio/mp4",
468
+ sizeBytes: 486400,
469
+ direction: "inbound",
470
+ sender: "customer",
471
+ timestamp: at("15:15:00")
472
+ },
473
+ {
474
+ id: "fixture-doc-mp4-file",
475
+ type: "document",
476
+ uploadId: "preview/documentos/video-do-produto.mp4",
477
+ filename: "video-do-produto.mp4",
478
+ mimeType: "video/mp4",
479
+ sizeBytes: 8388608,
480
+ direction: "inbound",
481
+ sender: "customer",
482
+ timestamp: at("15:16:00")
483
+ },
484
+ {
485
+ id: "fixture-doc-jpg",
486
+ type: "document",
487
+ uploadId: "preview/documentos/foto-do-recibo.jpg",
488
+ filename: "foto-do-recibo.jpg",
489
+ mimeType: "image/jpeg",
490
+ sizeBytes: 262144,
491
+ direction: "inbound",
492
+ sender: "customer",
493
+ timestamp: at("15:17:00")
494
+ },
495
+ // As espécies restantes que a Meta aceita, sem nome de arquivo — fecham a lista de tipos
496
+ // aceitos e exercitam o fallback de ícone pela família do mimeType.
497
+ {
498
+ id: "fixture-media-jpeg",
499
+ type: "image",
500
+ mediaId: "preview-image-jpeg",
501
+ mimeType: "image/jpeg",
502
+ sizeBytes: 138240,
503
+ direction: "inbound",
504
+ sender: "customer",
505
+ timestamp: at("15:18:00")
506
+ },
507
+ {
508
+ id: "fixture-media-3gp",
509
+ type: "video",
510
+ mediaId: "preview-video-3gp",
511
+ mimeType: "video/3gp",
512
+ sizeBytes: 921600,
513
+ direction: "inbound",
514
+ sender: "customer",
515
+ timestamp: at("15:19:00")
516
+ },
517
+ {
518
+ id: "fixture-media-aac",
519
+ type: "audio",
520
+ mediaId: "preview-audio-aac",
521
+ mimeType: "audio/aac",
522
+ sizeBytes: 76800,
523
+ direction: "inbound",
524
+ sender: "customer",
525
+ timestamp: at("15:20:00")
526
+ },
527
+ {
528
+ id: "fixture-media-amr",
529
+ type: "audio",
530
+ mediaId: "preview-audio-amr",
531
+ mimeType: "audio/amr",
532
+ sizeBytes: 24576,
533
+ direction: "inbound",
534
+ sender: "customer",
535
+ timestamp: at("15:21:00")
412
536
  }
413
537
  ]
414
538
  };
539
+ var LIBRARY_MEDIA_TYPES = /* @__PURE__ */ new Set(["document", "image", "video", "audio", "sticker"]);
415
540
  var PREVIEW_DOCUMENTS = {
416
- "5511944443333": (PREVIEW_MESSAGES["5511944443333"] ?? []).filter((message) => message.type === "document").map((message) => ({
417
- id: message.uploadId ?? message.id,
418
- filename: message.filename ?? message.id,
541
+ "5511944443333": (PREVIEW_MESSAGES["5511944443333"] ?? []).filter((message) => LIBRARY_MEDIA_TYPES.has(message.type)).map((message) => ({
542
+ // Mídia não tem `uploadId` na mensagem: o backend deriva a key do id da mídia na Meta, e o
543
+ // caminho abaixo imita esse formato para o download assinado receber algo do mesmo feitio.
544
+ id: message.uploadId ?? (message.mediaId ? `preview/inbound/${message.mediaId}` : message.id),
545
+ // Só documento chega nomeado. Para os outros o backend salva o id da mídia como rótulo —
546
+ // manter isso aqui é o que faz o ícone por família do mimeType ser exercitado de verdade.
547
+ filename: message.filename ?? message.mediaId ?? message.id,
419
548
  mimeType: message.mimeType ?? "application/octet-stream",
420
549
  sizeBytes: message.sizeBytes ?? 0,
421
550
  source: message.sender,
@@ -423,8 +552,85 @@ var PREVIEW_DOCUMENTS = {
423
552
  }))
424
553
  };
425
554
 
555
+ // src/preview/previewFileSamples.ts
556
+ var PREVIEW_FILE_SAMPLES = {
557
+ "application/pdf": "data:application/pdf;base64,JVBERi0xLjQKMSAwIG9iajw8L1R5cGUvQ2F0YWxvZy9QYWdlcyAyIDAgUj4+ZW5kb2JqCjIgMCBvYmo8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDE+PmVuZG9iagozIDAgb2JqPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9NZWRpYUJveFswIDAgMjYwIDExMF0vQ29udGVudHMgNCAwIFIvUmVzb3VyY2VzPDwvRm9udDw8L0YxIDUgMCBSPj4+Pj4+ZW5kb2JqCjQgMCBvYmo8PC9MZW5ndGggNjI+PnN0cmVhbQpCVCAvRjEgMTMgVGYgMTggNTUgVGQgKERvY3VtZW50byBkZSBleGVtcGxvIGRvIHByZXZpZXcpIFRqIEVUCmVuZHN0cmVhbWVuZG9iago1IDAgb2JqPDwvVHlwZS9Gb250L1N1YnR5cGUvVHlwZTEvQmFzZUZvbnQvSGVsdmV0aWNhPj5lbmRvYmoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNTIgMDAwMDAgbiAKMDAwMDAwMDEwMSAwMDAwMCBuIAowMDAwMDAwMjExIDAwMDAwIG4gCjAwMDAwMDAzMTcgMDAwMDAgbiAKdHJhaWxlcjw8L1NpemUgNi9Sb290IDEgMCBSPj4Kc3RhcnR4cmVmCjM3OAolJUVPRgo=",
558
+ "image/png": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAACXBIWXMAAAABAAAAAQBPJcTWAAAATElEQVR4nO3OMRHAIAAAMbhDWo2hCYPdu/xIh0RB5rPP+JN1O/AlVISKUBEqQkWoCBWhIlSEilARKkJFqAgVoSJUhIpQESpCRagIlRcgDwI25HmPQAAAAABJRU5ErkJggg==",
559
+ "image/jpeg": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD//gAQTGF2YzYyLjExLjEwMAD/2wBDAAgEBAQEBAUFBQUFBQYGBgYGBgYGBgYGBgYHBwcICAgHBwcGBgcHCAgICAkJCQgICAgJCQoKCgwMCwsODg4RERT/xABMAAEBAAAAAAAAAAAAAAAAAAAABAEBAQAAAAAAAAAAAAAAAAAAAAYQAQAAAAAAAAAAAAAAAAAAAAARAQAAAAAAAAAAAAAAAAAAAAD/wAARCAAwADADASIAAhEAAxEA/9oADAMBAAIRAxEAPwCUBRpwAAAAAAAAAAAAAAAB/9k=",
560
+ "image/webp": "data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
561
+ "video/mp4": "data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAANdbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAASwAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAoh0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAASwAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAEAAAABAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAAEsAAAIAAABAAAAAAIAbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAoAAAAEABVxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAABq21pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAWtzdGJsAAAAv3N0c2QAAAAAAAAAAQAAAK9hdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAEAAQABIAAAASAAAAAAAAAABFUxhdmM2Mi4xMS4xMDAgbGlieDI2NAAAAAAAAAAAAAAAGP//AAAANWF2Y0MBZAAK/+EAGGdkAAqs2UQmwEQAAAMABAAAAwBQPEiWWAEABmjr48siwP34+AAAAAAQcGFzcAAAAAEAAAABAAAAFGJ0cnQAAAAAAABO2gAAAAAAAAAYc3R0cwAAAAAAAAABAAAAAwAABAAAAAAUc3RzcwAAAAAAAAABAAAAAQAAAChjdHRzAAAAAAAAAAMAAAABAAAIAAAAAAEAAAwAAAAAAQAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAMAAAABAAAAIHN0c3oAAAAAAAAAAAAAAAMAAALbAAAADgAAAAwAAAAUc3RjbwAAAAAAAAABAAADjQAAAGF1ZHRhAAAAWW1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALGlsc3QAAAAkqXRvbwAAABxkYXRhAAAAAQAAAABMYXZmNjIuMy4xMDAAAAAIZnJlZQAAAv1tZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2NSByMzIyMiBiMzU2MDVhIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyNSAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTIgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTEwIHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAJWWIhAA///7mdfgUoKykhh+DdK6xVynnAWznTQ4fVSgMZ1FwY28AAAAKQZoibEN//qePiAAAAAgBnkF5Df8HfQ==",
562
+ "audio/mpeg": "data:audio/mpeg;base64,SUQzBAAAAAAAIlRTU0UAAAAOAAADTGF2ZjYyLjMuMTAwAAAAAAAAAAAAAAD/40jAAAAAAAAAAAAASW5mbwAAAA8AAAAFAAAGwABVVVVVVVVVVVVVVVVVVVVVVVVVgICAgICAgICAgICAgICAgICAgICqqqqqqqqqqqqqqqqqqqqqqqqqqtXV1dXV1dXV1dXV1dXV1dXV1dXV//////////////////////////8AAAAATGF2YzYyLjExAAAAAAAAAAAAAAAAJALAAAAAAAAABsDR4nNFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/40jEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/40jEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/40jEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/40jEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/40jEAAAAA0gAAAAATEFNRTMuMTAwVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=",
563
+ "audio/ogg": "data:audio/ogg;base64,T2dnUwACAAAAAAAAAABXDTmmAAAAAKMnDKEBE09wdXNIZWFkAQE4AYC7AAAAAABPZ2dTAAAAAAAAAAAAAFcNOaYBAAAAZlPuKwE9T3B1c1RhZ3MMAAAATGF2ZjYyLjMuMTAwAQAAAB0AAABlbmNvZGVyPUxhdmM2Mi4xMS4xMDAgbGlib3B1c09nZ1MABLgmAAAAAAAAVw05pgIAAAA3uL6lCw8REhISEhISEhISeAvkwTbsxY2MSUaZpCaweAfJcifhROpV8fDA1d3pUmB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOB4B8l5yMlXwKISI/rvZ/NkwOA=",
564
+ "audio/mp4": "data:audio/mp4;base64,AAAAHGZ0eXBNNEEgAAACAE00QSBpc29taXNvMgAAAAhmcmVlAAAAQW1kYXTeAgBMYXZjNjIuMTEuMTAwAAIwQA4BGCAHARggBwEYIAcBGCAHARggBwEYIAcBGCAHARggBwEYIAcAAAMibW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAMgAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAk10cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAAMgAAAAAAAAAAAAAAAEBAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAADIAAAEAAABAAAAAAHFbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAACsRAAAJnRVxAAAAAAALWhkbHIAAAAAAAAAAHNvdW4AAAAAAAAAAAAAAABTb3VuZEhhbmRsZXIAAAABcG1pbmYAAAAQc21oZAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAABNHN0YmwAAABqc3RzZAAAAAAAAAABAAAAWm1wNGEAAAAAAAAAAQAAAAAAAAAAAAEAEAAAAACsRAAAAAAANmVzZHMAAAAAA4CAgCUAAQAEgICAF0AVAAAAAAB9AAAAB/oFgICABRIIVuUABoCAgAECAAAAIHN0dHMAAAAAAAAAAgAAAAkAAAQAAAAAAQAAAnQAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAoAAAABAAAAPHN0c3oAAAAAAAAAAAAAAAoAAAAVAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAAFHN0Y28AAAAAAAAAAQAAACwAAAAac2dwZAEAAAByb2xsAAAAAgAAAAH//wAAABxzYmdwAAAAAHJvbGwAAAABAAAACgAAAAEAAABhdWR0YQAAAFltZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAACxpbHN0AAAAJKl0b28AAAAcZGF0YQAAAAEAAAAATGF2ZjYyLjMuMTAw",
565
+ "audio/aac": "data:audio/aac;base64,//FQQAOf/N4CAExhdmM2Mi4xMS4xMDAAAjBADv/xUEABf/wBGCAH//FQQAF//AEYIAf/8VBAAX/8ARggB//xUEABf/wBGCAH//FQQAF//AEYIAf/8VBAAX/8ARggB//xUEABf/wBGCAH//FQQAF//AEYIAf/8VBAAX/8ARggBw==",
566
+ "text/plain": "data:text/plain;base64,TGlzdGEgZGUgY29tcHJhcwotIGFycm96IDJrZwotIG9sZW8gMXVuCg==",
567
+ "text/csv": "data:text/csv;base64,cHJvZHV0byxxdWFudGlkYWRlLHByZWNvCmFycm96LDIsMjQuOTAKb2xlbywxLDcuNTAK",
568
+ "application/zip": "data:application/zip;base64,UEsDBBQAAAAIANFO/FwtmGOxGwAAABkAAAARAAAAY29tcHJvdmFudGUtMS50eHRLzs8tKMovS8wrSVVISVUoSExPzE3NK8nnAgBQSwMEFAAAAAgA0U78XCftV8YWAAAAFAAAABEAAABjb21wcm92YW50ZS0yLnR4dCtOTS/NS8lXSM7PLSjKL0vMK0nlAgBQSwECFAMUAAAACADRTvxcLZhjsRsAAAAZAAAAEQAAAAAAAAAAAAAAgAEAAAAAY29tcHJvdmFudGUtMS50eHRQSwECFAMUAAAACADRTvxcJ+1XxhYAAAAUAAAAEQAAAAAAAAAAAAAAgAFKAAAAY29tcHJvdmFudGUtMi50eHRQSwUGAAAAAAIAAgB+AAAAjwAAAAAA",
569
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,UEsDBBQAAAAIANFO/Fx5bjPX6AAAAK0BAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbH1QyU7DMBD9FWuuKHHggBCK0wPLETiUDxjZk8SqN3nc0v49Tlt6QIXjzFv1+tXeO7GjzDYGBbdtB4KCjsaGScHn+rV5AMEFg0EXAyk4EMNq6NeHRCyqNrCCuZT0KCXrmTxyGxOFiowxeyz1zJNMqDc4kbzrunupYygUSlMWDxj6Zxpx64p42df3qUcmxyCeTsQlSwGm5KzGUnG5C+ZXSnNOaKvyyOHZJr6pBJBXExbk74Cz7r0Ok60h8YG5vKGvLPkVs5Em6q2vyvZ/mys94zhaTRf94pZy1MRcF/euvSAebfjpL49zD99QSwMEFAAAAAgA0U78XJv9N+qtAAAAKQEAAAsAAABfcmVscy8ucmVsc43POw7CMAwG4KtE3mlaBoRQ0y4IqSsqB7ASN61oHkrCo7cnAwNFDIy2f3+W6/ZpZnanECdnBVRFCYysdGqyWsClP232wGJCq3B2lgQsFKFt6jPNmPJKHCcfWTZsFDCm5A+cRzmSwVg4TzZPBhcMplwGzT3KK2ri27Lc8fBpwNpknRIQOlUB6xdP/9huGCZJRydvhmz6ceIrkWUMmpKAhwuKq3e7yCzwpuarF5sXUEsDBBQAAAAIANFO/FyraIuoqgAAAOcAAAARAAAAd29yZC9kb2N1bWVudC54bWxFjksOwjAMBa8SZU9TWCBUNWWBxAngAKExbaTGjpzQz+1JyoLNPFt+Grm9rn4SM3B0hFoeq1oKwJ6sw0HL5+N+uEgRk0FrJkLQcoMor127NJb6jwdMIgswNouWY0qhUSr2I3gTKwqA+fYm9ibllQe1ENvA1EOM2e8ndarrs/LGoSzKF9mtZCjggtTdCBObRMKCgBV8mPJIIjDMDpZWlU4h7ww7fx71/7H7AlBLAQIUAxQAAAAIANFO/Fx5bjPX6AAAAK0BAAATAAAAAAAAAAAAAACAAQAAAABbQ29udGVudF9UeXBlc10ueG1sUEsBAhQDFAAAAAgA0U78XJv9N+qtAAAAKQEAAAsAAAAAAAAAAAAAAIABGQEAAF9yZWxzLy5yZWxzUEsBAhQDFAAAAAgA0U78XKtoi6iqAAAA5wAAABEAAAAAAAAAAAAAAIAB7wEAAHdvcmQvZG9jdW1lbnQueG1sUEsFBgAAAAADAAMAuQAAAMgCAAAAAA==",
570
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,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"
571
+ };
572
+ var FAMILY_FALLBACK = {
573
+ image: "image/png",
574
+ video: "video/mp4",
575
+ audio: "audio/mpeg",
576
+ text: "text/plain"
577
+ };
578
+ function resolvePreviewFileSample(mimeType, filename) {
579
+ const normalized = (mimeType ?? "").split(";")[0].trim().toLowerCase();
580
+ const exact = PREVIEW_FILE_SAMPLES[normalized];
581
+ if (exact) return exact;
582
+ const family = FAMILY_FALLBACK[normalized.split("/")[0] ?? ""];
583
+ const byFamily = family ? PREVIEW_FILE_SAMPLES[family] : void 0;
584
+ if (byFamily) return byFamily;
585
+ const note = `Preview do SDK: nao existe amostra binaria valida para ${normalized || "tipo desconhecido"}${filename ? ` (${filename})` : ""}. Na tela real este arquivo vem do storage.`;
586
+ return `data:text/plain;charset=utf-8,${encodeURIComponent(note)}`;
587
+ }
588
+
589
+ // src/preview/previewMediaSource.ts
590
+ var blobUrlCache = /* @__PURE__ */ new Map();
591
+ function toBlob(dataUrl) {
592
+ const [head, payload] = dataUrl.split(",");
593
+ const mimeType = head.replace(/^data:/, "").replace(/;base64$/, "").replace(/;charset=.*$/, "");
594
+ if (!head.endsWith(";base64")) {
595
+ return new Blob([decodeURIComponent(payload)], { type: mimeType || "text/plain" });
596
+ }
597
+ const binary = atob(payload);
598
+ const bytes = new Uint8Array(binary.length);
599
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
600
+ return new Blob([bytes], { type: mimeType });
601
+ }
602
+ function previewFileUrl(mimeType, filename) {
603
+ const dataUrl = resolvePreviewFileSample(mimeType, filename);
604
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return dataUrl;
605
+ const cached = blobUrlCache.get(dataUrl);
606
+ if (cached) return cached;
607
+ const blobUrl = URL.createObjectURL(toBlob(dataUrl));
608
+ blobUrlCache.set(dataUrl, blobUrl);
609
+ return blobUrl;
610
+ }
611
+ function previewFileBase64(mimeType, filename) {
612
+ const dataUrl = resolvePreviewFileSample(mimeType, filename);
613
+ const [head, payload] = dataUrl.split(",");
614
+ const declared = head.replace(/^data:/, "").replace(/;base64$/, "").replace(/;charset=.*$/, "");
615
+ if (head.endsWith(";base64")) return { mimeType: declared, data: payload };
616
+ return { mimeType: declared, data: btoa(decodeURIComponent(payload)) };
617
+ }
618
+ function createPreviewMediaResolver() {
619
+ const byId = /* @__PURE__ */ new Map();
620
+ for (const documents of Object.values(PREVIEW_DOCUMENTS)) {
621
+ for (const document of documents) {
622
+ byId.set(document.id, { mimeType: document.mimeType, filename: document.filename });
623
+ }
624
+ }
625
+ return async (message) => {
626
+ const reference = message.uploadId ?? (message.mediaId ? `preview/inbound/${message.mediaId}` : void 0);
627
+ if (!reference) return null;
628
+ const known = byId.get(reference);
629
+ return previewFileUrl(known?.mimeType ?? message.mimeType, known?.filename ?? message.filename);
630
+ };
631
+ }
632
+
426
633
  // src/preview/createMockConversationsApi.ts
427
- var PREVIEW_IMAGE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYGD4DwABBAEAX+XyEgAAAABJRU5ErkJggg==";
428
634
  var DEFAULT_LATENCY_MS = 120;
429
635
  var PREVIEW_AGENT_ID = "preview-agent";
430
636
  var PREVIEW_TEMPLATES = [
@@ -542,11 +748,51 @@ function createMockConversationsApi(params) {
542
748
  ${names.join("\n")}`], { type: "application/zip" });
543
749
  });
544
750
  },
545
- getDocumentUrl() {
546
- return withLatency(() => `data:image/png;base64,${PREVIEW_IMAGE_BASE64}`);
751
+ /** Junta as bibliotecas de todas as conversas do fixture, com a origem de cada arquivo. */
752
+ getAllDocuments(documentParams) {
753
+ return withLatency(() => {
754
+ let all = Object.entries(PREVIEW_DOCUMENTS).flatMap(
755
+ ([conversationId, docs]) => docs.map((document) => ({ ...document, conversationId }))
756
+ );
757
+ const search = documentParams?.search?.trim().toLowerCase();
758
+ if (search) {
759
+ const digits = search.replace(/\D/g, "");
760
+ all = all.filter(
761
+ (document) => document.filename.toLowerCase().includes(search) || digits !== "" && document.conversationId.includes(digits)
762
+ );
763
+ }
764
+ const source = documentParams?.source;
765
+ if (source === "team") {
766
+ all = all.filter((document) => document.source === "agent" || document.source === "bot");
767
+ } else if (source) {
768
+ all = all.filter((document) => document.source === source);
769
+ }
770
+ all.sort(
771
+ (left, right) => documentParams?.sortDirection === "asc" ? left.linkedAt.localeCompare(right.linkedAt) : right.linkedAt.localeCompare(left.linkedAt)
772
+ );
773
+ const total = all.length;
774
+ const limit = documentParams?.limit ?? total;
775
+ const page = documentParams?.page ?? 1;
776
+ return { documents: all.slice((page - 1) * limit, page * limit), total };
777
+ });
778
+ },
779
+ // Devolve os bytes DO TIPO do documento, não uma imagem para tudo: antes, abrir um PDF entregava
780
+ // um PNG rotulado `application/pdf` e o leitor recusava o arquivo. O `uploadId` é a única pista
781
+ // que o contrato dá, então o tipo vem da própria biblioteca.
782
+ getDocumentUrl(uploadId) {
783
+ return withLatency(() => {
784
+ const found = Object.values(PREVIEW_DOCUMENTS).flat().find((document) => document.id === uploadId);
785
+ return previewFileUrl(found?.mimeType, found?.filename);
786
+ });
547
787
  },
548
- getMediaProxyUrl() {
549
- return withLatency(() => ({ mimeType: "image/png", data: PREVIEW_IMAGE_BASE64 }));
788
+ // Caminho da mídia ainda não ingerida: o backend busca na Meta e devolve base64. Resolve pelo
789
+ // id para a bolha receber os bytes DO TIPO dela — devolvendo um PNG para todo id, vídeo e áudio
790
+ // apareciam quebrados na thread mesmo havendo amostra válida do formato.
791
+ getMediaProxyUrl(mediaId) {
792
+ return withLatency(() => {
793
+ const found = Object.values(PREVIEW_DOCUMENTS).flat().find((document) => document.id === `preview/inbound/${mediaId}`);
794
+ return previewFileBase64(found?.mimeType, found?.filename);
795
+ });
550
796
  },
551
797
  takeover(conversationId) {
552
798
  return withLatency(
@@ -777,12 +1023,65 @@ function startPreviewScript(params) {
777
1023
  }, intervalMs);
778
1024
  return () => clearInterval(timer);
779
1025
  }
1026
+
1027
+ // src/preview/MediaTypesPreview.tsx
1028
+ import { useMemo as useMemo2 } from "react";
1029
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1030
+ var MEDIA_TYPES_CONVERSATION_ID = "5511944443333";
1031
+ function MediaTypesPreview({
1032
+ conversationId = MEDIA_TYPES_CONVERSATION_ID,
1033
+ className
1034
+ }) {
1035
+ const store = useMemo2(
1036
+ () => createPreviewStore({ conversations: PREVIEW_CONVERSATIONS, messages: PREVIEW_MESSAGES }),
1037
+ []
1038
+ );
1039
+ const api = useMemo2(() => createMockConversationsApi({ store }), [store]);
1040
+ const sse = useMemo2(() => createMockSSEProvider({ store }), [store]);
1041
+ const messages = PREVIEW_MESSAGES[conversationId] ?? [];
1042
+ const documents = PREVIEW_DOCUMENTS[conversationId] ?? [];
1043
+ const mimeTypes = [...new Set(documents.map((document) => document.mimeType))];
1044
+ return /* @__PURE__ */ jsx2(ConversationsProvider, { api, sse, children: /* @__PURE__ */ jsxs2("div", { className, children: [
1045
+ /* @__PURE__ */ jsxs2("header", { className: "border-b px-4 py-3 dark:border-gray-700", children: [
1046
+ /* @__PURE__ */ jsx2("h1", { className: "text-lg font-semibold", children: "Teste manual de m\xEDdia" }),
1047
+ /* @__PURE__ */ jsxs2("p", { className: "text-sm text-gray-500", children: [
1048
+ documents.length,
1049
+ " arquivos, ",
1050
+ mimeTypes.length,
1051
+ " tipos. Clique no olho para abrir em aba nova e no bot\xE3o da bolha para carregar a m\xEDdia na thread \u2014 \xE9 o que teste automatizado n\xE3o v\xEA."
1052
+ ] })
1053
+ ] }),
1054
+ /* @__PURE__ */ jsxs2("div", { className: "grid gap-4 p-4 lg:grid-cols-2", children: [
1055
+ /* @__PURE__ */ jsxs2("section", { className: "space-y-3", children: [
1056
+ /* @__PURE__ */ jsx2("h2", { className: "text-sm font-semibold uppercase tracking-wide text-gray-500", children: "Biblioteca da empresa" }),
1057
+ /* @__PURE__ */ jsx2(DocumentsLibrary, { perPage: documents.length || 20 })
1058
+ ] }),
1059
+ /* @__PURE__ */ jsxs2("section", { className: "space-y-3", children: [
1060
+ /* @__PURE__ */ jsx2("h2", { className: "text-sm font-semibold uppercase tracking-wide text-gray-500", children: "Painel da conversa" }),
1061
+ /* @__PURE__ */ jsx2(ConversationDocumentsPanel, { conversationId, open: true, perPage: documents.length || 20 }),
1062
+ /* @__PURE__ */ jsx2("h2", { className: "text-sm font-semibold uppercase tracking-wide text-gray-500", children: "Bolhas na thread" }),
1063
+ /* @__PURE__ */ jsx2(ConversationWallpaper, { className: "max-h-[70vh] overflow-y-auto rounded-lg px-3 py-2", children: messages.map((message, index) => /* @__PURE__ */ jsx2(
1064
+ MessageBubble,
1065
+ {
1066
+ message,
1067
+ isMine: message.direction === "outbound",
1068
+ isFirstInGroup: index === 0 || messages[index - 1]?.sender !== message.sender
1069
+ },
1070
+ message.id
1071
+ )) })
1072
+ ] })
1073
+ ] })
1074
+ ] }) });
1075
+ }
780
1076
  export {
781
1077
  ConversationPreview,
782
1078
  DEFAULT_PREVIEW_SCRIPT,
783
1079
  GLOBAL_CHANNEL,
1080
+ MEDIA_TYPES_CONVERSATION_ID,
1081
+ MediaTypesPreview,
784
1082
  PREVIEW_CONVERSATIONS,
785
1083
  PREVIEW_DOCUMENTS,
1084
+ PREVIEW_FILE_SAMPLES,
786
1085
  PREVIEW_MESSAGES,
787
1086
  PreviewInProductionError,
788
1087
  PreviewWebhookRejectedError,
@@ -791,7 +1090,11 @@ export {
791
1090
  createMockConversationsApi,
792
1091
  createMockEventSource,
793
1092
  createMockSSEProvider,
1093
+ createPreviewMediaResolver,
794
1094
  createPreviewStore,
795
1095
  createPreviewWebhookClient,
1096
+ previewFileBase64,
1097
+ previewFileUrl,
1098
+ resolvePreviewFileSample,
796
1099
  startPreviewScript
797
1100
  };
@@ -1,3 +1,5 @@
1
+ import * as react from 'react';
2
+
1
3
  interface ConversationsUIConfig {
2
4
  apiBaseUrl: string;
3
5
  theme?: ConversationsTheme;
@@ -51,6 +53,16 @@ interface MessagePayload {
51
53
  isLastInGroup?: boolean;
52
54
  }
53
55
 
56
+ type ResolveMediaUrl = (message: MessagePayload) => Promise<string | null>;
57
+ interface MediaRendererProps {
58
+ message: MessagePayload;
59
+ onLightbox: (src: string) => void;
60
+ onResolveUrl?: ResolveMediaUrl;
61
+ /** Aplicado no wrapper de cada tipo de mídia — imagem, vídeo, áudio e documento. */
62
+ className?: string;
63
+ }
64
+ declare function MediaRenderer({ message, onLightbox, onResolveUrl, className }: MediaRendererProps): react.JSX.Element | null;
65
+
54
66
  /**
55
67
  * Canal de origem da conversa e o que cada um permite.
56
68
  *
@@ -154,6 +166,14 @@ interface ListDocumentsParams {
154
166
  source?: string;
155
167
  sortDirection?: 'asc' | 'desc';
156
168
  }
169
+ /** Arquivo na biblioteca da empresa: o mesmo da conversa, mais de qual conversa veio. */
170
+ interface CompanyDocument extends ConversationDocument {
171
+ conversationId: string;
172
+ }
173
+ interface CompanyDocumentPage {
174
+ documents: CompanyDocument[];
175
+ total: number;
176
+ }
157
177
  interface ConversationDocumentPage {
158
178
  documents: ConversationDocument[];
159
179
  total: number;
@@ -212,6 +232,13 @@ interface ConversationsApi {
212
232
  * lote em vez de oferecer um botão que falha.
213
233
  */
214
234
  downloadDocumentsArchive?(conversationId: string, uploadIds: readonly string[]): Promise<Blob>;
235
+ /**
236
+ * Biblioteca de TODAS as conversas, para uma tela de Documentos fora do atendimento.
237
+ *
238
+ * Opcional por capacidade: host que só expõe anexo dentro da conversa não implementa, e o
239
+ * componente de biblioteca simplesmente não é usável — melhor que uma tela que sempre erra.
240
+ */
241
+ getAllDocuments?(params?: ListDocumentsParams): Promise<CompanyDocumentPage>;
215
242
  getMediaProxyUrl(mediaId: string): Promise<{
216
243
  mimeType: string;
217
244
  data: string;
@@ -294,4 +321,4 @@ interface ConversationDocument {
294
321
  linkedAt: string;
295
322
  }
296
323
 
297
- export { CHANNEL_CAPABILITIES as C, DEFAULT_CONVERSATION_CHANNEL as D, type FormatContactHandleParams as F, HANDLE_KIND as H, type ListConversationsParams as L, type MessagePayload as M, REOPEN_MECHANISM as R, type SSEProvider as S, CHANNEL_FILTER_ALL as a, CONVERSATION_CHANNEL as b, type ChannelCapabilities as c, type ChannelFilter as d, type ChannelFilterOption as e, type ConversationChannel as f, type ConversationDocument as g, type ConversationDocumentPage as h, type ConversationEventSource as i, type ConversationPage as j, type ConversationSummary as k, type ConversationTemplate as l, type ConversationsApi as m, type ConversationsFeatures as n, type ConversationsTheme as o, type ConversationsUIConfig as p, type HandleKind as q, type ListDocumentsParams as r, type ReopenMechanism as s, capabilitiesOf as t, channelFiltersFor as u, contactFlag as v, formatContactHandle as w };
324
+ export { contactFlag as A, formatContactHandle as B, CHANNEL_CAPABILITIES as C, DEFAULT_CONVERSATION_CHANNEL as D, type FormatContactHandleParams as F, HANDLE_KIND as H, type ListConversationsParams as L, MediaRenderer as M, REOPEN_MECHANISM as R, type SSEProvider as S, CHANNEL_FILTER_ALL as a, CONVERSATION_CHANNEL as b, type ChannelCapabilities as c, type ChannelFilter as d, type ChannelFilterOption as e, type CompanyDocument as f, type CompanyDocumentPage as g, type ConversationChannel as h, type ConversationDocument as i, type ConversationDocumentPage as j, type ConversationEventSource as k, type ConversationPage as l, type ConversationSummary as m, type ConversationTemplate as n, type ConversationsApi as o, type ConversationsFeatures as p, type ConversationsTheme as q, type ConversationsUIConfig as r, type HandleKind as s, type ListDocumentsParams as t, type MediaRendererProps as u, type MessagePayload as v, type ReopenMechanism as w, type ResolveMediaUrl as x, capabilitiesOf as y, channelFiltersFor as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adatechnology/conversations-ui",
3
- "version": "0.1.0-rc.5",
3
+ "version": "0.1.0-rc.6",
4
4
  "description": "WhatsApp conversation UI components — parametrizável por endpoint, tema e feature flags",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/Avatar.tsx CHANGED
@@ -1,8 +1,18 @@
1
+ export interface AvatarLabels {
2
+ /** Lido por leitor de tela quando não há nome nem imagem — a silhueta genérica. */
3
+ unnamedContact: string
4
+ }
5
+
1
6
  export interface AvatarProps {
2
7
  name?: string | null
3
8
  avatarUrl?: string
4
9
  size?: 'sm' | 'md' | 'lg'
5
10
  className?: string
11
+ labels?: Partial<AvatarLabels>
12
+ }
13
+
14
+ export const DEFAULT_AVATAR_LABELS: AvatarLabels = {
15
+ unnamedContact: 'Contato sem nome',
6
16
  }
7
17
 
8
18
  const sizeClasses = {
@@ -36,8 +46,9 @@ function getBgColor(name: string | undefined | null): string {
36
46
 
37
47
  // Paridade com financiamento-imobiliario-bot/apps/web/src/components/Avatar.tsx —
38
48
  // mesmas iniciais (primeira+última letra), mesmo hash de cor, mesmas classes de tamanho.
39
- export function Avatar({ name, avatarUrl, size = 'md', className = '' }: AvatarProps) {
49
+ export function Avatar({ name, avatarUrl, size = 'md', className = '', labels }: AvatarProps) {
40
50
  const sizeClass = sizeClasses[size]
51
+ const unnamedContactLabel = labels?.unnamedContact ?? DEFAULT_AVATAR_LABELS.unnamedContact
41
52
 
42
53
  if (avatarUrl) {
43
54
  return (
@@ -58,7 +69,7 @@ export function Avatar({ name, avatarUrl, size = 'md', className = '' }: AvatarP
58
69
  <div
59
70
  className={`${sizeClass} ${bgColor} rounded-full flex items-center justify-center text-white flex-shrink-0 ${className}`}
60
71
  role="img"
61
- aria-label="Contato sem nome"
72
+ aria-label={unnamedContactLabel}
62
73
  >
63
74
  <svg viewBox="0 0 24 24" fill="currentColor" className="h-[60%] w-[60%]" aria-hidden>
64
75
  <path d="M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-4.42 0-8 2.24-8 5v1h16v-1c0-2.76-3.58-5-8-5Z" />