@adatechnology/conversations-ui 0.1.0-rc.8 → 0.1.0-rc.9

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.
@@ -151,9 +151,63 @@ function waToHTMLInline(text) {
151
151
  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>");
152
152
  }
153
153
 
154
+ // src/useDarkMode.ts
155
+ import { useState, useEffect, useCallback } from "react";
156
+ var STORAGE_KEY = "conversations-ui-dark-mode";
157
+ function getInitialDark() {
158
+ if (typeof window === "undefined") return false;
159
+ const stored = localStorage.getItem(STORAGE_KEY);
160
+ if (stored !== null) {
161
+ return stored === "true";
162
+ }
163
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
164
+ }
165
+ function useIsDarkTheme() {
166
+ const [isDark, setIsDark] = useState(
167
+ () => typeof document !== "undefined" && document.documentElement.classList.contains("dark")
168
+ );
169
+ useEffect(() => {
170
+ const root = document.documentElement;
171
+ const observer = new MutationObserver(() => setIsDark(root.classList.contains("dark")));
172
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
173
+ setIsDark(root.classList.contains("dark"));
174
+ return () => observer.disconnect();
175
+ }, []);
176
+ return isDark;
177
+ }
178
+ function useDarkMode() {
179
+ const [isDark, setIsDark] = useState(getInitialDark);
180
+ useEffect(() => {
181
+ const root = document.documentElement;
182
+ if (isDark) {
183
+ root.classList.add("dark");
184
+ } else {
185
+ root.classList.remove("dark");
186
+ }
187
+ localStorage.setItem(STORAGE_KEY, String(isDark));
188
+ }, [isDark]);
189
+ useEffect(() => {
190
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
191
+ const handleChange = (event) => {
192
+ const stored = localStorage.getItem(STORAGE_KEY);
193
+ if (stored === null) {
194
+ setIsDark(event.matches);
195
+ }
196
+ };
197
+ mediaQuery.addEventListener("change", handleChange);
198
+ return () => mediaQuery.removeEventListener("change", handleChange);
199
+ }, []);
200
+ const toggle = useCallback(() => {
201
+ setIsDark((prev) => !prev);
202
+ }, []);
203
+ return { isDark, toggle };
204
+ }
205
+
154
206
  export {
155
207
  parseWhatsAppFormatting,
156
208
  waToHTML,
157
209
  htmlToWA,
158
- waToHTMLInline
210
+ waToHTMLInline,
211
+ useIsDarkTheme,
212
+ useDarkMode
159
213
  };
@@ -1,6 +1,7 @@
1
1
  import {
2
- parseWhatsAppFormatting
3
- } from "./chunk-OGRRHQQW.js";
2
+ parseWhatsAppFormatting,
3
+ useIsDarkTheme
4
+ } from "./chunk-2AYDBWNE.js";
4
5
 
5
6
  // src/ConversationLocalesProvider.tsx
6
7
  import { createContext, useContext } from "react";
