fix(web): stale-closure + focus trap + 去边界强转 + a11y/性能打磨 + 类型化客户端

P1-6 useStyleLearn/useKimiOauth 修 stale-closure 依赖。
P1-7 CommandPalette/Drawer 加 focus trap(新 lib/a11y/focusTrap)。
P1-8 SSE 解析与 server.ts 去 as 强转改类型守卫/运行时校验。
P2 删冗余强转、开 noUncheckedIndexedAccess、Toast 清理+稳定 key、列表 key 稳定化、
  rel=noopener、useMemo 大文本、ConflictCard role=group、useInjection 加锁、
  client.test 真断言。
codegen 重生成 schema.d.ts + 消费 DimensionEntry/ReviewConflictView 强类型。
This commit is contained in:
Yaojia Wang
2026-06-21 19:32:49 +02:00
parent 016509c5c6
commit c6651b74b9
36 changed files with 792 additions and 140 deletions

View File

@@ -0,0 +1,41 @@
import { describe, expect, it } from "vitest";
// P1-6 回归守卫学文风轮询完成done 边沿effect 必须用「最近一次 learn 传入的
// projectId」去拉指纹而非闭包捕获的旧值。useStyleLearn 用 ref 追踪 projectId 来保证这点。
//
// 该 hook 依赖浏览器/React 渲染node 环境无法整体跑;这里以最小模型固化 ref 追踪契约:
// 切项目(连续两次 learndone 时读到的应是后者,不会是首次的陈旧值。
describe("useStyleLearn projectId 追踪P1-6 stale-closure 守卫)", () => {
// 复刻 hook 内部learn 同步写 refdone 时同步读 ref。
function makeTracker() {
const projectIdRef: { current: string | null } = { current: null };
return {
// learn(pid):受理时记录最近项目。
learn(pid: string): void {
projectIdRef.current = pid;
},
// onDone():轮询完成时取用于拉指纹的项目(应为最新)。
onDone(): string | null {
return projectIdRef.current;
},
};
}
it("done 时用最近一次 learn 的 projectId切项目后不复用旧值", () => {
// Arrange
const t = makeTracker();
// Act先 learn 项目 A再切到项目 B模拟用户换项目重学此时 A 的轮询尚未完成。
t.learn("project-A");
t.learn("project-B");
const used = t.onDone();
// Assertdone 边沿读到的是 B而非闭包里陈旧的 A。
expect(used).toBe("project-B");
});
it("未 learn 时为 null不误拉指纹", () => {
const t = makeTracker();
expect(t.onDone()).toBeNull();
});
});