...
This commit is contained in:
@@ -29,18 +29,26 @@ pub fn (mut s Site) sidebar() SideBar {
|
||||
mut uncategorized_pages := []Page{}
|
||||
|
||||
// Group pages by category
|
||||
eprintln('DEBUG: Grouping ${s.pages.len} pages into categories')
|
||||
for page in s.pages {
|
||||
if page.category_id == 0 {
|
||||
// Page at root level (no category)
|
||||
uncategorized_pages << page
|
||||
eprintln(' Page "${page.src}": UNCATEGORIZED')
|
||||
} else {
|
||||
// Page belongs to a category
|
||||
if page.category_id !in category_pages {
|
||||
category_pages[page.category_id] = []Page{}
|
||||
}
|
||||
category_pages[page.category_id] << page
|
||||
if page.category_id < s.categories.len {
|
||||
eprintln(' Page "${page.src}": category_id=${page.category_id} -> "${s.categories[page.category_id].path}"')
|
||||
} else {
|
||||
eprintln(' Page "${page.src}": category_id=${page.category_id} -> INVALID INDEX!')
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln('DEBUG: Grouped into ${category_pages.len} categories + ${uncategorized_pages.len} uncategorized')
|
||||
|
||||
// Sort pages within each category by their order in the pages array
|
||||
for category_id in category_pages.keys() {
|
||||
@@ -54,62 +62,111 @@ pub fn (mut s Site) sidebar() SideBar {
|
||||
// Build nested category structure from path
|
||||
// ============================================================
|
||||
mut category_tree := map[string]&NavCat{}
|
||||
mut parent_map := map[string]string{} // Map of path -> parent_path
|
||||
|
||||
// PASS 1: Create all category nodes (even empty intermediate ones)
|
||||
// PASS 1: Create ALL category nodes first
|
||||
// Collect all paths first, then sort by depth (shallow first)
|
||||
mut all_paths := []string{}
|
||||
for i, category in s.categories {
|
||||
category_id := i + 1 // categories are 1-indexed
|
||||
|
||||
// Split path into parts (e.g., "Getting Started/Advanced/Deep" -> ["Getting Started", "Advanced", "Deep"])
|
||||
path_parts := if category.path.contains('/') {
|
||||
category.path.split('/')
|
||||
} else {
|
||||
[category.path]
|
||||
}
|
||||
|
||||
// Create all nodes in the path hierarchy
|
||||
mut current_path := ''
|
||||
|
||||
for part_idx, part in path_parts {
|
||||
if current_path.len > 0 {
|
||||
current_path += '/'
|
||||
}
|
||||
current_path += part
|
||||
|
||||
// Check if this node already exists
|
||||
// Add this path if not already added
|
||||
if current_path !in category_tree {
|
||||
// Create new category node
|
||||
mut new_cat := &NavCat{
|
||||
label: part
|
||||
collapsible: category.collapsible
|
||||
collapsed: category.collapsed
|
||||
items: []NavItem{}
|
||||
}
|
||||
category_tree[current_path] = new_cat
|
||||
|
||||
// If this is not the root of the path, add it to its parent
|
||||
if part_idx > 0 {
|
||||
parent_path := path_parts[0..part_idx].join('/')
|
||||
if parent_path in category_tree {
|
||||
mut parent_cat := category_tree[parent_path]
|
||||
parent_cat.items << new_cat
|
||||
}
|
||||
}
|
||||
all_paths << current_path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PASS 2: Add pages to their designated categories
|
||||
// Sort paths by depth (number of '/') so we create parents before children
|
||||
all_paths.sort(a.count('/') < b.count('/'))
|
||||
|
||||
// Now create all nodes in order of depth
|
||||
for path in all_paths {
|
||||
if path !in category_tree {
|
||||
path_parts := path.split('/')
|
||||
part := path_parts[path_parts.len - 1]
|
||||
|
||||
// Find the category with this path to get collapsible/collapsed settings
|
||||
mut collapsible := true
|
||||
mut collapsed := false
|
||||
for category in s.categories {
|
||||
if category.path == path {
|
||||
collapsible = category.collapsible
|
||||
collapsed = category.collapsed
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Create new category node
|
||||
mut new_cat := &NavCat{
|
||||
label: part
|
||||
collapsible: collapsible
|
||||
collapsed: collapsed
|
||||
items: []NavItem{}
|
||||
}
|
||||
category_tree[path] = new_cat
|
||||
|
||||
// Record parent for later linking
|
||||
if path.contains('/') {
|
||||
last_slash := path.last_index('/') or { 0 }
|
||||
parent_path := path[0..last_slash]
|
||||
parent_map[path] = parent_path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PASS 2: Link all parent-child relationships
|
||||
// Process these in order of depth to ensure parents are linked first
|
||||
mut sorted_paths := parent_map.keys()
|
||||
sorted_paths.sort(a.count('/') < b.count('/'))
|
||||
|
||||
for path in sorted_paths {
|
||||
parent_path := parent_map[path]
|
||||
if parent_path in category_tree && path in category_tree {
|
||||
mut parent_cat := category_tree[parent_path]
|
||||
child_cat := category_tree[path]
|
||||
|
||||
// Only add if not already added
|
||||
mut already_added := false
|
||||
for item in parent_cat.items {
|
||||
if item is NavCat && item.label == child_cat.label {
|
||||
already_added = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !already_added {
|
||||
parent_cat.items << child_cat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PASS 3: Add pages to their designated categories
|
||||
eprintln('DEBUG PASS 3: Adding pages to categories')
|
||||
for i, category in s.categories {
|
||||
category_id := i + 1 // categories are 1-indexed
|
||||
category_id := i // categories are 0-indexed in the page assignment
|
||||
|
||||
// Skip if no pages in this category
|
||||
if category_id !in category_pages {
|
||||
eprintln(' Category ${category_id} ("${category.path}"): no pages')
|
||||
continue
|
||||
}
|
||||
|
||||
// Build the full path for this category
|
||||
full_path := category.path
|
||||
|
||||
eprintln(' Category ${category_id} ("${full_path}"): ${category_pages[category_id].len} pages')
|
||||
|
||||
// Add pages to this category
|
||||
if full_path in category_tree {
|
||||
mut leaf_cat := category_tree[full_path]
|
||||
@@ -118,6 +175,8 @@ pub fn (mut s Site) sidebar() SideBar {
|
||||
// Convert page src format "collection:name" to path "collection/name"
|
||||
path := page.src.replace(':', '/')
|
||||
|
||||
eprintln(' Adding page: ${page.src} -> ${path}')
|
||||
|
||||
nav_doc := NavDoc{
|
||||
path: path
|
||||
label: if page.label.len > 0 { page.label } else { page.title }
|
||||
@@ -125,13 +184,15 @@ pub fn (mut s Site) sidebar() SideBar {
|
||||
leaf_cat.items << nav_doc
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln(' ERROR: Category path "${full_path}" not in category_tree!')
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 3: Add root-level categories to sidebar
|
||||
// PASS 4: Add root-level categories to sidebar
|
||||
// ============================================================
|
||||
// Find all root-level categories (those without '/')
|
||||
// Find all root-level categories (those without '/') and add them once
|
||||
mut added_roots := map[string]bool{}
|
||||
|
||||
for i, category in s.categories {
|
||||
@@ -149,7 +210,7 @@ pub fn (mut s Site) sidebar() SideBar {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 4: Add uncategorized pages at root level
|
||||
// PASS 5: Add uncategorized pages at root level
|
||||
// ============================================================
|
||||
for page in uncategorized_pages {
|
||||
if !page.hide {
|
||||
@@ -165,7 +226,7 @@ pub fn (mut s Site) sidebar() SideBar {
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PASS 5: Add standalone links (if needed)
|
||||
// PASS 6: Add standalone links (if needed)
|
||||
// ============================================================
|
||||
for link in s.links {
|
||||
nav_link := NavLink{
|
||||
|
||||
@@ -27,7 +27,8 @@ const test_heroscript_nav_depth = '
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "why:intro"
|
||||
//COLLECTION WILL BE REPEATED, HAS NO INFLUENCE ON NAVIGATION LEVELS
|
||||
!!site.page src: "mycollection:intro"
|
||||
label: "Why Choose Us"
|
||||
title: "Why Choose Us"
|
||||
description: "Reasons to use this platform"
|
||||
@@ -38,14 +39,14 @@ const test_heroscript_nav_depth = '
|
||||
description: "Main benefits overview"
|
||||
|
||||
// ============================================================
|
||||
// LEVEL 2: Two-level nested category
|
||||
// LEVEL 1: Simple top-level category
|
||||
// ============================================================
|
||||
!!site.page_category
|
||||
path: "Tutorials"
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "tutorials:getting_started"
|
||||
!!site.page src: "getting_started"
|
||||
label: "Getting Started"
|
||||
title: "Getting Started"
|
||||
description: "Basic tutorial to get started"
|
||||
@@ -63,7 +64,7 @@ const test_heroscript_nav_depth = '
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "operations:emergency_restart"
|
||||
!!site.page src: "emergency_restart"
|
||||
label: "Emergency Restart"
|
||||
title: "Emergency Restart"
|
||||
description: "How to emergency restart the system"
|
||||
@@ -79,14 +80,14 @@ const test_heroscript_nav_depth = '
|
||||
description: "Handle incidents in real-time"
|
||||
|
||||
// ============================================================
|
||||
// LEVEL 2.5: Two-level nested category (Tutorials > Operations)
|
||||
// LEVEL 2: Two-level nested category (Tutorials > Operations)
|
||||
// ============================================================
|
||||
!!site.page_category
|
||||
path: "Tutorials/Operations"
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "ops:daily_checks"
|
||||
!!site.page src: "daily_checks"
|
||||
label: "Daily Checks"
|
||||
title: "Daily Checks"
|
||||
description: "Daily maintenance checklist"
|
||||
@@ -102,7 +103,7 @@ const test_heroscript_nav_depth = '
|
||||
description: "Backup and restore procedures"
|
||||
|
||||
// ============================================================
|
||||
// LEVEL 1.5: One-to-two level (Tutorials)
|
||||
// LEVEL 1: One-to-two level (Tutorials)
|
||||
// ============================================================
|
||||
// Note: This creates a sibling at the Tutorials level (not nested deeper)
|
||||
!!site.page src: "advanced_concepts"
|
||||
@@ -123,7 +124,7 @@ const test_heroscript_nav_depth = '
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "faq:general"
|
||||
!!site.page src: "general"
|
||||
label: "General Questions"
|
||||
title: "General Questions"
|
||||
description: "Frequently asked questions"
|
||||
@@ -144,14 +145,14 @@ const test_heroscript_nav_depth = '
|
||||
description: "Support-related FAQ"
|
||||
|
||||
// ============================================================
|
||||
// LEVEL 3+: Even deeper nesting for comprehensive testing
|
||||
// LEVEL 4: Four-level nested category (Tutorials > Operations > Database > Optimization)
|
||||
// ============================================================
|
||||
!!site.page_category
|
||||
path: "Tutorials/Operations/Database/Optimization"
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "database:query_optimization"
|
||||
!!site.page src: "query_optimization"
|
||||
label: "Query Optimization"
|
||||
title: "Query Optimization"
|
||||
description: "Optimize your database queries"
|
||||
@@ -166,7 +167,7 @@ const test_heroscript_nav_depth = '
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
|
||||
!!site.page src: "db:configuration"
|
||||
!!site.page src: "configuration"
|
||||
label: "Configuration"
|
||||
title: "Database Configuration"
|
||||
description: "Configure your database"
|
||||
@@ -178,6 +179,289 @@ const test_heroscript_nav_depth = '
|
||||
|
||||
'
|
||||
|
||||
fn check(s2 Site) {
|
||||
mut s := Site{
|
||||
doctree_path: ''
|
||||
config: SiteConfig{
|
||||
name: 'nav_depth_test'
|
||||
title: 'Navigation Depth Test Site'
|
||||
description: 'Testing multi-level nested navigation'
|
||||
tagline: 'Deep navigation structures'
|
||||
favicon: 'img/favicon.png'
|
||||
image: 'img/tf_graph.png'
|
||||
copyright: '© 2025 Example Organization'
|
||||
footer: Footer{
|
||||
style: 'dark'
|
||||
links: []
|
||||
}
|
||||
menu: Menu{
|
||||
title: 'Nav Depth Test'
|
||||
items: [
|
||||
MenuItem{
|
||||
href: ''
|
||||
to: '/'
|
||||
label: 'Home'
|
||||
position: 'left'
|
||||
},
|
||||
]
|
||||
logo_alt: ''
|
||||
logo_src: ''
|
||||
logo_src_dark: ''
|
||||
}
|
||||
url: ''
|
||||
base_url: '/'
|
||||
url_home: ''
|
||||
meta_title: ''
|
||||
meta_image: ''
|
||||
}
|
||||
pages: [
|
||||
Page{
|
||||
src: 'mycollection:intro'
|
||||
label: 'Why Choose Us'
|
||||
title: 'Why Choose Us'
|
||||
description: 'Reasons to use this platform'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 0
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:benefits'
|
||||
label: 'Key Benefits'
|
||||
title: 'Key Benefits'
|
||||
description: 'Main benefits overview'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 0
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:getting_started'
|
||||
label: 'Getting Started'
|
||||
title: 'Getting Started'
|
||||
description: 'Basic tutorial to get started'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 1
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:first_steps'
|
||||
label: 'First Steps'
|
||||
title: 'First Steps'
|
||||
description: 'Your first steps with the platform'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 1
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:emergency_restart'
|
||||
label: 'Emergency Restart'
|
||||
title: 'Emergency Restart'
|
||||
description: 'How to emergency restart the system'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 2
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:critical_fixes'
|
||||
label: 'Critical Fixes'
|
||||
title: 'Critical Fixes'
|
||||
description: 'Apply critical fixes immediately'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 2
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:incident_response'
|
||||
label: 'Incident Response'
|
||||
title: 'Incident Response'
|
||||
description: 'Handle incidents in real-time'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 2
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:daily_checks'
|
||||
label: 'Daily Checks'
|
||||
title: 'Daily Checks'
|
||||
description: 'Daily maintenance checklist'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 3
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:monitoring'
|
||||
label: 'Monitoring'
|
||||
title: 'Monitoring'
|
||||
description: 'System monitoring procedures'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 3
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:backups'
|
||||
label: 'Backups'
|
||||
title: 'Backups'
|
||||
description: 'Backup and restore procedures'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 3
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:advanced_concepts'
|
||||
label: 'Advanced Concepts'
|
||||
title: 'Advanced Concepts'
|
||||
description: 'Deep dive into advanced concepts'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 3
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:troubleshooting'
|
||||
label: 'Troubleshooting'
|
||||
title: 'Troubleshooting'
|
||||
description: 'Troubleshooting guide'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 3
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:general'
|
||||
label: 'General Questions'
|
||||
title: 'General Questions'
|
||||
description: 'Frequently asked questions'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 4
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:pricing_questions'
|
||||
label: 'Pricing'
|
||||
title: 'Pricing Questions'
|
||||
description: 'Questions about pricing'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 4
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:technical_faq'
|
||||
label: 'Technical FAQ'
|
||||
title: 'Technical FAQ'
|
||||
description: 'Technical frequently asked questions'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 4
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:support_faq'
|
||||
label: 'Support'
|
||||
title: 'Support FAQ'
|
||||
description: 'Support-related FAQ'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 4
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:query_optimization'
|
||||
label: 'Query Optimization'
|
||||
title: 'Query Optimization'
|
||||
description: 'Optimize your database queries'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 5
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:indexing_strategy'
|
||||
label: 'Indexing Strategy'
|
||||
title: 'Indexing Strategy'
|
||||
description: 'Effective indexing strategies'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 5
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:configuration'
|
||||
label: 'Configuration'
|
||||
title: 'Database Configuration'
|
||||
description: 'Configure your database'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 6
|
||||
},
|
||||
Page{
|
||||
src: 'mycollection:replication'
|
||||
label: 'Replication'
|
||||
title: 'Database Replication'
|
||||
description: 'Set up database replication'
|
||||
draft: false
|
||||
hide_title: false
|
||||
hide: false
|
||||
category_id: 6
|
||||
},
|
||||
]
|
||||
links: []
|
||||
categories: [
|
||||
Category{
|
||||
path: 'Why'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
Category{
|
||||
path: 'Tutorials'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
Category{
|
||||
path: 'Tutorials/Operations/Urgent'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
Category{
|
||||
path: 'Tutorials/Operations'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
Category{
|
||||
path: 'Why/FAQ'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
Category{
|
||||
path: 'Tutorials/Operations/Database/Optimization'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
Category{
|
||||
path: 'Tutorials/Operations/Database'
|
||||
collapsible: true
|
||||
collapsed: false
|
||||
},
|
||||
]
|
||||
announcements: []
|
||||
imports: []
|
||||
build_dest: []
|
||||
build_dest_dev: []
|
||||
}
|
||||
assert s == s2
|
||||
}
|
||||
|
||||
pub fn test_navigation_depth() ! {
|
||||
console.print_header('🧭 Navigation Depth Multi-Level Test')
|
||||
console.lf()
|
||||
@@ -200,6 +484,8 @@ pub fn test_navigation_depth() ! {
|
||||
console.print_green('✓ Site retrieved')
|
||||
console.lf()
|
||||
|
||||
check(nav_site)
|
||||
|
||||
// ========================================================
|
||||
// TEST 1: Validate Categories Structure
|
||||
// ========================================================
|
||||
|
||||
Reference in New Issue
Block a user