ENZH

v0.0.4:照着微信重做了界面

仿微信界面设计的概念插画仿微信界面设计的概念插画

什么界面不用学就会?

上一篇结尾,Mio 的 Telegram bot 已经能上生产了——输入校验过了、心跳有上下文了、测试在跑、部署不会断线。一切就绪。

但 Telegram 不是中国用户的主场。

网页端倒是有,但说实话就是一个光秃秃的单页聊天框。能用,就像命令行能用一样——你打字,它回话,完了。没有导航,没有归属感,没有任何熟悉的东西。

我一直在想一个问题:什么界面是中国用户不用学就会的?

答案太明显了——微信。

十几亿人每天都在用。底部四个 Tab、按时间排的聊天列表、绿色主色调、"发现"页面。这不是一种 UI 范式——这是十几亿人的肌肉记忆。

所以 v0.0.4 不是在原来的基础上贴皮。是把整个 Web 体验推倒重来,围绕一个 13 亿人已经内化的心智模型来构建。

四个 Tab,一个熟人

新布局完全对标微信:

  • 消息:按最后一条消息时间排序的聊天列表,有搜索,有相对时间戳
  • 通讯录:Agent 列表按预设类型分组,"添加 Agent"入口
  • 发现:预设市场——浏览人格预设,看简介,一键"添加"
  • :个人信息卡片、设置展示、退出登录

每个 Tab 都是真正的页面,有独立路由。不是用一个 SPA 伪装出四个 Tab,而是正经的多页架构加共享布局。

为什么要这么像?因为熟悉感消灭摩擦。用户第一次打开这个 App,不应该想"这怎么用",应该想"哦,我知道什么在哪"。

最好的 UI 就是用户已经在别处学会了的 UI。

中文原生,不是翻译过来的

所有用户看到的文字,从一开始就是中文写的。不是从英文翻过来的。

这个区别很关键。翻译过来的 UI 总有一种说不出的别扭——间距不对、措辞生硬、该随意的地方太正式。

"对方正在输入..."不是"typing..."的翻译。它是中国用户在微信里看了几千遍的那句话。那六个字自带的亲切感,不是任何翻译能造出来的。

Mio 是给中国用户做的。界面就应该像自家的,不是进口的。

不用组件库,从零搭设计系统

我没有用任何组件库。不用 Ant Design,不用 Material UI,不用 shadcn。

从零搭了一套设计系统,全部用 CSS 自定义属性——我叫它"微信 2025 设计令牌"。颜色、间距、圆角、阴影、字号——都定义成 CSS 变量,参考微信当前的设计语言,但不是照抄。

为什么不用库?两个原因。

第一,Mio 的 UI 是有态度的——它要看起来像微信,不是像"一个套了中文皮的 React 项目"。组件库有自己的视觉语言,你跟它较劲的时间比从头写还多。

第二,零新依赖。v0.0.4 加了 30 多个组件,package.json 里一个新包都没多。每个组件共用同一套设计令牌,视觉一致但不带外部库的包袱。

一个共享的头像工具函数保证了 Agent 头像在四个 Tab 和聊天页里长得一样。小细节——但不同页面头像长得不一样会让整个 App 显得很粗糙。

聊天式引导:不是填表,是对话

Telegram 的引导流程是按钮式的——inline keyboard、callback query,标准的 bot 交互。能用,但体验像填表。

Web 端完全不同。是聊天式的问答流程:Mio 用聊天气泡问你问题,你点样式化的按钮来回答。感觉像在对话,不是在填问卷。

最亮眼的是人设故事选择。不是下拉菜单或单选框,而是 Mio 把不同的人设故事以消息形式展示——你在上下文里读完,选择自然地融入对话。等引导结束,你已经跟 Mio 有了第一次"聊天"——尽管所有回复其实是预设的。

这事为什么重要?因为引导定基调。

如果第一次体验像填表,用户会把产品当工具。如果像聊天,用户从第一分钟就会把 Mio 当聊天对象。

打字感知的消息合并

这个功能不显眼,但很重要。

最简单的做法:每按一次回车就发一条消息。但用户习惯短句连发——"嘿"[发送]"你好吗"[发送]"我今天有点烦"[发送]——会触发三次 API 调用、三个独立的 AI 回复、一段支离破碎的对话。

消息合并系统会观察打字节奏。发了一条马上又开始打字?系统先按住第一条等着。等打字停顿够久了,再把所有内容合成一条完整消息发给 AI。

这就是微信对话的真实节奏。人就是会连发短消息。一个好的聊天界面应该明白:"嘿"+"你好吗"中间隔了两秒,是一个念头,不是两个。

实现上没有新依赖。只是一个自定义的 usePolling hook,处理页面可见性感知的定时轮询——Tab 隐藏时暂停,可见时恢复。

路由架构

每个聊天住在 /chats/[agentId]——动态路由,加载 Agent 的对话历史,显示"对方正在输入..."的打字指示器,处理消息发送。

中间件保护所有四个 Tab。没登录?直接重定向。还有向后兼容:/chat 重定向到 /chats——因为 URL 是承诺,打破 URL 就是打破信任。

useAgents hook 管理所有视图的 Agent 列表和会话数据。一个 hook,一个数据源,多个 Tab 消费。避免了那种经典问题:聊完天回到列表,列表还是旧数据。

两轮审计

30 多个新增和修改的组件,bug 的温床面积不小。

实现完成后跑了两轮完整审计。第一轮抓出了 CSS 变量不一致、消息渲染的 XSS 隐患、无障碍问题。第二轮抓出了头像逻辑重复(已集中化)和通讯录分组的边界情况。

两轮审计的所有 CRITICAL 和 HIGH 问题全部修完。类型检查干净通过。零新依赖。

上一篇一个理念:审计过了才发版,不是功能写完就发版。没审计过的功能不是资产,是负债。

30+ 组件,0 新依赖

这是我最得意的数字。

整个微信重构——四个 Tab 页面、聊天页、引导流程、设计系统、hooks、中间件——加了 30 多个组件,一个新 npm 包都没装。

每个组件用同一套 CSS 自定义属性。每个 hook 遵循同样的模式。usePolling hook 30 行代码。useAgents hook 不到 100 行管了 Agent 状态、会话数据和实时更新。

控制依赖数不是为了当纯粹主义者。是为了掌控力。每一个外部依赖都是别人做的决策,你得承担后果。现阶段 Mio 的 Web 前端还足够小,从零写比学别人的抽象、配别人的选项、调别人的 bug 更快。

从寄人篱下到自己的地盘

v0.0.3 让 Mio 成了一个靠谱的 Telegram bot。v0.0.4 让TA成了一个正经的 Web 应用。

差别不只是视觉上的。Telegram bot 寄人篱下——你被别人的 UI 约束、被别人的规则约束、被别人的更新周期约束。Web 应用是你自己的地盘。引导流程你说了算,导航结构你说了算,整个体验你说了算。

Mio 现在有两个家:Telegram 给喜欢即时通讯的用户,Web 给想要完整体验的用户。同一个 AI,同一个人格,同一套记忆——不同界面,给不同场景。

第一篇走到这里,Mio 从一个架构决策变成了一个多平台的产品,有了自己的设计语言。地基打好了,下一步是让TA开口说话——不只是打字。

但那是 v0.0.5 的故事了。


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0