From 8c8369c42bbbe1b4a6b5d31f8f7e3f5b3002d2ca Mon Sep 17 00:00:00 2001 From: despiegk Date: Tue, 2 Dec 2025 04:15:22 +0100 Subject: [PATCH] ... --- lib/web/doctree/meta/model_announcement.v | 11 ++ lib/web/doctree/meta/model_builddest.v | 7 + lib/web/doctree/meta/model_category.v | 8 + lib/web/doctree/meta/model_import.v | 11 ++ lib/web/doctree/meta/model_link.v | 8 + lib/web/doctree/meta/model_page.v | 12 +- lib/web/doctree/meta/model_sidebar.v | 6 +- lib/web/doctree/meta/model_site.v | 17 +- lib/web/doctree/meta/model_siteconfig.v | 33 ---- lib/web/doctree/meta/play_announcement.v | 8 +- lib/web/doctree/meta/play_imports.v | 5 +- lib/web/doctree/meta/play_pages.v | 193 ------------------- lib/web/doctree/meta/play_pages_categories.v | 97 ++++++++++ lib/web/doctree/utils.v | 1 + 14 files changed, 169 insertions(+), 248 deletions(-) create mode 100644 lib/web/doctree/meta/model_announcement.v create mode 100644 lib/web/doctree/meta/model_builddest.v create mode 100644 lib/web/doctree/meta/model_category.v create mode 100644 lib/web/doctree/meta/model_import.v create mode 100644 lib/web/doctree/meta/model_link.v delete mode 100644 lib/web/doctree/meta/play_pages.v create mode 100644 lib/web/doctree/meta/play_pages_categories.v diff --git a/lib/web/doctree/meta/model_announcement.v b/lib/web/doctree/meta/model_announcement.v new file mode 100644 index 00000000..85df6825 --- /dev/null +++ b/lib/web/doctree/meta/model_announcement.v @@ -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'] +} diff --git a/lib/web/doctree/meta/model_builddest.v b/lib/web/doctree/meta/model_builddest.v new file mode 100644 index 00000000..ce3eb412 --- /dev/null +++ b/lib/web/doctree/meta/model_builddest.v @@ -0,0 +1,7 @@ +module meta + +pub struct BuildDest { +pub mut: + path string + ssh_name string +} diff --git a/lib/web/doctree/meta/model_category.v b/lib/web/doctree/meta/model_category.v new file mode 100644 index 00000000..4b7ed2b2 --- /dev/null +++ b/lib/web/doctree/meta/model_category.v @@ -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 +} diff --git a/lib/web/doctree/meta/model_import.v b/lib/web/doctree/meta/model_import.v new file mode 100644 index 00000000..fd0242f9 --- /dev/null +++ b/lib/web/doctree/meta/model_import.v @@ -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 +} diff --git a/lib/web/doctree/meta/model_link.v b/lib/web/doctree/meta/model_link.v new file mode 100644 index 00000000..baf6cf15 --- /dev/null +++ b/lib/web/doctree/meta/model_link.v @@ -0,0 +1,8 @@ +module meta + +struct Link { +pub mut: + label string + href string + description string +} diff --git a/lib/web/doctree/meta/model_page.v b/lib/web/doctree/meta/model_page.v index 788806cf..3fff9249 100644 --- a/lib/web/doctree/meta/model_page.v +++ b/lib/web/doctree/meta/model_page.v @@ -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 } - - diff --git a/lib/web/doctree/meta/model_sidebar.v b/lib/web/doctree/meta/model_sidebar.v index a02b3100..ee67ba0d 100644 --- a/lib/web/doctree/meta/model_sidebar.v +++ b/lib/web/doctree/meta/model_sidebar.v @@ -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 { diff --git a/lib/web/doctree/meta/model_site.v b/lib/web/doctree/meta/model_site.v index 2cdc0886..9cc017d1 100644 --- a/lib/web/doctree/meta/model_site.v +++ b/lib/web/doctree/meta/model_site.v @@ -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{} } diff --git a/lib/web/doctree/meta/model_siteconfig.v b/lib/web/doctree/meta/model_siteconfig.v index 82ceea69..fce68ed0 100644 --- a/lib/web/doctree/meta/model_siteconfig.v +++ b/lib/web/doctree/meta/model_siteconfig.v @@ -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 -} diff --git a/lib/web/doctree/meta/play_announcement.v b/lib/web/doctree/meta/play_announcement.v index 8abce9f1..66f7c8e9 100644 --- a/lib/web/doctree/meta/play_announcement.v +++ b/lib/web/doctree/meta/play_announcement.v @@ -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')! diff --git a/lib/web/doctree/meta/play_imports.v b/lib/web/doctree/meta/play_imports.v index 0d4ae8a9..64a511f0 100644 --- a/lib/web/doctree/meta/play_imports.v +++ b/lib/web/doctree/meta/play_imports.v @@ -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 } } diff --git a/lib/web/doctree/meta/play_pages.v b/lib/web/doctree/meta/play_pages.v deleted file mode 100644 index bbcfec95..00000000 --- a/lib/web/doctree/meta/play_pages.v +++ /dev/null @@ -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') -} diff --git a/lib/web/doctree/meta/play_pages_categories.v b/lib/web/doctree/meta/play_pages_categories.v new file mode 100644 index 00000000..14dfde51 --- /dev/null +++ b/lib/web/doctree/meta/play_pages_categories.v @@ -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 + } + } +} diff --git a/lib/web/doctree/utils.v b/lib/web/doctree/utils.v index 25c15352..5e75f084 100644 --- a/lib/web/doctree/utils.v +++ b/lib/web/doctree/utils.v @@ -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] }