Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
2515da2
feat(search): add conversation message content search
pia Aug 15, 2026
1efe210
docs(search): design search hit jump and flash highlight
pia Aug 15, 2026
e3d6df3
feat(search): jump to search hits and flash highlight
pia Aug 15, 2026
12e6e54
fix(search): finish index progress and stabilize hit navigation
pia Aug 15, 2026
e2d6c7f
fix(search): index full turns and trim dead result fields
pia Aug 16, 2026
345d2a2
docs(search): add search dialog and result screenshots
pia Aug 16, 2026
95d6979
Merge remote-tracking branch 'upstream/main' into feat/message-conten…
pia Aug 16, 2026
7b12f4d
docs: update agent integration cost model in strategy memo
asteroida123 Aug 16, 2026
51d8a6e
docs: record the perf audit and qoder integration status in the strat…
asteroida123 Aug 17, 2026
8f9dba0
feat(pk): agent PK arena — one task, N agents, side by side
asteroida123 Aug 17, 2026
14f0f86
fix(pk): gate the arena launcher on a real git repo
asteroida123 Aug 17, 2026
24167fc
fix(pk): drive the battle panes by connection id and settle on turn_c…
asteroida123 Aug 17, 2026
7ca83bb
fix(pk): recover worktree creation from an unborn HEAD
asteroida123 Aug 17, 2026
e3d682a
feat(pk): round-level permission presets in the launcher
asteroida123 Aug 17, 2026
425a180
fix(pk): stop the arena crawl, memoize battle panes, add a way back in
asteroida123 Aug 17, 2026
edb8761
fix(pk): apply the round permission mode from the by-id entry
asteroida123 Aug 17, 2026
4a9fea1
feat(pk): bare mode — instruct contestants to use no skills
asteroida123 Aug 17, 2026
254f2f5
feat(pk): ready phase — per-contestant model and reasoning effort pic…
asteroida123 Aug 17, 2026
2c8c159
fix(pk): wait for the specific selector field, not either
asteroida123 Aug 18, 2026
c009ab9
fix(pk): field-priority poll across both connection entries
asteroida123 Aug 18, 2026
d2cf291
fix(pk): guard against accidental arena close; disconnect contestants…
asteroida123 Aug 18, 2026
bd25a15
fix(pk): base-branch diff, round-status self-heal, always-visible PK …
asteroida123 Aug 18, 2026
4bb92f6
feat(pk): minimize-to-pill background mode; lift the contestant cap to 8
asteroida123 Aug 18, 2026
2c728ef
docs: sync the strategy memo; ignore local ZCode session artifacts
asteroida123 Aug 18, 2026
e54f6f9
feat(pk): export a finished round as a self-contained HTML report
asteroida123 Aug 18, 2026
086c429
fix(pk): keep arena connections alive, settle swept turns as failures…
asteroida123 Aug 18, 2026
36eafa7
fix(reasoning): render paragraphs as div to stop nested-<p> hydration…
asteroida123 Aug 18, 2026
0acabd8
feat(pk): preflight contestants at launch; abandon-ready closes clean…
asteroida123 Aug 18, 2026
d07319d
feat(pk): wrap up — delete rounds and one-tap rematch from the last s…
asteroida123 Aug 18, 2026
4a28a3e
fix(search): repair FTS queries, scoped filtering, and ranking
pia Aug 18, 2026
baf241b
fix(search): make indexing lifecycle robust and privacy-aware
pia Aug 18, 2026
5c321a2
fix(search): align hit navigation with backend offsets
pia Aug 18, 2026
0b2da75
Merge remote-tracking branch 'upstream/main' into feat/message-conten…
pia Aug 18, 2026
d652a5f
chore(search): adapt to upstream merge and normalize formatting
pia Aug 18, 2026
b6880b2
Merge remote-tracking branch 'upstream/main' into feat/agent-pk-arena
asteroida123 Aug 19, 2026
9f11576
feat(pk): persist rounds to DB instead of localStorage
asteroida123 Aug 19, 2026
56a956b
feat(pk): quick-start templates + judge agent auto-scoring
asteroida123 Aug 19, 2026
61a3070
feat(pk): control-variable PK — same agent in multiple slots
asteroida123 Aug 19, 2026
6791b77
feat(pk): real-engineering PK — pull tasks from git commits
asteroida123 Aug 19, 2026
b9e0d6b
fix(pk): gate Tauri command wrappers behind tauri-runtime feature
asteroida123 Aug 19, 2026
18712ee
docs(pk): 40 detailed test cases for PK arena features
asteroida123 Aug 19, 2026
da961ba
fix(pk-arena): fix multi-agent concurrency settling and permission mo…
asteroida123 Aug 19, 2026
542bbbc
docs(pk): record 11 confirmed issues with source evidence
asteroida123 Aug 19, 2026
4a84fa6
docs(pk): add issue #0 — server mode contestants stuck at ready, judg…
asteroida123 Aug 19, 2026
e936cf0
docs(pk): add issue #12 — server mode cannot open/reveal files in folder
asteroida123 Aug 19, 2026
0e5f71d
docs(pk): add issue #13 — arena dialog cannot be closed, only minimized
asteroida123 Aug 19, 2026
d9e9a22
docs(pk): add issue #14 — session management UI needs redesign
asteroida123 Aug 19, 2026
541d29e
docs(pk): add issue #15 — battle/diff columns fixed w-80, not responsive
asteroida123 Aug 19, 2026
293fcaa
docs(pk): add issue #16 — report token/turns columns all show '—'
asteroida123 Aug 19, 2026
f863b68
docs(pk): add issues #17 #18 — templates need Chinese prompts + black…
asteroida123 Aug 19, 2026
16e6a04
fix(pk): #0 选手完成后状态卡 ready,裁判不触发
asteroida123 Aug 19, 2026
fb83dd1
fix(pk): #4 裁判评分持久化到 DB
asteroida123 Aug 19, 2026
2a5230d
fix(pk): #1 取消回合后也触发裁判
asteroida123 Aug 19, 2026
54ef423
fix(pk): #14b PK 会话标题锁定,防止 agent auto-title 覆盖
asteroida123 Aug 19, 2026
99d816c
fix(pk): #2 导出报告加入裁判评分板块
asteroida123 Aug 19, 2026
88b2a9b
fix(pk): #3 截图包含裁判评分面板
asteroida123 Aug 19, 2026
aca7883
fix(pk): #15 battle/diff 列自适应宽度
asteroida123 Aug 19, 2026
ec0e52c
fix(pk): #13 arena 对话框始终显示关闭按钮
asteroida123 Aug 19, 2026
897c58f
fix(pk): #16 hydrate 时回填已完成选手的 usage
asteroida123 Aug 19, 2026
33048a0
fix(pk): #8 裁判可手动重跑
asteroida123 Aug 19, 2026
adfd556
feat(pk): #17 模板提示词改中文 + 新增黑洞效果模板
asteroida123 Aug 20, 2026
44f7dec
feat(pk): 重写导出报告——渐变 hero + 卡片式排名 + 颁奖台式裁判
asteroida123 Aug 20, 2026
d0f24c0
feat(pk): #18 模板和从提交拉取分区 + commit 列表增强
asteroida123 Aug 20, 2026
f5cef3e
docs(pk): 更新优先级表,标记 12 个已修复问题
asteroida123 Aug 20, 2026
08bed1d
fix(pk): #12 server 模式隐藏'在系统文件管理器打开'入口
asteroida123 Aug 20, 2026
d3d5ccb
feat(pk): #10 裁判评分维度可配置
asteroida123 Aug 20, 2026
02d411e
feat(pk): #14a 回合切换下拉框显示状态+任务+冠军
asteroida123 Aug 20, 2026
07f7801
feat(pk): #6 控制变量 UI——launcher 选手选择改槽位模式
asteroida123 Aug 20, 2026
ee0d7d2
feat(pk): #7 任务来源加'从改动拉取'
asteroida123 Aug 20, 2026
2b58d00
feat(pk): 统一起点选择——删'从改动拉取',commit 不过滤,worktree 从父提交切出
asteroida123 Aug 20, 2026
7dd2b23
feat(pk): 多轮介入——arena 每个选手可追加输入
asteroida123 Aug 20, 2026
3d1c493
feat(pk): #14c PK 会话侧边栏可见——按 round 分组显示选手会话
asteroida123 Aug 20, 2026
10fd975
fix(pk): 修复创建回合 NOT NULL 约束失败 + launcher 旧格式数据崩溃
asteroida123 Aug 20, 2026
d056dfe
feat(pk): improve arena history reports and lifecycle
asteroida123 Aug 20, 2026
464641f
fix(pk): stabilize judge slots and runnable reports
asteroida123 Aug 20, 2026
782ad7b
fix(pk): restore rounds and export playable reports
asteroida123 Aug 20, 2026
3f0df67
fix(pk): preserve ready rounds when closing arena
asteroida123 Aug 20, 2026
7352fc5
fix(pk): keep report export compatible with es2020
asteroida123 Aug 20, 2026
ce1d0fb
fix(pk): reconcile canceled rounds and restore entry
asteroida123 Aug 20, 2026
f4eb68c
fix(pk): keep shared reports runnable in sandbox
asteroida123 Aug 20, 2026
dc200c0
refactor(pk): remove redundant titlebar entry
asteroida123 Aug 20, 2026
407f57e
fix(pk): order round migration safely
asteroida123 Aug 21, 2026
fd2bad6
docs(pk): add arena screenshots
asteroida123 Aug 21, 2026
9605728
fix(pk): preserve runnable historical reports
asteroida123 Aug 21, 2026
3d2d2d8
feat(pk): integrate arena rounds into workspace
asteroida123 Aug 21, 2026
5c168c5
fix(pk): resolve desktop report state from app handle
asteroida123 Aug 21, 2026
02d7a7e
Merge remote-tracking branch 'upstream/main' into feat/agent-pk-arena
asteroida123 Aug 21, 2026
6cb1499
docs(pk): refresh PR screenshots
asteroida123 Aug 21, 2026
2c240f5
fix(pk): hide minimized pill for terminal rounds
asteroida123 Aug 21, 2026
04030eb
Merge remote-tracking branch 'upstream/main' into feat/message-conten…
pia Aug 21, 2026
6066d1c
Merge remote-tracking branch 'upstream/main' into feat/agent-pk-arena
asteroida123 Aug 25, 2026
a14cec9
feat(pk): 报告文案抽离多语言模块、归档快照清理容错与保存失败重试
asteroida123 Aug 25, 2026
9810b1a
fix(path): stop mixing separators in Windows path display
xintaofei Aug 25, 2026
188406b
Merge remote-tracking branch 'origin/main'
xintaofei Aug 25, 2026
4febb21
Merge remote-tracking branch 'upstream/main' into feat/agent-pk-arena
asteroida123 Aug 25, 2026
1bb6909
feat(tasks): offer the worktree cleanup on the delivery acceptance
xintaofei Aug 25, 2026
4dee957
Merge pull request #534 from asteroida123/feat/agent-pk-arena
asteroida123 Aug 25, 2026
46f2bdd
Merge remote-tracking branch 'origin/main'
xintaofei Aug 25, 2026
b7f956a
fix(search): satisfy clippy 1.98 in schema test
pia Aug 25, 2026
27a77af
Revert "feat(pk): 增加多智能体竞技场"
xintaofei Aug 25, 2026
12abd0f
Merge pull request #574 from xintaofei/revert-534-feat/agent-pk-arena
xintaofei Aug 25, 2026
5b4038c
Merge remote-tracking branch 'upstream/main' into feat/message-conten…
pia Aug 25, 2026
83f14a0
style(search): format merged search detail panel
pia Aug 25, 2026
72da71f
Merge remote-tracking branch 'upstream/main' into feat/message-conten…
pia Aug 25, 2026
b91cec1
Merge remote-tracking branch 'upstream/main' into feat/message-conten…
pia Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/assets/search-box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/search-results.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
238 changes: 238 additions & 0 deletions docs/plans/2026-08-14-message-content-search-implementation.md

