Explorar el Código

Fix base_dir for GitHub PageS

yhirose hace 1 mes
padre
commit
bda599bfb4

+ 1 - 0
.gitignore

@@ -55,6 +55,7 @@ test/*.pem
 test/*.srl
 test/*.log
 test/_build_*
+test/cpp-httplib/
 work/
 benchmark/server*
 docs-gen/target/

+ 33 - 6
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 `"/<repo-name>"` 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`         | `<base_path>/en/`           | `en/index.html`                     |
+| `en/tour/index.md`    | `<base_path>/en/tour/`      | `en/tour/index.html`                |
+| `en/tour/01-foo.md`   | `<base_path>/en/tour/01-foo/` | `en/tour/01-foo/index.html`       |
+
+A root `index.html` is generated automatically, redirecting `<base_path>/` to `<base_path>/<default_lang>/` (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/
+```
 
-A root `index.html` is generated automatically, redirecting `/` to `/<default_lang>/` (respecting `localStorage` preference).
+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

+ 23 - 17
docs-gen/src/builder.rs

@@ -27,6 +27,7 @@ struct SiteContext {
     title: String,
     version: Option<String>,
     base_url: String,
+    base_path: String,
     langs: Vec<String>,
 }
 
@@ -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<Vec<Page>> {
     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 -> /<lang>/dir/
+            // index.md -> <base_path>/<lang>/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 -> /<lang>/foo/
+            // foo.md -> <base_path>/<lang>/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: /<lang>/ or /<lang>/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<NavItem> {
         // 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<NavItem> {
 
         let children: Vec<NavItem> = 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#"<!DOCTYPE html>
 <html>
@@ -301,19 +307,19 @@ fn generate_root_redirect(out: &Path, config: &SiteConfig) -> Result<()> {
 <meta charset="utf-8">
 <script>
 (function() {{
-  var lang = localStorage.getItem('preferred-lang') || '{}';
-  window.location.replace('/' + lang + '/');
+  var lang = localStorage.getItem('preferred-lang') || '{default_lang}';
+  window.location.replace('{base_path}/' + lang + '/');
 }})();
 </script>
-<meta http-equiv="refresh" content="0;url=/{default_lang}/">
+<meta http-equiv="refresh" content="0;url={base_path}/{default_lang}/">
 <title>Redirecting...</title>
 </head>
 <body>
-<p>Redirecting to <a href="/{default_lang}/">/{default_lang}/</a>...</p>
+<p>Redirecting to <a href="{base_path}/{default_lang}/">{base_path}/{default_lang}/</a>...</p>
 </body>
 </html>"#,
-        config.i18n.default_lang,
         default_lang = config.i18n.default_lang,
+        base_path = base_path,
     );
 
     fs::write(out.join("index.html"), html)?;

+ 6 - 1
docs-gen/src/config.rs

@@ -14,6 +14,8 @@ pub struct Site {
     pub title: String,
     pub version: Option<String>,
     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)
     }
 

+ 3 - 0
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"

+ 4 - 1
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;
   });
 })();

+ 6 - 6
docs-src/templates/base.html

@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html lang="{{ lang }}">
+<html lang="{{ lang }}" data-base-path="{{ site.base_path }}">
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title>{{ page.title }} - {{ site.title }}</title>
-  <link rel="stylesheet" href="/css/main.css">
+  <link rel="stylesheet" href="{{ site.base_path }}/css/main.css">
   <script>
     (function() {
       var t = localStorage.getItem('preferred-theme');
@@ -16,11 +16,11 @@
 <body>
   <header class="header">
     <div class="header-inner">
-      <a href="/{{ lang }}/" class="header-title">{{ site.title }}{% if site.version %} <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v{{ site.version }}</span>{% endif %}</a>
+      <a href="{{ site.base_path }}/{{ lang }}/" class="header-title">{{ site.title }}{% if site.version %} <span style="font-size:0.75em;font-weight:normal;margin-left:4px">v{{ site.version }}</span>{% endif %}</a>
       <div class="header-spacer"></div>
       <nav class="header-nav">
-        <a href="/{{ lang }}/">Home</a>
-        <a href="/{{ lang }}/tour/">Tour</a>
+        <a href="{{ site.base_path }}/{{ lang }}/">Home</a>
+        <a href="{{ site.base_path }}/{{ lang }}/tour/">Tour</a>
       </nav>
       <div class="header-tools">
         <button class="theme-toggle" aria-label="Toggle theme"></button>
@@ -49,6 +49,6 @@
     &copy; 2026 yhirose. All rights reserved.
   </footer>
 
-  <script src="/js/main.js"></script>
+  <script src="{{ site.base_path }}/js/main.js"></script>
 </body>
 </html>

+ 5 - 0
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

+ 1 - 1
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