This commit is contained in:
2025-12-02 04:15:22 +01:00
parent 29ab30788e
commit 8c8369c42b
14 changed files with 169 additions and 248 deletions

View File

@@ -0,0 +1,11 @@
module meta
// Announcement bar config structure
pub struct Announcement {
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']
}

View File

@@ -0,0 +1,7 @@
module meta
pub struct BuildDest {
pub mut:
path string
ssh_name string
}

View File

@@ -0,0 +1,8 @@
module meta
struct Category {
pub mut:
path string // e.g. Operations/Daily (means 2 levels deep, first level is Operations)
collapsible bool = true
collapsed bool
}

View File

@@ -0,0 +1,11 @@
module meta
// is to import one site into another, can be used to e.g. import static parts from one location into the build one we are building
pub struct ImportItem {
pub mut:
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
}

View File

@@ -0,0 +1,8 @@
module meta
struct Link {
pub mut:
label string
href string
description string
}

View File

@@ -1,14 +1,14 @@
module meta
import incubaid.herolib.web.doctree.client as doctree_client
// Page represents a single documentation page
pub struct Page {
pub mut:
id string // Unique identifier: "collection:page_name"
src string // Unique identifier: "collection:page_name" marks where the page is from. (is also name_fix'ed)
label string // Display label in navigation e.g. "Getting Started"
title string // Display title (optional, extracted from markdown if empty)
description string // Brief description for metadata
draft bool // Is this page a draft? Means only show in development mode
hide_title bool // Should the title be hidden on the page?
hide bool // Should the page be hidden from navigation?
category_id int // Optional category ID this page belongs to, if 0 it means its at root level
}

View File

@@ -1,9 +1,8 @@
module meta
import json
// ============================================================================
// Sidebar Navigation Models (Domain Types)
// is the result of walking through the pages, links and categories to build the sidebar structure
// ============================================================================
pub struct SideBar {
@@ -15,9 +14,8 @@ pub type NavItem = NavDoc | NavCat | NavLink
pub struct NavDoc {
pub:
id string
path string // path is $collection/$name without .md, this is a subdir of the doctree export dir
label string
hide_title bool
}
pub struct NavCat {

View File

@@ -3,7 +3,18 @@ module meta
@[heap]
pub struct Site {
pub mut:
pages map[string]Page // key: "collection:page_name"
nav SideBar // Navigation sidebar configuration
siteconfig SiteConfig // Full site configuration
doctree_path string // path to the export of the doctree site
config SiteConfig // Full site configuration
pages []Page
links []Link
categories []Category
announcements []Announcement // there can be more than 1 announcement
imports []ImportItem
build_dest []BuildDest // Production build destinations (from !!site.build_dest)
build_dest_dev []BuildDest // Development build destinations (from !!site.build_dest_dev)
}
pub fn (mut s Site) sidebar() SideBar {
// TODO: implement, use all info abouve []page, []categories, []links to build the sidebar
return SideBar{}
}

View File

@@ -15,7 +15,6 @@ pub mut:
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:)
@@ -24,21 +23,6 @@ pub mut:
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)
}
// Announcement bar config structure
pub 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']
}
// Footer config structures
@@ -78,20 +62,3 @@ pub mut:
logo_src string @[json: 'logoSrc']
logo_src_dark string @[json: 'logoSrcDark']
}
pub struct BuildDest {
pub mut:
path string
ssh_name string
}
// 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
pub 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
}

View File

@@ -1,15 +1,11 @@
module meta
import os
import incubaid.herolib.core.playbook { PlayBook }
import incubaid.herolib.core.texttools
import time
import incubaid.herolib.ui.console
// ============================================================
// ANNOUNCEMENT: Process announcement bar (optional)
// ============================================================
fn play_announcement(mut plbook PlayBook, mut config SiteConfig) ! {
fn play_announcement(mut plbook PlayBook, mut site Site) ! {
mut announcement_actions := plbook.find(filter: 'site.announcement')!
if announcement_actions.len > 0 {
@@ -21,7 +17,7 @@ fn play_announcement(mut plbook PlayBook, mut config SiteConfig) ! {
return error('!!site.announcement: must specify "content"')
}
config.announcement = AnnouncementBar{
site.announcements << Announcement{
// id: p.get('id')!
content: content
background_color: p.get_default('background_color', '#20232a')!

View File

@@ -9,7 +9,7 @@ import incubaid.herolib.ui.console
// ============================================================
// IMPORTS: Process content imports
// ============================================================
fn play_imports(mut plbook PlayBook, mut config SiteConfig) ! {
fn play_imports(mut plbook PlayBook, mut site Site) ! {
mut import_actions := plbook.find(filter: 'site.import')!
for mut action in import_actions {
@@ -37,7 +37,6 @@ fn play_imports(mut plbook PlayBook, mut config SiteConfig) ! {
// Create import item
mut import_item := ImportItem{
name: p.get_default('name', '')!
url: p.get_default('url', '')!
path: import_path
dest: p.get_default('dest', '')!
@@ -45,7 +44,7 @@ fn play_imports(mut plbook PlayBook, mut config SiteConfig) ! {
visible: p.get_default_false('visible')
}
config.imports << import_item
site.imports << import_item
action.done = true
}
}

View File

@@ -1,193 +0,0 @@
module meta
import os
import incubaid.herolib.core.playbook { PlayBook }
import incubaid.herolib.core.texttools
import time
import incubaid.herolib.ui.console
// ============================================================
// Internal structure for tracking category information
// ============================================================
struct CategoryInfo {
pub mut:
name string
label string
position int
nav_items []NavItem
}
// ============================================================
// PAGES: Process pages and build navigation structure
// ============================================================
fn play_pages(mut plbook PlayBook, mut website Site) ! {
mut collection_current := '' // Track current collection for reuse
mut categories := map[string]CategoryInfo{} // Map of category name -> info
mut category_current := '' // Track current active category
mut root_nav_items := []NavItem{} // Root-level items (pages without category)
mut next_category_position := 100 // Auto-increment position for categories
// ============================================================
// PASS 1: Process all page and category actions
// ============================================================
mut all_actions := plbook.find(filter: 'site.')!
for mut action in all_actions {
if action.done {
continue
}
// ========== PAGE CATEGORY ==========
if action.name == 'page_category' {
mut p := action.params
category_name := p.get('name') or {
return error('!!site.page_category: must specify "name"')
}
category_name_fixed := texttools.name_fix(category_name)
// label is empty when not specified
mut label := p.get_default('label', "")!
mut position := p.get_int_default('position', next_category_position)!
// Auto-increment position if using default
if position == next_category_position {
next_category_position += 100
}
// Create and store category info
categories[category_name_fixed] = CategoryInfo{
name: category_name_fixed
label: label
position: position
nav_items: []NavItem{}
}
category_current = category_name_fixed
console.print_item('Created page category: "${label}" (${category_name_fixed})')
action.done = true
continue
}
// ========== PAGE ==========
if action.name == 'page' {
mut p := action.params
mut page_src := p.get_default('src', '')!
mut page_collection := ''
mut page_name := ''
// Parse collection:page format from src
if page_src.contains(':') {
parts := page_src.split(':')
page_collection = texttools.name_fix(parts[0])
page_name = normalize_page_name(parts[1])
} else {
// Use previously specified collection if available
if collection_current.len > 0 {
page_collection = collection_current
page_name = normalize_page_name(page_src)
} else {
return error('!!site.page: must specify source as "collection:page_name" in "src".\nGot src="${page_src}" with no collection previously set.\nEither specify "collection:page_name" or define a collection first.')
}
}
// Validation
if page_name.len == 0 {
return error('!!site.page: could not extract valid page name from src="${page_src}"')
}
if page_collection.len == 0 {
return error('!!site.page: could not determine collection')
}
// Store collection for subsequent pages
collection_current = page_collection
// Build page ID
page_id := '${page_collection}:${page_name}'
// Get optional page metadata
page_title := p.get_default('title', '')!
page_description := p.get_default('description', '')!
page_draft := p.get_default_false('draft')
page_hide_title := p.get_default_false('hide_title')
// Create page
mut page := Page{
id: page_id
title: page_title
description: page_description
draft: page_draft
hide_title: page_hide_title
src: page_id
}
website.pages[page_id] = page
// Create navigation item with human-readable label
// nav_label := page_title.len
nav_doc := NavDoc{
id: page.id
label: page.title
hide_title: page.hide_title
}
// Add to appropriate category or root
if category_current.len > 0 {
if category_current in categories {
mut cat_info := categories[category_current]
cat_info.nav_items << nav_doc
categories[category_current] = cat_info
console.print_debug('Added page "${page_id}" to category "${category_current}"')
}
} else {
root_nav_items << nav_doc
console.print_debug('Added root page "${page_id}"')
}
action.done = true
continue
}
}
// ============================================================
// PASS 2: Build final navigation structure from categories
// ============================================================
console.print_item('Building navigation structure...')
mut final_nav_items := []NavItem{}
// Add root items first
for item in root_nav_items {
final_nav_items << item
}
// Sort categories by position and add them
mut sorted_categories := []CategoryInfo{}
for _, cat_info in categories {
sorted_categories << cat_info
}
// Sort by position
sorted_categories.sort(a.position < b.position)
// Convert categories to NavCat items and add to navigation
for cat_info in sorted_categories {
// Unwrap NavDoc items from cat_info.nav_items (they're already NavItem)
nav_cat := NavCat{
label: cat_info.label
collapsible: true
collapsed: false
items: cat_info.nav_items
}
final_nav_items << nav_cat
console.print_debug('Added category to nav: "${cat_info.label}" with ${cat_info.nav_items.len} items')
}
// Update website navigation
website.nav.my_sidebar = final_nav_items
console.print_green('Navigation structure built with ${website.pages.len} pages in ${categories.len} categories')
}

View File

@@ -0,0 +1,97 @@
module meta
import incubaid.herolib.core.playbook { PlayBook }
import incubaid.herolib.web.doctree as doctreetools
import incubaid.herolib.ui.console
//=========================================================
// PAGES: Process pages and build navigation structure
// ============================================================
fn play_pages(mut plbook PlayBook, mut website Site) ! {
mut collection_current := ''
mut category_current := 0
// ============================================================
// PASS 1: Process all page and category actions
// ============================================================
mut all_actions := plbook.find(filter: 'site.')!
for mut action in all_actions {
if action.done {
continue
}
// ========== PAGE CATEGORY ==========
if action.name == 'page_category' {
mut p := action.params
// label is empty when not specified (we support label & path for flexibility)
mut category := Category{
path: p.get_default('path', p.get_default('label', '')!)!
collapsible: p.get_default_true('collapsible')
collapsed: p.get_default_true('collapsed')
}
website.categories << category
category_current = website.categories.len - 1
console.print_item('Created page category: "${category.path}" ')
action.done = true
continue
}
// ========== PAGE ==========
if action.name == 'page' {
mut p := action.params
mut page_src := p.get_default('src', '')!
mut page_collection := ''
mut page_name := ''
// Parse collection:page format from src
if page_src.contains(':') {
page_collection, page_name = doctreetools.key_parse(page_src)!
} else {
// Use previously specified collection if available
if collection_current.len > 0 {
page_collection = collection_current
page_name = doctreetools.name_fix(page_src)
} else {
return error('!!site.page: must specify source as "collection:page_name" in "src".\nGot src="${page_src}" with no collection previously set.\nEither specify "collection:page_name" or define a collection first.')
}
}
// Validation
if page_name.len == 0 {
return error('!!site.page: could not extract valid page name from src="${page_src}"')
}
if page_collection.len == 0 {
return error('!!site.page: could not determine collection')
}
// Store collection for subsequent pages
collection_current = page_collection
// Get optional page metadata
page_label := p.get_default('label', p.get_default('title', '')!)! // is what is shown in the sidebar
page_title := p.get_default('title', '')! // is title shown on the page, if not from the page content, if empty then will be brought in from the content
page_description := p.get_default('description', '')!
// Create page
mut page := Page{
src: '${page_collection}:${page_name}'
label: page_label
title: page_title
description: page_description
draft: p.get_default_false('draft')
hide_title: p.get_default_false('hide_title')
category_id: category_current
hide: p.get_default_false('hide')
}
website.pages << page
action.done = true
continue
}
}
}

View File

@@ -20,6 +20,7 @@ pub fn key_parse(key string) !(string, string) {
pub fn name_fix(name string) string {
mut result := name
// Remove .md extension if present for processing
result = result.replace('/', '_')
if result.ends_with('.md') {
result = result[0..result.len - 3]
}