Docs: more tweaks to the documentation

This commit is contained in:
Pieter Vander Vennet 2025-10-08 01:29:00 +02:00
parent 428e462faa
commit 558a899218
5 changed files with 84 additions and 105 deletions

View file

@ -12,6 +12,7 @@
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script src="./_paths.js"></script>
<script> <script>
let language = "en" let language = "en"
if (document.location.pathname.startsWith("/nl/")) { if (document.location.pathname.startsWith("/nl/")) {
@ -25,7 +26,7 @@
repo: "https://source.mapcomplete.org/mapcomplete/mapcomplete/", repo: "https://source.mapcomplete.org/mapcomplete/mapcomplete/",
search: { search: {
maxAge: 86400000, // 1 day cache maxAge: 86400000, // 1 day cache
paths: "auto", paths: docsify_paths, // Defined in sibling script _paths.js
auto2top: true, auto2top: true,
placeholder: language === "nl" ? "Zoeken" : "Search", placeholder: language === "nl" ? "Zoeken" : "Search",
noData: language === "nl" ? "Geen resultaten gevonden" : "No Results found", noData: language === "nl" ? "Geen resultaten gevonden" : "No Results found",

View file

@ -1,85 +0,0 @@
<!DOCTYPE html>
<!-- This is meant to be run with docsify -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
</head>
<body>
<div id="app"></div>
<script>
let language = "en"
if (document.location.pathname.startsWith("/nl/")) {
language = "nl"
}
window.$docsify = {
name: "MapComplete Docs",
noEmoji: true,
loadSidebar: true,
autoToTop: true,
repo: "https://source.mapcomplete.org/mapcomplete/mapcomplete/",
search: {
maxAge: 86400000, // 1 day cache
paths: "auto",
auto2top: true,
placeholder: language === "nl" ? "Zoeken" : "Search",
noData: language === "nl" ? "Geen resultaten gevonden" : "No Results found",
depth: 2,
},
}
</script>
<!-- Docsify v4 -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.js"></script>
<style>
.sidebar a {
line-height: 1.3em;
}
.sidebar {
padding-left: 1rem;
}
.sidebar strong {
font-size: 1.2em;
}
.sidebar p:has(strong){
margin: 0;
top: 0;
position: sticky;
background: white;
padding: 0.25rem;
}
.sidebar a:has(strong) {
margin-top: 1em;
}
.active a {
color: #42b983 !important;;
color: var(--theme-color, #42b983) !important;
font-weight: 600 !important;
border-right: 2px solid !important;
}
/*noinspection CssUnusedSymbol*/
.back-to-mc {
display: block;
padding: 0.5rem;
background: #3f3f39;
color: white;
border: 2px solid white;
border-radius: 0.5rem;
}
</style>
</body>
</html>

View file

@ -141,7 +141,7 @@
"optimize-images-scaledown-jpg": "cd assets/ && find . -regextype sed -regex \".*/.*.\\(jpg\\|JPG\\|JPEG\\|jpeg\\)\" -exec mogrify -resize 640x640\\> '{}' \\; && echo 'JPGs are optimized'", "optimize-images-scaledown-jpg": "cd assets/ && find . -regextype sed -regex \".*/.*.\\(jpg\\|JPG\\|JPEG\\|jpeg\\)\" -exec mogrify -resize 640x640\\> '{}' \\; && echo 'JPGs are optimized'",
"generate:schemas": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && ./scripts/generateSchemas.sh && echo 'tsjson is done' && vite-node scripts/fixSchemas.ts ", "generate:schemas": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && ./scripts/generateSchemas.sh && echo 'tsjson is done' && vite-node scripts/fixSchemas.ts ",
"reuse-compliance": "reuse lint", "reuse-compliance": "reuse lint",
"housekeeping": "export NODE_OPTIONS=\"--max-old-space-size=1200\" && git pull && npx update-browserslist-db@latest && npm run generate && npm run generate:schemas && npm run generate:contributor-list && vite-node scripts/fetchLanguages.ts && vite-node scripts/generateSunnyUnlabeled.ts && npm run format && npm run lint:themes && git add assets/ langs/ Docs/ **/*.ts Docs/* src/* && vite-node scripts/generateTaginfoProjectFiles.ts && git commit -m 'chore: automated housekeeping...'", "housekeeping": "export NODE_OPTIONS=\"--max-old-space-size=1200\" && git pull && npx update-browserslist-db@latest && npm run generate && npm run generate:schemas && npm run generate:contributor-list && vite-node scripts/fetchLanguages.ts && vite-node scripts/generateSunnyUnlabeled.ts && npm run format && npm run lint:themes && git add assets/ langs/ Docs/ **/*.ts Docs/* src/* && git commit -m 'chore: automated housekeeping...'",
"###": "MICROSERVICES AND SERVER MAINTAINENCE", "###": "MICROSERVICES AND SERVER MAINTAINENCE",
"release": "standard-version && git push --follow-tags", "release": "standard-version && git push --follow-tags",
"release:minor": "standard-version --release-as minor", "release:minor": "standard-version --release-as minor",

View file

@ -130,6 +130,8 @@ class WikiPageGenerator {
} }
export class GenerateDocs extends Script { export class GenerateDocs extends Script {
private generatedPaths: string[] = []
constructor() { constructor() {
super("Generates various documentation files") super("Generates various documentation files")
} }
@ -192,7 +194,11 @@ export class GenerateDocs extends Script {
mkdirSync("./Docs/Themes") mkdirSync("./Docs/Themes")
} }
this.generateOverviewsForAllSingleLayer() this.generateOverviewsForAllSingleLayer()
this.generateNormalLayerOverview() this.generateNormalLayerOverview("Layers")
this.generateNormalLayerOverview("Layers", "nl")
this.generateNormalLayerOverview("Themes")
this.generateNormalLayerOverview("Themes", "nl")
if (!existsSync("./Docs/nl")) { if (!existsSync("./Docs/nl")) {
mkdirSync("./Docs/nl") mkdirSync("./Docs/nl")
} }
@ -212,10 +218,16 @@ export class GenerateDocs extends Script {
]) ])
new WikiPageGenerator().generate() new WikiPageGenerator().generate()
this.generatedPaths.push("wikiIndex.txt")
this.generateSidebar() // Must be last as it inspects the generated markdown files this.generateSidebar() // Must be last as it inspects the generated markdown files
this.generateSidebar("nl") this.generateSidebar("nl")
this.generatedPaths.push(".gitignore")
writeFileSync("./Docs/.gitignore", this.generatedPaths
.map(p => p.replace("./Docs/", ""))
.join("\n"), "utf-8")
console.log("Generated docs") console.log("Generated docs")
} }
@ -272,6 +284,7 @@ export class GenerateDocs extends Script {
writeFileSync(filename, warnAutomated + md + (options?.noWarn ? "" : "\n\n" + generatedFrom + "\n")) writeFileSync(filename, warnAutomated + md + (options?.noWarn ? "" : "\n\n" + generatedFrom + "\n"))
this.generatedPaths.push(filename)
} }
@ -725,8 +738,9 @@ export class GenerateDocs extends Script {
/** /**
* Generates the '_sidebar.md' file that is used by docsify * Generates the '_sidebar.md' file that is used by docsify
* Returns _all_ the filepaths (including ''hidden'' ones)
*/ */
private generateSidebar(subdirectory = "") { private generateSidebar(subdirectory = ""): string[] {
const tr = Translations.t.app.back.textFor(subdirectory) const tr = Translations.t.app.back.textFor(subdirectory)
const sidebar: string[] = [ const sidebar: string[] = [
`<a href='https://mapcomplete.org' class='back-to-mc'>${tr}</a>` `<a href='https://mapcomplete.org' class='back-to-mc'>${tr}</a>`
@ -734,8 +748,8 @@ export class GenerateDocs extends Script {
const allFiles = ScriptUtils.readDirRecSync("./Docs/" + subdirectory) const allFiles = ScriptUtils.readDirRecSync("./Docs/" + subdirectory)
.filter(path => path.endsWith(".md")) .filter(path => path.endsWith(".md"))
.filter(path => !path.startsWith("_")) .filter(path => !path.startsWith("_"))
.filter(path => !path.startsWith("./Docs/nl/") || path.startsWith("./Docs/" + subdirectory))
.map(path => path.substring("./Docs/".length + subdirectory.length + 1)) .map(path => path.substring("./Docs/".length + subdirectory.length + 1))
console.log("AllFiles: " + subdirectory, allFiles)
const perDirectory = new Map<string, string[]>() const perDirectory = new Map<string, string[]>()
function addFile(dir: string, path: string) { function addFile(dir: string, path: string) {
@ -761,6 +775,9 @@ export class GenerateDocs extends Script {
} }
} }
perDirectory.delete("nl")
// The directories to run over: // The directories to run over:
const directories: [string, Translation | string][] = [ const directories: [string, Translation | string][] = [
["", ""], ["", ""],
@ -769,7 +786,6 @@ export class GenerateDocs extends Script {
["Layers", new Translation({ en: "Overview of layers", nl: "Overzicht van de lagen" })], ["Layers", new Translation({ en: "Overview of layers", nl: "Overzicht van de lagen" })],
["Themes", new Translation({ en: "Overview of map themes", nl: "Overzicht van de themas" })], ["Themes", new Translation({ en: "Overview of map themes", nl: "Overzicht van de themas" })],
["UserTests", "Usability tests with users"], ["UserTests", "Usability tests with users"],
["nl", null] // indicate skip
] ]
@ -789,6 +805,9 @@ export class GenerateDocs extends Script {
} }
sidebar.push(`\n\n [**${titleStr}**](${dir}/README.md)`) sidebar.push(`\n\n [**${titleStr}**](${dir}/README.md)`)
} }
if (dir === "Layers" || dir == "Themes") {
continue
}
for (const path of files) { for (const path of files) {
if (path.startsWith("_") || path.endsWith("README.md")) { if (path.startsWith("_") || path.endsWith("README.md")) {
continue continue
@ -806,17 +825,60 @@ export class GenerateDocs extends Script {
noTableOfContents: true, noTableOfContents: true,
noWarn: true, noWarn: true,
}) })
const scriptPath = `./Docs/${subdirectory}/_paths.js`
writeFileSync(scriptPath, "var docsify_paths = " + JSON.stringify(allFiles))
this.generatedPaths.push(scriptPath)
return allFiles
} }
private generateNormalLayerOverview() { private generateNormalLayerOverview(type: "Layers" | "Themes", subdir = "") {
const doc = ["# Layers",
`The following layers are available in MapComplete ${Constants.vNumber}:`, const layerinfo: [string, string, string][] = []
MarkdownUtils.list( const source: {
Array.from(AllSharedLayers.sharedLayers.keys()).map( get(id: string): { id: string, name?: Translation, title?: Translation, description?: Translation },
(id) => `[${id}](./Layers/${id}.md)`, keys(): Iterable<string>
), }
)] = type === "Layers" ? AllSharedLayers.sharedLayers : AllKnownLayouts.allKnownLayouts
this.writeMarkdownFile("./Docs/Layers/README.md", doc.join("\n\n"), ["./assets/layers/*.json"]) const keys = Array.from(source.keys())
keys.sort()
for (const id of keys) {
const layer = source.get(id)
let name = layer.title
if (type === "Layers") {
const layer_ = (<LayerConfig><unknown>layer)
if (!layer_.isNormal()) {
continue
}
name = layer.name
}
layerinfo.push([`[${id}](./Layers/${id})`, name.textFor(subdir), (layer["shortDescription"] ?? layer.description)?.textFor(subdir)])
}
const titles = {
"Layers": new Translation({ en: "Layers", nl: "Lagen" }),
"Themes": new Translation({ en: "Themes", nl: "Kaartthema's" })
}
const intro: Record<string, TypedTranslation<{ version }>> = {
"Layers": new TypedTranslation<{ version }>({
en: "The following layers are available in MapComplete {version}:",
nl: "De volgende lagen zijn beschikbaar in MapComplete {version}:"
}),
"Themes": new TypedTranslation<{ version }>({
en: "The following themes are available in MapComplete {version}:",
nl: "De volgende kaartthemas zijn beschikbaar in MapComplete {version}:"
})
}
const doc = ["# " + titles[type].textFor(subdir),
intro[type].Subs({ version: Constants.vNumber }).textFor(subdir)
, MarkdownUtils.table(
["id", "name", "description"],
layerinfo)
]
this.writeMarkdownFile(`./Docs/${subdir}/${type}/README.md`, doc.join("\n\n"), [`./assets/${type.toLowerCase()}/*.json`])
} }
/** /**

View file

@ -225,10 +225,11 @@ export class Translation {
* @param copyContext if set, the context of 'this' will be added to the new translation. If not, _context will be removed alltogether * @param copyContext if set, the context of 'this' will be added to the new translation. If not, _context will be removed alltogether
* @return The object itself (this) if all strings are the same * @return The object itself (this) if all strings are the same
* *
* new Translation({"en": "This is a sentence. This is another sentence"}).FirstSentence().textFor("en") // "This is a sentence" * new Translation({"en": "This is a sentence. This is another sentence"}).FirstSentence().textFor("en") // => "This is a sentence"
* new Translation({"en": "This is a sentence <br/> This is another sentence"}).FirstSentence().textFor("en") // "This is a sentence" * new Translation({"en": "This is a sentence <br/> This is another sentence"}).FirstSentence().textFor("en") // => "This is a sentence"
* new Translation({"en": "This is a sentence <br> This is another sentence"}).FirstSentence().textFor("en") // "This is a sentence" * new Translation({"en": "This is a sentence \n\n This is another sentence"}).FirstSentence().textFor("en") // => "This is a sentence"
* new Translation({"en": "This is a sentence with a <b>bold</b> word. This is another sentence"}).FirstSentence().textFor("en") // "This is a sentence with a <b>bold</b> word" * new Translation({"en": "This is a sentence <br> This is another sentence"}).FirstSentence().textFor("en") // => "This is a sentence"
* new Translation({"en": "This is a sentence with a <b>bold</b> word. This is another sentence"}).FirstSentence().textFor("en") // => "This is a sentence with a <b>bold</b> word"
* @constructor * @constructor
*/ */
public FirstSentence(copyContext: boolean = false): Translation { public FirstSentence(copyContext: boolean = false): Translation {
@ -239,7 +240,7 @@ export class Translation {
continue continue
} }
let txt = this.translations[lng] let txt = this.translations[lng]
txt = txt.replace(/(\.|<br\/>|<br>|。).*/, "").trim() txt = txt.replace(/(\.|<br\/>|<br>||\n).*/s, "").trim()
txt = Utils.EllipsesAfter(txt, 255) txt = Utils.EllipsesAfter(txt, 255)
allSame = allSame && txt == this.translations[lng] allSame = allSame && txt == this.translations[lng]
tr[lng] = txt tr[lng] = txt