Site Module
The Site module provides a structured way to define website configurations, navigation menus, pages, and sections using HeroScript. It's designed to work with static site generators like Docusaurus.
Purpose
The Site module allows you to:
- Define website structure and configuration in a declarative way using HeroScript
- Organize pages into sections/categories
- Configure navigation menus and footers
- Manage page metadata (title, description, slug, etc.)
- Support multiple content collections
- Define build and publish destinations
Quick Start
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.develop.gittools
import incubaid.herolib.web.site
import incubaid.herolib.core.playcmds
// Clone or use existing repository with HeroScript files
mysitepath := gittools.path(
git_url: 'https://git.ourworld.tf/tfgrid/docs_tfgrid4/src/branch/main/ebooks/tech'
git_pull: true
)!
// Process all HeroScript files in the path
playcmds.run(heroscript_path: mysitepath.path)!
// Get the configured site
mut mysite := site.get(name: 'tfgrid_tech')!
println(mysite)
HeroScript Syntax
Basic Configuration
!!site.config
name: "my_site"
title: "My Documentation Site"
description: "Comprehensive documentation"
tagline: "Your awesome documentation"
favicon: "img/favicon.png"
image: "img/site-image.png"
copyright: "© 2024 My Organization"
url: "https://docs.example.com"
base_url: "/"
Navigation Menu
!!site.navbar
title: "My Site"
logo_alt: "Site Logo"
logo_src: "img/logo.svg"
logo_src_dark: "img/logo-dark.svg"
!!site.navbar_item
label: "Documentation"
to: "docs/intro"
position: "left"
!!site.navbar_item
label: "GitHub"
href: "https://github.com/myorg/myrepo"
position: "right"
Footer Configuration
!!site.footer
style: "dark"
!!site.footer_item
title: "Docs"
label: "Introduction"
to: "intro"
!!site.footer_item
title: "Docs"
label: "Getting Started"
href: "https://docs.example.com/getting-started"
!!site.footer_item
title: "Community"
label: "Discord"
href: "https://discord.gg/example"
Page Organization
Example 1: Simple Pages Without Categories
When you don't need categories, pages are added sequentially. The collection only needs to be specified once, then it's reused for subsequent pages.
!!site.page src: "mycelium_tech:introduction"
description: "Introduction to ThreeFold Technology"
slug: "/"
!!site.page src: "vision"
description: "Our Vision for the Future Internet"
!!site.page src: "what"
description: "What ThreeFold is Building"
!!site.page src: "presentation"
description: "ThreeFold Technology Presentation"
!!site.page src: "status"
description: "Current Development Status"
Key Points:
- First page specifies collection as
tech:introduction(collection:page_name format) - Subsequent pages only need the page name (e.g.,
vision) - thetechcollection is reused - If
titleis not specified, it will be extracted from the markdown file itself - Pages are ordered by their appearance in the HeroScript file
slugcan be used to customize the URL path (e.g.,"/"for homepage)
Example 2: Pages with Categories
Categories (sections) help organize pages into logical groups with their own navigation structure.
!!site.page_category
name: "first_principle_thinking"
label: "First Principle Thinking"
!!site.page src: "first_principle_thinking:hardware_badly_used"
description: "Hardware is not used properly, why it is important to understand hardware"
!!site.page src: "internet_risk"
description: "Internet risk, how to mitigate it, and why it is important"
!!site.page src: "onion_analogy"
description: "Compare onion with a computer, layers of abstraction"
Key Points:
!!site.page_categorycreates a new section/categorynameis the internal identifier (snake_case)labelis the display name (automatically derived fromnameif not specified)- Category name is converted to title case:
first_principle_thinking→ "First Principle Thinking" - Once a category is defined, all subsequent pages belong to it until a new category is declared
- Collection persistence works the same: specify once (e.g.,
first_principle_thinking:hardware_badly_used), then reuse
Example 3: Advanced Page Configuration
!!site.page_category
name: "components"
label: "System Components"
position: 100
!!site.page src: "mycelium_tech:mycelium"
title: "Mycelium Network"
description: "Peer-to-peer overlay network"
slug: "mycelium-network"
position: 1
draft: false
hide_title: false
!!site.page src: "fungistor"
title: "Fungistor Storage"
description: "Distributed storage system"
position: 2
Available Page Parameters:
src: Source reference ascollection:page_name(required for first page in collection)title: Page title (optional, extracted from markdown if not provided)description: Page description for metadataslug: Custom URL slugposition: Manual ordering (auto-incremented if not specified)draft: Mark page as draft (default: false)hide_title: Hide the page title in rendering (default: false)path: Custom path for the page (defaults to category name)category: Override the current category for this page
File Organization
HeroScript files should be organized with numeric prefixes to control execution order:
docs/
├── 0_config.heroscript # Site configuration
├── 1_menu.heroscript # Navigation and footer
├── 2_intro_pages.heroscript # Introduction pages
├── 3_tech_pages.heroscript # Technical documentation
└── 4_api_pages.heroscript # API reference
Important: Files are processed in alphabetical order, so use numeric prefixes (0_, 1_, 2_, etc.) to ensure correct execution sequence.
Import External Content
!!site.import
url: "https://github.com/example/external-docs"
dest: "external"
replace: "PROJECT_NAME:My Project,VERSION:1.0.0"
visible: true
Publish Destinations
!!site.publish
path: "/var/www/html/docs"
ssh_name: "production_server"
!!site.publish_dev
path: "/tmp/docs-preview"
Factory Methods
Create or Get a Site
import incubaid.herolib.web.site
// Create a new site
mut mysite := site.new(name: 'my_docs')!
// Get an existing site
mut mysite := site.get(name: 'my_docs')!
// Get default site
mut mysite := site.default()!
// Check if site exists
if site.exists(name: 'my_docs') {
println('Site exists')
}
// List all sites
sites := site.list()
println(sites)
Using with PlayBook
import incubaid.herolib.core.playbook
import incubaid.herolib.web.site
// Create playbook from path
mut plbook := playbook.new(path: '/path/to/heroscripts')!
// Process site configuration
site.play(mut plbook)!
// Access the configured site
mut mysite := site.get(name: 'my_site')!
Data Structures
Site
pub struct Site {
pub mut:
pages []Page
sections []Section
siteconfig SiteConfig
}
Page
pub struct Page {
pub mut:
name string // Page identifier
title string // Display title
description string // Page description
draft bool // Draft status
position int // Sort order
hide_title bool // Hide title in rendering
src string // Source as collection:page_name
path string // URL path (without page name)
section_name string // Category/section name
title_nr int // Title numbering level
slug string // Custom URL slug
}
Section
pub struct Section {
pub mut:
name string // Internal identifier
position int // Sort order
path string // URL path
label string // Display name
}
Best Practices
- File Naming: Use numeric prefixes (0_, 1_, 2_) to control execution order
- Collection Reuse: Specify collection once, then reuse for subsequent pages
- Category Organization: Group related pages under categories for better navigation
- Title Extraction: Let titles be extracted from markdown files when possible
- Position Management: Use automatic positioning unless you need specific ordering
- Description: Always provide descriptions for better SEO and navigation
- Draft Status: Use
draft: truefor work-in-progress pages
Complete Example
See examples/web/site/site_example.vsh for a complete working example.
For a real-world example, check: https://git.ourworld.tf/tfgrid/docs_tfgrid4/src/branch/main/ebooks/tech