feat: add multi-site support and playbook enhancements

- Refactor `site` module to process multiple configurations
- Add environment variable templating for playbook actions
- Activate playbook actions for setting coderoot and params
- Improve docusaurus config with metadata fallbacks
- Fix docusaurus navbar generation when logo is not defined
This commit is contained in:
Mahmoud-Emad
2025-08-03 12:14:55 +03:00
parent 198a394be8
commit d747977185
11 changed files with 136 additions and 160 deletions

3
.gitignore vendored
View File

@@ -51,3 +51,6 @@ compile_summary.log
server
HTTP_REST_MCP_DEMO.md
MCP_HTTP_REST_IMPLEMENTATION_PLAN.md
.roo
.kilocode
.continue

View File

@@ -1,9 +0,0 @@
{
"chat.mcp.discovery.enabled": true,
"mcpServers": {
"VSCode": {
"type": "internal",
"tools": ["chat_send", "apply_edits", "read_file", "write_file", "get_file_tree"]
}
}
}

View File

@@ -1,3 +0,0 @@
{
"mcpServers": {}
}

View File

@@ -1,77 +0,0 @@
!!docusaurus.config
name:"my-documentation"
title:"My Documentation Site"
tagline:"Documentation made simple with V and Docusaurus"
url:"https://docs.example.com"
url_home:"docs/"
base_url:"/"
favicon:"img/favicon.png"
image:"img/hero.png"
copyright:"© 2025 Example Organization"
!!docusaurus.config_meta
description:"Comprehensive documentation for our amazing project"
image:"https://docs.example.com/img/social-card.png"
title:"My Documentation | Official Docs"
!!docusaurus.ssh_connection
name:"production"
host:"example.com"
login:"deploy"
port:22
key_path:"~/.ssh/id_rsa"
!!docusaurus.build_dest
ssh_name:"production"
path:"/var/www/docs"
!!docusaurus.navbar
title:"My Project"
!!docusaurus.navbar_item
label:"Documentation"
href:"/docs"
position:"left"
!!docusaurus.navbar_item
label:"API"
href:"/api"
position:"left"
!!docusaurus.navbar_item
label:"GitHub"
href:"https://github.com/example/repo"
position:"right"
!!docusaurus.footer
style:"dark"
!!docusaurus.footer_item
title:"Documentation"
label:"Introduction"
to:"/docs"
!!docusaurus.footer_item
title:"Documentation"
label:"API Reference"
to:"/api"
!!docusaurus.footer_item
title:"Community"
label:"GitHub"
href:"https://github.com/example/repo"
!!docusaurus.footer_item
title:"Community"
label:"Discord"
href:"https://discord.gg/example"
!!docusaurus.footer_item
title:"More"
label:"Blog"
href:"https://blog.example.com"
!!docusaurus.import_source
url:"https://github.com/example/external-docs"
dest:"external"
replace:"PROJECT_NAME:My Project, VERSION:1.0.0"

View File

