优化项目对话、刷新续流、实时滚动与工具状态恢复 (#245)

* feat(chat): add project-based conversation sidebar

* feat(chat): refine Codex-style conversation UI

* feat(chat): add Codex-style conversation workflow

* feat(ui): 优化对话框与项目侧边栏交互

* fix(chat): 修复暗色输入框圆角填色

* fix(chat): 恢复输入区分层错位布局

* fix(hitl): isolate reviewer state per conversation

* feat(hitl): 增加双入口审批与倒计时进度

* fix(hitl): 汇总项目审批并隔离对话状态

* fix(ui): 修复审批状态与无项目新任务

* fix(ui): 优化审批状态与对话切换性能

* fix(ui): 修复中断任务审批仍计时

* fix(ui): 修复多对话并发切换卡顿

* fix(hitl): 主动同步审批并关闭中断状态

* fix(ui): 固定项目审批汇总为绿色

* feat(ui): 同步系统模型与推理强度

* feat(ui): 完善项目侧栏预览与新建入口

* fix(ui): 防止无项目文件夹误展开

* fix(ui): 防止长历史对话滚动误触审批

* fix(ui): 修复对话操作并补充项目置顶

* fix(ui): 移除对话分组并调整项目置顶排序

* feat(ui): 优化迭代导航与审批交互

* fix(hitl): 将 write_file 加入内置免审批工具

* fix(chat): 支持回车发送与 Shift 回车换行

* fix(chat): 优化对话刷新与 Codex 风格交互

* fix(ui): 显示对话具体更新时间

* fix(ui): 优化对话刷新与项目加载

* fix(ui): 修复 Agent 审查文字裁切

* fix(chat): 修复刷新续流与多标签页同步

* fix(chat): 修复滚动跟随与中断任务终态

* fix(ui): 修复流式滚动跳动与工具状态恢复

* fix(ui): 修复刷新后流式输出停止粘底
This commit is contained in:
RuoJi6
2026-08-13 21:28:37 +08:00
committed by GitHub
parent b170f2c4b1
commit eb6bab574f
31 changed files with 10763 additions and 886 deletions
@@ -3,6 +3,7 @@ package database
import (
"path/filepath"
"testing"
"time"
"go.uber.org/zap"
)
@@ -105,6 +106,63 @@ func TestProcessDetailsSummaryDoesNotReportPersistedOrphanAsRunning(t *testing.T
}
}
func TestProcessDetailsSummaryIncludesPersistedTurnTiming(t *testing.T) {
db, _, messageID := setupProcessDetailsSummaryTest(t)
startedAt := "2026-08-10T08:00:00Z"
completedAt := "2026-08-10T08:12:59Z"
if _, err := db.Exec(
"UPDATE messages SET content = ?, created_at = ?, updated_at = ? WHERE id = ?",
"done", startedAt, completedAt, messageID,
); err != nil {
t.Fatalf("update message timing: %v", err)
}
summary, err := db.GetProcessDetailsSummary(messageID)
if err != nil {
t.Fatalf("GetProcessDetailsSummary: %v", err)
}
if summary.Status != "completed" {
t.Fatalf("status = %q, want completed", summary.Status)
}
if summary.StartedAt == nil || summary.CompletedAt == nil {
t.Fatalf("timing missing: %#v", summary)
}
if want := int64((12*time.Minute + 59*time.Second) / time.Millisecond); summary.DurationMs != want {
t.Fatalf("durationMs = %d, want %d", summary.DurationMs, want)
}
}
func TestProcessDetailsSummaryTreatsCancelledPlaceholderAsTerminal(t *testing.T) {
db, conversationID, messageID := setupProcessDetailsSummaryTest(t)
startedAt := "2026-08-10T08:00:00Z"
if _, err := db.Exec(
"UPDATE messages SET content = ?, created_at = ?, updated_at = ? WHERE id = ?",
"处理中...", startedAt, startedAt, messageID,
); err != nil {
t.Fatalf("update running placeholder: %v", err)
}
if _, err := db.Exec(`
INSERT INTO process_details (id, message_id, conversation_id, event_type, message, data, created_at)
VALUES ('cancelled-detail', ?, ?, 'cancelled', 'interrupted', '{}', '2026-08-10T08:02:05Z')`,
messageID, conversationID); err != nil {
t.Fatalf("insert cancelled detail: %v", err)
}
summary, err := db.GetProcessDetailsSummary(messageID)
if err != nil {
t.Fatalf("GetProcessDetailsSummary: %v", err)
}
if summary.Status != "cancelled" {
t.Fatalf("status = %q, want cancelled", summary.Status)
}
if summary.CompletedAt == nil {
t.Fatal("cancelled summary should expose a fixed completion time")
}
if want := int64((2*time.Minute + 5*time.Second) / time.Millisecond); summary.DurationMs != want {
t.Fatalf("durationMs = %d, want %d", summary.DurationMs, want)
}
}
func setupProcessDetailsSummaryTest(t *testing.T) (*DB, string, string) {
t.Helper()
db, err := NewDB(filepath.Join(t.TempDir(), "process-details.db"), zap.NewNop())