Files
writer-work-flow/apps/web/components/review/AnnotatedText.tsx
Yaojia Wang c6651b74b9 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 强类型。
2026-06-21 19:32:49 +02:00

55 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import type { ReviewConflict } from "@/lib/review/sse";
interface AnnotatedTextProps {
text: string;
conflicts: ReviewConflict[];
// 当前聚焦的冲突下标(来自报告卡「跳转」),用于锚点联动。
focusedIndex: number | null;
onAnchorClick: (index: number) => void;
}
// 正文就地标注UX §8.3 / §6.1)。
// M2 占位R6`where` 是文字定位如「第4段」M2 不做精确字符 range —
// 改为段级/锚点联动:每个冲突渲染为一枚朱砂波浪线锚点挂在正文上方,点击=回跳报告卡。
// 精确 inline range 标注留 M3+(需后端给字符 offset
export function AnnotatedText({
text,
conflicts,
focusedIndex,
onAnchorClick,
}: AnnotatedTextProps) {
return (
<div className="flex h-full flex-col">
{conflicts.length > 0 ? (
<div className="mb-3 flex flex-wrap gap-2 border-b border-line pb-3">
{conflicts.map((c, i) => (
<button
key={`${c.type}-${c.where}-${i}`}
type="button"
id={`anchor-${i}`}
onClick={() => onAnchorClick(i)}
aria-current={focusedIndex === i ? "true" : undefined}
className={`conflict-anchor rounded px-2 py-0.5 text-xs ${
focusedIndex === i
? "bg-cinnabar text-panel"
: "text-conflict hover:bg-[var(--color-conflict)]/10"
}`}
title={`${c.type}${c.where || "正文"}`}
>
<span aria-hidden="true"></span> {c.type}
{c.where ? ` · ${c.where}` : ""}
</button>
))}
</div>
) : null}
<article className="flex-1 overflow-auto whitespace-pre-wrap font-serif text-[17px] leading-[1.9] text-ink">
{text || (
<span className="text-ink-soft/60"></span>
)}
</article>
</div>
);
}