Large diffs are not rendered by default.

427 changes: 427 additions & 0 deletions docs/specs/2026-08-14-message-content-search-design.md

Large diffs are not rendered by default.

227 changes: 227 additions & 0 deletions docs/specs/2026-08-15-search-result-jump-highlight-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# 搜索命中跳转与闪烁高亮设计

## 背景

当前 Ctrl+K 搜索已经能在会话标题和正文中找到关键词。用户点击搜索结果后进入会话时,还看不到关键词在哪条消息中,也无法快速定位。

本次新增交互目标:

1. 点击搜索结果后自动滚动到第一条命中的正文位置,并让该位置停在屏幕中部附近。
2. 命中的关键词以黄色闪烁后渐隐。
3. 如果命中的是标题,则高亮标题中的关键词并闪烁。
4. 如果正文有多条命中,提供“下一条匹配”按钮,支持循环跳转。

## 范围

- 只针对 Ctrl+K 会话搜索结果进入会话后的跳转。
- 不改变现有标题搜索、正文搜索、文件搜索和索引策略。
- 不持久保存高亮状态,高亮动画结束后恢复普通文本。
- 正文匹配只处理 User/Assistant 文本块;工具、图片、系统提示、思考内容不参与。

## 交互选择

用户已确认选择方案 B:

