diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 8024b54dc..226812d6c 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -1,20 +1,24 @@ -name: đŸĨ˜ Build & Deploy Docs HB +name: đŸĨ˜ Build & Deploy mkdocs Documentation on: pull_request: branches: - - main + - docs/deploy + - docs/deploy-test paths: # Trigger on changes to docs, mkdocs config, or the workflow itself - "docs/**" + - "!docs/book/**" - "mkdocs.yml" - ".github/workflows/build-deploy-docs.yml" push: branches: - - main + - docs/deploy + - docs/deploy-test paths: # Trigger on changes to docs, mkdocs config, or the workflow itself - "docs/**" + - "!docs/book/**" - "mkdocs.yml" - ".github/workflows/build-deploy-docs.yml" @@ -52,6 +56,7 @@ jobs: . ~/otp-27.0/activate echo "Erlang version:" erl -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().' + # Install system dependencies needed for HyperBEAM - name: Install system dependencies run: | @@ -62,15 +67,19 @@ jobs: ncurses-dev \ libssl-dev \ ca-certificates + # Debug step - display the region with syntax error - name: Debug syntax error region run: | echo "Showing the region with syntax error in hb_message.erl:" sed -n '1440,1460p' src/hb_message.erl || echo "File not found or cannot be read" + echo "Checking for syntax error fix files:" find . -name "*.erl.fix" -o -name "hb_message.erl.*" | grep -v ".beam" || echo "No fix files found" + echo "Erlang version:" . ~/otp-27.0/activate && erl -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().' + # Install rebar3 - name: Install rebar3 run: | @@ -79,12 +88,14 @@ jobs: curl -O https://s3.amazonaws.com/rebar3/rebar3 && chmod +x rebar3 sudo mv rebar3 /usr/local/bin/rebar3 . ~/otp-27.0/activate && rebar3 --version + # Install Rust toolchain (needed for WASM components) - name: Install Rust and Cargo run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH source "$HOME/.cargo/env" + # Setup Node.js - name: ⎔ Setup Node uses: actions/setup-node@v3 @@ -96,18 +107,20 @@ jobs: run: | python -m pip install --upgrade pip pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin - - name: 🛠 Build Docs + + - name: 🛠 Build mkdocs Documentation run: | . ~/otp-27.0/activate SKIP_COMPILE=1 SKIP_EDOC=1 ./docs/build-all.sh -v + # Build and deploy the artifacts to Arweave via ArDrive deploy: - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/docs/deploy' || github.ref == 'refs/heads/docs/deploy-test' runs-on: ubuntu-22.04 # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these deployments to complete. concurrency: - group: deploy + group: deploy-mkdocs cancel-in-progress: false steps: - name: âŦ‡ī¸ Checkout repo @@ -131,6 +144,7 @@ jobs: . ~/otp-27.0/activate echo "Erlang version:" erl -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().' + # Install system dependencies needed for HyperBEAM - name: Install system dependencies run: | @@ -141,15 +155,19 @@ jobs: ncurses-dev \ libssl-dev \ ca-certificates + # Debug step - display the region with syntax error - name: Debug syntax error region run: | echo "Showing the region with syntax error in hb_message.erl:" sed -n '1440,1460p' src/hb_message.erl || echo "File not found or cannot be read" + echo "Checking for syntax error fix files:" find . -name "*.erl.fix" -o -name "hb_message.erl.*" | grep -v ".beam" || echo "No fix files found" + echo "Erlang version:" . ~/otp-27.0/activate && erl -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().' + # Install rebar3 - name: Install rebar3 run: | @@ -158,17 +176,20 @@ jobs: curl -O https://s3.amazonaws.com/rebar3/rebar3 && chmod +x rebar3 sudo mv rebar3 /usr/local/bin/rebar3 . ~/otp-27.0/activate && rebar3 --version + # Install Rust toolchain (needed for WASM components) - name: Install Rust and Cargo run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH source "$HOME/.cargo/env" + # Install pip dependencies and cache them - name: đŸ“Ļ Install Python dependencies run: | python -m pip install --upgrade pip pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin + # Setup Node.js (needed for npx deploy command) - name: ⎔ Setup Node uses: actions/setup-node@v3 @@ -184,21 +205,34 @@ jobs: VER=`node --version`; echo "Node ver: $VER" VER=`npm --version`; echo "npm ver: $VER" . ~/otp-27.0/activate && erl -eval 'io:format("Erlang OTP version: ~s~n", [erlang:system_info(otp_release)]), halt().' - - name: 🛠 Build Docs + + - name: 🛠 Build mkdocs Documentation id: build_artifacts run: | . ~/otp-27.0/activate SKIP_COMPILE=1 SKIP_EDOC=1 ./docs/build-all.sh -v touch mkdocs-site/.nojekyll + echo "artifacts_output_dir=mkdocs-site" >> $GITHUB_OUTPUT + - name: 💾 Publish to Arweave id: publish_artifacts run: | - npx permaweb-deploy \ - --arns-name=dps-testing-facility \ - --ant-process=${{ secrets.ANT_PROCESS }} \ - --deploy-folder=${ARTIFACTS_OUTPUT_DIR} + if [[ "${{ github.ref }}" == "refs/heads/docs/deploy" ]]; then + echo "Deploying to production ArNS" + npx permaweb-deploy \ + --arns-name=hyperbeam \ + --ant-process=${{ secrets.ANT_PROCESS }} \ + --deploy-folder=${ARTIFACTS_OUTPUT_DIR} + elif [[ "${{ github.ref }}" == "refs/heads/docs/deploy-test" ]]; then + echo "Deploying to preview ArNS undername" + npx permaweb-deploy \ + --arns-name=hyperbeam \ + --undername=preview \ + --ant-process=${{ secrets.ANT_PROCESS }} \ + --deploy-folder=${ARTIFACTS_OUTPUT_DIR} + fi env: DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} ARTIFACTS_OUTPUT_DIR: ${{ steps.build_artifacts.outputs.artifacts_output_dir }} - ANT_PROCESS: ${{ secrets.ANT_PROCESS }} + ANT_PROCESS: ${{ secrets.ANT_PROCESS }} \ No newline at end of file diff --git a/.github/workflows/build-deploy-mdbook.yml b/.github/workflows/build-deploy-mdbook.yml new file mode 100644 index 000000000..717c493b9 --- /dev/null +++ b/.github/workflows/build-deploy-mdbook.yml @@ -0,0 +1,117 @@ +name: 📖 Build & Deploy mdBook Documentation + +on: + pull_request: + branches: + - edge + paths: + # Trigger on changes to source code, mdbook config, or the workflow itself + - "src/**" + - "docs/book/book.toml" + - "docs/book/custom.css" + - "docs/book/custom.js" + - "docs/generate-literate-docs.sh" + - ".github/workflows/build-deploy-mdbook.yml" + push: + branches: + - edge + paths: + # Trigger on changes to source code, mdbook config, or the workflow itself + - "src/**" + - "docs/book/book.toml" + - "docs/book/custom.css" + - "docs/book/custom.js" + - "docs/generate-literate-docs.sh" + - ".github/workflows/build-deploy-mdbook.yml" + + # Perform a release using a workflow dispatch + workflow_dispatch: + +defaults: + run: + shell: bash + +jobs: + # Run the build as part of PRs to confirm the site properly builds + check_build: + if: ${{ startsWith(github.ref, 'refs/pull/') }} + runs-on: ubuntu-22.04 + steps: + - name: âŦ‡ī¸ Checkout repo + uses: actions/checkout@v3 + + # Install mdBook for literate documentation + - name: 📖 Install mdBook + run: | + curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.40/mdbook-v0.4.40-x86_64-unknown-linux-gnu.tar.gz | tar xz + chmod +x mdbook + sudo mv mdbook /usr/local/bin/mdbook + mdbook --version + + - name: 📝 Generate Literate Erlang Documentation + run: | + ./docs/generate-literate-docs.sh -v + + - name: 📚 Build mdBook Documentation + run: | + cd docs/book && mdbook build + + # Build and deploy the mdBook to Arweave + deploy: + if: github.ref == 'refs/heads/edge' + runs-on: ubuntu-22.04 + # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. + # However, do NOT cancel in-progress runs as we want to allow these deployments to complete. + concurrency: + group: deploy-mdbook + cancel-in-progress: false + steps: + - name: âŦ‡ī¸ Checkout repo + uses: actions/checkout@v3 + + # Setup Node.js (needed for npx deploy command) + - name: ⎔ Setup Node + uses: actions/setup-node@v3 + with: + node-version: 22 + + - name: 👀 Env + run: | + echo "Event name: ${{ github.event_name }}" + echo "Git ref: ${{ github.ref }}" + echo "GH actor: ${{ github.actor }}" + echo "SHA: ${{ github.sha }}" + VER=`node --version`; echo "Node ver: $VER" + VER=`npm --version`; echo "npm ver: $VER" + + # Install mdBook for literate documentation + - name: 📖 Install mdBook + run: | + curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.40/mdbook-v0.4.40-x86_64-unknown-linux-gnu.tar.gz | tar xz + chmod +x mdbook + sudo mv mdbook /usr/local/bin/mdbook + mdbook --version + + - name: 📝 Generate Literate Erlang Documentation + run: | + ./docs/generate-literate-docs.sh -v + + - name: 📚 Build mdBook Documentation + id: build_mdbook + run: | + cd docs/book && mdbook build + cd ../.. + echo "mdbook_output_dir=docs/book/dist" >> $GITHUB_OUTPUT + + - name: 📖 Publish mdBook to Arweave + id: publish_mdbook + run: | + npx permaweb-deploy \ + --arns-name=hyperbeam \ + --undername=book \ + --ant-process=${{ secrets.ANT_PROCESS }} \ + --deploy-folder=${MDBOOK_OUTPUT_DIR} + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + MDBOOK_OUTPUT_DIR: ${{ steps.build_mdbook.outputs.mdbook_output_dir }} + ANT_PROCESS: ${{ secrets.ANT_PROCESS }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5823721c3..7f989aaeb 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,13 @@ mkdocs-site/ mkdocs-site-id.txt mkdocs-site-manifest.csv +# mdbook generated documentation +docs/book/dist/ + +# Generated literate Erlang documentation files +docs/literate-erlang/ +docs/book/src/*.erl.md + !test/admissible-report-wallet.json !test/admissible-report.json !test/config.json \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 4168a5bed..72d1db150 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,13 @@ - ## Documentation -HyperBEAM uses [MkDocs](https://www.mkdocs.org/) with the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) theme to build its documentation site. +HyperBEAM uses two documentation systems: + +1. **[MkDocs](https://www.mkdocs.org/)** - Main documentation site with Material theme +2. **[mdBook](https://rust-lang.github.io/mdBook/)** - Literate programming documentation generated from Erlang source + +## MkDocs Documentation + +The main documentation site uses MkDocs with the Material for MkDocs theme. Building the documentation requires Python 3 and pip. It's recommended to use a virtual environment: @@ -52,6 +58,31 @@ Press `Ctrl+C` in the terminal where the server is running to stop it. The final static site is generated in the `mkdocs-site` directory, as configured in `mkdocs.yml` (`site_dir: mkdocs-site`). +## mdBook Documentation (Literate Programming) + +The literate programming documentation system generates browsable documentation directly from Erlang source code comments. + +**Live Site:** [hyperbeam.arweave.dev/book](https://hyperbeam.arweave.dev/book) + +### Quick Start + +```bash +# Generate literate docs and build the book (integrated workflow) +./docs/build-literate-erlang.sh # Generate .erl.md files and copy to mdBook +cd docs/book && mdbook build # Build the documentation site +cd docs/book && mdbook serve # Serve locally on http://localhost:3471 +``` + +### Automated Deployment + +Documentation is automatically deployed via GitHub Actions: + +- **Triggers**: Pushes to `edge` branch affecting `src/**` or mdBook configuration +- **Process**: Generates fresh documentation from source → builds mdBook → deploys to ArNS +- **URL**: [hyperbeam.arweave.dev/book](https://hyperbeam.arweave.dev/book) + +For complete mdBook documentation details, see [book/README.md](book/README.md). + ### Contributing to the Documentation To contribute documentation to HyperBEAM, follow these steps: @@ -112,4 +143,4 @@ To contribute documentation to HyperBEAM, follow these steps: - Be prepared to make adjustments based on feedback - Once approved, your documentation will be merged into the main repository -For more detailed contribution guidelines, see the [Community Guidelines](./docs/misc/community/guidelines.md) and [Development Setup](./docs/misc/community/setup.md) documentation. +For more detailed contribution guidelines, see the [Community Guidelines](./docs/misc/community/guidelines.md) and [Development Setup](./docs/misc/community/setup.md) documentation. \ No newline at end of file diff --git a/docs/book/README.md b/docs/book/README.md new file mode 100644 index 000000000..fd87cc052 --- /dev/null +++ b/docs/book/README.md @@ -0,0 +1,105 @@ +# HyperBEAM mdBook - Literate Documentation + +This directory contains the mdBook configuration for generating literate programming documentation from HyperBEAM Erlang source code. + +**Live Documentation**: [hyperbeam.arweave.dev/book](https://hyperbeam.arweave.dev/book) + +## Overview + +The mdBook system generates comprehensive documentation by extracting comments and code structure from Erlang source files, creating a browsable reference that stays in sync with the codebase. + +## Quick Start + +```bash +# From project root - generate and build in one step +./docs/build-literate-erlang.sh -v + +# Build mdBook only (if .erl.md files already exist) +cd docs/book && mdbook build + +# Serve locally for development +cd docs/book && mdbook serve # Opens on http://localhost:3471 +``` + +## Automated Deployment + +Documentation automatically deploys when source code changes: + +- **Triggers**: Pushes to `edge` branch affecting `src/**` or mdBook config +- **GitHub Action**: `build-deploy-mdbook.yml` +- **Process**: Generates `.erl.md` → builds mdBook → deploys to ArNS +- **Live URL**: [hyperbeam.arweave.dev/book](https://hyperbeam.arweave.dev/book) + +## File Structure + +``` +book/ +├── book.toml # mdBook configuration +├── custom.css # HyperBEAM brand styling +├── custom.js # Enhanced copy functionality +├── src/ +│ ├── SUMMARY.md # Navigation structure +│ ├── introduction.md # Welcome page +│ └── *.erl.md # Generated module docs (auto-copied) +└── dist/ # Built documentation (gitignored) +``` + +## Features + +### HyperBEAM Branding +- Custom CSS with brand colors across all mdBook themes +- Enhanced search interface with theme-aware styling +- Professional appearance matching HyperBEAM design + +### Enhanced Copy Functionality +- Copy button (📋) for LLM consumption +- Fetches raw markdown preserving all formatting +- Perfect for AI analysis and code understanding + +### Source Integration +- Direct links to GitHub source files on `edge` branch +- Module documentation extracted from `%%%` comments +- Function documentation and type specifications +- Clean code block formatting + +## Configuration + +### book.toml Key Settings + +```toml +[book] +title = "HyperBEAM Book" +src = "src" + +[build] +build-dir = "dist" + +[output.html] +additional-css = ["custom.css"] +additional-js = ["custom.js"] +edit-url-template = "https://github.com/permaweb/HyperBEAM/edit/edge/src/{path}" +``` + +### Port Configuration +- **Default**: `localhost:3000` +- **Configured**: `localhost:3471` (to avoid conflicts) + +## Development + +### Manual Workflow +1. **Edit Erlang source** with proper documentation comments +2. **Generate docs**: `./docs/build-literate-erlang.sh -v` +3. **Preview**: `cd docs/book && mdbook serve` + +### Production Workflow +1. **Push to edge** with source changes +2. **GitHub Actions** handles the rest automatically + +## Dependencies + +- **mdBook**: `cargo install mdbook` or [download binary](https://github.com/rust-lang/mdBook/releases) +- **Generated by**: `build-literate-erlang.sh` script + +--- + +For the complete documentation overview including MkDocs, see [../README.md](../README.md). \ No newline at end of file diff --git a/docs/book/book.toml b/docs/book/book.toml new file mode 100644 index 000000000..51f3cc0fa --- /dev/null +++ b/docs/book/book.toml @@ -0,0 +1,56 @@ +[book] +authors = ["HyperBEAM Core Team"] +language = "en" +src = "src" +title = "HyperBEAM Book" +description = "Literate programming documentation for the HyperBEAM decentralized operating system, combining Erlang source code with comprehensive documentation." + +[build] +build-dir = "dist" + +# Configure for non-Rust documentation - this prevents mdBook from trying to compile code blocks as Rust +[output.html] +default-theme = "rust" +additional-css = ["custom.css", "theme/highlight.css"] +additional-js = ["custom.js"] +mathjax-support = false +copy-fonts = true +site-url = "/HyperBEAM-Literate-Documentation/" +cname = "" +edit-url-template = "https://github.com/permaweb/HyperBEAM/edit/edge/src/{path}" +git-repository-url = "https://github.com/permaweb/HyperBEAM" +git-repository-icon = "fa-github" +no-section-label = true + +[output.html.serve] +port = 3471 + +[output.html.live-reload] +enable = true + +[output.html.fold] +enable = true +level = 1 + +[output.html.playground] +editable = false +copyable = true +copy-js = true +line-numbers = false +runnable = false + + +[output.html.search] +enable = true +limit-results = 30 +teaser-word-count = 30 +use-boolean-and = true +boost-title = 2 +boost-hierarchy = 1 +boost-paragraph = 1 +expand = true +heading-split-level = 3 + +[preprocessor.links] + +[preprocessor.index] \ No newline at end of file diff --git a/docs/book/custom.css b/docs/book/custom.css new file mode 100644 index 000000000..f71e592e8 --- /dev/null +++ b/docs/book/custom.css @@ -0,0 +1,92 @@ +/* HyperBEAM Brand Extension for mdBook - MINIMAL approach */ +/* Only add brand colors, no layout modifications */ + +:root { + /* HyperBEAM brand palette */ + --hyperbeam-neon: #00ff94; + --hyperbeam-cyan: #00d4ff; + --hyperbeam-yellow: #fff700; + --hyperbeam-magenta: #ff006a; +} + +/* Only override accent colors, keep all layout/structure intact */ +.ayu { + --sidebar-active: var(--hyperbeam-neon); + --links: var(--hyperbeam-cyan); +} + +.coal { + --sidebar-active: var(--hyperbeam-cyan); + --links: var(--hyperbeam-neon); +} + +.light { + --sidebar-active: var(--hyperbeam-cyan); + --links: var(--hyperbeam-magenta); +} + +.navy { + --sidebar-active: var(--hyperbeam-cyan); + --links: var(--hyperbeam-neon); +} + +.rust { + --sidebar-active: var(--hyperbeam-neon); + --links: var(--hyperbeam-cyan); +} + +/* Fix excessive heading margins in generated documentation */ +.content h2 { + margin-top: 0.5em !important; + margin-bottom: 0.5em !important; +} + +.content h3 { + margin-top: 0.5em !important; + margin-bottom: 0.75em !important; +} + +/* Search input styling */ +#searchbar { + outline: none !important; + border: 1px solid transparent !important; + transition: border-color 300ms ease-in-out, box-shadow 300ms ease-in-out !important; +} + +#searchbar:focus, +#searchbar.active { + box-shadow: none !important; + border-color: var(--links) !important; +} + +/* Search result highlighting */ +.searchresults mark { + background-color: var(--links) !important; + color: var(--bg) !important; + opacity: 0.8; +} + +/* Search input clear button (X) styling */ +#searchbar::-webkit-search-cancel-button { + -webkit-appearance: none; + appearance: none; + height: 14px; + width: 14px; + cursor: pointer; + position: relative; +} + +#searchbar::-webkit-search-cancel-button::before { + content: "×"; + font-size: 18px; + color: var(--links); + font-weight: bold; + position: absolute; + top: -2px; + left: 0; + line-height: 1; +} + +#searchbar::-webkit-search-cancel-button:hover::before { + color: var(--sidebar-active); +} \ No newline at end of file diff --git a/docs/book/custom.js b/docs/book/custom.js new file mode 100644 index 000000000..d3bbf168c --- /dev/null +++ b/docs/book/custom.js @@ -0,0 +1,246 @@ +// HyperBEAM mdBook Enhancement +// Following mdBook best practices: minimal JavaScript additions + +document.addEventListener('DOMContentLoaded', function() { + // Set default theme to Rust if no theme is stored + setDefaultTheme(); + + // Initialize theme change detection + initThemeDetection(); + + // Add copy buttons to code blocks (disabled - using mdbook's built-in) + // addCopyButtons(); + + // Replace edit icon with page copy functionality + replaceEditWithCopy(); +}); + +function setDefaultTheme() { + // Check if there's already a stored theme preference + const storedTheme = localStorage.getItem('mdbook-theme'); + + // If no theme is stored, set default to rust + if (!storedTheme) { + localStorage.setItem('mdbook-theme', 'rust'); + document.documentElement.className = document.documentElement.className.replace(/\brust\b|\blight\b|\bnavy\b|\bayu\b/g, '').trim(); + document.documentElement.classList.add('rust'); + } +} + +function initThemeDetection() { + // Watch for theme changes via class changes on html element + const observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + handleThemeChange(); + } + }); + }); + + // Start observing + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'] + }); + + // Also listen for storage changes (theme switching) + window.addEventListener('storage', function(e) { + if (e.key === 'mdbook-theme') { + handleThemeChange(); + } + }); + + // Initial setup + handleThemeChange(); +} + +function handleThemeChange() { + // Re-apply theme-aware styling to any dynamic elements + const copyButtons = document.querySelectorAll('.copy-code-btn'); + copyButtons.forEach(function(btn) { + // Update button colors to match current theme + btn.style.background = 'var(--theme-popup-bg, #333)'; + btn.style.color = 'var(--fg, #fff)'; + btn.style.borderColor = 'var(--theme-popup-border, #555)'; + }); + + // Log theme change for debugging + const currentTheme = document.documentElement.className || 'default'; + console.log('HyperBEAM: Theme changed to', currentTheme); +} + +function addCopyButtons() { + const codeBlocks = document.querySelectorAll('pre code'); + + codeBlocks.forEach(function(codeBlock) { + const pre = codeBlock.parentElement; + + // Skip if copy button already exists + if (pre.querySelector('.copy-code-btn')) return; + + const copyBtn = document.createElement('button'); + copyBtn.className = 'copy-code-btn'; + copyBtn.textContent = '📋'; + copyBtn.setAttribute('aria-label', 'Copy code to clipboard'); + copyBtn.style.cssText = ` + position: absolute; + top: 8px; + right: 8px; + background: var(--theme-popup-bg, #333); + color: var(--fg, #fff); + border: 1px solid var(--theme-popup-border, #555); + padding: 4px 6px; + border-radius: 4px; + font-size: 11px; + cursor: pointer; + opacity: 0; + transition: all 0.2s ease; + z-index: 10; + line-height: 1; + min-width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + `; + + pre.style.position = 'relative'; + pre.appendChild(copyBtn); + + // Show/hide on hover + pre.addEventListener('mouseenter', function() { + copyBtn.style.opacity = '0.8'; + }); + + pre.addEventListener('mouseleave', function() { + copyBtn.style.opacity = '0'; + }); + + // Hover effect + copyBtn.addEventListener('mouseenter', function() { + copyBtn.style.opacity = '1'; + copyBtn.style.background = 'var(--hyperbeam-accent-primary, #00ff94)'; + copyBtn.style.color = '#000'; + }); + + copyBtn.addEventListener('mouseleave', function() { + copyBtn.style.background = 'var(--theme-popup-bg, #333)'; + copyBtn.style.color = 'var(--fg, #fff)'; + }); + + // Copy functionality + copyBtn.addEventListener('click', function() { + navigator.clipboard.writeText(codeBlock.textContent).then(function() { + const originalText = copyBtn.textContent; + copyBtn.textContent = '✓'; + copyBtn.style.background = 'var(--hyperbeam-accent-primary, #00ff94)'; + copyBtn.style.color = '#000'; + + setTimeout(function() { + copyBtn.textContent = originalText; + copyBtn.style.background = 'var(--theme-popup-bg, #333)'; + copyBtn.style.color = 'var(--fg, #fff)'; + }, 1500); + }).catch(function() { + copyBtn.textContent = '✗'; + setTimeout(function() { + copyBtn.textContent = '📋'; + }, 1500); + }); + }); + }); +} + +function replaceEditWithCopy() { + // Remove the edit button and replace with copy page button + const editButton = document.querySelector('a[title="Suggest an edit"], a[href*="edit"]'); + if (editButton) { + // Create new copy button + const copyPageBtn = document.createElement('button'); + copyPageBtn.innerHTML = ``; + copyPageBtn.title = 'Copy page content for LLM'; + copyPageBtn.setAttribute('aria-label', 'Copy page content for LLM use'); + copyPageBtn.className = editButton.className; + copyPageBtn.style.cssText = ` + background: none; + border: none; + color: inherit; + font-size: inherit; + cursor: pointer; + padding: 8px; + border-radius: 4px; + transition: background-color 0.2s ease; + display: inline-flex; + align-items: center; + justify-content: center; + vertical-align: middle; + `; + + // Add hover effect + copyPageBtn.addEventListener('mouseenter', function() { + copyPageBtn.style.backgroundColor = 'var(--theme-hover, rgba(255,255,255,0.1))'; + }); + + copyPageBtn.addEventListener('mouseleave', function() { + copyPageBtn.style.backgroundColor = 'transparent'; + }); + + // Copy functionality + copyPageBtn.addEventListener('click', function() { + // Get the current page path and construct the markdown file URL + const currentPath = window.location.pathname; + const pathParts = currentPath.split('/'); + const fileName = pathParts[pathParts.length - 1] || pathParts[pathParts.length - 2]; + + // Construct the path to the original markdown file + let markdownPath = ''; + if (fileName && fileName.endsWith('.html')) { + markdownPath = fileName.replace('.html', '.md'); + } else { + // Handle index pages or other cases + markdownPath = 'index.md'; + } + + // Try to fetch the original markdown file + fetch(`src/${markdownPath}`) + .then(response => { + if (response.ok) { + return response.text(); + } + throw new Error('Could not fetch markdown file'); + }) + .then(markdownContent => { + navigator.clipboard.writeText(markdownContent).then(function() { + const originalContent = copyPageBtn.innerHTML; + copyPageBtn.innerHTML = ``; + copyPageBtn.style.color = 'var(--hyperbeam-accent-primary, #00ff94)'; + + setTimeout(function() { + copyPageBtn.innerHTML = originalContent; + copyPageBtn.style.color = 'inherit'; + }, 2000); + }).catch(function() { + showCopyError(); + }); + }) + .catch(function() { + showCopyError(); + }); + + function showCopyError() { + const originalContent = copyPageBtn.innerHTML; + copyPageBtn.innerHTML = ``; + copyPageBtn.style.color = '#ff6b6b'; + + setTimeout(function() { + copyPageBtn.innerHTML = originalContent; + copyPageBtn.style.color = 'inherit'; + }, 2000); + } + }); + + // Replace the edit button + editButton.parentNode.replaceChild(copyPageBtn, editButton); + } +} + diff --git a/docs/book/src/README.md b/docs/book/src/README.md new file mode 100644 index 000000000..1376130f0 --- /dev/null +++ b/docs/book/src/README.md @@ -0,0 +1,159 @@ +# HyperBEAM Literate Erlang Documentation + +This directory contains literate Erlang documentation generated from the HyperBEAM source code. +Each file combines the source code with embedded documentation in a format optimized for GitHub. + +## Modules + +| Module | Description | +|--------|-------------| +| [ar_bundles](./ar_bundles.erl.md) | Erlang module... | +| [ar_deep_hash](./ar_deep_hash.erl.md) | Erlang module... | +| [ar_rate_limiter](./ar_rate_limiter.erl.md) | Erlang module... | +| [ar_timestamp](./ar_timestamp.erl.md) | Erlang module... | +| [ar_tx](./ar_tx.erl.md) | Erlang module... | +| [ar_wallet](./ar_wallet.erl.md) | Erlang module... | +| [dev_apply](./dev_apply.erl.md) | Erlang module... | +| [dev_arweave_block_cache](./dev_arweave_block_cache.erl.md) | Erlang module... | +| [dev_arweave](./dev_arweave.erl.md) | Erlang module... | +| [dev_auth_hook](./dev_auth_hook.erl.md) | Erlang module... | +| [dev_cache](./dev_cache.erl.md) | Erlang module... | +| [dev_cacheviz](./dev_cacheviz.erl.md) | Erlang module... | +| [dev_codec_ans104_from](./dev_codec_ans104_from.erl.md) | Erlang module... | +| [dev_codec_ans104_to](./dev_codec_ans104_to.erl.md) | Erlang module... | +| [dev_codec_ans104](./dev_codec_ans104.erl.md) | Erlang module... | +| [dev_codec_cookie_auth](./dev_codec_cookie_auth.erl.md) | Erlang module... | +| [dev_codec_cookie_test_vectors](./dev_codec_cookie_test_vectors.erl.md) | Erlang module... | +| [dev_codec_cookie](./dev_codec_cookie.erl.md) | Erlang module... | +| [dev_codec_flat](./dev_codec_flat.erl.md) | Erlang module... | +| [dev_codec_http_auth](./dev_codec_http_auth.erl.md) | Erlang module... | +| [dev_codec_httpsig_conv](./dev_codec_httpsig_conv.erl.md) | Erlang module... | +| [dev_codec_httpsig_keyid](./dev_codec_httpsig_keyid.erl.md) | Erlang module... | +| [dev_codec_httpsig_proxy](./dev_codec_httpsig_proxy.erl.md) | Erlang module... | +| [dev_codec_httpsig_siginfo](./dev_codec_httpsig_siginfo.erl.md) | Erlang module... | +| [dev_codec_httpsig](./dev_codec_httpsig.erl.md) | Erlang module... | +| [dev_codec_json](./dev_codec_json.erl.md) | Erlang module... | +| [dev_codec_structured](./dev_codec_structured.erl.md) | Erlang module... | +| [dev_copycat_arweave](./dev_copycat_arweave.erl.md) | Erlang module... | +| [dev_copycat_graphql](./dev_copycat_graphql.erl.md) | Erlang module... | +| [dev_copycat](./dev_copycat.erl.md) | Erlang module... | +| [dev_cron](./dev_cron.erl.md) | Erlang module... | +| [dev_cu](./dev_cu.erl.md) | Erlang module... | +| [dev_dedup](./dev_dedup.erl.md) | Erlang module... | +| [dev_delegated_compute](./dev_delegated_compute.erl.md) | Erlang module... | +| [dev_faff](./dev_faff.erl.md) | Erlang module... | +| [dev_genesis_wasm](./dev_genesis_wasm.erl.md) | Erlang module... | +| [dev_green_zone](./dev_green_zone.erl.md) | Erlang module... | +| [dev_hook](./dev_hook.erl.md) | Erlang module... | +| [dev_hyperbuddy](./dev_hyperbuddy.erl.md) | Erlang module... | +| [dev_json_iface](./dev_json_iface.erl.md) | Erlang module... | +| [dev_local_name](./dev_local_name.erl.md) | Erlang module... | +| [dev_lookup](./dev_lookup.erl.md) | Erlang module... | +| [dev_lua_lib](./dev_lua_lib.erl.md) | Erlang module... | +| [dev_lua_test_ledgers](./dev_lua_test_ledgers.erl.md) | Erlang module... | +| [dev_lua_test](./dev_lua_test.erl.md) | Erlang module... | +| [dev_lua](./dev_lua.erl.md) | Erlang module... | +| [dev_manifest](./dev_manifest.erl.md) | Erlang module... | +| [dev_message](./dev_message.erl.md) | Erlang module... | +| [dev_meta](./dev_meta.erl.md) | Erlang module... | +| [dev_monitor](./dev_monitor.erl.md) | Erlang module... | +| [dev_multipass](./dev_multipass.erl.md) | Erlang module... | +| [dev_name](./dev_name.erl.md) | Erlang module... | +| [dev_node_process](./dev_node_process.erl.md) | Erlang module... | +| [dev_p4](./dev_p4.erl.md) | Erlang module... | +| [dev_patch](./dev_patch.erl.md) | Erlang module... | +| [dev_poda](./dev_poda.erl.md) | Erlang module... | +| [dev_process_cache](./dev_process_cache.erl.md) | Erlang module... | +| [dev_process_worker](./dev_process_worker.erl.md) | Erlang module... | +| [dev_process](./dev_process.erl.md) | Erlang module... | +| [dev_profile](./dev_profile.erl.md) | Erlang module... | +| [dev_push](./dev_push.erl.md) | Erlang module... | +| [dev_query_arweave](./dev_query_arweave.erl.md) | Erlang module... | +| [dev_query_graphql](./dev_query_graphql.erl.md) | Erlang module... | +| [dev_query_test_vectors](./dev_query_test_vectors.erl.md) | Erlang module... | +| [dev_query](./dev_query.erl.md) | Erlang module... | +| [dev_relay](./dev_relay.erl.md) | Erlang module... | +| [dev_router](./dev_router.erl.md) | Erlang module... | +| [dev_scheduler_cache](./dev_scheduler_cache.erl.md) | Erlang module... | +| [dev_scheduler_formats](./dev_scheduler_formats.erl.md) | Erlang module... | +| [dev_scheduler_registry](./dev_scheduler_registry.erl.md) | Erlang module... | +| [dev_scheduler_server](./dev_scheduler_server.erl.md) | Erlang module... | +| [dev_scheduler](./dev_scheduler.erl.md) | Erlang module... | +| [dev_secret](./dev_secret.erl.md) | Erlang module... | +| [dev_simple_pay](./dev_simple_pay.erl.md) | Erlang module... | +| [dev_snp_nif](./dev_snp_nif.erl.md) | Erlang module... | +| [dev_snp](./dev_snp.erl.md) | Erlang module... | +| [dev_stack](./dev_stack.erl.md) | Erlang module... | +| [dev_test](./dev_test.erl.md) | Erlang module... | +| [dev_volume](./dev_volume.erl.md) | Erlang module... | +| [dev_wasi](./dev_wasi.erl.md) | Erlang module... | +| [dev_wasm](./dev_wasm.erl.md) | Erlang module... | +| [dev_whois](./dev_whois.erl.md) | Erlang module... | +| [hb_ao_test_vectors](./hb_ao_test_vectors.erl.md) | Erlang module... | +| [hb_ao](./hb_ao.erl.md) | Erlang module... | +| [hb_app](./hb_app.erl.md) | Erlang module... | +| [hb_beamr_io](./hb_beamr_io.erl.md) | Erlang module... | +| [hb_beamr](./hb_beamr.erl.md) | Erlang module... | +| [hb_cache_control](./hb_cache_control.erl.md) | Erlang module... | +| [hb_cache_render](./hb_cache_render.erl.md) | Erlang module... | +| [hb_cache](./hb_cache.erl.md) | Erlang module... | +| [hb_client](./hb_client.erl.md) | Erlang module... | +| [hb_crypto](./hb_crypto.erl.md) | Erlang module... | +| [hb_debugger](./hb_debugger.erl.md) | Erlang module... | +| [hb_escape](./hb_escape.erl.md) | Erlang module... | +| [hb_event](./hb_event.erl.md) | Erlang module... | +| [hb_examples](./hb_examples.erl.md) | Erlang module... | +| [hb_features](./hb_features.erl.md) | Erlang module... | +| [hb_format](./hb_format.erl.md) | Erlang module... | +| [hb_gateway_client](./hb_gateway_client.erl.md) | Erlang module... | +| [hb_http_benchmark_tests](./hb_http_benchmark_tests.erl.md) | Erlang module... | +| [hb_http_client_sup](./hb_http_client_sup.erl.md) | Erlang module... | +| [hb_http_client](./hb_http_client.erl.md) | Erlang module... | +| [hb_http_multi](./hb_http_multi.erl.md) | Erlang module... | +| [hb_http_server](./hb_http_server.erl.md) | Erlang module... | +| [hb_http](./hb_http.erl.md) | Erlang module... | +| [hb_json](./hb_json.erl.md) | Erlang module... | +| [hb_keccak](./hb_keccak.erl.md) | Erlang module... | +| [hb_link](./hb_link.erl.md) | Erlang module... | +| [hb_logger](./hb_logger.erl.md) | Erlang module... | +| [hb_maps](./hb_maps.erl.md) | Erlang module... | +| [hb_message_test_vectors](./hb_message_test_vectors.erl.md) | Erlang module... | +| [hb_message](./hb_message.erl.md) | Erlang module... | +| [hb_metrics_collector](./hb_metrics_collector.erl.md) | Erlang module... | +| [hb_name](./hb_name.erl.md) | Erlang module... | +| [hb_opts](./hb_opts.erl.md) | Erlang module... | +| [hb_path](./hb_path.erl.md) | Erlang module... | +| [hb_persistent](./hb_persistent.erl.md) | Erlang module... | +| [hb_private](./hb_private.erl.md) | Erlang module... | +| [hb_process_monitor](./hb_process_monitor.erl.md) | Erlang module... | +| [hb_router](./hb_router.erl.md) | Erlang module... | +| [hb_singleton](./hb_singleton.erl.md) | Erlang module... | +| [hb_store_fs](./hb_store_fs.erl.md) | Erlang module... | +| [hb_store_gateway](./hb_store_gateway.erl.md) | Erlang module... | +| [hb_store_lmdb](./hb_store_lmdb.erl.md) | Erlang module... | +| [hb_store_lru](./hb_store_lru.erl.md) | Erlang module... | +| [hb_store_opts](./hb_store_opts.erl.md) | Erlang module... | +| [hb_store_remote_node](./hb_store_remote_node.erl.md) | Erlang module... | +| [hb_store_rocksdb](./hb_store_rocksdb.erl.md) | Erlang module... | +| [hb_store](./hb_store.erl.md) | Erlang module... | +| [hb_structured_fields](./hb_structured_fields.erl.md) | Erlang module... | +| [hb_sup](./hb_sup.erl.md) | Erlang module... | +| [hb_test_utils](./hb_test_utils.erl.md) | Erlang module... | +| [hb_tracer](./hb_tracer.erl.md) | Erlang module... | +| [hb_util](./hb_util.erl.md) | Erlang module... | +| [hb_volume](./hb_volume.erl.md) | Erlang module... | +| [hb](./hb.erl.md) | Erlang module... | +| [rsa_pss](./rsa_pss.erl.md) | Erlang module... | + +## About Literate Programming + +Literate programming is a methodology that combines a programming language with a documentation language, +making programs more robust, more portable, and more easily maintained than programs written only in a +high-level language. + +These files present the HyperBEAM source code in a narrative format, with documentation and code +interwoven to provide better understanding of the implementation. + +--- + +*Generated on 2025-09-19 17:15:25 UTC* diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md new file mode 100644 index 000000000..64394767e --- /dev/null +++ b/docs/book/src/SUMMARY.md @@ -0,0 +1,155 @@ +# Summary + +[Introduction](introduction.md) + +# Arweave Foundation + +- [ar_bundles](ar_bundles.erl.md) +- [ar_deep_hash](ar_deep_hash.erl.md) +- [ar_rate_limiter](ar_rate_limiter.erl.md) +- [ar_timestamp](ar_timestamp.erl.md) +- [ar_tx](ar_tx.erl.md) +- [ar_wallet](ar_wallet.erl.md) + +# Codec Modules + +- [dev_codec_ans104](dev_codec_ans104.erl.md) +- [dev_codec_ans104_from](dev_codec_ans104_from.erl.md) +- [dev_codec_ans104_to](dev_codec_ans104_to.erl.md) +- [dev_codec_cookie](dev_codec_cookie.erl.md) +- [dev_codec_cookie_auth](dev_codec_cookie_auth.erl.md) +- [dev_codec_cookie_test_vectors](dev_codec_cookie_test_vectors.erl.md) +- [dev_codec_flat](dev_codec_flat.erl.md) +- [dev_codec_http_auth](dev_codec_http_auth.erl.md) +- [dev_codec_httpsig](dev_codec_httpsig.erl.md) +- [dev_codec_httpsig_conv](dev_codec_httpsig_conv.erl.md) +- [dev_codec_httpsig_keyid](dev_codec_httpsig_keyid.erl.md) +- [dev_codec_httpsig_proxy](dev_codec_httpsig_proxy.erl.md) +- [dev_codec_httpsig_siginfo](dev_codec_httpsig_siginfo.erl.md) +- [dev_codec_json](dev_codec_json.erl.md) +- [dev_codec_structured](dev_codec_structured.erl.md) + +# Devices + +- [dev_apply](dev_apply.erl.md) +- [dev_cache](dev_cache.erl.md) +- [dev_cacheviz](dev_cacheviz.erl.md) +- [dev_copycat](dev_copycat.erl.md) +- [dev_copycat_arweave](dev_copycat_arweave.erl.md) +- [dev_copycat_graphql](dev_copycat_graphql.erl.md) +- [dev_cron](dev_cron.erl.md) +- [dev_cu](dev_cu.erl.md) +- [dev_dedup](dev_dedup.erl.md) +- [dev_faff](dev_faff.erl.md) +- [dev_monitor](dev_monitor.erl.md) +- [dev_multipass](dev_multipass.erl.md) +- [dev_test](dev_test.erl.md) +- [dev_arweave](dev_arweave.erl.md) +- [dev_arweave_block_cache](dev_arweave_block_cache.erl.md) +- [dev_auth_hook](dev_auth_hook.erl.md) +- [dev_secret](dev_secret.erl.md) +- [dev_delegated_compute](dev_delegated_compute.erl.md) +- [dev_genesis_wasm](dev_genesis_wasm.erl.md) +- [dev_green_zone](dev_green_zone.erl.md) +- [dev_hook](dev_hook.erl.md) +- [dev_hyperbuddy](dev_hyperbuddy.erl.md) +- [dev_json_iface](dev_json_iface.erl.md) +- [dev_local_name](dev_local_name.erl.md) +- [dev_lookup](dev_lookup.erl.md) +- [dev_lua](dev_lua.erl.md) +- [dev_lua_lib](dev_lua_lib.erl.md) +- [dev_lua_test](dev_lua_test.erl.md) +- [dev_lua_test_ledgers](dev_lua_test_ledgers.erl.md) +- [dev_manifest](dev_manifest.erl.md) +- [dev_message](dev_message.erl.md) +- [dev_meta](dev_meta.erl.md) +- [dev_name](dev_name.erl.md) +- [dev_node_process](dev_node_process.erl.md) +- [dev_p4](dev_p4.erl.md) +- [dev_patch](dev_patch.erl.md) +- [dev_poda](dev_poda.erl.md) +- [dev_process](dev_process.erl.md) +- [dev_process_cache](dev_process_cache.erl.md) +- [dev_process_worker](dev_process_worker.erl.md) +- [dev_profile](dev_profile.erl.md) +- [dev_push](dev_push.erl.md) +- [dev_query](dev_query.erl.md) +- [dev_query_arweave](dev_query_arweave.erl.md) +- [dev_query_graphql](dev_query_graphql.erl.md) +- [dev_query_test_vectors](dev_query_test_vectors.erl.md) +- [dev_relay](dev_relay.erl.md) +- [dev_router](dev_router.erl.md) +- [dev_scheduler](dev_scheduler.erl.md) +- [dev_scheduler_cache](dev_scheduler_cache.erl.md) +- [dev_scheduler_formats](dev_scheduler_formats.erl.md) +- [dev_scheduler_registry](dev_scheduler_registry.erl.md) +- [dev_scheduler_server](dev_scheduler_server.erl.md) +- [dev_simple_pay](dev_simple_pay.erl.md) +- [dev_snp](dev_snp.erl.md) +- [dev_snp_nif](dev_snp_nif.erl.md) +- [dev_stack](dev_stack.erl.md) +- [dev_volume](dev_volume.erl.md) +- [dev_wasi](dev_wasi.erl.md) +- [dev_wasm](dev_wasm.erl.md) +- [dev_whois](dev_whois.erl.md) + +# HyperBEAM Core + +- [hb](hb.erl.md) +- [hb_ao](hb_ao.erl.md) +- [hb_ao_test_vectors](hb_ao_test_vectors.erl.md) +- [hb_app](hb_app.erl.md) +- [hb_beamr](hb_beamr.erl.md) +- [hb_beamr_io](hb_beamr_io.erl.md) +- [hb_cache](hb_cache.erl.md) +- [hb_cache_control](hb_cache_control.erl.md) +- [hb_cache_render](hb_cache_render.erl.md) +- [hb_client](hb_client.erl.md) +- [hb_crypto](hb_crypto.erl.md) +- [hb_debugger](hb_debugger.erl.md) +- [hb_escape](hb_escape.erl.md) +- [hb_event](hb_event.erl.md) +- [hb_examples](hb_examples.erl.md) +- [hb_features](hb_features.erl.md) +- [hb_format](hb_format.erl.md) +- [hb_gateway_client](hb_gateway_client.erl.md) +- [hb_http](hb_http.erl.md) +- [hb_http_benchmark_tests](hb_http_benchmark_tests.erl.md) +- [hb_http_client](hb_http_client.erl.md) +- [hb_http_client_sup](hb_http_client_sup.erl.md) +- [hb_http_multi](hb_http_multi.erl.md) +- [hb_http_server](hb_http_server.erl.md) +- [hb_json](hb_json.erl.md) +- [hb_keccak](hb_keccak.erl.md) +- [hb_link](hb_link.erl.md) +- [hb_logger](hb_logger.erl.md) +- [hb_maps](hb_maps.erl.md) +- [hb_message](hb_message.erl.md) +- [hb_message_test_vectors](hb_message_test_vectors.erl.md) +- [hb_metrics_collector](hb_metrics_collector.erl.md) +- [hb_name](hb_name.erl.md) +- [hb_opts](hb_opts.erl.md) +- [hb_path](hb_path.erl.md) +- [hb_persistent](hb_persistent.erl.md) +- [hb_private](hb_private.erl.md) +- [hb_process_monitor](hb_process_monitor.erl.md) +- [hb_router](hb_router.erl.md) +- [hb_singleton](hb_singleton.erl.md) +- [hb_store](hb_store.erl.md) +- [hb_store_fs](hb_store_fs.erl.md) +- [hb_store_gateway](hb_store_gateway.erl.md) +- [hb_store_lmdb](hb_store_lmdb.erl.md) +- [hb_store_lru](hb_store_lru.erl.md) +- [hb_store_opts](hb_store_opts.erl.md) +- [hb_store_remote_node](hb_store_remote_node.erl.md) +- [hb_store_rocksdb](hb_store_rocksdb.erl.md) +- [hb_structured_fields](hb_structured_fields.erl.md) +- [hb_sup](hb_sup.erl.md) +- [hb_test_utils](hb_test_utils.erl.md) +- [hb_tracer](hb_tracer.erl.md) +- [hb_util](hb_util.erl.md) +- [hb_volume](hb_volume.erl.md) + +# Other Modules + +- [rsa_pss](rsa_pss.erl.md) \ No newline at end of file diff --git a/docs/book/src/introduction.md b/docs/book/src/introduction.md new file mode 100644 index 000000000..7603aba13 --- /dev/null +++ b/docs/book/src/introduction.md @@ -0,0 +1,33 @@ +# HyperBEAM Literate Documentation + +Welcome to the comprehensive literate documentation for HyperBEAM, a reference implementation of AO-Core written in Erlang. + +## About This Documentation + +This documentation combines source code with comprehensive explanations using a literate programming approach. Each module page includes: + +- **Module overview** with purpose and functionality +- **Exported functions** with signatures and documentation +- **Implementation details** with annotated source code +- **Test functions** demonstrating usage patterns + +## Navigation + +Use the sidebar to browse modules organized by category: + +- **Arweave Foundation**: Core Arweave protocol implementations +- **Devices**: AO device implementations and utilities +- **Codec Modules**: Data encoding/decoding functionality + (Core Services grouped under Devices) +- **HyperBEAM Core**: Foundation modules and utilities + +## About HyperBEAM + +HyperBEAM is a client implementation of the AO-Core protocol, providing a framework for decentralized computations. It offers: + +1. **Hashpaths**: Mechanism for referencing program state-space locations +2. **Unified data structures**: HTTP document representation of program states +3. **Commitment protocol**: Cryptographic proofs of state representations +4. **Meta-VM**: Support for multiple virtual machines and computational models + +For more information, visit the [HyperBEAM repository](https://github.com/permaweb/HyperBEAM). \ No newline at end of file diff --git a/docs/book/theme/highlight.css b/docs/book/theme/highlight.css new file mode 100644 index 000000000..ea3f4ab3e --- /dev/null +++ b/docs/book/theme/highlight.css @@ -0,0 +1,341 @@ +/* + GitHub-like syntax highlighting theme for Highlight.js + Optimized for mdBook and HyperBEAM branding +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #24292e; + background: #f6f8fa; + border-radius: 0.25em; +} + +.hljs-comment, +.hljs-quote { + color: #6a737d; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #d73a49; + font-weight: bold; +} + +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: #005cc5; +} + +.hljs-string, +.hljs-doctag { + color: #032f62; +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: #6f42c1; + font-weight: bold; +} + +.hljs-subst { + font-weight: normal; +} + +.hljs-type, +.hljs-class .hljs-title { + color: #e36209; + font-weight: bold; +} + +.hljs-tag, +.hljs-name, +.hljs-attribute { + color: #22863a; + font-weight: normal; +} + +.hljs-regexp, +.hljs-link { + color: #032f62; +} + +.hljs-symbol, +.hljs-bullet { + color: #e36209; +} + +.hljs-built_in, +.hljs-builtin-name { + color: #005cc5; +} + +.hljs-meta { + color: #6a737d; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +/* Rust theme - warm, earthy color scheme */ +.rust .hljs { + color: #2d2a24; + background: hsl(60deg 6.35% 80%);; +} + +.rust .hljs-comment, +.rust .hljs-quote { + color: #5a5750; + font-style: italic; +} + +.rust .hljs-keyword, +.rust .hljs-selector-tag, +.rust .hljs-subst { + color: #a0714a; + font-weight: bold; +} + +.rust .hljs-string, +.rust .hljs-doctag { + color: #8b5a2b; +} + +.rust .hljs-number, +.rust .hljs-literal, +.rust .hljs-variable, +.rust .hljs-template-variable { + color: #d4935c; +} + +.rust .hljs-title, +.rust .hljs-section, +.rust .hljs-selector-id { + color: #a0714a; + font-weight: bold; +} + +.rust .hljs-type, +.rust .hljs-class .hljs-title { + color: #a97951; + font-weight: bold; +} + +.rust .hljs-built_in, +.rust .hljs-builtin-name { + color: #c7814b; +} + +.rust .hljs-meta { + color: #5a5750; + font-weight: bold; +} + +.rust .hljs-tag, +.rust .hljs-name, +.rust .hljs-attribute { + color: #a0714a; +} + +.rust .hljs-regexp, +.rust .hljs-link { + color: #b5753a; +} + +.rust .hljs-symbol, +.rust .hljs-bullet { + color: #c7814b; +} + +/* Override CSS rules that force blue/green colors in rust theme */ +.rust .content a > .hljs, +.rust #searchresults a > .hljs, +.rust a:link > .hljs, +.rust a:visited > .hljs { + color: #d4935c !important; +} + +.rust .chapter li a.active > .hljs { + color: #d4935c !important; +} + +/* Navy theme - keep existing dark styling */ +.navy .hljs, +.coal .hljs, +.ayu .hljs { + color: #c9d1d9; + background: #0d1117; +} + +.navy .hljs-comment, +.coal .hljs-comment, +.ayu .hljs-comment, +.navy .hljs-quote, +.coal .hljs-quote, +.ayu .hljs-quote { + color: #8b949e; +} + +.navy .hljs-keyword, +.coal .hljs-keyword, +.ayu .hljs-keyword { + color: #ff7b72; +} + +.navy .hljs-string, +.coal .hljs-string, +.ayu .hljs-string { + color: #a5d6ff; +} + +.navy .hljs-number, +.coal .hljs-number, +.ayu .hljs-number { + color: #79c0ff; +} + +.navy .hljs-literal, +.coal .hljs-literal, +.ayu .hljs-literal, +.navy .hljs-variable, +.coal .hljs-variable, +.ayu .hljs-variable, +.navy .hljs-template-variable, +.coal .hljs-template-variable, +.ayu .hljs-template-variable, +.navy .hljs-tag .hljs-attr, +.coal .hljs-tag .hljs-attr, +.ayu .hljs-tag .hljs-attr { + color: #79c0ff; +} + +.navy .hljs-title, +.coal .hljs-title, +.ayu .hljs-title, +.navy .hljs-section, +.coal .hljs-section, +.ayu .hljs-section, +.navy .hljs-selector-id, +.coal .hljs-selector-id, +.ayu .hljs-selector-id { + color: #d2a8ff; + font-weight: bold; +} + +.navy .hljs-type, +.coal .hljs-type, +.ayu .hljs-type, +.navy .hljs-class .hljs-title, +.coal .hljs-class .hljs-title, +.ayu .hljs-class .hljs-title { + color: #ffa657; + font-weight: bold; +} + +.navy .hljs-tag, +.coal .hljs-tag, +.ayu .hljs-tag, +.navy .hljs-name, +.coal .hljs-name, +.ayu .hljs-name, +.navy .hljs-attribute, +.coal .hljs-attribute, +.ayu .hljs-attribute { + color: #7ee787; + font-weight: normal; +} + +.navy .hljs-regexp, +.coal .hljs-regexp, +.ayu .hljs-regexp, +.navy .hljs-link, +.coal .hljs-link, +.ayu .hljs-link { + color: #a5d6ff; +} + +.navy .hljs-symbol, +.coal .hljs-symbol, +.ayu .hljs-symbol, +.navy .hljs-bullet, +.coal .hljs-bullet, +.ayu .hljs-bullet { + color: #ffa657; +} + +.navy .hljs-built_in, +.coal .hljs-built_in, +.ayu .hljs-built_in, +.navy .hljs-builtin-name, +.coal .hljs-builtin-name, +.ayu .hljs-builtin-name { + color: #79c0ff; +} + +.navy .hljs-meta, +.coal .hljs-meta, +.ayu .hljs-meta { + color: #7d8590; + font-weight: bold; +} + +.navy .hljs-selector-tag, +.coal .hljs-selector-tag, +.ayu .hljs-selector-tag, +.navy .hljs-subst, +.coal .hljs-subst, +.ayu .hljs-subst { + color: #ff7b72; + font-weight: bold; +} + +.navy .hljs-doctag, +.coal .hljs-doctag, +.ayu .hljs-doctag { + color: #a5d6ff; +} + +.navy .hljs-deletion, +.coal .hljs-deletion, +.ayu .hljs-deletion { + background: #490202; +} + +.navy .hljs-addition, +.coal .hljs-addition, +.ayu .hljs-addition { + background: #033a16; +} + +.navy .hljs-emphasis, +.coal .hljs-emphasis, +.ayu .hljs-emphasis { + font-style: italic; +} + +.navy .hljs-strong, +.coal .hljs-strong, +.ayu .hljs-strong { + font-weight: bold; +} \ No newline at end of file diff --git a/docs/book/theme/highlight.js b/docs/book/theme/highlight.js new file mode 100644 index 000000000..95aaf3459 --- /dev/null +++ b/docs/book/theme/highlight.js @@ -0,0 +1,17 @@ +/* + Highlight.js 10.1.1 (93fd0d73) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); +hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}()); +hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}()); +hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}()); +hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}()); +hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}()); +hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}()); +hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}()); +hljs.registerLanguage("css",function(){"use strict";return function(e){var n={begin:/(?:[A-Z\_\.\-]+|--[a-zA-Z0-9_-]+)\s*:/,returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",begin:/[\w-]+/},{begin:/\(/,end:/\)/,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=\/|'\$]/,contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:/\.[A-Za-z0-9_-]+/},{className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",illegal:/:/,returnBegin:!0,contains:[{className:"keyword",begin:/@\-?\w[\w]*(\-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0},{begin:"{",end:"}",illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,n]}]}}}()); +hljs.registerLanguage("diff",function(){"use strict";return function(e){return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{begin:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{begin:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{className:"comment",variants:[{begin:/Index: /,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^\-{3}/,end:/$/},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/}]},{className:"addition",begin:"^\\+",end:"$"},{className:"deletion",begin:"^\\-",end:"$"},{className:"addition",begin:"^\\!",end:"$"}]}}}()); +// Add Erlang language support +hljs.registerLanguage("erlang",function(){"use strict";return function(e){var n="[a-z'][a-zA-Z0-9_']*",r="("+n+":"+n+"|"+n+")",a={keyword:"after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun if let not of orelse|10 query receive rem try when xor",literal:"false true"},i=e.COMMENT("%","$"),c={className:"number",begin:"\\b(\\d+(_\\d+)*#[a-fA-F0-9]+(_[a-fA-F0-9]+)*|\\d+(_\\d+)*(\\.\\d+(_\\d+)*)?([eE][-+]?\\d+)?)",relevance:0},s={begin:"fun\\s+"+n+"/\\d+"},t={begin:r+"\\(",end:"\\)",returnBegin:!0,relevance:0,contains:[{begin:r,relevance:0},{begin:"\\(",end:"\\)",endsWithParent:!0,returnEnd:!0,relevance:0}]},d={begin:"{",end:"}",relevance:0},o={begin:"\\b_([A-Z][A-Za-z0-9_]*)?",relevance:0},l={begin:"[A-Z][a-zA-Z0-9_]*",relevance:0},b={begin:"#"+e.UNDERSCORE_IDENT_RE,relevance:0,returnBegin:!0,contains:[{begin:"#"+e.UNDERSCORE_IDENT_RE,relevance:0},{begin:"{",end:"}",relevance:0}]},g={beginKeywords:"fun receive if try case",end:"end",keywords:a};g.contains=[i,s,e.inherit(e.APOS_STRING_MODE,{className:""}),g,t,e.QUOTE_STRING_MODE,c,d,o,l,b];var u=[i,s,g,t,e.QUOTE_STRING_MODE,c,d,o,l,b];t.contains[1].contains=u,d.contains=u,b.contains[1].contains=u;var E={className:"params",begin:"\\(",end:"\\)",contains:u};return{name:"Erlang",aliases:["erl"],keywords:a,illegal:"(",returnBegin:!0,illegal:"\\(|#|//|/\\*|\\\\|:|;",contains:[E,e.inherit(e.TITLE_MODE,{begin:n})],starts:{end:";|\\.",keywords:a,contains:u}},i,{begin:"^-",end:"\\.",relevance:0,excludeEnd:!0,returnBegin:!0,keywords:{$pattern:"-"+e.IDENT_RE,keyword:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec"},contains:[E]},c,e.QUOTE_STRING_MODE,b,o,l,d,{begin:/\.$/}]}}}()); \ No newline at end of file diff --git a/docs/build-all.sh b/docs/build-docs.sh similarity index 100% rename from docs/build-all.sh rename to docs/build-docs.sh diff --git a/docs/deploy-dry-run.sh b/docs/deploy-dry-run.sh new file mode 100755 index 000000000..32f3024a0 --- /dev/null +++ b/docs/deploy-dry-run.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +# Deployment Dry Run Script +# Simulates the permaweb deployment without actually deploying + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Default values (can be overridden by environment variables) +ARNS_NAME="${ARNS_NAME:-hyperbeam}" +UNDERNAME="${UNDERNAME:-book}" +DEPLOY_FOLDER="${DEPLOY_FOLDER:-docs/book/dist}" + +echo -e "${YELLOW}============================================${NC}" +echo -e "${YELLOW} DEPLOYMENT DRY RUN SIMULATION${NC}" +echo -e "${YELLOW}============================================${NC}" +echo "" + +# Simulate the deployment command that would be run +echo -e "${GREEN}Would execute deployment command:${NC}" +echo "npx permaweb-deploy \\" +echo " --arns-name=${ARNS_NAME} \\" +echo " --undername=${UNDERNAME} \\" +echo " --ant-process=\${ANT_PROCESS} \\" +echo " --deploy-folder=${DEPLOY_FOLDER}" +echo "" + +# Check if the deploy folder exists +if [ -d "$DEPLOY_FOLDER" ]; then + echo -e "${GREEN}✓ Deploy folder exists: ${DEPLOY_FOLDER}${NC}" + + # Count files in deploy folder + FILE_COUNT=$(find "$DEPLOY_FOLDER" -type f | wc -l) + FOLDER_SIZE=$(du -sh "$DEPLOY_FOLDER" | cut -f1) + echo -e "${GREEN} - Contains ${FILE_COUNT} files${NC}" + echo -e "${GREEN} - Total size: ${FOLDER_SIZE}${NC}" + + # List some key files + echo "" + echo -e "${GREEN}Key files to be deployed:${NC}" + if [ -f "$DEPLOY_FOLDER/index.html" ]; then + echo -e "${GREEN} ✓ index.html${NC}" + else + echo -e "${RED} ✗ index.html (missing - deployment would fail)${NC}" + fi + + # Check for CSS and JS files + CSS_COUNT=$(find "$DEPLOY_FOLDER" -name "*.css" | wc -l) + JS_COUNT=$(find "$DEPLOY_FOLDER" -name "*.js" | wc -l) + HTML_COUNT=$(find "$DEPLOY_FOLDER" -name "*.html" | wc -l) + + echo -e "${GREEN} - ${CSS_COUNT} CSS files${NC}" + echo -e "${GREEN} - ${JS_COUNT} JavaScript files${NC}" + echo -e "${GREEN} - ${HTML_COUNT} HTML files${NC}" + +else + echo -e "${RED}✗ Deploy folder does not exist: ${DEPLOY_FOLDER}${NC}" + echo -e "${RED} Deployment would fail!${NC}" + echo "" + echo -e "${YELLOW}To fix this, run:${NC}" + echo " cd docs/book && mdbook build" + exit 1 +fi + +echo "" +echo -e "${GREEN}Environment Variables Check:${NC}" +if [ -n "$DEPLOY_KEY" ]; then + echo -e "${GREEN} ✓ DEPLOY_KEY is set${NC}" +else + echo -e "${YELLOW} ! DEPLOY_KEY not set (would use default)${NC}" +fi + +if [ -n "$ANT_PROCESS" ]; then + echo -e "${GREEN} ✓ ANT_PROCESS is set${NC}" +else + echo -e "${YELLOW} ! ANT_PROCESS not set (required for deployment)${NC}" +fi + +echo "" +echo -e "${GREEN}Deployment Target:${NC}" +echo -e "${GREEN} - ArNS Name: ${ARNS_NAME}${NC}" +echo -e "${GREEN} - Undername: ${UNDERNAME}${NC}" +echo -e "${GREEN} - Full URL: https://${UNDERNAME}_${ARNS_NAME}.arweave.net${NC}" + +echo "" +echo -e "${YELLOW}============================================${NC}" +echo -e "${YELLOW} DRY RUN COMPLETE${NC}" +echo -e "${YELLOW}============================================${NC}" +echo "" +echo -e "${GREEN}✓ Deployment validation successful!${NC}" +echo -e "${YELLOW}To perform actual deployment, run without --dry-run flag${NC}" \ No newline at end of file diff --git a/docs/erlang-literate-parser.js b/docs/erlang-literate-parser.js new file mode 100755 index 000000000..477fecb0f --- /dev/null +++ b/docs/erlang-literate-parser.js @@ -0,0 +1,1633 @@ +#!/usr/bin/env node + +/** + * Comprehensive Erlang Literate Documentation Generator + * + * Handles all comment types: + * - %%% Module documentation + * - %% Function/section documentation + * - % Inline comments (converted to prose) + * - @doc, @param, @returns annotations + */ + +import fs from 'fs'; +import path from 'path'; + +// Character codes for faster comparisons +const CHAR_CODES = { + PERCENT: 37, // '%' + DASH: 45, // '-' + OPEN_PAREN: 40, // '(' + CLOSE_PAREN: 41, // ')' + OPEN_BRACE: 123, // '{' + CLOSE_BRACE: 125, // '}' + OPEN_BRACKET: 91, // '[' + CLOSE_BRACKET: 93, // ']' + DOT: 46 // '.' +}; + +// String constants to avoid repeated allocations +const STRINGS = { + EMPTY: '', + SPACE: ' ', + NEWLINE: '\n', + TRIPLE_PERCENT: '%%%', + DOUBLE_PERCENT: '%%', + SINGLE_PERCENT: '%', + SPEC_PREFIX: '-spec ', + ERLANG: 'erlang', + BACKTICK: '`', + PARAM_TAG: '@param', + RETURNS_TAG: '@returns', + PARAMETERS_HEADER: '### Parameters', + RETURNS_HEADER: '### Returns', + EXPORTED_FUNCTIONS: '## Exported Functions', + SEPARATOR: '---' +}; + +// Precompiled regex patterns for performance +const REGEX = { + MODULE: /^-module\(([^)]+)\)/, + EXPORT: /^-export\(\[([^\]]+)\]\)/, + EXPORT_PREFIX: /^-export\(\[/, + INCLUDE: /^-include(?:_lib)?\(["']([^"']+)["']\)/, + DEFINE: /^-define\(([^,)]+)(?:,\s*(.*))?\)/, + BEHAVIOUR: /^-behaviour\(([^)]+)\)/, + RECORD: /^-record\(([^,)]+),\s*\{/, + TYPE: /^-type\s+([a-z][a-z0-9_]*)\(/, + SPEC: /^-spec\s+([a-z][a-z0-9_]*)\s*\(/, + FUNCTION: /^([a-z][a-z0-9_]*)\s*\(/, + ATTRIBUTE: /^-([a-z][a-z0-9_]*)\(/, + DOC_BLOCK_START: /^%% @doc/, + COMMENT_SINGLE: /^\s*%[^%]/, + COMMENT_DOUBLE: /^\s*%%[^%]/, + BACKTICK_QUOTE: /`([^']*?)'/g, + HTML_ENTITIES_LT: /<</g, + HTML_ENTITIES_GT: />>/g, + PRE_TAG: /
([\s\S]*?)<\/pre>/g,
+    // Match numbered list items that start with either "1." or "1:" (allow optional space before the punctuation)
+    NUMBERED_LIST: /^\d+\s*[.:]\s/,
+    BULLET_LIST: /^[-*]\s/,
+    HEADING: /^#{1,6}\s/,
+    CODE_FENCE: /^```/,
+    RETURNS_TOKENS: /(\{[^}]+\}|\bok\b|\berror\b|\bnot_found\b|\btrue\b|\bfalse\b)/gi,
+    LEADING_RETURN_TOKEN: /^(\s*)(\{[^}]+\}|\[[^\]]+\]|ok|error|not_found|true|false)(\b|\s|$)/i,
+    STANDALONE_TUPLE: /(^|[^`])(\{[^}]+\})([^`]|$)/g,
+    PARAM: /^@param\s+(\S+)\s*(.*)/,
+    RETURNS: /^@returns?\s*/,
+    OPTION_DEF: /^(`[^`]+`)\s*:\s*(.*)$/,
+    DEFINITION: /^(\S+(?:\s*:\s*\S+)?)\s*:\s*(.*)$/,
+    MULTIPLE_NEWLINES: /\n\s*\n\s*\n/g,
+    TRAILING_SPACES: /[ \t]+$/gm,
+    TRIM: /^\s+|\s+$/g,
+    RETURNS_LIKE_TUPLE: /^`?\{[^}]+\}`?/,
+    RETURNS_LIKE_ATOM: /^`?(ok|error|not_found|true|false)\b/i,
+    REMOVE_DOC: /@doc\s*/g,
+    WHITESPACE_NORMALIZE: /\s+/g,
+    COMMA_START: /^,\s*/,
+    COMMA_END: /,\s*$/,
+    EMPTY_LINE: /^\s*$/,
+    COLON_END: /:\s*$/,
+    COMMENT_DOUBLE_PREFIX: /^\s*%%\s?/,
+    COMMENT_SINGLE_PREFIX: /^\s*%\s?/
+};
+
+class ErlangLiterateParser {
+    constructor(options = {}) {
+        this.options = {
+            githubBase: 'https://github.com/permaweb/HyperBEAM/blob/edge/src',
+            verbose: false,
+            ...options
+        };
+        this.reset();
+    }
+
+    reset() {
+        this.lines = [];
+        this.moduleInfo = {
+            name: null,
+            doc: null,
+            exports: [],
+            includes: [],
+            defines: [],
+            behaviours: [],
+            records: [],
+            types: [],
+            specs: [],
+            attributes: []
+        };
+        this.functions = [];
+        this.undocumentedFunctions = [];
+        this.commentedCodeBlocks = [];
+        this.conditionalDirectives = [];
+        this.sections = [];
+        this.currentState = {
+            inFunction: false,
+            functionName: STRINGS.EMPTY,
+            functionSpec: STRINGS.EMPTY,
+            functionDoc: STRINGS.EMPTY,
+            functionLines: [],
+            pendingDoc: STRINGS.EMPTY,
+            specFunctionName: STRINGS.EMPTY,
+            braceDepth: 0,
+            parenDepth: 0,
+            inlineDocTags: []
+        };
+    }
+
+    parseFile(filePath) {
+        const content = fs.readFileSync(filePath, 'utf8');
+        this.reset();
+        this.lines = content.split(STRINGS.NEWLINE);
+
+        this.extractModuleInfo();
+        this.processFunctions();
+
+        return this.generateMarkdown(path.basename(filePath));
+    }
+
+    extractModuleInfo() {
+        const moduleDoc = [];
+        let inModuleDoc = false;
+        let moduleDocCollected = false;
+        const linesLength = this.lines.length;
+
+        for (let i = 0; i < linesLength; i++) {
+            const line = this.lines[i];
+            const trimmed = line.trim();
+
+            if (!trimmed) {
+                if (inModuleDoc) moduleDoc.push(STRINGS.EMPTY);
+                continue;
+            }
+
+            // Fast character check before regex
+            const firstChar = trimmed.charCodeAt(0);
+
+            // Handle all module-level declarations
+            if (firstChar === CHAR_CODES.DASH) {
+                // Module name
+                if (trimmed.startsWith('-module(')) {
+                    const moduleMatch = trimmed.match(REGEX.MODULE);
+                    if (moduleMatch) {
+                        this.moduleInfo.name = moduleMatch[1];
+                    }
+                    continue;
+                }
+
+                // Exports - handle multi-line exports
+                if (REGEX.EXPORT_PREFIX.test(trimmed)) {
+                    const exportLines = this.collectMultiLineConstruct(i, '[', ']');
+                    const fullExport = exportLines.join(' ');
+                    const exportMatch = fullExport.match(REGEX.EXPORT);
+                    if (exportMatch) {
+                        const exports = exportMatch[1]
+                            .split(',')
+                            .map(e => e.trim())
+                            .filter(Boolean);
+                        this.moduleInfo.exports.push(...exports);
+                    }
+                    i += exportLines.length - 1;
+                    continue;
+                }
+
+                // Includes
+                const includeMatch = trimmed.match(REGEX.INCLUDE);
+                if (includeMatch) {
+                    this.moduleInfo.includes.push({
+                        file: includeMatch[1],
+                        line: trimmed
+                    });
+                    continue;
+                }
+
+                // Defines
+                const defineMatch = trimmed.match(REGEX.DEFINE);
+                if (defineMatch) {
+                    this.moduleInfo.defines.push({
+                        name: defineMatch[1],
+                        value: defineMatch[2] || '',
+                        line: trimmed
+                    });
+                    continue;
+                }
+
+                // Behaviours
+                const behaviourMatch = trimmed.match(REGEX.BEHAVIOUR);
+                if (behaviourMatch) {
+                    this.moduleInfo.behaviours.push(behaviourMatch[1]);
+                    continue;
+                }
+
+                // Records
+                if (REGEX.RECORD.test(trimmed)) {
+                    const recordLines = this.collectMultiLineConstruct(i, '{', '}');
+                    const recordMatch = trimmed.match(REGEX.RECORD);
+                    if (recordMatch) {
+                        this.moduleInfo.records.push({
+                            name: recordMatch[1],
+                            definition: recordLines.join('\n')
+                        });
+                    }
+                    i += recordLines.length - 1;
+                    continue;
+                }
+
+                // Types
+                const typeMatch = trimmed.match(REGEX.TYPE);
+                if (typeMatch) {
+                    const typeLines = this.collectMultiLineConstruct(i, '(', ')');
+                    this.moduleInfo.types.push({
+                        name: typeMatch[1],
+                        definition: typeLines.join('\n')
+                    });
+                    i += typeLines.length - 1;
+                    continue;
+                }
+
+                // Specs (collect but don't process here)
+                if (REGEX.SPEC.test(trimmed)) {
+                    const specLines = this.collectMultiLineConstruct(i, '(', ')');
+                    const specMatch = trimmed.match(REGEX.SPEC);
+                    if (specMatch) {
+                        this.moduleInfo.specs.push({
+                            function: specMatch[1],
+                            definition: specLines.join('\n')
+                        });
+                    }
+                    i += specLines.length - 1;
+                    continue;
+                }
+
+                // Other attributes
+                const attrMatch = trimmed.match(REGEX.ATTRIBUTE);
+                if (attrMatch) {
+                    this.moduleInfo.attributes.push({
+                        name: attrMatch[1],
+                        line: trimmed
+                    });
+                    continue;
+                }
+            }
+
+            // Module documentation - only collect at the beginning of the file
+            if (firstChar === CHAR_CODES.PERCENT && trimmed.startsWith(STRINGS.TRIPLE_PERCENT) && !moduleDocCollected) {
+                inModuleDoc = true;
+                let docLine = trimmed.substring(3).trim();
+                docLine = docLine.replace(REGEX.REMOVE_DOC, STRINGS.EMPTY);
+
+                // Check for termination pattern (%%% ''')
+                if (docLine === "'''") {
+                    inModuleDoc = false; // End module documentation processing
+                    moduleDocCollected = true; // Mark as collected
+                    continue; // Continue processing rest of file
+                }
+
+                moduleDoc.push(docLine);
+            } else if (inModuleDoc && (firstChar === CHAR_CODES.PERCENT || firstChar === CHAR_CODES.DASH)) {
+                inModuleDoc = false;
+                moduleDocCollected = true; // Mark as collected
+                // Don't break - continue processing this line for module declarations
+                i--; // Re-process this line outside module doc context
+            }
+        }
+
+        this.moduleInfo.doc = this.cleanDocumentation(this.fixModuleDocCodeBlocks(moduleDoc.join(STRINGS.NEWLINE)));
+    }
+
+    collectMultiLineConstruct(startIdx, openChar, closeChar) {
+        const lines = [];
+        let depth = 0;
+        let found = false;
+
+        for (let i = startIdx; i < this.lines.length; i++) {
+            const line = this.lines[i];
+            lines.push(line);
+
+            for (let j = 0; j < line.length; j++) {
+                const char = line[j];
+                if (char === openChar) {
+                    depth++;
+                    found = true;
+                } else if (char === closeChar && found) {
+                    depth--;
+                    if (depth === 0) {
+                        return lines;
+                    }
+                }
+            }
+
+            // Safety check for runaway constructs
+            if (i - startIdx > 100) break;
+        }
+
+        return lines;
+    }
+
+    processFunctions() {
+        const linesLength = this.lines.length;
+        const processedFunctions = new Set();
+        const commentedCodeBlocks = [];
+        let currentCommentedBlock = [];
+        let inCommentedBlock = false;
+
+        for (let i = 0; i < linesLength; i++) {
+            const line = this.lines[i];
+            const trimmed = line.trim();
+
+            if (!trimmed) {
+                if (inCommentedBlock) {
+                    currentCommentedBlock.push(line);
+                }
+                continue;
+            }
+
+            // Check for comment-style section headers
+            if (trimmed.startsWith('%%%') && trimmed.match(/^%%%-{10,}$/)) {
+                // This is a dash line, check if next line is a header and line after that is also dashes
+                if (i + 1 < linesLength && i + 2 < linesLength) {
+                    const nextLine = this.lines[i + 1].trim();
+                    const afterLine = this.lines[i + 2].trim();
+
+                    if (nextLine.startsWith('%%%') && !nextLine.match(/^%%%-{10,}$/) &&
+                        afterLine.match(/^%%%-{10,}$/)) {
+                        // Extract header text
+                        const headerText = nextLine.replace(/^%%%\s*/, '').trim();
+                        if (headerText) {
+                            this.sections.push({
+                                type: 'section_header',
+                                title: headerText,
+                                lineNumber: i + 2 // Store line number for sorting later
+                            });
+                        }
+                        // Skip the next two lines
+                        i += 2;
+                        continue;
+                    }
+                }
+            }
+
+            // Check for commented-out code blocks (lines starting with % but containing code patterns)
+            if (trimmed.startsWith('%') && !trimmed.startsWith('%%')) {
+                const uncommented = trimmed.substring(1).trim();
+                if (this.looksLikeCode(uncommented)) {
+                    if (!inCommentedBlock) {
+                        inCommentedBlock = true;
+                        currentCommentedBlock = [];
+                    }
+                    currentCommentedBlock.push(line);
+                    continue;
+                } else if (inCommentedBlock) {
+                    // End of commented code block
+                    if (currentCommentedBlock.length > 0) {
+                        commentedCodeBlocks.push({
+                            type: 'commented_code',
+                            lines: [...currentCommentedBlock],
+                            startLine: i - currentCommentedBlock.length + 1
+                        });
+                    }
+                    inCommentedBlock = false;
+                    currentCommentedBlock = [];
+                }
+            } else if (inCommentedBlock) {
+                // End of commented code block
+                if (currentCommentedBlock.length > 0) {
+                    commentedCodeBlocks.push({
+                        type: 'commented_code',
+                        lines: [...currentCommentedBlock],
+                        startLine: i - currentCommentedBlock.length + 1
+                    });
+                }
+                inCommentedBlock = false;
+                currentCommentedBlock = [];
+            }
+
+            // Check for start of function documentation block
+            if (REGEX.DOC_BLOCK_START.test(trimmed)) {
+                this.collectFunctionDoc(i);
+                continue;
+            }
+
+            // Check for -spec
+            if (trimmed.startsWith(STRINGS.SPEC_PREFIX)) {
+                this.collectParamTagsBeforeSpec(i);
+                this.collectSpec(i);
+                const specMatch = trimmed.match(REGEX.SPEC);
+                if (specMatch) {
+                    this.currentState.specFunctionName = specMatch[1];
+                }
+                continue;
+            }
+
+            // Check for conditional compilation directives
+            if (trimmed.startsWith('-ifdef(') || trimmed.startsWith('-ifndef(') ||
+                trimmed.startsWith('-else') || trimmed.startsWith('-endif')) {
+                this.addDirectiveToOutput(line, i);
+                continue;
+            }
+
+            // Check for function start
+            const funcMatch = trimmed.match(REGEX.FUNCTION);
+            if (funcMatch && !this.currentState.inFunction) {
+                const functionName = this.currentState.specFunctionName || funcMatch[1];
+                processedFunctions.add(functionName);
+
+                if (this.currentState.pendingDoc) {
+                    this.currentState.functionDoc = this.currentState.pendingDoc;
+                    this.currentState.pendingDoc = STRINGS.EMPTY;
+                    this.startFunction(functionName, i);
+                } else {
+                    // This is an undocumented function
+                    this.startUndocumentedFunction(functionName, i);
+                    // Skip ahead to avoid reprocessing
+                    while (i < linesLength && !this.isFunctionEnd(this.lines[i])) {
+                        i++;
+                    }
+                }
+                this.currentState.specFunctionName = STRINGS.EMPTY;
+            }
+
+            // If in function, collect lines
+            if (this.currentState.inFunction) {
+                this.collectFunctionLine(line);
+                if (this.isFunctionEnd(line)) {
+                    this.endFunction();
+                }
+            }
+        }
+
+        // Handle remaining commented code block
+        if (inCommentedBlock && currentCommentedBlock.length > 0) {
+            commentedCodeBlocks.push({
+                type: 'commented_code',
+                lines: currentCommentedBlock,
+                startLine: linesLength - currentCommentedBlock.length + 1
+            });
+        }
+
+        if (this.currentState.inFunction) {
+            this.endFunction();
+        }
+
+        // Store commented code blocks for later inclusion
+        this.commentedCodeBlocks = commentedCodeBlocks;
+    }
+
+    collectFunctionDoc(startIdx) {
+        const docLines = [];
+        const linesLength = this.lines.length;
+
+        for (let i = startIdx; i < linesLength; i++) {
+            const line = this.lines[i];
+            const trimmed = line.trim();
+
+            if (trimmed.startsWith(STRINGS.DOUBLE_PERCENT)) {
+                let docLine = trimmed.substring(2).trim();
+                if (i === startIdx) {
+                    docLine = docLine.replace(REGEX.REMOVE_DOC, STRINGS.EMPTY);
+                }
+                docLines.push(docLine);
+            } else if (!trimmed) {
+                // Look ahead for more %% comments
+                let j = i + 1;
+                while (j < linesLength && !this.lines[j].trim()) j++;
+
+                if (j < linesLength && this.lines[j].trim().startsWith(STRINGS.DOUBLE_PERCENT)) {
+                    docLines.push(STRINGS.EMPTY);
+                } else {
+                    break;
+                }
+            } else if (!trimmed.startsWith(STRINGS.SINGLE_PERCENT)) {
+                break;
+            }
+        }
+
+        this.currentState.pendingDoc = docLines.join(STRINGS.NEWLINE);
+    }
+
+    collectParamTagsBeforeSpec(specIdx) {
+        const paramLines = [];
+        let hitDocBlock = false;
+
+        for (let i = specIdx - 1; i >= 0; i--) {
+            const trimmed = this.lines[i].trim();
+
+            if (REGEX.DOC_BLOCK_START.test(trimmed)) {
+                hitDocBlock = true;
+                break;
+            }
+
+            if (!trimmed || (!trimmed.startsWith(STRINGS.DOUBLE_PERCENT) && !trimmed.startsWith(STRINGS.SINGLE_PERCENT))) {
+                break;
+            }
+
+            if (trimmed.startsWith(STRINGS.DOUBLE_PERCENT) &&
+                (trimmed.includes(STRINGS.PARAM_TAG) || trimmed.includes(STRINGS.RETURNS_TAG))) {
+                paramLines.unshift(trimmed.substring(2).trim());
+            }
+        }
+
+        if (paramLines.length > 0 && !hitDocBlock) {
+            const existingDoc = this.currentState.pendingDoc;
+            const newDoc = paramLines.join(STRINGS.NEWLINE);
+            this.currentState.pendingDoc = existingDoc ?
+                existingDoc + STRINGS.NEWLINE + newDoc : newDoc;
+        }
+    }
+
+    collectSpec(startIdx) {
+        const specLines = [];
+        let depth = 0;
+        const linesLength = this.lines.length;
+
+        for (let i = startIdx; i < linesLength; i++) {
+            const line = this.lines[i];
+            specLines.push(line);
+
+            // Fast character-based depth tracking
+            for (let j = 0, len = line.length; j < len; j++) {
+                const charCode = line.charCodeAt(j);
+                if (charCode === CHAR_CODES.OPEN_PAREN) depth++;
+                else if (charCode === CHAR_CODES.CLOSE_PAREN) depth--;
+            }
+
+            if (line.trim().endsWith('.') && depth === 0) {
+                break;
+            }
+        }
+
+        this.currentState.functionSpec = specLines.join(STRINGS.NEWLINE);
+    }
+
+    startFunction(name, lineNumber = 0) {
+        this.currentState.inFunction = true;
+        this.currentState.functionName = name;
+        this.currentState.functionLineNumber = lineNumber;
+        this.currentState.functionLines.length = 0;
+        this.currentState.braceDepth = 0;
+        this.currentState.parenDepth = 0;
+        this.currentState.inlineDocTags.length = 0;
+    }
+
+    collectFunctionLine(line) {
+        this.currentState.functionLines.push(line);
+
+        // Fast character-based depth tracking
+        for (let i = 0, len = line.length; i < len; i++) {
+            const charCode = line.charCodeAt(i);
+            switch (charCode) {
+                case CHAR_CODES.OPEN_BRACE:
+                case CHAR_CODES.OPEN_BRACKET:
+                    this.currentState.braceDepth++;
+                    break;
+                case CHAR_CODES.CLOSE_BRACE:
+                case CHAR_CODES.CLOSE_BRACKET:
+                    this.currentState.braceDepth--;
+                    break;
+                case CHAR_CODES.OPEN_PAREN:
+                    this.currentState.parenDepth++;
+                    break;
+                case CHAR_CODES.CLOSE_PAREN:
+                    this.currentState.parenDepth--;
+                    break;
+            }
+        }
+    }
+
+    isFunctionEnd(line) {
+        const trimmed = line.trim();
+        return this.currentState.braceDepth === 0 &&
+               this.currentState.parenDepth === 0 &&
+               trimmed.charCodeAt(trimmed.length - 1) === CHAR_CODES.DOT &&
+               trimmed.charCodeAt(0) !== CHAR_CODES.PERCENT;
+    }
+
+    endFunction() {
+        const processedBody = this.processFunctionBody(this.currentState.functionLines);
+
+        this.functions.push({
+            name: this.currentState.functionName,
+            spec: this.currentState.functionSpec,
+            doc: this.currentState.functionDoc,
+            body: processedBody,
+            hasImplementation: true,
+            lineNumber: this.currentState.functionLineNumber
+        });
+
+        // Reset state efficiently
+        this.currentState.inFunction = false;
+        this.currentState.functionName = STRINGS.EMPTY;
+        this.currentState.functionLineNumber = 0;
+        this.currentState.functionSpec = STRINGS.EMPTY;
+        this.currentState.functionDoc = STRINGS.EMPTY;
+        this.currentState.functionLines.length = 0;
+        this.currentState.specFunctionName = STRINGS.EMPTY;
+        this.currentState.inlineDocTags.length = 0;
+    }
+
+    startUndocumentedFunction(name, startLine) {
+        const functionLines = [];
+        let braceDepth = 0;
+        let parenDepth = 0;
+        let i = startLine;
+
+        // Collect the entire function
+        while (i < this.lines.length) {
+            const line = this.lines[i];
+            functionLines.push(line);
+
+            // Track depth
+            for (let j = 0; j < line.length; j++) {
+                const charCode = line.charCodeAt(j);
+                switch (charCode) {
+                    case CHAR_CODES.OPEN_BRACE:
+                    case CHAR_CODES.OPEN_BRACKET:
+                        braceDepth++;
+                        break;
+                    case CHAR_CODES.CLOSE_BRACE:
+                    case CHAR_CODES.CLOSE_BRACKET:
+                        braceDepth--;
+                        break;
+                    case CHAR_CODES.OPEN_PAREN:
+                        parenDepth++;
+                        break;
+                    case CHAR_CODES.CLOSE_PAREN:
+                        parenDepth--;
+                        break;
+                }
+            }
+
+            // Check if function ended
+            const trimmed = line.trim();
+            if (braceDepth === 0 && parenDepth === 0 &&
+                trimmed.charCodeAt(trimmed.length - 1) === CHAR_CODES.DOT &&
+                trimmed.charCodeAt(0) !== CHAR_CODES.PERCENT) {
+                break;
+            }
+
+            i++;
+        }
+
+        // Find corresponding spec
+        const spec = this.moduleInfo.specs.find(s => s.function === name);
+
+        this.undocumentedFunctions.push({
+            name,
+            spec: spec ? spec.definition : null,
+            body: this.processFunctionBody(functionLines),
+            lines: functionLines
+        });
+    }
+
+    // Helper method to detect if a line looks like code
+    looksLikeCode(line) {
+        if (!line || line.length === 0) return false;
+
+        // Check for common code patterns
+        const codePatterns = [
+            /^[a-z][a-z0-9_]*\s*\(/,           // function calls: function(
+            /^[A-Z][a-zA-Z0-9_]*\s*=/,         // variable assignments: Var =
+            /^\s*\{/,                          // tuples/records: {
+            /^\s*\[/,                          // lists: [
+            /^\s*case\s+/,                     // case statements
+            /^\s*if\s+/,                       // if statements
+            /^\s*catch\s+/,                    // catch blocks
+            /^\s*after\s+/,                    // after blocks
+            /^\s*end[,.]?\s*$/,                // end keywords
+            /^\s*ok\s*$/,                      // ok atoms
+            /^\s*true\s*$/,                    // boolean atoms
+            /^\s*false\s*$/,                   // boolean atoms
+            /->\s*$/,                          // arrow operators
+            /^\s*\?[A-Z]/,                     // macro usage
+            /^\s*[a-z_][a-z0-9_]*\s*\(/,      // function definitions
+            /^\s*\d+\s*$/,                     // numbers
+            /^\s*".*"\s*$/,                   // strings
+            /^\s*<<.*>>\s*$/,                  // binaries
+            /^\s*#\w+/,                        // record syntax
+            /^\s*receive\s+/,                  // receive blocks
+            /^\s*spawn/,                       // spawn calls
+            /^\s*gen_server:/,                 // gen_server calls
+            /^\s*supervisor:/,                 // supervisor calls
+            /\bmatch\b|\bguard\b|\btry\b|\bfun\b/, // erlang keywords
+        ];
+
+        return codePatterns.some(pattern => pattern.test(line));
+    }
+
+    // Helper method to add conditional compilation directives
+    addDirectiveToOutput(line, lineNumber) {
+        this.conditionalDirectives.push({
+            line: line.trim(),
+            lineNumber: lineNumber + 1,
+            type: 'conditional_compilation'
+        });
+    }
+
+    processFunctionBody(lines) {
+        const segments = [];
+        const currentCode = [];
+        const pendingTagLines = [];
+
+        const flushCode = () => {
+            if (currentCode.length > 0) {
+                segments.push({ type: 'code', content: currentCode.join(STRINGS.NEWLINE) });
+                currentCode.length = 0;
+            }
+        };
+
+        const flushTags = () => {
+            if (pendingTagLines.length > 0) {
+                const tagText = pendingTagLines.join(STRINGS.NEWLINE);
+                const parsed = this.parseDocumentation(tagText);
+                const docParts = [];
+
+                if (parsed.params.length > 0) {
+                    docParts.push(STRINGS.PARAMETERS_HEADER, STRINGS.EMPTY);
+                    parsed.params.forEach(p => {
+                        const desc = this.cleanDocumentation(p.description || STRINGS.EMPTY, true);
+                        docParts.push(`- ${STRINGS.BACKTICK}${p.name}${STRINGS.BACKTICK} - ${desc}`);
+                    });
+                    docParts.push(STRINGS.EMPTY);
+                }
+
+                if (parsed.returns.length > 0) {
+                    docParts.push(STRINGS.RETURNS_HEADER, STRINGS.EMPTY);
+                    const expanded = parsed.returns.flatMap(r => this.splitReturnsIntoOutcomes(r));
+                    expanded.forEach(r => docParts.push(`- ${this.formatReturnsText(r)}`));
+                    docParts.push(STRINGS.EMPTY);
+                }
+
+                if (docParts.length > 0) {
+                    segments.push({ type: 'doc', content: docParts.join(STRINGS.NEWLINE) });
+                }
+                pendingTagLines.length = 0;
+            }
+        };
+
+        for (const line of lines) {
+            const trimmed = line.trim();
+
+            if (REGEX.COMMENT_SINGLE.test(trimmed) || REGEX.COMMENT_DOUBLE.test(trimmed)) {
+                flushCode();
+
+                const commentText = REGEX.COMMENT_DOUBLE.test(trimmed)
+                    ? line.replace(REGEX.COMMENT_DOUBLE_PREFIX, STRINGS.EMPTY)
+                    : line.replace(REGEX.COMMENT_SINGLE_PREFIX, STRINGS.EMPTY);
+                const cleaned = this.cleanInlineComment(commentText);
+
+                const returnsLikeTuple = REGEX.RETURNS_LIKE_TUPLE.test(cleaned);
+                const returnsLikeAtom = REGEX.RETURNS_LIKE_ATOM.test(cleaned);
+                const isTagParam = cleaned.startsWith(STRINGS.PARAM_TAG);
+                const isTagReturns = cleaned.startsWith(STRINGS.RETURNS_TAG);
+
+                if (isTagParam || isTagReturns || returnsLikeTuple || returnsLikeAtom) {
+                    const lineAsTag = (isTagParam || isTagReturns)
+                        ? cleaned.trim()
+                        : `${STRINGS.RETURNS_TAG} ${cleaned.trim()}`;
+                    pendingTagLines.push(lineAsTag);
+                } else if (pendingTagLines.length > 0) {
+                    const lastTag = pendingTagLines[pendingTagLines.length - 1];
+                    if (lastTag.startsWith(STRINGS.RETURNS_TAG) || lastTag.startsWith(STRINGS.PARAM_TAG)) {
+                        pendingTagLines.push(cleaned.trim());
+                    } else {
+                        flushTags();
+                        segments.push({ type: 'comment', content: cleaned });
+                    }
+                } else {
+                    flushTags();
+                    segments.push({ type: 'comment', content: cleaned });
+                }
+            } else {
+                flushTags();
+                currentCode.push(line);
+            }
+        }
+
+        flushTags();
+        flushCode();
+        return segments;
+    }
+
+    cleanInlineComment(text) {
+        return text.replace(REGEX.BACKTICK_QUOTE, `${STRINGS.BACKTICK}$1${STRINGS.BACKTICK}`).trim();
+    }
+
+    fixCodeBlocks(text) {
+        if (!text) return text;
+
+        const lines = text.split(STRINGS.NEWLINE);
+        const result = [];
+        let inCodeBlock = false;
+
+        for (let i = 0; i < lines.length; i++) {
+            const line = lines[i];
+            const trimmed = line.trim();
+
+            // Check if this is a code block delimiter
+            if (REGEX.CODE_FENCE.test(trimmed)) {
+                if (!inCodeBlock && trimmed === '```') {
+                    // Start of unmarked code block - check if it's empty first
+                    let blockContent = [];
+                    let j = i + 1;
+
+                    // Collect content until closing ```
+                    while (j < lines.length) {
+                        const contentLine = lines[j];
+                        if (contentLine.trim() === '```') {
+                            break;
+                        }
+                        blockContent.push(contentLine);
+                        j++;
+                    }
+
+                    // If block is empty, skip it entirely
+                    const hasContent = blockContent.some(line => line.trim() !== '');
+                    if (!hasContent) {
+                        // Skip the empty code block entirely
+                        i = j; // Skip to after the closing ```
+                        continue;
+                    }
+
+                    // Determine appropriate language based on content
+                    const nextLine = blockContent.length > 0 ? blockContent[0].trim() : '';
+                    let language = 'text'; // default
+
+                    // Heuristics to determine language based on content
+                    if (nextLine.startsWith('/') || nextLine.includes('Parameters:') ||
+                        nextLine.includes('- `') || nextLine.includes('(optional)')) {
+                        language = 'text';
+                    } else if (nextLine.includes('#{') || nextLine.includes('<<') ||
+                              nextLine.includes('->') || nextLine.match(/^[a-z_]+\(/)) {
+                        language = 'erlang';
+                    }
+
+                    // For text blocks, add ignore attribute to prevent mdBook testing
+                    if (language === 'text') {
+                        result.push('```text,ignore');
+                    } else {
+                        result.push('```' + language);
+                    }
+                    inCodeBlock = true;
+                } else if (inCodeBlock && trimmed === '```') {
+                    // End of code block
+                    result.push(line);
+                    inCodeBlock = false;
+                } else {
+                    // Already has language specifier or other case
+                    result.push(line);
+                    if (trimmed.startsWith('```')) {
+                        inCodeBlock = !inCodeBlock;
+                    }
+                }
+            } else {
+                result.push(line);
+            }
+        }
+
+        return result.join(STRINGS.NEWLINE);
+    }
+
+    fixModuleDocCodeBlocks(text) {
+        if (!text) return STRINGS.EMPTY;
+
+        const lines = text.split(STRINGS.NEWLINE);
+        const result = [];
+        let inCodeBlock = false;
+        let codeBlockStart = -1;
+
+        for (let i = 0; i < lines.length; i++) {
+            const line = lines[i];
+            const trimmed = line.trim();
+
+            // Check for code block start
+            if (trimmed === '```' && !inCodeBlock) {
+                inCodeBlock = true;
+                codeBlockStart = i;
+                result.push(line);
+                continue;
+            }
+
+            // Check for code block end
+            if (trimmed === '```' && inCodeBlock) {
+                inCodeBlock = false;
+                result.push(line);
+                continue;
+            }
+
+            // Check for implicit code block end (new section starting with /)
+            if (inCodeBlock && trimmed.startsWith('/')) {
+                // Close the previous code block
+                result.push('```');
+                result.push('');
+                inCodeBlock = false;
+            }
+
+            result.push(line);
+        }
+
+        // Close any unclosed code block at the end
+        if (inCodeBlock) {
+            result.push('```');
+        }
+
+        return result.join(STRINGS.NEWLINE);
+    }
+
+    cleanDocumentation(text, skipCodeBlockFix = false) {
+        if (!text) return STRINGS.EMPTY;
+
+        text = text.replace(REGEX.PRE_TAG, (match, content) => this.formatPreContent(content));
+
+        // Only fix unmarked code blocks for module-level documentation
+        // Skip for function documentation to avoid excessive text,ignore blocks
+        if (!skipCodeBlockFix) {
+            text = this.fixCodeBlocks(text);
+        }
+
+        let cleaned = text
+            .replace(REGEX.BACKTICK_QUOTE, `${STRINGS.BACKTICK}$1${STRINGS.BACKTICK}`)
+            .replace(REGEX.HTML_ENTITIES_LT, '<<')
+            .replace(REGEX.HTML_ENTITIES_GT, '>>')
+            .replace(REGEX.REMOVE_DOC, STRINGS.EMPTY)
+            .replace(REGEX.MULTIPLE_NEWLINES, '\n\n')
+            .replace(REGEX.TRAILING_SPACES, STRINGS.EMPTY)
+            .replace(REGEX.TRIM, STRINGS.EMPTY);
+
+        return this.reflowNumberedLists(cleaned);
+    }
+
+    convertCommentStyleHeaders(text) {
+        if (!text) return text;
+
+        const lines = text.split(STRINGS.NEWLINE);
+        const result = [];
+
+        for (let i = 0; i < lines.length; i++) {
+            const line = lines[i];
+            const trimmed = line.trim();
+
+            // Look for pattern: dashes followed by text followed by dashes
+            if (trimmed.match(/^-{10,}$/)) {
+                // This is a dash line, check if next line is a header
+                if (i + 1 < lines.length) {
+                    const nextLine = lines[i + 1];
+                    const nextTrimmed = nextLine.trim();
+
+                    // Check if the line after is also dashes (closing the header)
+                    if (i + 2 < lines.length && lines[i + 2].trim().match(/^-{10,}$/)) {
+                        // This is a comment-style header: convert to markdown
+                        if (nextTrimmed) {
+                            result.push(`## ${nextTrimmed}`);
+                            result.push(STRINGS.EMPTY);
+                        }
+                        // Skip the next two lines (header text and closing dashes)
+                        i += 2;
+                        continue;
+                    }
+                }
+            }
+
+            result.push(line);
+        }
+
+        return result.join(STRINGS.NEWLINE);
+    }
+
+    generateInterleavedContent(md) {
+        // Create a combined list of sections and functions, sorted by line number
+        const contentItems = [];
+
+        // Add sections
+        for (const section of this.sections) {
+            if (section.type === 'section_header') {
+                contentItems.push({
+                    type: 'section',
+                    lineNumber: section.lineNumber,
+                    title: section.title
+                });
+            }
+        }
+
+        // Add functions
+        const groupedFunctions = this.groupFunctionsByName(this.functions);
+        for (const group of groupedFunctions) {
+            // Use the line number of the first function in the group
+            const lineNumber = group.functions[0]?.lineNumber || 0;
+            contentItems.push({
+                type: 'function_group',
+                lineNumber: lineNumber,
+                group: group
+            });
+        }
+
+        // Sort by line number
+        contentItems.sort((a, b) => a.lineNumber - b.lineNumber);
+
+        // Generate markdown for each item
+        for (const item of contentItems) {
+            if (item.type === 'section') {
+                md.push(`## ${item.title}`);
+                md.push(STRINGS.EMPTY);
+            } else if (item.type === 'function_group') {
+                this.generateFunctionGroupMarkdown(md, item.group);
+            }
+        }
+    }
+
+    generateFunctionGroupMarkdown(md, group) {
+        md.push(`## ${group.name}`);
+        md.push(STRINGS.EMPTY);
+
+        const combinedDoc = this.combineFunctionDocs(group.functions);
+        if (combinedDoc) {
+            md.push(combinedDoc);
+            md.push(STRINGS.EMPTY);
+        }
+
+        for (const func of group.functions) {
+            if (func.spec) {
+                md.push(`\`\`\`${STRINGS.ERLANG}`);
+                md.push(func.spec.trim());
+                md.push('```');
+                md.push(STRINGS.EMPTY);
+            }
+
+            if (func.body?.length > 0) {
+                md.push(STRINGS.EMPTY);
+                for (const segment of func.body) {
+                    if (segment.type === 'comment') {
+                        md.push(segment.content);
+                        md.push(STRINGS.EMPTY);
+                    } else if (segment.type === 'doc') {
+                        md.push(segment.content);
+                        md.push(STRINGS.EMPTY);
+                    } else if (segment.type === 'code') {
+                        md.push(`\`\`\`${STRINGS.ERLANG}`);
+                        md.push(segment.content.trim());
+                        md.push('```');
+                        md.push(STRINGS.EMPTY);
+                    }
+                }
+            }
+        }
+
+        md.push(STRINGS.EMPTY);
+    }
+
+    formatReturnsText(text) {
+        if (!text) return STRINGS.EMPTY;
+        let result = this.cleanDocumentation(text, true);
+
+        const leadingMatch = result.match(REGEX.LEADING_RETURN_TOKEN);
+        if (leadingMatch) {
+            const [, leadSpace, token] = leadingMatch;
+            result = leadSpace + STRINGS.BACKTICK + token + STRINGS.BACKTICK +
+                    result.slice(leadSpace.length + token.length);
+        }
+
+        return result.replace(REGEX.STANDALONE_TUPLE,
+            (m, pre, tuple, post) => `${pre}${STRINGS.BACKTICK}${tuple}${STRINGS.BACKTICK}${post}`);
+    }
+
+    splitReturnsIntoOutcomes(text) {
+        if (!text) return [];
+        const s = this.cleanDocumentation(text, true);
+        const matches = [];
+        let match;
+
+        // Reset regex lastIndex to avoid issues with global regex
+        REGEX.RETURNS_TOKENS.lastIndex = 0;
+        while ((match = REGEX.RETURNS_TOKENS.exec(s)) !== null) {
+            matches.push({ index: match.index, token: match[0] });
+        }
+
+        if (matches.length === 0 || matches[0].index > 0) {
+            return [s.trim()];
+        }
+
+        const parts = [];
+        const matchesLength = matches.length;
+        for (let i = 0; i < matchesLength; i++) {
+            const start = matches[i].index;
+            const nextStart = (i + 1 < matchesLength) ? matches[i + 1].index : s.length;
+            let segment = s.slice(start, nextStart).trim()
+                .replace(REGEX.COMMA_START, STRINGS.EMPTY)
+                .replace(REGEX.COMMA_END, STRINGS.EMPTY);
+            if (segment) parts.push(segment.trim());
+        }
+
+        return parts.filter(Boolean);
+    }
+
+    reflowNumberedLists(text) {
+        if (!text) return STRINGS.EMPTY;
+        const lines = text.split(STRINGS.NEWLINE);
+        const out = [];
+        let inNumbered = false;
+        let lastNumIndex = -1;
+
+        const linesLength = lines.length;
+        for (let i = 0; i < linesLength; i++) {
+            const raw = lines[i];
+            const trimmed = raw.trim();
+
+            const isNumbered = REGEX.NUMBERED_LIST.test(trimmed);
+            const isBullet = REGEX.BULLET_LIST.test(trimmed);
+            const isHeading = REGEX.HEADING.test(trimmed);
+            const isCodeFence = REGEX.CODE_FENCE.test(trimmed);
+
+            // Ensure a blank line BEFORE any list (numbered or bullet) begins
+            if ((isNumbered || isBullet) && out.length > 0) {
+                const prev = out[out.length - 1];
+                if (prev.trim() !== STRINGS.EMPTY) {
+                    out.push(STRINGS.EMPTY);
+                }
+            }
+
+            if (isNumbered) {
+                out.push(trimmed);
+                inNumbered = true;
+                lastNumIndex = out.length - 1;
+                continue;
+            }
+
+            // Pass through bullet list lines unchanged (no reflow of bullets for now)
+            if (isBullet) {
+                out.push(raw);
+                continue;
+            }
+
+            if (inNumbered) {
+                if (!trimmed) {
+                    out.push(STRINGS.EMPTY);
+                    inNumbered = false;
+                    lastNumIndex = -1;
+                    continue;
+                }
+                if (!isNumbered && !isBullet && !isHeading && !isCodeFence) {
+                    out[lastNumIndex] = out[lastNumIndex] + STRINGS.SPACE + trimmed;
+                    continue;
+                }
+                inNumbered = false;
+                lastNumIndex = -1;
+            }
+
+            out.push(raw);
+        }
+
+        // Ensure blank line separation
+        const separated = [];
+        const outLength = out.length;
+        for (let i = 0; i < outLength; i++) {
+            const cur = out[i];
+            const next = i + 1 < outLength ? out[i + 1] : STRINGS.EMPTY;
+            separated.push(cur);
+            // If a paragraph ends with ':' and is immediately followed by a numbered list,
+            // insert a blank line between them to satisfy Markdown list rendering rules.
+            if (REGEX.COLON_END.test(cur.trim()) && next && REGEX.NUMBERED_LIST.test(next.trim())) {
+                if (separated[separated.length - 1] !== STRINGS.EMPTY) {
+                    separated.push(STRINGS.EMPTY);
+                }
+            }
+        }
+
+        return separated.join(STRINGS.NEWLINE);
+    }
+
+    formatPreContent(content) {
+        const lines = content.trim().split(STRINGS.NEWLINE);
+        const formatted = [];
+
+        let i = 0;
+        const linesLength = lines.length;
+        while (i < linesLength) {
+            const line = lines[i].trim();
+
+            if (!line) {
+                i++;
+                continue;
+            }
+
+            const defMatch = line.match(REGEX.DEFINITION);
+
+            if (defMatch) {
+                const [, term, initialDesc] = defMatch;
+                let fullDescription = initialDesc.trim();
+
+                let j = i + 1;
+                while (j < linesLength) {
+                    const nextLine = lines[j];
+
+                    if (!nextLine.trim()) {
+                        j++;
+                        continue;
+                    }
+
+                    if (nextLine.trim().match(REGEX.DEFINITION)) {
+                        break;
+                    }
+
+                    if (nextLine.trim()) {
+                        fullDescription += STRINGS.SPACE + nextLine.trim();
+                    }
+                    j++;
+                }
+
+                formatted.push(STRINGS.EMPTY, `**${term.trim()}**`, STRINGS.EMPTY, fullDescription);
+                i = j;
+            } else {
+                if (line.toLowerCase().includes('hyperbeam') && line.includes('options')) {
+                    formatted.push(STRINGS.EMPTY, `### ${line}`, STRINGS.EMPTY);
+                } else {
+                    const optMatch = line.match(REGEX.OPTION_DEF);
+                    if (optMatch) {
+                        const [, optName, optDesc] = optMatch;
+                        formatted.push(STRINGS.EMPTY, `**${optName}**`, STRINGS.EMPTY, optDesc);
+                    } else {
+                        formatted.push(line);
+                    }
+                }
+                i++;
+            }
+        }
+
+        return formatted.join(STRINGS.NEWLINE);
+    }
+
+    parseDocumentation(docText) {
+        const lines = docText.split(STRINGS.NEWLINE);
+        const result = {
+            description: [],
+            params: [],
+            returns: []
+        };
+
+        let currentSection = 'description';
+        let currentParam = null;
+        let lastReturnIndex = -1;
+
+        for (const line of lines) {
+            const trimmed = line.trim();
+
+            const paramMatch = trimmed.match(REGEX.PARAM);
+            if (paramMatch) {
+                if (currentParam) {
+                    result.params.push(currentParam);
+                }
+                currentParam = {
+                    name: paramMatch[1],
+                    description: paramMatch[2] || STRINGS.EMPTY
+                };
+                currentSection = 'param';
+                continue;
+            }
+
+            if (REGEX.RETURNS.test(trimmed)) {
+                if (currentParam) {
+                    result.params.push(currentParam);
+                    currentParam = null;
+                }
+                const returnsText = trimmed.replace(REGEX.RETURNS, STRINGS.EMPTY);
+                result.returns.push(returnsText);
+                lastReturnIndex = result.returns.length - 1;
+                currentSection = 'returns';
+                continue;
+            }
+
+            if (currentSection === 'description') {
+                if (trimmed) {
+                    result.description.push(trimmed);
+                } else {
+                    const last = result.description[result.description.length - 1];
+                    if (last !== STRINGS.EMPTY) {
+                        result.description.push(STRINGS.EMPTY);
+                    }
+                }
+            } else if (currentSection === 'param' && currentParam && trimmed) {
+                currentParam.description += STRINGS.SPACE + trimmed;
+            } else if (currentSection === 'returns' && trimmed) {
+                if (lastReturnIndex >= 0) {
+                    result.returns[lastReturnIndex] =
+                        (result.returns[lastReturnIndex] + STRINGS.SPACE + trimmed)
+                        .replace(REGEX.WHITESPACE_NORMALIZE, STRINGS.SPACE).trim();
+                } else {
+                    result.returns.push(trimmed);
+                    lastReturnIndex = result.returns.length - 1;
+                }
+            }
+        }
+
+        if (currentParam) {
+            result.params.push(currentParam);
+        }
+
+        return result;
+    }
+
+    addSeparator(md) {
+        // Only add separator if the last entry is not empty and not already a separator
+        if (md.length > 0) {
+            const lastLine = md[md.length - 1];
+            const prevLine = md.length > 1 ? md[md.length - 2] : '';
+
+            // Don't add separator if last line is already empty and previous is separator
+            if (lastLine === STRINGS.EMPTY && prevLine === STRINGS.SEPARATOR) {
+                return;
+            }
+
+            // Don't add separator if last line is already a separator
+            if (lastLine === STRINGS.SEPARATOR) {
+                return;
+            }
+
+            // Add separator with proper spacing
+            if (lastLine !== STRINGS.EMPTY) {
+                md.push(STRINGS.EMPTY);
+            }
+            md.push(STRINGS.SEPARATOR);
+            md.push(STRINGS.EMPTY);
+        }
+    }
+
+    generateMarkdown(fileName) {
+        const githubUrl = `${this.options.githubBase}/${fileName}`;
+        const md = [];
+
+        // Header
+        md.push(`# ${this.moduleInfo.name || fileName.replace('.erl', STRINGS.EMPTY)}`);
+        md.push(STRINGS.EMPTY);
+        md.push(`[View source on GitHub](${githubUrl})`);
+        md.push(STRINGS.EMPTY);
+
+        // Metadata section
+        this.generateMetadataSection(md);
+
+        // Module documentation
+        if (this.moduleInfo.doc) {
+            md.push(this.moduleInfo.doc);
+            this.addSeparator(md);
+        }
+
+        // Generate interleaved content (sections and functions) sorted by line number
+        this.generateInterleavedContent(md);
+
+        // Commented-out code blocks
+        if (this.commentedCodeBlocks && this.commentedCodeBlocks.length > 0) {
+            md.push('## Commented-Out Code');
+            md.push(STRINGS.EMPTY);
+            md.push('*The following code blocks are commented out but may contain useful examples:*');
+            md.push(STRINGS.EMPTY);
+
+            for (const block of this.commentedCodeBlocks) {
+                md.push('```erlang');
+                for (const line of block.lines) {
+                    md.push(line);
+                }
+                md.push('```');
+                md.push(STRINGS.EMPTY);
+            }
+        }
+
+        // Conditional compilation directives
+        if (this.conditionalDirectives && this.conditionalDirectives.length > 0) {
+            md.push('## Conditional Compilation');
+            md.push(STRINGS.EMPTY);
+            md.push('*The following conditional compilation directives are used in this module:*');
+            md.push(STRINGS.EMPTY);
+
+            md.push('```erlang');
+            for (const directive of this.conditionalDirectives) {
+                md.push(directive.line);
+            }
+            md.push('```');
+            md.push(STRINGS.EMPTY);
+        }
+
+        // Undocumented functions section
+        if (this.undocumentedFunctions.length > 0) {
+            md.push('## Undocumented Functions');
+            md.push(STRINGS.EMPTY);
+            md.push('*The following functions lack documentation comments but are included for completeness:*');
+            md.push(STRINGS.EMPTY);
+
+            for (const func of this.undocumentedFunctions) {
+                md.push(`### ${func.name}`);
+                md.push(STRINGS.EMPTY);
+
+                if (func.spec) {
+                    md.push(`\`\`\`${STRINGS.ERLANG}`);
+                    md.push(func.spec.trim());
+                    md.push('```');
+                    md.push(STRINGS.EMPTY);
+                }
+
+                if (func.body?.length > 0) {
+                    for (const segment of func.body) {
+                        if (segment.type === 'comment') {
+                            md.push(segment.content);
+                            md.push(STRINGS.EMPTY);
+                        } else if (segment.type === 'code') {
+                            md.push(`\`\`\`${STRINGS.ERLANG}`);
+                            md.push(segment.content.trim());
+                            md.push('```');
+                            md.push(STRINGS.EMPTY);
+                        }
+                    }
+                }
+            }
+
+            this.addSeparator(md);
+        }
+
+        this.addSeparator(md);
+        md.push(`*Generated from [${fileName}](${githubUrl})*`);
+
+        const finalMarkdown = md.join(STRINGS.NEWLINE);
+        return finalMarkdown;
+    }
+
+    generateMetadataSection(md) {
+        md.push('## Module Metadata');
+        md.push(STRINGS.EMPTY);
+
+        // Basic module information
+        md.push(`**Module:** \`${this.moduleInfo.name || 'unknown'}\``);
+        md.push(`**Exports:** ${this.moduleInfo.exports.length} functions`);
+
+        if (this.moduleInfo.behaviours.length > 0) {
+            md.push(`**Behaviours:** ${this.moduleInfo.behaviours.map(b => `\`${b}\``).join(', ')}`);
+        }
+
+        if (this.moduleInfo.includes.length > 0) {
+            md.push(`**Includes:** ${this.moduleInfo.includes.length} files`);
+        }
+
+        if (this.moduleInfo.defines.length > 0) {
+            md.push(`**Defines:** ${this.moduleInfo.defines.length} macros`);
+        }
+
+        if (this.moduleInfo.records.length > 0) {
+            md.push(`**Records:** ${this.moduleInfo.records.length} records`);
+        }
+
+        if (this.moduleInfo.types.length > 0) {
+            md.push(`**Types:** ${this.moduleInfo.types.length} type definitions`);
+        }
+
+        md.push(STRINGS.EMPTY);
+
+        // Exports section
+        if (this.moduleInfo.exports.length > 0) {
+            md.push('### Exported Functions');
+            md.push(STRINGS.EMPTY);
+            this.moduleInfo.exports.forEach(exp => {
+                md.push(`- \`${exp}\``);
+            });
+            md.push(STRINGS.EMPTY);
+        }
+
+        // Includes section
+        if (this.moduleInfo.includes.length > 0) {
+            md.push('### Includes');
+            md.push(STRINGS.EMPTY);
+            md.push('```erlang');
+            this.moduleInfo.includes.forEach(inc => {
+                md.push(inc.line);
+            });
+            md.push('```');
+            md.push(STRINGS.EMPTY);
+        }
+
+        // Defines section
+        if (this.moduleInfo.defines.length > 0) {
+            md.push('### Macro Definitions');
+            md.push(STRINGS.EMPTY);
+            md.push('```erlang');
+            this.moduleInfo.defines.forEach(def => {
+                md.push(def.line);
+            });
+            md.push('```');
+            md.push(STRINGS.EMPTY);
+        }
+
+        // Records section
+        if (this.moduleInfo.records.length > 0) {
+            md.push('### Record Definitions');
+            md.push(STRINGS.EMPTY);
+            this.moduleInfo.records.forEach(rec => {
+                md.push(`#### \`${rec.name}\``);
+                md.push(STRINGS.EMPTY);
+                md.push('```erlang');
+                md.push(rec.definition);
+                md.push('```');
+                md.push(STRINGS.EMPTY);
+            });
+        }
+
+        // Types section
+        if (this.moduleInfo.types.length > 0) {
+            md.push('### Type Definitions');
+            md.push(STRINGS.EMPTY);
+            this.moduleInfo.types.forEach(type => {
+                md.push(`#### \`${type.name}\``);
+                md.push(STRINGS.EMPTY);
+                md.push('```erlang');
+                md.push(type.definition);
+                md.push('```');
+                md.push(STRINGS.EMPTY);
+            });
+        }
+
+        this.addSeparator(md);
+    }
+
+    groupFunctionsByName(functions) {
+        const groups = [];
+        let currentGroup = null;
+
+        for (const func of functions) {
+            if (!currentGroup || currentGroup.name !== func.name) {
+                currentGroup = { name: func.name, functions: [func] };
+                groups.push(currentGroup);
+            } else {
+                currentGroup.functions.push(func);
+            }
+        }
+
+        return groups;
+    }
+
+    combineFunctionDocs(functions) {
+        for (const func of functions) {
+            if (func.doc) {
+                const parsed = this.parseDocumentation(func.doc);
+                const combinedDoc = [];
+
+                if (parsed.description.length > 0) {
+                    combinedDoc.push(this.cleanDocumentation(parsed.description.join(STRINGS.NEWLINE), true));
+                    combinedDoc.push(STRINGS.EMPTY);
+                }
+
+                if (parsed.params.length > 0) {
+                    combinedDoc.push(STRINGS.PARAMETERS_HEADER);
+                    combinedDoc.push(STRINGS.EMPTY);
+                    parsed.params.forEach(param => {
+                        const desc = this.cleanDocumentation(param.description, true);
+                        combinedDoc.push(`- ${STRINGS.BACKTICK}${param.name}${STRINGS.BACKTICK} - ${desc}`);
+                    });
+                    combinedDoc.push(STRINGS.EMPTY);
+                }
+
+                if (parsed.returns.length > 0) {
+                    combinedDoc.push(STRINGS.RETURNS_HEADER);
+                    combinedDoc.push(STRINGS.EMPTY);
+                    const expanded = parsed.returns.flatMap(r => this.splitReturnsIntoOutcomes(r));
+                    expanded.forEach(ret => combinedDoc.push(`- ${this.formatReturnsText(ret)}`));
+                    combinedDoc.push(STRINGS.EMPTY);
+                }
+
+                return combinedDoc.join(STRINGS.NEWLINE);
+            }
+        }
+        return null;
+    }
+}
+
+// CLI Interface
+function main() {
+    const args = process.argv.slice(2);
+    const verbose = args.includes('-v') || args.includes('--verbose');
+
+    const srcDir = process.env.SRC_DIR || path.join(process.cwd(), 'src');
+    const outputDir = process.env.OUTPUT_DIR || path.join(process.cwd(), 'docs/book/src');
+
+    if (!fs.existsSync(outputDir)) {
+        fs.mkdirSync(outputDir, { recursive: true });
+    }
+
+    const files = fs.readdirSync(srcDir).filter(f => f.endsWith('.erl'));
+    const parser = new ErlangLiterateParser({ verbose });
+
+    console.log(`Processing ${files.length} Erlang files...`);
+
+    for (const file of files) {
+        if (verbose) console.log(`  Processing ${file}...`);
+
+        try {
+            const inputPath = path.join(srcDir, file);
+            const outputPath = path.join(outputDir, `${file}.md`);
+            const markdown = parser.parseFile(inputPath);
+            fs.writeFileSync(outputPath, markdown);
+        } catch (error) {
+            console.error(`Error processing ${file}:`, error.message);
+        }
+    }
+
+    console.log(`✓ Generated documentation in ${outputDir}`);
+}
+
+if (import.meta.url === `file://${process.argv[1]}`) {
+    main();
+}
+
+export default ErlangLiterateParser;
\ No newline at end of file
diff --git a/docs/generate-literate-docs.sh b/docs/generate-literate-docs.sh
new file mode 100755
index 000000000..0c1328897
--- /dev/null
+++ b/docs/generate-literate-docs.sh
@@ -0,0 +1,200 @@
+#!/bin/bash
+
+# Comprehensive Erlang Literate Documentation Builder (JavaScript Implementation)
+# This script wraps the JavaScript parser for seamless integration
+
+# Colors for output
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+RED='\033[0;31m'
+NC='\033[0m' # No Color
+
+# Get script directory and root
+SCRIPT_DIR="$(dirname "$(realpath "$0")")"
+ROOT_DIR="$(dirname "$SCRIPT_DIR")"
+cd "$ROOT_DIR"
+
+# Configuration
+SRC_DIR="${SRC_DIR:-$ROOT_DIR/src}"
+OUTPUT_DIR="${OUTPUT_DIR:-$ROOT_DIR/docs/book/src}"
+PARSER_SCRIPT="$SCRIPT_DIR/erlang-literate-parser.js"
+
+# Parse arguments
+VERBOSE=false
+DRY_RUN=false
+SHOW_HELP=false
+
+if [[ "$@" == *"-h"* ]] || [[ "$@" == *"--help"* ]]; then
+    SHOW_HELP=true
+fi
+if [[ "$@" == *"-v"* ]] || [[ "$@" == *"--verbose"* ]]; then
+    VERBOSE=true
+fi
+if [[ "$@" == *"--dry-run"* ]] || [[ "$@" == *"--dryrun"* ]]; then
+    DRY_RUN=true
+fi
+
+if [ "$SHOW_HELP" = true ]; then
+    echo -e "${GREEN}HyperBEAM Literate Erlang Documentation Generator (JavaScript)${NC}"
+    echo "========================================================"
+    echo ""
+    echo "Usage: $0 [OPTIONS]"
+    echo ""
+    echo "Options:"
+    echo "  -v, --verbose     Enable verbose output"
+    echo "  --dry-run        Simulate deployment without actually deploying"
+    echo "  -h, --help       Show this help message"
+    echo ""
+    echo "Environment Variables:"
+    echo "  SRC_DIR          Source directory (default: ./src)"
+    echo "  OUTPUT_DIR       Output directory (default: ./docs/book/src)"
+    echo "  DEPLOY_KEY       Arweave wallet key for deployment"
+    echo "  ANT_PROCESS      ANT process ID for ArNS deployment"
+    echo ""
+    echo "Examples:"
+    echo "  $0                    # Generate documentation normally"
+    echo "  $0 -v                 # Generate with verbose output"
+    echo "  $0 --dry-run          # Test deployment without deploying"
+    echo "  $0 --dry-run -v       # Dry run with verbose output"
+    echo ""
+    exit 0
+fi
+
+echo -e "${GREEN}HyperBEAM Literate Erlang Documentation Generator (JavaScript)${NC}"
+if [ "$DRY_RUN" = true ]; then
+    echo -e "${YELLOW}[DRY RUN MODE] - No actual deployment will occur${NC}"
+fi
+echo "========================================================"
+
+# Check for Node.js
+if ! command -v node &> /dev/null; then
+    echo -e "${RED}Error: Node.js is required but not installed.${NC}"
+    echo "Please install Node.js (version 14 or later) to use this parser."
+    echo ""
+    echo "On macOS with Homebrew: brew install node"
+    echo "On Ubuntu: curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt-get install -y nodejs"
+    exit 1
+fi
+
+# Check Node.js version (need ES modules support)
+NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1)
+if [ "$NODE_VERSION" -lt 14 ]; then
+    echo -e "${RED}Error: Node.js version 14 or later is required for ES modules support.${NC}"
+    echo "Current version: $(node --version)"
+    exit 1
+fi
+
+# Verify source directory exists
+if [ ! -d "$SRC_DIR" ]; then
+    echo -e "${RED}Error: Source directory not found: $SRC_DIR${NC}"
+    exit 1
+fi
+
+# Verify parser script exists
+if [ ! -f "$PARSER_SCRIPT" ]; then
+    echo -e "${RED}Error: Parser script not found: $PARSER_SCRIPT${NC}"
+    exit 1
+fi
+
+# Make parser executable
+chmod +x "$PARSER_SCRIPT"
+
+# Count Erlang files
+ERL_COUNT=$(find "$SRC_DIR" -name "*.erl" -type f | wc -l)
+if [ "$ERL_COUNT" -eq 0 ]; then
+    echo -e "${YELLOW}Warning: No .erl files found in $SRC_DIR${NC}"
+    exit 0
+fi
+
+echo "Source directory: $SRC_DIR"
+echo "Output directory: $OUTPUT_DIR"
+echo "Found $ERL_COUNT Erlang files"
+echo ""
+
+# Create output directory if it doesn't exist
+mkdir -p "$OUTPUT_DIR"
+
+# Run the JavaScript parser
+echo -e "${GREEN}Generating literate documentation...${NC}"
+
+# Set environment variables and run parser
+export SRC_DIR="$SRC_DIR"
+export OUTPUT_DIR="$OUTPUT_DIR"
+
+if [ "$VERBOSE" = true ]; then
+    node "$PARSER_SCRIPT" --verbose
+else
+    node "$PARSER_SCRIPT"
+fi
+
+PARSER_EXIT_CODE=$?
+
+if [ $PARSER_EXIT_CODE -eq 0 ]; then
+    echo ""
+    echo -e "${GREEN}✓ Literate documentation generated successfully${NC}"
+
+    # List generated files
+    if [ "$VERBOSE" = true ]; then
+        echo ""
+        echo "Generated files:"
+        ls -la "$OUTPUT_DIR"/*.md 2>/dev/null | while read -r line; do
+            echo "  $line"
+        done
+    fi
+
+    # Copy to mdBook if it exists
+    if [ -d "$ROOT_DIR/docs/book/src" ]; then
+        if [ "$DRY_RUN" = true ]; then
+            echo -e "${YELLOW}[DRY RUN] Would copy documentation to mdBook...${NC}"
+            echo -e "${YELLOW}[DRY RUN] ✓ Documentation would be copied to mdBook${NC}"
+        else
+            echo -e "${GREEN}Copying documentation to mdBook...${NC}"
+            cp "$OUTPUT_DIR"/*.md "$ROOT_DIR/docs/book/src/" 2>/dev/null
+            if [ $? -eq 0 ]; then
+                echo -e "${GREEN}✓ Documentation copied to mdBook${NC}"
+            else
+                echo -e "${YELLOW}Warning: Could not copy to mdBook (no files generated?)${NC}"
+            fi
+        fi
+    fi
+
+    # Build mdBook if available
+    if command -v mdbook &> /dev/null && [ -f "$ROOT_DIR/docs/book/book.toml" ]; then
+        if [ "$DRY_RUN" = true ]; then
+            echo -e "${YELLOW}[DRY RUN] Would build mdBook...${NC}"
+            echo -e "${YELLOW}[DRY RUN] ✓ mdBook would be built successfully${NC}"
+            echo -e "${YELLOW}[DRY RUN] Would be viewable at: file://$ROOT_DIR/docs/book/dist/index.html${NC}"
+        else
+            echo -e "${GREEN}Building mdBook...${NC}"
+            cd "$ROOT_DIR/docs/book"
+            mdbook build
+            if [ $? -eq 0 ]; then
+                echo -e "${GREEN}✓ mdBook built successfully${NC}"
+                echo "View at: file://$ROOT_DIR/docs/book/dist/index.html"
+            else
+                echo -e "${YELLOW}Warning: mdBook build failed${NC}"
+            fi
+            cd "$ROOT_DIR"
+        fi
+    fi
+
+    # Run deployment dry run if requested
+    if [ "$DRY_RUN" = true ]; then
+        echo ""
+        echo -e "${YELLOW}Running deployment dry run...${NC}"
+        "$SCRIPT_DIR/deploy-dry-run.sh"
+    fi
+
+    echo ""
+    if [ "$DRY_RUN" = true ]; then
+        echo -e "${YELLOW}Documentation generation and deployment dry run complete!${NC}"
+    else
+        echo -e "${GREEN}Documentation generation complete!${NC}"
+    fi
+    echo "Output directory: $OUTPUT_DIR"
+
+else
+    echo ""
+    echo -e "${RED}✗ Documentation generation failed (exit code: $PARSER_EXIT_CODE)${NC}"
+    exit $PARSER_EXIT_CODE
+fi
\ No newline at end of file
diff --git a/docs/serve-book.sh b/docs/serve-book.sh
new file mode 100755
index 000000000..ef7ddaea6
--- /dev/null
+++ b/docs/serve-book.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# HyperBEAM Documentation Build and Serve Script
+# This script generates literate documentation and serves it locally
+
+set -e
+
+# Colors for output
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+echo -e "${BLUE}🚀 Building and serving HyperBEAM documentation...${NC}"
+
+# Generate literate documentation
+echo -e "${GREEN}📚 Generating literate documentation...${NC}"
+./generate-literate-docs.sh
+
+# Build and serve mdBook
+echo -e "${GREEN}📖 Building mdBook...${NC}"
+cd book
+mdbook build
+
+echo -e "${GREEN}🌐 Starting development server...${NC}"
+echo -e "${BLUE}📖 Documentation will be available at: http://localhost:3033${NC}"
+echo -e "${BLUE}🔄 Auto-reload enabled - changes will be reflected automatically${NC}"
+echo -e "${BLUE}âšī¸  Press Ctrl+C to stop the server${NC}"
+
+mdbook serve --port 3033
\ No newline at end of file