@mdxui/zero 6.0.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 ADDED
@@ -0,0 +1,205 @@
1
+ # @mdxui/zero
2
+
3
+ AI-powered email client components for mdxui. Provides a complete email application UI including mail lists, thread display, composition, and dashboard layouts.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @mdxui/zero
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { MailZeroPage } from '@mdxui/zero'
15
+
16
+ function App() {
17
+ return (
18
+ <MailZeroPage
19
+ initialThreads={threads}
20
+ initialFolders={folders}
21
+ initialLabels={labels}
22
+ user={{ name: 'John', email: 'john@example.com.ai' }}
23
+ />
24
+ )
25
+ }
26
+ ```
27
+
28
+ ## Package Structure
29
+
30
+ ```
31
+ @mdxui/zero
32
+ ├── /mail # Core mail components
33
+ │ ├── MailList # Thread list with selection
34
+ │ ├── MailItem # Individual thread item
35
+ │ ├── ThreadDisplay # Full thread view
36
+ │ └── MessageView # Single message display
37
+ ├── /compose # Email composition
38
+ │ └── EmailComposer # Full compose UI with toolbar
39
+ ├── /dashboard # Layout components
40
+ │ ├── MailShell # Three-panel resizable layout
41
+ │ └── MailSidebar # Folder/label navigation
42
+ ├── /landing # Landing page components
43
+ └── /pages # Complete page compositions
44
+ └── MailZeroPage # Full email app with state
45
+ ```
46
+
47
+ ## Components
48
+
49
+ ### MailZeroPage
50
+
51
+ Complete email application with internal state management. Use this for a full email experience.
52
+
53
+ ```tsx
54
+ import { MailZeroPage } from '@mdxui/zero'
55
+
56
+ <MailZeroPage
57
+ initialThreads={threads}
58
+ initialFolders={folders}
59
+ initialLabels={labels}
60
+ initialFolderId="inbox"
61
+ user={{ name: 'John Doe', email: 'john@example.com.ai' }}
62
+ />
63
+ ```
64
+
65
+ All interactions (reply, archive, delete, etc.) are handled internally. Check the browser console for action logs.
66
+
67
+ ### MailShell
68
+
69
+ Three-panel resizable layout combining sidebar, mail list, and thread display. Use when you need custom state management.
70
+
71
+ ```tsx
72
+ import { MailShell } from '@mdxui/zero/dashboard'
73
+
74
+ <MailShell
75
+ // Sidebar props
76
+ folders={folders}
77
+ activeFolderId="inbox"
78
+ onFolderClick={(folderId) => setActiveFolder(folderId)}
79
+
80
+ // List props
81
+ threads={threads}
82
+ onThreadClick={(thread) => setActiveThread(thread)}
83
+
84
+ // Thread display props
85
+ activeThread={activeThread}
86
+ onReply={(messageId) => openComposer({ replyTo: messageId })}
87
+ onArchive={() => archiveThread(activeThread.id)}
88
+ />
89
+ ```
90
+
91
+ ### MailList
92
+
93
+ Thread list with selection, virtualization, and keyboard navigation.
94
+
95
+ ```tsx
96
+ import { MailList } from '@mdxui/zero/mail'
97
+
98
+ <MailList
99
+ threads={threads}
100
+ selectedIds={selectedIds}
101
+ activeId={activeId}
102
+ selectionMode="multiple"
103
+ displayMode="comfortable"
104
+ onThreadClick={(thread) => setActiveThread(thread)}
105
+ onSelectionChange={(ids) => setSelectedIds(ids)}
106
+ />
107
+ ```
108
+
109
+ ### ThreadDisplay
110
+
111
+ Full thread view with message list and actions.
112
+
113
+ ```tsx
114
+ import { ThreadDisplay } from '@mdxui/zero/mail'
115
+
116
+ <ThreadDisplay
117
+ thread={thread}
118
+ onReply={(messageId) => openComposer({ replyTo: messageId })}
119
+ onReplyAll={(messageId) => openComposer({ replyAllTo: messageId })}
120
+ onForward={(messageId) => openComposer({ forward: messageId })}
121
+ onSnooze={(isoDate) => snoozeThread(thread.id, isoDate)}
122
+ onMove={(folderId) => moveThread(thread.id, folderId)}
123
+ onLabel={(labelIds) => labelThread(thread.id, labelIds)}
124
+ />
125
+ ```
126
+
127
+ ### EmailComposer
128
+
129
+ Rich email composition with toolbar, recipient input, and AI assistance.
130
+
131
+ ```tsx
132
+ import { EmailComposer } from '@mdxui/zero/compose'
133
+
134
+ <EmailComposer
135
+ mode="compose"
136
+ initialDraft={{ to: [], subject: '', body: '' }}
137
+ onSend={(draft) => sendEmail(draft)}
138
+ onSaveDraft={(draft) => saveDraft(draft)}
139
+ onDiscard={() => closeComposer()}
140
+ />
141
+ ```
142
+
143
+ ## Callback Signatures
144
+
145
+ All callbacks receive appropriate context:
146
+
147
+ | Callback | Signature | Context |
148
+ |----------|-----------|---------|
149
+ | `onReply` | `(messageId: string) => void` | Reply to specific message |
150
+ | `onReplyAll` | `(messageId: string) => void` | Reply-all to specific message |
151
+ | `onForward` | `(messageId: string) => void` | Forward specific message |
152
+ | `onSnooze` | `(isoDate: string) => void` | Snooze until date |
153
+ | `onMove` | `(folderId: string) => void` | Move to folder |
154
+ | `onLabel` | `(labelIds: string[]) => void` | Toggle labels |
155
+ | `onThreadClick` | `(thread: Thread) => void` | Thread object |
156
+ | `onFolderClick` | `(folderId: string) => void` | Folder ID |
157
+
158
+ Thread-level actions (archive, delete, spam, star, print) are void callbacks - the parent tracks the active thread.
159
+
160
+ ## Types
161
+
162
+ Types are defined in `mdxui` package:
163
+
164
+ ```tsx
165
+ import type {
166
+ MailThread as Thread,
167
+ MailMessage as Message,
168
+ Folder,
169
+ MailLabel as Label,
170
+ MailShellProps,
171
+ ThreadDisplayProps,
172
+ MailListProps,
173
+ } from 'mdxui'
174
+ ```
175
+
176
+ ## Development
177
+
178
+ ```bash
179
+ # Type check
180
+ pnpm --filter @mdxui/zero typecheck
181
+
182
+ # Run Storybook
183
+ pnpm storybook
184
+
185
+ # Visual regression tests (requires Storybook running)
186
+ pnpm --filter @mdxui/zero test:visual
187
+
188
+ # Update visual baselines
189
+ pnpm --filter @mdxui/zero test:visual:update
190
+ ```
191
+
192
+ ## Current Limitations
193
+
194
+ This package currently provides **UI components only**. It lacks the full app infrastructure found in [Mail-0/Zero](https://github.com/Mail-0/Zero):
195
+
196
+ - No react-router integration
197
+ - No state management (jotai/react-query)
198
+ - No data layer / API integration
199
+ - No proper page routing
200
+
201
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for the target architecture and implementation plan.
202
+
203
+ ## Credits
204
+
205
+ Components ported from [Mail-0/Zero](https://github.com/Mail-0/Zero), an AI-powered email client.
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@mdxui/zero",
3
+ "version": "6.0.0",
4
+ "description": "Zero Email components for mdxui - AI-powered email client UI",
5
+ "sideEffects": false,
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./mail": "./src/mail/index.ts",
10
+ "./compose": "./src/compose/index.ts",
11
+ "./landing": "./src/landing/index.ts",
12
+ "./dashboard": "./src/dashboard/index.ts",
13
+ "./components": "./src/components/index.ts"
14
+ },
15
+ "devDependencies": {
16
+ "@playwright/test": "^1.49.0",
17
+ "@types/react": "^19.2.7",
18
+ "@types/react-dom": "^19.2.3",
19
+ "typescript": "5.9.3",
20
+ "@mdxui/typescript-config": "6.0.0"
21
+ },
22
+ "dependencies": {
23
+ "lucide-react": "^0.561.0",
24
+ "motion": "^12.23.26",
25
+ "react": "^19.2.3",
26
+ "zod": "^4.3.5",
27
+ "@mdxui/primitives": "6.0.0",
28
+ "mdxui": "6.0.0"
29
+ },
30
+ "peerDependencies": {
31
+ "react": "^18.0.0 || ^19.0.0"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "typecheck": "tsc --noEmit",
38
+ "clean": "rm -rf .turbo node_modules",
39
+ "test:visual": "playwright test",
40
+ "test:visual:update": "playwright test --update-snapshots",
41
+ "test:visual:ui": "playwright test --ui"
42
+ }
43
+ }
@@ -0,0 +1,55 @@
1
+ import { defineConfig, devices } from '@playwright/test'
2
+ import { visualTestDefaults } from '../../playwright.base.config'
3
+
4
+ /**
5
+ * Playwright Visual Regression Test Configuration for @mdxui/zero
6
+ *
7
+ * These tests compare our Zero components against the original Mail-0/Zero design.
8
+ * Extends visualTestDefaults from base config.
9
+ */
10
+ export default defineConfig({
11
+ testDir: './tests/visual',
12
+ snapshotDir: './tests/visual/snapshots',
13
+ snapshotPathTemplate: '{snapshotDir}/{testFilePath}/{arg}{ext}',
14
+
15
+ fullyParallel: true,
16
+ forbidOnly: !!process.env.CI,
17
+ retries: process.env.CI ? 2 : 0,
18
+ workers: process.env.CI ? 1 : undefined,
19
+
20
+ reporter: [
21
+ ['html', { outputFolder: './tests/visual/report' }],
22
+ ['list'],
23
+ ],
24
+
25
+ // Standard visual comparison settings
26
+ ...visualTestDefaults,
27
+
28
+ use: {
29
+ baseURL: 'http://localhost:6006',
30
+ trace: 'on-first-retry',
31
+ screenshot: 'only-on-failure',
32
+ },
33
+
34
+ projects: [
35
+ {
36
+ name: 'chromium',
37
+ use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 800 } },
38
+ },
39
+ {
40
+ name: 'chromium-wide',
41
+ use: { ...devices['Desktop Chrome'], viewport: { width: 1920, height: 1080 } },
42
+ },
43
+ {
44
+ name: 'mobile',
45
+ use: { ...devices['iPhone 14'] },
46
+ },
47
+ ],
48
+
49
+ webServer: {
50
+ command: 'pnpm storybook',
51
+ url: 'http://localhost:6006',
52
+ reuseExistingServer: !process.env.CI,
53
+ timeout: 120_000,
54
+ },
55
+ })
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared Components
3
+ *
4
+ * Reusable UI components used across Zero:
5
+ * - PricingCard: Pricing tier card
6
+ * - PricingSwitch: Monthly/annual toggle
7
+ * - SnoozeDialog: Email snooze picker
8
+ * - AttachmentDialog: Attachment viewer
9
+ * - LabelBadge: Email label badge
10
+ */
11
+
12
+ // Components will be added as they are ported from Zero
13
+ // export { PricingCard } from './pricing-card'
14
+ // export { PricingSwitch } from './pricing-switch'
15
+ // export { SnoozeDialog } from './snooze-dialog'
16
+ // export { AttachmentDialog } from './attachment-dialog'
17
+ // export { LabelBadge } from './label-badge'
18
+
19
+ // Placeholder export to make this a valid module
20
+ export {};
@@ -0,0 +1,219 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import type { DraftState } from "mdxui";
3
+ import { EditorToolbar, EmailComposer, RecipientInput } from "./email-composer";
4
+
5
+ const sampleDraft: DraftState = {
6
+ to: [{ address: "alex@startup.io", name: "Alex Rivera" }],
7
+ cc: [],
8
+ bcc: [],
9
+ subject: "Quick follow-up on our call",
10
+ body: "Hi Alex,\n\nJust wanted to follow up on our conversation earlier today about the product roadmap.\n\nLet me know if you have any questions!\n\nBest,\nMe",
11
+ attachments: [],
12
+ };
13
+
14
+ const replyToMessage = {
15
+ id: "m1",
16
+ threadId: "t1",
17
+ subject: "Re: Product Launch Strategy",
18
+ from: { address: "alex@startup.io", name: "Alex Rivera" },
19
+ to: [{ address: "me@company.com", name: "Me" }],
20
+ date: new Date().toISOString(),
21
+ snippet: "Thanks for the update! Looking forward to the launch.",
22
+ textBody: "Thanks for the update! Looking forward to the launch.",
23
+ attachments: [],
24
+ labels: [],
25
+ isRead: true,
26
+ isStarred: false,
27
+ isImportant: false,
28
+ isDraft: false,
29
+ };
30
+
31
+ const meta: Meta<typeof EmailComposer> = {
32
+ title: "Zero/Compose/EmailComposer",
33
+ component: EmailComposer,
34
+ parameters: {
35
+ layout: "fullscreen",
36
+ },
37
+ tags: ["autodocs"],
38
+ decorators: [
39
+ (Story) => (
40
+ <div className="h-screen w-full max-w-4xl mx-auto p-4 bg-background">
41
+ <Story />
42
+ </div>
43
+ ),
44
+ ],
45
+ };
46
+
47
+ export default meta;
48
+ type Story = StoryObj<typeof meta>;
49
+
50
+ /**
51
+ * New email composition.
52
+ * Matches the original Zero compose interface.
53
+ */
54
+ export const Default: Story = {
55
+ args: {
56
+ mode: "new",
57
+ showCcBcc: false,
58
+ enableAI: true,
59
+ enableShortcuts: true,
60
+ },
61
+ };
62
+
63
+ /**
64
+ * With draft content pre-filled.
65
+ */
66
+ export const WithDraft: Story = {
67
+ args: {
68
+ mode: "new",
69
+ initialDraft: sampleDraft,
70
+ showCcBcc: false,
71
+ enableAI: true,
72
+ },
73
+ };
74
+
75
+ /**
76
+ * Reply mode with quoted message.
77
+ */
78
+ export const Reply: Story = {
79
+ args: {
80
+ mode: "reply",
81
+ replyTo: replyToMessage,
82
+ showCcBcc: false,
83
+ enableAI: true,
84
+ },
85
+ };
86
+
87
+ /**
88
+ * Reply-all mode.
89
+ */
90
+ export const ReplyAll: Story = {
91
+ args: {
92
+ mode: "reply-all",
93
+ replyTo: replyToMessage,
94
+ showCcBcc: true,
95
+ enableAI: true,
96
+ },
97
+ };
98
+
99
+ /**
100
+ * Forward mode.
101
+ */
102
+ export const Forward: Story = {
103
+ args: {
104
+ mode: "forward",
105
+ replyTo: replyToMessage,
106
+ showCcBcc: false,
107
+ enableAI: true,
108
+ },
109
+ };
110
+
111
+ /**
112
+ * Maximized view.
113
+ */
114
+ export const Maximized: Story = {
115
+ args: {
116
+ mode: "new",
117
+ isMaximized: true,
118
+ showCcBcc: true,
119
+ enableAI: true,
120
+ },
121
+ };
122
+
123
+ /**
124
+ * With CC/BCC fields visible.
125
+ */
126
+ export const WithCcBcc: Story = {
127
+ args: {
128
+ mode: "new",
129
+ showCcBcc: true,
130
+ enableAI: true,
131
+ },
132
+ };
133
+
134
+ /**
135
+ * Sending state.
136
+ */
137
+ export const Sending: Story = {
138
+ args: {
139
+ mode: "new",
140
+ initialDraft: sampleDraft,
141
+ isSending: true,
142
+ enableAI: true,
143
+ },
144
+ };
145
+
146
+ /**
147
+ * Without AI features.
148
+ */
149
+ export const NoAI: Story = {
150
+ args: {
151
+ mode: "new",
152
+ showCcBcc: false,
153
+ enableAI: false,
154
+ },
155
+ };
156
+
157
+ /**
158
+ * With email signature.
159
+ */
160
+ export const WithSignature: Story = {
161
+ args: {
162
+ mode: "new",
163
+ showCcBcc: false,
164
+ enableAI: true,
165
+ enableSignature: true,
166
+ signature: "\n\n--\nJohn Doe\nProduct Manager\nAcme Inc.",
167
+ },
168
+ };
169
+
170
+ // RecipientInput story
171
+ export const RecipientInputDefault: StoryObj<typeof RecipientInput> = {
172
+ render: () => (
173
+ <div className="max-w-2xl p-4 bg-background">
174
+ <RecipientInput
175
+ field="to"
176
+ value={[
177
+ { address: "alex@example.com.ai", name: "Alex Rivera" },
178
+ { address: "sarah@example.com.ai", name: "Sarah Chen" },
179
+ ]}
180
+ onChange={() => {}}
181
+ placeholder="Add recipients"
182
+ showAvatars={true}
183
+ disabled={false}
184
+ autoFocus={false}
185
+ allowFreeEntry={true}
186
+ />
187
+ </div>
188
+ ),
189
+ };
190
+
191
+ // EditorToolbar story
192
+ export const ToolbarDefault: StoryObj<typeof EditorToolbar> = {
193
+ render: () => (
194
+ <div className="max-w-2xl p-4 bg-background border rounded-lg">
195
+ <EditorToolbar
196
+ onAction={() => {}}
197
+ showAI={true}
198
+ disabled={false}
199
+ compact={false}
200
+ sticky={false}
201
+ />
202
+ </div>
203
+ ),
204
+ };
205
+
206
+ // EditorToolbar compact
207
+ export const ToolbarCompact: StoryObj<typeof EditorToolbar> = {
208
+ render: () => (
209
+ <div className="max-w-xl p-4 bg-background border rounded-lg">
210
+ <EditorToolbar
211
+ onAction={() => {}}
212
+ showAI={true}
213
+ disabled={false}
214
+ compact={true}
215
+ sticky={false}
216
+ />
217
+ </div>
218
+ ),
219
+ };