- 自动定位并闪烁第一条正文命中;
- 显示“下一条匹配”按钮;
- 多条命中按会话顺序循环跳转;
- 标题命中直接高亮标题,不显示“下一条”。

## 架构

### 后端

新增精确匹配位置模型,并返回给前端。

#### 1. 规范化文本块偏移表

`message_search_document` 增加一个 `block_offsets` JSON 字段。每个索引文本块保存:

```json
[
{
"turn_id": "turn-01",
"block_index": 0,
"start": 0,
"end": 120,
"leading_trim": 3
}
]
```

字段含义:

- `turn_id`:后端 `MessageTurn.id`。
- `block_index`:该 turn 中的 Text block 索引。
- `start` / `end`:在规范化全文中的字符区间,半开区间。
- `leading_trim`:原始 block 文本开头被 `trim()` 去掉的字符数,用于把规范化区间映射回原始 block。

### 2. 搜索结果模型

`DbConversationSearchResult` 增加:

```rust
pub matches: Vec<SearchMatchLocation>,
pub total_match_count: u32,
```

`SearchMatchLocation`:

```rust
pub enum SearchMatchLocationKind {
Title,
Content,
}

pub struct SearchMatchLocation {
pub kind: SearchMatchLocationKind,
pub turn_id: Option<String>,
pub block_index: Option<usize>,
pub char_start: usize,
pub char_end: usize,
}
```

