This commit is contained in:
2025-12-02 04:34:43 +01:00
parent ad65392806
commit 75b548b439
5 changed files with 334 additions and 156 deletions

View File

@@ -22,8 +22,7 @@ pub fn new(args FactoryArgs) !&Site {
} }
mut site := Site{ mut site := Site{
nav: SideBar{} config: SiteConfig{
siteconfig: SiteConfig{
name: name name: name
} }
} }
@@ -45,6 +44,10 @@ pub fn exists(args FactoryArgs) bool {
return name in sites_global return name in sites_global
} }
pub fn reset() {
sites_global.clear()
}
pub fn default() !&Site { pub fn default() !&Site {
if sites_global.len == 0 { if sites_global.len == 0 {
return new(name: 'default')! return new(name: 'default')!

View File

@@ -23,7 +23,7 @@ pub fn play(mut plbook PlayBook) ! {
name := p.get_default('name', 'default')! name := p.get_default('name', 'default')!
mut website := new(name: name)! mut website := new(name: name)!
mut config := &website.siteconfig mut config := &website.config
// Load core configuration // Load core configuration
config.name = texttools.name_fix(name) config.name = texttools.name_fix(name)
@@ -60,7 +60,7 @@ pub fn play(mut plbook PlayBook) ! {
// STEP 3: Configure content imports // STEP 3: Configure content imports
// ============================================================ // ============================================================
console.print_item('Step 3: Configuring content imports') console.print_item('Step 3: Configuring content imports')
play_imports(mut plbook, mut config)! play_imports(mut plbook, mut website)!
// ============================================================ // ============================================================
// STEP 4: Configure navigation menu // STEP 4: Configure navigation menu
@@ -78,13 +78,13 @@ pub fn play(mut plbook PlayBook) ! {
// STEP 6: Configure announcement bar (optional) // STEP 6: Configure announcement bar (optional)
// ============================================================ // ============================================================
console.print_item('Step 6: Configuring announcement bar (if present)') console.print_item('Step 6: Configuring announcement bar (if present)')
play_announcement(mut plbook, mut config)! play_announcement(mut plbook, mut website)!
// ============================================================ // ============================================================
// STEP 7: Configure publish destinations // STEP 7: Configure publish destinations
// ============================================================ // ============================================================
console.print_item('Step 7: Configuring publish destinations') console.print_item('Step 7: Configuring publish destinations')
play_publishing(mut plbook, mut config)! play_publishing(mut plbook, mut website)!
// ============================================================ // ============================================================
// STEP 8: Build pages and navigation structure // STEP 8: Build pages and navigation structure

View File

@@ -9,7 +9,7 @@ import incubaid.herolib.ui.console
// ============================================================ // ============================================================
// PUBLISHING: Configure build and publish destinations // PUBLISHING: Configure build and publish destinations
// ============================================================ // ============================================================
fn play_publishing(mut plbook PlayBook, mut config SiteConfig) ! { fn play_publishing(mut plbook PlayBook, mut website Site) ! {
// Production publish destinations // Production publish destinations
mut build_dest_actions := plbook.find(filter: 'site.publish')! mut build_dest_actions := plbook.find(filter: 'site.publish')!
for mut action in build_dest_actions { for mut action in build_dest_actions {
@@ -23,7 +23,7 @@ fn play_publishing(mut plbook PlayBook, mut config SiteConfig) ! {
path: path path: path
ssh_name: p.get_default('ssh_name', '')! ssh_name: p.get_default('ssh_name', '')!
} }
config.build_dest << dest website.build_dest << dest
action.done = true action.done = true
} }
@@ -40,7 +40,7 @@ fn play_publishing(mut plbook PlayBook, mut config SiteConfig) ! {
path: path path: path
ssh_name: p.get_default('ssh_name', '')! ssh_name: p.get_default('ssh_name', '')!
} }
config.build_dest_dev << dest website.build_dest_dev << dest
action.done = true action.done = true
} }
} }

View File

@@ -2,7 +2,6 @@
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. 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.
## Quick Start ## Quick Start
### Minimal HeroScript Example ### Minimal HeroScript Example
@@ -13,33 +12,33 @@ The Site module provides a structured way to define website configurations, navi
title: "My Documentation" title: "My Documentation"
!!site.page src: "docs:introduction" !!site.page src: "docs:introduction"
label: "Getting Started"
title: "Getting Started" title: "Getting Started"
!!site.page src: "setup" !!site.page src: "setup"
label: "Installation"
title: "Installation" title: "Installation"
``` ```
### Processing with V Code ### Processing with V Code
```v ```v
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.core.playbook import incubaid.herolib.core.playbook
import incubaid.herolib.web.site import incubaid.herolib.web.doctree.meta as site_module
import incubaid.herolib.ui.console import incubaid.herolib.ui.console
// Process HeroScript file // Process HeroScript file
mut plbook := playbook.new(path: './site_config.heroscript')! mut plbook := playbook.new(path: './site_config.heroscript')!
// Execute site configuration // Execute site configuration
site.play(mut plbook)! site_module.play(mut plbook)!
// Access the configured site // Access the configured site
mut mysite := site.get(name: 'my_docs')! mut mysite := site_module.get(name: 'my_docs')!
// Print available pages // Print available pages
for page_id, page in mysite.pages { for page in mysite.pages {
console.print_item('Page: ${page_id} - "${page.title}"') console.print_item('Page: "${page.src}" - "${page.title}"')
} }
println('Site has ${mysite.pages.len} pages') println('Site has ${mysite.pages.len} pages')
@@ -55,31 +54,38 @@ Factory functions to create and retrieve site instances:
```v ```v
// Create a new site // Create a new site
mut mysite := site.new(name: 'my_docs')! mut mysite := site_module.new(name: 'my_docs')!
// Get existing site // Get existing site
mut mysite := site.get(name: 'my_docs')! mut mysite := site_module.get(name: 'my_docs')!
// Check if site exists // Check if site exists
if site.exists(name: 'my_docs') { if site_module.exists(name: 'my_docs') {
println('Site exists') println('Site exists')
} }
// Get all site names // Get all site names
site_names := site.list() // Returns []string site_names := site_module.list() // Returns []string
// Get default site (creates if needed) // Get default site (creates if needed)
mut default := site.default()! mut default := site_module.default()!
``` ```
### Site Object Structure ### Site Object Structure
```v ```v
@[heap]
pub struct Site { pub struct Site {
pub mut: pub mut:
pages map[string]Page // key: "collection:page_name" doctree_path string // path to the export of the doctree site
nav NavConfig // Navigation sidebar config SiteConfig // Full site configuration
siteconfig SiteConfig // Full configuration pages []Page // Array of pages
links []Link // Array of links
categories []Category // Array of categories
announcements []Announcement // Array of announcements (can be multiple)
imports []ImportItem // Array of imports
build_dest []BuildDest // Production build destinations
build_dest_dev []BuildDest // Development build destinations
} }
``` ```
@@ -87,44 +93,60 @@ pub mut:
```v ```v
// Access all pages // Access all pages
pages := mysite.pages // map[string]Page pages := mysite.pages // []Page
// Get specific page // Access specific page by index
page := mysite.pages['docs:introduction'] page := mysite.pages[0]
// Page structure // Page structure
pub struct Page { pub struct Page {
pub mut: pub mut:
id string // "collection:page_name" src string // "collection:page_name" format (unique identifier)
title string // Display title label string // Display label in navigation
title string // Display title on page (extracted from markdown if empty)
description string // SEO metadata description string // SEO metadata
draft bool // Hidden if true draft bool // Hide from navigation if true
hide_title bool // Don't show title in rendering hide_title bool // Don't show title on page
src string // Source reference hide bool // Hide page completely
category_id int // Optional category ID (0 = root level)
} }
``` ```
### Navigation Structure ### Categories and Navigation
```v ```v
// Access sidebar navigation // Access all categories
sidebar := mysite.nav.my_sidebar // []NavItem categories := mysite.categories // []Category
// Category structure
pub struct Category {
pub mut:
path string // e.g., "Getting Started" or "Operations/Daily"
collapsible bool = true
collapsed bool
}
// Generate sidebar navigation
sidebar := mysite.sidebar() // Returns SideBar
// Sidebar structure
pub struct SideBar {
pub mut:
my_sidebar []NavItem
}
// NavItem is a sum type (can be one of three types):
pub type NavItem = NavDoc | NavCat | NavLink pub type NavItem = NavDoc | NavCat | NavLink
// Navigation items:
pub struct NavDoc { pub struct NavDoc {
pub: pub:
id string // page id path string // path is $collection/$name without .md
label string // display name label string
} }
pub struct NavCat { pub struct NavCat {
pub mut: pub mut:
label string label string
collapsible bool collapsible bool = true
collapsed bool collapsed bool
items []NavItem // nested NavDoc/NavCat/NavLink items []NavItem // nested NavDoc/NavCat/NavLink
} }
@@ -137,10 +159,11 @@ pub:
} }
// Example: iterate navigation // Example: iterate navigation
for item in mysite.nav.my_sidebar { sidebar := mysite.sidebar()
for item in sidebar.my_sidebar {
match item { match item {
NavDoc { NavDoc {
println('Page: ${item.label} (${item.id})') println('Page: ${item.label} (${item.path})')
} }
NavCat { NavCat {
println('Category: ${item.label} (${item.items.len} items)') println('Category: ${item.label} (${item.items.len} items)')
@@ -150,11 +173,15 @@ for item in mysite.nav.my_sidebar {
} }
} }
} }
// Print formatted sidebar
println(mysite.sidebar_str())
``` ```
### Site Configuration ### Site Configuration
```v ```v
@[heap]
pub struct SiteConfig { pub struct SiteConfig {
pub mut: pub mut:
// Core // Core
@@ -175,15 +202,14 @@ pub mut:
meta_title string // SEO title override meta_title string // SEO title override
meta_image string // OG image override meta_image string // OG image override
// Navigation & Footer
footer Footer
menu Menu
// Publishing // Publishing
build_dest []BuildDest // Production destinations build_dest []BuildDest // Production destinations
build_dest_dev []BuildDest // Development destinations build_dest_dev []BuildDest // Development destinations
// Navigation & Footer
footer Footer
menu Menu
announcement AnnouncementBar
// Imports // Imports
imports []ImportItem imports []ImportItem
} }
@@ -193,6 +219,59 @@ pub mut:
path string path string
ssh_name string ssh_name string
} }
pub struct Menu {
pub mut:
title string
items []MenuItem
logo_alt string
logo_src string
logo_src_dark string
}
pub struct MenuItem {
pub mut:
href string
to string
label string
position string // "left" or "right"
}
pub struct Footer {
pub mut:
style string // e.g., "dark" or "light"
links []FooterLink
}
pub struct FooterLink {
pub mut:
title string
items []FooterItem
}
pub struct FooterItem {
pub mut:
label string
to string
href string
}
pub struct Announcement {
pub mut:
content string
background_color string
text_color string
is_closeable bool
}
pub struct ImportItem {
pub mut:
url string // http or git url
path string
dest string // location in docs folder
replace map[string]string
visible bool = true
}
``` ```
--- ---
@@ -200,20 +279,32 @@ pub mut:
## Core Concepts ## Core Concepts
### Site ### Site
A website configuration that contains pages, navigation structure, and metadata. A website configuration that contains pages, navigation structure, and metadata. Each site is registered globally and can be retrieved by name.
### Page ### Page
A single page with: A single documentation page with:
- **ID**: `collection:page_name` format - **src**: `collection:page_name` format (unique identifier)
- **Title**: Display name (optional - extracted from markdown if not provided) - **label**: Display name in sidebar
- **Description**: SEO metadata - **title**: Display name on page (extracted from markdown if empty)
- **Draft**: Hidden from navigation if true - **description**: SEO metadata
- **draft**: Hidden from navigation if true
- **category_id**: Links page to a category (0 = root level)
### Category (Section) ### Category (Section)
Groups related pages together in the navigation sidebar. Automatically collapsed/expandable. Groups related pages together in the navigation sidebar. Categories can be nested and are automatically collapsed/expandable.
```heroscript
!!site.page_category
path: "Getting Started"
collapsible: true
collapsed: false
!!site.page src: "tech:intro"
category_id: 1 // Links to the category above
```
### Collection ### Collection
A logical group of pages. Pages reuse the collection once specified. A logical group of pages. Pages reuse the collection once specified:
```heroscript ```heroscript
!!site.page src: "tech:intro" # Specifies collection "tech" !!site.page src: "tech:intro" # Specifies collection "tech"
@@ -326,6 +417,8 @@ Overrides specific metadata for SEO without changing core config.
### 5. Announcement Bar (Optional) ### 5. Announcement Bar (Optional)
Multiple announcements are supported and stored in an array:
```heroscript ```heroscript
!!site.announcement !!site.announcement
content: "🎉 Version 2.0 is now available!" content: "🎉 Version 2.0 is now available!"
@@ -334,61 +427,71 @@ Overrides specific metadata for SEO without changing core config.
is_closeable: true is_closeable: true
``` ```
**Note:** Each `!!site.announcement` block adds to the `announcements[]` array. Only the first is typically displayed, but all are stored.
### 6. Pages and Categories ### 6. Pages and Categories
#### Simple: Pages Without Categories #### Simple: Pages Without Categories
```heroscript ```heroscript
!!site.page src: "guides:introduction" !!site.page src: "guides:introduction"
label: "Getting Started"
title: "Getting Started" title: "Getting Started"
description: "Introduction to the platform" description: "Introduction to the platform"
!!site.page src: "installation" !!site.page src: "installation"
label: "Installation"
title: "Installation" title: "Installation"
!!site.page src: "configuration"
title: "Configuration"
``` ```
#### Advanced: Pages With Categories #### Advanced: Pages With Categories
```heroscript ```heroscript
!!site.page_category !!site.page_category
name: "basics" path: "Getting Started"
label: "Getting Started" collapsible: true
collapsed: false
!!site.page src: "guides:introduction" !!site.page src: "guides:introduction"
label: "Introduction"
title: "Introduction" title: "Introduction"
description: "Learn the basics" description: "Learn the basics"
!!site.page src: "installation" !!site.page src: "installation"
label: "Installation"
title: "Installation" title: "Installation"
!!site.page src: "configuration" !!site.page src: "configuration"
label: "Configuration"
title: "Configuration" title: "Configuration"
!!site.page_category !!site.page_category
name: "advanced" path: "Advanced Topics"
label: "Advanced Topics" collapsible: true
collapsed: false
!!site.page src: "advanced:performance" !!site.page src: "advanced:performance"
label: "Performance Tuning"
title: "Performance Tuning" title: "Performance Tuning"
!!site.page src: "scaling" !!site.page src: "scaling"
label: "Scaling Guide"
title: "Scaling Guide" title: "Scaling Guide"
``` ```
**Page Parameters:** **Page Parameters:**
- `src` - Source as `collection:page` (first page) or just `page_name` (reuse collection) - `src` - Source as `collection:page_name` (first page) or just `page_name` (reuse collection)
- `label` - Display label in sidebar (required)
- `title` - Page title (optional, extracted from markdown if not provided) - `title` - Page title (optional, extracted from markdown if not provided)
- `description` - Page description - `description` - Page description
- `draft` - Hide from navigation (default: false) - `draft` - Hide from navigation (default: false)
- `hide_title` - Don't show title in page (default: false) - `hide_title` - Don't show title in page (default: false)
- `hide` - Hide page completely (default: false)
**Category Parameters:** **Category Parameters:**
- `name` - Category identifier (required) - `path` - Category path/label (required)
- `label` - Display label (auto-generated from name if omitted) - `collapsible` - Allow collapsing (default: true)
- `position` - Sort order (auto-incremented if omitted) - `collapsed` - Initially collapsed (default: false)
### 7. Content Imports ### 7. Content Imports
@@ -424,33 +527,42 @@ Overrides specific metadata for SEO without changing core config.
title: "Technical Documentation" title: "Technical Documentation"
!!site.page_category !!site.page_category
name: "getting_started" path: "Getting Started"
label: "Getting Started" collapsible: true
collapsed: false
!!site.page src: "docs:intro" !!site.page src: "docs:intro"
label: "Introduction"
title: "Introduction" title: "Introduction"
!!site.page src: "installation" !!site.page src: "installation"
label: "Installation"
title: "Installation" title: "Installation"
!!site.page_category !!site.page_category
name: "concepts" path: "Core Concepts"
label: "Core Concepts" collapsible: true
collapsed: false
!!site.page src: "concepts:architecture" !!site.page src: "concepts:architecture"
label: "Architecture"
title: "Architecture" title: "Architecture"
!!site.page src: "components" !!site.page src: "components"
label: "Components"
title: "Components" title: "Components"
!!site.page_category !!site.page_category
name: "api" path: "API Reference"
label: "API Reference" collapsible: true
collapsed: false
!!site.page src: "api:rest" !!site.page src: "api:rest"
label: "REST API"
title: "REST API" title: "REST API"
!!site.page src: "graphql" !!site.page src: "graphql"
label: "GraphQL"
title: "GraphQL" title: "GraphQL"
``` ```
@@ -462,12 +574,15 @@ Overrides specific metadata for SEO without changing core config.
title: "Knowledge Base" title: "Knowledge Base"
!!site.page src: "articles:first_post" !!site.page src: "articles:first_post"
label: "Welcome to Our Blog"
title: "Welcome to Our Blog" title: "Welcome to Our Blog"
!!site.page src: "second_post" !!site.page src: "second_post"
label: "Understanding the Basics"
title: "Understanding the Basics" title: "Understanding the Basics"
!!site.page src: "third_post" !!site.page src: "third_post"
label: "Advanced Techniques"
title: "Advanced Techniques" title: "Advanced Techniques"
``` ```
@@ -484,20 +599,25 @@ Overrides specific metadata for SEO without changing core config.
visible: true visible: true
!!site.page_category !!site.page_category
name: "product" path: "Product Guide"
label: "Product Guide" collapsible: true
collapsed: false
!!site.page src: "docs:overview" !!site.page src: "docs:overview"
label: "Overview"
title: "Overview" title: "Overview"
!!site.page src: "features" !!site.page src: "features"
label: "Features"
title: "Features" title: "Features"
!!site.page_category !!site.page_category
name: "resources" path: "Shared Resources"
label: "Shared Resources" collapsible: true
collapsed: false
!!site.page src: "shared:common" !!site.page src: "shared:common"
label: "Common Patterns"
title: "Common Patterns" title: "Common Patterns"
``` ```
@@ -554,4 +674,3 @@ hero docs -d -p /path/to/my_ebook
# Build for production # Build for production
hero docs -p /path/to/my_ebook hero docs -p /path/to/my_ebook
``` ```

