From bda599bfb43841a67ca36381939ce7b7e1fa4c70 Mon Sep 17 00:00:00 2001 From: yhirose Date: Sat, 28 Feb 2026 20:56:46 -0500 Subject: [PATCH] Fix base_dir for GitHub PageS --- .gitignore | 1 + docs-gen/README.md | 39 +++++++++++++++++++++++++++++------ docs-gen/src/builder.rs | 40 +++++++++++++++++++++--------------- docs-gen/src/config.rs | 7 ++++++- docs-src/config.toml | 3 +++ docs-src/static/js/main.js | 5 ++++- docs-src/templates/base.html | 12 +++++------ justfile | 5 +++++ test/Makefile | 2 +- 9 files changed, 82 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 4093094..b4e5e56 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ test/*.pem test/*.srl test/*.log test/_build_* +test/cpp-httplib/ work/ benchmark/server* docs-gen/target/ diff --git a/docs-gen/README.md b/docs-gen/README.md index ddec2b0..4923a48 100644 --- a/docs-gen/README.md +++ b/docs-gen/README.md @@ -54,6 +54,7 @@ docs-src/ [site] title = "My Project" base_url = "https://example.github.io/my-project" +base_path = "/my-project" [i18n] default_lang = "en" @@ -64,6 +65,19 @@ theme = "base16-eighties.dark" # Dark mode syntax theme (syntect built-in theme_light = "base16-ocean.light" # Light mode syntax theme (optional) ``` +### `base_path` + +`base_path` controls the URL prefix prepended to all generated links, CSS/JS paths, and redirects. + +| Value | Use case | +|---|---| +| `"/my-project"` | GitHub Pages (`https://user.github.io/my-project/`) | +| `""` | Local development at `http://localhost:8000/` | + +Leave empty for local-only use; set to `"/"` before deploying to GitHub Pages. + +### `highlight` + When `theme_light` is set, code blocks are rendered twice (dark and light) and toggled via CSS classes `.code-dark` / `.code-light`. Available themes: `base16-ocean.dark`, `base16-ocean.light`, `base16-eighties.dark`, `base16-mocha.dark`, `InspiredGitHub`, `Solarized (dark)`, `Solarized (light)`. @@ -89,13 +103,25 @@ order: 1 Markdown files are mapped to URLs as follows: -| File path | URL | Output file | -|-----------------------|-----------------|--------------------------| -| `en/index.md` | `/en/` | `en/index.html` | -| `en/tour/index.md` | `/en/tour/` | `en/tour/index.html` | -| `en/tour/01-foo.md` | `/en/tour/01-foo/` | `en/tour/01-foo/index.html` | +| File path | URL | Output file | +|-----------------------|-----------------------------|-------------------------------------| +| `en/index.md` | `/en/` | `en/index.html` | +| `en/tour/index.md` | `/en/tour/` | `en/tour/index.html` | +| `en/tour/01-foo.md` | `/en/tour/01-foo/` | `en/tour/01-foo/index.html` | -A root `index.html` is generated automatically, redirecting `/` to `//` (respecting `localStorage` preference). +A root `index.html` is generated automatically, redirecting `/` to `//` (respecting `localStorage` preference). + +## Local Debugging vs GitHub Pages + +To preview locally with the same URL structure as GitHub Pages, set `base_path = "/cpp-httplib"` in `config.toml`, then: + +```bash +./docs-gen/target/release/docs-gen docs-src --out /tmp/test/cpp-httplib +cd /tmp/test && python3 -m http.server +# Open http://localhost:8000/cpp-httplib/ +``` + +For a plain local preview (no prefix), set `base_path = ""` and open `http://localhost:8000/`. ## Navigation @@ -122,6 +148,7 @@ Templates use [Tera](https://keats.github.io/tera/) syntax. Available variables: | `lang` | string | Current language code | | `site.title` | string | Site title from config | | `site.base_url` | string | Base URL from config | +| `site.base_path` | string | Base path prefix (e.g. `"/cpp-httplib"` or `""`) | | `site.langs` | list | Available language codes | ### page.html only diff --git a/docs-gen/src/builder.rs b/docs-gen/src/builder.rs index 74c24f7..20d6b57 100644 --- a/docs-gen/src/builder.rs +++ b/docs-gen/src/builder.rs @@ -27,6 +27,7 @@ struct SiteContext { title: String, version: Option, base_url: String, + base_path: String, langs: Vec, } @@ -67,7 +68,7 @@ pub fn build(src: &Path, out: &Path) -> Result<()> { continue; } - let pages = collect_pages(&pages_dir, lang, out, &renderer)?; + let pages = collect_pages(&pages_dir, lang, out, &renderer, &config.site.base_path)?; let nav = build_nav(&pages); for page in &pages { @@ -81,7 +82,7 @@ pub fn build(src: &Path, out: &Path) -> Result<()> { let section_nav: Vec<&NavItem> = nav .iter() .filter(|item| { - let item_section = extract_section(&item.url); + let item_section = extract_section(&item.url, &config.site.base_path); item_section == page.section }) .collect(); @@ -98,6 +99,7 @@ pub fn build(src: &Path, out: &Path) -> Result<()> { title: config.site.title.clone(), version: config.site.version.clone(), base_url: config.site.base_url.clone(), + base_path: config.site.base_path.clone(), langs: config.i18n.langs.clone(), }); @@ -148,6 +150,7 @@ fn collect_pages( lang: &str, out: &Path, renderer: &MarkdownRenderer, + base_path: &str, ) -> Result> { let mut pages = Vec::new(); @@ -172,30 +175,30 @@ fn collect_pages( // Compute URL and output path let (url, out_path) = if rel.file_name().map_or(false, |f| f == "index.md") { - // index.md -> //dir/ + // index.md -> //dir/ let parent = rel.parent().unwrap_or(Path::new("")); if parent.as_os_str().is_empty() { // Root index.md ( - format!("/{}/", lang), + format!("{}/{}/", base_path, lang), out.join(lang).join("index.html"), ) } else { ( - format!("/{}/{}/", lang, parent.display()), + format!("{}/{}/{}/", base_path, lang, parent.display()), out.join(lang).join(parent).join("index.html"), ) } } else { - // foo.md -> //foo/ + // foo.md -> //foo/ let stem = rel.with_extension(""); ( - format!("/{}/{}/", lang, stem.display()), + format!("{}/{}/{}/", base_path, lang, stem.display()), out.join(lang).join(&stem).join("index.html"), ) }; - let section = extract_section(&url); + let section = extract_section(&url, base_path); pages.push(Page { frontmatter, @@ -210,9 +213,11 @@ fn collect_pages( Ok(pages) } -fn extract_section(url: &str) -> String { +fn extract_section(url: &str, base_path: &str) -> String { + // Strip base_path prefix before parsing + let stripped = url.strip_prefix(base_path).unwrap_or(url); // URL format: // or //section/... - let parts: Vec<&str> = url.trim_matches('/').split('/').collect(); + let parts: Vec<&str> = stripped.trim_matches('/').split('/').collect(); if parts.len() >= 2 { parts[1].to_string() } else { @@ -249,7 +254,7 @@ fn build_nav(pages: &[Page]) -> Vec { // Find the section index page let index_page = section_pages .iter() - .find(|p| p.rel_path.ends_with("index.md") && extract_section(&p.url) == section); + .find(|p| p.rel_path.ends_with("index.md") && p.section == section); let section_title = index_page .map(|p| p.frontmatter.title.clone()) @@ -260,7 +265,7 @@ fn build_nav(pages: &[Page]) -> Vec { let children: Vec = section_pages .iter() - .filter(|p| !p.rel_path.ends_with("index.md") || extract_section(&p.url) != section) + .filter(|p| !p.rel_path.ends_with("index.md") || p.section != section) .map(|p| NavItem { title: p.frontmatter.title.clone(), url: p.url.clone(), @@ -294,6 +299,7 @@ fn set_active(item: &mut NavItem, current_url: &str) { } fn generate_root_redirect(out: &Path, config: &SiteConfig) -> Result<()> { + let base_path = &config.site.base_path; let html = format!( r#" @@ -301,19 +307,19 @@ fn generate_root_redirect(out: &Path, config: &SiteConfig) -> Result<()> { - + Redirecting... -

Redirecting to /{default_lang}/...

+

Redirecting to {base_path}/{default_lang}/...

"#, - config.i18n.default_lang, default_lang = config.i18n.default_lang, + base_path = base_path, ); fs::write(out.join("index.html"), html)?; diff --git a/docs-gen/src/config.rs b/docs-gen/src/config.rs index 007ee47..db3302d 100644 --- a/docs-gen/src/config.rs +++ b/docs-gen/src/config.rs @@ -14,6 +14,8 @@ pub struct Site { pub title: String, pub version: Option, pub base_url: String, + #[serde(default)] + pub base_path: String, } #[derive(Debug, Deserialize)] @@ -33,8 +35,11 @@ impl SiteConfig { let path = src_dir.join("config.toml"); let content = std::fs::read_to_string(&path).with_context(|| format!("Failed to read {}", path.display()))?; - let config: SiteConfig = + let mut config: SiteConfig = toml::from_str(&content).with_context(|| format!("Failed to parse {}", path.display()))?; + // Normalize base_path: strip trailing slash (but keep empty for root) + let bp = config.site.base_path.trim_end_matches('/').to_string(); + config.site.base_path = bp; Ok(config) } diff --git a/docs-src/config.toml b/docs-src/config.toml index faeaa5b..5143d73 100644 --- a/docs-src/config.toml +++ b/docs-src/config.toml @@ -2,6 +2,9 @@ title = "cpp-httplib" version = "0.35.0" base_url = "https://yhirose.github.io/cpp-httplib" +# Base path for URL generation. Use "/cpp-httplib" for GitHub Pages, +# or "" for local development (python3 -m http.server). +base_path = "/cpp-httplib" [i18n] default_lang = "en" diff --git a/docs-src/static/js/main.js b/docs-src/static/js/main.js index 3bdb6bb..5ed3fbe 100644 --- a/docs-src/static/js/main.js +++ b/docs-src/static/js/main.js @@ -19,8 +19,11 @@ e.preventDefault(); var lang = link.getAttribute('data-lang'); localStorage.setItem('preferred-lang', lang); + var basePath = document.documentElement.getAttribute('data-base-path') || ''; var path = window.location.pathname; - var newPath = path.replace(/^\/[a-z]{2}\//, '/' + lang + '/'); + // Strip base path prefix, replace lang, then re-add base path + var pathWithoutBase = path.slice(basePath.length); + var newPath = basePath + pathWithoutBase.replace(/^\/[a-z]{2}\//, '/' + lang + '/'); window.location.href = newPath; }); })(); diff --git a/docs-src/templates/base.html b/docs-src/templates/base.html index 305587b..5644f16 100644 --- a/docs-src/templates/base.html +++ b/docs-src/templates/base.html @@ -1,10 +1,10 @@ - + {{ page.title }} - {{ site.title }} - + + diff --git a/justfile b/justfile index 7d50f18..42eff67 100644 --- a/justfile +++ b/justfile @@ -48,3 +48,8 @@ bench: docs: cargo build --release --manifest-path docs-gen/Cargo.toml ./docs-gen/target/release/docs-gen docs-src --out docs + +docs-test: + cargo build --release --manifest-path docs-gen/Cargo.toml + ./docs-gen/target/release/docs-gen docs-src --out test/cpp-httplib + cd test && python3 -m http.server diff --git a/test/Makefile b/test/Makefile index 62e5756..9021a9c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -254,5 +254,5 @@ cert.pem: ./gen-certs.sh clean: - rm -rf test test_split test_mbedtls test_split_mbedtls test_wolfssl test_split_wolfssl test_no_tls, test_split_no_tls test_proxy test_proxy_mbedtls test_proxy_wolfssl server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM *_shard_*.log + rm -rf test test_split test_mbedtls test_split_mbedtls test_wolfssl test_split_wolfssl test_no_tls, test_split_no_tls test_proxy test_proxy_mbedtls test_proxy_wolfssl server_fuzzer *.pem *.0 *.o *.1 *.srl httplib.h httplib.cc _build* *.dSYM *_shard_*.log cpp-httplib