@better-zap/react 0.1.0 → 0.2.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 CHANGED
@@ -1,3 +1,531 @@
1
1
  # @better-zap/react
2
2
 
3
- React UI components for Better Zap conversations and message views.
3
+ React UI components for Better Zap conversations and message views. The package ships presentational compound primitives for chat layout plus a domain-aware adapter for existing consumers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @better-zap/react
9
+ # or
10
+ npm install @better-zap/react
11
+ # or
12
+ yarn add @better-zap/react
13
+ ```
14
+
15
+ | Requirement | Version |
16
+ | --- | --- |
17
+ | `react` (peer) | `^19.0.0` |
18
+ | `react-dom` (peer) | `^19.0.0` |
19
+ | Node.js | `>=20` |
20
+
21
+ ### Styling (Tailwind CSS v4)
22
+
23
+ Components are styled with Tailwind CSS utility classes; you need a **Tailwind CSS v4**
24
+ build pipeline in the consuming app. Add the package stylesheet to your global CSS:
25
+
26
+ ```css
27
+ @import "@better-zap/react/tailwind.css";
28
+ ```
29
+
30
+ That file is not compiled CSS — it is a Tailwind v4 *source manifest* (an `@source "."`
31
+ directive) that tells Tailwind's JIT scanner where to find the utility classes used
32
+ inside `@better-zap/react`'s compiled output, so they get included in your build.
33
+
34
+ **Without a Tailwind v4 build, the components still function but render unstyled
35
+ markup** (no colors, spacing, or layout classes take effect).
36
+
37
+ ## Quick start
38
+
39
+ A turnkey WhatsApp-style dashboard: a conversation list, a message pane with header
40
+ and message list, and a composer — wired together with `useState` for the selected
41
+ conversation.
42
+
43
+ ```tsx
44
+ "use client";
45
+
46
+ import { useState } from "react";
47
+ import {
48
+ ConversationList,
49
+ MessageInput,
50
+ MessageList,
51
+ MessageView,
52
+ MessageViewContent,
53
+ MessageViewHeader,
54
+ WhatsappDashboard,
55
+ } from "@better-zap/react";
56
+ import type { Conversation, UIMessage } from "@better-zap/react";
57
+
58
+ function Dashboard({
59
+ conversations,
60
+ messagesByConversation,
61
+ onSend,
62
+ }: {
63
+ conversations: Conversation[];
64
+ messagesByConversation: Record<string, UIMessage[]>;
65
+ onSend: (conversationId: string, text: string) => void;
66
+ }) {
67
+ const [selectedId, setSelectedId] = useState<string | null>(
68
+ conversations[0]?.id ?? null,
69
+ );
70
+ const selected = conversations.find((c) => c.id === selectedId) ?? null;
71
+ const messages = selected ? messagesByConversation[selected.id] ?? [] : [];
72
+
73
+ return (
74
+ <WhatsappDashboard>
75
+ <ConversationList
76
+ conversations={conversations}
77
+ selectedConversationId={selectedId}
78
+ onSelect={setSelectedId}
79
+ />
80
+ <MessageView>
81
+ <MessageViewHeader conversation={selected ?? undefined} />
82
+ <MessageViewContent>
83
+ <MessageList messages={messages} />
84
+ </MessageViewContent>
85
+ <MessageInput
86
+ onSend={(text) => selected && onSend(selected.id, text)}
87
+ conversation={selected}
88
+ />
89
+ </MessageView>
90
+ </WhatsappDashboard>
91
+ );
92
+ }
93
+ ```
94
+
95
+ `WhatsappDashboard` handles list/chat navigation on mobile viewports automatically;
96
+ `ConversationList`, `MessageView`, and `MessageInput` all work the same way whether
97
+ or not they're inside it (see the sections below).
98
+
99
+ ## Two layers
100
+
101
+ The package has two layers: generic presentational **primitives** with no Better Zap
102
+ dependency, and Better Zap **adapters** that map domain data onto those primitives.
103
+
104
+ | Concern | Primitives (`Bubble*`, `Message*` layout, `Composer*`, `DateDivider`) | Adapters (`MessageBubble`, `MessageInput`, `MessageList`, `ConversationList`, `MessageView*`, `WhatsappDashboard`) |
105
+ | --- | --- | --- |
106
+ | Data mapping (domain → presentation) | None — you pass `align`/`variant` directly | Own it — map `Conversation`/`UIMessage` fields (`sender`/`status`/`direction`) to presentational props |
107
+ | Row layout | `Message`, `MessageAvatar`, `MessageContent`, `MessageHeader`, `MessageFooter` | Composed internally by `MessageBubble` / `MessageList`'s default renderer |
108
+ | Bubble surface | `Bubble`, `BubbleContent`, `BubbleReactions` | Composed internally; still swappable via `renderMessage` (see [MessageList](#messagelist)) |
109
+ | Metadata / timestamps | You render them (`MessageFooter`, etc.) | Own default formatting (`formatDate`/`formatTime`), overridable via props |
110
+ | Actions | `ComposerButton`, `ComposerSend`, etc. — you wire callbacks | `MessageInput` gates action buttons on callbacks being provided (see [Actions and failures](#actions-and-failures)) |
111
+ | Responsive orchestration | None | `WhatsappDashboard` owns mobile list/chat navigation; `ConversationList`/`MessageView` read it optionally |
112
+ | Localization | Locale-free — you own all copy | pt-BR defaults (labels, `HOJE`/`ONTEM`, `HH:mm`), overridable via `labels`/`formatDate`/`formatTime` props |
113
+
114
+ See [Message vs Bubble](#message-vs-bubble) and [Composer vs MessageInput](#composer-vs-messageinput)
115
+ for the detailed anatomy of each pair.
116
+
117
+ ## Subpath imports
118
+
119
+ The root entry (`@better-zap/react`) re-exports the full public surface and is a **client** boundary (aggregates client modules). Prefer leaf subpaths when you only need a slice of the UI — especially server components that should not pull virtualization or icon deps:
120
+
121
+ | Import | Boundary | Notes |
122
+ | --- | --- | --- |
123
+ | `@better-zap/react` | client | Full barrel |
124
+ | `@better-zap/react/bubble` | server-safe | Presentational bubble primitives |
125
+ | `@better-zap/react/message` | server-safe | Row layout primitives |
126
+ | `@better-zap/react/message-bubble` | server-safe | Domain `MessageBubble` adapter |
127
+ | `@better-zap/react/utils` | server-safe | `cn`, `getDisplayDate`, `renderSlot` |
128
+ | `@better-zap/react/composer` | client | Draft/send orchestration |
129
+ | `@better-zap/react/message-input` | client | Domain input + freeform window |
130
+ | `@better-zap/react/message-view` | client | Chat pane + `MessageList` |
131
+ | `@better-zap/react/conversation-list` | client | Virtualized sidebar |
132
+ | `@better-zap/react/whatsapp-dashboard` | client | Layout provider |
133
+ | `@better-zap/react/tailwind.css` | asset | Stylesheet |
134
+
135
+ Published client entries lead with `"use client"` in both ESM and CJS. There are **no** wildcard exports (`@better-zap/react/*` is not a public surface).
136
+
137
+ ```tsx
138
+ // Server Component — no client graph / no LegendList
139
+ import { Bubble, BubbleContent } from "@better-zap/react/bubble";
140
+
141
+ // Client Component entry
142
+ import { Composer, ComposerTextarea, ComposerSend } from "@better-zap/react/composer";
143
+ ```
144
+
145
+ ## Message vs Bubble
146
+
147
+ **Message** owns the row layout: alignment (`start` | `end`), optional avatar, header/footer slots, and metadata placement around the bubble.
148
+
149
+ **Bubble** owns the visible chat surface: presentational `variant` (`default` | `primary` | `destructive` | `outline` | `muted`), corner `align`, content, and optional reactions.
150
+
151
+ ```
152
+ Message — row: alignment, spacing
153
+ ├── MessageAvatar — optional
154
+ └── MessageContent
155
+ ├── MessageHeader — optional
156
+ ├── Bubble — visible surface (variant + align)
157
+ │ ├── BubbleContent
158
+ │ └── BubbleReactions — optional
159
+ └── MessageFooter — optional
160
+ BubbleGroup / MessageGroup — consecutive-run stacking
161
+ ```
162
+
163
+ ### Minimal composition
164
+
165
+ ```tsx
166
+ import {
167
+ Message,
168
+ MessageAvatar,
169
+ MessageContent,
170
+ MessageHeader,
171
+ MessageFooter,
172
+ Bubble,
173
+ BubbleContent,
174
+ BubbleReactions,
175
+ } from "@better-zap/react";
176
+
177
+ function Example() {
178
+ return (
179
+ <Message align="end">
180
+ <MessageAvatar>B</MessageAvatar>
181
+ <MessageContent>
182
+ <MessageHeader>Bot</MessageHeader>
183
+ <Bubble variant="primary" align="end">
184
+ <BubbleContent>Hello</BubbleContent>
185
+ <BubbleReactions>👍</BubbleReactions>
186
+ </Bubble>
187
+ <MessageFooter>12:34</MessageFooter>
188
+ </MessageContent>
189
+ </Message>
190
+ );
191
+ }
192
+ ```
193
+
194
+ ### Adapter: `MessageBubble`
195
+
196
+ `MessageBubble` remains the Better Zap-aware compatibility component. It maps domain `sender` / `status` to presentational `align` / `variant`:
197
+
198
+ | Domain | Presentational |
199
+ | --- | --- |
200
+ | `sender="user"` | `align="start"`, `variant="default"` |
201
+ | `sender="bot"` (ok) | `align="end"`, `variant="primary"` |
202
+ | `status="failed"` | `align="end"`, `variant="destructive"` |
203
+
204
+ Prefer composing `Message` + `Bubble` for custom metadata placement, grouping, or interactive surfaces.
205
+
206
+ ### Notes
207
+
208
+ - `BubbleGroup` / `MessageGroup` only stack children — they do **not** auto-adjust corner rounding.
209
+ - Interactive bubbles use `BubbleContent`'s `render` prop (Base UI-style element polymorphism), not `asChild`.
210
+ - Composition model follows shadcn-style compound parts: children + variants, no required React context between Message and Bubble.
211
+
212
+ ## Composer vs MessageInput
213
+
214
+ **Composer** owns draft state and send orchestration via React context (`useComposer`).
215
+ Parts: `Composer`, `ComposerTextarea`, `ComposerSend`, `ComposerButton`, `ComposerError`.
216
+
217
+ **MessageInput** is the Better Zap domain adapter: freeform 24h window gating
218
+ (`useFreeformMessageWindow`), default pt-BR labels, optional action callbacks, and
219
+ the closed-window banner. Prefer `Composer*` when you need custom chrome or
220
+ controlled multi-conversation drafts.
221
+
222
+ ```
223
+ MessageInput — domain adapter (window + labels + optional actions)
224
+ └── Composer — draft + send orchestration (context)
225
+ ├── ComposerButton* — emoji/attach/mic only if callbacks provided
226
+ ├── ComposerTextarea
227
+ ├── ComposerSend
228
+ └── ComposerError
229
+ ```
230
+
231
+ ### Controlled multi-conversation drafts
232
+
233
+ Parent owns the draft map; `Composer` is controlled when `value !== undefined`:
234
+
235
+ ```tsx
236
+ const [activeId, setActiveId] = useState(conversationId);
237
+ const [drafts, setDrafts] = useState<Record<string, string>>({});
238
+
239
+ <Composer
240
+ value={drafts[activeId] ?? ""}
241
+ onValueChange={(next) =>
242
+ setDrafts((prev) => ({ ...prev, [activeId]: next }))
243
+ }
244
+ onSubmit={handleSend}
245
+ >
246
+ <ComposerTextarea aria-label="Mensagem" />
247
+ <ComposerSend aria-label="Enviar" />
248
+ </Composer>
249
+ ```
250
+
251
+ ### Actions and failures
252
+
253
+ - Emoji / attach / mic on `MessageInput` render **only** when `onEmojiClick` /
254
+ `onAttachClick` / `onMicClick` are provided. Without callbacks they are omitted
255
+ (deliberate — no enabled inert controls).
256
+ - `onSubmit` / `onSend` may throw or reject; the draft is preserved, controls restore,
257
+ and `onError` / `onSendError` run. `send()` is fire-and-forget and never surfaces a
258
+ rejecting promise to click/keydown handlers.
259
+ - Freeform window: `useFreeformMessageWindow` schedules a timer at `expiresAt` and
260
+ `MessageInput` revalidates immediately before calling `onSend`.
261
+
262
+ ### Client boundary
263
+
264
+ Published client entries (`composer`, `message-input`, and the root barrel)
265
+ lead with `"use client"` in both ESM and CJS. Prefer `@better-zap/react/composer`
266
+ or `@better-zap/react/message-input` when you want an explicit client boundary
267
+ without the full dashboard graph.
268
+
269
+ ## ConversationList
270
+
271
+ **ConversationList** is a virtualized conversation sidebar with search, unread
272
+ filter chips, and row chrome. It works **standalone** or inside
273
+ `WhatsappDashboard`.
274
+
275
+ ### Standalone vs dashboard
276
+
277
+ Outside a provider the list is always visible and selection only calls
278
+ `onSelect`. Inside `WhatsappDashboard`, selecting a row also sets mobile view to
279
+ `"chat"` (list hides on small viewports).
280
+
281
+ ```tsx
282
+ // Standalone — no provider required
283
+ <ConversationList
284
+ conversations={conversations}
285
+ selectedConversationId={activeId}
286
+ onSelect={setActiveId}
287
+ />
288
+
289
+ // Dashboard layout
290
+ <WhatsappDashboard>
291
+ <ConversationList conversations={...} onSelect={...} />
292
+ <MessageView>...</MessageView>
293
+ </WhatsappDashboard>
294
+ ```
295
+
296
+ `useOptionalWhatsappDashboard()` returns the context value or `null` outside the
297
+ provider. Prefer `useWhatsappDashboard()` only when absence should throw.
298
+
299
+ ### Controlled search and filter
300
+
301
+ Search/filter are controlled when `search` / `filter` are `!== undefined`
302
+ (same convention as Composer):
303
+
304
+ ```tsx
305
+ const [search, setSearch] = useState("");
306
+ const [filter, setFilter] = useState<"all" | "unread">("all");
307
+
308
+ <ConversationList
309
+ conversations={conversations}
310
+ search={search}
311
+ onSearchChange={setSearch}
312
+ filter={filter}
313
+ onFilterChange={setFilter}
314
+ />
315
+ ```
316
+
317
+ Uncontrolled defaults: `defaultSearch=""`, `defaultFilter="all"`. Change
318
+ callbacks still fire when uncontrolled if provided.
319
+
320
+ ### Render seams and labels
321
+
322
+ - `renderItem(conversation, { isSelected, select })` — replace the whole row
323
+ (wins over `renderAvatar`).
324
+ - `renderAvatar(conversation)` — swap the default avatar inside
325
+ `ConversationItem`.
326
+ - `labels` — partial overrides for search, chips, loading/error/empty, preview
327
+ prefix, and `"Ontem"`.
328
+ - `formatTime(isoDate)` — optional time label override for rows.
329
+
330
+ ## MessageList
331
+
332
+ **MessageList** is a virtualized, date-grouped message scroller (LegendList).
333
+ It works inside `MessageViewContent` (scroll context) or **standalone** with
334
+ direct `autoScroll` / `onScrollTop` props. Direct props win over context.
335
+
336
+ ### Default use
337
+
338
+ ```tsx
339
+ import { MessageList } from "@better-zap/react";
340
+
341
+ <MessageList
342
+ messages={messages}
343
+ renderMessageLabel={(m) =>
344
+ m.direction === "outgoing" ? "Assistente" : undefined
345
+ }
346
+ />
347
+ ```
348
+
349
+ Default rendering uses `MessageBubble`, `getDisplayDate` (pt-BR HOJE/ONTEM),
350
+ and pt-BR `HH:mm` timestamps. Default appearance does **not** change corners
351
+ or spacing based on `groupPosition` — that field is for custom renderers.
352
+
353
+ ### Custom rich-message renderer
354
+
355
+ Inject `renderMessage` to compose public `Message` + `Bubble` primitives.
356
+ `MessageRenderContext` exposes stable `id`, `direction`, presentation `align`
357
+ (`incoming` → `start`, `outgoing` → `end`), neighbor-derived `groupPosition`
358
+ (`single` | `first` | `middle` | `last`), and optional `label`.
359
+
360
+ Grouping: two adjacent messages share a group when `formatDate(a.sentAt) ===
361
+ formatDate(b.sentAt)` **and** `a.direction === b.direction`. Date boundaries
362
+ and direction changes start a new group.
363
+
364
+ ```tsx
365
+ import {
366
+ MessageList,
367
+ Message,
368
+ MessageContent,
369
+ MessageFooter,
370
+ Bubble,
371
+ BubbleContent,
372
+ } from "@better-zap/react";
373
+
374
+ <MessageList
375
+ messages={messages}
376
+ renderMessage={({ message, align, groupPosition, label }) => (
377
+ <Message align={align} data-group={groupPosition}>
378
+ <MessageContent>
379
+ {label ? <span>{label}</span> : null}
380
+ <Bubble
381
+ variant={align === "end" ? "primary" : "default"}
382
+ align={align}
383
+ >
384
+ <BubbleContent>{message.content}</BubbleContent>
385
+ </Bubble>
386
+ <MessageFooter>{/* your time / status */}</MessageFooter>
387
+ </MessageContent>
388
+ </Message>
389
+ )}
390
+ />
391
+ ```
392
+
393
+ ### Custom date chrome
394
+
395
+ ```tsx
396
+ import { MessageList, DateDivider } from "@better-zap/react";
397
+
398
+ <MessageList
399
+ messages={messages}
400
+ formatDate={(iso) => new Date(iso).toLocaleDateString("en-US")}
401
+ renderDateDivider={({ label, date }) => (
402
+ <DateDivider data-raw={date}>{label}</DateDivider>
403
+ )}
404
+ />
405
+ ```
406
+
407
+ `DateDivider` is a public presentational pill (`children` + standard `div`
408
+ props). Date row ids are `date:${label}` for the first occurrence of a label
409
+ in the list walk; later duplicates use `date:${label}:${occurrence}` (1-based
410
+ after the first) so malformed timestamps that both format to
411
+ `"Invalid Date"` stay unique. Occurrence suffixes may shift when history
412
+ prepends *duplicate-label* groups (accepted).
413
+
414
+ ### Standalone scroll props
415
+
416
+ ```tsx
417
+ <MessageList
418
+ messages={messages}
419
+ autoScroll={false}
420
+ onScrollTop={() => loadOlder()}
421
+ />
422
+ ```
423
+
424
+ Also available: `formatTime(iso)` for the **default** bubble timestamp only
425
+ (custom `renderMessage` owns its own time formatting).
426
+
427
+ ## MessageView
428
+
429
+ **MessageView** is the chat pane shell (background, empty state, header, content).
430
+ It works **standalone** or inside `WhatsappDashboard`. Leaves use
431
+ `useOptionalWhatsappDashboard()` — no provider required.
432
+
433
+ ### Standalone
434
+
435
+ ```tsx
436
+ // No provider — always visible (desktop semantics)
437
+ <MessageView>
438
+ <MessageViewHeader conversation={active} onInfoClick={openInfo} />
439
+ <MessageViewContent>
440
+ <MessageList messages={messages} />
441
+ </MessageViewContent>
442
+ </MessageView>
443
+ ```
444
+
445
+ Outside a provider, empty `MessageView` (no children) renders the default
446
+ pt-BR "Better Zap" empty state. Inside a mobile dashboard with no children it
447
+ returns `null` so the list can fill the viewport.
448
+
449
+ ### Header composition
450
+
451
+ - `conversation` is optional; omit or pass `children` to replace the identity
452
+ block (name + phone).
453
+ - Back button: `showBackButton ?? ctx?.isMobile ?? false`. Click calls
454
+ `ctx?.setMobileView("list")` then `onBack`.
455
+ - Info button renders **only** when `onInfoClick` is provided (no inert
456
+ controls). `actions` replaces the default info slot entirely.
457
+ - `labels` partial overrides for back/info `aria-label` defaults
458
+ (`"Voltar"` / `"Informações"`).
459
+
460
+ ```tsx
461
+ <MessageViewHeader
462
+ conversation={active}
463
+ showBackButton
464
+ onBack={() => setView("list")}
465
+ onInfoClick={openInfo}
466
+ labels={{ back: "Back", info: "Info" }}
467
+ actions={<button type="button">More</button>}
468
+ />
469
+ ```
470
+
471
+ ### Empty content seam
472
+
473
+ `MessageViewEmpty` accepts optional `children` to replace the default icon +
474
+ copy. Used automatically by empty `MessageView` on desktop / standalone.
475
+
476
+ ### Style merge
477
+
478
+ Consumer `style` composes with internal visibility. When the pane is hidden on
479
+ mobile (`mobileView !== "chat"`), internal `display: "none"` is applied **after**
480
+ consumer styles so a consumer `display` cannot reveal a hidden pane.
481
+
482
+ ## WhatsappDashboard
483
+
484
+ Layout provider for list/chat mobile navigation. Context value is memoized.
485
+
486
+ ```tsx
487
+ // Uncontrolled
488
+ <WhatsappDashboard defaultMobileView="list">
489
+ <ConversationList ... />
490
+ <MessageView>...</MessageView>
491
+ </WhatsappDashboard>
492
+
493
+ // Controlled navigation + app-owned breakpoint
494
+ const [mobileView, setMobileView] = useState<"list" | "chat">("list");
495
+
496
+ <WhatsappDashboard
497
+ isMobile={isNarrow}
498
+ mobileView={mobileView}
499
+ onMobileViewChange={setMobileView}
500
+ >
501
+ ...
502
+ </WhatsappDashboard>
503
+ ```
504
+
505
+ - Controlled when `mobileView !== undefined` (local state not written).
506
+ - Uncontrolled uses `defaultMobileView` (`"list"`). `onMobileViewChange` still
507
+ fires when the view changes if provided.
508
+ - When `isMobile` is set, `matchMedia` is not attached; the prop value is used.
509
+
510
+ ## Migration from the monolithic API
511
+
512
+ All high-level components you may already be using are unchanged in name and
513
+ remain public — nothing described below removes an export.
514
+
515
+ | Legacy usage | Now | Notes |
516
+ | --- | --- | --- |
517
+ | `MessageBubble` | retained — compatibility adapter over `Message` + `Bubble` | compose primitives directly for custom metadata placement or grouping (see [Message vs Bubble](#message-vs-bubble)) |
518
+ | `MessageInput` | retained — failure-safe adapter over `Composer*` + `useFreeformMessageWindow` | inert action buttons (emoji/attach/mic) no longer render without callbacks (see [Actions and failures](#actions-and-failures)) |
519
+ | `MessageList` | retained — now extensible (`renderMessage`, `renderDateDivider`, grouping context, `formatDate`/`formatTime`) | default rendering is unchanged if you pass no new props |
520
+ | `ConversationList` | retained — standalone-capable, controlled search/filter, `renderItem`/`renderAvatar`/`labels` | no provider required (see [Standalone vs dashboard](#standalone-vs-dashboard)) |
521
+ | `MessageView` (+ header/content/empty) | retained — context-optional | standalone renders desktop semantics, no provider required (see [MessageView](#messageview)) |
522
+ | `WhatsappDashboard` | retained — controllable (`mobileView`/`onMobileViewChange`/`isMobile`) | uncontrolled default behavior is unchanged |
523
+
524
+ ### Versioning
525
+
526
+ `@better-zap/react` is `0.x`; per SemVer, minor releases in this range may
527
+ include breaking changes. This wave, however, removes **no** high-level
528
+ export: `MessageBubble`, `MessageInput`, `MessageList`, `ConversationList`,
529
+ `MessageView`, and `WhatsappDashboard` all remain public. The composable
530
+ primitives (`Bubble*`, `Message*` layout parts, `Composer*`, `DateDivider`)
531
+ are additive — they sit alongside the adapters, not in place of them.
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@better-zap/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "React components for Better Zap.",
5
5
  "license": "ISC",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Dosbodoke/better-zap",
9
+ "directory": "packages/react"
10
+ },
6
11
  "type": "module",
7
12
  "main": "./dist/index.cjs",
8
13
  "module": "./dist/index.mjs",
@@ -18,6 +23,51 @@
18
23
  "import": "./dist/index.mjs",
19
24
  "require": "./dist/index.cjs"
20
25
  },
26
+ "./bubble": {
27
+ "types": "./dist/bubble.d.mts",
28
+ "import": "./dist/bubble.mjs",
29
+ "require": "./dist/bubble.cjs"
30
+ },
31
+ "./message": {
32
+ "types": "./dist/message.d.mts",
33
+ "import": "./dist/message.mjs",
34
+ "require": "./dist/message.cjs"
35
+ },
36
+ "./message-bubble": {
37
+ "types": "./dist/message-bubble.d.mts",
38
+ "import": "./dist/message-bubble.mjs",
39
+ "require": "./dist/message-bubble.cjs"
40
+ },
41
+ "./composer": {
42
+ "types": "./dist/composer.d.mts",
43
+ "import": "./dist/composer.mjs",
44
+ "require": "./dist/composer.cjs"
45
+ },
46
+ "./message-input": {
47
+ "types": "./dist/message-input.d.mts",
48
+ "import": "./dist/message-input.mjs",
49
+ "require": "./dist/message-input.cjs"
50
+ },
51
+ "./message-view": {
52
+ "types": "./dist/message-view.d.mts",
53
+ "import": "./dist/message-view.mjs",
54
+ "require": "./dist/message-view.cjs"
55
+ },
56
+ "./conversation-list": {
57
+ "types": "./dist/conversation-list.d.mts",
58
+ "import": "./dist/conversation-list.mjs",
59
+ "require": "./dist/conversation-list.cjs"
60
+ },
61
+ "./whatsapp-dashboard": {
62
+ "types": "./dist/whatsapp-dashboard.d.mts",
63
+ "import": "./dist/whatsapp-dashboard.mjs",
64
+ "require": "./dist/whatsapp-dashboard.cjs"
65
+ },
66
+ "./utils": {
67
+ "types": "./dist/utils.d.mts",
68
+ "import": "./dist/utils.mjs",
69
+ "require": "./dist/utils.cjs"
70
+ },
21
71
  "./tailwind.css": "./dist/tailwind.css",
22
72
  "./package.json": "./package.json"
23
73
  },
@@ -37,15 +87,18 @@
37
87
  "class-variance-authority": "^0.7.1",
38
88
  "clsx": "^2.1.1",
39
89
  "tailwind-merge": "^3.0.0",
40
- "better-zap": "0.1.0"
90
+ "better-zap": "0.2.0"
41
91
  },
42
92
  "peerDependencies": {
43
93
  "react": "^19.0.0",
44
94
  "react-dom": "^19.0.0"
45
95
  },
46
96
  "devDependencies": {
97
+ "@testing-library/react": "^16.3.2",
98
+ "@testing-library/user-event": "^14.6.1",
47
99
  "@types/react": "^19.0.0",
48
100
  "@types/react-dom": "^19.0.0",
101
+ "jsdom": "^29.1.1",
49
102
  "tsdown": "^0.21.2",
50
103
  "typescript": "^6.0.2",
51
104
  "vitest": "^4.1.0"
@@ -53,6 +106,6 @@
53
106
  "scripts": {
54
107
  "build": "tsdown",
55
108
  "typecheck": "tsc --noEmit",
56
- "test": "vitest run --passWithNoTests"
109
+ "test": "pnpm run build && vitest run"
57
110
  }
58
111
  }