@adatechnology/conversations-ui 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +96 -1
  2. package/dist/{ConversationSimulatorPanel-CrfFjiWh.d.ts → ConversationSimulatorPanel-q-wVgBCN.d.ts} +1 -1
  3. package/dist/{chunk-HMCOTZAX.js → chunk-NSOAUYNE.js} +33 -28
  4. package/dist/chunk-ZSOD5AQT.js +19 -0
  5. package/dist/flows/index.js +4 -2
  6. package/dist/index.d.ts +4 -4
  7. package/dist/index.js +5 -3
  8. package/dist/participant/index.d.ts +48 -3
  9. package/dist/participant/index.js +782 -347
  10. package/dist/preview/index.d.ts +3 -3
  11. package/dist/{quickReply.types-B4gmZsSr.d.ts → quickReply.types-DzWH3NYV.d.ts} +1 -1
  12. package/dist/styles.css +173 -52
  13. package/package.json +2 -1
  14. package/src/Tooltip.tsx +5 -12
  15. package/src/TooltipDom.test.tsx +78 -0
  16. package/src/domHarness.test-helper.ts +53 -0
  17. package/src/participant/ConversationThread.test.tsx +165 -0
  18. package/src/participant/ConversationThread.tsx +117 -0
  19. package/src/participant/ParticipantBackButton.tsx +1 -1
  20. package/src/participant/ParticipantBubbleBody.tsx +54 -0
  21. package/src/participant/ParticipantBubbleStatus.tsx +22 -28
  22. package/src/participant/ParticipantChannelBadges.tsx +10 -2
  23. package/src/participant/ParticipantComposer.tsx +1 -1
  24. package/src/participant/ParticipantComposerControls.tsx +2 -0
  25. package/src/participant/ParticipantCopyableToken.tsx +1 -0
  26. package/src/participant/ParticipantFailedMenu.tsx +40 -0
  27. package/src/participant/ParticipantFailedMenuList.tsx +41 -0
  28. package/src/participant/ParticipantMessageBubble.test.tsx +6 -6
  29. package/src/participant/ParticipantMessageBubble.tsx +26 -54
  30. package/src/participant/ParticipantMessageText.test.tsx +1 -1
  31. package/src/participant/ParticipantMessageText.tsx +4 -2
  32. package/src/participant/ParticipantProtocolBadge.tsx +1 -0
  33. package/src/participant/ParticipantScrollToLatest.tsx +1 -1
  34. package/src/participant/ParticipantThread.tsx +30 -10
  35. package/src/participant/ParticipantThreadHeader.test.tsx +1 -1
  36. package/src/participant/ParticipantThreadHeader.tsx +10 -0
  37. package/src/participant/ParticipantThreadScreen.tsx +6 -46
  38. package/src/participant/ParticipantTimeline.tsx +6 -3
  39. package/src/participant/conversationThreadApi.test.ts +27 -0
  40. package/src/participant/conversationThreadApi.ts +24 -0
  41. package/src/participant/conversationThreadCss.test.ts +116 -0
  42. package/src/participant/conversationThreadSummary.ts +34 -0
  43. package/src/participant/failedMenuKeyboard.ts +29 -0
  44. package/src/participant/index.ts +3 -0
  45. package/src/participant/participantContrast.test-helper.ts +116 -0
  46. package/src/participant/participantFailedMenuDom.test.tsx +154 -0
  47. package/src/participant/participantFailedMenuState.test.ts +61 -0
  48. package/src/participant/participantFailedMenuState.ts +57 -0
  49. package/src/participant/participantFailedMessage.test.tsx +83 -0
  50. package/src/participant/participantGolden.test.tsx +46 -0
  51. package/src/participant/participantGoldenScenarios.test-helper.tsx +57 -0
  52. package/src/participant/participantLabels.ts +6 -0
  53. package/src/participant/participantMessages.ts +4 -3
  54. package/src/participant/participantNewMessages.test.ts +21 -0
  55. package/src/participant/participantNewMessages.ts +8 -3
  56. package/src/participant/participantPerspective.test.ts +67 -0
  57. package/src/participant/participantPerspective.ts +50 -0
  58. package/src/participant/participantPerspectiveRender.test.tsx +94 -0
  59. package/src/participant/participantThemeContrast.test.ts +106 -0
  60. package/src/participant/participantTooltips.test.tsx +74 -0
  61. package/src/participant/test-fixtures/conversations-list.golden.html +1 -0
  62. package/src/participant/test-fixtures/conversations-selected.golden.html +1 -0
  63. package/src/participant/test-fixtures/thread-avatars-initials.golden.html +1 -0
  64. package/src/participant/test-fixtures/thread-closed.golden.html +1 -0
  65. package/src/participant/test-fixtures/thread-default.golden.html +1 -0
  66. package/src/participant/test-fixtures/thread-error.golden.html +1 -0
  67. package/src/participant/test-fixtures/thread-loading.golden.html +1 -0
  68. package/src/participant/test-fixtures/thread-plain.golden.html +1 -0
  69. package/src/participant/test-fixtures/thread-tail-off.golden.html +1 -0
  70. package/src/participant/useFailedMenuEffects.ts +59 -0
  71. package/src/participant/useParticipantConversationView.ts +3 -0
  72. package/src/participant/useParticipantFailedMenu.ts +60 -0
  73. package/src/participant/useParticipantThread.ts +13 -2
  74. package/src/participant/useParticipantThreadController.ts +112 -0
  75. package/src/participant/useParticipantThreadScroll.ts +3 -1
  76. package/src/participant/useParticipantUnread.test.tsx +40 -0
  77. package/src/participant/useParticipantUnread.ts +38 -0
  78. package/src/participant/useSuppressTouchTooltips.ts +38 -0
  79. package/src/styles.css +43 -17
  80. package/src/tooltipLayerRegistry.test.ts +56 -0
  81. package/src/tooltipLayerRegistry.ts +34 -0
