Skip to content

web.site #

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: '/'
!!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'
!!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) - the tech collection is reused
  • If title is not specified, it will be extracted from the markdown file itself
  • Pages are ordered by their appearance in the HeroScript file
  • slug can 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_category creates a new section/category
  • name is the internal identifier (snake_case)
  • label is the display name (automatically derived from name if 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 as collection:page_name (required for first page in collection)
  • title: Page title (optional, extracted from markdown if not provided)
  • description: Page description for metadata
  • slug: Custom URL slug
  • position: 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

  1. File Naming: Use numeric prefixes (0_, 1_, 2_) to control execution order
  2. Collection Reuse: Specify collection once, then reuse for subsequent pages
  3. Category Organization: Group related pages under categories for better navigation
  4. Title Extraction: Let titles be extracted from markdown files when possible
  5. Position Management: Use automatic positioning unless you need specific ordering
  6. Description: Always provide descriptions for better SEO and navigation
  7. Draft Status: Use draft: true for 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

fn default #

fn default() !&Site

fn exists #

fn exists(args FactoryArgs) bool

fn get #

fn get(args FactoryArgs) !&Site

fn list #

fn list() []string

list returns all site names that have been created

fn new #

fn new(args FactoryArgs) !&Site

fn play #

fn play(mut plbook PlayBook) !

struct AnnouncementBar #

struct AnnouncementBar {
pub mut:
	id               string @[json: 'id']
	content          string @[json: 'content']
	background_color string @[json: 'backgroundColor']
	text_color       string @[json: 'textColor']
	is_closeable     bool   @[json: 'isCloseable']
}

Announcement bar config structure

struct BuildDest #

struct BuildDest {
pub mut:
	path     string
	ssh_name string
}

struct FactoryArgs #

@[params]
struct FactoryArgs {
pub mut:
	name string = 'default'
}

struct FooterItem #

struct FooterItem {
pub mut:
	label string
	to    string
	href  string
}

Footer config structures

struct ImportItem #

struct ImportItem {
pub mut:
	name    string // will normally be empty
	url     string // http git url can be to specific path
	path    string
	dest    string            // location in the docs folder of the place where we will build the documentation site e.g. docusaurus
	replace map[string]string // will replace ${NAME} in the imported content
	visible bool = true
}

is to import one docusaurus site into another, can be used to e.g. import static parts from one location into the build one we are building

struct Page #

struct Page {
pub mut:
	name         string
	title        string
	description  string
	draft        bool
	position     int
	hide_title   bool
	src          string @[required] // always in format collection:page_name, can use the default collection if no : specified
	path         string @[required] // is without the page name, so just the path to the folder where the page is in
	section_name string
	title_nr     int
	slug         string
}

struct Section #

struct Section {
pub mut:
	name        string
	position    int
	path        string
	label       string
	description string
}

struct Site #

@[heap]
struct Site {
pub mut:
	pages      []Page
	sections   []Section
	siteconfig SiteConfig
}

struct SiteConfig #

@[heap]
struct SiteConfig {
pub mut:
	name        string
	title       string = 'My Documentation Site' // General site title
	description string // General site description, can be used for meta if meta_description not set
	tagline     string
	favicon     string = 'img/favicon.png'
	image       string = 'img/tf_graph.png' // General site image, can be used for meta if meta_image not set
	copyright   string = 'someone'
	footer      Footer
	menu        Menu
	imports     []ImportItem

	// New fields for Docusaurus compatibility
	url      string // The main URL of the site (from !!site.config url:)
	base_url string // The base URL for Docusaurus (from !!site.config base_url:)
	url_home string // The home page path relative to base_url (from !!site.config url_home:)

	meta_title string // Specific title for SEO metadata (from !!site.config_meta title:)
	meta_image string // Specific image for SEO metadata (og:image) (from !!site.config_meta image:)

	build_dest     []BuildDest // Production build destinations (from !!site.build_dest)
	build_dest_dev []BuildDest // Development build destinations (from !!site.build_dest_dev)

	announcement AnnouncementBar // Announcement bar configuration (from !!site.announcement)
}