@adatechnology/conversations-ui 0.6.0 → 0.7.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.
- package/README.md +126 -19
- package/dist/{chunk-HOKNKXIT.js → chunk-HMCOTZAX.js} +1 -1
- package/dist/{chunk-FAQLC3TX.js → chunk-L3OFAQPS.js} +2 -4
- package/dist/{chunk-CBYEHPX6.js → chunk-PJNR6UMN.js} +63 -1
- package/dist/flows/index.js +3 -5
- package/dist/index.js +834 -764
- package/dist/participant/index.d.ts +18 -1
- package/dist/participant/index.js +1188 -313
- package/dist/preview/index.js +2 -3
- package/dist/styles.css +591 -19
- package/package.json +1 -1
- package/src/cssBlocks.test-helper.ts +31 -0
- package/src/legacyStyles.golden.test.ts +2 -31
- package/src/participant/ParticipantAuthorAvatar.test.tsx +30 -0
- package/src/participant/ParticipantAuthorAvatar.tsx +24 -0
- package/src/participant/ParticipantBackButton.tsx +14 -0
- package/src/participant/ParticipantBubbleStatus.tsx +67 -0
- package/src/participant/ParticipantChannelBadges.tsx +13 -4
- package/src/participant/ParticipantComposer.tsx +8 -40
- package/src/participant/ParticipantComposerControls.tsx +83 -0
- package/src/participant/ParticipantConversationAvatar.tsx +13 -0
- package/src/participant/ParticipantConversations.tsx +7 -0
- package/src/participant/ParticipantConversationsOptions.test.tsx +87 -0
- package/src/participant/ParticipantCopyAnnouncer.tsx +48 -0
- package/src/participant/ParticipantCopyableToken.tsx +51 -0
- package/src/participant/ParticipantDayDivider.tsx +18 -0
- package/src/participant/ParticipantInbox.tsx +3 -1
- package/src/participant/ParticipantInboxRow.tsx +6 -4
- package/src/participant/ParticipantMessageBubble.test.tsx +52 -5
- package/src/participant/ParticipantMessageBubble.tsx +25 -81
- package/src/participant/ParticipantMessageNodes.tsx +54 -0
- package/src/participant/ParticipantMessageText.test.tsx +106 -0
- package/src/participant/ParticipantMessageText.tsx +52 -0
- package/src/participant/ParticipantProtocolBadge.tsx +15 -7
- package/src/participant/ParticipantProtocolView.test.tsx +4 -6
- package/src/participant/ParticipantScrollToLatest.test.tsx +153 -0
- package/src/participant/ParticipantScrollToLatest.tsx +40 -0
- package/src/participant/ParticipantThread.test.tsx +37 -0
- package/src/participant/ParticipantThread.tsx +45 -95
- package/src/participant/ParticipantThreadAvatarSequence.test.tsx +65 -0
- package/src/participant/ParticipantThreadHeader.test.tsx +181 -0
- package/src/participant/ParticipantThreadHeader.tsx +73 -0
- package/src/participant/ParticipantThreadScreen.tsx +24 -10
- package/src/participant/ParticipantTimeline.tsx +103 -0
- package/src/participant/index.ts +1 -0
- package/src/participant/participantAutoGrow.test.ts +43 -0
- package/src/participant/participantAutoGrow.ts +12 -0
- package/src/participant/participantAvatar.test.ts +74 -0
- package/src/participant/participantAvatar.ts +48 -0
- package/src/participant/participantBubbleTail.test.tsx +107 -0
- package/src/participant/participantChatDesign.test.tsx +198 -0
- package/src/participant/participantCopyAnnouncement.test.ts +52 -0
- package/src/participant/participantCopyAnnouncement.ts +37 -0
- package/src/participant/participantCss.test-helper.ts +34 -0
- package/src/participant/participantDay.test.ts +22 -0
- package/src/participant/participantDay.ts +8 -0
- package/src/participant/participantDocuments.ts +45 -0
- package/src/participant/participantHeaderA11y.test.tsx +85 -0
- package/src/participant/participantHeaderDesign.test.tsx +111 -0
- package/src/participant/participantLabels.ts +11 -0
- package/src/participant/participantMessageAtoms.ts +53 -0
- package/src/participant/participantMessageCopyables.test.ts +141 -0
- package/src/participant/participantMessageCopyables.ts +76 -0
- package/src/participant/participantMessageFormat.test-helper.ts +21 -0
- package/src/participant/participantMessageFormat.test.ts +177 -0
- package/src/participant/participantMessageFormat.ts +135 -0
- package/src/participant/participantMessageFormat.types.ts +22 -0
- package/src/participant/participantMessageInline.test.ts +82 -0
- package/src/participant/participantMessageInline.ts +99 -0
- package/src/participant/participantMessageLinks.test.ts +100 -0
- package/src/participant/participantMessageLinks.ts +54 -0
- package/src/participant/participantMessageTextStyle.test.ts +137 -0
- package/src/participant/participantScrollFocus.test.ts +22 -0
- package/src/participant/participantScrollFocus.ts +10 -0
- package/src/participant/participantScrollView.test.ts +74 -0
- package/src/participant/participantScrollView.ts +38 -0
- package/src/participant/participantScrollbars.test.ts +156 -0
- package/src/participant/participantSubjectIcon.test.tsx +27 -0
- package/src/participant/participantSubjectIcon.ts +26 -0
- package/src/participant/useParticipantCopyFeedback.ts +33 -0
- package/src/participant/useParticipantThreadScroll.ts +10 -2
- package/src/participant/useRevealActiveChip.ts +20 -0
- package/src/participant/useScrollAwayState.ts +24 -0
- package/src/participant/useStickToBottom.ts +16 -2
- package/src/participantLayoutShrink.test.ts +82 -0
- package/src/styles.css +140 -21
- package/dist/chunk-MRVFFJEH.js +0 -64
- package/dist/chunk-OVFCLRQQ.js +0 -84
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Telas de conversa parametrizáveis por endpoint, tema e capacidades. Subpaths: `
|
|
|
5
5
|
|
|
6
6
|
## Visão do participante (`/participant`)
|
|
7
7
|
|
|
8
|
-
Tela de conversa do lado de **quem conversa com a empresa** (app
|
|
8
|
+
Tela de conversa do lado de **quem conversa com a empresa** (app de campo, portal do cliente). Não usa
|
|
9
9
|
`ConversationsProvider`, não puxa `@xyflow/react` nem o `ConversationsWorkspace`, e não depende de Tailwind:
|
|
10
10
|
todas as classes são `.cv-p-*` e vêm de `@adatechnology/conversations-ui/styles.css`.
|
|
11
11
|
|
|
@@ -157,9 +157,9 @@ pacote nunca gera nem conhece o formato, só **exibe, copia e busca**. Campo aus
|
|
|
157
157
|
|
|
158
158
|
- **Lista:** a linha mostra o protocolo abaixo do título (fonte mono); o prefixo `labels.protocolPrefix` é um `<span>` só para
|
|
159
159
|
leitor de tela (sem `aria-label` em elemento genérico, que o ARIA 1.2 proíbe).
|
|
160
|
-
- **Cabeçalho da conversa:**
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
- **Cabeçalho da conversa:** ver "Cabeçalho da conversa" abaixo. O botão de copiar usa `navigator.clipboard.writeText`;
|
|
161
|
+
falha de cópia é silenciosa. É **só um ícone** (`Copy`, vira `Check` por 3 s) com `aria-label` = "`copyProtocol` + código";
|
|
162
|
+
durante os 3 s aparece ao lado do ícone o texto curto `labels.protocolCopied` e o aviso também vai numa região
|
|
163
163
|
`aria-live="polite"` sempre na árvore; copiar de novo dentro dos 3 s reinicia o prazo e reanuncia.
|
|
164
164
|
- **Busca:** um campo `type="search"` acima da lista filtra por título (sem acento nem caixa) **ou** protocolo
|
|
165
165
|
(parcial, sem traço, sem caixa: `k7m2` acha `261009-K7M2`). Aparece só quando alguma conversa tem `protocol` ou
|
|
@@ -193,33 +193,140 @@ cv-p-channel--{canal}">`, um prefixo sr-only `Channels in this conversation: ` e
|
|
|
193
193
|
o leitor dizer "Channels in this conversation: App, WhatsApp".
|
|
194
194
|
- **`renderSubjectIcon`** deve ser **puro e não lançar** (o pacote não tem error boundary); `null`, `undefined`, `false` e
|
|
195
195
|
`''` valem como ausente.
|
|
196
|
-
- **Cabeçalho da conversa:** os mesmos selos
|
|
196
|
+
- **Cabeçalho da conversa:** os mesmos selos, na variante `inline` (ícone de 16px), na mesma linha do protocolo.
|
|
197
197
|
- **Labels novas** (defaults em inglês): `channelsGroup` ("Channels in this conversation"), `channelApp`,
|
|
198
198
|
`channelWhatsapp`, `channelEmail`, `channelPortal`, `channelWebchat`.
|
|
199
199
|
- **Classes novas:** `.cv-p-channels`, `.cv-p-channel`, `.cv-p-row__tags`, `.cv-p-inbox__icon`; cores opcionais
|
|
200
200
|
`--cv-p-channel-app` (padrão: o destaque) e `--cv-p-channel-whatsapp` (padrão `#20914f`, ≥ 3:1 sobre as superfícies claras e
|
|
201
201
|
escuras; ao trocar, mantenha 3:1).
|
|
202
202
|
|
|
203
|
+
### Conversa no desenho do WhatsApp, no nosso estilo
|
|
204
|
+
|
|
205
|
+
A conversa segue os padrões de chat do WhatsApp, mas só com os tokens `--cv-p-*` (por exemplo, em um tema escuro de cantos
|
|
206
|
+
retos, rótulos em mono). Tudo é `.cv-p-*`; nenhum componente do painel do operador é usado.
|
|
207
|
+
|
|
208
|
+
**Cabeçalho** (barra única com piso de 4rem (com título longo, protocolo longo e 3 canais a linha meta quebra e a barra chega a cerca de 110px), fundo `--cv-p-surface-raised`, borda inferior de 1px), da esquerda para a direita:
|
|
209
|
+
(1) **voltar** só com ícone (seta de 24px, sem caixa nem borda, área de toque 44x44, `aria-label` = label `back`, foco visível;
|
|
210
|
+
ausente sem `onBack`); (2) **avatar da conversa**: tile de 2.5rem (`border-radius: var(--cv-p-radius)`) com o ícone do assunto, o
|
|
211
|
+
mesmo da lista (`renderSubjectIcon` do host; se devolver vazio, o `icon` do grupo em `subjectGroups`; sem nenhum dos dois não há
|
|
212
|
+
tile e o título recua). o `renderSubjectIcon` e o `icon` do grupo, que já existiam, passam a desenhar também esse tile (o cabeçalho não tem o
|
|
213
|
+
fallback de duas letras da linha da lista); (3) **título em duas linhas**: o `subjectLabel` (uma linha com reticências, cortada só no `span` interno
|
|
214
|
+
`.cv-p-thread__title-text` para o anel de foco do botão não ser recortado; clicável só com `onOpenSubject`) e a **linha meta**
|
|
215
|
+
(`.cv-p-thread__meta`) com o protocolo, o botão de copiar só com ícone e os selos de canal inline. O rótulo do grupo (eyebrow) some quando há tile: o tile já representa o grupo e o espaço em 320px é curto. A área de toque do copiar tem 44px de
|
|
216
|
+
largura e 36px de altura; a margem negativa de cima é coberta pelo `padding-top` da linha meta, então nunca invade o título.
|
|
217
|
+
Sem `protocol` não há protocolo nem botão; sem `channels` não há selos; sem nenhum dos dois a linha meta não existe. O cartão de
|
|
218
|
+
`renderSubjectCard` (do host) deve trazer só o que o cabeçalho não diz.
|
|
219
|
+
|
|
220
|
+
**Fundo**: a lista de mensagens tem um padrão geométrico de pontos em CSS puro, derivado de `--cv-p-border`, de contraste
|
|
221
|
+
mínimo. `--cv-p-wallpaper` o substitui (qualquer valor de `background-image`, em geral um gradiente; `none` desliga),
|
|
222
|
+
com `--cv-p-wallpaper-size` para o tamanho do ladrilho (padrão `24px 24px`). Some em `forced-colors: active` e na impressão.
|
|
223
|
+
|
|
224
|
+
**Separador de dia**: pílula centralizada (fundo elevado, borda de 1px, texto mono pequeno, raio do token), `role="separator"`
|
|
225
|
+
com um `<time datetime="AAAA-MM-DD">` do dia local.
|
|
226
|
+
|
|
227
|
+
**Rolagem**: as áreas verticais (mensagens, lista de conversas e campo de texto) têm barra fina (`scrollbar-width: thin`, 6px
|
|
228
|
+
em WebKit, trilho transparente, sem setas), polegar `--cv-p-scrollbar-thumb` (padrão `--cv-p-border`) e `--cv-p-scrollbar-thumb-hover`
|
|
229
|
+
(padrão `--cv-p-accent`) ao passar o mouse ou arrastar, sem reservar espaço (`scrollbar-gutter`) para as bolhas não se mexerem, e
|
|
230
|
+
`overscroll-behavior-y: contain` para não encadear a rolagem na página. As faixas horizontais (filtros e respostas rápidas) rolam
|
|
231
|
+
sem barra, com esmaecimento nas bordas, `scroll-snap-type: x proximity` e o filtro ativo trazido à vista ao ser escolhido. Em
|
|
232
|
+
`forced-colors: active` voltam a barra do sistema e a faixa sem máscara. `scroll-behavior: smooth` só vale sob
|
|
233
|
+
`prefers-reduced-motion: no-preference` (lista e faixas; o scroller da conversa continua instantâneo, pois o posicionamento inicial e a
|
|
234
|
+
âncora ao carregar mensagens antigas atribuem `scrollTop`).
|
|
235
|
+
|
|
236
|
+
**Ir para a última mensagem**: com o leitor longe do fim da conversa (o mesmo limiar de `shouldStickToBottom`, 80px), um botão
|
|
237
|
+
flutuante de 44x44 com `ChevronDown` aparece no canto inferior do scroller; o `aria-label` é o label novo `scrollToLatest`
|
|
238
|
+
(padrão "Scroll to latest message"). Um selo mostra quantas mensagens do outro lado chegaram enquanto o leitor estava longe (limite
|
|
239
|
+
"99+"); ele é decorativo, e a região `aria-live` continua anunciando só o label `newMessages`. O clique rola ao fim (suave só sem
|
|
240
|
+
`prefers-reduced-motion: reduce`) e o botão some ao chegar. Nada é desenhado perto do fim ou sem mensagens.
|
|
241
|
+
|
|
242
|
+
**Direção do texto (RTL)**: margens, alinhamento e hora da bolha usam propriedades lógicas e espelham sozinhos; o **rabinho** do balão
|
|
243
|
+
(triângulos e canto reto) usa posições físicas e em RTL continua à esquerda/direita como em LTR (limitação conhecida).
|
|
244
|
+
|
|
245
|
+
**Bolha**: hora e ticks ficam na mesma bolha, juntos ao pé e à direita (`.cv-p-bubble__meta`); se o texto cabe com folga, a
|
|
246
|
+
hora fica na mesma linha dele, senão cai para a linha de baixo, à direita, sem sobrepor. Largura máxima de 85%.
|
|
247
|
+
|
|
248
|
+
**Compositor**: `[anexar] [campo] [enviar]` alinhados pela base, em barra elevada com borda superior; anexar e enviar são ícones
|
|
249
|
+
(clipe e avião de papel, com `aria-label`), o campo é multilinha, parte de 44px e cresce até cerca de 4 linhas (depois rola),
|
|
250
|
+
enviar é o botão primário e fica desabilitado sem conteúdo. As respostas rápidas continuam como chips acima da linha.
|
|
251
|
+
|
|
252
|
+
Classes novas: `.cv-p-thread__avatar`, `.cv-p-thread__eyebrow`, `.cv-p-thread__meta`, `.cv-p-protocol__copied`, `.cv-p-day__label`,
|
|
253
|
+
`.cv-p-composer__attach`, `.cv-p-composer__send`.
|
|
254
|
+
|
|
255
|
+
### Avatar do autor (opt-in)
|
|
256
|
+
|
|
257
|
+
**O SDK não tem foto de usuário**: o cadastro só traz `authorName`. O avatar é opt-in e o host fornece a foto pelo slot.
|
|
258
|
+
|
|
259
|
+
- `avatars="initials"` (em `ParticipantConversations`): desenha, à esquerda da bolha **recebida**, um quadrado de 32px
|
|
260
|
+
(`border-radius: var(--cv-p-radius)`, respeita tema reto) com até 2 iniciais do autor (primeiro e último nome, ignorando
|
|
261
|
+
preposições e pontuação); nome vazio, `?` ou só emoji cai no ícone neutro. Aparece só na **primeira** mensagem de uma
|
|
262
|
+
sequência do mesmo autor (as seguintes reservam o espaço para alinhar as bolhas); mensagem própria nunca tem avatar.
|
|
263
|
+
O avatar é `aria-hidden`: o nome do autor já está no texto da bolha.
|
|
264
|
+
- `renderAuthorAvatar?: (author: { name: string | null }) => ReactNode`: slot do host (uma `<img>`, por exemplo) desenhado
|
|
265
|
+
dentro do mesmo quadrado de 32px; tem precedência sobre as iniciais, liga o avatar sozinho e, se devolver `null`, `undefined`,
|
|
266
|
+
`false` ou `''`, cai nas iniciais. Deve ser puro e não lançar.
|
|
267
|
+
- Sem nenhuma das duas props o markup da bolha é idêntico ao anterior. Classes novas: `.cv-p-avatar`, `.cv-p-bubble-row`,
|
|
268
|
+
`.cv-p-bubble-row__avatar`. Nenhum label novo.
|
|
269
|
+
|
|
270
|
+
### Rabinho da bolha (`tail`)
|
|
271
|
+
|
|
272
|
+
A bolha da conversa é um balão de fala: um rabinho triangular de 9px cola no canto **inferior esquerdo** da bolha recebida e
|
|
273
|
+
no **inferior direito** da própria (convenção de chat). Ligado por padrão; `tail={false}` (em `ParticipantConversations`)
|
|
274
|
+
remove o rabinho e devolve o canto arredondado (`.cv-p-bubble--no-tail`).
|
|
275
|
+
|
|
276
|
+
- O rabo são dois triângulos de `border` (`::before` na cor da borda, `::after` na cor do fundo, 1px deslocado), então a
|
|
277
|
+
borda de 1px continua contínua. As cores saem dos mesmos tokens da bolha (`--cv-p-border`, `--cv-p-surface-raised`; na
|
|
278
|
+
própria, `--cv-p-accent` e `--cv-p-highlight`) e acompanham `.dark`.
|
|
279
|
+
- Funciona com tema reto (`--cv-p-radius: 0`) e arredondado: o canto inferior correspondente fica reto para o rabo emendar.
|
|
280
|
+
- É decorativo (`pointer-events: none`, pseudo-elemento): não muda área de toque, conteúdo nem altura mínima. Com avatar a
|
|
281
|
+
bolha recua 0.25rem para o rabo não encostar no quadrado. Sem animação.
|
|
282
|
+
- Em `forced-colors: active` e na impressão os triângulos somem (viram quadrados sólidos sem `border-color` transparente) e o
|
|
283
|
+
canto volta ao raio do token.
|
|
284
|
+
|
|
285
|
+
### Texto da mensagem (formatação, links e valores copiáveis)
|
|
286
|
+
|
|
287
|
+
A bolha usa um renderizador próprio da visão do participante (`ParticipantMessageText`); o `MessageText` e a formatação do painel e dos outros fluxos **não mudam**. O texto vira nós React (nunca `innerHTML`); acima de 8000 caracteres aparece como foi digitado.
|
|
288
|
+
|
|
289
|
+
| Digitado | Resultado |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `*negrito*`, `_itálico_`, `~riscado~`, `*_ambos_*` | `<strong>`, `<em>`, `<del>`, aninhados; o marcador cola no texto (`* a *`, `2 * 3 * 4`, `snake_case_nome` ficam como estão) |
|
|
292
|
+
| `` `código` `` e bloco entre três crases | `<code>` / `<pre><code>`; nada é formatado nem detectado dentro |
|
|
293
|
+
| `> citação` (linhas seguidas = um bloco) | `<blockquote>` |
|
|
294
|
+
| `- item`, `* item`, `• item` / `1. item`, `1) item` | `<ul>` / `<ol>` (exige o espaço depois do marcador) |
|
|
295
|
+
| `http(s)://…` | `<a>` com `rel="noopener noreferrer nofollow ugc"` e `target="_blank"`, mais um botão de copiar a URL ao lado; pontuação final fica fora |
|
|
296
|
+
| telefone BR/internacional, e-mail, CPF, CNPJ (numérico e alfanumérico), chave de acesso de 44 dígitos | botão que copia o valor como escrito |
|
|
297
|
+
|
|
298
|
+
**Allowlist de links:** só `http:` e `https:` são gerados no texto (a allowlist de `isAllowedLinkHref` aceita também `mailto:` e `tel:`, mas o renderizador nunca os produz a partir do texto digitado); `javascript:`, `data:`, `vbscript:`, qualquer outro protocolo e URL com usuário/senha (`https://user:pass@host`) ficam como texto. Em bloco de código, um rótulo de linguagem sozinho na primeira linha (` ```js `) é descartado.
|
|
299
|
+
|
|
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
|
+
|
|
302
|
+
### Limitações conhecidas
|
|
303
|
+
|
|
304
|
+
- O avatar alinha pelo topo da bolha, não pelo rabinho no canto inferior.
|
|
305
|
+
- Sem suporte a RTL: o rabinho e o avatar assumem escrita da esquerda para a direita.
|
|
306
|
+
|
|
203
307
|
### Variáveis `--cv-p-*`
|
|
204
308
|
|
|
205
309
|
Defina no `.cv-p`, no wrapper (`className`) ou em qualquer ancestral. Há também a prop `theme`
|
|
206
310
|
(`primaryColor` → `--cv-p-accent`, `backgroundColor` → `--cv-p-surface`, `textPrimary` → `--cv-p-text`,
|
|
207
311
|
`textSecondary` → `--cv-p-text-muted`).
|
|
208
312
|
|
|
209
|
-
| Variável | Para que serve
|
|
210
|
-
| ------------------------- |
|
|
211
|
-
| `--cv-p-surface` | fundo da tela
|
|
212
|
-
| `--cv-p-surface-raised` | fundo de bolhas recebidas e campos
|
|
213
|
-
| `--cv-p-text` | texto principal
|
|
214
|
-
| `--cv-p-text-muted` | texto secundário
|
|
215
|
-
| `--cv-p-border` | bordas
|
|
216
|
-
| `--cv-p-accent` | destaque, botão primário, bolha própria
|
|
217
|
-
| `--cv-p-accent-contrast` | texto sobre o destaque
|
|
218
|
-
| `--cv-p-highlight` | fundo de "espera sua resposta"
|
|
219
|
-
| `--cv-p-danger` | erro e falha de envio
|
|
220
|
-
| `--cv-p-radius` | raio dos cantos
|
|
221
|
-
| `--cv-p-
|
|
222
|
-
| `--cv-p-
|
|
313
|
+
| Variável | Para que serve | Padrão (claro) |
|
|
314
|
+
| ------------------------- | -------------------------------------------- | -------------- |
|
|
315
|
+
| `--cv-p-surface` | fundo da tela | `#ffffff` |
|
|
316
|
+
| `--cv-p-surface-raised` | fundo de bolhas recebidas e campos | `#f3f4f6` |
|
|
317
|
+
| `--cv-p-text` | texto principal | `#111827` |
|
|
318
|
+
| `--cv-p-text-muted` | texto secundário | `#5b6573` |
|
|
319
|
+
| `--cv-p-border` | bordas | `#d5d9df` |
|
|
320
|
+
| `--cv-p-accent` | destaque, botão primário, bolha própria | `#a85a1c` |
|
|
321
|
+
| `--cv-p-accent-contrast` | texto sobre o destaque | `#ffffff` |
|
|
322
|
+
| `--cv-p-highlight` | fundo de "espera sua resposta" | `#fbefe2` |
|
|
323
|
+
| `--cv-p-danger` | erro e falha de envio | `#c62828` |
|
|
324
|
+
| `--cv-p-radius` | raio dos cantos | `0` |
|
|
325
|
+
| `--cv-p-wallpaper` | fundo da lista de mensagens (`none` desliga) | pontos sutis |
|
|
326
|
+
| `--cv-p-scrollbar-thumb` | polegar das barras de rolagem finas | `--cv-p-border` |
|
|
327
|
+
| `--cv-p-scrollbar-thumb-hover` | polegar ao passar o mouse ou arrastar | `--cv-p-accent` |
|
|
328
|
+
| `--cv-p-channel-app` | cor do selo do canal app | o destaque |
|
|
329
|
+
| `--cv-p-channel-whatsapp` | cor do selo do canal WhatsApp | `#20914f` |
|
|
223
330
|
|
|
224
331
|
O tema escuro segue a classe `.dark` (mesma do restante do pacote). Os nomes `.cv-p-*` e `--cv-p-*` são API
|
|
225
332
|
pública e não mudam sem nova versão maior.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
cn,
|
|
3
|
+
parseWhatsAppFormatting,
|
|
3
4
|
useIsDarkTheme
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-PJNR6UMN.js";
|
|
5
6
|
import {
|
|
6
7
|
CONVERSATION_CHANNEL,
|
|
7
8
|
DEFAULT_CONVERSATION_CHANNEL,
|
|
@@ -13,9 +14,6 @@ import {
|
|
|
13
14
|
formatTimestamp,
|
|
14
15
|
isSameDay
|
|
15
16
|
} from "./chunk-O6457XHK.js";
|
|
16
|
-
import {
|
|
17
|
-
parseWhatsAppFormatting
|
|
18
|
-
} from "./chunk-CBYEHPX6.js";
|
|
19
17
|
|
|
20
18
|
// src/ConversationLocalesProvider.tsx
|
|
21
19
|
import { createContext, useContext } from "react";
|
|
@@ -217,10 +217,72 @@ function waToHTMLInline(text) {
|
|
|
217
217
|
return escapeHtml(text).replace(/\*([^*\n]+)\*/g, "<strong>$1</strong>").replace(/_([^_\n]+)_/g, "<em>$1</em>").replace(/~([^~\n]+)~/g, "<del>$1</del>").replace(/`([^`\n]+)`/g, "<code>$1</code>");
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
// src/useDarkMode.ts
|
|
221
|
+
import { useState, useEffect, useCallback } from "react";
|
|
222
|
+
var STORAGE_KEY = "conversations-ui-dark-mode";
|
|
223
|
+
function getInitialDark() {
|
|
224
|
+
if (typeof window === "undefined") return false;
|
|
225
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
226
|
+
if (stored !== null) {
|
|
227
|
+
return stored === "true";
|
|
228
|
+
}
|
|
229
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
230
|
+
}
|
|
231
|
+
function useIsDarkTheme() {
|
|
232
|
+
const [isDark, setIsDark] = useState(
|
|
233
|
+
() => typeof document !== "undefined" && document.documentElement.classList.contains("dark")
|
|
234
|
+
);
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
const root = document.documentElement;
|
|
237
|
+
const observer = new MutationObserver(() => setIsDark(root.classList.contains("dark")));
|
|
238
|
+
observer.observe(root, { attributes: true, attributeFilter: ["class"] });
|
|
239
|
+
setIsDark(root.classList.contains("dark"));
|
|
240
|
+
return () => observer.disconnect();
|
|
241
|
+
}, []);
|
|
242
|
+
return isDark;
|
|
243
|
+
}
|
|
244
|
+
function useDarkMode() {
|
|
245
|
+
const [isDark, setIsDark] = useState(getInitialDark);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
const root = document.documentElement;
|
|
248
|
+
if (isDark) {
|
|
249
|
+
root.classList.add("dark");
|
|
250
|
+
} else {
|
|
251
|
+
root.classList.remove("dark");
|
|
252
|
+
}
|
|
253
|
+
localStorage.setItem(STORAGE_KEY, String(isDark));
|
|
254
|
+
}, [isDark]);
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
257
|
+
const handleChange = (event) => {
|
|
258
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
259
|
+
if (stored === null) {
|
|
260
|
+
setIsDark(event.matches);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
264
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
265
|
+
}, []);
|
|
266
|
+
const toggle = useCallback(() => {
|
|
267
|
+
setIsDark((prev) => !prev);
|
|
268
|
+
}, []);
|
|
269
|
+
return { isDark, toggle };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/lib/cn.ts
|
|
273
|
+
import { clsx } from "clsx";
|
|
274
|
+
import { twMerge } from "tailwind-merge";
|
|
275
|
+
function cn(...inputs) {
|
|
276
|
+
return twMerge(clsx(inputs));
|
|
277
|
+
}
|
|
278
|
+
|
|
220
279
|
export {
|
|
280
|
+
cn,
|
|
221
281
|
parseWhatsAppFormatting,
|
|
222
282
|
ZERO_WIDTH_SPACE,
|
|
223
283
|
waToHTML,
|
|
224
284
|
htmlToWA,
|
|
225
|
-
waToHTMLInline
|
|
285
|
+
waToHTMLInline,
|
|
286
|
+
useIsDarkTheme,
|
|
287
|
+
useDarkMode
|
|
226
288
|
};
|
package/dist/flows/index.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TooltipLayer,
|
|
3
3
|
WhatsAppMessagePreview
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HMCOTZAX.js";
|
|
5
5
|
import {
|
|
6
|
+
parseWhatsAppFormatting,
|
|
6
7
|
useIsDarkTheme
|
|
7
|
-
} from "../chunk-
|
|
8
|
-
import {
|
|
9
|
-
parseWhatsAppFormatting
|
|
10
|
-
} from "../chunk-CBYEHPX6.js";
|
|
8
|
+
} from "../chunk-PJNR6UMN.js";
|
|
11
9
|
|
|
12
10
|
// src/flows/FlowNodeCard.tsx
|
|
13
11
|
import { useState } from "react";
|