Nova v3.2.0:会话内消息搜索,和一次搜索栏返工

Nova v3.2.0:会话内消息搜索,和一次搜索栏返工

💡 原文中文,约5900字,阅读约需14分钟。
📝

内容提要

Nova v3.2.0 发布,核心功能为会话内搜索:服务端返回匹配下标,客户端按需补拉上下文并跳转,并处理了目标未加载、无落点等情况。此外新增 notice 提示消息,修复中文标题截断乱码,将 ApiClient 拆分为 api 子包,调整工具卡片样式,并修复 409 重试、错误消息删除等问题。

🔎

延伸解读

搜索跳转的加载策略:按需补拉而非逐页翻

Nova 的会话内搜索由服务端返回匹配下标,客户端再按需补拉上下文。当目标消息不在已加载区间时,客户端一次请求目标上方 20 条到当前起点之间的所有消息,而不是逐页加载。这样跳转到靠前的结果时,避免了多次网络往返,但补拉区间较大时可能带来一次性流量和内存开销。文章也提到,若目标比已加载的最新消息还新,会先触发刷新再定位。

搜索与手动翻页的冲突:用标记和延迟规避

搜索结果跳转与用户手动翻页可能同时触发加载。作者用 searchJumpLoading 标记阻止 triggerLoadOlder 在搜索跳转期间执行,并在 400ms 后重试跳转。这是一种简化处理,代价是跳转可能慢半拍。文章坦言,更彻底的方案是给消息列表加统一的串行加载通道,但本次未实现。读者可留意:在快速连续操作时,搜索跳转的响应可能不够即时。

中文标题截断乱码:UTF-16 码元与码点的区别

删除会话确认弹窗中,中文长标题或含 emoji 的标题可能显示为乱码。原因是 String.length() 和 substring 按 UTF-16 码元计数,一个 emoji 占两个码元,截断可能切在代理对中间,留下孤立代理项。修复方法是按码点截断,并统一了标题、消息预览和 cwd 的截断逻辑。这提醒开发者:处理多语言文本时,应使用码点而非码元来避免截断错误。

ApiClient 拆分与遗留问题:门面模式下的结构优化

v3.2.0 将原本臃肿的 ApiClient 拆分为门面加 api 子包,每个功能一个类,对外方法名不变,调用方无感知。这提升了代码可维护性,但文章指出 ChatActivity 仍高达 106 KB,搜索相关约 300 行又加了进去,尚未按功能拆分。此外,为满足 javac compliance,两个小类被提为独立文件。读者可关注:后续版本是否会继续拆分 ChatActivity。

❓

Q&A

Nova v3.2.0 的会话内搜索是怎么实现的?服务端返回什么?

搜索在服务端进行,chat.nvim 对每条消息的 content 和 reasoning_content 做大小写不敏感的字面量子串匹配,只返回匹配消息的 1-based 升序下标列表(如 {"count":4,"indices":[3,17,42,88]}),不返回正文。客户端拿到下标后先在本地已加载消息中查找,找不到再按需补拉上下文。

搜索跳转时如果目标消息不在内存里,Nova 是怎么处理的?

Nova 不会把整个会话读进内存,currentSince 是已加载区间的起点。当目标下标小于 currentSince 时,会调用 loadOlderForSearchJump,一次补拉 [target-20, currentSince-1] 区间(目标上方保留 20 条上下文),而不是一页一页翻,避免多次往返。反方向(目标比已加载最新一条还新)则先触发刷新,再滚动到目标附近。

搜索时如果目标消息在列表里没有对应项怎么办?

有些 assistant 消息只带 reasoning_content 且被过滤掉,列表里没有对应项,但服务端能搜到。此时 findVisiblePositionByServerIndex 会退到服务端索引最接近的可见项,落在相邻一条上,让用户至少能看到上下文。

Nova v3.2.0 新增的 notice 提示消息是什么?为什么需要它?

chat.nvim 会往会话里写一些只做提示的消息,比如上下文被压缩、API Key 被换掉、自动续写撞到次数上限。这些不是对话内容,以前 Nova 只认 user、assistant、tool、error 四种类型,这类消息走不进任何分支,屏幕上什么都不显示,用户会看到会话空了一段。现在新增 notice 类型,居中显示一条窄卡片,使用单独的 drawable 背景,不参与左右对齐。

中文标题被截断成乱码的原因是什么?怎么修复的?

原因是 String.length() 数的是 UTF-16 code unit,一个 emoji 占两个 unit,substring(0,30) 可能从代理对中间切开,留下孤立代理项导致乱码。修复方法是新增 truncateByCodePoints 方法,用 codePointCount 和 offsetByCodePoints 按码点截断;标题和消息预览从头截断,cwd 从尾部截断只留最后 30 个码点。

ApiClient 拆成 api 子包后结构有什么变化?对外调用受影响吗?

原来所有请求堆在 ApiClient 一个类里,现在拆成一个门面加 api 子包,一个功能一个类:MessageApi、SessionApi、SessionSettingsApi、SessionActionApi、ChatApi、ProviderApi、UploadApi、BridgeApi、WeChatApi、ServerApi、ApiConfig。ApiClient 只剩转发和回调定义,对外方法名一个没改,调用方完全无感知。

Nova v3.2.0 修复了哪些其他问题?

主要修复包括:POST /session/new 收到 409 时自动重试一次;服务端已确认的 error 消息可以从长按菜单删除;删账号或服务器连不上时会话列表会重新刷新;开发版检查更新不再跳过已发布的正式版。此外还调整了工具卡片样式,修复了行内代码折行时背景粘连的问题。

🏷️

标签

➡️

继续阅读