Skip to content

feat(search): Obsidian-style global search with file/folder targeting#494

Open
KevinYoung-Kw wants to merge 13 commits intoop7418:mainfrom
KevinYoung-Kw:feat/global-search
Open

feat(search): Obsidian-style global search with file/folder targeting#494
KevinYoung-Kw wants to merge 13 commits intoop7418:mainfrom
KevinYoung-Kw:feat/global-search

Conversation

@KevinYoung-Kw
Copy link
Copy Markdown
Contributor

@KevinYoung-Kw KevinYoung-Kw commented Apr 15, 2026

Summary

在原有“仅搜索会话名”的基础上,升级为 Obsidian 风格的全局搜索:支持搜索会话、消息内容、文件、目录,并附带上下文预览和直达定位。

Scope 说明(更新):

  1. 本 PR 交付的是稳定可用的全局搜索 UI/UX,以及文件/目录定位链路。
  2. session 内 message 精确滚动定位(点击消息后在同一会话内滚动并高亮到某条消息)当前不在本 PR 范围内
  3. 原因是该能力仍受已知滚动竞争问题影响(与会话区自动滚动/StickToBottom 存在 race condition),为避免引入不稳定体验,暂不在本 PR 合入。

与原搜索的区别

维度 之前 现在
搜索范围 只能搜 会话标题 会话标题、消息内容文件名目录名
结果展示 平铺 10 条混合结果,看不出消息属于哪个会话 消息按 会话折叠分组;文件、会话各自独立成组
命中预览 显示消息 snippet,关键词高亮
文件定位 点击后自动打开文件树、展开目录、滚动到目标节点并闪烁高亮
global-search-demo

设计思路

1. 搜索结果分组展示

之前搜到 10 条消息时,根本不知道它们来自哪几个会话。现在消息按会话折叠分组,文件和会话也各自成组,扫一眼就能定位到想找的上下文。

2. 结果 preview 更友好

  • 对话框放宽到 sm:max-w-3xl,snippet 能显示更多内容
  • 固定 min(80vh, 520px) 高度,输入框始终置顶,不会因结果加载而上下跳动
  • 命中关键词用主题色高亮,一眼知道为什么匹配
  • 用图标区分 human / assistant / tool / file / folder

3. 文件/目录可搜索、可直达

  • 之前只能搜到文件,现在目录也可以被搜索(比如只记得 tests/ 但不记得具体文件名)
  • 点击后自动打开右侧文件树、展开父目录、滚动到目标节点并闪烁高亮
  • 考虑到文件树会在 AI 流式输出结束后自动刷新,加了 seekKeyRef 保护,防止刷新后把用户正在手动浏览的滚动位置又拽回高亮节点

4. 搜索前缀更顺手

提示文案改成单数 session: / message: / file:,API 同时兼容复数旧写法。snippet 截取策略把关键词往前靠,避免被单行 truncate 截掉。


Follow-up fixes (post review)

根据 UI/UX 验收反馈,补充了以下稳定性与可用性修复:

  1. 默认全局搜索补齐文件/目录维度

    • scope=all 现在也会返回 files(不再需要必须写 file: 才能命中)
  2. 文件 deep-link 不再被默认面板初始化覆盖

    • 从搜索结果进入 ?file= 时,优先保持文件树打开,避免“点了结果但文件树被关掉”
  3. 关闭搜索弹窗时中止 in-flight 请求

    • close/unmount 都会 abort 当前请求,避免弹窗重开后出现残留旧结果
  4. 关键词高亮对前缀查询生效

    • session:xxx / message:xxx / file:xxx 会用解析后的实际关键词 xxx 进行高亮
  5. 消息分组折叠头支持键盘操作

    • 用可聚焦的 CommandItem 承载折叠逻辑,支持 command 列表内键盘导航与回车切换
  6. IME 结束时避免重复请求

    • 去掉 onCompositionEnd 里的即时搜索,统一走去抖查询
  7. 文件树重复定位与跨会话定位稳定性修复

    • 引入 seek 标识并将定位键扩展为 path + seek (+ workingDirectory),避免第二次同路径跳转被误判为“已定位”
    • 在工作目录切换时清理陈旧 seek 状态,仅在真实命中节点后才标记完成定位
  8. 更新检查接口降级处理

    • 当上游 release 拉取失败时,/api/app/updates 返回可降级响应,避免前端出现 502 噪音