View File

@@ -2,7 +2,6 @@ module meta
import incubaid.herolib.core.playbook import incubaid.herolib.core.playbook
import incubaid.herolib.ui.console import incubaid.herolib.ui.console
import os
// Big comprehensive HeroScript for testing // Big comprehensive HeroScript for testing
const test_heroscript = ' const test_heroscript = '
@@ -94,74 +93,86 @@ const test_heroscript = '
is_closeable: true is_closeable: true
!!site.page_category !!site.page_category
name: "getting_started" path: "Getting Started"
label: "Getting Started" collapsible: true
position: 10 collapsed: false
!!site.page src: "guides:introduction" !!site.page src: "guides:introduction"
label: "Introduction to Test Docs"
title: "Introduction to Test Docs" title: "Introduction to Test Docs"
description: "Learn what this project is about" description: "Learn what this project is about"
!!site.page src: "installation" !!site.page src: "installation"
label: "Installation Guide"
title: "Installation Guide" title: "Installation Guide"
description: "How to install and setup" description: "How to install and setup"
!!site.page src: "quick_start" !!site.page src: "quick_start"
label: "Quick Start"
title: "Quick Start" title: "Quick Start"
description: "5 minute quick start guide" description: "5 minute quick start guide"
!!site.page_category !!site.page_category
name: "concepts" path: "Core Concepts"
label: "Core Concepts" collapsible: true
position: 20 collapsed: false
!!site.page src: "concepts:architecture" !!site.page src: "concepts:architecture"
label: "Architecture Overview"
title: "Architecture Overview" title: "Architecture Overview"
description: "Understanding the system architecture" description: "Understanding the system architecture"
!!site.page src: "components" !!site.page src: "components"
label: "Key Components"
title: "Key Components" title: "Key Components"
description: "Learn about the main components" description: "Learn about the main components"
!!site.page src: "workflow" !!site.page src: "workflow"
label: "Typical Workflow"
title: "Typical Workflow" title: "Typical Workflow"
description: "How to use the system" description: "How to use the system"
!!site.page_category !!site.page_category
name: "api" path: "API Reference"
label: "API Reference" collapsible: true
position: 30 collapsed: false
!!site.page src: "api:rest" !!site.page src: "api:rest"
label: "REST API"
title: "REST API" title: "REST API"
description: "Complete REST API reference" description: "Complete REST API reference"
!!site.page src: "graphql" !!site.page src: "graphql"
label: "GraphQL API"
title: "GraphQL API" title: "GraphQL API"
description: "GraphQL API documentation" description: "GraphQL API documentation"
!!site.page src: "webhooks" !!site.page src: "webhooks"
label: "Webhooks"
title: "Webhooks" title: "Webhooks"
description: "Webhook configuration and examples" description: "Webhook configuration and examples"
!!site.page_category !!site.page_category
name: "advanced" path: "Advanced Topics"
label: "Advanced Topics" collapsible: true
position: 40 collapsed: false
!!site.page src: "advanced:performance" !!site.page src: "advanced:performance"
label: "Performance Optimization"
title: "Performance Optimization" title: "Performance Optimization"
description: "Tips for optimal performance" description: "Tips for optimal performance"
!!site.page src: "scaling" !!site.page src: "scaling"
label: "Scaling Guide"
title: "Scaling Guide" title: "Scaling Guide"
description: "How to scale the system"
!!site.page src: "security" !!site.page src: "security"
label: "Security Best Practices"
title: "Security Best Practices" title: "Security Best Practices"
description: "Security considerations and best practices" description: "Security considerations and best practices"
!!site.page src: "troubleshooting" !!site.page src: "troubleshooting"
label: "Troubleshooting"
title: "Troubleshooting" title: "Troubleshooting"
description: "Common issues and solutions" description: "Common issues and solutions"
draft: false draft: false
@@ -174,10 +185,8 @@ const test_heroscript = '
path: "/tmp/docs-dev" path: "/tmp/docs-dev"
' '
fn test_site1() ! { fn test_site1() ! {
console.print_header('Site Module Comprehensive Test') console.print_header('Site Module Comprehensive Test - Part 1')
console.lf() console.lf()
// ======================================================== // ========================================================
@@ -200,7 +209,7 @@ fn test_site1() ! {
// TEST 3: Retrieve site and validate // TEST 3: Retrieve site and validate
// ======================================================== // ========================================================
console.print_item('TEST 3: Retrieving configured site') console.print_item('TEST 3: Retrieving configured site')
mut test_site := site.get(name: 'test_docs')! mut test_site := get(name: 'test_docs')!
console.print_green(' Site retrieved successfully') console.print_green(' Site retrieved successfully')
console.lf() console.lf()
@@ -208,7 +217,7 @@ fn test_site1() ! {
// TEST 4: Validate SiteConfig // TEST 4: Validate SiteConfig
// ======================================================== // ========================================================
console.print_header('Validating SiteConfig') console.print_header('Validating SiteConfig')
mut config := &test_site.siteconfig mut config := &test_site.config
help_test_string('Site Name', config.name, 'test_docs') help_test_string('Site Name', config.name, 'test_docs')
help_test_string('Site Title', config.title, 'Test Documentation Site') help_test_string('Site Title', config.title, 'Test Documentation Site')
@@ -221,15 +230,14 @@ fn test_site1() ! {
help_test_string('Meta Title', config.meta_title, 'Test Docs - Advanced') help_test_string('Meta Title', config.meta_title, 'Test Docs - Advanced')
help_test_string('Meta Image', config.meta_image, 'img/test-og-alternative.png') help_test_string('Meta Image', config.meta_image, 'img/test-og-alternative.png')
assert config.build_dest.len == 1, 'Should have 1 production build destination' assert test_site.build_dest.len == 1, 'Should have 1 production build destination'
console.print_green(' Production build dest: ${config.build_dest[0].path}') console.print_green(' Production build dest: ${test_site.build_dest[0].path}')
assert config.build_dest_dev.len == 1, 'Should have 1 dev build destination' assert test_site.build_dest_dev.len == 1, 'Should have 1 dev build destination'
console.print_green(' Dev build dest: ${config.build_dest_dev[0].path}') console.print_green(' Dev build dest: ${test_site.build_dest_dev[0].path}')
console.lf() console.lf()
// ======================================================== // ========================================================
// TEST 5: Validate Menu Configuration // TEST 5: Validate Menu Configuration
// ======================================================== // ========================================================
@@ -253,7 +261,6 @@ fn test_site1() ! {
console.lf() console.lf()
// ======================================================== // ========================================================
// TEST 6: Validate Footer Configuration // TEST 6: Validate Footer Configuration
// ======================================================== // ========================================================
@@ -291,7 +298,10 @@ fn test_site1() ! {
// TEST 7: Validate Announcement Bar // TEST 7: Validate Announcement Bar
// ======================================================== // ========================================================
console.print_header('Validating Announcement Bar') console.print_header('Validating Announcement Bar')
mut announcement := config.announcement assert test_site.announcements.len == 1, 'Should have 1 announcement, got ${test_site.announcements.len}'
console.print_green(' Announcement bar present')
mut announcement := test_site.announcements[0]
help_test_string('Announcement Content', announcement.content, '🎉 Version 2.0 is now available! Check out the new features.') help_test_string('Announcement Content', announcement.content, '🎉 Version 2.0 is now available! Check out the new features.')
help_test_string('Announcement BG Color', announcement.background_color, '#1a472a') help_test_string('Announcement BG Color', announcement.background_color, '#1a472a')
@@ -300,109 +310,150 @@ fn test_site1() ! {
console.print_green(' Announcement bar configured correctly') console.print_green(' Announcement bar configured correctly')
console.lf() console.lf()
} }
fn test_site2() ! { fn test_site2() ! {
console.print_header('Site Module Comprehensive Test') console.print_header('Site Module Comprehensive Test - Part 2')
console.lf() console.lf()
reset()
mut plbook := playbook.new(text: test_heroscript)! mut plbook := playbook.new(text: test_heroscript)!
mut test_site := site.get(name: 'test_docs')! play(mut plbook)!
mut test_site := get(name: 'test_docs')!
// ======================================================== // ========================================================
// TEST 8: Validate Pages // TEST 8: Validate Pages
// ======================================================== // ========================================================
console.print_header('Validating Pages') console.print_header('Validating Pages')
mut pages := test_site.pages.clone()
assert pages.len == 13, 'Should have 13 pages, got ${pages.len}' println(test_site)
console.print_green(' Total pages: ${pages.len}')
assert test_site.pages.len == 13, 'Should have 13 pages, got ${test_site.pages.len}'
console.print_green(' Total pages: ${test_site.pages.len}')
// List and validate pages // List and validate pages
mut page_ids := pages.keys() for i, page in test_site.pages {
page_ids.sort() console.print_debug(' Page ${i}: "${page.src}" - "${page.label}"')
for page_id in page_ids {
mut page := pages[page_id]
console.print_debug(' Page: ${page_id} - "${page.title}"')
} }
// Validate specific pages // Validate specific pages exist by src
assert 'guides:introduction' in pages, 'guides:introduction page not found' mut src_exists := false
for page in test_site.pages {
if page.src == 'guides:introduction' {
src_exists = true
break
}
}
assert src_exists, 'guides:introduction page not found'
console.print_green(' Found guides:introduction') console.print_green(' Found guides:introduction')
assert 'concepts:architecture' in pages, 'concepts:architecture page not found' src_exists = false
for page in test_site.pages {
if page.src == 'concepts:architecture' {
src_exists = true
break
}
}
assert src_exists, 'concepts:architecture page not found'
console.print_green(' Found concepts:architecture') console.print_green(' Found concepts:architecture')
assert 'api:rest' in pages, 'api:rest page not found' src_exists = false
for page in test_site.pages {
if page.src == 'api:rest' {
src_exists = true
break
}
}
assert src_exists, 'api:rest page not found'
console.print_green(' Found api:rest') console.print_green(' Found api:rest')
console.lf() console.lf()
// ======================================================== // ========================================================
// TEST 9: Validate Navigation Structure // TEST 9: Validate Categories
// ======================================================== // ========================================================
console.print_header('Validating Navigation Structure') console.print_header('Validating Categories')
mut sidebar := unsafe { test_site.nav.my_sidebar.clone() }
console.print_item('Navigation sidebar has ${sidebar.len} items') assert test_site.categories.len == 4, 'Should have 4 categories, got ${test_site.categories.len}'
console.print_green(' Total categories: ${test_site.categories.len}')
// Count categories for i, category in test_site.categories {
mut category_count := 0 console.print_debug(' Category ${i}: "${category.path}" (collapsible: ${category.collapsible}, collapsed: ${category.collapsed})')
mut doc_count := 0 }
for item in sidebar { // Validate category paths
mut category_paths := test_site.categories.map(it.path)
assert category_paths.contains('Getting Started'), 'Missing "Getting Started" category'
console.print_green(' Found "Getting Started" category')
assert category_paths.contains('Core Concepts'), 'Missing "Core Concepts" category'
console.print_green(' Found "Core Concepts" category')
assert category_paths.contains('API Reference'), 'Missing "API Reference" category'
console.print_green(' Found "API Reference" category')
assert category_paths.contains('Advanced Topics'), 'Missing "Advanced Topics" category'
console.print_green(' Found "Advanced Topics" category')
console.lf()
// ========================================================
// TEST 10: Validate Navigation Structure (Sidebar)
// ========================================================
console.print_header('Validating Navigation Structure (Sidebar)')
mut sidebar := test_site.sidebar()
console.print_item('Sidebar has ${sidebar.my_sidebar.len} root items')
assert sidebar.my_sidebar.len > 0, 'Sidebar should not be empty'
console.print_green(' Sidebar generated successfully')
// Count categories in sidebar
mut sidebar_category_count := 0
mut sidebar_doc_count := 0
for item in sidebar.my_sidebar {
match item { match item {
NavCat { NavCat {
category_count++ sidebar_category_count++
console.print_debug(' Category: "${item.label}" with ${item.items.len} sub-items')
} }
NavDoc { NavDoc {
doc_count++ sidebar_doc_count++
console.print_debug(' Doc: "${item.label}" (${item.id})')
} }
NavLink { else {
console.print_debug(' Link: "${item.label}" -> ${item.href}') // Other types
} }
} }
} }
assert category_count == 4, 'Should have 4 categories, got ${category_count}' console.print_item('Sidebar contains: ${sidebar_category_count} categories, ${sidebar_doc_count} docs')
console.print_green(' Navigation has 4 categories')
// Validate category structure // Detailed sidebar validation
for item in sidebar { for i, item in sidebar.my_sidebar {
match item { match item {
NavCat { NavCat {
console.print_item('Category: "${item.label}"') console.print_debug(' Category ${i}: "${item.label}" (${item.items.len} items)')
println(' Collapsible: ${item.collapsible}, Collapsed: ${item.collapsed}')
println(' Items: ${item.items.len}')
// Validate sub-items
for sub_item in item.items { for sub_item in item.items {
match sub_item { match sub_item {
NavDoc { NavDoc {
println(' - ${sub_item.label} (${sub_item.id})') console.print_debug(' Doc: "${sub_item.label}" (${sub_item.path})')
}
else {
println(' - Unexpected item type')
} }
else {}
} }
} }
} }
NavDoc {
console.print_debug(' Doc ${i}: "${item.label}" (${item.path})')
}
else {} else {}
} }
} }
console.lf() console.lf()
// ======================================================== // ========================================================
// TEST 10: Validate Site Factory // TEST 11: Validate Site Factory
// ======================================================== // ========================================================
console.print_header('Validating Site Factory') console.print_header('Validating Site Factory')
@@ -420,11 +471,16 @@ fn test_site2() ! {
console.lf() console.lf()
// println(test_site) // ========================================================
// if true{panic("ss33")} // TEST 12: Validate Print Output
// ========================================================
console.print_header('Site Sidebar String Output')
println(test_site.sidebar_str())
if true {
panic('End of tests reached - panic to stop further execution')
}
} }
// ============================================================ // ============================================================
// Helper Functions for Testing // Helper Functions for Testing
// ============================================================ // ============================================================