AI Chat
一个功能完整的 AI 聊天应用,逐特性移植自官方 Nuxt AI Chatbot 模板(Nuxt UI + Vercel AI SDK)——这可能是迄今对 Vue Lynx 要求最高的「真实世界」验证:流式响应与思考/推理过程、带代码高亮和表格的 Markdown、工具调用卡片(天气、图表、网络搜索来源)、按日期分组的聊天历史、点赞/点踩、消息编辑、分享/重命名/删除、模型选择器、可持久化的明暗模式运行时主题、附件、模糊命令面板搜索,以及带侧滑抽屉的响应式移动端布局。
试试下面的快捷提示——"What is the weather in Bordeaux?" 会流式输出推理过程和天气卡片;"Show me a chart of sales data" 渲染 SVG 折线图;"Help me create a Vue composable" 展示带高亮代码的 Markdown。在 native 标签页扫描二维码即可在 LynxExplorer 中运行同一个 bundle。
上面的演练场完全自包含:应用会探测 API 服务器,不可达时自动回退到内置演示后端(相同的种子历史与确定性 mock AI 流)。在本地用 pnpm dev:server 运行示例时,同一套 UI 会连接一个复刻了 Nuxt 模板 API 路由的独立 Node 服务器——设置 AI_GATEWAY_API_KEY 后还可通过 Vercel AI Gateway 流式调用真实模型。
原生聊天的关键技巧
一次流畅的发送并不只是一段动画,而是输入框、消息列表、键盘和流式响应之间的一次交接。Vercel 的 v0 iOS 技术复盘启发我们从用户能看见的几个瞬间来讲这次移植,再回到支撑这些瞬间的系统设计。
第一条消息先搭好舞台
在助手消息出现之前,应用先记录输入框和视口的几何信息。发出的文字暂时留在独立的动画层中,而真实消息、滚动位置和响应空间在它下面完成提交。等这次交接结束后才显示助手内容,因此不会先闪到目标位置。
气泡的起始位移由气泡高度、视口、输入框和键盘共同决定。我们先把气泡放到记录下来的输入框位置,提交布局,再在后续帧启动 transform。布局和动画不会争抢同一个首帧。
第二条消息才是真正的考验
空白页会掩盖很多问题,连续发送则会把它们暴露出来。原生端把新的用户轮次对齐到顶部,但在移动气泡接管目标位置之前,上一轮内容会保持稳定。Vue Lynx 的 nextTick()先等待待处理操作到达主线程,再用 scrollIntoView完成最终对齐;之后才开始显示助手流。
Web 端没有同样的原生定位能力,因此采用另一套策略:持续更新可观察的 scroll-top,在发送和流式输出期间把消息列表保持在底部。机制不同,但交互仍然符合 Web 用户的预期。
键盘也是布局的一部分
Lynx 的 <input> 元素不会自动避让键盘。输入框始终绝对定位在底部,并监听 keyboardstatuschanged 全局事件。应用通过 setNativeProps 使用事件报告的键盘高度;消息列表只在原本跟随新内容时继续滚动。
把手势反馈留在主线程
抽屉手势和按压反馈使用 Vue Lynx 的主线程脚本。带有 'main thread' 标记的函数直接响应 main-thread-bindtouch* 事件并更新元素样式,无需往返后台线程。普通的 tap 处理器仍在 Vue 后台线程中执行应用操作。
移植笔记
移植保留了原版的数据模型(AI SDK v5 UIMessage parts + UI-message-stream 协议)和几乎全部 composable/页面逻辑,同时用 Lynx 元素重写了与 DOM 耦合的层:
示例目录包含完整的逐特性对照 PRD(65 项移植/适配,10 项注明原因跳过)、记录复用与重写决策及平台坑点的 PORTING.md,以及在 Lynx for Web 上与原版实况对拍的逐屏截图对比。