feat: improve app ui and project metadata

This commit is contained in:
Yaojia Wang
2026-06-28 07:31:20 +02:00
parent 3bd464d400
commit 90a66437d7
86 changed files with 4892 additions and 1108 deletions

View File

@@ -1,10 +1,16 @@
"use client";
import type { ReviewConflict } from "@/lib/review/sse";
import { AlertTriangle, CheckCircle2, LocateFixed, Sparkles } from "lucide-react";
import { Badge } from "@/components/ui/Badge";
import { TextInput } from "@/components/ui/TextInput";
import { buttonClass } from "@/lib/ui/variants";
import type { DecisionDraft, Verdict } from "@/lib/review/decisions";
import type { ReviewConflict } from "@/lib/review/sse";
interface ConflictCardProps {
index: number;
total: number;
conflict: ReviewConflict;
draft: DecisionDraft;
missing: boolean;
@@ -28,15 +34,16 @@ const VERDICT_OPTIONS: { value: Verdict; label: string; primary: boolean }[] = [
// 裁决按钮样式:激活=朱砂填充;未激活的主按钮=朱砂描边强调,次级=低对比描边。
function verdictClass(active: boolean, primary: boolean): string {
if (active) return "bg-cinnabar text-panel";
if (primary) return "border border-cinnabar text-cinnabar hover:bg-cinnabar/10";
return "border border-line text-ink-soft hover:border-cinnabar hover:text-ink";
if (active) return buttonClass({ variant: "primary", size: "sm" });
if (primary) return buttonClass({ variant: "outline", size: "sm" });
return buttonClass({ variant: "secondary", size: "sm" });
}
// 单个冲突报告卡UX §6.4):五类徽标 + where + refs + suggestion + 裁决三态。
// 冲突=赭红;未决/缺判时加图标+文案不单靠色a11y §10
export function ConflictCard({
index,
total,
conflict,
draft,
missing,
@@ -60,13 +67,14 @@ export function ConflictCard({
}`}
>
<div className="flex items-start gap-3">
<span
className="shrink-0 rounded bg-[var(--color-conflict)]/10 px-2 py-0.5 text-xs text-conflict"
aria-hidden="true"
>
{conflict.type}
</span>
<p className="flex-1 text-sm text-ink">{conflict.suggestion}</p>
<Badge variant="danger" className="shrink-0">
<AlertTriangle className="h-3 w-3" aria-hidden="true" />
{index + 1}/{total}
</Badge>
<div className="min-w-0 flex-1">
<p className="text-xs text-conflict">{conflict.type}</p>
<p className="mt-1 text-sm text-ink">{conflict.suggestion}</p>
</div>
</div>
{conflict.original && conflict.replacement ? (
@@ -95,9 +103,10 @@ export function ConflictCard({
<button
type="button"
onClick={() => onJump(index)}
className="rounded border border-line px-2 py-0.5 hover:border-cinnabar hover:text-cinnabar"
className={buttonClass({ variant: "secondary", size: "sm" })}
>
{conflict.where}
<LocateFixed className="h-4 w-4" aria-hidden="true" />
{conflict.where}
</button>
) : null}
{conflict.refs.map((ref) => (
@@ -121,23 +130,24 @@ export function ConflictCard({
aria-pressed={active}
disabled={rewriting}
onClick={() => onVerdict(index, opt.value)}
className={`rounded px-3 py-1 text-xs disabled:cursor-not-allowed disabled:opacity-40 ${verdictClass(
active,
opt.primary,
)}`}
className={verdictClass(active, opt.primary)}
>
{opt.label}
</button>
);
})}
{rewriting ? (
<span className="text-xs text-info"> AI </span>
<Badge variant="info">
<Sparkles className="h-3 w-3" aria-hidden="true" />
AI
</Badge>
) : resolved ? (
<span className="text-xs text-pass" aria-hidden="true">
</span>
<Badge variant="success">
<CheckCircle2 className="h-3 w-3" aria-hidden="true" />
</Badge>
) : (
<span className="text-xs text-conflict"></span>
<Badge variant="danger"></Badge>
)}
</div>
{draft.verdict === "manual" ? (
@@ -145,14 +155,16 @@ export function ConflictCard({
<label htmlFor={`note-${index}`} className="sr-only">
</label>
<input
<TextInput
id={`note-${index}`}
type="text"
value={draft.note}
onChange={(e) => onNote(index, e.target.value)}
placeholder="手改说明(可选)"
className="w-full rounded border border-line bg-bg px-3 py-1.5 text-sm text-ink focus:border-cinnabar focus:outline-none"
/>
<p className="mt-1 text-xs text-ink-soft">
</p>
</div>
) : null}
</div>