ENZH
和 AI 讨论这篇文章
ChatGPTClaude

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

📊 幻灯片

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

上一篇讲到 Mio 的 Telegram bot 已经能上生产了:输入校验有了,心跳带 context 了,测试在跑,部署也不会断线。但 Telegram 不是中国用户的主场。web 端倒是一直有一个,只能说就是个光秃秃的单页聊天框:你打字,它回话,没有导航,没有别的页面。能用是能用,就跟命令行也能用一个意思。所以 v0.0.4 干的事情就一件:把整个 web 端推倒重来,照着微信重做一遍。不是在原来那个聊天框上贴皮,是整个重建。

为什么照着微信?这个其实没什么好纠结的。你要找一个中国用户不用学就会的界面,那只能是微信。十几亿人每天都在用,底部四个 tab、按时间排的聊天列表、绿色主色调、发现页,这套东西大家已经用成肌肉记忆了。与其自己发明一套导航让用户学,不如直接用大家已经会的这套,第一次打开 app 就知道什么在哪。

四个 tab

新布局完全对标微信,具体是这样的:

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

每个 tab 都是一个独立页面,有自己的路由,正经的多页架构加共享布局,没有走那种单页里假装切 tab 的做法。

文案从第一天就是中文

所有用户看到的文字,从一开始就是拿中文写的,不是英文写完再翻。这个区别挺大的:翻过来的 UI 总有一种说不出的别扭,间距不对、措辞生硬、该随意的地方太正式。「对方正在输入...」就是最好的例子——它不是「typing...」的翻译,它是中国用户在微信里看了几千遍的那句原话,直接用就行。Mio 是给中国用户做的,界面从第一个字就得直接拿中文想,而不是英文写完再翻。

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

这版没有用任何组件库。Ant Design、Material UI、shadcn,一个都没用,全部组件从零写,样式全走 CSS 自定义属性。这套 design token 我管它叫「微信 2025」:颜色、间距、圆角、阴影、字号都定义成 CSS 变量,参考微信当前的设计语言,但不是照抄。

为什么不用库,大概两个原因:

  1. Mio 的 UI 目标很明确——要长得像微信,不是像「一个套了中文皮的 React 项目」。组件库有自己的视觉语言,你跟它较劲的时间比从头写还多。
  2. 依赖控制。v0.0.4 加了 30 多个组件,package.json 里一个新包都没多。每个组件共用同一套 design token,视觉一致,也不用背外部库的包袱。

另外写了一个共享的头像工具函数,保证 agent 头像在四个 tab 和聊天页里长得一样。很小的细节,但不同页面头像长得不一样,整个 app 一下就显得糙了。

引导做成聊天,不做成表单

Telegram 那边的引导是按钮式的,inline keyboard 加 callback query,标准的 bot 交互。能用,但体验就是在填表。

web 端这次完全换了个做法:引导本身就是一场聊天。Mio 用聊天气泡问你问题,你点样式化的按钮回答。选人设故事这一步也不是下拉菜单或者单选框,Mio 直接把不同的人设故事一条一条以消息的形式发给你,你顺着对话把它们读完,选哪个自然就成了聊天的一部分。等引导走完,你其实已经跟 Mio 完成了第一次聊天,虽然那些回复全是预设的。

这个事我觉得还挺重要的。引导要是做成表单,用户上来就会把 Mio 当工具用。做成聊天的话,从第一分钟起用户就是在跟 TA 聊天了。

引导做成表单还是聊天决定用户的心态:表单式引导让人把 Mio 当工具,聊天式引导让人从第一分钟就把 TA 当伙伴引导做成表单还是聊天决定用户的心态:表单式引导让人把 Mio 当工具,聊天式引导让人从第一分钟就把 TA 当伙伴

打字感知的消息合并

这个功能不显眼,但很重要。最简单的做法是每按一次回车就发一条消息。但用户真实的打字习惯是短句连发:「嘿」,发送;「你好吗」,发送;「我今天有点烦」,发送。按最简单的做法,这就是三次 API 调用、三个独立的 AI 回复,对话直接碎掉。

合并系统干的事情是观察打字节奏:你发了一条马上又开始打字,系统就先按住第一条不发;等打字停顿够久了,再把这几条合成一条完整消息发给 AI。微信对话的真实节奏就是这样,人就是会连发短消息,「嘿」隔两秒跟一句「你好吗」,其实是同一个念头拆成两条发出来的,界面就得照着这个习惯来处理。实现上也没加依赖,就是一个自定义的 usePolling hook,做页面可见性感知的定时轮询,tab 隐藏就暂停,可见了再恢复。

打字感知的消息合并:连发的短消息被按住等待,停顿够久后合成一条再发给 AI,换来一个完整回复打字感知的消息合并:连发的短消息被按住等待,停顿够久后合成一条再发给 AI,换来一个完整回复

路由

每个聊天住在 /chats/[agentId],动态路由,负责加载这个 agent 的对话历史、显示「对方正在输入...」的打字指示器、处理消息发送。middleware 保护所有四个 tab,没登录直接重定向。老的 /chat 也留了重定向到 /chats,向后兼容——URL 发出去了就别弄断,存过旧链接的用户点开还得能用。

agent 列表和会话数据统一走一个 useAgents hook:一个 hook,一个数据源,四个 tab 都从它消费。这样就避开了那个经典问题:聊完天回到列表,列表还是旧数据。

两轮 audit

30 多个新增和改动的组件,出 bug 的面积不小,所以实现完之后跑了两轮完整 audit。第一轮抓出来的是 CSS 变量不一致、消息渲染的 XSS 隐患、还有一些无障碍问题;第二轮抓的是头像逻辑重复(后来集中化了)和通讯录分组的边界情况。两轮的 CRITICAL 和 HIGH 全部修完,类型检查干净,依然零新依赖。跟上一篇是同一个思路,audit 过了才发版。

没装一个新包

四个 tab 页、聊天页、引导流程、设计系统、hooks、middleware,这一整套重构加了 30 多个组件,一个新 npm 包都没装。usePolling 就 30 行代码,useAgents 不到 100 行,管了 agent 状态、会话数据和实时更新。

控制依赖数倒不是为了当纯粹主义者,主要是外部依赖等于让别人替你做了决策,出了问题还得自己收拾。现阶段 Mio 的 web 前端还足够小,从零写反而更快,不用去学别人的抽象,也不用修别人的 bug。

从 bot 到应用

v0.0.3 之后 Mio 是一个靠谱的 Telegram bot,v0.0.4 之后 TA 是一个正经的 web 应用。差别不只是好不好看:做 Telegram bot,UI、交互规则、更新节奏全捏在别人手里;web 是自己的,引导怎么走、导航怎么排,全都自己定。

现在 Mio 有两个家,喜欢即时通讯的用户用 Telegram,想要完整体验的走 web。背后是同一个 AI,人格和记忆也是同一套,只是换了个界面给不同的场景用。

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

和 AI 讨论这篇文章
ChatGPTClaude

订阅更新

新文章发布时发到你的邮箱,不发别的。


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