v0.50.3 验收更新

已将本 PR 分支对齐到 v0.50.3 基线后重新验收:

  • npm run test 通过(1047/1047)
  • npm run build 通过
  • playwrightglobal-search-file-seek.spec.ts 通过
  • playwrightglobal-search-modes.spec.ts 通过

结论:在 v0.50.3 上,当前 PR 覆盖范围内的搜索功能与 UI/UX 未发现新增回归。


Test plan

  • 搜索关键词 → 消息按会话分组、图标正常
  • 默认搜索(不加前缀)可返回文件/目录结果
  • file:xxx → 仅显示文件/目录结果
  • 点击文件/目录 → 文件树自动展开并定位到目标
  • 同一 session 重复点击同一路径结果可重复定位
  • 跨 session 点击文件结果可正确定位
  • 树自动刷新后,手动滚动不再被抢回
  • 缩放窗口 → 对话框高度平滑变化,无跳动
  • 前缀查询下关键词高亮正确
  • 键盘可切换消息分组折叠

Relates to #482


Latest incremental update

  • 新增:当输入命中有效前缀(session: / message: / file:)时,在搜索框下方显示主题色范围提示条(含当前 scope 文案与前缀标识),降低“当前正在搜哪一类内容”的认知负担。

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 15, 2026

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

KevinYoung-Kw and others added 12 commits April 16, 2026 09:11
- Add /api/search endpoint supporting scoped queries (sessions:, messages:, files:) and default cross-dimension search
- Add GlobalSearchDialog using cmdk CommandDialog with grouped results
- Add Cmd/Ctrl+K shortcut via useGlobalSearchShortcut hook
- Wire ChatListPanel search button to open global search
- Remove legacy session-only search dialog from ChatListPanel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redesign the global search dialog with session-grouped messages,
Obsidian-style previews, and larger dialog sizing.

- GlobalSearchDialog: group messages by session with foldable groups;
  distinguish user/assistant/tool via icons; enlarge to sm:max-w-3xl;
  highlight matched keyword in snippet with primary color
- File/Folder search: pass ?file=path&q=query; auto-open file tree,
  expand parent folders and target directory, scroll and flash-highlight
  the matched item (files and directories both supported)
- Search API:兼容单数前缀 (session:/message:/file:) and return
  contentType for icon selection; folders are now searchable
- Snippet generation: bias keyword toward the front so it survives
  single-line truncation in the UI list
- i18n: add globalSearch.toolLabel for zh/en

Relates to op7418#482

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Give CommandDialog a fixed height (h-[60vh] max-h-[600px]) so the
overall dialog no longer expands and contracts as results appear.
Remove max-h from CommandList and let it fill remaining space with
flex-1, so only the result list scrolls while the input stays put.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…arning

- Use h-[min(80vh,520px)] for smooth viewport scaling instead of
  breakpoint-based hard switch
- Override CommandList default max-h-[300px] with max-h-none so
  results fill the entire dialog and the bottom white area is gone
- Replace <button> in CommandGroup heading with <div> to silence
  the aria-hidden/focus browser warning
- Remove unused FolderOpen import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dings

Apply bg-muted/40, rounded corners, and font-medium text-foreground
to session-level message group headers so they visually separate
from individual message items and create clearer hierarchy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…animations

Switch AIFileTree from defaultExpanded to controlled expanded so that
changing highlightPath actually opens parent folders in real time.
Add polling (100ms × 15) instead of a single setTimeout so the
scroll-to-highlight waits for Collapsible animation to finish.
Reset flash tracker on highlightPath change to avoid stale state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…reshes

Replace the global hasFlashedRef flag with a seekKeyRef tied to the
specific highlightPath. This stops the polling interval from restarting
whenever the file tree auto-refreshes (e.g. after streaming ends), which
was causing users to be snapped back to the highlighted file while they
were manually scrolling.

Also removes the unnecessary loading dependency from the scroll effect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- add seek token on file result navigation

- use reactive search params in chat/file-tree panels

- key file-tree seeking by path+seek token

- degrade update API to no-update payload on upstream failures
- avoid consuming seek key before target is found

- include workingDirectory in seek key

- clear stale tree state on project switch

- add Playwright regression for repeated + cross-session file seeks
- cover all/session/message/file search modes

- seed sessions/messages/files deterministically

- make Cmd/Ctrl+K open global search even while editing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant