Compare commits
3 Commits
3e10890ed1
...
33c60aa08a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33c60aa08a | ||
| 15b2812507 | |||
| d46152ccb8 |
@@ -475,6 +475,27 @@
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -922,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>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,24 +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*(.+)$/);
|
||||
if (!m2) return;
|
||||
isPart2 = true;
|
||||
const cnMap = { '一': '01', '二': '02', '三': '03', '四': '04', '五': '05', '六': '06', '七': '07', '八': '08', '九': '09', '十': '10' };
|
||||
const arabic = cnMap[m2[1]] || m2[1];
|
||||
num = 'II · ' + arabic;
|
||||
title = m2[2];
|
||||
var kicker = 'PART II · CHAPTER ' + arabic;
|
||||
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 '';
|
||||
|
||||
Reference in New Issue
Block a user