排序规则:

- 标题命中排在最前。
- 正文命中按 `turn_id` 在会话中的顺序排列;同一 turn 中按 `block_index` 和字符位置排序。
- 结果最多返回 200 条匹配位置,避免极端大文档导致响应膨胀。
- 标题命中只包含 `kind=title`,不包含 `turn_id`/`block_index`。

### 3. 查询路径

- 搜索服务读取最终结果对应的 `message_search_document`,使用 `block_offsets` 和规范化文本计算所有命中位置。
- 查询路径不读取原始转录文件,保持既有约束。
- `snippet_prefix/match/suffix` 继续由第一条内容命中生成。
- `content_match_count` 改为实际统计值,但显示和返回仍可受上限保护。

### 4. 索引与迁移

- 在现有迁移之后新增一个小迁移,给 `message_search_document` 增加 `block_offsets` 字段。
- 将 `search_index_state.schema_version` 提升到 2,触发已有索引重新构建。
- 索引器在 `normalize_turns` 时同时生成 block offset 清单,并在 upsert 文档时写入。
- 旧文档如果没有 `block_offsets`,搜索时降级为“只返回 snippet,不返回精确跳转”,前端只显示无跳转结果,避免破坏搜索。

### 前端

#### 1. 搜索焦点状态

新增一个轻量级搜索焦点状态,保存从搜索结果到会话页的传递信息:

```ts
interface SearchFocus {
conversationId: number
query: string
matches: SearchMatchLocation[]
activeMatchIndex: number
}
```

搜索对话框选择会话时:

1. 把 `SearchFocus` 写入状态;
2. 照常调用 `openTab`;
3. 对话框关闭。

状态不写入持久化存储。

#### 2. 会话详情加载与跳转

`ConversationDetailPanel` / `MessageListView` 读取当前会话的 `SearchFocus`:

