Improve breadcrumb and code block controls

This commit is contained in:
2026-05-29 15:08:44 +08:00
parent cf2d5fc77b
commit eb73d41d35
7 changed files with 465 additions and 54 deletions
+68 -31
View File
@@ -103,6 +103,72 @@ function renderPage({ title, nav, docTheme, customThemeHref, themes, initialThem
});
}
function normalizeEntryName(name) {
return String(name)
.replace(/\.(md|markdown)$/i, '')
.toLowerCase()
.replace(/[\s\-_.]+/g, '');
}
function selectDefaultFile(nav, rootDir) {
const rootFiles = Array.isArray(nav)
? nav.filter((item) => item.type === 'file')
: [];
if (rootFiles.length === 0) {
return null;
}
const rootDirName = normalizeEntryName(path.basename(rootDir));
const matchers = [
(name) => name.includes('index'),
(name) => name.includes('首页'),
(name) => rootDirName && (name.includes(rootDirName) || rootDirName.includes(name)),
(name) => name.includes('readme')
];
for (const matcher of matchers) {
const matched = rootFiles.find((item) => matcher(normalizeEntryName(item.name)));
if (matched) {
return matched.path;
}
}
return rootFiles[0].path;
}
function buildInitialContent(renderer, rootDir, relativeFilePath) {
if (!relativeFilePath) {
return null;
}
const fullPath = path.join(rootDir, relativeFilePath);
if (!fs.existsSync(fullPath)) {
return null;
}
const content = fs.readFileSync(fullPath, 'utf-8');
const env = { filePath: relativeFilePath };
const html = renderer.render(stripLeadingTitle(content), env);
const stats = fs.statSync(fullPath);
const match = content.match(/^#\s+(.+)$/m);
const fileName = path.basename(relativeFilePath);
const pageTitle = match ? match[1].trim() : fileName.replace(/\.(md|markdown)$/i, '');
const chineseChars = (content.match(/[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/g) || []).length;
const englishWords = (content.match(/[a-zA-Z0-9]+/g) || []).length;
return {
title: pageTitle,
html,
meta: {
created: stats.birthtime.toISOString(),
modified: stats.mtime.toISOString(),
wordCount: chineseChars + englishWords,
fileName
}
};
}
/**
* Create and start the webook server
*/
@@ -205,37 +271,8 @@ function createServer(opts) {
app.get('/', (req, res) => {
try {
const nav = scanDirectory(dir);
// Try to find a README.md or index.md as default
const defaultFiles = ['README.md', 'readme.md', 'index.md', 'INDEX.md'];
let initialContent = null;
let defaultFile = null;
for (const df of defaultFiles) {
const dfPath = path.join(dir, df);
if (fs.existsSync(dfPath)) {
defaultFile = df;
const content = fs.readFileSync(dfPath, 'utf-8');
const env = { filePath: df };
const html = renderer.render(stripLeadingTitle(content), env);
const stats = fs.statSync(dfPath);
const match = content.match(/^#\s+(.+)$/m);
const dfTitle = match ? match[1].trim() : df.replace(/\.md$/i, '');
const chineseChars = (content.match(/[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff]/g) || []).length;
const englishWords = (content.match(/[a-zA-Z0-9]+/g) || []).length;
initialContent = {
title: dfTitle,
html,
meta: {
created: stats.birthtime.toISOString(),
modified: stats.mtime.toISOString(),
wordCount: chineseChars + englishWords,
fileName: df
}
};
break;
}
}
const defaultFile = selectDefaultFile(nav, dir);
const initialContent = buildInitialContent(renderer, dir, defaultFile);
res.send(renderPage({
title,