diff --git a/lib/web/doctree/meta/model_site.v b/lib/web/doctree/meta/model_site.v index a6d01d92..d95584ac 100644 --- a/lib/web/doctree/meta/model_site.v +++ b/lib/web/doctree/meta/model_site.v @@ -4,7 +4,7 @@ module meta pub struct Site { pub mut: doctree_path string // path to the export of the doctree site - config SiteConfig // Full site configuration + config SiteConfig // Full site configuration pages []Page links []Link categories []Category @@ -51,8 +51,11 @@ pub fn (mut s Site) sidebar() SideBar { uncategorized_pages.sort(a.src < b.src) // ============================================================ - // PASS 1: Add categories with their pages + // Build nested category structure from path // ============================================================ + mut category_tree := map[string]&NavCat{} + + // PASS 1: Create all categories and their nested hierarchy for i, category in s.categories { category_id := i + 1 // categories are 1-indexed @@ -61,40 +64,82 @@ pub fn (mut s Site) sidebar() SideBar { continue } - mut category_items := []NavItem{} + // 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] + } - // Add pages in this category - for page in category_pages[category_id] { - if !page.hide { - // Convert page src format "collection:name" to path "collection/name" - path := page.src.replace(':', '/') + // Navigate/create the nested structure + mut current_path := '' - nav_doc := NavDoc{ - path: path - label: if page.label.len > 0 { page.label } else { page.title } + for part_idx, part in path_parts { + if current_path.len > 0 { + current_path += '/' + } + current_path += part + + // Check if this node already exists + 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 + } } - category_items << nav_doc } } - // Only add category if it has visible items - if category_items.len > 0 { - nav_cat := NavCat{ - label: if category.path.len > 0 { - category.path - } else { - 'Section ${category_id}' + // Add pages to the leaf category + if current_path in category_tree { + mut leaf_cat := category_tree[current_path] + for page in category_pages[category_id] { + if !page.hide { + // Convert page src format "collection:name" to path "collection/name" + path := page.src.replace(':', '/') + + nav_doc := NavDoc{ + path: path + label: if page.label.len > 0 { page.label } else { page.title } + } + leaf_cat.items << nav_doc } - collapsible: category.collapsible - collapsed: category.collapsed - items: category_items } - result.my_sidebar << nav_cat } } // ============================================================ - // PASS 2: Add uncategorized pages at root level + // PASS 2: Add root-level categories to sidebar + // ============================================================ + for i, category in s.categories { + // Only add the root of each category tree to the sidebar. + // If a category path contains '/', it means it's a nested category, + // and its root (first part of the path) would have already been processed in PASS 1 + // and added to the category_tree. + // We should only add the top-level categories, or categories without '/', + // to the main sidebar. + if !category.path.contains('/') && category.path.len > 0 { + root_path := category.path + if root_path in category_tree { + result.my_sidebar << category_tree[root_path] + } + } + } + + // ============================================================ + // PASS 3: Add uncategorized pages at root level // ============================================================ for page in uncategorized_pages { if !page.hide { @@ -110,7 +155,7 @@ pub fn (mut s Site) sidebar() SideBar { } // ============================================================ - // PASS 3: Add standalone links (if needed) + // PASS 4: Add standalone links (if needed) // ============================================================ for link in s.links { nav_link := NavLink{