- 等待 detail 加载完成。
- 根据 `turn_id` 找到对应 timeline item 的索引。
- 如果目标 turn 不在当前加载窗口:
- 调用 `loadOlderTurns`;
- 最多尝试固定次数;
- 超过上限则停止并清除搜索焦点。
- 使用虚拟列表的 `scrollApiRef.scrollToIndex(index, { align: "center" })` 或等价接口滚动。
- 滚动完成后再触发高亮。

#### 3. 正文高亮

- 只对当前激活匹配对应的 turn/block 做高亮。
- 高亮操作在渲染后的 DOM 文本节点中进行:
- 找到目标 block 的文本范围;
- 用 `TreeWalker` / 文本节点扫描定位关键词;
- 把命中的文本节点拆开并包成 `<mark data-search-flash>`。
- 不修改 Markdown 解析器和内容数据,避免影响复制、导出、编辑和后续流式更新。
- 动画结束或切换到下一条时移除旧 `<mark>`。

#### 4. 下一条匹配

- 仅当正文匹配数量大于 1 时显示按钮。
- 按钮放在消息列表右侧或滚动按钮附近。
- 点击后:
- `activeMatchIndex = (activeMatchIndex + 1) % matches.length`;
- 滚动到新位置;
- 重新闪烁。
- 按钮显示当前序号和总匹配数,例如 `2 / 8`。

#### 5. 标题命中

- 标题命中 `turn_id` 为空。
- `ConversationDetailHeader` 根据 `char_start` / `char_end` 将标题子串包成 `<mark data-search-flash>`。
- 使用与正文相同的闪烁动画。
- 标题命中不触发消息列表滚动,也不显示“下一条”。

#### 6. 动画

CSS keyframes:

```css
@keyframes search-flash {
0% { background-color: #fde047; }
100% { background-color: transparent; }
}

[data-search-flash] {
animation: search-flash 1.8s ease-out forwards;
}
```

动画结束后:

- 移除 `<mark>` 或将其样式恢复为透明;
- 不残留黄色背景。

## 错误处理

- 会话已删除:清除 `SearchFocus`,不阻止会话打开。
- 索引状态落后或匹配位置不可用:只滚动到对应消息,降级为不高亮。
- 找不到对应 DOM 文本:不清除会话状态,只跳过闪烁。
- 多条匹配中存在已被流式内容替换的位置:重新按当前 DOM 扫描一次;失败则跳到下一条。

## 兼容性

- 没有 `block_offsets` 的旧文档仍可被搜索,只是不能精确跳转。
- 关闭正文搜索时不会产生正文匹配。
- 不改变文件搜索、空查询、文件夹和 Agent 过滤行为。
- Web 客户端和桌面客户端共用同一套数据结构和前端状态。

## 测试计划

### Rust

- `block_offsets` JSON 序列化/反序列化。
- Unicode 字符区间、`leading_trim` 映射。
- 标题命中和正文命中排序。
- 同一 turn 多个 block、同一 block 多次命中。
- 旧文档缺少 offset 时的降级。

### 前端

- 选择搜索结果后写入 `SearchFocus`。
- 等待 detail 后调用滚动接口。
- “下一条”按钮循环和计数。
- 正文 `mark` 动画类添加和移除。
- 标题命中的 `mark` 渲染。
- 找不到目标时的降级行为。

## 不做什么

- 不做跨会话的全局高亮。
- 不做关键词在多个消息中的“全部标黄”。
- 不做持久化搜索焦点。
- 不改变搜索排序和 50 条上限。
4 changes: 4 additions & 0 deletions src-tauri/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ pub struct AppState {
/// The upgrade UI subscribes to it and re-syncs from a snapshot on mount,
/// so download progress survives settings-page navigation and reloads.
pub update_state: crate::update::AppUpdateStateHandle,
/// Background content-search indexer. Optional in tests and in
/// desktop-mode HTTP routers that cannot see a Tauri-managed instance.
pub search_indexer: Option<Arc<crate::search::indexer::MessageSearchIndexer>>,
}

pub fn default_system_op_lock() -> Arc<tokio::sync::Mutex<()>> {
Expand Down Expand Up @@ -247,6 +250,7 @@ impl AppState {
chat_authoring_config,
system_op_lock: default_system_op_lock(),
update_state: default_update_state(),
search_indexer: None,
}
}
}
Loading
Loading