diff --git a/site/src/components/ChangelogPage.astro b/site/src/components/ChangelogPage.astro index 6038e79b36..9406658d60 100644 --- a/site/src/components/ChangelogPage.astro +++ b/site/src/components/ChangelogPage.astro @@ -3,23 +3,22 @@ import Base from '../layouts/Base.astro'; import SiteHeader from './SiteHeader.astro'; import type { ReleaseItem, ReleaseRecord } from '../data/releases'; import { releasePath } from '../data/releases'; +import { isThematicTitle } from '../scripts/release-title.js'; import '../styles/changelog.css'; -const { release, releases } = Astro.props as { release: ReleaseRecord; releases: ReleaseRecord[] }; +const { release, releases, canonical } = Astro.props as { + release: ReleaseRecord; + releases: ReleaseRecord[]; + canonical?: string; +}; const base = import.meta.env.BASE_URL.replace(/\/$/, ''); const repo = 'https://github.com/esengine/DeepSeek-Reasonix'; const prHref = (ref: number) => `${repo}/pull/${ref}`; -const sectionItems = [ - ...(release.guides.length ? [{ id: 'guides', en: 'Guides', zh: '使用攻略' }] : []), - { id: 'highlights', en: 'Highlights', zh: '重点内容' }, - ...(release.changes.new.length ? [{ id: 'new', en: 'New', zh: '新功能' }] : []), - ...(release.changes.improved.length ? [{ id: 'improved', en: 'Improved', zh: '改进' }] : []), - ...(release.changes.fixed.length ? [{ id: 'fixed', en: 'Fixed', zh: '修复' }] : []), - { id: 'upgrade', en: 'Upgrade notes', zh: '升级提醒' }, - { id: 'risks', en: 'Risk notes', zh: '风险提示' }, - ...(release.contributors.length ? [{ id: 'thanks', en: 'Thanks', zh: '致谢' }] : []), - { id: 'download', en: 'Download', zh: '下载与安装' }, -]; + +/* A title that is exactly the version string ("Reasonix 1.17.14") means a + patch release — it gets the compact hero; anything else (including + "Reasonix 1.18.0 — Better agents") keeps the full hero. */ +const isThematic = isThematicTitle(release.title.en, release.version); const kindLabel = (kind: string) => ({ new: { en: 'New', zh: '新增' }, @@ -38,12 +37,38 @@ const surfaceLabel = (surface: string) => ({ plugin: { en: 'Plugins', zh: '插件' }, }[surface] ?? { en: surface, zh: surface }); +/* Sections render only when they have content; numbering is sequential. */ +const sectionDefs = [ + ...(release.guides.length ? [{ id: 'guides', en: 'Guides', zh: '使用攻略' }] : []), + { id: 'highlights', en: 'Highlights', zh: '重点内容' }, + ...(['new', 'improved', 'fixed'] as const) + .filter((kind) => release.changes[kind].length) + .map((kind) => ({ id: kind, ...kindLabel(kind) })), + ...(release.upgrade.length ? [{ id: 'upgrade', en: 'Upgrade notes', zh: '升级提醒' }] : []), + ...(release.risks.length ? [{ id: 'risks', en: 'Risk notes', zh: '风险提示' }] : []), + ...(release.contributors.length ? [{ id: 'thanks', en: 'Thanks', zh: '致谢' }] : []), +]; +const sectionNum = new Map(sectionDefs.map((s, i) => [s.id, String(i + 1).padStart(2, '0')])); +const tocItems = [...sectionDefs, { id: 'download', en: 'Download', zh: '下载与安装' }]; + +/* Version rail: group by minor, show the latest few patches per group, + older ones behind a native
expander. */ +const minorKey = (v: string) => v.split('.').slice(0, 2).join('.'); +const railGroups: { key: string; items: ReleaseRecord[] }[] = []; +for (const item of releases) { + const key = minorKey(item.version); + const last = railGroups[railGroups.length - 1]; + if (last && last.key === key) last.items.push(item); + else railGroups.push({ key, items: [item] }); +} +const RAIL_VISIBLE = 3; + const titleEn = `Reasonix v${release.version} release notes`; const titleZh = `Reasonix v${release.version} 更新日志`; const description = release.summary.en; --- - +
@@ -58,23 +83,46 @@ const description = release.summary.en;
-
+
{release.channel}
-

Reasonix / v{release.version}

+ {isThematic &&

Reasonix / v{release.version}

}

{release.title.en}{release.title.zh}

{release.surfaces.map((surface) => { @@ -82,16 +130,22 @@ const description = release.summary.en; return {label.en}{label.zh}; })}
-
{release.summary.en}{release.summary.zh}
- + {isThematic ? ( +
{release.summary.en}{release.summary.zh}
+ ) : ( +

{release.summary.en}{release.summary.zh}

+ )} + {isThematic && ( + + )}
{release.guides.length > 0 && (
-
01

Start here使用攻略

+
{sectionNum.get('guides')}

Start here使用攻略

{release.guides.map((guide) => ( @@ -105,7 +159,7 @@ const description = release.summary.en; )}
-
02

Highlights重点内容

+
{sectionNum.get('highlights')}

Highlights重点内容

{release.highlights.map((item, index) => { const kind = kindLabel(item.kind); @@ -124,13 +178,13 @@ const description = release.summary.en;
- {(['new', 'improved', 'fixed'] as const).map((kind, index) => { + {(['new', 'improved', 'fixed'] as const).map((kind) => { const items = release.changes[kind]; if (!items.length) return null; const label = kindLabel(kind); return (
-
{String(index + 3).padStart(2, '0')}

{label.en}{label.zh}

+
{sectionNum.get(kind)}

{label.en}{label.zh}

+
+ )} {release.contributors.length > 0 && (
-

Thanks致谢

+
{sectionNum.get('thanks')}

Thanks致谢

This release was shaped by:感谢本版本的贡献者:

{release.contributors.map((name) => @{name})}
@@ -195,7 +253,7 @@ const description = release.summary.en;
diff --git a/site/src/components/SiteHeader.astro b/site/src/components/SiteHeader.astro index 9b74d80ce8..5be86daaf1 100644 --- a/site/src/components/SiteHeader.astro +++ b/site/src/components/SiteHeader.astro @@ -51,6 +51,11 @@ const items: Array<{ +
+ + + +
{accountSlot ? ( ) : ( diff --git a/site/src/layouts/Base.astro b/site/src/layouts/Base.astro index 9a89429da8..aea5b2480d 100644 --- a/site/src/layouts/Base.astro +++ b/site/src/layouts/Base.astro @@ -1,8 +1,8 @@ --- import '../styles/global.css'; const base = import.meta.env.BASE_URL.replace(/\/$/, ''); -const { title, description, titleEn, titleZh } = Astro.props; -const canonical = new URL(Astro.url.pathname, Astro.site).href; +const { title, description, titleEn, titleZh, canonical: canonicalOverride } = Astro.props; +const canonical = canonicalOverride ?? new URL(Astro.url.pathname, Astro.site).href; const ogImage = new URL(`${base}/og.png`, Astro.site).href; --- @@ -16,6 +16,9 @@ const ogImage = new URL(`${base}/og.png`, Astro.site).href; + @@ -34,7 +37,6 @@ const ogImage = new URL(`${base}/og.png`, Astro.site).href; -
@@ -21,7 +24,7 @@ const canonical = new URL(Astro.url.pathname, Astro.site).href;