diff --git a/designs/wemail-macos-client/app.jsx b/designs/wemail-macos-client/app.jsx
new file mode 100644
index 0000000..ab4646e
--- /dev/null
+++ b/designs/wemail-macos-client/app.jsx
@@ -0,0 +1,164 @@
+const {
+ Sidebar,
+ MessageList,
+ DetailPane,
+ MenuBar,
+ QuickPopover,
+ PreferencesSheet,
+ ComposeSheet,
+ Toast,
+ Icon,
+ IconButton,
+ ToolbarButton
+} = window;
+
+function filterMessages(messages, filter, query) {
+ const normalizedQuery = query.trim().toLowerCase();
+ return messages.filter((message) => {
+ const matchesFilter =
+ filter === "all" ||
+ (filter === "code" && message.type === "code") ||
+ (filter === "link" && message.type === "link") ||
+ (filter === "unread" && message.unread);
+
+ if (!matchesFilter) return false;
+ if (!normalizedQuery) return true;
+
+ return [message.sender, message.from, message.subject, message.preview, message.extractionValue]
+ .join(" ")
+ .toLowerCase()
+ .includes(normalizedQuery);
+ });
+}
+
+function App() {
+ const [theme, setTheme] = React.useState("light");
+ const [activeMailboxId, setActiveMailboxId] = React.useState(window.mailboxData[0].id);
+ const [selectedMessageId, setSelectedMessageId] = React.useState(window.messageData[0].id);
+ const [filter, setFilter] = React.useState("all");
+ const [query, setQuery] = React.useState("");
+ const [showQuick, setShowQuick] = React.useState(false);
+ const [showPreferences, setShowPreferences] = React.useState(false);
+ const [showCompose, setShowCompose] = React.useState(false);
+ const [showRaw, setShowRaw] = React.useState(false);
+ const [toast, setToast] = React.useState(null);
+
+ React.useEffect(() => {
+ document.documentElement.dataset.theme = theme;
+ }, [theme]);
+
+ React.useEffect(() => {
+ if (!toast) return undefined;
+ const timer = window.setTimeout(() => setToast(null), 2400);
+ return () => window.clearTimeout(timer);
+ }, [toast]);
+
+ const activeMailbox = window.mailboxData.find((mailbox) => mailbox.id === activeMailboxId) ?? window.mailboxData[0];
+ const mailboxMessages = window.messageData.filter((message) => message.mailboxId === activeMailbox.id);
+ const visibleMessages = filterMessages(mailboxMessages, filter, query);
+ const selectedMessage =
+ visibleMessages.find((message) => message.id === selectedMessageId) ??
+ mailboxMessages.find((message) => message.id === selectedMessageId) ??
+ visibleMessages[0] ??
+ null;
+
+ const quickMessages = window.messageData.filter((message) => message.type !== "muted");
+ const highValueCount = quickMessages.filter((message) => message.unread).length;
+
+ function handleSelectMailbox(mailboxId) {
+ const nextMessages = window.messageData.filter((message) => message.mailboxId === mailboxId);
+ setActiveMailboxId(mailboxId);
+ setSelectedMessageId(nextMessages[0]?.id ?? null);
+ setShowRaw(false);
+ }
+
+ function handleCopy(value) {
+ const text = value || "未提取";
+ if (navigator.clipboard?.writeText) {
+ navigator.clipboard.writeText(text).catch(() => undefined);
+ }
+ setToast({
+ title: "已复制到剪贴板",
+ detail: text
+ });
+ }
+
+ function handleSendTest() {
+ setShowCompose(false);
+ setToast({
+ title: "测试邮件已加入队列",
+ detail: "发件箱会显示发送记录和异常状态"
+ });
+ }
+
+ return (
+
+
setShowQuick((value) => !value)}
+ onToggleTheme={() => setTheme((value) => (value === "dark" ? "light" : "dark"))}
+ />
+ {showQuick ? : null}
+
+ setShowPreferences(true)}
+ onSelectMailbox={handleSelectMailbox}
+ />
+
+
+
+
+
+
收件处理
+ {activeMailbox.address}
+
+
+
+ handleCopy("已刷新当前邮箱")} />
+ setShowCompose(true)} />
+ handleCopy(selectedMessage?.extractionValue)} />
+
+
+
+ setQuery(event.target.value)}
+ placeholder="搜索发件人、主题或验证码"
+ value={query}
+ />
+ {query ? setQuery("")} /> : }
+
+
+
+ {
+ setSelectedMessageId(messageId);
+ setShowRaw(false);
+ }}
+ selectedMessageId={selectedMessage?.id ?? null}
+ />
+ setShowRaw((value) => !value)}
+ showRaw={showRaw}
+ />
+
+
+
+ {showPreferences ? setShowPreferences(false)} /> : null}
+ {showCompose ? setShowCompose(false)} onSend={handleSendTest} /> : null}
+
+
+ );
+}
+
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render();
diff --git a/designs/wemail-macos-client/components.jsx b/designs/wemail-macos-client/components.jsx
new file mode 100644
index 0000000..4a313da
--- /dev/null
+++ b/designs/wemail-macos-client/components.jsx
@@ -0,0 +1,441 @@
+const { Icon } = window;
+
+function TrafficLights() {
+ return (
+
+
+
+
+
+ );
+}
+
+function IconButton({ icon, label, active = false, onClick }) {
+ return (
+
+ );
+}
+
+function ToolbarButton({ icon, label, primary = false, onClick }) {
+ return (
+
+ );
+}
+
+function SegmentedControl({ value, options, onChange }) {
+ return (
+
+ {options.map((option) => (
+
+ ))}
+
+ );
+}
+
+function Sidebar({ activeMailboxId, onSelectMailbox, onOpenPreferences }) {
+ return (
+
+ );
+}
+
+function ExtractionChip({ message }) {
+ const iconName = message.type === "code" ? "key-round" : message.type === "link" ? "link-2" : "minus";
+ return (
+
+
+ {message.type === "code" ? message.extractionValue : message.extractionLabel}
+
+ );
+}
+
+function MessageRow({ message, active, onSelect }) {
+ return (
+
+ );
+}
+
+function MessageList({ mailbox, messages, selectedMessageId, filter, onFilterChange, onSelectMessage }) {
+ const filterOptions = [
+ { id: "all", label: "全部", icon: "list-filter" },
+ { id: "code", label: "验证码", icon: "key-round" },
+ { id: "link", label: "链接", icon: "link-2" },
+ { id: "unread", label: "未读", icon: "circle" }
+ ];
+
+ return (
+
+
+
+
+
{mailbox.name}
+
{mailbox.address}
+
+
+
+ {mailbox.extractions}
+ 待提取
+
+
+ {messages.length}
+ 消息
+
+
+
+
+
+
+
+
+ {messages.length ? (
+ messages.map((message) => (
+
+ ))
+ ) : (
+
当前条件下没有可显示的消息。
+ )}
+
+
+ );
+}
+
+function DetailPane({ message, onCopy, showRaw, onToggleRaw }) {
+ if (!message) {
+ return (
+
+ 选择一封邮件查看提取结果、正文和调试信息。
+
+ );
+ }
+
+ const hasTaskValue = Boolean(message.extractionValue);
+ const taskLabel = message.type === "code" ? "识别到验证码" : message.type === "link" ? "识别到登录链接" : "未识别高价值结果";
+
+ return (
+
+
+
+
{message.subject}
+
+ {message.from}
+ {message.time}
+ {message.attachments ? `${message.attachments} 个附件` : "无附件"}
+
+
+
+
+ onCopy("原始邮件链接已准备打开")} />
+ onCopy(message.extractionValue || message.subject)} />
+
+
+
+
+
+
+
+ {taskLabel}
+
+
+ {hasTaskValue ? message.extractionValue : "未提取"}
+
+
+
+
+ 置信度
+ {message.confidence}%
+
+
+
+
+
+
+ 正文预览
+ {showRaw ? (
+ {JSON.stringify({ id: message.id, extraction: message.extractionValue, meta: message.meta }, null, 2)}
+ ) : (
+ message.body.map((paragraph) => {paragraph}
)
+ )}
+
+
+
+
提取上下文
+
+ {Object.entries(message.meta).map(([key, value]) => (
+
+
- {key}
+ - {value}
+
+ ))}
+
+
+
+
处理链路
+
+ {message.timeline.map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
本机行为
+
+ 高价值邮件通知
+
+
+
+ 复制后标记已读
+ 开启
+
+
+
+
+
+
+ );
+}
+
+function MenuBar({ theme, onToggleTheme, quickCount, showQuick, onToggleQuick }) {
+ return (
+
+
+ WeMail
+ File
+ Message
+ Rules
+
+
+
+
+ 09:42
+
+
+ );
+}
+
+function QuickPopover({ messages, onCopy }) {
+ return (
+
+
+ 最新可复制结果
+
+
+
+ {messages.slice(0, 4).map((message) => (
+
+
+ {message.sender}
+ {message.subject}
+
+
+
+ ))}
+
+
+ );
+}
+
+function PreferencesSheet({ onClose }) {
+ return (
+
+
+
+
WeMail 偏好设置
+
+
+
+
+
+
通知策略
+
验证码和登录链接触发系统通知,普通邮件仅进入收件处理列表。
+
高价值邮件立即通知
+
通知声音Glass
+
+
+
复制行为
+
从详情页或菜单栏复制后,自动记录操作并可选标记为已读。
+
复制后标记已读开启
+
保留剪贴板记录30 分钟
+
+
+
路由与告警
+
异常或未匹配邮件进入发件箱异常视图,同时保留原始邮件上下文。
+
Webhook已连接
+
Telegram仅失败告警
+
+
+
桌面工作台
+
默认进入收件处理,消息列表优先展示提取结果而不是长主题。
+
默认视图验证码
+
阅读密度紧凑
+
+
+
+
+
+
+
+
+ );
+}
+
+function ComposeSheet({ onClose, onSend }) {
+ return (
+
+
+
+
发送测试邮件
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function Toast({ toast }) {
+ if (!toast) return null;
+ return (
+
+
+
+ {toast.title}
+ {toast.detail}
+
+
+ );
+}
+
+Object.assign(window, {
+ TrafficLights,
+ IconButton,
+ ToolbarButton,
+ SegmentedControl,
+ Sidebar,
+ MessageList,
+ DetailPane,
+ MenuBar,
+ QuickPopover,
+ PreferencesSheet,
+ ComposeSheet,
+ Toast
+});
diff --git a/designs/wemail-macos-client/data.jsx b/designs/wemail-macos-client/data.jsx
new file mode 100644
index 0000000..6fd5c31
--- /dev/null
+++ b/designs/wemail-macos-client/data.jsx
@@ -0,0 +1,259 @@
+const mailboxData = [
+ {
+ id: "qa-login",
+ name: "QA Login Flow",
+ address: "qa-login@wemail.dev",
+ unread: 7,
+ extractions: 12,
+ attachments: 3
+ },
+ {
+ id: "staging",
+ name: "Staging Accounts",
+ address: "staging@wemail.dev",
+ unread: 3,
+ extractions: 6,
+ attachments: 1
+ },
+ {
+ id: "billing",
+ name: "Billing Sandbox",
+ address: "billing@wemail.dev",
+ unread: 2,
+ extractions: 2,
+ attachments: 0
+ },
+ {
+ id: "security",
+ name: "Security Review",
+ address: "security@wemail.dev",
+ unread: 0,
+ extractions: 1,
+ attachments: 5
+ }
+];
+
+const messageData = [
+ {
+ id: "msg-vercel",
+ mailboxId: "qa-login",
+ sender: "Vercel",
+ from: "login@vercel.com",
+ subject: "Your Vercel verification code",
+ preview: "Use this code to continue signing in. The code expires in 10 minutes.",
+ time: "09:42",
+ unread: true,
+ type: "code",
+ extractionLabel: "验证码",
+ extractionValue: "482913",
+ confidence: 96,
+ attachments: 0,
+ body: [
+ "You requested a sign-in code for the Vercel staging workspace.",
+ "Enter the code above in the browser to complete login. If this was not you, ignore this message.",
+ "Workspace: WeOpen QA. Region: Singapore edge."
+ ],
+ meta: {
+ "提取类型": "auth_code",
+ "来源规则": "six-digit-code",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass, SPF pass"
+ },
+ timeline: ["收到 Cloudflare Email Routing 事件", "postal-mime 解析正文", "命中验证码提取规则", "已推送 macOS 通知"]
+ },
+ {
+ id: "msg-github",
+ mailboxId: "qa-login",
+ sender: "GitHub",
+ from: "noreply@github.com",
+ subject: "Confirm your device for WeMail tests",
+ preview: "A new device is trying to access your GitHub account. Confirm this sign-in request.",
+ time: "09:31",
+ unread: true,
+ type: "link",
+ extractionLabel: "LOGIN LINK",
+ extractionValue: "github.com/login/device/verify",
+ confidence: 91,
+ attachments: 0,
+ body: [
+ "A new device wants to access the WeMail QA organization.",
+ "Use the login link above to confirm the request. This link is only valid for this session.",
+ "Device: Chrome macOS. IP range: internal QA network."
+ ],
+ meta: {
+ "提取类型": "auth_link",
+ "来源规则": "login-url-primary",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass, SPF neutral"
+ },
+ timeline: ["收到邮件", "识别登录按钮 URL", "折叠长链接", "标记为高价值消息"]
+ },
+ {
+ id: "msg-linear",
+ mailboxId: "qa-login",
+ sender: "Linear",
+ from: "security@linear.app",
+ subject: "Magic link for QA session",
+ preview: "Click the link to finish signing in to Linear. The link will expire shortly.",
+ time: "09:10",
+ unread: false,
+ type: "link",
+ extractionLabel: "MAGIC LINK",
+ extractionValue: "linear.app/login/magic/QA-7K",
+ confidence: 88,
+ attachments: 0,
+ body: [
+ "Here is your magic link for Linear.",
+ "Use it only if you requested access from the WeMail disposable inbox flow."
+ ],
+ meta: {
+ "提取类型": "service_link",
+ "来源规则": "magic-link-button",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass"
+ },
+ timeline: ["收到邮件", "解析 HTML 正文", "命中 magic link", "记录提取 JSON"]
+ },
+ {
+ id: "msg-notion",
+ mailboxId: "qa-login",
+ sender: "Notion",
+ from: "team@makenotion.com",
+ subject: "Welcome to the QA workspace",
+ preview: "Your workspace is ready. No verification code was detected in this message.",
+ time: "08:48",
+ unread: false,
+ type: "muted",
+ extractionLabel: "未提取",
+ extractionValue: "",
+ confidence: 0,
+ attachments: 1,
+ body: [
+ "Welcome to the QA workspace.",
+ "This message contains onboarding context but no login code or link. It remains readable in the desktop client for audit trails."
+ ],
+ meta: {
+ "提取类型": "none",
+ "来源规则": "no-match",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass, SPF pass"
+ },
+ timeline: ["收到邮件", "正文解析完成", "未命中验证码或链接", "保留完整正文"]
+ },
+ {
+ id: "msg-stripe",
+ mailboxId: "staging",
+ sender: "Stripe",
+ from: "support@stripe.com",
+ subject: "Confirm your staging payout account",
+ preview: "Your Stripe staging account requires a confirmation code before continuing.",
+ time: "08:18",
+ unread: true,
+ type: "code",
+ extractionLabel: "验证码",
+ extractionValue: "761228",
+ confidence: 94,
+ attachments: 0,
+ body: [
+ "Use this confirmation code to continue setting up the staging payout account.",
+ "This code expires soon. Do not share it with anyone outside the QA run."
+ ],
+ meta: {
+ "提取类型": "auth_code",
+ "来源规则": "six-digit-code",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass"
+ },
+ timeline: ["收到邮件", "解析纯文本正文", "提取验证码", "写入收件箱视图模型"]
+ },
+ {
+ id: "msg-slack",
+ mailboxId: "staging",
+ sender: "Slack",
+ from: "feedback@slack.com",
+ subject: "Security alert for staging login",
+ preview: "We noticed a sign-in from a new browser. Review the session if you do not recognize it.",
+ time: "07:54",
+ unread: true,
+ type: "muted",
+ extractionLabel: "未提取",
+ extractionValue: "",
+ confidence: 0,
+ attachments: 0,
+ body: [
+ "A sign-in happened from a browser that has not been seen before.",
+ "No actionable login code or link was present, so WeMail keeps the message in a lower-priority state."
+ ],
+ meta: {
+ "提取类型": "none",
+ "来源规则": "no-match",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass"
+ },
+ timeline: ["收到邮件", "安全提示分类", "未提取", "保留给人工确认"]
+ },
+ {
+ id: "msg-aws",
+ mailboxId: "billing",
+ sender: "AWS",
+ from: "no-reply-aws@amazon.com",
+ subject: "Root account verification",
+ preview: "Enter the verification code for the billing sandbox root account.",
+ time: "昨天",
+ unread: true,
+ type: "code",
+ extractionLabel: "验证码",
+ extractionValue: "390144",
+ confidence: 97,
+ attachments: 0,
+ body: [
+ "Enter this verification code to complete access to the AWS billing sandbox.",
+ "This is a controlled disposable mailbox for test automation and manual QA only."
+ ],
+ meta: {
+ "提取类型": "auth_code",
+ "来源规则": "six-digit-code",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass, SPF pass"
+ },
+ timeline: ["收到邮件", "提取验证码", "命中账单沙箱邮箱", "通知已发送"]
+ },
+ {
+ id: "msg-security-report",
+ mailboxId: "security",
+ sender: "Cloudflare",
+ from: "notify@cloudflare.com",
+ subject: "Email Routing weekly digest",
+ preview: "Weekly routing digest with attachment logs and delivery summary.",
+ time: "周一",
+ unread: false,
+ type: "muted",
+ extractionLabel: "未提取",
+ extractionValue: "",
+ confidence: 0,
+ attachments: 5,
+ body: [
+ "This digest summarizes inbound routing events for the WeMail project.",
+ "The macOS client keeps these messages available, but does not promote them above verification tasks."
+ ],
+ meta: {
+ "提取类型": "none",
+ "来源规则": "digest-message",
+ "处理链路": "inbound-email-routing",
+ "Headers": "DKIM pass"
+ },
+ timeline: ["收到 digest", "识别附件", "无验证码", "归档到安全审查邮箱"]
+ }
+];
+
+const navItems = [
+ { id: "inbox", label: "收件处理", icon: "inbox", count: 12 },
+ { id: "outbound", label: "发件箱", icon: "send", count: 3 },
+ { id: "settings", label: "邮件规则", icon: "sliders-horizontal", count: 0 }
+];
+
+Object.assign(window, {
+ mailboxData,
+ messageData,
+ navItems
+});
diff --git a/designs/wemail-macos-client/icons.jsx b/designs/wemail-macos-client/icons.jsx
new file mode 100644
index 0000000..830f9a2
--- /dev/null
+++ b/designs/wemail-macos-client/icons.jsx
@@ -0,0 +1,25 @@
+function Icon({ name, label, size = 16, className = "" }) {
+ React.useEffect(() => {
+ if (window.lucide) {
+ window.lucide.createIcons({
+ attrs: {
+ "stroke-width": "1.8",
+ "absolute-stroke-width": "true"
+ }
+ });
+ }
+ });
+
+ return (
+
+ );
+}
+
+Object.assign(window, { Icon });
diff --git a/designs/wemail-macos-client/prototype-desktop.png b/designs/wemail-macos-client/prototype-desktop.png
new file mode 100644
index 0000000..0274804
Binary files /dev/null and b/designs/wemail-macos-client/prototype-desktop.png differ
diff --git a/designs/wemail-macos-client/prototype-mobile.png b/designs/wemail-macos-client/prototype-mobile.png
new file mode 100644
index 0000000..3452266
Binary files /dev/null and b/designs/wemail-macos-client/prototype-mobile.png differ
diff --git a/designs/wemail-macos-client/wemail-macos-client.html b/designs/wemail-macos-client/wemail-macos-client.html
new file mode 100644
index 0000000..008ec20
--- /dev/null
+++ b/designs/wemail-macos-client/wemail-macos-client.html
@@ -0,0 +1,1340 @@
+
+
+
+
+
+ WeMail macOS Client Prototype
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/designs/wemail-macos-client2/WeMail macOS Client.html b/designs/wemail-macos-client2/WeMail macOS Client.html
new file mode 100644
index 0000000..2bf30d5
--- /dev/null
+++ b/designs/wemail-macos-client2/WeMail macOS Client.html
@@ -0,0 +1,1433 @@
+
+
+
+
+
+ WeMail macOS Client - High Fidelity Prototype
+
+
+
+
+
+
+
+
+
+
+
+
Safari - checkout.example
+
+
+
+
+
+
+
W
+
+
WeMail 识别到验证码
+
来自 Stripe 的邮件包含验证码 482193,可直接复制到当前注册流程。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 当前临时邮箱
+ signup-aqua-27@wemail.dev
+
+
+
+
+
+
+
+
+ 每 5 秒刷新,剩余 01:42
+ 超时后自动降频
+
+
+
+
+
+
+
验证码
+
482193
+
+ Stripe 登录验证
+ 2 秒前 · 已由服务端 extraction 识别
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 登录凭据
+ Token 存储在 macOS Keychain
+
+
安全
+
+
+
+ 刷新策略
+ 等待模式 5 秒,后台 90 秒
+
+
+
+
+
+ 隐私边界
+ 不读取剪贴板,不扫描浏览器页面
+
+
本地
+
+
+
+ 连接
+ api.wemail.dev · 已认证
+
+
+
+
+
+
+
+
+
已复制到剪贴板
+
+
+
+
+
+
+
+
+
diff --git a/designs/wemail-macos-client2/prototype-screenshot.png b/designs/wemail-macos-client2/prototype-screenshot.png
new file mode 100644
index 0000000..6519412
Binary files /dev/null and b/designs/wemail-macos-client2/prototype-screenshot.png differ