@@ -616,8 +617,32 @@ function MessageBubble({
616
617
 
617
618
  // src/Wallpaper.tsx
618
619
  import { jsx as jsx10 } from "react/jsx-runtime";
619
- function ConversationWallpaper({ children, className }) {
620
- return /* @__PURE__ */ jsx10("div", { className: cn("cv-wallpaper", className), children });
620
+ function doodlePattern(strokeColor, opacity) {
621
+ const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><g fill='none' stroke='${strokeColor}' stroke-width='1.2' opacity='${opacity}'><circle cx='15' cy='15' r='3'/><path d='M40 10 q5 8 0 16 q-5 -8 0 -16z'/><circle cx='70' cy='28' r='2'/><path d='M18 55 l6 6 m-6 0 l6 -6'/><circle cx='55' cy='68' r='2.5'/><path d='M85 58 q6 6 0 12 q-6 -6 0 -12z'/><circle cx='8' cy='85' r='2'/><path d='M65 90 l5 5 m-5 0 l5 -5'/></g></svg>`;
622
+ return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
623
+ }
624
+ var LIGHT_WALLPAPER = {
625
+ backgroundColor: "#efeae2",
626
+ backgroundImage: doodlePattern("#d7cfc0", 0.7),
627
+ backgroundRepeat: "repeat",
628
+ backgroundSize: "100px 100px"
629
+ };
630
+ var DARK_WALLPAPER = {
631
+ backgroundColor: "#0b141a",
632
+ backgroundImage: doodlePattern("#19232a", 0.9),
633
+ backgroundRepeat: "repeat",
634
+ backgroundSize: "100px 100px"
635
+ };
636
+ function ConversationWallpaper({ children, className, style }) {
637
+ const isDark = useIsDarkTheme();
638
+ return /* @__PURE__ */ jsx10(
639
+ "div",
640
+ {
641
+ className: cn("cv-wallpaper", className),
642
+ style: { ...isDark ? DARK_WALLPAPER : LIGHT_WALLPAPER, ...style },
643
+ children
644
+ }
645
+ );
621
646
  }
622
647
 
623
648
  // src/emojiCatalog.ts
@@ -1,9 +1,7 @@
1
1
  import {
2
+ parseWhatsAppFormatting,
2
3
  useIsDarkTheme
3
- } from "../chunk-NV2RZ5KT.js";
4
- import {
5
- parseWhatsAppFormatting
6
- } from "../chunk-OGRRHQQW.js";
4
+ } from "../chunk-2AYDBWNE.js";
7
5
 
8
6
  // src/flows/FlowNodeCard.tsx
9
7
  import { Handle, Position } from "@xyflow/react";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import react__default, { ReactNode, FormEvent } from 'react';
2
+ import react__default, { ReactNode, CSSProperties, FormEvent } from 'react';
3
3
  import { y as MessagePayload, A as ResolveMediaUrl, v as InteractiveSelection, t as InteractivePayload, p as ConversationsFeatures, m as ConversationSummary, h as ConversationChannel, L as ListConversationsParams, o as ConversationsApi, S as SSEProvider, w as ListDocumentsParams, i as ConversationDocument, n as ConversationTemplate } from './types-B5C1DLu1.js';
4
4
  export { C as CHANNEL_CAPABILITIES, a as CHANNEL_FILTER_ALL, b as CONVERSATION_CHANNEL, c as ChannelCapabilities, d as ChannelFilter, e as ChannelFilterOption, f as CompanyDocument, g as CompanyDocumentPage, j as ConversationDocumentPage, k as ConversationEventSource, l as ConversationPage, q as ConversationsTheme, r as ConversationsUIConfig, D as DEFAULT_CONVERSATION_CHANNEL, F as FormatContactHandleParams, H as HANDLE_KIND, s as HandleKind, I as InteractiveOption, u as InteractiveSection, M as MediaRenderer, x as MediaRendererProps, R as REOPEN_MECHANISM, z as ReopenMechanism, B as capabilitiesOf, E as channelFiltersFor, G as contactFlag, J as formatContactHandle } from './types-B5C1DLu1.js';
5
5
 
@@ -41,8 +41,10 @@ declare function InteractiveMessage({ payload, onSelect, labels, className }: In
41
41
  interface ConversationWallpaperProps {
42
42
  children?: ReactNode;
43
43
  className?: string;
44
+ /** Ajusta ou substitui o fundo padrão — para produto com identidade visual própria. */
45
+ style?: CSSProperties;
44
46
  }
45
- declare function ConversationWallpaper({ children, className }: ConversationWallpaperProps): react.JSX.Element;
47
+ declare function ConversationWallpaper({ children, className, style }: ConversationWallpaperProps): react.JSX.Element;
46
48
 
47
49
  interface ConversationLocales {
48
50
  bubble: {
package/dist/index.js CHANGED
@@ -1,7 +1,3 @@
1
- import {
2
- useDarkMode,
3
- useIsDarkTheme
4
- } from "./chunk-NV2RZ5KT.js";
5
1
  import {
6
2
  AudioPlayer,
7
3
  ConversationDocumentsPanel,
@@ -43,13 +39,15 @@ import {
43
39
  useConversationDocuments,
44
40
  useConversationLocales,
45
41
  useConversations
46
- } from "./chunk-73MW5HNT.js";
42
+ } from "./chunk-G5BM3VBP.js";
47
43
  import {
48
44
  htmlToWA,
49
45
  parseWhatsAppFormatting,
46
+ useDarkMode,
47
+ useIsDarkTheme,
50
48
  waToHTML,
51
49
  waToHTMLInline
52
- } from "./chunk-OGRRHQQW.js";
50
+ } from "./chunk-2AYDBWNE.js";
53
51
 
54
52
  // src/WhatsAppMessageEditor.tsx
55
53
  import { useRef } from "react";
@@ -6,8 +6,8 @@ import {
6
6
  DocumentsLibrary,
7
7
  MessageBubble,
8
8
  MessageComposer
9
- } from "../chunk-73MW5HNT.js";
10
- import "../chunk-OGRRHQQW.js";
9
+ } from "../chunk-G5BM3VBP.js";
10
+ import "../chunk-2AYDBWNE.js";
11
11
 
12
12
  // src/preview/previewStore.ts
13
13
  var GLOBAL_CHANNEL = "global";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adatechnology/conversations-ui",
3
- "version": "0.1.0-rc.8",
3
+ "version": "0.1.0-rc.9",
4
4
  "description": "WhatsApp conversation UI components — parametrizável por endpoint, tema e feature flags",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { renderToStaticMarkup } from 'react-dom/server'
3
+
4
+ import { ConversationWallpaper } from './Wallpaper'
5
+
6
+ describe('ConversationWallpaper', () => {
7
+ it('desenha o fundo sozinho, sem depender do styles.css do host', () => {
8
+ const markup = renderToStaticMarkup(<ConversationWallpaper />)
9
+
10
+ expect(markup).toContain('background-color:#efeae2')
11
+ expect(markup).toContain('background-image:url(&quot;data:image/svg+xml,')
12
+ expect(markup).toContain('cv-wallpaper')
13
+ })
14
+
15
+ it('deixa o produto sobrescrever o fundo por style', () => {
16
+ const markup = renderToStaticMarkup(<ConversationWallpaper style={{ backgroundColor: '#123456' }} />)
17
+
18
+ expect(markup).toContain('background-color:#123456')
19
+ expect(markup).not.toContain('background-color:#efeae2')
20
+ })
21
+ })
package/src/Wallpaper.tsx CHANGED
@@ -1,15 +1,61 @@
1
- import type { ReactNode } from 'react'
1
+ /**
2
+ * Fundo da conversa — a textura clara/escura do WhatsApp.
3
+ *
4
+ * O fundo vem embutido, e não da folha de estilo do pacote: `styles.css` é um import opcional que o
5
+ * host precisa lembrar de fazer, e esquecê-lo deixava a conversa sobre branco liso, sem erro nenhum
6
+ * para denunciar o problema. Fundo é identidade do componente, não tema do host.
7
+ *
8
+ * A classe `cv-wallpaper` continua no elemento para quem já sobrescreve por CSS.
9
+ */
10
+
11
+ import type { CSSProperties, ReactNode } from 'react'
2
12
 
3
13
  import { cn } from './lib/cn'
14
+ import { useIsDarkTheme } from './useDarkMode'
15
+
16
+ /** Textura do WhatsApp: rabiscos esparsos que se repetem a cada 100px. */
17
+ function doodlePattern(strokeColor: string, opacity: number): string {
18
+ const svg =
19
+ `<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'>` +
20
+ `<g fill='none' stroke='${strokeColor}' stroke-width='1.2' opacity='${opacity}'>` +
21
+ `<circle cx='15' cy='15' r='3'/><path d='M40 10 q5 8 0 16 q-5 -8 0 -16z'/>` +
22
+ `<circle cx='70' cy='28' r='2'/><path d='M18 55 l6 6 m-6 0 l6 -6'/>` +
23
+ `<circle cx='55' cy='68' r='2.5'/><path d='M85 58 q6 6 0 12 q-6 -6 0 -12z'/>` +
24
+ `<circle cx='8' cy='85' r='2'/><path d='M65 90 l5 5 m-5 0 l5 -5'/>` +
25
+ `</g></svg>`
26
+ return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`
27
+ }
28
+
29
+ const LIGHT_WALLPAPER: CSSProperties = {
30
+ backgroundColor: '#efeae2',
31
+ backgroundImage: doodlePattern('#d7cfc0', 0.7),
32
+ backgroundRepeat: 'repeat',
33
+ backgroundSize: '100px 100px',
34
+ }
35
+
36
+ const DARK_WALLPAPER: CSSProperties = {
37
+ backgroundColor: '#0b141a',
38
+ backgroundImage: doodlePattern('#19232a', 0.9),
39
+ backgroundRepeat: 'repeat',
40
+ backgroundSize: '100px 100px',
41
+ }
4
42
 
5
43
  export interface ConversationWallpaperProps {
6
44
  children?: ReactNode
7
45
  className?: string
46
+ /** Ajusta ou substitui o fundo padrão — para produto com identidade visual própria. */
47
+ style?: CSSProperties
8
48
  }
9
49
 
10
- // Requer o import de '@adatechnology/conversations-ui/styles.css' uma vez no host —
11
- // a classe `.cv-wallpaper` (e sua variante `.dark`) vive nesse stylesheet, não em CSS-in-JS,
12
- // para não depender da configuração de Tailwind do consumidor.
13
- export function ConversationWallpaper({ children, className }: ConversationWallpaperProps) {
14
- return <div className={cn('cv-wallpaper', className)}>{children}</div>
50
+ export function ConversationWallpaper({ children, className, style }: ConversationWallpaperProps) {
51
+ const isDark = useIsDarkTheme()
52
+
53
+ return (
54
+ <div
55
+ className={cn('cv-wallpaper', className)}
56
+ style={{ ...(isDark ? DARK_WALLPAPER : LIGHT_WALLPAPER), ...style }}
57
+ >
58
+ {children}
59
+ </div>
60
+ )
15
61
  }
@@ -1,56 +0,0 @@
1
- // src/useDarkMode.ts
2
- import { useState, useEffect, useCallback } from "react";
3
- var STORAGE_KEY = "conversations-ui-dark-mode";
4
- function getInitialDark() {
5
- if (typeof window === "undefined") return false;
6
- const stored = localStorage.getItem(STORAGE_KEY);
7
- if (stored !== null) {
8
- return stored === "true";
9
- }
10
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
11
- }
12
- function useIsDarkTheme() {
13
- const [isDark, setIsDark] = useState(
14
- () => typeof document !== "undefined" && document.documentElement.classList.contains("dark")
15
- );
16
- useEffect(() => {
17
- const root = document.documentElement;
18
- const observer = new MutationObserver(() => setIsDark(root.classList.contains("dark")));
19
- observer.observe(root, { attributes: true, attributeFilter: ["class"] });
20
- setIsDark(root.classList.contains("dark"));
21
- return () => observer.disconnect();
22
- }, []);
23
- return isDark;
24
- }
25
- function useDarkMode() {
26
- const [isDark, setIsDark] = useState(getInitialDark);
27
- useEffect(() => {
28
- const root = document.documentElement;
29
- if (isDark) {
30
- root.classList.add("dark");
31
- } else {
32
- root.classList.remove("dark");
33
- }
34
- localStorage.setItem(STORAGE_KEY, String(isDark));
35
- }, [isDark]);
36
- useEffect(() => {
37
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
38
- const handleChange = (event) => {
39
- const stored = localStorage.getItem(STORAGE_KEY);
40
- if (stored === null) {
41
- setIsDark(event.matches);
42
- }
43
- };
44
- mediaQuery.addEventListener("change", handleChange);
45
- return () => mediaQuery.removeEventListener("change", handleChange);
46
- }, []);
47
- const toggle = useCallback(() => {
48
- setIsDark((prev) => !prev);
49
- }, []);
50
- return { isDark, toggle };
51
- }
52
-
53
- export {
54
- useIsDarkTheme,
55
- useDarkMode
56
- };