@@ -4,6 +4,7 @@ module playcmds
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.data.doctree
import freeflowuniverse.herolib.biz.bizmodel
import freeflowuniverse.herolib.web.docusaurus
// import freeflowuniverse.herolib.hero.publishing
// import freeflowuniverse.herolib.threefold.grid4.gridsimulator
// import freeflowuniverse.herolib.installers.sysadmintools.daguserver
@@ -19,17 +20,17 @@ pub struct PlayArgs {
pub mut:
heroscript string
heroscript_path string
plbook ?playbook.PlayBook
plbook ?PlayBook
reset bool
}
pub fn run(args_ PlayArgs) ! {
mut args := args_
mut plbook := args.plbook or {
playbook.new(text: args.heroscript, path: args.heroscript_path)!
}
play_core(mut plbook)!
play_git(mut plbook)!
@@ -46,13 +47,11 @@ pub fn run(args_ PlayArgs) ! {
// plbook = gridsimulator.play(mut plbook)!
bizmodel.play(mut plbook)!
doctree.play(mut plbook)!
docusaurus.play(mut plbook)!
// slides.play(mut plbook)!
// base_install(play(mut plbook)!
// coredns.play(mut plbook)!
// plbook.empty_check()!
}

View File

@@ -3,6 +3,7 @@ module playcmds
import freeflowuniverse.herolib.develop.gittools
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.texttools
// !!context.configure
// name:'test'
@@ -76,35 +77,51 @@ fn play_core(mut plbook PlayBook) ! {
sitename := session.env_get('SITENAME') or { '' }
// for mut action in plbook.find(filter: 'core.coderoot_set')! {
// mut p := action.params
// if p.exists('coderoot') {
// coderoot := p.get_path_create('coderoot')!
// mut gs := session.context.gitstructure()!
// if gs.rootpath.path != coderoot {
// mut db := session.context.contextdb.db_get(dbname: 'context')!
// db.set('coderoot', coderoot)!
// session.context.gitstructure_reload()!
// }
// } else {
// return error('coderoot needs to be specified')
// }
// action.done = true
// }
// for mut action in plbook.find(filter: 'core.params_context_set')! {
// mut p := action.params
// for param in p.params {
// session.context.params.set(param.key, param.value)
// }
// action.done = true
// }
// for mut action in plbook.find(filter: 'core.params_session_set')! {
// mut p := action.params
// for param in p.params {
// session.params.set(param.key, param.value)
// }
// action.done = true
// }
// Apply template replacement from session environment variables
if session.env.len > 0 {
// Create a map with name_fix applied to keys for template replacement
mut env_fixed := map[string]string{}
for key, value in session.env {
env_fixed[texttools.name_fix(key)] = value
}
for mut action in plbook.actions {
if !action.done {
action.params.replace(env_fixed)
}
}
}
for mut action in plbook.find(filter: 'core.coderoot_set')! {
mut p := action.params
if p.exists('coderoot') {
coderoot := p.get_path_create('coderoot')!
if session.context.config.coderoot != coderoot {
session.context.config.coderoot = coderoot
session.context.save()!
}
} else {
return error('coderoot needs to be specified')
}
action.done = true
}
for mut action in plbook.find(filter: 'core.params_context_set')! {
mut p := action.params
mut context_params := session.context.params()!
for param in p.params {
context_params.set(param.key, param.value)
}
session.context.save()!
action.done = true
}
for mut action in plbook.find(filter: 'core.params_session_set')! {
mut p := action.params
for param in p.params {
session.params.set(param.key, param.value)
}
session.save()!
action.done = true
}
}

View File

@@ -3,9 +3,7 @@ module doctree
import freeflowuniverse.herolib.core.playbook { PlayBook }
// import freeflowuniverse.herolib.ui.console
pub fn play(mut plbook playbook.PlayBook) ! {
pub fn play(mut plbook PlayBook) ! {
mut doctrees := map[string]&Tree{}
collection_actions := plbook.find(filter: 'doctree.scan')!
@@ -27,7 +25,8 @@ pub fn play(mut plbook playbook.PlayBook) ! {
}
export_actions := plbook.find(filter: 'doctree.export')!
if export_actions.len == 0 {
if export_actions.len == 0 && collection_actions.len > 0 {
// Only auto-export if we have collections to export
name0 := 'main'
mut doctree0 := doctrees[name0] or { panic("can't find doctree with name ${name0}") }
doctree0.export()!
@@ -56,5 +55,4 @@ pub fn play(mut plbook playbook.PlayBook) ! {
// println(tree_list())
// println(tree_get("main")!)
// panic("sd")
}

View File

@@ -2,7 +2,6 @@ module docusaurus
import freeflowuniverse.herolib.web.site
pub struct Configuration {
pub mut:
main Main
@@ -73,6 +72,7 @@ pub mut:
href string @[omitempty]
to string @[omitempty]
}
// ... (struct definitions remain the same) ...
// This function is now a pure transformer: site.SiteConfig -> docusaurus.Configuration
@@ -114,9 +114,21 @@ pub fn new_configuration(site_cfg site.SiteConfig) !Configuration {
url_home: site_cfg.url_home
image: site_cfg.image
metadata: Metadata{
title: site_cfg.meta_title
description: site_cfg.description
image: site_cfg.meta_image
title: if site_cfg.meta_title == '' {
site_cfg.title
} else {
site_cfg.meta_title
}
description: if site_cfg.description == '' {
site_cfg.tagline
} else {
site_cfg.description
}
image: if site_cfg.meta_image == '' {
site_cfg.image
} else {
site_cfg.meta_image
}
}
build_dest: site_cfg.build_dest.map(it.path)
build_dest_dev: site_cfg.build_dest_dev.map(it.path)
@@ -141,6 +153,18 @@ pub fn new_configuration(site_cfg site.SiteConfig) !Configuration {
}
fn config_fix(config Configuration) !Configuration {
// Fix empty logo sources by removing logo entirely if all fields are empty
mut navbar_fixed := config.navbar
if config.navbar.logo.src == '' && config.navbar.logo.src_dark == ''
&& config.navbar.logo.alt == '' {
// Create navbar without logo if all logo fields are empty
navbar_fixed = Navbar{
title: config.navbar.title
items: config.navbar.items
// logo field omitted entirely
}
}
return Configuration{
...config
main: Main{
@@ -150,5 +174,6 @@ fn config_fix(config Configuration) !Configuration {
url: if config.main.url == '' { 'https://example.com' } else { config.main.url }
base_url: if config.main.base_url == '' { '/' } else { config.main.base_url }
}
} // ... (no changes needed here) ...
navbar: navbar_fixed
}
}

View File

@@ -1,13 +1,20 @@
module site
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.core.playbook { Action, PlayBook }
import freeflowuniverse.herolib.core.texttools
import time
pub fn play(mut plbook PlayBook) ! {
// Handle multiple site configurations
config_actions := plbook.find(filter: 'site.config')!
mut website := play_config(mut plbook)!
if config_actions.len == 0 {
return error('No site.config actions found')
}
// Process each site configuration separately
for config_action in config_actions {
mut website := play_config_single(config_action)!
mut config := &website.siteconfig
play_import(mut plbook, mut config)!
@@ -17,14 +24,38 @@ pub fn play(mut plbook PlayBook)! {
play_build_dest_dev(mut plbook, mut config)!
play_pages(mut plbook, mut website)!
}
}
fn play_config_single(action Action) !&Site {
mut p := action.params
name := p.get('name') or {
// If name is not specified, try to derive it from title or use a default
title := p.get_default('title', 'default-site')!
texttools.name_fix(title)
}
mut website := new(name: name)!
mut config := &website.siteconfig
config.title = p.get_default('title', config.title)!
config.description = p.get_default('description', config.description)!
config.tagline = p.get_default('tagline', config.tagline)!
config.favicon = p.get_default('favicon', config.favicon)!
config.image = p.get_default('image', config.image)!
config.copyright = p.get_default('copyright', config.copyright)!
config.url = p.get_default('url', config.url)!
config.base_url = p.get_default('base_url', config.base_url)!
config.url_home = p.get_default('url_home', config.url_home)!
return website
}
fn play_config(mut plbook PlayBook) !&Site {
mut action := plbook.get(filter: 'site.config')!
mut p := action.params
name := p.get('name') or {return error("need to specify name in site.config")}
name := p.get('name') or { return error('need to specify name in site.config') }
mut website := new(name: name)!
mut config := &website.siteconfig
@@ -180,4 +211,3 @@ fn play_build_dest_dev(mut plbook PlayBook, mut config SiteConfig) ! {
config.build_dest_dev << dest_dev
}
}

View File

@@ -1,7 +0,0 @@
2025-07-31 02:25:13.191 CEST [63643] LOG: starting PostgreSQL 17.5 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit
2025-07-31 02:25:13.192 CEST [63643] LOG: listening on IPv6 address "::1", port 5432
2025-07-31 02:25:13.192 CEST [63643] LOG: listening on IPv4 address "127.0.0.1", port 5432
2025-07-31 02:25:13.192 CEST [63643] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"
2025-07-31 02:25:13.194 CEST [63646] LOG: database system was shut down at 2025-07-31 02:24:30 CEST
2025-07-31 02:25:13.196 CEST [63643] LOG: database system is ready to accept connections
2025-07-31 02:25:18.216 CEST [63775] FATAL: database "despiegk" does not exist