Files
koring-launcher/src/components/about-version/index.tsx
T
dream_pep 9eaa482a4d feat: 新增版本更新日志页面并调整初始化引导流程
- 新增 AboutVersion 组件,支持拉取 GitHub Release 日志并按提交类型分类展示
- 为 OOBE 和 UPvP 流程新增 about-version 页面,调整原版本页的跳转逻辑,先展示更新日志再进入后续步骤
- 注册新的路由配置,更新 README 与 DEV.md 文档说明新功能
2026-09-04 19:48:00 +08:00

168 lines
7.0 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.
// AboutVersion:展示当前版本的更新内容。
// 数据源:getReleaseNotes()GitHub release-notes.md,主进程自动切加速源,回退最新版)。
// 不直接渲染 Markdown —— 解析后按提交类型(新增/修复/优化/重构/文档/其他)分组,
// 每组配图标,用卡片展示。
import { useCallback, useEffect, useState } from "react";
import { Sparkles, Bug, Gauge, RefreshCw, FileText, Wrench, ExternalLink, GitCommitHorizontal } from "lucide-react";
import { getReleaseNotes, type ReleaseNotesResult } from "@/api/update";
import { VERSION } from "@/lib/version";
import { parseReleaseNotes, type ChangeType, type VersionChange } from "./parse";
const GITHUB_RELEASES = "https://github.com/dream-pep/koring-launcher/releases";
const CATEGORY_ORDER: ChangeType[] = ["feat", "fix", "perf", "refactor", "docs", "other"];
const CATEGORY_META: Record<ChangeType, { label: string; icon: React.ComponentType<{ className?: string }>; iconCls: string; dotCls: string }> = {
feat: { label: "新增功能", icon: Sparkles, iconCls: "text-sky-600 dark:text-sky-400 bg-sky-500/10", dotCls: "bg-sky-500" },
fix: { label: "修复", icon: Bug, iconCls: "text-red-600 dark:text-red-400 bg-red-500/10", dotCls: "bg-red-500" },
perf: { label: "性能优化", icon: Gauge, iconCls: "text-emerald-600 dark:text-emerald-400 bg-emerald-500/10", dotCls: "bg-emerald-500" },
refactor: { label: "重构", icon: RefreshCw, iconCls: "text-violet-600 dark:text-violet-400 bg-violet-500/10", dotCls: "bg-violet-500" },
docs: { label: "文档", icon: FileText, iconCls: "text-amber-600 dark:text-amber-400 bg-amber-500/10", dotCls: "bg-amber-500" },
other: { label: "其他", icon: Wrench, iconCls: "text-foreground/60 bg-foreground/[0.06]", dotCls: "bg-foreground/40" },
};
/** 一条变更(含 commit 标)+ 短分隔条 */
function ChangeItem({ change }: { change: VersionChange }) {
return (
<div className="flex items-start gap-2.5 px-4 py-2.5 first:pt-3 last:pb-3">
{change.commit && (
<span className="inline-flex items-center gap-1 shrink-0 mt-[3px] font-mono text-[10px] px-1.5 py-0.5 rounded bg-foreground/[0.05] dark:bg-white/[0.05] text-muted-foreground/80">
<GitCommitHorizontal className="w-3 h-3" />
{change.commit}
</span>
)}
<div className="flex-1 min-w-0">
<p className="text-[13px] font-medium text-foreground leading-snug">{change.title}</p>
{change.description && (
<p className="text-[12px] text-muted-foreground/80 leading-relaxed mt-0.5">{change.description}</p>
)}
</div>
</div>
);
}
/** 一个分类卡片:图标头部 + 该类型的变更列表 */
function CategoryCard({ type, items }: { type: ChangeType; items: VersionChange[] }) {
const meta = CATEGORY_META[type];
const Icon = meta.icon;
return (
<div className="rounded-xl overflow-hidden border border-black/[0.06] dark:border-white/[0.07] bg-white/85 dark:bg-black/45 backdrop-blur-[12px]">
<div className="flex items-center gap-2 px-4 py-2.5 border-b border-black/[0.05] dark:border-white/[0.06]">
<span className={`flex items-center justify-center w-6 h-6 rounded-md ${meta.iconCls}`}>
<Icon className="w-3.5 h-3.5" />
</span>
<span className="text-[13px] font-semibold text-foreground">{meta.label}</span>
<span className="text-[11px] text-muted-foreground/60 ml-auto tabular-nums">{items.length} </span>
</div>
<div className="divide-y divide-black/[0.04] dark:divide-white/[0.05]">
{items.map((c, i) => (
<ChangeItem key={`${c.commit ?? ""}-${i}`} change={c} />
))}
</div>
</div>
);
}
function EmptyCard({ text }: { text: string }) {
return (
<div className="rounded-xl border border-black/[0.06] dark:border-white/[0.07] bg-white/85 dark:bg-black/45 backdrop-blur-[12px] px-5 py-10 text-center">
<p className="text-[13px] text-muted-foreground">{text}</p>
</div>
);
}
export function AboutVersion({ className }: { className?: string }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [result, setResult] = useState<ReleaseNotesResult | null>(null);
const load = useCallback(async () => {
setLoading(true);
setError(null);
try {
const r = await getReleaseNotes();
setResult(r);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
load();
}, [load]);
const parsed = result ? parseReleaseNotes(result.notes, result.version, result.tag) : null;
const grouped = parsed
? CATEGORY_ORDER.map((t) => ({ type: t, items: parsed.changes.filter((c) => c.type === t) })).filter(
(g) => g.items.length > 0,
)
: [];
return (
<div className={className}>
{/* 头部:版本 + 来源 */}
<div className="flex items-center justify-between mb-2 px-1">
<span className="text-[12px] text-muted-foreground/80">
{parsed ? (
<>
版本 v{parsed.version}
{result?.isLatest && parsed.version !== VERSION && (
<span className="ml-2 opacity-70">(当前版本暂无说明,展示最新版本内容)</span>
)}
</>
) : (
<>版本 v{VERSION}</>
)}
</span>
</div>
{loading ? (
<div className="space-y-2">
{[0, 1, 2].map((i) => (
<div key={i} className="h-[76px] rounded-xl bg-white/50 dark:bg-white/[0.04] animate-pulse" />
))}
</div>
) : error ? (
<EmptyCard text="获取此版本的更新内容失败" />
) : !result || !parsed ? (
<EmptyCard text="未能获取到发布说明(GitHub 与加速源均不可用,或该版本尚未发布)" />
) : (
<div className="space-y-2.5">
{parsed.noCommits && parsed.changes.length === 0 ? (
<EmptyCard text="此版本暂无变更记录" />
) : grouped.length === 0 ? (
<EmptyCard text="此版本暂无变更记录" />
) : (
grouped.map((g) => <CategoryCard key={g.type} type={g.type} items={g.items} />)
)}
</div>
)}
{/* 底部操作 */}
<div className="flex items-center gap-2 mt-3 px-1">
{error && (
<>
<p className="text-[11px] text-destructive/80 flex-1 truncate">{error}</p>
<button
onClick={load}
className="text-[12px] font-medium text-primary hover:underline shrink-0"
>
重试
</button>
</>
)}
<button
onClick={() => window.electronAPI?.openExternal(GITHUB_RELEASES)}
className="inline-flex items-center gap-0.5 text-[12px] text-muted-foreground hover:text-foreground transition-colors ml-auto shrink-0"
>
查看 GitHub Releases
<ExternalLink className="w-3 h-3" />
</button>
</div>
</div>
);
}