This commit is contained in:
2025-07-30 17:35:19 +02:00
parent 802f6e074d
commit bc163eb61d
30 changed files with 99 additions and 1706 deletions

View File

@@ -23,38 +23,21 @@ Key characteristics:
## Processing HeroScript in Vlang
HeroScript can be parsed into a `playbook.PlayBook` object, allowing structured access to actions and their parameters,
a good way how to do this as part of a module in a play.v file is shown below.
HeroScript can be parsed into a `playbook.PlayBook` object, allowing structured access to actions and their parameters, this is used in most of the herolib modules, it allows configuration or actions in a structured way.
```v
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.ui.console
@[params]
pub struct PlayArgs {
pub mut:
heroscript string
heroscript_path string
plbook ?PlayBook
reset bool
}
pub fn play(mut plbook PlayBook) ! {
pub fn play(args_ PlayArgs) ! {
mut args := args_
mut plbook := args.plbook or {
playbook.new(text: args.heroscript, path: args.heroscript_path)!
}
// Initialize Docusaurus site manager based on 'docusaurus.define' action
mut ds := new()!
if plbook.exists_once(filter: 'docusaurus.define') {
mut action := plbook.action_get(actor: 'docusaurus', name: 'define')!
mut action := plbook.get(filter: 'docusaurus.define')!
mut p := action.params
//example how we get parameters from the action see core_params.md for more details
ds = new(
path_publish: p.get_default('path_publish', '')!
path_build: p.get_default('path_build', '')!
path: p.get_default('path_publish', '')!
production: p.get_default_false('production')
update: p.get_default_false('update')
)!
}
@@ -62,15 +45,7 @@ pub fn play(args_ PlayArgs) ! {
actions := plbook.find(filter: 'docusaurus.add')!
for action in actions {
mut p := action.params
mut site := ds.get(
name: p.get_default('name', 'main')!
nameshort: p.get_default('nameshort', p.get_default('name', 'main')!)!
git_reset: p.get_default_false('git_reset')
//... more
)!
if plbook.exists_once(filter: 'docusaurus.dev') {
site.dev()!
}
//do more processing here
}
}
```

View File

@@ -0,0 +1,25 @@
# PlayBook
## get & execute a playbook
HeroScript can be parsed into a `playbook.PlayBook` object, allowing structured access to actions and their parameters.
```v
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.core.playcmds
// path string
// text string
// git_url string
// git_pull bool
// git_branch string
// git_reset bool
// session ?&base.Session is optional
mut plbook := playbook.new(path: "....")!
//now we run all the commands as they are pre-defined in herolib, this will execute the playbook and do all actions.
playcmds.run(mut plbook)!
```

View File

