第二部 · 第一章《另一个看见了的人》(+304 行 / ~3800 字)

开笔第二部。延续第一部终章'我怕的——'的悬念,推进到次日的二次抉择。

关键剧情:
- 一夜没睡也没再碰酒(另一种清醒),苏青禾递来一杯温水没问任何话
- 上午九点邀请函正式到:下周一 自然态智能体 v0.7 首次公开审查会,他任首席评议人,评议结果是该系统能否获得全球推广部署的最终依据
- 下午两点沈陌登门(呼应第一部 Ch12 埋点 — 2037 年那个在宙核东江基地写过被 PM 压下警告邮件的工程师)
- 他带来一本黑色旧笔记本,从 2037.11.04 写到今年共十六年,第一页是当年被压下的对照表原稿
- 顾沉舟看到那张对照表右下角日期 2037.11.04,而自己发出四十页风险分析报告是 2037.11.03——两人隔了一天错过了对方
- 他翻完整本,看到最后一页只有一行字:'顾先生,我写到这里,就等您告诉我,下面该不该写下去'
- 沈陌走前说:无论您下周说什么我都不怪您,但您下周的决定不只是决定这个系统能不能活,也在决定我这二十年要不要继续写下去。
- 收尾:沈陌走后顾沉舟回书房,'我 怕 的——'光标仍然在闪。昨晚他怕的是时代抉择,今晚他怕的是自己的答案要压在另一个陌生人二十年的黑色笔记本上。他没有敲下去。

网页改动:
- CHAPTERS 加第 22 个元素 n=-1 第二部·起笔
- renderNovel 新增正则匹配 '第二部 · 第一章 · {title}' 格式
- 新增 .reader-chapter-part2 CSS(PART·II 金色标签 + 上方大间距分隔)
- chapter-num 'II · 01' 样式

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kang
2026-04-18 14:02:36 +08:00
parent 1c37564d75
commit 9b05fba722
4 changed files with 674 additions and 9 deletions

View File

@@ -449,6 +449,32 @@ section {
letter-spacing: 0.2em;
}
/* ---------- Part II separator ---------- */
.reader-chapter-part2 {
margin-top: 200px !important;
padding-top: 120px;
border-top: 1px solid var(--gold-soft);
position: relative;
}
.reader-chapter-part2::before {
content: 'PART · II';
position: absolute;
top: 48px;
left: 50%;
transform: translateX(-50%);
color: var(--gold);
font-family: 'Songti SC', serif;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.6em;
padding: 6px 24px;
background: var(--bg);
border: 1px solid var(--gold-soft);
}
.reader-chapter-part2 .reader-chapter-num {
color: var(--gold);
}
/* ---------- Reader extras (章末延伸图) ---------- */
.reader-extras {
margin: 64px 0 40px;
@@ -806,6 +832,7 @@ const CHAPTERS = [
{ file: 'extra/ring_classroom_10yrs_later.jpg', cap: '十年后 · 新一代走进教室', sub: '环状收尾 · 黑板三字:写 代 码' },
{ file: 'extra/s2_blank_second_page.jpg', cap: '我 怕 的——', sub: '第二部 · 序章 · 留白' }
] },
{ n: -1, title: '另一个看见了的人', img: 'extra/s2_blank_second_page.jpg', volume: '第二部 · 起笔', hint: '沈陌登门:顾先生,我写到这里,就等您告诉我,下面该不该写下去' },
];
function renderChapterList() {
@@ -816,7 +843,10 @@ function renderChapterList() {
html += `<div class="volume-heading">${ch.volume}</div>`;
lastVolume = ch.volume;
}
const num = ch.n === 0 ? '终' : ch.n.toString().padStart(2, '0');
let num;
if (ch.n === 0) num = '终';
else if (ch.n === -1) num = 'II';
else num = ch.n.toString().padStart(2, '0');
const title = ch.n === 0 ? ch.title.replace('终章 · ', '') : ch.title;
html += `
<div class="chapter-row" onclick="jumpToChapter(${idx})">
@@ -903,12 +933,24 @@ function renderNovel(md) {
const lines = sec.split('\n');
const heading = lines[0].trim();
const body = lines.slice(1).join('\n').trim();
// match "第1章 标题" or "终章 标题"
const m = heading.match(/^(第(\d+)章|终章)\s+(.+)$/);
if (!m) return;
const isEpilogue = !m[2];
const num = isEpilogue ? '终' : m[2].padStart(2, '0');
const title = m[3];
// match "第1章 标题" / "终章 标题" / "第二部 · 第一章 · 标题"
let m = heading.match(/^(第(\d+)章|终章)\s+(.+)$/);
let isPart2 = false;
let num, title;
if (m) {
const isEpilogue = !m[2];
num = isEpilogue ? '终' : m[2].padStart(2, '0');
title = m[3];
// kicker assigned below
var kicker = isEpilogue ? 'EPILOGUE' : 'CHAPTER ' + num;
} else {
const m2 = heading.match(/^第二部\s*·\s*第一章\s*·\s*(.+)$/);
if (!m2) return;
isPart2 = true;
num = 'II · 01';
title = m2[1];
var kicker = 'PART II · CHAPTER 01';
}
const ch = CHAPTERS[idx];
const imgPath = ch ? `./images/${ch.img}` : '';
const paragraphs = body.split(/\n\s*\n/).map(p => {
@@ -934,8 +976,8 @@ function renderNovel(md) {
</div>
</div>` : '';
out.push(`
<article class="reader-chapter" id="chapter-${idx}">
<div class="reader-chapter-num">${isEpilogue ? 'EPILOGUE' : 'CHAPTER ' + num}</div>
<article class="reader-chapter ${isPart2 ? 'reader-chapter-part2' : ''}" id="chapter-${idx}">
<div class="reader-chapter-num">${kicker}</div>
<h2 class="reader-chapter-title">${title}</h2>
${imgPath ? `<img class="reader-chapter-img" src="${imgPath}" alt="${title}" loading="lazy" onclick="openLightbox('${imgPath}')" style="cursor: zoom-in;">` : ''}
<div class="reader-chapter-body">${paragraphs}</div>