@kenjura/ursa 0.72.0 → 0.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/meta/default-template.html +32 -6
- package/meta/default.css +365 -175
- package/meta/menu.js +153 -80
- package/meta/search.js +7 -13
- package/meta/sectionify.js +10 -0
- package/meta/toc-generator.js +12 -6
- package/meta/widgets.js +376 -0
- package/package.json +1 -1
- package/src/dev.js +39 -11
- package/src/helper/automenu.js +102 -12
- package/src/helper/breadcrumbs.js +42 -0
- package/src/helper/build/autoIndex.js +80 -23
- package/src/helper/build/menu.js +4 -4
- package/src/helper/customMenu.js +118 -29
- package/src/helper/imageProcessor.js +38 -8
- package/src/jobs/generate.js +52 -9
package/src/jobs/generate.js
CHANGED
|
@@ -39,6 +39,7 @@ import { createWhitelistFilter } from "../helper/whitelistFilter.js";
|
|
|
39
39
|
import { processAllImages, transformImageTags, clearImageCache, copyAllImagesFast } from "../helper/imageProcessor.js";
|
|
40
40
|
import { extractImageReferences } from "../helper/imageExtractor.js";
|
|
41
41
|
import { checkFileSize, readFileStreaming, formatFileSize } from "../helper/streamingReader.js";
|
|
42
|
+
import { generateBreadcrumbs } from "../helper/breadcrumbs.js";
|
|
42
43
|
import {
|
|
43
44
|
loadNavCache,
|
|
44
45
|
saveNavCache,
|
|
@@ -462,7 +463,9 @@ export async function generate({
|
|
|
462
463
|
const searchUrl = relativePath.startsWith('/') ? relativePath : '/' + relativePath;
|
|
463
464
|
|
|
464
465
|
// Generate title from filename (in title case)
|
|
465
|
-
|
|
466
|
+
// For index/home files, use parent folder name instead
|
|
467
|
+
const titleBase = (base === 'index' || base === 'home') ? basename(dirname(file)) : base;
|
|
468
|
+
const title = toTitleCase(titleBase || base);
|
|
466
469
|
|
|
467
470
|
// Always add to search index (lightweight: title + path only, content added lazily)
|
|
468
471
|
searchIndex.push({
|
|
@@ -543,6 +546,18 @@ export async function generate({
|
|
|
543
546
|
useWorker: true
|
|
544
547
|
});
|
|
545
548
|
|
|
549
|
+
// Inject default H1 if body doesn't start with one
|
|
550
|
+
if (!body || !body.trimStart().startsWith('<h1')) {
|
|
551
|
+
const h1Title = fileMeta?.title || title;
|
|
552
|
+
body = `<h1>${h1Title}</h1>\n` + (body || '');
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Inject breadcrumbs before the H1
|
|
556
|
+
const breadcrumbs = generateBreadcrumbs(dir, base, fileMeta);
|
|
557
|
+
if (breadcrumbs) {
|
|
558
|
+
body = breadcrumbs + body;
|
|
559
|
+
}
|
|
560
|
+
|
|
546
561
|
// Inject frontmatter table after first H1 (for markdown files with metadata)
|
|
547
562
|
if ((type === '.md' || type === '.mdx') && fileMeta) {
|
|
548
563
|
body = injectFrontmatterTable(body, fileMeta);
|
|
@@ -640,7 +655,7 @@ export async function generate({
|
|
|
640
655
|
// Build final HTML with all replacements in a single regex pass
|
|
641
656
|
// This avoids creating 8 intermediate strings
|
|
642
657
|
const replacements = {
|
|
643
|
-
"${title}": title,
|
|
658
|
+
"${title}": fileMeta?.title || title,
|
|
644
659
|
"${menu}": menu,
|
|
645
660
|
"${meta}": JSON.stringify(fileMeta),
|
|
646
661
|
"${transformedMetadata}": lazyTransformedMeta,
|
|
@@ -654,13 +669,19 @@ export async function generate({
|
|
|
654
669
|
const pattern = /\$\{(title|menu|meta|transformedMetadata|body|styleLink|customScript|searchIndex|footer)\}/g;
|
|
655
670
|
let finalHtml = template.replace(pattern, (match) => replacements[match] ?? match);
|
|
656
671
|
|
|
657
|
-
//
|
|
672
|
+
// Add menu data attributes to body
|
|
658
673
|
if (customMenuInfo) {
|
|
659
|
-
const menuPosition = customMenuInfo.menuPosition || '
|
|
674
|
+
const menuPosition = customMenuInfo.menuPosition || 'top';
|
|
660
675
|
finalHtml = finalHtml.replace(
|
|
661
676
|
/<body([^>]*)>/,
|
|
662
677
|
`<body$1 data-custom-menu="${customMenuInfo.menuJsonPath}" data-menu-position="${menuPosition}">`
|
|
663
678
|
);
|
|
679
|
+
} else {
|
|
680
|
+
// No custom menu — default to top menu
|
|
681
|
+
finalHtml = finalHtml.replace(
|
|
682
|
+
/<body([^>]*)>/,
|
|
683
|
+
`<body$1 data-menu-position="top">`
|
|
684
|
+
);
|
|
664
685
|
}
|
|
665
686
|
|
|
666
687
|
// Resolve relative URLs in raw HTML elements (img src, etc.)
|
|
@@ -802,7 +823,7 @@ export async function generate({
|
|
|
802
823
|
// Include menuPosition in the JSON so client knows how to render
|
|
803
824
|
const customMenuJson = JSON.stringify({
|
|
804
825
|
menuData: menuInfo.menuData,
|
|
805
|
-
menuPosition: menuInfo.menuPosition || '
|
|
826
|
+
menuPosition: menuInfo.menuPosition || 'top',
|
|
806
827
|
});
|
|
807
828
|
progress.log(`Writing custom menu: ${menuInfo.menuJsonPath}`);
|
|
808
829
|
await outputFile(customMenuPath, customMenuJson);
|
|
@@ -1027,6 +1048,9 @@ export async function generate({
|
|
|
1027
1048
|
// Print profiler report
|
|
1028
1049
|
progress.log(profiler.report());
|
|
1029
1050
|
|
|
1051
|
+
// Terminate worker pool so threads don't keep the process alive
|
|
1052
|
+
await terminateParserPool();
|
|
1053
|
+
|
|
1030
1054
|
// Return deferred processing promises if in deferred mode
|
|
1031
1055
|
// Caller can await these to know when background processing is complete
|
|
1032
1056
|
return {
|
|
@@ -1084,8 +1108,9 @@ export async function regenerateSingleFile(changedFile, {
|
|
|
1084
1108
|
.replace(parse(changedFile).ext, ".html");
|
|
1085
1109
|
const url = '/' + outputFilename.replace(output, '');
|
|
1086
1110
|
|
|
1087
|
-
// Title from filename
|
|
1088
|
-
const
|
|
1111
|
+
// Title from filename (for index/home, use parent folder name)
|
|
1112
|
+
const titleBase = (base === 'index' || base === 'home') ? basename(dirname(changedFile)) : base;
|
|
1113
|
+
const title = toTitleCase(titleBase || base);
|
|
1089
1114
|
|
|
1090
1115
|
// Extract metadata
|
|
1091
1116
|
const fileMeta = extractMetadata(rawBody);
|
|
@@ -1117,6 +1142,18 @@ export async function regenerateSingleFile(changedFile, {
|
|
|
1117
1142
|
basename: base,
|
|
1118
1143
|
});
|
|
1119
1144
|
}
|
|
1145
|
+
|
|
1146
|
+
// Inject default H1 if body doesn't start with one
|
|
1147
|
+
if (!body || !body.trimStart().startsWith('<h1')) {
|
|
1148
|
+
const h1Title = fileMeta?.title || title;
|
|
1149
|
+
body = `<h1>${h1Title}</h1>\n` + (body || '');
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Inject breadcrumbs before the H1
|
|
1153
|
+
const breadcrumbs = generateBreadcrumbs(dir, base, fileMeta);
|
|
1154
|
+
if (breadcrumbs) {
|
|
1155
|
+
body = breadcrumbs + body;
|
|
1156
|
+
}
|
|
1120
1157
|
|
|
1121
1158
|
// Inject frontmatter table for markdown/mdx files
|
|
1122
1159
|
if ((type === '.md' || type === '.mdx') && fileMeta) {
|
|
@@ -1191,7 +1228,7 @@ export async function regenerateSingleFile(changedFile, {
|
|
|
1191
1228
|
// Build final HTML
|
|
1192
1229
|
let finalHtml = template;
|
|
1193
1230
|
const replacements = {
|
|
1194
|
-
"${title}": title,
|
|
1231
|
+
"${title}": fileMeta?.title || title,
|
|
1195
1232
|
"${menu}": menu,
|
|
1196
1233
|
"${meta}": JSON.stringify(fileMeta),
|
|
1197
1234
|
"${transformedMetadata}": transformedMetadata,
|
|
@@ -1207,11 +1244,17 @@ export async function regenerateSingleFile(changedFile, {
|
|
|
1207
1244
|
|
|
1208
1245
|
// If this page has a custom menu, add data attributes to body
|
|
1209
1246
|
if (customMenuInfo) {
|
|
1210
|
-
const menuPosition = customMenuInfo.menuPosition || '
|
|
1247
|
+
const menuPosition = customMenuInfo.menuPosition || 'top';
|
|
1211
1248
|
finalHtml = finalHtml.replace(
|
|
1212
1249
|
/<body([^>]*)>/,
|
|
1213
1250
|
`<body$1 data-custom-menu="${customMenuInfo.menuJsonPath}" data-menu-position="${menuPosition}">`
|
|
1214
1251
|
);
|
|
1252
|
+
} else {
|
|
1253
|
+
// No custom menu — default to top menu
|
|
1254
|
+
finalHtml = finalHtml.replace(
|
|
1255
|
+
/<body([^>]*)>/,
|
|
1256
|
+
`<body$1 data-menu-position="top">`
|
|
1257
|
+
);
|
|
1215
1258
|
}
|
|
1216
1259
|
|
|
1217
1260
|
// Resolve relative URLs in raw HTML elements (img src, etc.)
|