Compare commits
29 Commits
9b05fba722
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d10679ea51 | ||
| 481b47b0b2 | |||
|
|
33c60aa08a | ||
| 15b2812507 | |||
| d46152ccb8 | |||
|
|
3e10890ed1 | ||
| c3cacbb5a9 | |||
| 063675e848 | |||
|
|
d78383e222 | ||
| c053bfc4f4 | |||
|
|
fe9f24b895 | ||
| cf4033eb1d | |||
|
|
1d3034b75e | ||
| 47b480b2a9 | |||
|
|
1f3126d251 | ||
| e1c603ff17 | |||
| b23feaefd6 | |||
| d7378a08bd | |||
| cb74d3eb53 | |||
| 1513e37458 | |||
| a0eb6fd3ae | |||
|
|
18670d2267 | ||
| 2239b6d718 | |||
|
|
c11b61bfbe | ||
| 6a4857c097 | |||
|
|
64b80f1f5f | ||
|
|
368fcfa577 | ||
| b71ec7ea0f | |||
| 2691af44f3 |
@@ -307,6 +307,209 @@
|
||||
"message": "auto-save 2026-04-18 13:56 (~1)",
|
||||
"hash": "1c37564",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:02:36+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第一章《另一个看见了的人》(+304 行 / ~3800 字)",
|
||||
"hash": "9b05fba",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:06:31+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:04 (~1)",
|
||||
"hash": "2691af4",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:13:18+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:13 (~1)",
|
||||
"hash": "b71ec7e",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:17:40+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第二章《他差一点把那些刺长回来》(+3800 字)",
|
||||
"hash": "368fcfa",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:18:26+08:00",
|
||||
"type": "commit",
|
||||
"message": "网页适配第二部多章:正则支持第N章(中文/阿拉伯数字)+ CHAPTERS 加第二部 Ch2 条目",
|
||||
"hash": "64b80f1",
|
||||
"files_changed": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:19:00+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:18 (~1)",
|
||||
"hash": "6a4857c",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:24:52+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第三章《她说她找不到一个值得攻击的地方》(+3800 字)",
|
||||
"hash": "c11b61b",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:25:11+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:25 (~1)",
|
||||
"hash": "2239b6d",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:31:03+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第四章《那一夜,他关掉了屏幕》(+3800 字)",
|
||||
"hash": "18670d2",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:31:20+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:31 (~1)",
|
||||
"hash": "a0eb6fd",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:37:45+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:37 (~1)",
|
||||
"hash": "1513e37",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:43:32+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:43 (~1)",
|
||||
"hash": "cb74d3e",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:49:20+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:49 (~1)",
|
||||
"hash": "d7378a0",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T14:55:41+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 14:54 (~1)",
|
||||
"hash": "b23feae",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:02:58+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:02 (~3)",
|
||||
"hash": "e1c603f",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:03:08+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第五章《让光回到所有看见的人身上》(+4500 字)",
|
||||
"hash": "1f3126d",
|
||||
"files_changed": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:09:00+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:08 (~1)",
|
||||
"hash": "47b480b",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:10:53+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第六章《那封邮件的作者》(+3800 字)",
|
||||
"hash": "1d3034b",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:14:50+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:14 (~2)",
|
||||
"hash": "cf4033e",
|
||||
"files_changed": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:17:20+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第七章《她记得的那碗面》(+3800 字)",
|
||||
"hash": "fe9f24b",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:21:04+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:20 (~1)",
|
||||
"hash": "c053bfc",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:23:25+08:00",
|
||||
"type": "commit",
|
||||
"message": "第二部 · 第八章《接下来这二十年,换我来写》(+3500 字) —— 全书·完",
|
||||
"hash": "d78383e",
|
||||
"files_changed": 4
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:28:26+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:26 (~1)",
|
||||
"hash": "063675e",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:34:38+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:34 (~1)",
|
||||
"hash": "c3cacbb",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:40:38+08:00",
|
||||
"type": "commit",
|
||||
"message": "worklog.json auto-save tail",
|
||||
"hash": "3e10890",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:42:03+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:40 (~1)",
|
||||
"hash": "d46152c",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:48:56+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:48 (~2)",
|
||||
"hash": "15b2812",
|
||||
"files_changed": 2
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:50:40+08:00",
|
||||
"type": "commit",
|
||||
"message": "修阅读'一直载入中':novel.md gzip + 章节索引 + loading 状态",
|
||||
"hash": "33c60aa",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-04-18T15:55:26+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-04-18 15:54 (~1)",
|
||||
"hash": "481b47b",
|
||||
"files_changed": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
3663
novel/《古法代码之王》.md
3663
novel/《古法代码之王》.md
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,13 @@ RUN printf 'server {\n\
|
||||
access_log off;\n\
|
||||
}\n\
|
||||
\n\
|
||||
# html / md: always revalidate\n\
|
||||
# novel.md specifically — force text/markdown so gzip triggers\n\
|
||||
location = /novel.md {\n\
|
||||
default_type "text/markdown; charset=utf-8";\n\
|
||||
add_header Cache-Control "public, max-age=0, must-revalidate";\n\
|
||||
}\n\
|
||||
\n\
|
||||
# html / md catch-all: always revalidate\n\
|
||||
location ~* \\.(html|md)$ {\n\
|
||||
add_header Cache-Control "public, max-age=0, must-revalidate";\n\
|
||||
}\n\
|
||||
@@ -26,8 +32,10 @@ RUN printf 'server {\n\
|
||||
}\n\
|
||||
\n\
|
||||
gzip on;\n\
|
||||
gzip_types text/plain text/markdown text/css application/javascript application/json image/svg+xml;\n\
|
||||
gzip_min_length 1024;\n\
|
||||
gzip_comp_level 6;\n\
|
||||
gzip_types text/plain text/markdown text/css application/javascript application/json image/svg+xml text/html;\n\
|
||||
gzip_min_length 512;\n\
|
||||
gzip_vary on;\n\
|
||||
}\n' > /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
@@ -833,6 +833,13 @@ const CHAPTERS = [
|
||||
{ file: 'extra/s2_blank_second_page.jpg', cap: '我 怕 的——', sub: '第二部 · 序章 · 留白' }
|
||||
] },
|
||||
{ n: -1, title: '另一个看见了的人', img: 'extra/s2_blank_second_page.jpg', volume: '第二部 · 起笔', hint: '沈陌登门:顾先生,我写到这里,就等您告诉我,下面该不该写下去' },
|
||||
{ n: -2, title: '他差一点把那些刺长回来', img: null, volume: '第二部 · 起笔', hint: '三天读尽白皮书,差一点对一个系统动了信任测试的念头;出门去见了韩锐' },
|
||||
{ n: -3, title: '她说她找不到一个值得攻击的地方', img: null, volume: '第二部 · 起笔', hint: '许幼宁 86 次攻防 0 成功。她揭穿他三年前埋的第三层签名偏差——"我想看你什么时候自己能看到它不再需要"' },
|
||||
{ n: -4, title: '那一夜,他关掉了屏幕', img: null, volume: '第二部 · 起笔', hint: '审查会前夜:读完沈陌整本笔记 + 想起二十三年前老总监那封被遗忘的邮件"替我看住这套东西" + 关掉屏幕不写答案直接去睡 + 苏青禾那句"我信你"' },
|
||||
{ n: -5, title: '让光回到所有看见的人身上', img: null, volume: '第二部 · 起笔', hint: '审查会现场:帆布包里三本东西摆上评议桌。有条件通过。请许幼宁+沈陌上台站到他两侧。请联盟找回那位说"让他自己醒醒"的安全侧负责人旁听。走出大厅下起春雨,他发给苏青禾三个字"我回家"' },
|
||||
{ n: -6, title: '那封邮件的作者', img: null, volume: '第二部 · 起笔', hint: '老总监周以谦被找到:潮屿海边阳台,"那封邮件我后来很多年都后悔发过。那不是上级该对下级说的话。那是一个认输的人在找替身"。拒绝上台:不是所有被按下去的人都需要被带回舞台。你那根刺这次我看见你真的开始把它收起来了' },
|
||||
{ n: -7, title: '她记得的那碗面', img: null, volume: '第二部 · 起笔', hint: '北方县城养老院:母亲轻度阿尔茨海默"你是修理工吗?我家那台老电视机最近总是雪花"。他不纠正她。她用讲给陌生人的语气讲他小时候。临别她清了一瞬"阿舟,你最近辛苦了吧"。他去县城街角小餐馆吃葱花挂面——这辈子第一次为自己流眼泪' },
|
||||
{ n: -8, title: '接下来这二十年,换我来写', img: null, volume: '第二部 · 收束', hint: '七年流速切片:v0.7 全球落地 / 周以谦病了看够了 / 沈陌女儿入学挑《操作系统基础 I》/ 母亲走了他在老屋自己煮一碗葱花面 / 城中村被拆成空地他没进去 / 沈陌接替他坐上评议席 / 71 岁出书《那三个夜晚和之后的事》扉页三个名字 / 沈陌新笔记第一页"接下来这二十年,换我来写" / 阳台摇椅"反正我也在" —— 全书·完' },
|
||||
];
|
||||
|
||||
function renderChapterList() {
|
||||
@@ -845,7 +852,7 @@ function renderChapterList() {
|
||||
}
|
||||
let num;
|
||||
if (ch.n === 0) num = '终';
|
||||
else if (ch.n === -1) num = 'II';
|
||||
else if (ch.n < 0) num = 'II·' + String(-ch.n).padStart(2, '0');
|
||||
else num = ch.n.toString().padStart(2, '0');
|
||||
const title = ch.n === 0 ? ch.title.replace('终章 · ', '') : ch.title;
|
||||
html += `
|
||||
@@ -915,13 +922,21 @@ function closeLightbox() {
|
||||
}
|
||||
|
||||
async function loadNovel() {
|
||||
const container = document.getElementById('reader-content');
|
||||
try {
|
||||
const resp = await fetch('./novel.md');
|
||||
container.innerHTML = '<div class="loading">正在下载全本 …</div>';
|
||||
const resp = await fetch('./novel.md', { cache: 'no-cache' });
|
||||
if (!resp.ok) throw new Error('HTTP ' + resp.status);
|
||||
container.innerHTML = '<div class="loading">正在渲染章节 …</div>';
|
||||
const text = await resp.text();
|
||||
// Yield to the browser so the loading message paints before heavy work
|
||||
await new Promise(r => setTimeout(r, 0));
|
||||
renderNovel(text);
|
||||
} catch (e) {
|
||||
document.getElementById('reader-content').innerHTML =
|
||||
'<div class="loading" style="color: var(--red)">小说载入失败:' + e.message + '</div>';
|
||||
console.error('loadNovel failed:', e);
|
||||
container.innerHTML =
|
||||
'<div class="loading" style="color: var(--red)">小说载入失败:' + e.message +
|
||||
'。<a href="./novel.md" style="color: var(--gold); text-decoration: underline;">直接打开 novel.md</a></div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -937,22 +952,28 @@ function renderNovel(md) {
|
||||
let m = heading.match(/^(第(\d+)章|终章)\s+(.+)$/);
|
||||
let isPart2 = false;
|
||||
let num, title;
|
||||
const cnMap = { '一': '01', '二': '02', '三': '03', '四': '04', '五': '05', '六': '06', '七': '07', '八': '08', '九': '09', '十': '10' };
|
||||
const cnToInt = { '一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10 };
|
||||
let kicker, chN;
|
||||
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;
|
||||
kicker = isEpilogue ? 'EPILOGUE' : 'CHAPTER ' + num;
|
||||
chN = isEpilogue ? 0 : parseInt(m[2]);
|
||||
} else {
|
||||
const m2 = heading.match(/^第二部\s*·\s*第一章\s*·\s*(.+)$/);
|
||||
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 arabic = cnMap[m2[1]] || m2[1];
|
||||
num = 'II · ' + arabic;
|
||||
title = m2[2];
|
||||
kicker = 'PART II · CHAPTER ' + arabic;
|
||||
chN = -(cnToInt[m2[1]] || parseInt(m2[1])); // -1 = Part II Ch1, -2 = Part II Ch2, ...
|
||||
}
|
||||
const ch = CHAPTERS[idx];
|
||||
const imgPath = ch ? `./images/${ch.img}` : '';
|
||||
// Match chapter by semantic n (immune to section index drift caused by non-chapter ## headings)
|
||||
const ch = CHAPTERS.find(c => c.n === chN);
|
||||
const imgPath = (ch && ch.img) ? `./images/${ch.img}` : '';
|
||||
const paragraphs = body.split(/\n\s*\n/).map(p => {
|
||||
const line = p.trim();
|
||||
if (!line) return '';
|
||||
|
||||
3663
web/novel.md
3663
web/novel.md
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user