package/README.md CHANGED
@@ -299,6 +299,99 @@ A bolha usa um renderizador próprio da visão do participante (`ParticipantMess
299
299
 
300
300
  **Valores copiáveis:** a detecção é conservadora. CPF e CNPJ só valem com dígitos verificadores corretos (sequências repetidas não valem), a chave exige exatamente 44 dígitos (grupos de 4 opcionais) e o telefone exige 10 a 15 dígitos (BR: DDD 11–99) **com formatação**: sem máscara, só vale com `+55`, DDD entre parênteses ou separador depois do DDD (`1234567890` solto, como em "pedido 1234567890", não é telefone); número internacional exige `+`. Valores monetários, datas e números curtos não são destacados. O toque copia, mostra "Copied" no próprio valor e anuncia na região `aria-live` única da conversa. Os rótulos `copyValue`, `valueCopied` e `externalLinkHint` vêm de `labels` (padrão em inglês). O SDK só escreve na área de transferência quando a pessoa toca: não envia nem armazena nada.
301
301
 
302
+ ### `ConversationThread`: uma conversa, vista por qualquer um dos lados
303
+
304
+ `ConversationThread` desenha **uma** conversa (um assunto) no mesmo desenho da visão do participante, sem lista
305
+ e sem inbox. Serve ao app do participante e, com `perspective="operator"`, ao painel de quem atende. O SDK não
306
+ conhece as ações do host: encerrar, reabrir ou qualquer outra entram pelo slot `headerActions`.
307
+
308
+ ```tsx
309
+ import { ConversationThread } from '@adatechnology/conversations-ui/participant'
310
+
311
+ <ConversationThread
312
+ api={operatorApi}
313
+ subject={{ subjectType: 'case', subjectId: caseId }}
314
+ perspective="operator"
315
+ title={caseTitle}
316
+ protocol={protocol}
317
+ channels={['app', 'whatsapp']}
318
+ status={isClosed ? 'closed' : 'open'}
319
+ counterpartLabel={`Participante: ${participantName}`}
320
+ headerActions={<button type="button" onClick={handleClose}>Fechar</button>}
321
+ avatars="initials"
322
+ quickReplies={quickReplies}
323
+ labels={{ messageInputPlaceholder: 'Mensagem ao participante' }}
324
+ />
325
+ ```
326
+
327
+ | Prop | Tipo | Efeito |
328
+ | ---- | ---- | ------ |
329
+ | `api` | `ConversationThreadApi` | `fetchMessages`, `sendMessage` e `resolveAttachmentUrl` obrigatórios; `markRead` e `subscribe` opcionais (sem `markRead` não há marcação de leitura nem `onMarkedRead`). Deve ser **estável** (constante de módulo ou `useMemo`): um objeto novo a cada render recria o adapter e a revalidação. É o mesmo adapter de `ParticipantConversationsApi`, sem `listConversations` e `openConversation` |
330
+ | `subject` | `ParticipantSubjectRef` | o assunto da conversa |
331
+ | `perspective` | `'participant' \| 'operator'` | padrão `participant` (comportamento da tela de sempre) |
332
+ | `title` | `string` | título do cabeçalho |
333
+ | `protocol?`, `channels?` | | selo copiável e selos de canal; ausentes não desenham nada |
334
+ | `status?` | `'open' \| 'closed'` | `closed` tira o compositor e mostra `labels.closedNotice` |
335
+ | `counterpartLabel?` | `string` | linha curta sob o título, montada pelo host |
336
+ | `headerActions?` | `ReactNode` | slot à direita do cabeçalho; ausente não desenha nada. Espera botões curtos ou só ícone, cada um com `aria-label`: com ações, a barra quebra de linha e o título mantém no mínimo `6rem` |
337
+ | `onBack?` | `() => void` | ausente = sem seta (o diálogo do host já tem o próprio fechar) |
338
+ | `quickReplies?` | `QuickReply[]` | chips acima do campo; tocar **preenche**, nunca envia |
339
+ | `renderSubjectIcon?`, `subjectGroups?` | | ícone do assunto no cabeçalho, como na lista |
340
+ | `avatars?`, `renderAuthorAvatar?`, `tail?` | | iguais aos de `ParticipantConversations` |
341
+ | `labels?`, `theme?`, `className?`, `locale?`, `channel?` | | iguais aos de `ParticipantConversations` |
342
+ | `pendingMessages?`, `onRetryPending?`, `onMarkedRead?` | | fila durável do host e aviso de leitura registrada |
343
+
344
+ **Perspectiva.** `direction` é sempre do ponto de vista da empresa. Em `participant`, "minha" é `inbound`.
345
+ Em `operator`, "minha" é `outbound`: a bolha própria vai à direita, os tiques de enviada, entregue e lida
346
+ aparecem nas mensagens do operador, e o avatar de iniciais e o rótulo de autor (`authorName`) aparecem nas da
347
+ contraparte (`inbound`). O envio, o rascunho local, os estados `sending`/`queued`/`failed` e o retry são os de
348
+ sempre. A leitura segue a perspectiva: o componente chama `markRead(subject)` quando há mensagens **da outra
349
+ parte** sem `readAt` e sem `status: 'read'`; o operador marca só as `inbound`. Sem `subscribe`, a conversa
350
+ revalida ao voltar o foco, a visibilidade e a conexão.
351
+
352
+ **Exemplo de adapter do operador.** O painel já tem as rotas dele; o adapter só traduz:
353
+
354
+ ```ts
355
+ import { z } from 'zod'
356
+
357
+ import { participantMessageSchema } from '@adatechnology/conversation-contracts'
358
+ import type { ConversationThreadApi } from '@adatechnology/conversations-ui/participant'
359
+
360
+ export const operatorThreadApi: ConversationThreadApi = {
361
+ fetchMessages: async ({ subjectType, subjectId }, params) =>
362
+ z.array(participantMessageSchema).parse(await http.get(`/conversations/${subjectType}/${subjectId}/messages`, { params })),
363
+ sendMessage: async ({ subject, clientMessageId, text, files }) => {
364
+ const body = await http.post(`/conversations/${subject.subjectType}/${subject.subjectId}/messages`, { text, files }, {
365
+ headers: { 'Idempotency-Key': clientMessageId },
366
+ })
367
+ return { outcome: 'sent', message: participantMessageSchema.parse(body) }
368
+ },
369
+ markRead: ({ subjectType, subjectId }) => http.post(`/conversations/${subjectType}/${subjectId}/read`),
370
+ resolveAttachmentUrl: async (attachment, disposition) => (await http.get(`/attachments/${attachment.id}/url`, { params: { disposition } })).url,
371
+ }
372
+ ```
373
+
374
+ **Tema claro do painel.** Os tokens padrão já são claros. Para um painel bege com acento laranja, basta mapear:
375
+
376
+ ```css
377
+ .panel-conversation {
378
+ --cv-p-surface: #f2efe9;
379
+ --cv-p-surface-raised: #ffffff;
380
+ --cv-p-text: #1f2a30;
381
+ --cv-p-text-muted: #59636a;
382
+ --cv-p-border: #cfc8bb;
383
+ --cv-p-accent: #a85a1c;
384
+ --cv-p-highlight: #fbf3ea;
385
+ }
386
+ ```
387
+
388
+ Os contrastes são verificados por teste (`participantThemeContrast.test.ts`) com os padrões claro e escuro e com este
389
+ mapeamento: texto, hora, autor, links e valores copiáveis nas duas bolhas a 4,5:1 ou mais; tiques, borda da bolha
390
+ própria e sublinhado dos valores copiáveis a 3:1 ou mais; o fundo pontilhado se mantém sutil (no máximo 2:1 contra a
391
+ superfície) e nenhum texto depende dele (o erro de carga ganha painel sólido). A borda da bolha **recebida** é
392
+ decorativa (cerca de 1,4:1): quem a distingue da página é o preenchimento, e o texto passa de 4,5:1. Ao mapear o
393
+ acento, mantenha-o a 4,5:1 contra `--cv-p-highlight`, que é onde ficam os links da bolha própria. Os contrastes só valem com `--cv-p-highlight` **opaco**: o padrão do tema escuro do pacote é translúcido (`rgb(213 138 71 / 10%)`), então o host que mapeia o token deve fornecer um valor opaco.
394
+
302
395
  ### Limitações conhecidas
303
396
 
304
397
  - O avatar alinha pelo topo da bolha, não pelo rabinho no canto inferior.
@@ -319,12 +412,14 @@ Defina no `.cv-p`, no wrapper (`className`) ou em qualquer ancestral. Há també
319
412
  | `--cv-p-border` | bordas | `#d5d9df` |
320
413
  | `--cv-p-accent` | destaque, botão primário, bolha própria | `#a85a1c` |
321
414
  | `--cv-p-accent-contrast` | texto sobre o destaque | `#ffffff` |
322
- | `--cv-p-highlight` | fundo de "espera sua resposta" | `#fbefe2` |
415
+ | `--cv-p-highlight` | fundo de "espera sua resposta" e da bolha própria | `#fcf3ea` (use cor opaca: é fundo de bolha, e translúcido deixa o wallpaper aparecer) |
323
416
  | `--cv-p-danger` | erro e falha de envio | `#c62828` |
324
417
  | `--cv-p-radius` | raio dos cantos | `0` |
325
418
  | `--cv-p-wallpaper` | fundo da lista de mensagens (`none` desliga) | pontos sutis |
326
419
  | `--cv-p-scrollbar-thumb` | polegar das barras de rolagem finas | `--cv-p-border` |
327
420
  | `--cv-p-scrollbar-thumb-hover` | polegar ao passar o mouse ou arrastar | `--cv-p-accent` |
421
+ | `--cv-p-tick` | tiques de enviada/entregue na bolha própria | `--cv-p-text-muted` |
422
+ | `--cv-p-tick-read` | tique de lida | `#0369a1` (escuro `#5cc8ff`) |
328
423
  | `--cv-p-channel-app` | cor do selo do canal app | o destaque |
329
424
  | `--cv-p-channel-whatsapp` | cor do selo do canal WhatsApp | `#20914f` |
330
425
 
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { M as MessagePayload, b as MessageTranscription, C as ConversationChannel, Q as QuickReply, c as QuickReplyInput, d as QuickReplyAttachment, S as StoredAttachmentSendResult, I as InteractiveSelection } from './quickReply.types-B4gmZsSr.js';
3
+ import { M as MessagePayload, a as MessageTranscription, C as ConversationChannel, Q as QuickReply, b as QuickReplyInput, c as QuickReplyAttachment, S as StoredAttachmentSendResult, I as InteractiveSelection } from './quickReply.types-DzWH3NYV.js';
4
4
  import { InteractiveReplyOption, InboundMediaType } from '@adatechnology/meta-whatsapp-contracts/testing';
5
5
 
6
6
  type ResolveMediaUrl = (message: MessagePayload) => Promise<string | null>;
@@ -1,10 +1,36 @@
1
- import {
2
- cn
3
- } from "./chunk-PJNR6UMN.js";
4
-
5
1
  // src/Tooltip.tsx
6
2
  import { useEffect, useRef, useState } from "react";
7
3
  import { createPortal } from "react-dom";
4
+
5
+ // src/tooltipLayerRegistry.ts
6
+ function createTooltipLayerRegistry() {
7
+ const layers = /* @__PURE__ */ new Map();
8
+ let owner;
9
+ function handOff() {
10
+ const next = layers.entries().next();
11
+ if (next.done) {
12
+ owner = void 0;
13
+ return;
14
+ }
15
+ const [layer, onOwnershipChange] = next.value;
16
+ owner = layer;
17
+ onOwnershipChange(true);
18
+ }
19
+ return {
20
+ register(layer, onOwnershipChange) {
21
+ layers.set(layer, onOwnershipChange);
22
+ if (owner === void 0) handOff();
23
+ return () => {
24
+ layers.delete(layer);
25
+ if (owner !== layer) return;
26
+ owner = void 0;
27
+ handOff();
28
+ };
29
+ }
30
+ };
31
+ }
32
+
33
+ // src/Tooltip.tsx
8
34
  import { jsx } from "react/jsx-runtime";
9
35
  var TOOLTIP_ATTRIBUTE = "data-cv-tooltip";
10
36
  var TOOLTIP_DELAY_MS = 120;
@@ -26,20 +52,12 @@ function tooltipStateFor(target, text) {
26
52
  });
