- {blocks.length > 0 ? (
+ {docHtml ? (
+ <>
+ {showIdCard &&
}
+
+ >
+ ) : blocks.length > 0 ? (
<>
{blocks.map((block) =>
)}
{showIdCard &&
}
@@ -157,4 +168,14 @@ const typo = await pageTypography('siteAbout', 'about');
color: var(--ink-2);
max-width: var(--measure);
}
+ /* 方案 A 白纸直编:单栏、行距放宽,交给全局 .rich 排版体系 */
+ .about-doc { max-width: var(--measure); }
+ .about-doc :global(h2) {
+ font-size: 15px;
+ font-family: var(--font-mono);
+ color: var(--ink-3);
+ letter-spacing: 0.06em;
+ margin: var(--space-6) 0 var(--space-3);
+ }
+ .about-doc :global(p) { line-height: 1.85; }
diff --git a/src/content.config.ts b/src/content.config.ts
index 80889c0..1e6ec95 100644
--- a/src/content.config.ts
+++ b/src/content.config.ts
@@ -279,6 +279,12 @@ export const collections = {
// → src/lib/page-blocks.ts warn + skip,保证后台填错块型不炸构建
schema: ({ image }) =>
z.object({
+ /**
+ * 白纸直编(Phase 5k 用户选定方案 A):整页正文一个 markdown 字段,
+ * 想写什么写什么。非空时整页只渲染它(IdCard 仍在顶部);
+ * 为空时回落 blocks 块式通道 → intro → 内置默认(兼容链保持不断)。
+ */
+ doc: bilingual.optional(),
intro: bilingual.optional(),
/** 页面级排版覆盖(层 2,T3 车道):正文默认字体类 / 强调色类 / 字号缩放 */
typography: z
diff --git a/src/lib/site-content.ts b/src/lib/site-content.ts
index f11c261..5602bdc 100644
--- a/src/lib/site-content.ts
+++ b/src/lib/site-content.ts
@@ -42,7 +42,7 @@ export const SITE_DEFAULTS = {
const pick = (value: string | undefined, fallback: string): string =>
value && value.trim() ? value : fallback;
-const pickBi = (value: { zh?: string; en?: string } | undefined, fallback: Bi, locale: Locale): string =>
+export const pickBi = (value: { zh?: string; en?: string } | undefined, fallback: Bi, locale: Locale): string =>
pick(value?.[locale], fallback[locale]);
/** 主页 tagline(zh/en) */
diff --git a/src/pages/about-preview.astro b/src/pages/about-preview.astro
deleted file mode 100644
index 1501ca8..0000000
--- a/src/pages/about-preview.astro
+++ /dev/null
@@ -1,336 +0,0 @@
----
-/**
- * 关于页形态预览(Phase 5k):一页并陈五个方案,共用 about.yaml 真实数据,供用户点选。
- *
- * 为什么单开预览路由:关于页是"讲人"的页面,形态是纯审美 + 编辑自由度的取舍,
- * 改错要回滚一轮。选定后我把选中方案落进 AboutPage.astro,然后删掉本路由。
- *
- * 五案数据完全同源(content/site/about.yaml 的 blocks),只是结构与视觉不同——
- * 无论选哪个,内容都不用迁移。
- */
-import { getCollection } from 'astro:content';
-import BaseLayout from '../layouts/BaseLayout.astro';
-import IdCard from '../components/IdCard.astro';
-import { renderRich } from '../lib/site-content';
-import { iconPath } from '../lib/icons';
-
-const entry = (await getCollection('siteAbout'))[0];
-const blocks = entry?.data.blocks ?? [];
-const proseBlocks = blocks.filter((b) => b.type === 'prose');
-const tracksBlock: any = blocks.find((b) => b.type === 'tracks') ?? null;
-const linksBlock: any = blocks.find((b) => b.type === 'linklist') ?? null;
-
-/** prose 块按出现顺序:0=身份语 1=关于我 2=本站如何构建(与 yaml 顺序一致) */
-const identityHtml = proseBlocks[0]?.body?.zh ? await renderRich(proseBlocks[0].body.zh) : '';
-const aboutMeHtml = proseBlocks[1]?.body?.zh ? await renderRich(proseBlocks[1].body.zh) : '';
-const howHtml = proseBlocks[2]?.body?.zh ? await renderRich(proseBlocks[2].body.zh) : '';
-const aboutMePlain = (proseBlocks[1]?.body?.zh ?? '').replace(/<[^>]+>/g, '').trim();
-const trackItems: any[] = tracksBlock?.items ?? [];
-const trackTitle = tracksBlock?.title?.zh ?? '';
-const linkItems: any[] = linksBlock?.items ?? [];
-const linkTitle = linksBlock?.title?.zh ?? '';
-
-const sections = [
- { no: '01', title: '关于我', html: aboutMeHtml },
- { no: '02', title: trackTitle, isTracks: true },
- { no: '03', title: linkTitle, isLinks: true },
- { no: '04', title: '本站如何构建', html: howHtml },
-];
----
-
-
- 关于页:五个方案
-
- 五案共用同一份内容( about.yaml ),只是结构与视觉不同——选哪个都不用迁移内容。
-
- 回字母告诉我选哪个(A / B / C / D / E,也可以说「B 但左栏窄一点」这类微调)。
-
-
-
-
-
-
-
-
-
-
- {linkTitle}
-
- {linkItems.map((it) => (
- - {it.label.zh}{it.note?.zh && — {it.note.zh}}
- ))}
-
- 本站如何构建
-
- {trackTitle}
- {trackItems.map((it) => it.name.zh).join(' · ')}
-
-
-
-
-
-
-
-
-
-
-
{linkTitle}
-
- {linkItems.map((it) => (
- - {it.label.zh}{it.note?.zh && — {it.note.zh}}
- ))}
-
-
本站如何构建
-
-
-
-
-
-
-
-
- {identityHtml}
-
-
- {sections.map((sec) => (
-
- {sec.no} {sec.title}
- {sec.isTracks ? (
-
- {trackItems.map((it) => (
- -
- {iconPath(it.icon) && (
-
- )}
- {it.name.zh}
- {it.desc.zh}
-
- ))}
-
- ) : sec.isLinks ? (
-
- {linkItems.map((it) => (
- - {it.label.zh}{it.note?.zh && — {it.note.zh}}
- ))}
-
- ) : (
-
- )}
-
- ))}
-
-
-
-
-
-
-
-
-
- -
-
-
起点:气象
- 一个想做预报的人,依旧把云图当日常读物。
-
- -
-
-
第二主线:代码
- Linux、容器与自托管,把日常工具一件件磨顺手。
-
- -
-
-
作品:wait4x.online
- 把读过的书、按下的快门都放进同一个工作台。
-
- -
-
-
信条
- 拥抱开源,热爱开源。
-
-
-
-
-
-
-
-
-
-
-
-
{trackTitle}
-
- {trackItems.map((it) => (
- - {it.name.zh}{it.desc.zh}
- ))}
-
-
-
{linkTitle}
-
- {linkItems.map((it) => (
- - {it.label.zh}{it.note?.zh && — {it.note.zh}}
- ))}
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/heatmap-preview.astro b/src/pages/heatmap-preview.astro
deleted file mode 100644
index 67eeec5..0000000
--- a/src/pages/heatmap-preview.astro
+++ /dev/null
@@ -1,387 +0,0 @@
----
-/**
- * 热力图方案 v4(Phase 5j):用户认可 FJ(蛇 + 月柱联动),要求**优化蛇的行动路径**。
- *
- * 现状问题(真实数据):371 格里只有 51 天有贡献(14%),原路径是"按列扫整张网格",
- * 于是 86% 的行程在空格上、且格子跳变(每 26ms 瞬移一格)显得机械。
- *
- * 本版给三条路径优化,都保留 FJ 的月柱联动:
- * P1 智能寻食:只按"最近的有贡献格子"走,中间用子格插值滑行——蛇总在吃东西附近
- * P2 平滑蛇体:仍扫全网,但头是连续坐标滑行 + 拖尾,看起来像真蛇在游
- * P3 分月清扫:按月成块清扫,月底停一拍并让该月柱子立起(节奏感来自"月")
- */
-import BaseLayout from '../layouts/BaseLayout.astro';
-import HeatGrid from '../components/geek/HeatGrid.astro';
-import { loadHeatmap, type HeatDay } from '../lib/heatmap';
-
-const { weeks, total } = await loadHeatmap();
-const days: HeatDay[] = weeks.flat();
-const buildTime = new Date().toISOString().slice(0, 10);
-const activeDays = days.filter((d) => d.count > 0).length;
-
-/* 月度聚合(柱子) */
-const byMonth = new Map
();
-for (const d of days) {
- const key = d.date.slice(0, 7);
- byMonth.set(key, (byMonth.get(key) ?? 0) + d.count);
-}
-const months = [...byMonth.entries()].sort(([a], [b]) => a.localeCompare(b));
-const maxMonth = Math.max(...months.map(([, v]) => v));
----
-
-
- 蛇的路径优化(FJ 骨架)
-
- 你认可 FJ,问题在路径。真实数据:371 格里只有 {activeDays} 天有贡献(
- {Math.round((activeDays / days.length) * 100)}%),原路径"按列扫全网"意味着
- {100 - Math.round((activeDays / days.length) * 100)}% 的行程在空转,
- 而且每 26ms 瞬移一格,看着机械。三案都保留月柱联动。回 P1 / P2 / P3 告诉我选哪个。
-
-
-
-
-
-
-
-
-
- {months.map(([m, v], i) => (
- -
-
- {m.slice(5)}
-
- ))}
-
- 准备出发… · 第 1 轮
-
-
-
-
-
-
-
- {months.map(([m, v], i) => (
- -
-
- {m.slice(5)}
-
- ))}
-
- 准备出发… · 第 1 轮
-
-
-
-
-
-
-
- {months.map(([m, v], i) => (
- -
-
- {m.slice(5)}
-
- ))}
-
- 准备出发… · 当前 —
-
-
-
-
-
-
-
-