feat: add docs landing page slug handling

- Add function to find first doc in sidebar
- Pass found doc ID to process_page
- Set slug: / for landing page in frontmatter
This commit is contained in:
Mahmoud-Emad
2025-12-01 11:54:02 +02:00
parent ed9ff35807
commit 8fc560ae78
17 changed files with 483 additions and 19 deletions

View File

@@ -6,11 +6,11 @@ import incubaid.herolib.web.site
pub struct Configuration {
pub mut:
main Main
navbar Navbar
footer Footer
sidebar_json_txt string //will hold the sidebar.json content
announcement AnnouncementBar
main Main
navbar Navbar
footer Footer
sidebar_json_txt string // will hold the sidebar.json content
announcement AnnouncementBar
}
pub struct Main {
@@ -86,9 +86,7 @@ pub mut:
is_closeable bool @[json: 'isCloseable']
}
// ... (struct definitions remain the same) ...
// This function is now a pure transformer: site.SiteConfig -> docusaurus.Configuration
// This function is a pure transformer: site.SiteConfig -> docusaurus.Configuration
fn new_configuration(mysite site.Site) !Configuration {
// Transform site.SiteConfig to docusaurus.Configuration
mut site_cfg := mysite.siteconfig
@@ -121,7 +119,7 @@ fn new_configuration(mysite site.Site) !Configuration {
sidebar_json_txt := mysite.nav.sidebar_to_json()!
cfg := Configuration{
main: Main{
main: Main{
title: site_cfg.title
tagline: site_cfg.tagline
favicon: site_cfg.favicon
@@ -151,7 +149,7 @@ fn new_configuration(mysite site.Site) !Configuration {
copyright: site_cfg.copyright
name: site_cfg.name
}
navbar: Navbar{
navbar: Navbar{
title: site_cfg.menu.title
logo: Logo{
alt: site_cfg.menu.logo_alt
@@ -160,11 +158,11 @@ fn new_configuration(mysite site.Site) !Configuration {
}
items: nav_items
}
footer: Footer{
footer: Footer{
style: site_cfg.footer.style
links: footer_links
}
announcement: AnnouncementBar{
announcement: AnnouncementBar{
// id: site_cfg.announcement.id
content: site_cfg.announcement.content
background_color: site_cfg.announcement.background_color

View File

@@ -11,6 +11,30 @@ import os
// Doc Linking - Generate Docusaurus docs from Atlas collections
// ============================================================================
// get_first_doc_from_sidebar recursively finds the first doc ID in the sidebar.
// Used to determine which page should get slug: / in frontmatter when url_home ends with "/".
fn get_first_doc_from_sidebar(items []site.NavItem) string {
for item in items {
match item {
site.NavDoc {
return site.extract_page_id(item.id)
}
site.NavCat {
// Recursively search in category items
doc := get_first_doc_from_sidebar(item.items)
if doc.len > 0 {
return doc
}
}
site.NavLink {
// Skip links, we want docs
continue
}
}
}
return ''
}
// link_docs generates markdown files from site page definitions.
// Pages are fetched from Atlas collections and written with frontmatter.
pub fn (mut docsite DocSite) link_docs() ! {
@@ -23,8 +47,15 @@ pub fn (mut docsite DocSite) link_docs() ! {
mut client := atlas_client.new(export_dir: c.atlas_dir)!
mut errors := []string{}
// Determine if we need to set a docs landing page (when url_home ends with "/")
first_doc_page := if docsite.website.siteconfig.url_home.ends_with('/') {
get_first_doc_from_sidebar(docsite.website.nav.my_sidebar)
} else {
''
}
for _, page in docsite.website.pages {
process_page(mut client, docs_path, page, mut errors)
process_page(mut client, docs_path, page, first_doc_page, mut errors)
}
if errors.len > 0 {
@@ -51,7 +82,7 @@ fn report_errors(mut client atlas_client.AtlasClient, errors []string) ! {
// Page Processing
// ============================================================================
fn process_page(mut client atlas_client.AtlasClient, docs_path string, page site.Page, mut errors []string) {
fn process_page(mut client atlas_client.AtlasClient, docs_path string, page site.Page, first_doc_page string, mut errors []string) {
collection, page_name := parse_page_src(page.src) or {
errors << err.msg()
return
@@ -62,7 +93,10 @@ fn process_page(mut client atlas_client.AtlasClient, docs_path string, page site
return
}
write_page(docs_path, page_name, page, content) or {
// Check if this page is the docs landing page
is_landing_page := first_doc_page.len > 0 && page_name == first_doc_page
write_page(docs_path, page_name, page, content, is_landing_page) or {
errors << "Failed to write page '${page_name}': ${err.msg()}"
return
}
@@ -79,8 +113,8 @@ fn parse_page_src(src string) !(string, string) {
return parts[0], parts[1]
}
fn write_page(docs_path string, page_name string, page site.Page, content string) ! {
frontmatter := build_frontmatter(page, content)
fn write_page(docs_path string, page_name string, page site.Page, content string, is_landing_page bool) ! {
frontmatter := build_frontmatter(page, content, is_landing_page)
final_content := frontmatter + '\n\n' + content
output_path := '${docs_path}/${page_name}.md'
@@ -97,7 +131,7 @@ fn copy_page_assets(mut client atlas_client.AtlasClient, docs_path string, colle
// Frontmatter Generation
// ============================================================================
fn build_frontmatter(page site.Page, content string) string {
fn build_frontmatter(page site.Page, content string, is_landing_page bool) string {
title := get_title(page, content)
description := get_description(page, title)
@@ -105,6 +139,11 @@ fn build_frontmatter(page site.Page, content string) string {
lines << "title: '${escape_yaml(title)}'"
lines << "description: '${escape_yaml(description)}'"
// Add slug: / for the docs landing page so /docs/ works directly
if is_landing_page {
lines << 'slug: /'
}
if page.draft {
lines << 'draft: true'
}

View File

@@ -0,0 +1,82 @@
# Docusaurus Link Resolution Test
This directory contains a comprehensive test for the herolib documentation linking mechanism.
## Structure
```
for_testing/
├── README.md # This file
├── collections/
│ └── test_collection/ # Markdown source files
│ ├── .collection # Collection metadata
│ ├── page1.md # Introduction
│ ├── page2.md # Basic Concepts
│ ├── page3.md # Configuration
│ ├── page4.md # Advanced Features
│ ├── page5.md # Troubleshooting
│ ├── page6.md # Best Practices
│ └── page7.md # Conclusion
└── ebooks/
└── test_site/ # Heroscript configuration
├── heroscriptall # Master configuration (entry point)
├── config.heroscript # Site configuration
├── pages.heroscript # Page definitions
└── docusaurus.heroscript # Docusaurus settings
```
## What This Tests
1. **Link Resolution** - Each page contains links using the `[text](collection:page)` format
2. **Navigation Chain** - Pages link sequentially: 1 → 2 → 3 → 4 → 5 → 6 → 7
3. **Sidebar Generation** - All 7 pages should appear in the sidebar
4. **Category Support** - Pages are organized into categories (root, basics, advanced, reference)
## Running the Test
From the herolib root directory:
```bash
# Build herolib first
./cli/compile.vsh
# Run the test site
/Users/mahmoud/hero/bin/hero docs -d -p lib/web/docusaurus/for_testing/ebooks/test_site
```
## Expected Results
When the test runs successfully:
1. ✅ All 7 pages are generated in `~/hero/var/docusaurus/build/docs/`
2. ✅ Sidebar shows all pages organized by category
3. ✅ Clicking navigation links works (page1 → page2 → ... → page7)
4. ✅ No broken links or 404 errors
5. ✅ Back-links also work (e.g., page7 → page1)
## Link Syntax Being Tested
```markdown
[Next Page](test_collection:page2)
```
This should resolve to a proper Docusaurus link when the site is built.
## Verification
After running the test:
1. Open http://localhost:3000/test/ in your browser
2. Click through all navigation links from Page 1 to Page 7
3. Verify the back-link on Page 7 returns to Page 1
4. Check the sidebar displays all pages correctly
## Troubleshooting
If links don't resolve:
1. Check that the collection is registered in the atlas
2. Verify page names match (no typos)
3. Run with debug flag (`-d`) to see detailed output
4. Check `~/hero/var/docusaurus/build/docs/` for generated files

View File

@@ -0,0 +1,3 @@
name: test_collection
description: Test collection for link resolution testing

View File

@@ -0,0 +1,21 @@
# Page 1: Introduction
Welcome to the documentation linking test. This page serves as the entry point for testing herolib's link resolution mechanism.
## Overview
This test suite consists of 7 interconnected pages that form a chain. Each page links to the next, demonstrating that the `collection:page_name` link syntax works correctly across multiple layers.
## What We're Testing
- Link resolution using `collection:page_name` format
- Proper generation of Docusaurus-compatible links
- Navigation chain integrity from page 1 through page 7
- Sidebar generation with all pages
## Navigation
Continue to the next section to learn about the basic concepts.
**Next:** [Page 2: Basic Concepts](test_collection:page2)

View File

@@ -0,0 +1,30 @@
# Page 2: Basic Concepts
This page covers the basic concepts of the documentation system.
## Link Syntax
In herolib, links between pages use the format:
```
[Link Text](collection_name:page_name)
```
For example, to link to `page3` in `test_collection`:
```markdown
[Go to Page 3](test_collection:page3)
```
## How It Works
1. The parser identifies links with the `collection:page` format
2. During site generation, these are resolved to actual file paths
3. Docusaurus receives properly formatted relative links
## Navigation
**Previous:** [Page 1: Introduction](test_collection:page1)
**Next:** [Page 3: Configuration](test_collection:page3)

View File

@@ -0,0 +1,39 @@
# Page 3: Configuration
This page explains configuration options for the documentation system.
## Site Configuration
The site is configured using heroscript files:
```heroscript
!!site.config
name:"test_site"
title:"Test Documentation"
base_url:"/test/"
url_home:"docs/page1"
```
## Page Definitions
Each page is defined using the `!!site.page` action:
```heroscript
!!site.page src:"test_collection:page1"
title:"Introduction"
```
## Important Settings
| Setting | Description |
|---------|-------------|
| `name` | Unique page identifier |
| `collection` | Source collection name |
| `title` | Display title in sidebar |
## Navigation
**Previous:** [Page 2: Basic Concepts](test_collection:page2)
**Next:** [Page 4: Advanced Features](test_collection:page4)

View File

@@ -0,0 +1,37 @@
# Page 4: Advanced Features
This page covers advanced features of the linking mechanism.
## Cross-Collection Links
You can link to pages in different collections:
```markdown
[Link to other collection](other_collection:some_page)
```
## Categories
Pages can be organized into categories:
```heroscript
!!site.page_category name:'advanced' label:"Advanced Topics"
!!site.page name:'page4' collection:'test_collection'
title:"Advanced Features"
```
## Multiple Link Formats
The system supports various link formats:
1. **Collection links:** `[text](collection:page)`
2. **Relative links:** `[text](./other_page.md)`
3. **External links:** `[text](https://example.com)`
## Navigation
**Previous:** [Page 3: Configuration](test_collection:page3)
**Next:** [Page 5: Troubleshooting](test_collection:page5)

View File

@@ -0,0 +1,43 @@
# Page 5: Troubleshooting
This page helps you troubleshoot common issues.
## Common Issues
### Broken Links
If links appear broken, check:
1. The collection name is correct
2. The page name matches the markdown filename (without `.md`)
3. The collection is properly registered in the atlas
### Page Not Found
Ensure the page is defined in your heroscript:
```heroscript
!!site.page name:'page5' collection:'test_collection'
title:"Troubleshooting"
```
## Debugging Tips
- Run with debug flag: `hero docs -d -p .`
- Check the generated `sidebar.json`
- Verify the docs output in `~/hero/var/docusaurus/build/docs/`
## Error Messages
| Error | Solution |
|-------|----------|
| "Page not found" | Check page name spelling |
| "Collection not found" | Verify atlas configuration |
| "Link resolution failed" | Check link syntax |
## Navigation
**Previous:** [Page 4: Advanced Features](test_collection:page4)
**Next:** [Page 6: Best Practices](test_collection:page6)

View File

@@ -0,0 +1,44 @@
# Page 6: Best Practices
This page outlines best practices for documentation.
## Naming Conventions
- Use lowercase for page names: `page_name.md`
- Use underscores for multi-word names: `my_long_page_name.md`
- Keep names short but descriptive
## Link Organization
### Do This ✓
```markdown
See the [configuration guide](test_collection:page3) for details.
```
### Avoid This ✗
```markdown
Click [here](test_collection:page3) for more.
```
## Documentation Structure
A well-organized documentation site should:
1. **Start with an introduction** - Explain what the documentation covers
2. **Progress logically** - Each page builds on the previous
3. **End with reference material** - API docs, troubleshooting, etc.
## Content Guidelines
- Keep paragraphs short
- Use code blocks for examples
- Include navigation links at the bottom of each page
## Navigation
**Previous:** [Page 5: Troubleshooting](test_collection:page5)
**Next:** [Page 7: Conclusion](test_collection:page7)

View File

@@ -0,0 +1,37 @@
# Page 7: Conclusion
Congratulations! You've reached the final page of the documentation linking test.
## Summary
This test suite demonstrated:
- ✅ Link resolution using `collection:page_name` format
- ✅ Navigation chain across 7 pages
- ✅ Proper sidebar generation
- ✅ Docusaurus-compatible output
## Test Verification
If you've reached this page by clicking through all the navigation links, the linking mechanism is working correctly!
### Link Chain Verified
1. [Page 1: Introduction](test_collection:page1) → Entry point
2. [Page 2: Basic Concepts](test_collection:page2) → Link syntax
3. [Page 3: Configuration](test_collection:page3) → Site setup
4. [Page 4: Advanced Features](test_collection:page4) → Cross-collection links
5. [Page 5: Troubleshooting](test_collection:page5) → Common issues
6. [Page 6: Best Practices](test_collection:page6) → Guidelines
7. **Page 7: Conclusion** → You are here!
## What's Next
You can now use the herolib documentation system with confidence that links will resolve correctly across your entire documentation site.
## Navigation
**Previous:** [Page 6: Best Practices](test_collection:page6)
**Back to Start:** [Page 1: Introduction](test_collection:page1)

View File

@@ -0,0 +1,16 @@
!!site.config
name:"test_site"
title:"Link Resolution Test"
tagline:"Testing herolib documentation linking mechanism"
url:"http://localhost:3000"
url_home:"docs/"
base_url:"/test/"
favicon:"img/favicon.png"
copyright:"© 2024 Herolib Test"
default_collection:"test_collection"
!!site.config_meta
description:"Test suite for verifying herolib documentation link resolution across multiple pages"
title:"Link Resolution Test - Herolib"
keywords:"herolib, docusaurus, testing, links, documentation"

View File

@@ -0,0 +1,4 @@
!!docusaurus.define name:'test_site'
!!atlas.export include:true

View File

@@ -0,0 +1,33 @@
// Navbar configuration
!!site.navbar
title:"Link Test"
!!site.navbar_item
label:"Documentation"
to:"docs/"
position:"left"
!!site.navbar_item
label:"GitHub"
href:"https://github.com/incubaid/herolib"
position:"right"
// Footer configuration
!!site.footer
style:"dark"
!!site.footer_item
title:"Docs"
label:"Introduction"
to:"docs/"
!!site.footer_item
title:"Docs"
label:"Configuration"
to:"docs/page3"
!!site.footer_item
title:"Community"
label:"GitHub"
href:"https://github.com/incubaid/herolib"

View File

@@ -0,0 +1,34 @@
// Page Definitions for Link Resolution Test
// Each page maps to a markdown file in the test_collection
// Root pages (no category)
!!site.page src:"test_collection:page1"
title:"Introduction"
!!site.page src:"page2"
title:"Basic Concepts"
// Basics category
!!site.page_category name:'basics' label:"Getting Started"
!!site.page src:"page3"
title:"Configuration"
!!site.page src:"page4"
title:"Advanced Features"
// Advanced category
!!site.page_category name:'advanced' label:"Advanced Topics"
!!site.page src:"page5"
title:"Troubleshooting"
!!site.page src:"page6"
title:"Best Practices"
// Reference category
!!site.page_category name:'reference' label:"Reference"
!!site.page src:"page7"
title:"Conclusion"

View File

@@ -0,0 +1,2 @@
!!atlas.scan path:"../../collections/test_collection"

View File

@@ -93,7 +93,9 @@ fn from_category(cat NavCat) SidebarItem {
}
}
fn extract_page_id(id string) string {
// extract_page_id extracts the page name from a "collection:page_name" format.
// If the id doesn't contain a colon, returns the id as-is.
pub fn extract_page_id(id string) string {
parts := id.split(':')
if parts.len == 2 {
return parts[1]