27
53
  return { text, ...position };
28
54
  }
29
- var layerOwner;
55
+ var layerRegistry = createTooltipLayerRegistry();
30
56
  function TooltipLayer() {
31
57
  const identity = useRef({});
32
58
  const [isOwner, setIsOwner] = useState(false);
33
59
  const [tooltip, setTooltip] = useState(void 0);
34
- useEffect(() => {
35
- const owned = identity.current;
36
- if (layerOwner && layerOwner !== owned) return void 0;
37
- layerOwner = owned;
38
- setIsOwner(true);
39
- return () => {
40
- if (layerOwner === owned) layerOwner = void 0;
41
- };
42
- }, []);
60
+ useEffect(() => layerRegistry.register(identity.current, setIsOwner), []);
43
61
  useEffect(() => {
44
62
  if (!isOwner) return void 0;
45
63
  let timer;
@@ -108,20 +126,7 @@ function TooltipLayer() {
108
126
  );
109
127
  }
110
128
 
111
- // src/lib/WhatsAppMessagePreview.tsx
112
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
113
- function WhatsAppMessagePreview({ children, belowBubble, footer, className }) {
114
- return /* @__PURE__ */ jsxs("div", { className: cn("rounded-xl bg-[#e5ddd5] dark:bg-gray-900 p-3 space-y-1.5", className), children: [
115
- /* @__PURE__ */ jsxs("div", { className: "max-w-[85%]", children: [
116
- /* @__PURE__ */ jsx2("div", { className: "rounded-lg rounded-tl-none bg-white dark:bg-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 shadow-sm whitespace-pre-wrap break-words", children }),
117
- belowBubble
118
- ] }),
119
- footer
120
- ] });
121
- }
122
-
123
129
  export {
124
130
  TOOLTIP_ATTRIBUTE,
125
- TooltipLayer,
126
- WhatsAppMessagePreview
131
+ TooltipLayer
127
132
  };
@@ -0,0 +1,19 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-PJNR6UMN.js";
4
+
5
+ // src/lib/WhatsAppMessagePreview.tsx
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function WhatsAppMessagePreview({ children, belowBubble, footer, className }) {
8
+ return /* @__PURE__ */ jsxs("div", { className: cn("rounded-xl bg-[#e5ddd5] dark:bg-gray-900 p-3 space-y-1.5", className), children: [
9
+ /* @__PURE__ */ jsxs("div", { className: "max-w-[85%]", children: [
10
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg rounded-tl-none bg-white dark:bg-gray-700 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 shadow-sm whitespace-pre-wrap break-words", children }),
11
+ belowBubble
12
+ ] }),
13
+ footer
14
+ ] });
15
+ }
16
+
17
+ export {
18
+ WhatsAppMessagePreview
19
+ };
@@ -1,7 +1,9 @@
1
1
  import {
2
- TooltipLayer,
3
2
  WhatsAppMessagePreview
4
- } from "../chunk-HMCOTZAX.js";
3
+ } from "../chunk-ZSOD5AQT.js";
4
+ import {
5
+ TooltipLayer
6
+ } from "../chunk-NSOAUYNE.js";
5
7
  import {
6
8
  parseWhatsAppFormatting,
7
9
  useIsDarkTheme
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { ReactNode, CSSProperties, UIEvent, FormEvent, RefObject, KeyboardEvent } from 'react';
3
- import { M as MessagePayload, I as InteractiveSelection, b as MessageTranscription, C as ConversationChannel, e as InteractivePayload, f as ConversationsFeatures, Q as QuickReply$1, T as TranscriptionMode, g as QueuedAttachment, c as QuickReplyInput, d as QuickReplyAttachment, h as ConversationVariable, i as ChannelFilter, j as ChannelFilterOption } from './quickReply.types-B4gmZsSr.js';
4
- export { k as CHANNEL_CAPABILITIES, l as CHANNEL_FILTER_ALL, m as CONVERSATION_CHANNEL, n as ChannelCapabilities, a as ConversationsTheme, o as ConversationsUIConfig, D as DEFAULT_CONVERSATION_CHANNEL, F as FormatContactHandleParams, H as HANDLE_KIND, p as HandleKind, q as InteractiveOption, r as InteractiveSection, s as QUICK_REPLY_ATTACHMENT_LIMIT, R as REOPEN_MECHANISM, t as ReopenMechanism, S as StoredAttachmentSendResult, u as TranscriptionStatus, v as capabilitiesOf, w as channelFiltersFor, x as contactFlag, y as formatContactHandle } from './quickReply.types-B4gmZsSr.js';
5
- import { R as ResolveMediaUrl, C as ConversationSummary, L as ListConversationsParams, b as ConversationsApi, S as SSEProvider, Q as ListDocumentsParams, d as ConversationDocument, U as ConversationTemplate, k as ConversationSimulatorClient, m as ConversationSimulatorPanelLabels, P as PreviewUploadedMedia } from './ConversationSimulatorPanel-CrfFjiWh.js';
6
- export { A as AudioRecorderButton, g as AudioRecorderButtonLabels, h as AudioRecorderButtonProps, V as CompanyDocument, W as CompanyDocumentPage, X as ConversationDocumentPage, a as ConversationEventSource, c as ConversationPage, D as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, Y as DEFAULT_MAX_RECORDING_MILLISECONDS, Z as MediaRenderer, _ as MediaRendererProps, y as SendSimulatorMediaParams, z as SimulatorMediaKind } from './ConversationSimulatorPanel-CrfFjiWh.js';
3
+ import { M as MessagePayload, I as InteractiveSelection, a as MessageTranscription, C as ConversationChannel, e as InteractivePayload, f as ConversationsFeatures, Q as QuickReply$1, T as TranscriptionMode, g as QueuedAttachment, b as QuickReplyInput, c as QuickReplyAttachment, h as ConversationVariable, i as ChannelFilter, j as ChannelFilterOption } from './quickReply.types-DzWH3NYV.js';
4
+ export { k as CHANNEL_CAPABILITIES, l as CHANNEL_FILTER_ALL, m as CONVERSATION_CHANNEL, n as ChannelCapabilities, d as ConversationsTheme, o as ConversationsUIConfig, D as DEFAULT_CONVERSATION_CHANNEL, F as FormatContactHandleParams, H as HANDLE_KIND, p as HandleKind, q as InteractiveOption, r as InteractiveSection, s as QUICK_REPLY_ATTACHMENT_LIMIT, R as REOPEN_MECHANISM, t as ReopenMechanism, S as StoredAttachmentSendResult, u as TranscriptionStatus, v as capabilitiesOf, w as channelFiltersFor, x as contactFlag, y as formatContactHandle } from './quickReply.types-DzWH3NYV.js';
5
+ import { R as ResolveMediaUrl, C as ConversationSummary, L as ListConversationsParams, b as ConversationsApi, S as SSEProvider, Q as ListDocumentsParams, d as ConversationDocument, U as ConversationTemplate, k as ConversationSimulatorClient, m as ConversationSimulatorPanelLabels, P as PreviewUploadedMedia } from './ConversationSimulatorPanel-q-wVgBCN.js';
6
+ export { A as AudioRecorderButton, g as AudioRecorderButtonLabels, h as AudioRecorderButtonProps, V as CompanyDocument, W as CompanyDocumentPage, X as ConversationDocumentPage, a as ConversationEventSource, c as ConversationPage, D as DEFAULT_AUDIO_RECORDER_BUTTON_LABELS, Y as DEFAULT_MAX_RECORDING_MILLISECONDS, Z as MediaRenderer, _ as MediaRendererProps, y as SendSimulatorMediaParams, z as SimulatorMediaKind } from './ConversationSimulatorPanel-q-wVgBCN.js';
7
7
  import { ChannelCapability } from '@adatechnology/conversation-contracts';
8
8
  export { ChannelCapability } from '@adatechnology/conversation-contracts';
9
9
  import '@adatechnology/meta-whatsapp-contracts/testing';
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import {
2
- TOOLTIP_ATTRIBUTE,
3
- TooltipLayer,
4
2
  WhatsAppMessagePreview
5
- } from "./chunk-HMCOTZAX.js";
3
+ } from "./chunk-ZSOD5AQT.js";
4
+ import {
5
+ TOOLTIP_ATTRIBUTE,
6
+ TooltipLayer
7
+ } from "./chunk-NSOAUYNE.js";
6
8
  import {
7
9
  AudioPlayer,
8
10
  AudioRecorderButton,
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { ParticipantConversationSummary, ParticipantSubjectRef, ParticipantConversationPage, ParticipantMessage, ParticipantAttachment } from '@adatechnology/conversation-contracts';
3
+ import { ParticipantConversationSummary, ParticipantConversationPage, ParticipantSubjectRef, ParticipantMessage, ParticipantAttachment } from '@adatechnology/conversation-contracts';
4
4
  export { ParticipantAttachment, ParticipantConversationPage, ParticipantConversationSummary, ParticipantMessage, ParticipantSubjectRef } from '@adatechnology/conversation-contracts';
5
- import { C as ConversationChannel, a as ConversationsTheme, Q as QuickReply } from '../quickReply.types-B4gmZsSr.js';
5
+ import { C as ConversationChannel, d as ConversationsTheme, Q as QuickReply } from '../quickReply.types-DzWH3NYV.js';
6
6
 
7
7
  type ParticipantAuthorAvatarRenderer = (author: {
8
8
  name: string | null;
@@ -140,9 +140,12 @@ type ParticipantConversationsLabels = {
140
140
  readonly statusRead: string;
141
141
  readonly statusFailed: string;
142
142
  readonly statusFailedShort: string;
143
+ /** @deprecated No longer rendered by the default view: the retry button is icon-only and named by `statusFailed`. */
143
144
  readonly retry: string;
144
145
  readonly discard: string;
145
146
  readonly edit: string;
147
+ /** Name of the menu button on a failed message. Optional; translated hosts should fill it, otherwise the English default shows. */
148
+ readonly messageOptions?: string;
146
149
  readonly notFound: string;
147
150
  readonly loading: string;
148
151
  readonly loadError: string;
@@ -198,6 +201,48 @@ type ParticipantConversationsProps = {
198
201
  };
199
202
  declare function ParticipantConversations(props: ParticipantConversationsProps): react.JSX.Element;
200
203
 
204
+ /** What one conversation needs from the host: no list, no open-by-subject. `markRead` and `subscribe` are optional. */
205
+ type ConversationThreadApi = Pick<ParticipantConversationsApi, 'fetchMessages' | 'sendMessage' | 'resolveAttachmentUrl'> & Partial<Pick<ParticipantConversationsApi, 'markRead' | 'subscribe'>>;
206
+
207
+ /** Who is looking at the conversation. `participant` is the outside party; `operator` is the company. */
208
+ type ParticipantPerspective = 'participant' | 'operator';
209
+
210
+ type ConversationThreadProps = {
211
+ readonly api: ConversationThreadApi;
212
+ readonly subject: ParticipantSubjectRef;
213
+ /** `participant` (default) is the outside party; `operator` is the company, which owns the outbound messages. */
214
+ readonly perspective?: ParticipantPerspective;
215
+ readonly title: string;
216
+ readonly protocol?: string;
217
+ readonly channels?: ParticipantConversationSummary['channels'];
218
+ /** `closed` removes the composer and shows `labels.closedNotice`. */
219
+ readonly status?: 'open' | 'closed';
220
+ /** Short line under the title, written by the host (for example "Participant: Ana"). */
221
+ readonly counterpartLabel?: string;
222
+ /** Slot at the end of the header bar for the host's own actions (close, reopen...). The package knows none of them. */
223
+ readonly headerActions?: ReactNode;
224
+ /** Absent draws no back arrow. */
225
+ readonly onBack?: () => void;
226
+ readonly renderSubjectIcon?: ParticipantSubjectIconRenderer;
227
+ readonly subjectGroups?: readonly ParticipantSubjectGroup[];
228
+ /** Chips above the field: tapping one fills the text, it never sends. */
229
+ readonly quickReplies?: readonly QuickReply[];
230
+ readonly labels?: Partial<ParticipantConversationsLabels>;
231
+ readonly theme?: ConversationsTheme;
232
+ readonly className?: string;
233
+ readonly locale?: string;
234
+ readonly channel?: ConversationChannel;
235
+ readonly pendingMessages?: readonly ParticipantPendingMessage[];
236
+ readonly onRetryPending?: (clientMessageId: string) => void;
237
+ /** Tells the host the other side's messages were marked read, to refresh its own badges. */
238
+ readonly onMarkedRead?: (subject: ParticipantSubjectRef) => void;
239
+ readonly tail?: boolean;
240
+ readonly avatars?: 'initials';
241
+ readonly renderAuthorAvatar?: ParticipantAuthorAvatarRenderer;
242
+ };
243
+ /** One conversation about one subject, drawn like the participant app and seen from either side. */
244
+ declare function ConversationThread(props: ConversationThreadProps): react.JSX.Element;
245
+
201
246
  declare function groupParticipantConversations(params: GroupParticipantConversationsParams): ParticipantInboxView;
202
247
 
203
- export { DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS, type GroupParticipantConversationsParams, type ParticipantAuthorAvatarRenderer, type ParticipantConversationEvent, ParticipantConversations, type ParticipantConversationsApi, type ParticipantConversationsClassNames, type ParticipantConversationsLabels, type ParticipantConversationsProps, type ParticipantInboxSection, type ParticipantInboxView, type ParticipantLocalPendingMessage, type ParticipantPendingMessage, type ParticipantSendInput, type ParticipantSendResult, type ParticipantSubjectGroup, type ParticipantSubjectIconRenderer, groupParticipantConversations };
248
+ export { ConversationThread, type ConversationThreadApi, type ConversationThreadProps, DEFAULT_PARTICIPANT_CONVERSATIONS_LABELS, type GroupParticipantConversationsParams, type ParticipantAuthorAvatarRenderer, type ParticipantConversationEvent, ParticipantConversations, type ParticipantConversationsApi, type ParticipantConversationsClassNames, type ParticipantConversationsLabels, type ParticipantConversationsProps, type ParticipantInboxSection, type ParticipantInboxView, type ParticipantLocalPendingMessage, type ParticipantPendingMessage, type ParticipantPerspective, type ParticipantSendInput, type ParticipantSendResult, type ParticipantSubjectGroup, type ParticipantSubjectIconRenderer, groupParticipantConversations };