@@ -102,7 +102,7 @@ pub fn (mut plbook PlayBook) exists(args FindArgs) bool {
return res.len > 0
}
pub fn (mut plbook PlayBook) find_one(args FindArgs) !&Action {
pub fn (mut plbook PlayBook) get(args FindArgs) !&Action {
mut res := plbook.find(args)!
if res.len == 0 {
return error("can't find action: '${args.filter}'")
@@ -112,14 +112,6 @@ pub fn (mut plbook PlayBook) find_one(args FindArgs) !&Action {
return res[0] or { panic('bug') }
}
pub fn (mut plbook PlayBook) find_max_one(args FindArgs) ![]&Action {
mut res := plbook.find(args)!
if res.len > 1 {
return error("found more than one action: '${args.filter}'")
}
return res
}
fn (action Action) match_items(items []string) bool {
for p in items {
mut actor := ''

View File

@@ -113,71 +113,72 @@ pub fn (mut plbook PlayBook) names() ![]string {
return names
}
@[params]
pub struct ActionGetArgs {
pub mut:
id int
actor string
name string
actiontype ActionType = .sal
}
// @[params]
// pub struct ActionGetArgs {
// pub mut:
// id int
// actor string
// name string
// filter string
// actiontype ActionType = .sal
// }
// Find all actions based on ActionGetArgs
// - If id == 0, then matches all ids; when id is specified, can only return 1.
// - If actor == "", then matches all actors.
// - If name == "", then matches all actions from the defined actor (if defined).
// - If actiontype == .unknown, then matches all action types; when specified, filters by the action type, default .sal
pub fn (mut plbook PlayBook) actions_find(args ActionGetArgs) ![]&Action {
mut res := []&Action{}
for a in plbook.actions {
// If id is specified, return only the action with that id
if args.id != 0 {
if a.id == args.id {
return [a]
}
continue
}
// Filter by actor if specified
if args.actor.len > 0 && a.actor != args.actor {
continue
}
// Filter by name if specified
if args.name.len > 0 && a.name != args.name {
continue
}
// Filter by actiontype if specified
if args.actiontype != .unknown && a.actiontype != args.actiontype {
continue
}
// If the action passes all filters, add it to the result
res << a
}
return res
}
// // Find all actions based on ActionGetArgs
// // - If id == 0, then matches all ids; when id is specified, can only return 1.
// // - If actor == "", then matches all actors.
// // - If name == "", then matches all actions from the defined actor (if defined).
// // - If actiontype == .unknown, then matches all action types; when specified, filters by the action type, default .sal
// pub fn (mut plbook PlayBook) actions_find(args ActionGetArgs) ![]&Action {
// mut res := []&Action{}
// for a in plbook.actions {
// // If id is specified, return only the action with that id
// if args.id != 0 {
// if a.id == args.id {
// return [a]
// }
// continue
// }
// // Filter by actor if specified
// if args.actor.len > 0 && a.actor != args.actor {
// continue
// }
// // Filter by name if specified
// if args.name.len > 0 && a.name != args.name {
// continue
// }
// // Filter by actiontype if specified
// if args.actiontype != .unknown && a.actiontype != args.actiontype {
// continue
// }
// // If the action passes all filters, add it to the result
// res << a
// }
// return res
// }
pub fn (mut plbook PlayBook) action_exists(args ActionGetArgs) bool {
// Use actions_find to get the filtered actions
actions := plbook.actions_find(args) or { return false }
if actions.len == 1 {
return true
} else if actions.len == 0 {
return false
} else {
return false
}
}
// pub fn (mut plbook PlayBook) action_exists(args ActionGetArgs) bool {
// // Use actions_find to get the filtered actions
// actions := plbook.actions_find(args) or { return false }
// if actions.len == 1 {
// return true
// } else if actions.len == 0 {
// return false
// } else {
// return false
// }
// }
pub fn (mut plbook PlayBook) action_get(args ActionGetArgs) !&Action {
// Use actions_find to get the filtered actions
actions := plbook.actions_find(args)!
if actions.len == 1 {
return actions[0]
} else if actions.len == 0 {
return error("couldn't find action with args: ${args}")
} else {
return error('multiple actions found with args: ${args}, expected only one')
}
}
// pub fn (mut plbook PlayBook) action_get(args ActionGetArgs) !&Action {
// // Use actions_find to get the filtered actions
// actions := plbook.actions_find(args)!
// if actions.len == 1 {
// return actions[0]
// } else if actions.len == 0 {
// return error("couldn't find action with args: ${args}")
// } else {
// return error('multiple actions found with args: ${args}, expected only one')
// }
// }
pub fn (plbook PlayBook) hashkey() string {
mut out := []string{}

View File

@@ -1,8 +0,0 @@
!!hero_code.generate_client
name:'mdbook'
classname:'MDBooks'
singleton:0
default:1
hasconfig:1
reset:0

View File

@@ -1,49 +0,0 @@
// module mdbook
// import freeflowuniverse.herolib.core.base
// import os
// import crypto.md5
// @[params]
// pub struct Config {
// pub mut:
// path_build string = '${os.home_dir()}/hero/var/mdbuild'
// path_publish string = '${os.home_dir()}/hero/www/info'
// }
// // @[heap]
// // pub struct MDBooks {
// // pub:
// // cfg Config
// // }
// @[params]
// pub struct InitParams{
// action string
// name string
// }
// fn (cfg MDBooks) init(args InitParams) {
// }
// pub fn get(cfg_ Config) !MDBooks {
// mut c := base.context()!
// // lets get a unique name based on the used build and publishpaths
// mut cfg := cfg_
// cfg.path_build = cfg.path_build.replace('~', os.home_dir())
// cfg.path_publish = cfg.path_publish.replace('~', os.home_dir())
// mut name := md5.hexhash('${cfg.path_build}${cfg.path_publish}')
// mut myparams := c.params()!
// mut self := MDBooks{
// cfg: cfg
// }
// if myparams.exists('mdbookname') {
// name = myparams.get('mdbookname')!
// self.init('mdbook', name: name, .get, cfg)!
// } else {
// self.init('mdbook', name, .set, cfg)!
// myparams.set('mdbookname', name)
// }
// return self
// }

View File

@@ -1,37 +0,0 @@
module mdbook
import freeflowuniverse.herolib.develop.vscode
import freeflowuniverse.herolib.osal.core as osal
import freeflowuniverse.herolib.core.base
import os
pub fn book_open(name string) ! {
mut c := base.context()!
mut r := c.redis()!
mut path_publish := r.get('mdbook:${name}:publish')!
path_publish = path_publish.replace('~', os.home_dir())
if path_publish.len == 0 {
return error("can't find book: ${name}, was it generated before?")
}
if !os.exists(path_publish) {
return error("can't find generated book in ${path_publish}, was it generated properly.")
}
cmd3 := "open '${path_publish}/index.html'"
// console.print_debug(cmd3)
osal.exec(cmd: cmd3)!
}
pub fn book_edit(name string) ! {
mut c := base.context()!
mut r := c.redis()!
path_build := r.get('mdbook:${name}:build')!
if path_build.len == 0 {
return error("can't find book: ${name}, was it generated before?")
}
edit_path := '${path_build}/edit'.replace('~', os.home_dir())
if !os.exists(edit_path) {
return error("can't find book edit path in ${edit_path}, was it generated properly.")
}
vscode_helper := vscode.new(edit_path)
vscode_helper.open()!
}

View File

@@ -1,101 +0,0 @@
module mdbook
import v.embed_file
// import freeflowuniverse.herolib.installers.web.mdbook as mdbook_installer
// import freeflowuniverse.herolib.installers.web.imagemagick
import os
@[heap]
pub struct FileLoader {
pub mut:
embedded_files []embed_file.EmbedFileData @[skip; str: skip]
}
fn (mut loader FileLoader) load() ! {
loader.embedded_files << $embed_file('template/css/print.css')
loader.embedded_files << $embed_file('template/css/variables.css')
loader.embedded_files << $embed_file('template/css/general.css')
loader.embedded_files << $embed_file('template/mermaid-init.js')
loader.embedded_files << $embed_file('template/echarts.min.js')
loader.embedded_files << $embed_file('template/mermaid.min.js')
}
fn loader() !FileLoader {
if 'OFFLINE' !in os.environ() {
// console.print_debug(" - CHECK INSTALLER")
// mdbook_installer.install()!
// imagemagick.install()!
}
mut loader := FileLoader{}
return loader
}
// if true {
// panic('generate')
// }
// now we have to reset the rev keys, so we remember current status
// for key, mut status in self.gitrepos_status {
// osal.done_set('mdbookrev_${key}', status.revnew)!
// status.revlast = status.revnew
// }
// pub struct RepoStatus {
// pub mut:
// revlast string
// revnew string
// }
// make sure all intial states for the revisions are reset
// fn (mut self MDBooksFactory) reset_state() ! {
// for key, mut status in self.gitrepos_status {
// osal.done_set('mdbookrev_${key}', '')!
// status.revlast = ''
// }
// }
// get all content
// pub fn (mut self MDBooksFactory) pull(reset bool) ! {
// console.print_header(' pull mdbooks')
// print_backtrace()
// self.init()!
// for key, repo_ in self.gitrepos {
// mut repo := repo_
// if reset {
// repo.pull_reset(reload: true)! // need to overwrite all changes
// } else {
// repo.pull(reload: true)! // will not overwrite changes
// }
// revnew := repo.rev()!
// lastrev := osal.done_get('mdbookrev_${key}') or { '' }
// self.gitrepos_status[key] = RepoStatus{
// revnew: revnew
// revlast: lastrev
// }
// }
// }
// @[params]
// pub struct WatchArgs {
// pub mut:
// period int = 300 // 5 min default
// reset bool
// }
// pub fn (mut self MDBooksFactory) watch(args WatchArgs) {
// mut t := ourtime.now()
// mut last := i64(0)
// for {
// t.now()
// console.print_stdout('${t} ${t.unix()} period:${args.period}')
// if t.unix() > last + args.period {
// console.print_header(' will try to check the mdbooks')
// self.pull(args.reset) or { panic(" - ERROR: couldn't pull the repo's.\n${err}") }
// self.generate() or { panic(" - ERROR: couldn't generate the repo's.\n${err}") }
// last = t.unix()
// }
// time.sleep(time.second)
// if args.period == 0 {
// return
// }
// }
// }

View File

@@ -1,296 +0,0 @@
module mdbook
import freeflowuniverse.herolib.osal.core as osal
import os
import freeflowuniverse.herolib.data.doctree.collection
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.develop.gittools
@[heap]
pub struct MDBook {
pub mut:
name string
books &MDBooks @[skip; str: skip]
path_build pathlib.Path
path_publish pathlib.Path
args MDBookArgs
errors []collection.CollectionError
}
@[params]
pub struct MDBookArgs {
pub mut:
name string @[required]
title string
foldlevel int
printbook bool
// summary_url string // url of the summary.md file
summary_path string // can also give the path to the summary file (can be the dir or the summary itself)
// doctree_url string
// doctree_path string
publish_path string
build_path string
production bool
collections []string
description string
export bool // whether mdbook should be built
}
pub fn (mut books MDBooks) generate(args_ MDBookArgs) !&MDBook {
console.print_header(' mdbook: ${args_.name}')
mut args := args_
if args.title == '' {
args.title = args.name
}
if args.build_path.len == 0 {
args.build_path = '${books.path_build}/${args.name}'
}
if args.publish_path.len == 0 {
args.publish_path = '${books.path_publish}/${args.name}'
}
mut mycontext := base.context()!
mut r := mycontext.redis()!
r.set('mdbook:${args.name}:build', args.build_path)!
r.set('mdbook:${args.name}:publish', args.publish_path)!
r.expire('mdbook:${args.name}:build', 3600 * 12)! // expire after 12h
r.expire('mdbook:${args.name}:publish', 3600 * 12)!
_ := gittools.get()!
mut src_path := pathlib.get_dir(path: '${args.build_path}/src', create: true)!
_ := pathlib.get_dir(path: '${args.build_path}/.edit', create: true)!
mut collection_set := map[string]bool{}
for col_path in args.collections {
// link collections from col_path to src
mut p := pathlib.get_dir(path: col_path)!
_ := p.list(dirs_only: true, recursive: false)!
if _ := collection_set[p.name()] {
return error('collection with name ${p.name()} already exists')
}
p.link('${src_path.path}/${p.name()}', true)!
// QUESTION: why was this ever implemented per entry?
// for mut entry in entries.paths {
// if _ := collection_set[entry.name()] {
// println('collection with name ${entry.name()} already exists')
// // return error('collection with name ${entry.name()} already exists')
// }
// collection_set[entry.name()] = true
// entry.link('${src_path.path}/${entry.name()}', true)!
// }
}
mut book := MDBook{
args: args
path_build: pathlib.get_dir(path: args.build_path, create: true)!
path_publish: pathlib.get_dir(path: args.publish_path, create: true)!
books: &books
}
mut summary := book.summary(args_.production)!
mut dir_list := src_path.list(dirs_only: true, include_links: true, recursive: false)!
for mut collection_dir_path in dir_list.paths {
collectionname := collection_dir_path.path.all_after_last('/')
// should always work because done in summary
// check if there are errors, if yes add to summary
if os.exists('${collection_dir_path.path}/errors.md') {
summary.add_error_page(collectionname, 'errors.md')
}
// now link the exported collection into the build dir
collection_dirbuild_str := '${book.path_build.path}/src/${collectionname}'.replace('~',
os.home_dir())
if !pathlib.path_equal(collection_dirbuild_str, collection_dir_path.path) {
collection_dir_path.link(collection_dirbuild_str, true)!
}
if !os.exists('${collection_dir_path.path}/.linkedpages') {
continue
}
mut linked_pages := pathlib.get_file(path: '${collection_dir_path.path}/.linkedpages')!
lpagescontent := linked_pages.read()!
for lpagestr in lpagescontent.split_into_lines().filter(it.trim_space() != '') {
// console.print_green('find linked page: ${lpagestr}')
// format $collection:$pagename.md
splitted := lpagestr.split(':')
assert splitted.len == 2
summary.add_page_additional(splitted[0], splitted[1])
}
// if collection_dir_path.file_exists('.linkedpages') {
// mut lpages := collection_dir_path.file_get('.linkedpages')!
// lpagescontent := lpages.read()!
// for lpagestr in lpagescontent.split_into_lines().filter(it.trim_space() != '') {
// // console.print_green('find linked page: ${lpagestr}')
// // format $collection:$pagename.md
// splitted := lpagestr.split(':')
// assert splitted.len == 2
// summary.add_page_additional(splitted[0], splitted[1])
// }
// }
}
// create the additional collection (is a system collection)
addpath := '${book.path_build.path}/src/additional'
mut a := pathlib.get_file(path: '${addpath}/additional.md', create: true)!
mut b := pathlib.get_file(path: '${addpath}/errors.md', create: true)!
mut c := pathlib.get_file(path: '${addpath}/pages.md', create: true)!
a.write('
# Additional pages
A normal user can ignore these pages, they are for the authors to see e.g. errors
')!
b.write('
# Errors
Be the mother for our errors.
')!
c.write('
# Additional pages
You can ignore these pages, they are just to get links to work.
')!
if book.errors.len > 0 {
book.errors_report()!
summary.errors << SummaryItem{
level: 2
description: 'errors mdbook'
pagename: 'errors_mdbook.md'
collection: 'additional'
}
}
path_summary_str := '${book.path_build.path}/src/SUMMARY.md'
mut path_summary := pathlib.get_file(path: path_summary_str, create: true)!
path_summary.write(summary.str())!
book.template_install()!
if args.export {
book.generate()!
}
console.print_header(' mdbook prepared: ${book.path_build.path}')
return &book
}
// write errors.md in the collection, this allows us to see what the errors are
fn (book MDBook) errors_report() ! {
errors_path_str := '${book.path_build.path}/src/additional/errors_mdbook.md'
mut dest := pathlib.get_file(path: errors_path_str, create: true)!
if book.errors.len == 0 {
dest.delete()!
return
}
c := $tmpl('template/errors.md')
dest.write(c)!
}
@[params]
pub struct ErrorArgs {
pub mut:
path string
msg string
cat collection.CollectionErrorCat
}
pub fn (mut book MDBook) error(args ErrorArgs) {
path2 := pathlib.get(args.path)
e := collection.CollectionError{
path: path2
msg: args.msg
cat: args.cat
}
book.errors << e
console.print_stderr(args.msg)
}
pub fn (mut book MDBook) open() ! {
console.print_header('open book: ${book.name}')
cmd := 'open \'${book.path_publish.path}/index.html\''
// console.print_debug(cmd)
// cmd:='bash ${book.path_build.path}/develop.sh'
osal.exec(cmd: cmd)!
}
pub fn (mut book MDBook) generate() ! {
console.print_header(' book generate: ${book.name} on ${book.path_build.path}')
book.summary_image_set()!
osal.exec(
cmd: '
cd ${book.path_build.path}
mdbook build --dest-dir ${book.path_publish.path}
'
retry: 0
)!
}
fn (mut book MDBook) template_install() ! {
// get embedded files to the mdbook dir
// console.print_debug(book.str())
mut l := loader()!
l.load()!
for item in l.embedded_files {
dpath := '${book.path_build.path}/${item.path.all_after_first('/')}'
// console.print_debug(' embed: ${dpath}')
mut dpatho := pathlib.get_file(path: dpath, create: true)!
dpatho.write(item.to_string())!
}
c := $tmpl('template/book.toml')
mut tomlfile := book.path_build.file_get_new('book.toml')!
tomlfile.write(c)!
c1 := $tmpl('template/build.sh')
mut file1 := book.path_build.file_get_new('build.sh')!
file1.write(c1)!
file1.chmod(0o770)!
c2 := $tmpl('template/develop.sh')
mut file2 := book.path_build.file_get_new('develop.sh')!
file2.write(c2)!
file2.chmod(0o770)!
}
fn (mut book MDBook) summary_image_set() ! {
// this is needed to link the first image dir in the summary to the src, otherwise empty home image
summaryfile := '${book.path_build.path}/src/SUMMARY.md'
mut p := pathlib.get_file(path: summaryfile)!
c := p.read()!
mut first := true
for line in c.split_into_lines() {
if !(line.trim_space().starts_with('-')) {
continue
}
if line.contains('](') && first {
folder_first := line.all_after('](').all_before_last(')')
folder_first_dir_img := '${book.path_build.path}/src/${folder_first.all_before_last('/')}/img'
// console.print_debug(folder_first_dir_img)
// if true{panic("s")}
if os.exists(folder_first_dir_img) {
mut image_dir := pathlib.get_dir(path: folder_first_dir_img)!
image_dir.link('${book.path_build.path}/src/img', true)!
}
first = false
}
}
}

View File

@@ -1,102 +0,0 @@
module mdbook
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.ui.console
__global (
mdbook_global map[string]&MDBooks
mdbook_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string
}
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&MDBooks {
mut context := base.context()!
mut args := args_get(args_)
mut obj := MDBooks{
name: args.name
}
if args.name !in mdbook_global {
if !exists(args)! {
set(obj)!
} else {
heroscript := context.hero_config_get('mdbook', args.name)!
mut obj_ := heroscript_loads(heroscript)!
set_in_mem(obj_)!
}
}
return mdbook_global[args.name] or {
println(mdbook_global)
// bug if we get here because should be in globals
panic('could not get config for mdbook with name, is bug:${args.name}')
}
}
// register the config for the future
pub fn set(o MDBooks) ! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set('mdbook', o.name, heroscript)!
}
// does the config exists?
pub fn exists(args_ ArgsGet) !bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists('mdbook', args.name)
}
pub fn delete(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_delete('mdbook', args.name)!
if args.name in mdbook_global {
// del mdbook_global[args.name]
}
}
// only sets in mem, does not set as config
fn set_in_mem(o MDBooks) ! {
mut o2 := obj_init(o)!
mdbook_global[o.name] = &o2
mdbook_default = o.name
}
pub fn play(mut plbook PlayBook) ! {
mut install_actions := plbook.find(filter: 'mdbook.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript := install_action.heroscript()
mut obj2 := heroscript_loads(heroscript)!
set(obj2)!
}
}
}
// switch instance to be used for mdbook
pub fn switch(name string) {
mdbook_default = name
}
// helpers
@[params]
pub struct DefaultConfigArgs {
instance string = 'default'
}

View File

@@ -1,66 +0,0 @@
module mdbook
import freeflowuniverse.herolib.data.paramsparser
import os
pub const version = '1.14.3'
const singleton = false
const default = true
// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT
pub fn heroscript_default() !string {
heroscript := "
!!mdbook.configure
name:'mdbook'
mail_from: 'info@example.com'
mail_password: 'secretpassword'
mail_port: 587
mail_server: 'smtp-relay.brevo.com'
mail_username: 'kristof@incubaid.com'
"
// mail_from := os.getenv_opt('MAIL_FROM') or {'info@example.com'}
// mail_password := os.getenv_opt('MAIL_PASSWORD') or {'secretpassword'}
// mail_port := (os.getenv_opt('MAIL_PORT') or {"587"}).int()
// mail_server := os.getenv_opt('MAIL_SERVER') or {'smtp-relay.brevo.com'}
// mail_username := os.getenv_opt('MAIL_USERNAME') or {'kristof@incubaid.com'}
//
// heroscript:="
// !!mailclient.configure name:'default'
// mail_from: '${mail_from}'
// mail_password: '${mail_password}'
// mail_port: ${mail_port}
// mail_server: '${mail_server}'
// mail_username: '${mail_username}'
//
// "
//
return heroscript
}
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
@[heap]
pub struct MDBooks {
pub mut:
name string
path_build string
path_publish string
}
fn cfg_play(p paramsparser.Params) ! {
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
mut mycfg := MDBooks{
name: p.get_default('name', 'default')!
path_build: p.get_default('path_build', '${os.home_dir()}/hero/var/mdbuild')!
path_publish: p.get_default('path_publish', '${os.home_dir()}/hero/www/info')!
}
set(mycfg)!
}
fn obj_init(obj_ MDBooks) !MDBooks {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}

View File

@@ -1,32 +0,0 @@
# mdbook
To get started
```vlang
import freeflowuniverse.herolib.clients. mdbook
mut client:= mdbook.get()!
client...
```
## example heroscript
```hero
!!mdbook.configure
secret: '...'
host: 'localhost'
port: 8888
```

View File

@@ -1,222 +0,0 @@
module mdbook
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.texttools
import os
@[heap]
pub struct Summary {
pub mut:
items []SummaryItem
errors []SummaryItem // means we found errors, so we need to add to summary
addpages []SummaryItem // means we found pages as links, so we need to add them to the summary
collections []string
production bool
}
pub struct SummaryItem {
pub mut:
level int
description string
relpath string // relative path of summary item to source
collection string
pagename string
}
pub fn (mut book MDBook) summary(production bool) !Summary {
if !os.exists(book.args.summary_path) {
panic("summary file ${book.args.summary_path} doesn't exist")
}
mut summary := Summary{
production: production
}
mut summary_path := pathlib.get_file(path: book.args.summary_path, create: false)!
c := summary_path.read()!
summary_path.link('${book.path_build.path}/edit/summary.md', true)!
mut level := 0
mut ident := 2
for mut line in c.split_into_lines() {
if !(line.trim_space().starts_with('-')) {
continue
}
pre := line.all_before('-')
level = int(pre.len / ident)
// console.print_debug("${line} === '${pre}' ${level}")
line = line.trim_left(' -')
// - [Dunia Yetu](dy_intro/dunia_yetu/dunia_yetu.md)
// - [About Us](dy_intro/dunia_yetu/about_us.md)
if !line.starts_with('[') {
book.error(msg: "syntax error in summary: '${line}', needs to start with [")
continue
}
if !line.contains('](') {
book.error(msg: "syntax error in summary: '${line}', needs to have ](")
continue
}
description := line.all_after_first('[').all_before(']').trim_space()
path := line.all_after_last('(').all_before_last(')').trim_space()
if !path.contains('/') {
book.error(
msg: "syntax error in summary: '${line}', doesn't contain collectionname (is first element of path)"
)
continue
}
pagename := texttools.name_fix(path.all_after_last('/'))
collection := texttools.name_fix(path.all_before('/'))
if collection !in summary.collections {
summary.collections << collection
}
mut path_collection_str := '${book.args.build_path}/src/${collection}'.replace('~',
os.home_dir())
mut path_collection := pathlib.get_dir(path: path_collection_str, create: false) or {
book.error(
msg: "collection find error in summary: '${line}', can't find collection: ${path_collection_str} "
)
continue
}
list := path_collection.list()!
file_path_ := list.paths.filter(it.name() == pagename)
if file_path_.len == 0 {
book.error(
msg: "page find error in summary: '${line}', can't find page: ${pagename} in collection: ${path_collection_str}\n${file_path_} doesnt exist"
)
continue
} else if file_path_.len > 1 {
book.error(msg: 'duplicate page in collection: ${pagename}')
continue
}
file_path := file_path_[0].path
if !os.exists(file_path) || !os.is_file(file_path) {
book.error(
msg: "page find error in summary: '${line}', can't find page: ${pagename} in collection: ${path_collection_str}\n${file_path} doesnt exist"
)
continue
}
// NOTE: using this returns false because path_collection is a link
// if !path_collection.file_exists(pagename) {
// book.error(
// msg: "page find error in summary: '${line}', can't find page: ${pagename} in collection: ${path_collection_str}"
// )
// continue
// }
summary.items << SummaryItem{
level: level
description: description
pagename: pagename
relpath: file_path.all_after('${book.args.build_path}/src/') // relative path of page to src dir
collection: collection
}
}
return summary
}
fn (mut self Summary) add_error_page(collectionname string, pagename string) {
description := 'errors ${collectionname}'
self.errors << SummaryItem{
level: 2
description: description
pagename: pagename
collection: collectionname
}
}
fn (mut self Summary) is_in_summary(collection_name_ string, page_name_ string) bool {
mut collection_name := texttools.name_fix(collection_name_).to_lower()
mut page_name := texttools.name_fix(page_name_).to_lower()
if !(page_name.ends_with('.md')) {
page_name += '.md'
}
for i in self.items {
mut pname := texttools.name_fix(i.pagename.to_lower())
if !(pname.ends_with('.md')) {
pname += '.md'
}
if i.collection.to_lower() == collection_name && pname == page_name {
return true
}
}
for i in self.addpages {
mut pname := texttools.name_fix(i.pagename.to_lower())
if !(pname.ends_with('.md')) {
pname += '.md'
}
if i.collection.to_lower() == collection_name && pname == page_name {
return true
}
}
return false
}
fn (mut self Summary) add_page_additional(collectionname string, pagename string) {
if self.is_in_summary(collectionname, pagename) {
return
}
shortname := pagename.all_before_last('.').to_lower()
description := '${shortname}'
self.addpages << SummaryItem{
level: 2
description: description
pagename: pagename
collection: collectionname
}
}
pub fn (mut self Summary) str() string {
mut out := []string{}
for item in self.items {
mut pre := ''
for _ in 0 .. item.level {
pre += ' '
}
out << '${pre}- [${item.description}](${item.relpath})'
}
if self.addpages.len > 0 || (!self.production && self.errors.len > 0) {
out << '- [_](additional/additional.md)'
}
if self.addpages.len > 0 {
out << ' - [unlisted_pages](additional/pages.md)'
for item in self.addpages {
mut pre := ''
for _ in 0 .. item.level {
pre += ' '
}
out << '${pre}- [${item.description}](${item.collection}/${item.pagename})'
}
}
if !self.production && self.errors.len > 0 {
out << ' - [errors](additional/errors.md)'
for item in self.errors {
mut pre := ''
for _ in 0 .. item.level {
pre += ' '
}
out << '${pre}- [${item.description}](${item.collection}/${item.pagename})'
}
}
return out.join_lines()
}

View File

@@ -1,70 +0,0 @@
[book]
authors = []
language = "en"
multilingual = false
src = "src"
title = "@book.args.title"
[output.html.fold]
enable = true
level = @book.args.foldlevel
[output]
[output.html]
no-section-label = true
additional-css = ["css/general.css"]
additional-js = ["mermaid.min.js", "mermaid-init.js", "echarts.min.js"]
default-theme = "light"
mathjax-support = true
preserve-inline-html = true
[build]
create-missing = false
[output.html.print]
enable = @book.args.printbook
[preprocessor]
[preprocessor.mermaid]
command = "mdbook-mermaid"
# [preprocessor.last-changed]
# command = "mdbook-last-changed"
# renderer = ["html"]
#use as mdbook preprocessor
[preprocessor.echarts]
# [preprocessor.embed]
[preprocessor.kroki-preprocessor]
# [output.linkcheck]
# follow-web-links = true
# traverse-parent-directories = true
# warning-policy = "warn"
# [output.pdf]
# landscape = false
# display-header-footer = true
# print-background = true
# theme = "ayu"
# # scale = 0.7
# paper-width = 8.3
# paper-height = 11.7
# margin-top = 0.5
# margin-bottom = 0.5
# margin-left = 0.5
# margin-right = 0.5
# page-ranges = ""
# ignore-invalid-page-ranges = false
# header-template = "<h3 style='font-size:8px; margin-left: 48%' class='title'></h3>"
# footer-template = "<p style='font-size:10px; margin-left: 48%'><span class='pageNumber'></span> / <span class='totalPages'></span></p>"
# prefer-css-page-size = false
# [preprocessor.plantuml]
# plantuml-cmd="http://localhost:8080/plantuml"
# # plantuml-cmd="http://www.plantuml.com/plantuml"
# use-data-uris=true

View File

@@ -1,3 +0,0 @@
set -e
cd ${book.path_build.path}
mdbook build --dest-dir ${book.path_publish.path}

View File

@@ -1,208 +0,0 @@
/* Base styles and content styles */
@import 'variables.css';
:root {
/* Browser default font-size is 16px, this way 1 rem = 10px */
font-size: 62.5%;
color-scheme: var(--color-scheme);
}
html {
font-family: "Open Sans", sans-serif;
color: var(--fg);
background-color: var(--bg);
text-size-adjust: none;
-webkit-text-size-adjust: none;
}
body {
margin: 0;
font-size: 1.6rem;
overflow-x: hidden;
}
code {
font-family: var(--mono-font) !important;
font-size: var(--code-font-size);
}
/* make long words/inline code not x overflow */
main {
overflow-wrap: break-word;
}
/* make wide tables scroll if they overflow */
.table-wrapper {
overflow-x: auto;
text-align: left;
}
/* Don't change font size in headers. */
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
font-size: unset;
}
.left { float: left; }
.right { float: right; }
.boring { opacity: 0.6; }
.hide-boring .boring { display: none; }
.hidden { display: none !important; }
h2, h3 { margin-top: 2.5em; }
h4, h5 { margin-top: 2em; }
.header + .header h3,
.header + .header h4,
.header + .header h5 {
margin-top: 1em;
}
h1:target::before,
h2:target::before,
h3:target::before,
h4:target::before,
h5:target::before,
h6:target::before {
display: inline-block;
content: "»";
margin-left: -30px;
width: 30px;
}
/* This is broken on Safari as of version 14, but is fixed
in Safari Technology Preview 117 which I think will be Safari 14.2.
https://bugs.webkit.org/show_bug.cgi?id=218076
*/
:target {
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
}
.page {
outline: 0;
padding: 0 var(--page-padding);
margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
}
.page-wrapper {
box-sizing: border-box;
}
.js:not(.sidebar-resizing) .page-wrapper {
transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
}
.content {
overflow-y: auto;
padding: 0 5px 5px 5px;
}
.content main {
margin-left: 20;
margin-right: auto;
max-width: 1000px;
}
.content p { line-height: 1.45em; }
.content ol { line-height: 1.45em; }
.content ul { line-height: 1.45em; }
.content a { text-decoration: none; }
.content a:hover { text-decoration: underline; }
.content img, .content video { max-width: 100%; }
.content .header:link,
.content .header:visited {
color: var(--fg);
}
.content .header:link,
.content .header:visited:hover {
text-decoration: none;
}
table {
margin: 0 auto;
margin-left: 0;
margin-right: 0;
border-collapse: collapse;
width: 100%;
}
table td {
padding: 3px 20px;
border: 1px var(--table-border-color) solid;
}
table thead {
background: var(--table-header-bg);
}
table thead td {
font-weight: 700;
border: none;
}
table thead th {
padding: 3px 20px;
}
table thead tr {
border: 1px var(--table-header-bg) solid;
}
/* Alternate background colors for rows */
table tbody tr:nth-child(2n) {
background: var(--table-alternate-bg);
}
blockquote {
margin: 20px 0;
padding: 0 20px;
color: var(--fg);
background-color: var(--quote-bg);
border-top: .1em solid var(--quote-border);
border-bottom: .1em solid var(--quote-border);
}
kbd {
background-color: var(--table-border-color);
border-radius: 4px;
border: solid 1px var(--theme-popup-border);
box-shadow: inset 0 -1px 0 var(--theme-hover);
display: inline-block;
font-size: var(--code-font-size);
font-family: var(--mono-font);
line-height: 10px;
padding: 4px 5px;
vertical-align: middle;
}
:not(.footnote-definition) + .footnote-definition,
.footnote-definition + :not(.footnote-definition) {
margin-top: 2em;
}
.footnote-definition {
font-size: 0.9em;
margin: 0.5em 0;
}
.footnote-definition p {
display: inline;
}
.tooltiptext {
position: absolute;
visibility: hidden;
color: #fff;
background-color: #333;
transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
left: -8px; /* Half of the width of the icon */
top: -35px;
font-size: 0.8em;
text-align: center;
border-radius: 6px;
padding: 5px 8px;
margin: 5px;
z-index: 1000;
}
.tooltipped .tooltiptext {
visibility: visible;
}
.chapter li.part-title {
color: var(--sidebar-fg);
margin: 5px 0px;
font-weight: bold;
}
.result-no-output {
font-style: italic;
}

View File

@@ -1,71 +0,0 @@
#sidebar,
#menu-bar,
.nav-collections,
.mobile-nav-collections {
display: none;
}
#page-wrapper.page-wrapper {
transform: none;
margin-left: 0px;
overflow-y: initial;
}
#content {
max-width: none;
margin: 0;
padding: 0;
}
.page {
overflow-y: initial;
}
code {
background-color: #666666;
border-radius: 5px;
/* Force background to be printed in Chrome */
-webkit-print-color-adjust: exact;
}
pre > .buttons {
z-index: 2;
}
a, a:visited, a:active, a:hover {
color: #4183c4;
text-decoration: none;
}
h1, h2 {
page-break-inside: avoid;
page-break-after: avoid;
}
h3, h4, h5, h6 {
page-break-inside: avoid;
/* page-break-before: avoid; */
page-break-after: avoid;
}
pre, code {
page-break-inside: avoid;
white-space: pre-wrap;
}
.fa {
display: none !important;
}
body {
font-size: 1.25rem !important;
}
@@media print {
h1 {
page-break-before: always;
}
}

View File

@@ -1,265 +0,0 @@
/* Globals */
:root {
--sidebar-width: 250px;
--page-padding: 15px;
--content-max-width: 1600px;
--menu-bar-height: 50px;
--mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
--code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */
}
/* Themes */
.ayu {
--bg: hsl(210, 25%, 8%);
--fg: #c5c5c5;
--sidebar-bg: #14191f;
--sidebar-fg: #c8c9db;
--sidebar-non-existant: #5c6773;
--sidebar-active: #ffb454;
--sidebar-spacer: #2d334f;
--scrollbar: var(--sidebar-fg);
--icons: #737480;
--icons-hover: #b7b9cc;
--links: #0096cf;
--inline-code-color: #ffb454;
--theme-popup-bg: #14191f;
--theme-popup-border: #5c6773;
--theme-hover: #191f26;
--quote-bg: hsl(226, 15%, 17%);
--quote-border: hsl(226, 15%, 22%);
--table-border-color: hsl(210, 25%, 13%);
--table-header-bg: hsl(210, 25%, 28%);
--table-alternate-bg: hsl(210, 25%, 11%);
--searchbar-border-color: #848484;
--searchbar-bg: #424242;
--searchbar-fg: #fff;
--searchbar-shadow-color: #d4c89f;
--searchresults-header-fg: #666;
--searchresults-border-color: #888;
--searchresults-li-bg: #252932;
--search-mark-bg: #e3b171;
--color-scheme: dark;
}
.coal {
--bg: hsl(200, 7%, 8%);
--fg: #98a3ad;
--sidebar-bg: #292c2f;
--sidebar-fg: #a1adb8;
--sidebar-non-existant: #505254;
--sidebar-active: #3473ad;
--sidebar-spacer: #393939;
--scrollbar: var(--sidebar-fg);
--icons: #43484d;
--icons-hover: #b3c0cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
--theme-hover: #1f2124;
--quote-bg: hsl(234, 21%, 18%);
--quote-border: hsl(234, 21%, 23%);
--table-border-color: hsl(200, 7%, 13%);
--table-header-bg: hsl(200, 7%, 28%);
--table-alternate-bg: hsl(200, 7%, 11%);
--searchbar-border-color: #aaa;
--searchbar-bg: #b7b7b7;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #98a3ad;
--searchresults-li-bg: #2b2b2f;
--search-mark-bg: #355c7d;
--color-scheme: dark;
}
.light {
--bg: hsl(0, 0%, 100%);
--fg: hsl(0, 0%, 0%);
--sidebar-bg: #fafafa;
--sidebar-fg: hsl(0, 0%, 0%);
--sidebar-non-existant: #aaaaaa;
--sidebar-active: #1f1fff;
--sidebar-spacer: #f4f4f4;
--scrollbar: #8F8F8F;
--icons: #747474;
--icons-hover: #000000;
--links: #20609f;
--inline-code-color: #301900;
--theme-popup-bg: #fafafa;
--theme-popup-border: #cccccc;
--theme-hover: #e6e6e6;
--quote-bg: hsl(197, 37%, 96%);
--quote-border: hsl(197, 37%, 91%);
--table-border-color: hsl(0, 0%, 95%);
--table-header-bg: hsl(0, 0%, 80%);
--table-alternate-bg: hsl(0, 0%, 97%);
--searchbar-border-color: #aaa;
--searchbar-bg: #fafafa;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #888;
--searchresults-li-bg: #e4f2fe;
--search-mark-bg: #a2cff5;
--color-scheme: light;
}
.navy {
--bg: hsl(226, 23%, 11%);
--fg: #bcbdd0;
--sidebar-bg: #282d3f;
--sidebar-fg: #c8c9db;
--sidebar-non-existant: #505274;
--sidebar-active: #2b79a2;
--sidebar-spacer: #2d334f;
--scrollbar: var(--sidebar-fg);
--icons: #737480;
--icons-hover: #b7b9cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #161923;
--theme-popup-border: #737480;
--theme-hover: #282e40;
--quote-bg: hsl(226, 15%, 17%);
--quote-border: hsl(226, 15%, 22%);
--table-border-color: hsl(226, 23%, 16%);
--table-header-bg: hsl(226, 23%, 31%);
--table-alternate-bg: hsl(226, 23%, 14%);
--searchbar-border-color: #aaa;
--searchbar-bg: #aeaec6;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #5f5f71;
--searchresults-border-color: #5c5c68;
--searchresults-li-bg: #242430;
--search-mark-bg: #a2cff5;
--color-scheme: dark;
}
.rust {
--bg: hsl(60, 9%, 87%);
--fg: #262625;
--sidebar-bg: #3b2e2a;
--sidebar-fg: #c8c9db;
--sidebar-non-existant: #505254;
--sidebar-active: #e69f67;
--sidebar-spacer: #45373a;
--scrollbar: var(--sidebar-fg);
--icons: #737480;
--icons-hover: #262625;
--links: #2b79a2;
--inline-code-color: #6e6b5e;
--theme-popup-bg: #e1e1db;
--theme-popup-border: #b38f6b;
--theme-hover: #99908a;
--quote-bg: hsl(60, 5%, 75%);
--quote-border: hsl(60, 5%, 70%);
--table-border-color: hsl(60, 9%, 82%);
--table-header-bg: #b3a497;
--table-alternate-bg: hsl(60, 9%, 84%);
--searchbar-border-color: #aaa;
--searchbar-bg: #fafafa;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #888;
--searchresults-li-bg: #dec2a2;
--search-mark-bg: #e69f67;
--color-scheme: light;
}
@media (prefers-color-scheme: dark) {
.light.no-js {
--bg: hsl(200, 7%, 8%);
--fg: #98a3ad;
--sidebar-bg: #292c2f;
--sidebar-fg: #a1adb8;
--sidebar-non-existant: #505254;
--sidebar-active: #3473ad;
--sidebar-spacer: #393939;
--scrollbar: var(--sidebar-fg);
--icons: #43484d;
--icons-hover: #b3c0cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
--theme-hover: #1f2124;
--quote-bg: hsl(234, 21%, 18%);
--quote-border: hsl(234, 21%, 23%);
--table-border-color: hsl(200, 7%, 13%);
--table-header-bg: hsl(200, 7%, 28%);
--table-alternate-bg: hsl(200, 7%, 11%);
--searchbar-border-color: #aaa;
--searchbar-bg: #b7b7b7;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #98a3ad;
--searchresults-li-bg: #2b2b2f;
--search-mark-bg: #355c7d;
}
}

View File

@@ -1,3 +0,0 @@
set -e
cd ${book.path_build.path}
mdbook serve . -p 8884 -n localhost --open --dest-dir ${book.path_publish.path}

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +0,0 @@
# Errors
@for error in book.errors
## @error.cat
@error.msg
@end

View File

@@ -1 +0,0 @@
mermaid.initialize({startOnLoad:true});

File diff suppressed because one or more lines are too long

View File

@@ -5,20 +5,12 @@ import freeflowuniverse.herolib.ui.console
import os
@[params]
pub struct SiteGenArgs {
pub mut:
dest string
flat bool
sitename string = 'default'
}
pub fn play(mut plbook PlayBook, mut args SiteGenArgs) ! {
pub fn play(mut plbook PlayBook) ! {
if args.dest == '' {
args.dest = '${os.home_dir()}/hero/var/sitegen'
}
defaultdest = '${os.home_dir()}/hero/var/sitegen'
//if only 1 doctree is specified, then we use that as the default doctree name
mut doctreename := 'main'
if plbook.exists(filter: 'site.doctree') {
if plbook.exists_once(filter: 'site.doctree') {
@@ -36,7 +28,7 @@ pub fn play(mut plbook PlayBook, mut args SiteGenArgs) ! {
// description:"A description not filled in"
// draft:1 hide_title:1
mut factory := new(path: args.dest, flat: args.flat)!
mut factory := new(path: defaultdest, flat: true)!
// LETS FIRST DO THE CATEGORIES
category_actions := plbook.find(filter: 'site.page_category')!