Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29c2fccbe5 | |||
| 975c07fc2e |
@@ -51,7 +51,7 @@ fn do() ! {
|
|||||||
mut cmd := Command{
|
mut cmd := Command{
|
||||||
name: 'hero'
|
name: 'hero'
|
||||||
description: 'Your HERO toolset.'
|
description: 'Your HERO toolset.'
|
||||||
version: '1.0.16'
|
version: '1.0.14'
|
||||||
}
|
}
|
||||||
|
|
||||||
// herocmds.cmd_run_add_flags(mut cmd)
|
// herocmds.cmd_run_add_flags(mut cmd)
|
||||||
@@ -102,7 +102,6 @@ fn do() ! {
|
|||||||
// herocmds.cmd_juggler(mut cmd)
|
// herocmds.cmd_juggler(mut cmd)
|
||||||
herocmds.cmd_generator(mut cmd)
|
herocmds.cmd_generator(mut cmd)
|
||||||
herocmds.cmd_docusaurus(mut cmd)
|
herocmds.cmd_docusaurus(mut cmd)
|
||||||
herocmds.cmd_starlight(mut cmd)
|
|
||||||
// herocmds.cmd_docsorter(mut cmd)
|
// herocmds.cmd_docsorter(mut cmd)
|
||||||
// cmd.add_command(publishing.cmd_publisher(pre_func))
|
// cmd.add_command(publishing.cmd_publisher(pre_func))
|
||||||
cmd.setup()
|
cmd.setup()
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
|
||||||
|
|
||||||
//#!/usr/bin/env -S v -cg -enable-globals run
|
|
||||||
import freeflowuniverse.herolib.biz.bizmodel
|
|
||||||
import freeflowuniverse.herolib.core.playbook
|
|
||||||
import freeflowuniverse.herolib.core.playcmds
|
|
||||||
import os
|
|
||||||
|
|
||||||
const playbook_path = os.dir(@FILE) + '/playbook'
|
|
||||||
const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
|
|
||||||
|
|
||||||
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
|
||||||
|
|
||||||
mut model := bizmodel.getset("example")!
|
|
||||||
model.workdir = build_path
|
|
||||||
model.play(mut playbook.new(path: playbook_path)!)!
|
|
||||||
|
|
||||||
println(model.sheet)
|
|
||||||
println(model.sheet.export()!)
|
|
||||||
|
|
||||||
model.sheet.export(path:"~/Downloads/test.csv")!
|
|
||||||
model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2
examples/biztools/bizmodel/example/.gitignore
vendored
Normal file
2
examples/biztools/bizmodel/example/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
bizmodel
|
||||||
|
dest
|
||||||
41
examples/biztools/bizmodel/example/bizmodel.vsh
Executable file
41
examples/biztools/bizmodel/example/bizmodel.vsh
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
//#!/usr/bin/env -S v -cg -enable-globals run
|
||||||
|
import freeflowuniverse.herolib.data.doctree
|
||||||
|
import freeflowuniverse.herolib.biz.bizmodel
|
||||||
|
import freeflowuniverse.herolib.core.playbook
|
||||||
|
import freeflowuniverse.herolib.core.playcmds
|
||||||
|
import freeflowuniverse.herolib.web.mdbook
|
||||||
|
import os
|
||||||
|
|
||||||
|
const wikipath = os.dir(@FILE) + '/wiki'
|
||||||
|
const summarypath = os.dir(@FILE) + '/wiki/summary.md'
|
||||||
|
|
||||||
|
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
||||||
|
|
||||||
|
mut m := bizmodel.getset('example')!
|
||||||
|
m.workdir = wikipath
|
||||||
|
m.play(mut playbook.new(path: wikipath)!)!
|
||||||
|
m.export_sheets()!
|
||||||
|
bizmodel.set(m)
|
||||||
|
|
||||||
|
// // execute the actions so we have the info populated
|
||||||
|
// // playcmds.run(mut plb,false)!
|
||||||
|
|
||||||
|
// // just run the doctree & mdbook and it should
|
||||||
|
// // load the doctree, these are all collections
|
||||||
|
// mut tree := doctree.new(name: 'bizmodel')!
|
||||||
|
// tree.scan(path: wikipath)!
|
||||||
|
// tree.export(dest: buildpath, reset: true)!
|
||||||
|
|
||||||
|
// // mut bm:=bizmodel.get("test")!
|
||||||
|
// // println(bm)
|
||||||
|
|
||||||
|
// mut mdbooks := mdbook.get()!
|
||||||
|
// mdbooks.generate(
|
||||||
|
// name: 'bizmodel'
|
||||||
|
// summary_path: summarypath
|
||||||
|
// doctree_path: buildpath
|
||||||
|
// title: 'bizmodel example'
|
||||||
|
// )!
|
||||||
|
// mdbook.book_open('bizmodel')!
|
||||||
8
examples/biztools/bizmodel/example/wiki/employees.md
Normal file
8
examples/biztools/bizmodel/example/wiki/employees.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Hr Overview
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
!!!bizmodel.employees_wiki bizname:'test'
|
||||||
|
|
||||||
|
|
||||||
|
> note: Nr People like 0:5,20:5 means, month 0 (start) is 5, month 20 its 5 people
|
||||||
5
examples/biztools/bizmodel/example/wiki/hr/cto.md
Normal file
5
examples/biztools/bizmodel/example/wiki/hr/cto.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# CTO
|
||||||
|
|
||||||
|
!!!bizmodel.employee_wiki bizname:'test' name:'despiegk'
|
||||||
|
|
||||||
|
!!wiki.include page:cto_description.md
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## CTO Description
|
||||||
|
|
||||||
|
this is a page to test nested includes
|
||||||
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
52
examples/biztools/bizmodel/example/wiki/overview.md
Normal file
52
examples/biztools/bizmodel/example/wiki/overview.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# This is our business model planner
|
||||||
|
|
||||||
|
## P&L Overview
|
||||||
|
|
||||||
|
|
||||||
|
<!-- period is in months, 3 means every quarter -->
|
||||||
|
|
||||||
|
|
||||||
|
!!bizmodel.graph_bar_row rowname:revenue_total unit:million title:'A Title' title_sub:'Sub' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
Unit is in Million USD.
|
||||||
|
|
||||||
|
!!bizmodel.graph_bar_row rowname:revenue_total unit:million sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.graph_line_row rowname:revenue_total unit:million sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.graph_pie_row rowname:revenue_total unit:million size:'80%' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
## FUNDING
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki includefilter:'funding' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'REVENUE' includefilter:rev sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'Revenue Total' includefilter:'revtotal' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'REVENUE' includefilter:'revtotal2' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'COGS' includefilter:'cogs' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'Margin' includefilter:'margin' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'HR Teams' includefilter:'hrnr' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'HR Costs' includefilter:'hrcost' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'COSTS' includefilter:'ocost' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'HR Costs' includefilter:'hrcost' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'P&L Overview' includefilter:'pl' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki title:'P&L Overview' includefilter:'pl' sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
## Some Details
|
||||||
|
|
||||||
|
> show how we can do per month
|
||||||
|
|
||||||
|
!!bizmodel.sheet_wiki includefilter:'pl' period_months:1 sheetname:'bizmodel_test'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -4,31 +4,11 @@
|
|||||||
|
|
||||||
This company is a cloud company ...
|
This company is a cloud company ...
|
||||||
|
|
||||||
- name, e.g. for a specific project
|
|
||||||
- descr, description of the revenue line item
|
|
||||||
- revenue_items: does one of revenue, is not exterpolated
|
|
||||||
- revenue_growth: is a revenue stream which is being extrapolated
|
|
||||||
- revenue_setup, revenue for 1 item '1000usd'
|
|
||||||
- revenue_setup_delay
|
|
||||||
- revenue_monthly, revenue per month for 1 item
|
|
||||||
- revenue_monthly_delay, how many months before monthly revenue starts
|
|
||||||
- maintenance_month_perc, how much percent of revenue_setup will come back over months
|
|
||||||
- cogs_setup, cost of good for 1 item at setup
|
|
||||||
- cogs_setup_delay, how many months before setup cogs starts, after sales
|
|
||||||
- cogs_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
|
|
||||||
|
|
||||||
- cogs_monthly, cost of goods for the monthly per 1 item
|
|
||||||
- cogs_monthly_delay, how many months before monthly cogs starts, after sales
|
|
||||||
- cogs_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
|
|
||||||
|
|
||||||
- nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200, default is 1)
|
|
||||||
- nr_months_recurring: how many months is recurring, if 0 then no recurring
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
!!bizmodel.revenue_define bizname:'test'
|
!!bizmodel.revenue_define bizname:'test'
|
||||||
descr:'OEM Deals'
|
descr:'OEM Deals'
|
||||||
revenue_items:'10:1000000EUR,15:3333,20:1200000'
|
revenue_time:'10:1000000EUR,15:3333,20:1200000'
|
||||||
cogs_setup_perc: '1:5%,20:10%'
|
cogs_perc: '1:5%,20:10%'
|
||||||
|
|
||||||
!!bizmodel.revenue_define bizname:'test'
|
!!bizmodel.revenue_define bizname:'test'
|
||||||
descr:'License Deals'
|
descr:'License Deals'
|
||||||
@@ -43,7 +23,6 @@ This company is a cloud company ...
|
|||||||
revenue_nr:'10:1000,24:2000,60:40000'
|
revenue_nr:'10:1000,24:2000,60:40000'
|
||||||
cogs_perc: '10%'
|
cogs_perc: '10%'
|
||||||
rev_delay_month: 1
|
rev_delay_month: 1
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Revenue Items Recurring
|
## Revenue Items Recurring
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
bizmodel
|
|
||||||
dest
|
|
||||||
wiki
|
|
||||||
build
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
|
|
||||||
|
|
||||||
//#!/usr/bin/env -S v -cg -enable-globals run
|
|
||||||
import freeflowuniverse.herolib.biz.bizmodel
|
|
||||||
import freeflowuniverse.herolib.core.playbook
|
|
||||||
import freeflowuniverse.herolib.core.playcmds
|
|
||||||
import os
|
|
||||||
|
|
||||||
//TODO: need to fix wrong location
|
|
||||||
const playbook_path = os.dir(@FILE) + '/playbook'
|
|
||||||
const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
|
|
||||||
|
|
||||||
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
|
||||||
|
|
||||||
mut model := bizmodel.getset("example")!
|
|
||||||
model.workdir = build_path
|
|
||||||
model.play(mut playbook.new(path: playbook_path)!)!
|
|
||||||
|
|
||||||
println(model.sheet)
|
|
||||||
println(model.sheet.export()!)
|
|
||||||
|
|
||||||
// model.sheet.export(path:"~/Downloads/test.csv")!
|
|
||||||
// model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
report := model.new_report(
|
|
||||||
name: 'example_report'
|
|
||||||
title: 'Example Business Model'
|
|
||||||
)!
|
|
||||||
|
|
||||||
report.export(
|
|
||||||
path: build_path
|
|
||||||
overwrite: true
|
|
||||||
format: .docusaurus
|
|
||||||
)!
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
output dir of example
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "${script_dir}"
|
|
||||||
|
|
||||||
echo "Docs directory: $script_dir"
|
|
||||||
|
|
||||||
cd "${HOME}/hero/var/docusaurus"
|
|
||||||
|
|
||||||
export PATH=/tmp/docusaurus_build/node_modules/.bin:${HOME}/.bun/bin/:$PATH
|
|
||||||
|
|
||||||
rm -rf /Users/despiegk/hero/var/docusaurus/build/
|
|
||||||
|
|
||||||
. ${HOME}/.zprofile
|
|
||||||
|
|
||||||
bun docusaurus build
|
|
||||||
|
|
||||||
mkdir -p /Users/despiegk/code/github/freeflowuniverse/herolib/examples/biztools/bizmodel/example/docusaurus
|
|
||||||
echo SYNC TO /Users/despiegk/code/github/freeflowuniverse/herolib/examples/biztools/bizmodel/example/docusaurus
|
|
||||||
rsync -rv --delete /Users/despiegk/hero/var/docusaurus/build/ /Users/despiegk/code/github/freeflowuniverse/herolib/examples/biztools/bizmodel/example/docusaurus/
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"style":"dark","links":[]}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"name":"","title":"Docusaurus","tagline":"","favicon":"img/favicon.png","url":"http://localhost","url_home":"docs/introduction","baseUrl":"/","image":"img/tf_graph.png","metadata":{"description":"Docusaurus","image":"Docusaurus","title":"Docusaurus"},"buildDest":[],"buildDestDev":[]}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"title":"Business Model","items":[{"href":"https://threefold.info/kristof/","label":"ThreeFold Technology","position":"right"},{"href":"https://threefold.io","label":"Operational Plan","position":"left"}]}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "${script_dir}"
|
|
||||||
|
|
||||||
echo "Docs directory: $script_dir"
|
|
||||||
|
|
||||||
cd "${HOME}/hero/var/docusaurus"
|
|
||||||
|
|
||||||
export PATH=/tmp/docusaurus_build/node_modules/.bin:${HOME}/.bun/bin/:$PATH
|
|
||||||
|
|
||||||
. ${HOME}/.zprofile
|
|
||||||
|
|
||||||
bun run start -p 3100
|
|
||||||
42
examples/webtools/docusaurus/docusaurus_example.vsh
Executable file
42
examples/webtools/docusaurus/docusaurus_example.vsh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.web.docusaurus
|
||||||
|
// import freeflowuniverse.herolib.data.doctree
|
||||||
|
|
||||||
|
// Create a new docusaurus factory
|
||||||
|
mut docs := docusaurus.new(
|
||||||
|
build_path: '/tmp/docusaurus_build'
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Create a new docusaurus site
|
||||||
|
mut site := docs.dev(
|
||||||
|
url: 'https://git.ourworld.tf/despiegk/docs_kristof'
|
||||||
|
)!
|
||||||
|
|
||||||
|
// FOR FUTURE TO ADD CONTENT FROM DOCTREE
|
||||||
|
|
||||||
|
// Create a doctree for content
|
||||||
|
// mut tree := doctree.new(name: 'content')!
|
||||||
|
|
||||||
|
// // Add some content from a git repository
|
||||||
|
// tree.scan(
|
||||||
|
// git_url: 'https://github.com/yourusername/your-docs-repo'
|
||||||
|
// git_pull: true
|
||||||
|
// )!
|
||||||
|
|
||||||
|
// // Export the content to the docusaurus site
|
||||||
|
// tree.export(
|
||||||
|
// destination: '${site.path_build.path}/docs'
|
||||||
|
// reset: true
|
||||||
|
// keep_structure: true
|
||||||
|
// exclude_errors: false
|
||||||
|
// )!
|
||||||
|
|
||||||
|
// Build the docusaurus site
|
||||||
|
// site.build()!
|
||||||
|
|
||||||
|
// Generate the static site
|
||||||
|
// site.generate()!
|
||||||
|
|
||||||
|
// Optionally open the site in a browser
|
||||||
|
// site.open()!
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.web.starlight
|
|
||||||
// import freeflowuniverse.herolib.data.doctree
|
|
||||||
|
|
||||||
// Create a new starlight factory
|
|
||||||
mut docs := starlight.new(
|
|
||||||
build_path: '/tmp/starlight_build'
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Create a new starlight site
|
|
||||||
mut site := docs.get(
|
|
||||||
url: 'https://git.ourworld.tf/tfgrid/docs_aibox'
|
|
||||||
init:true //init means we put config files if not there
|
|
||||||
)!
|
|
||||||
|
|
||||||
site.dev()!
|
|
||||||
@@ -4,7 +4,7 @@ set -e
|
|||||||
|
|
||||||
os_name="$(uname -s)"
|
os_name="$(uname -s)"
|
||||||
arch_name="$(uname -m)"
|
arch_name="$(uname -m)"
|
||||||
version='1.0.16'
|
version='1.0.14'
|
||||||
|
|
||||||
|
|
||||||
# Base URL for GitHub releases
|
# Base URL for GitHub releases
|
||||||
|
|||||||
1
lib/biz/bizmodel/.gitignore
vendored
1
lib/biz/bizmodel/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
testdata
|
|
||||||
@@ -15,7 +15,7 @@ pub fn (mut m BizModel) act(action Action) !Action {
|
|||||||
m.funding_define_action(action)!
|
m.funding_define_action(action)!
|
||||||
}
|
}
|
||||||
'revenue_define' {
|
'revenue_define' {
|
||||||
m.revenue_action(action)!
|
m.funding_define_action(action)!
|
||||||
}
|
}
|
||||||
'costcenter_define' {
|
'costcenter_define' {
|
||||||
m.costcenter_define_action(action)!
|
m.costcenter_define_action(action)!
|
||||||
@@ -58,7 +58,7 @@ fn (mut m BizModel) export_sheet_action(action Action) !Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut m BizModel) export_graph_title_action(action Action) !Action {
|
fn (mut m BizModel) export_graph_title_action(action Action) !Action {
|
||||||
return m.export_action(m.sheet.wiki_title_chart(row_args_from_params(action.params)!)!, action)
|
return m.export_action(m.sheet.wiki_title_chart(row_args_from_params(action.params)!), action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut m BizModel) export_graph_line_action(action Action) !Action {
|
fn (mut m BizModel) export_graph_line_action(action Action) !Action {
|
||||||
|
|||||||
@@ -5,6 +5,18 @@ import freeflowuniverse.herolib.web.docusaurus
|
|||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
import freeflowuniverse.herolib.core.pathlib
|
||||||
|
|
||||||
|
pub struct Export {
|
||||||
|
pub:
|
||||||
|
path string
|
||||||
|
overwrite bool
|
||||||
|
format ExportFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ExportFormat {
|
||||||
|
docusaurus
|
||||||
|
mdbook
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Report {
|
pub struct Report {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
@@ -24,15 +36,7 @@ pub fn (b BizModel) new_report(report Report) !Report {
|
|||||||
name := if report.name != '' {report.name} else { texttools.snake_case(report.title) }
|
name := if report.name != '' {report.name} else { texttools.snake_case(report.title) }
|
||||||
path := pathlib.get_dir(
|
path := pathlib.get_dir(
|
||||||
path: os.join_path(os.home_dir(), '/hero/var/bizmodel/reports/${name}')
|
path: os.join_path(os.home_dir(), '/hero/var/bizmodel/reports/${name}')
|
||||||
create: true
|
|
||||||
empty: true
|
|
||||||
)!
|
)!
|
||||||
|
|
||||||
b.write_introduction(path.path)!
|
|
||||||
b.write_operational_plan(path.path)!
|
|
||||||
b.write_revenue_model(path.path)!
|
|
||||||
b.write_cost_structure(path.path)!
|
|
||||||
|
|
||||||
return Report {
|
return Report {
|
||||||
...report,
|
...report,
|
||||||
name: name
|
name: name
|
||||||
@@ -48,104 +52,39 @@ pub fn (b BizModel) new_report(report Report) !Report {
|
|||||||
// b.export_fundraising(export)
|
// b.export_fundraising(export)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Export {
|
|
||||||
pub:
|
|
||||||
path string
|
|
||||||
overwrite bool
|
|
||||||
format ExportFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ExportFormat {
|
|
||||||
docusaurus
|
|
||||||
mdbook
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (r Report) export(export Export) ! {
|
pub fn (r Report) export(export Export) ! {
|
||||||
match export.format {
|
match export.format {
|
||||||
.docusaurus {
|
.docusaurus {
|
||||||
mut dir := pathlib.get_dir(path: r.path)!
|
|
||||||
dir.copy(dest: '${export.path}/docs', delete: true)!
|
|
||||||
mut factory := docusaurus.new()!
|
mut factory := docusaurus.new()!
|
||||||
mut site := factory.get(
|
mut site := factory.get(
|
||||||
name: r.name
|
name: r.name
|
||||||
path: export.path
|
path: r.path
|
||||||
publish_path: export.path
|
publish_path: export.path
|
||||||
init: true
|
config: docusaurus.Config {} //TODO: is this needed
|
||||||
config: docusaurus.Config {
|
|
||||||
navbar: docusaurus.Navbar {
|
|
||||||
title: "Business Model",
|
|
||||||
items: [
|
|
||||||
docusaurus.NavbarItem{
|
|
||||||
"href": "https://threefold.info/kristof/",
|
|
||||||
"label": "ThreeFold Technology",
|
|
||||||
"position": "right"
|
|
||||||
},
|
|
||||||
docusaurus.NavbarItem{
|
|
||||||
"href": "https://threefold.io",
|
|
||||||
"label": "Operational Plan",
|
|
||||||
"position": "left"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
main: docusaurus.Main {
|
|
||||||
url_home: 'docs/introduction'
|
|
||||||
}
|
|
||||||
} //TODO: is this needed
|
|
||||||
)!
|
)!
|
||||||
site.generate()!
|
site.build()!
|
||||||
}
|
}
|
||||||
.mdbook {panic('MDBook export not fully implemented')}
|
.mdbook {panic('MDBook export not fully implemented')}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (model BizModel) write_introduction(path string) ! {
|
pub fn (b BizModel) export_operational_plan(export Export) ! {
|
||||||
mut index_page := pathlib.get_file(path: '${path}/introduction.md')!
|
mut hr_page := pathlib.get_file(path: '${export.path}/human_resources.md')!
|
||||||
// mut tmpl_index := $tmpl('templates/index.md')
|
hr_page.template_write('./templates/human_resources.md', export.overwrite)!
|
||||||
index_page.template_write($tmpl('templates/introduction.md'), true)!
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (model BizModel) write_operational_plan(path string) ! {
|
for key, employee in b.employees {
|
||||||
mut dir := pathlib.get_dir(path: '${path}/operational_plan')!
|
mut employee_page := pathlib.get_file(path: '${export.path}/${texttools.snake_case(employee.name)}.md')!
|
||||||
mut ops_page := pathlib.get_file(path: '${dir.path}/operational_plan.md')!
|
employee_page.template_write('./templates/employee.md', export.overwrite)!
|
||||||
ops_page.write('# Operational Plan')!
|
|
||||||
|
|
||||||
mut hr_dir := pathlib.get_dir(path: '${dir.path}/human_resources')!
|
|
||||||
mut hr_page := pathlib.get_file(path: '${hr_dir.path}/human_resources.md')!
|
|
||||||
hr_page.template_write($tmpl('./templates/human_resources.md'), true)!
|
|
||||||
|
|
||||||
for key, employee in model.employees {
|
|
||||||
mut employee_page := pathlib.get_file(path: '${hr_dir.path}/${texttools.snake_case(employee.name)}.md')!
|
|
||||||
employee_cost_chart := model.sheet.line_chart(rowname:'hr_cost_${employee.name}', unit: .million)!.mdx()
|
|
||||||
employee_page.template_write($tmpl('./templates/employee.md'), true)!
|
|
||||||
}
|
|
||||||
|
|
||||||
mut depts_dir := pathlib.get_dir(path: '${dir.path}/departments')!
|
|
||||||
for key, department in model.departments {
|
|
||||||
mut dept_page := pathlib.get_file(path: '${depts_dir.path}/${texttools.snake_case(department.name)}.md')!
|
|
||||||
// dept_cost_chart := model.sheet.line_chart(rowname:'hr_cost_${employee.name}', unit: .million)!.mdx()
|
|
||||||
// println(employee_cost_chart)
|
|
||||||
dept_page.template_write($tmpl('./templates/department.md'), true)!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (model BizModel) write_revenue_model(path string) ! {
|
pub fn (b BizModel) export_revenue_model(export Export) ! {
|
||||||
mut dir := pathlib.get_dir(path: '${path}/revenue_model')!
|
println('begin')
|
||||||
mut rm_page := pathlib.get_file(path: '${dir.path}/revenue_model.md')!
|
mut overview_page := pathlib.get_file(path: '${export.path}/revenue_overview.md')!
|
||||||
rm_page.write('# Revenue Model')!
|
overview_page.template_write('./templates/overview.md', export.overwrite)!
|
||||||
|
|
||||||
mut products_dir := pathlib.get_dir(path: '${dir.path}/products')!
|
|
||||||
mut products_page := pathlib.get_file(path: '${products_dir.path}/products.md')!
|
|
||||||
products_page.template_write('# Products', true)!
|
|
||||||
|
|
||||||
name1 := 'example'
|
for key, product in b.products {
|
||||||
for key, product in model.products {
|
mut product_page := pathlib.get_file(path: '${export.path}/${texttools.snake_case(product.name)}.md')!
|
||||||
mut product_page := pathlib.get_file(path: '${products_dir.path}/${texttools.snake_case(product.name)}.md')!
|
product_page.template_write('./templates/product.md', export.overwrite)!
|
||||||
product_page.template_write($tmpl('./templates/product.md'), true)!
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (model BizModel) write_cost_structure(path string) ! {
|
|
||||||
mut dir := pathlib.get_dir(path: '${path}/cost_structure')!
|
|
||||||
mut cs_page := pathlib.get_file(path: '${dir.path}/cost_structure.md')!
|
|
||||||
cs_page.write('# Cost Structure')!
|
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ fn employee_wiki(p paramsparser.Params, sim BizModel) !string {
|
|||||||
|
|
||||||
// theme := 'light'
|
// theme := 'light'
|
||||||
// theme := 'dark' // Removed unused variable
|
// theme := 'dark' // Removed unused variable
|
||||||
mut t := $tmpl('./templates/employee_old.md')
|
mut t := $tmpl('./templates/employee.md')
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import freeflowuniverse.herolib.biz.spreadsheet
|
|||||||
pub struct BizModel {
|
pub struct BizModel {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string
|
name string
|
||||||
description string
|
|
||||||
workdir string = '${os.home_dir()}/hero/var/bizmodel'
|
workdir string = '${os.home_dir()}/hero/var/bizmodel'
|
||||||
sheet &spreadsheet.Sheet
|
sheet &spreadsheet.Sheet
|
||||||
employees map[string]&Employee
|
employees map[string]&Employee
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ import freeflowuniverse.herolib.core.texttools
|
|||||||
// - cogs_setup, cost of good for 1 item at setup
|
// - cogs_setup, cost of good for 1 item at setup
|
||||||
// - cogs_setup_delay, how many months before setup cogs starts, after sales
|
// - cogs_setup_delay, how many months before setup cogs starts, after sales
|
||||||
// - cogs_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
|
// - cogs_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
|
||||||
|
|
||||||
// - cogs_monthly, cost of goods for the monthly per 1 item
|
// - cogs_monthly, cost of goods for the monthly per 1 item
|
||||||
// - cogs_monthly_delay, how many months before monthly cogs starts, after sales
|
// - cogs_monthly_delay, how many months before monthly cogs starts, after sales
|
||||||
// - cogs_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
|
// - cogs_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
|
||||||
// - nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200, default is 1)
|
|
||||||
|
// - nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200)
|
||||||
// - nr_months_recurring: how many months is recurring, if 0 then no recurring
|
// - nr_months_recurring: how many months is recurring, if 0 then no recurring
|
||||||
//
|
//
|
||||||
fn (mut m BizModel) revenue_action(action Action) !Action {
|
fn (mut m BizModel) revenue_action(action Action) !Action {
|
||||||
@@ -60,10 +62,6 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
|
|||||||
extrapolate: false
|
extrapolate: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
println(action)
|
|
||||||
println(revenue)
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
mut revenue_setup := m.sheet.row_new(
|
mut revenue_setup := m.sheet.row_new(
|
||||||
name: '${name}_revenue_setup'
|
name: '${name}_revenue_setup'
|
||||||
growth: action.params.get_default('revenue_setup', '0:0')!
|
growth: action.params.get_default('revenue_setup', '0:0')!
|
||||||
@@ -141,6 +139,11 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
|
|||||||
aggregatetype: .avg
|
aggregatetype: .avg
|
||||||
)!
|
)!
|
||||||
|
|
||||||
|
// if true{
|
||||||
|
// println(cogs_setup_perc)
|
||||||
|
// println(cogs_monthly_perc)
|
||||||
|
// panic("sdsd")
|
||||||
|
// }
|
||||||
|
|
||||||
mut nr_sold := m.sheet.row_new(
|
mut nr_sold := m.sheet.row_new(
|
||||||
name: '${name}_nr_sold'
|
name: '${name}_nr_sold'
|
||||||
@@ -208,6 +211,10 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
|
|||||||
nrmonths: nr_months_recurring
|
nrmonths: nr_months_recurring
|
||||||
aggregatetype: .max
|
aggregatetype: .max
|
||||||
)!
|
)!
|
||||||
|
// if true{
|
||||||
|
// println(nr_sold_recurring)
|
||||||
|
// panic('sd')
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// cogs as percentage of revenue
|
// cogs as percentage of revenue
|
||||||
@@ -222,17 +229,16 @@ fn (mut m BizModel) revenue_action(action Action) !Action {
|
|||||||
name: '${name}_cogs_monthly_from_perc'
|
name: '${name}_cogs_monthly_from_perc'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
println(action)
|
// if true{
|
||||||
println(nr_sold)
|
// println(revenue_setup_total)
|
||||||
println(revenue)
|
// println(cogs_setup_perc)
|
||||||
println(revenue_setup_total)
|
// println(cogs_setup_from_perc)
|
||||||
println(revenue_monthly_total)
|
// println("montlhy")
|
||||||
println(cogs_setup_perc)
|
// println(revenue_monthly_total)
|
||||||
println(cogs_setup_from_perc)
|
// println(cogs_monthly_perc)
|
||||||
println(cogs_monthly_perc)
|
// println(cogs_monthly_from_perc)
|
||||||
println(cogs_monthly_from_perc)
|
// panic("sdsd")
|
||||||
exit(0)
|
// }
|
||||||
|
|
||||||
|
|
||||||
// mut cogs_from_perc:=cogs_perc.action(action:.multiply,rows:[revenue],name:"cogs_from_perc")!
|
// mut cogs_from_perc:=cogs_perc.action(action:.multiply,rows:[revenue],name:"cogs_from_perc")!
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# @{department.name}
|
|
||||||
|
|
||||||
|
|
||||||
`@{department.description}`
|
|
||||||
|
|
||||||
**Cost To The Company:**
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
`@{employee.cost}`
|
`@{employee.cost}`
|
||||||
|
|
||||||
@{employee_cost_chart}
|
|
||||||
|
|
||||||
@if employee.cost_percent_revenue > 0.0
|
@if employee.cost_percent_revenue > 0.0
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# @{employee.name}
|
|
||||||
|
|
||||||
|
|
||||||
`@{employee.description}`
|
|
||||||
|
|
||||||
> department: `@{employee.department}`
|
|
||||||
|
|
||||||
**Cost To The Company:**
|
|
||||||
|
|
||||||
`@{employee.cost}`
|
|
||||||
|
|
||||||
@if employee.cost_percent_revenue > 0.0
|
|
||||||
|
|
||||||
**Cost Percent Revenue:**
|
|
||||||
|
|
||||||
`@{employee.cost_percent_revenue}%`
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@if employee.nrpeople.len > 1
|
|
||||||
|
|
||||||
**Number of People in this group**
|
|
||||||
|
|
||||||
`@{employee.nrpeople}`
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
| Name | Title | Nr People |
|
| Name | Title | Nr People |
|
||||||
|------|-------|-------|
|
|------|-------|-------|
|
||||||
@for employee in model.employees.values()
|
@for employee in model.employees.values().filter(it.department == dept.name)
|
||||||
| @{employee.name} | @{employee.title} | @{employee.nrpeople} |
|
| @{employee_names[employee.name]} | @{employee.title} | @{employee.nrpeople} |
|
||||||
|
@end
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# @{model.name}
|
|
||||||
|
|
||||||
@{model.description}
|
|
||||||
|
|
||||||
## FUNDING
|
|
||||||
|
|
||||||
@{model.sheet.wiki(includefilter:['funding']) or {panic(err)}}
|
|
||||||
|
|
||||||
## REVENUE vs COGS
|
|
||||||
|
|
||||||
@{model.sheet.wiki(includefilter:['rev']) or {panic(err)}}
|
|
||||||
|
|
||||||
#### Revenue Lines
|
|
||||||
|
|
||||||
@{model.sheet.wiki(title:'Revenue Total', includefilter:['revtotal']) or {panic(err)}}
|
|
||||||
|
|
||||||
#### COGS Lines
|
|
||||||
|
|
||||||
@{model.sheet.wiki(title:'COGS', includefilter:['cogstotal']) or {panic(err)}}
|
|
||||||
|
|
||||||
## HR
|
|
||||||
|
|
||||||
@{model.sheet.wiki(title:'HR Teams', includefilter:['hrnr']) or {panic(err)}}
|
|
||||||
|
|
||||||
@{model.sheet.wiki(title:'HR Costs', includefilter:['hrcost']) or {panic(err)}}
|
|
||||||
|
|
||||||
## Operational Costs
|
|
||||||
|
|
||||||
@{model.sheet.wiki(title:'COSTS', includefilter:['ocost']) or {panic(err)}}
|
|
||||||
|
|
||||||
## P&L Overview
|
|
||||||
|
|
||||||
<!-- period is in months, 3 means every quarter -->
|
|
||||||
|
|
||||||
@{model.sheet.wiki(title:'P&L Overview', includefilter:['pl']) or {panic(err)}}
|
|
||||||
|
|
||||||
@{model.sheet.bar_chart(rowname:'revenue_total', unit: .million, title:'A Title', title_sub:'Sub') or {panic(err)}.mdx()}
|
|
||||||
|
|
||||||
Unit is in Million USD.
|
|
||||||
|
|
||||||
@{model.sheet.line_chart(rowname:'revenue_total', unit: .million) or {panic(err)}.mdx()}
|
|
||||||
|
|
||||||
@{model.sheet.pie_chart(rowname:'revenue_total', unit: .million, size:'80%') or {panic(err)}.mdx()}
|
|
||||||
|
|
||||||
## Some Details
|
|
||||||
|
|
||||||
> show how we can do per month
|
|
||||||
|
|
||||||
@{model.sheet.wiki(includefilter:['pl'], period_type:.month) or {panic(err)}}
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
Product ${name1} has revenue events (one offs)
|
Product ${name1} has revenue events (one offs)
|
||||||
|
|
||||||
@{model.sheet.wiki() or {''}}
|
!!!spreadsheet.sheet_wiki
|
||||||
namefilter:'${name1}_revenue,${name1}_cogs,${name1}_cogs_perc,${name1}_maintenance_month_perc' sheetname:'bizmodel_tf9
|
namefilter:'${name1}_revenue,${name1}_cogs,${name1}_cogs_perc,${name1}_maintenance_month_perc' sheetname:'bizmodel_tf9
|
||||||
|
|
||||||
- COGS = Cost of Goods Sold (is our cost to deliver the product/service)
|
- COGS = Cost of Goods Sold (is our cost to deliver the product/service)
|
||||||
@@ -21,7 +21,7 @@ Product ${name1} has revenue events (one offs)
|
|||||||
|
|
||||||
Product sold and its revenue/cost of goods
|
Product sold and its revenue/cost of goods
|
||||||
|
|
||||||
@{model.sheet.wiki() or {''}}
|
!!!spreadsheet.sheet_wiki
|
||||||
namefilter:'${name1}_nr_sold,${name1}_revenue_setup,${name1}_revenue_monthly,${name1}_cogs_setup,${name1}_cogs_setup_perc,${name1}_cogs_monthly,${name1}_cogs_monthly_perc'
|
namefilter:'${name1}_nr_sold,${name1}_revenue_setup,${name1}_revenue_monthly,${name1}_cogs_setup,${name1}_cogs_setup_perc,${name1}_cogs_monthly,${name1}_cogs_monthly_perc'
|
||||||
sheetname:'bizmodel_tf9
|
sheetname:'bizmodel_tf9
|
||||||
|
|
||||||
@@ -40,19 +40,20 @@ This product ${name1} is recurring, means customer pays per month ongoing, the p
|
|||||||
|
|
||||||
#### the revenue/cogs calculated
|
#### the revenue/cogs calculated
|
||||||
|
|
||||||
@{model.sheet.wiki() or {''}}
|
|
||||||
|
!!!spreadsheet.sheet_wiki
|
||||||
namefilter:'${name1}_nr_sold_recurring'
|
namefilter:'${name1}_nr_sold_recurring'
|
||||||
sheetname:'bizmodel_tf9
|
sheetname:'bizmodel_tf9
|
||||||
|
|
||||||
This results in following revenues and cogs:
|
This results in following revenues and cogs:
|
||||||
|
|
||||||
@{model.sheet.wiki() or {''}}
|
!!!spreadsheet.sheet_wiki
|
||||||
namefilter:'${name1}_revenue_setup_total,${name1}_revenue_monthly_total,${name1}_cogs_setup_total,${name1}_cogs_monthly_total,${name1}_cogs_setup_from_perc,${name1}_cogs_monthly_from_perc,${name1}_maintenance_month,
|
namefilter:'${name1}_revenue_setup_total,${name1}_revenue_monthly_total,${name1}_cogs_setup_total,${name1}_cogs_monthly_total,${name1}_cogs_setup_from_perc,${name1}_cogs_monthly_from_perc,${name1}_maintenance_month,
|
||||||
${name1}_revenue_monthly_recurring,${name1}_cogs_monthly_recurring'
|
${name1}_revenue_monthly_recurring,${name1}_cogs_monthly_recurring'
|
||||||
sheetname:'bizmodel_tf9
|
sheetname:'bizmodel_tf9
|
||||||
|
|
||||||
resulting revenues:
|
resulting revenues:
|
||||||
@{model.sheet.wiki() or {''}}
|
!!!spreadsheet.sheet_wiki
|
||||||
namefilter:'${name1}_revenue_total,${name1}_cogs_total'
|
namefilter:'${name1}_revenue_total,${name1}_cogs_total'
|
||||||
sheetname:'bizmodel_tf9
|
sheetname:'bizmodel_tf9
|
||||||
|
|
||||||
@@ -60,3 +61,8 @@ resulting revenues:
|
|||||||
!!!spreadsheet.graph_line_row rowname:'${name1}_cogs_total' unit:million sheetname:'bizmodel_tf9'
|
!!!spreadsheet.graph_line_row rowname:'${name1}_cogs_total' unit:million sheetname:'bizmodel_tf9'
|
||||||
|
|
||||||
!!!spreadsheet.graph_line_row rowname:'${name1}_revenue_total' unit:million sheetname:'bizmodel_tf9'
|
!!!spreadsheet.graph_line_row rowname:'${name1}_revenue_total' unit:million sheetname:'bizmodel_tf9'
|
||||||
|
|
||||||
|
|
||||||
|
@end //product has_revenue
|
||||||
|
|
||||||
|
@end //loop
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
|
|
||||||
# @{product.title}
|
|
||||||
|
|
||||||
@{product.description}
|
|
||||||
|
|
||||||
#### parameters for the product
|
|
||||||
|
|
||||||
@if product.has_oneoffs
|
|
||||||
|
|
||||||
Product ${name1} has revenue events (one offs)
|
|
||||||
|
|
||||||
!!!spreadsheet.sheet_wiki
|
|
||||||
namefilter:'${name1}_revenue,${name1}_cogs,${name1}_cogs_perc,${name1}_maintenance_month_perc' sheetname:'bizmodel_tf9
|
|
||||||
|
|
||||||
- COGS = Cost of Goods Sold (is our cost to deliver the product/service)
|
|
||||||
- maintenance is fee we charge to the customer per month in relation to the revenue we charged e.g. 1% of a product which was sold for 1m EUR means we charge 1% of 1 m EUR per month.
|
|
||||||
|
|
||||||
@end //one offs
|
|
||||||
|
|
||||||
@if product.has_items
|
|
||||||
|
|
||||||
Product sold and its revenue/cost of goods
|
|
||||||
|
|
||||||
!!!spreadsheet.sheet_wiki
|
|
||||||
namefilter:'${name1}_nr_sold,${name1}_revenue_setup,${name1}_revenue_monthly,${name1}_cogs_setup,${name1}_cogs_setup_perc,${name1}_cogs_monthly,${name1}_cogs_monthly_perc'
|
|
||||||
sheetname:'bizmodel_tf9
|
|
||||||
|
|
||||||
- nr sold, is the nr sold per month of ${name1}
|
|
||||||
- revenue setup is setup per item for ${name1}, this is the money we receive. Similar there is a revenue monthly.
|
|
||||||
- cogs = Cost of Goods Sold (is our cost to deliver the product)
|
|
||||||
- can we as a setup per item, or per month per item
|
|
||||||
|
|
||||||
@if product.nr_months_recurring>1
|
|
||||||
|
|
||||||
This product ${name1} is recurring, means customer pays per month ongoing, the period customer is paying for in months is: **${product.nr_months_recurring}**
|
|
||||||
|
|
||||||
@end //recurring
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#### the revenue/cogs calculated
|
|
||||||
|
|
||||||
|
|
||||||
!!!spreadsheet.sheet_wiki
|
|
||||||
namefilter:'${name1}_nr_sold_recurring'
|
|
||||||
sheetname:'bizmodel_tf9
|
|
||||||
|
|
||||||
This results in following revenues and cogs:
|
|
||||||
|
|
||||||
!!!spreadsheet.sheet_wiki
|
|
||||||
namefilter:'${name1}_revenue_setup_total,${name1}_revenue_monthly_total,${name1}_cogs_setup_total,${name1}_cogs_monthly_total,${name1}_cogs_setup_from_perc,${name1}_cogs_monthly_from_perc,${name1}_maintenance_month,
|
|
||||||
${name1}_revenue_monthly_recurring,${name1}_cogs_monthly_recurring'
|
|
||||||
sheetname:'bizmodel_tf9
|
|
||||||
|
|
||||||
resulting revenues:
|
|
||||||
!!!spreadsheet.sheet_wiki
|
|
||||||
namefilter:'${name1}_revenue_total,${name1}_cogs_total'
|
|
||||||
sheetname:'bizmodel_tf9
|
|
||||||
|
|
||||||
|
|
||||||
!!!spreadsheet.graph_line_row rowname:'${name1}_cogs_total' unit:million sheetname:'bizmodel_tf9'
|
|
||||||
|
|
||||||
!!!spreadsheet.graph_line_row rowname:'${name1}_revenue_total' unit:million sheetname:'bizmodel_tf9'
|
|
||||||
|
|
||||||
|
|
||||||
@end //product has_revenue
|
|
||||||
|
|
||||||
@end //loop
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
module spreadsheet
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.data.markdownparser.elements
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
import freeflowuniverse.herolib.web.echarts
|
|
||||||
|
|
||||||
pub fn (s Sheet) title_chart(args RowGetArgs) echarts.EChartsOption {
|
|
||||||
return echarts.EChartsOption{
|
|
||||||
title: echarts.Title{
|
|
||||||
text: args.title
|
|
||||||
subtext: args.title_sub
|
|
||||||
left: 'center'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (s Sheet) line_chart(args_ RowGetArgs) !echarts.EChartsOption {
|
|
||||||
mut args := args_
|
|
||||||
|
|
||||||
rownames := s.rownames_get(args)!
|
|
||||||
header := s.header_get_as_string(args.period_type)!
|
|
||||||
mut series := []echarts.Series{}
|
|
||||||
|
|
||||||
for rowname in rownames {
|
|
||||||
data := s.data_get_as_string(RowGetArgs{
|
|
||||||
...args
|
|
||||||
rowname: rowname
|
|
||||||
})!
|
|
||||||
series << echarts.Series{
|
|
||||||
name: rowname
|
|
||||||
type_: 'line'
|
|
||||||
stack: 'Total'
|
|
||||||
data: data.split(',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return echarts.EChartsOption{
|
|
||||||
title: s.title_chart(args).title
|
|
||||||
tooltip: echarts.Tooltip{
|
|
||||||
trigger: 'axis'
|
|
||||||
}
|
|
||||||
legend: echarts.Legend{
|
|
||||||
data: rownames
|
|
||||||
}
|
|
||||||
grid: echarts.Grid{
|
|
||||||
left: '3%'
|
|
||||||
right: '4%'
|
|
||||||
bottom: '3%'
|
|
||||||
contain_label: true
|
|
||||||
}
|
|
||||||
toolbox: echarts.Toolbox{
|
|
||||||
feature: echarts.ToolboxFeature{
|
|
||||||
save_as_image: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x_axis: echarts.XAxis{
|
|
||||||
type_: 'category'
|
|
||||||
boundary_gap: false
|
|
||||||
data: header.split(',')
|
|
||||||
}
|
|
||||||
y_axis: echarts.YAxis{
|
|
||||||
type_: 'value'
|
|
||||||
}
|
|
||||||
series: series
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (s Sheet) bar_chart(args_ RowGetArgs) !echarts.EChartsOption {
|
|
||||||
mut args := args_
|
|
||||||
args.rowname = s.rowname_get(args)!
|
|
||||||
header := s.header_get_as_list(args.period_type)!
|
|
||||||
data := s.data_get_as_list(args)!
|
|
||||||
|
|
||||||
return echarts.EChartsOption{
|
|
||||||
title: s.title_chart(args).title
|
|
||||||
x_axis: echarts.XAxis{
|
|
||||||
type_: 'category'
|
|
||||||
data: header
|
|
||||||
}
|
|
||||||
y_axis: echarts.YAxis{
|
|
||||||
type_: 'value'
|
|
||||||
}
|
|
||||||
series: [
|
|
||||||
echarts.Series{
|
|
||||||
name: args.rowname
|
|
||||||
type_: 'bar'
|
|
||||||
data: data
|
|
||||||
stack: ''
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (s Sheet) pie_chart(args_ RowGetArgs) !echarts.EChartsOption {
|
|
||||||
mut args := args_
|
|
||||||
args.rowname = s.rowname_get(args)!
|
|
||||||
header := s.header_get_as_list(args.period_type)!
|
|
||||||
data := s.data_get_as_list(args)!
|
|
||||||
|
|
||||||
if header.len != data.len {
|
|
||||||
return error('Data and header lengths must match.')
|
|
||||||
}
|
|
||||||
|
|
||||||
mut pie_data := []map[string]string{}
|
|
||||||
for i, _ in data {
|
|
||||||
pie_data << {
|
|
||||||
'value': data[i].trim_space().trim("'")
|
|
||||||
'name': header[i].trim_space().trim("'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return echarts.EChartsOption{
|
|
||||||
title: s.title_chart(args).title
|
|
||||||
tooltip: echarts.Tooltip{
|
|
||||||
trigger: 'item'
|
|
||||||
}
|
|
||||||
legend: echarts.Legend{
|
|
||||||
data: header
|
|
||||||
orient: 'vertical'
|
|
||||||
left: 'left'
|
|
||||||
}
|
|
||||||
series: [
|
|
||||||
echarts.Series{
|
|
||||||
name: 'Data'
|
|
||||||
type_: 'pie'
|
|
||||||
radius: args.size.int()
|
|
||||||
data: pie_data.map(it.str())
|
|
||||||
emphasis: echarts.Emphasis{
|
|
||||||
item_style: echarts.ItemStyle{
|
|
||||||
shadow_blur: 10
|
|
||||||
shadow_offset_x: 0
|
|
||||||
shadow_color: 'rgba(0, 0, 0, 0.5)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
module spreadsheet
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.data.markdownparser.elements
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
import freeflowuniverse.herolib.web.echarts
|
|
||||||
|
|
||||||
fn test_title_chart() {
|
|
||||||
mut s := sheet_new() or { panic(err) }
|
|
||||||
mut nrnodes := s.row_new(
|
|
||||||
name: 'nrnodes'
|
|
||||||
growth: '5:100,55:1000'
|
|
||||||
tags: 'cat:nodes color:yellow urgent'
|
|
||||||
)!
|
|
||||||
args := RowGetArgs{
|
|
||||||
rowname: 'nrnodes'
|
|
||||||
title: 'Main Title'
|
|
||||||
title_sub: 'Subtitle'
|
|
||||||
}
|
|
||||||
title := s.title_chart(args).title
|
|
||||||
assert title.text == 'Main Title'
|
|
||||||
assert title.subtext == 'Subtitle'
|
|
||||||
assert title.left == 'center'
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_line_chart() {
|
|
||||||
mut s := sheet_new() or { panic(err) }
|
|
||||||
mut nrnodes := s.row_new(
|
|
||||||
name: 'nrnodes'
|
|
||||||
growth: '5:100,55:1000'
|
|
||||||
tags: 'cat:nodes color:yellow urgent'
|
|
||||||
)!
|
|
||||||
args := RowGetArgs{
|
|
||||||
rowname: 'nrnodes'
|
|
||||||
title: 'Line Chart'
|
|
||||||
period_type: .month
|
|
||||||
}
|
|
||||||
option := s.line_chart(args) or { panic(err) }
|
|
||||||
assert option.title.text == 'Line Chart'
|
|
||||||
assert option.tooltip.trigger == 'axis'
|
|
||||||
assert option.grid.contain_label == true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_bar_chart() {
|
|
||||||
mut s := sheet_new() or { panic(err) }
|
|
||||||
mut nrnodes := s.row_new(
|
|
||||||
name: 'nrnodes'
|
|
||||||
growth: '5:100,55:1000'
|
|
||||||
tags: 'cat:nodes color:yellow urgent'
|
|
||||||
)!
|
|
||||||
args := RowGetArgs{
|
|
||||||
rowname: 'nrnodes'
|
|
||||||
title: 'Bar Chart'
|
|
||||||
period_type: .year
|
|
||||||
}
|
|
||||||
option := s.bar_chart(args) or { panic(err) }
|
|
||||||
assert option.title.text == 'Bar Chart'
|
|
||||||
assert option.x_axis.type_ == 'category'
|
|
||||||
assert option.y_axis.type_ == 'value'
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_pie_chart() {
|
|
||||||
mut s := sheet_new() or { panic(err) }
|
|
||||||
mut nrnodes := s.row_new(
|
|
||||||
name: 'nrnodes'
|
|
||||||
growth: '5:100,55:1000'
|
|
||||||
tags: 'cat:nodes color:yellow urgent'
|
|
||||||
)!
|
|
||||||
args := RowGetArgs{
|
|
||||||
rowname: 'nrnodes'
|
|
||||||
title: 'Pie Chart'
|
|
||||||
period_type: .quarter
|
|
||||||
}
|
|
||||||
option := s.pie_chart(args) or { panic(err) }
|
|
||||||
assert option.title.text == 'Pie Chart'
|
|
||||||
assert option.tooltip.trigger == 'item'
|
|
||||||
assert option.legend.data.len > 0
|
|
||||||
}
|
|
||||||
@@ -92,7 +92,7 @@ pub fn playmacro(action Action) !string {
|
|||||||
content = sh.wiki(args) or { panic(err) }
|
content = sh.wiki(args) or { panic(err) }
|
||||||
}
|
}
|
||||||
'graph_title_row' {
|
'graph_title_row' {
|
||||||
content = sh.wiki_title_chart(args)!
|
content = sh.wiki_title_chart(args)
|
||||||
}
|
}
|
||||||
'graph_line_row' {
|
'graph_line_row' {
|
||||||
content = sh.wiki_line_chart(args)!
|
content = sh.wiki_line_chart(args)!
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ pub fn (s Sheet) tosmaller(args_ ToYearQuarterArgs) !&Sheet {
|
|||||||
// tagsfilter []string
|
// tagsfilter []string
|
||||||
// tags if set will see that there is at least one corresponding tag per row
|
// tags if set will see that there is at least one corresponding tag per row
|
||||||
// rawsfilter is list of names of rows which will be included
|
// rawsfilter is list of names of rows which will be included
|
||||||
pub fn (s Sheet) toyear(args ToYearQuarterArgs) !&Sheet {
|
pub fn (mut s Sheet) toyear(args ToYearQuarterArgs) !&Sheet {
|
||||||
mut args2 := args
|
mut args2 := args
|
||||||
args2.period_months = 12
|
args2.period_months = 12
|
||||||
return s.tosmaller(args2)
|
return s.tosmaller(args2)
|
||||||
@@ -215,7 +215,7 @@ pub fn (s Sheet) toyear(args ToYearQuarterArgs) !&Sheet {
|
|||||||
// tagsfilter []string
|
// tagsfilter []string
|
||||||
// tags if set will see that there is at least one corresponding tag per row
|
// tags if set will see that there is at least one corresponding tag per row
|
||||||
// rawsfilter is list of names of rows which will be included
|
// rawsfilter is list of names of rows which will be included
|
||||||
pub fn (s Sheet) toquarter(args ToYearQuarterArgs) !&Sheet {
|
pub fn (mut s Sheet) toquarter(args ToYearQuarterArgs) !&Sheet {
|
||||||
mut args2 := args
|
mut args2 := args
|
||||||
args2.period_months = 3
|
args2.period_months = 3
|
||||||
return s.tosmaller(args2)
|
return s.tosmaller(args2)
|
||||||
@@ -260,9 +260,7 @@ pub fn (mut s Sheet) json() string {
|
|||||||
|
|
||||||
// find row, report error if not found
|
// find row, report error if not found
|
||||||
pub fn (s Sheet) row_get(name string) !&Row {
|
pub fn (s Sheet) row_get(name string) !&Row {
|
||||||
row := s.rows[name] or {
|
row := s.rows[name] or { return error('could not find row with name: ${name}, available rows: ${s.rows.keys()}') }
|
||||||
return error('could not find row with name: ${name}, available rows: ${s.rows.keys()}')
|
|
||||||
}
|
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
module spreadsheet
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct ExportArgs{
|
|
||||||
pub mut:
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_number(val f64) string {
|
|
||||||
if val < 0.001 && val > -0.001 {
|
|
||||||
return '0'
|
|
||||||
}
|
|
||||||
if val >= 1000.0 || val <= -1000.0 {
|
|
||||||
return int(val).str()
|
|
||||||
}
|
|
||||||
// Format small numbers with 3 decimal places to handle floating point precision
|
|
||||||
return '${val:.3f}'
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut s Sheet) export(args ExportArgs) !string {
|
|
||||||
mut result := []string{}
|
|
||||||
|
|
||||||
// Add headers
|
|
||||||
mut header_row := ['Name', 'Description', 'AggregateType', 'Tags', 'Subgroup']
|
|
||||||
header_row << s.header()!
|
|
||||||
result << header_row.join('|')
|
|
||||||
|
|
||||||
// Add rows
|
|
||||||
for _, row in s.rows {
|
|
||||||
mut row_data := [row.name, row.description, row.aggregatetype.str(), row.tags, row.subgroup]
|
|
||||||
for cell in row.cells {
|
|
||||||
if cell.empty {
|
|
||||||
row_data << '-'
|
|
||||||
} else {
|
|
||||||
row_data << format_number(cell.val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result << row_data.join('|')
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.path.len>0{
|
|
||||||
mut p:=pathlib.get_file(path:args.path.replace("~",os.home_dir()), create:true, delete:true)!
|
|
||||||
p.write(result.join('\n'))!
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.join('\n')
|
|
||||||
}
|
|
||||||
@@ -71,19 +71,19 @@ pub fn (s Sheet) rowname_get(args RowGetArgs) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return e.g. "'Y1', 'Y2', 'Y3', 'Y4', 'Y5', 'Y6'" if year, is for header
|
// return e.g. "'Y1', 'Y2', 'Y3', 'Y4', 'Y5', 'Y6'" if year, is for header
|
||||||
pub fn (s Sheet) header_get_as_list(period_type PeriodType) ![]string {
|
pub fn (mut s Sheet) header_get_as_list(period_type PeriodType) ![]string {
|
||||||
str := s.header_get_as_string(period_type)!
|
str := s.header_get_as_string(period_type)!
|
||||||
return str.split(',')
|
return str.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
// return e.g. "'Y1', 'Y2', 'Y3', 'Y4', 'Y5', 'Y6'" if year, is for header
|
// return e.g. "'Y1', 'Y2', 'Y3', 'Y4', 'Y5', 'Y6'" if year, is for header
|
||||||
pub fn (s Sheet) data_get_as_list(args RowGetArgs) ![]string {
|
pub fn (mut s Sheet) data_get_as_list(args RowGetArgs) ![]string {
|
||||||
str := s.data_get_as_string(args)!
|
str := s.data_get_as_string(args)!
|
||||||
return str.split(',')
|
return str.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
// return e.g. "'Y1', 'Y2', 'Y3', 'Y4', 'Y5', 'Y6'" if year, is for header
|
// return e.g. "'Y1', 'Y2', 'Y3', 'Y4', 'Y5', 'Y6'" if year, is for header
|
||||||
pub fn (s Sheet) header_get_as_string(period_type PeriodType) !string {
|
pub fn (mut s Sheet) header_get_as_string(period_type PeriodType) !string {
|
||||||
err_pre := "Can't get header for sheet:${s.name}\n"
|
err_pre := "Can't get header for sheet:${s.name}\n"
|
||||||
nryears := int(s.nrcol / 12)
|
nryears := int(s.nrcol / 12)
|
||||||
mut out := ''
|
mut out := ''
|
||||||
@@ -112,7 +112,7 @@ pub fn (s Sheet) header_get_as_string(period_type PeriodType) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return the values
|
// return the values
|
||||||
pub fn (s Sheet) data_get_as_string(args RowGetArgs) !string {
|
pub fn (mut s Sheet) data_get_as_string(args RowGetArgs) !string {
|
||||||
if args.rowname == '' {
|
if args.rowname == '' {
|
||||||
return error('rowname needs to be specified')
|
return error('rowname needs to be specified')
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ pub fn (s Sheet) data_get_as_string(args RowGetArgs) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use RowGetArgs to get to smaller version of sheet
|
// use RowGetArgs to get to smaller version of sheet
|
||||||
pub fn (s Sheet) filter(args RowGetArgs) !&Sheet {
|
pub fn (mut s Sheet) filter(args RowGetArgs) !&Sheet {
|
||||||
period_months := match args.period_type {
|
period_months := match args.period_type {
|
||||||
.year { 12 }
|
.year { 12 }
|
||||||
.month { 1 }
|
.month { 1 }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import freeflowuniverse.herolib.core.texttools
|
|||||||
import freeflowuniverse.herolib.ui.console
|
import freeflowuniverse.herolib.ui.console
|
||||||
// format a sheet properly in wiki format
|
// format a sheet properly in wiki format
|
||||||
|
|
||||||
pub fn (s Sheet) wiki(args_ RowGetArgs) !string {
|
pub fn (mut s Sheet) wiki(args_ RowGetArgs) !string {
|
||||||
mut args := args_
|
mut args := args_
|
||||||
|
|
||||||
_ := match args.period_type {
|
_ := match args.period_type {
|
||||||
|
|||||||
@@ -3,12 +3,22 @@ module spreadsheet
|
|||||||
import freeflowuniverse.herolib.data.markdownparser.elements
|
import freeflowuniverse.herolib.data.markdownparser.elements
|
||||||
import freeflowuniverse.herolib.ui.console
|
import freeflowuniverse.herolib.ui.console
|
||||||
|
|
||||||
pub fn (s Sheet) wiki_title_chart(args RowGetArgs) !string {
|
pub fn (mut s Sheet) wiki_title_chart(args RowGetArgs) string {
|
||||||
return s.title_chart(args).markdown()
|
if args.title.len > 0 {
|
||||||
|
titletxt := "
|
||||||
|
title: {
|
||||||
|
text: '${args.title}',
|
||||||
|
subtext: '${args.title_sub}',
|
||||||
|
left: 'center'
|
||||||
|
},
|
||||||
|
"
|
||||||
|
return titletxt
|
||||||
|
}
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s_ Sheet) wiki_row_overview(args RowGetArgs) !string {
|
pub fn (mut s_ Sheet) wiki_row_overview(args RowGetArgs) !string {
|
||||||
s := s_.filter(args)!
|
mut s := s_.filter(args)!
|
||||||
|
|
||||||
rows_values := s.rows.values().map([it.name, it.description, it.tags])
|
rows_values := s.rows.values().map([it.name, it.description, it.tags])
|
||||||
mut rows := []elements.Row{}
|
mut rows := []elements.Row{}
|
||||||
@@ -33,18 +43,146 @@ pub fn (s_ Sheet) wiki_row_overview(args RowGetArgs) !string {
|
|||||||
|
|
||||||
// produce a nice looking bar chart see
|
// produce a nice looking bar chart see
|
||||||
// https://echarts.apache.org/examples/en/editor.html?c=line-stack
|
// https://echarts.apache.org/examples/en/editor.html?c=line-stack
|
||||||
pub fn (s Sheet) wiki_line_chart(args_ RowGetArgs) !string {
|
pub fn (mut s Sheet) wiki_line_chart(args_ RowGetArgs) !string {
|
||||||
return s.line_chart(args_)!.markdown()
|
mut args := args_
|
||||||
|
|
||||||
|
rownames := s.rownames_get(args)!
|
||||||
|
header := s.header_get_as_string(args.period_type)!
|
||||||
|
mut series_lines := []string{}
|
||||||
|
|
||||||
|
for rowname in rownames {
|
||||||
|
data := s.data_get_as_string(RowGetArgs{
|
||||||
|
...args
|
||||||
|
rowname: rowname
|
||||||
|
})!
|
||||||
|
series_lines << '{
|
||||||
|
name: \'${rowname}\',
|
||||||
|
type: \'line\',
|
||||||
|
stack: \'Total\',
|
||||||
|
data: [${data}]
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: need to implement the multiple results which can come back from the args, can be more than 1
|
||||||
|
|
||||||
|
// header := s.header_get_as_string(args.period_type)!
|
||||||
|
// data := s.data_get_as_string(args)!
|
||||||
|
// console.print_debug('HERE! ${header}')
|
||||||
|
// console.print_debug('HERE!! ${data}')
|
||||||
|
|
||||||
|
template := "
|
||||||
|
${s.wiki_title_chart(args)}
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ${rownames}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
saveAsImage: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [${header}]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [${series_lines.join(',')}]
|
||||||
|
"
|
||||||
|
out := remove_empty_line('```echarts\n{${template}\n};\n```\n')
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// produce a nice looking bar chart see
|
// produce a nice looking bar chart see
|
||||||
// https://echarts.apache.org/examples/en/index.html#chart-type-bar
|
// https://echarts.apache.org/examples/en/index.html#chart-type-bar
|
||||||
pub fn (s Sheet) wiki_bar_chart(args_ RowGetArgs) !string {
|
pub fn (mut s Sheet) wiki_bar_chart(args_ RowGetArgs) !string {
|
||||||
return s.bar_chart(args_)!.markdown()
|
mut args := args_
|
||||||
|
args.rowname = s.rowname_get(args)!
|
||||||
|
header := s.header_get_as_string(args.period_type)!
|
||||||
|
data := s.data_get_as_string(args)!
|
||||||
|
bar1 := "
|
||||||
|
${s.wiki_title_chart(args)}
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: [${header}]
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [${data}],
|
||||||
|
type: 'bar',
|
||||||
|
showBackground: true,
|
||||||
|
backgroundStyle: {
|
||||||
|
color: 'rgba(180, 180, 180, 0.2)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"
|
||||||
|
out := remove_empty_line('```echarts\n{${bar1}\n};\n```\n')
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// produce a nice looking bar chart see
|
// produce a nice looking bar chart see
|
||||||
// https://echarts.apache.org/examples/en/index.html#chart-type-bar
|
// https://echarts.apache.org/examples/en/index.html#chart-type-bar
|
||||||
pub fn (s Sheet) wiki_pie_chart(args_ RowGetArgs) !string {
|
pub fn (mut s Sheet) wiki_pie_chart(args_ RowGetArgs) !string {
|
||||||
return s.pie_chart(args_)!.markdown()
|
mut args := args_
|
||||||
|
args.rowname = s.rowname_get(args)!
|
||||||
|
header := s.header_get_as_list(args.period_type)!
|
||||||
|
data := s.data_get_as_list(args)!
|
||||||
|
|
||||||
|
mut radius := ''
|
||||||
|
if args.size.len > 0 {
|
||||||
|
radius = "radius: '${args.size}',"
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.len != data.len {
|
||||||
|
return error('data and header lengths must match.\n${header}\n${data}')
|
||||||
|
}
|
||||||
|
|
||||||
|
mut data_lines := []string{}
|
||||||
|
for i, _ in data {
|
||||||
|
data_lines << '{ value: ${data[i]}, name: ${header[i]}}'
|
||||||
|
}
|
||||||
|
data_str := '[${data_lines.join(',')}]'
|
||||||
|
|
||||||
|
bar1 := "
|
||||||
|
${s.wiki_title_chart(args)}
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Access From',
|
||||||
|
type: 'pie',
|
||||||
|
${radius}
|
||||||
|
data: ${data_str},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
"
|
||||||
|
out := remove_empty_line('```echarts\n{${bar1}\n};\n```\n')
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
# DedupeStore
|
|
||||||
|
|
||||||
DedupeStore is a content-addressable key-value store with built-in deduplication. It uses blake2b-160 content hashing to identify and deduplicate data, making it ideal for storing files or data blocks where the same content might appear multiple times.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Content-based deduplication using blake2b-160 hashing
|
|
||||||
- Efficient storage using RadixTree for hash lookups
|
|
||||||
- Persistent storage using OurDB
|
|
||||||
- Maximum value size limit of 1MB
|
|
||||||
- Fast retrieval of data using content hash
|
|
||||||
- Automatic deduplication of identical content
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```v
|
|
||||||
import freeflowuniverse.herolib.data.dedupestor
|
|
||||||
|
|
||||||
fn main() ! {
|
|
||||||
// Create a new dedupestore
|
|
||||||
mut ds := dedupestor.new(
|
|
||||||
path: 'path/to/store'
|
|
||||||
reset: false // Set to true to reset existing data
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Store some data
|
|
||||||
data := 'Hello, World!'.bytes()
|
|
||||||
hash := ds.store(data)!
|
|
||||||
println('Stored data with hash: ${hash}')
|
|
||||||
|
|
||||||
// Retrieve data using hash
|
|
||||||
retrieved := ds.get(hash)!
|
|
||||||
println('Retrieved data: ${retrieved.bytestr()}')
|
|
||||||
|
|
||||||
// Check if data exists
|
|
||||||
exists := ds.exists(hash)
|
|
||||||
println('Data exists: ${exists}')
|
|
||||||
|
|
||||||
// Attempting to store the same data again returns the same hash
|
|
||||||
same_hash := ds.store(data)!
|
|
||||||
assert hash == same_hash // True, data was deduplicated
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
DedupeStore uses two main components for storage:
|
|
||||||
|
|
||||||
1. **RadixTree**: Stores mappings from content hashes to data location IDs
|
|
||||||
2. **OurDB**: Stores the actual data blocks
|
|
||||||
|
|
||||||
When storing data:
|
|
||||||
1. The data is hashed using blake2b-160
|
|
||||||
2. If the hash exists in the RadixTree, the existing data location is returned
|
|
||||||
3. If the hash is new:
|
|
||||||
- Data is stored in OurDB, getting a new location ID
|
|
||||||
- Hash -> ID mapping is stored in RadixTree
|
|
||||||
- The hash is returned
|
|
||||||
|
|
||||||
When retrieving data:
|
|
||||||
1. The RadixTree is queried with the hash to get the data location ID
|
|
||||||
2. The data is retrieved from OurDB using the ID
|
|
||||||
|
|
||||||
## Size Limits
|
|
||||||
|
|
||||||
- Maximum value size: 1MB
|
|
||||||
- Attempting to store larger values will result in an error
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
The store methods return results that should be handled with V's error handling:
|
|
||||||
|
|
||||||
```v
|
|
||||||
// Handle potential errors
|
|
||||||
if hash := ds.store(large_data) {
|
|
||||||
// Success
|
|
||||||
println('Stored with hash: ${hash}')
|
|
||||||
} else {
|
|
||||||
// Error occurred
|
|
||||||
println('Error: ${err}')
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
The module includes comprehensive tests covering:
|
|
||||||
- Basic store/retrieve operations
|
|
||||||
- Deduplication functionality
|
|
||||||
- Size limit enforcement
|
|
||||||
- Edge cases
|
|
||||||
|
|
||||||
Run tests with:
|
|
||||||
```bash
|
|
||||||
v test lib/data/dedupestor/
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
module dedupestor
|
|
||||||
|
|
||||||
import crypto.blake2b
|
|
||||||
import freeflowuniverse.herolib.data.radixtree
|
|
||||||
import freeflowuniverse.herolib.data.ourdb
|
|
||||||
|
|
||||||
pub const max_value_size = 1024 * 1024 // 1MB
|
|
||||||
|
|
||||||
// DedupeStore provides a key-value store with deduplication based on content hashing
|
|
||||||
pub struct DedupeStore {
|
|
||||||
mut:
|
|
||||||
radix &radixtree.RadixTree // For storing hash -> id mappings
|
|
||||||
data &ourdb.OurDB // For storing the actual data
|
|
||||||
}
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct NewArgs {
|
|
||||||
pub mut:
|
|
||||||
path string // Base path for the store
|
|
||||||
reset bool // Whether to reset existing data
|
|
||||||
}
|
|
||||||
|
|
||||||
// new creates a new deduplication store
|
|
||||||
pub fn new(args NewArgs) !&DedupeStore {
|
|
||||||
// Create the radixtree for hash -> id mapping
|
|
||||||
mut rt := radixtree.new(
|
|
||||||
path: '${args.path}/radixtree'
|
|
||||||
reset: args.reset
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Create the ourdb for actual data storage
|
|
||||||
mut db := ourdb.new(
|
|
||||||
path: '${args.path}/data'
|
|
||||||
record_size_max: max_value_size
|
|
||||||
incremental_mode: true // We want auto-incrementing IDs
|
|
||||||
reset: args.reset
|
|
||||||
)!
|
|
||||||
|
|
||||||
return &DedupeStore{
|
|
||||||
radix: rt
|
|
||||||
data: db
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// store stores a value and returns its hash
|
|
||||||
// If the value already exists (same hash), returns the existing hash without storing again
|
|
||||||
pub fn (mut ds DedupeStore) store(value []u8) !string {
|
|
||||||
// Check size limit
|
|
||||||
if value.len > max_value_size {
|
|
||||||
return error('value size exceeds maximum allowed size of 1MB')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate blake160 hash of the value
|
|
||||||
hash := blake2b.sum160(value).hex()
|
|
||||||
|
|
||||||
// Check if this hash already exists
|
|
||||||
if _ := ds.radix.search(hash) {
|
|
||||||
// Value already exists, return the hash
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the actual data in ourdb
|
|
||||||
id := ds.data.set(data: value)!
|
|
||||||
|
|
||||||
// Convert id to bytes for storage in radixtree
|
|
||||||
id_bytes := u32_to_bytes(id)
|
|
||||||
|
|
||||||
// Store the mapping of hash -> id in radixtree
|
|
||||||
ds.radix.insert(hash, id_bytes)!
|
|
||||||
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// get retrieves a value by its hash
|
|
||||||
pub fn (mut ds DedupeStore) get(hash string) ![]u8 {
|
|
||||||
// Get the ID from radixtree
|
|
||||||
id_bytes := ds.radix.search(hash)!
|
|
||||||
|
|
||||||
// Convert bytes back to u32 id
|
|
||||||
id := bytes_to_u32(id_bytes)
|
|
||||||
|
|
||||||
// Get the actual data from ourdb
|
|
||||||
return ds.data.get(id)!
|
|
||||||
}
|
|
||||||
|
|
||||||
// exists checks if a value with the given hash exists
|
|
||||||
pub fn (mut ds DedupeStore) exists(hash string) bool {
|
|
||||||
return if _ := ds.radix.search(hash) { true } else { false }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to convert u32 to []u8
|
|
||||||
fn u32_to_bytes(n u32) []u8 {
|
|
||||||
return [u8(n), u8(n >> 8), u8(n >> 16), u8(n >> 24)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to convert []u8 to u32
|
|
||||||
fn bytes_to_u32(b []u8) u32 {
|
|
||||||
return u32(b[0]) | (u32(b[1]) << 8) | (u32(b[2]) << 16) | (u32(b[3]) << 24)
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
module dedupestor
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
fn testsuite_begin() ! {
|
|
||||||
// Ensure test directories exist and are clean
|
|
||||||
test_dirs := [
|
|
||||||
'/tmp/dedupestor_test',
|
|
||||||
'/tmp/dedupestor_test_size',
|
|
||||||
'/tmp/dedupestor_test_exists',
|
|
||||||
'/tmp/dedupestor_test_multiple'
|
|
||||||
]
|
|
||||||
|
|
||||||
for dir in test_dirs {
|
|
||||||
if os.exists(dir) {
|
|
||||||
os.rmdir_all(dir) or {}
|
|
||||||
}
|
|
||||||
os.mkdir_all(dir) or {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_basic_operations() ! {
|
|
||||||
mut ds := new(
|
|
||||||
path: '/tmp/dedupestor_test'
|
|
||||||
reset: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Test storing and retrieving data
|
|
||||||
value1 := 'test data 1'.bytes()
|
|
||||||
hash1 := ds.store(value1)!
|
|
||||||
|
|
||||||
retrieved1 := ds.get(hash1)!
|
|
||||||
assert retrieved1 == value1
|
|
||||||
|
|
||||||
// Test deduplication
|
|
||||||
hash2 := ds.store(value1)!
|
|
||||||
assert hash1 == hash2 // Should return same hash for same data
|
|
||||||
|
|
||||||
// Test different data gets different hash
|
|
||||||
value2 := 'test data 2'.bytes()
|
|
||||||
hash3 := ds.store(value2)!
|
|
||||||
assert hash1 != hash3 // Should be different hash for different data
|
|
||||||
|
|
||||||
retrieved2 := ds.get(hash3)!
|
|
||||||
assert retrieved2 == value2
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_size_limit() ! {
|
|
||||||
mut ds := new(
|
|
||||||
path: '/tmp/dedupestor_test_size'
|
|
||||||
reset: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Test data under size limit (1KB)
|
|
||||||
small_data := []u8{len: 1024, init: u8(index)}
|
|
||||||
small_hash := ds.store(small_data)!
|
|
||||||
retrieved := ds.get(small_hash)!
|
|
||||||
assert retrieved == small_data
|
|
||||||
|
|
||||||
// Test data over size limit (2MB)
|
|
||||||
large_data := []u8{len: 2 * 1024 * 1024, init: u8(index)}
|
|
||||||
if _ := ds.store(large_data) {
|
|
||||||
assert false, 'Expected error for data exceeding size limit'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_exists() ! {
|
|
||||||
mut ds := new(
|
|
||||||
path: '/tmp/dedupestor_test_exists'
|
|
||||||
reset: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
value := 'test data'.bytes()
|
|
||||||
hash := ds.store(value)!
|
|
||||||
|
|
||||||
assert ds.exists(hash) == true
|
|
||||||
assert ds.exists('nonexistent') == false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_multiple_operations() ! {
|
|
||||||
mut ds := new(
|
|
||||||
path: '/tmp/dedupestor_test_multiple'
|
|
||||||
reset: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Store multiple values
|
|
||||||
mut values := [][]u8{}
|
|
||||||
mut hashes := []string{}
|
|
||||||
|
|
||||||
for i in 0..5 {
|
|
||||||
value := 'test data ${i}'.bytes()
|
|
||||||
values << value
|
|
||||||
hash := ds.store(value)!
|
|
||||||
hashes << hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify all values can be retrieved
|
|
||||||
for i, hash in hashes {
|
|
||||||
retrieved := ds.get(hash)!
|
|
||||||
assert retrieved == values[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test deduplication by storing same values again
|
|
||||||
for i, value in values {
|
|
||||||
hash := ds.store(value)!
|
|
||||||
assert hash == hashes[i] // Should get same hash for same data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,15 +9,15 @@ import os
|
|||||||
pub const version = '1.14.3'
|
pub const version = '1.14.3'
|
||||||
const singleton = true
|
const singleton = true
|
||||||
const default = true
|
const default = true
|
||||||
pub const homedir = os.home_dir()
|
const homedir = os.home_dir()
|
||||||
|
|
||||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||||
@[heap]
|
@[heap]
|
||||||
pub struct DaguInstaller {
|
pub struct DaguInstaller {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string = 'default'
|
name string = 'default'
|
||||||
dagsdir string = '${os.home_dir()}/.dagu'
|
dagsdir string = '${homedir}/.dagu'
|
||||||
configpath string = '${os.home_dir()}/.config/dagu'
|
configpath string = '${homedir}/.config/dagu'
|
||||||
username string
|
username string
|
||||||
password string @[secret]
|
password string @[secret]
|
||||||
secret string @[secret]
|
secret string @[secret]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import os
|
|||||||
|
|
||||||
// checks if a certain version or above is installed
|
// checks if a certain version or above is installed
|
||||||
fn installed() !bool {
|
fn installed() !bool {
|
||||||
res := os.execute('tailwindcss -h')
|
res := os.execute('tailwind -h')
|
||||||
if res.exit_code == 0 {
|
if res.exit_code == 0 {
|
||||||
r := res.output.split_into_lines().filter(it.contains('tailwindcss v'))
|
r := res.output.split_into_lines().filter(it.contains('tailwindcss v'))
|
||||||
if r.len != 1 {
|
if r.len != 1 {
|
||||||
@@ -22,9 +22,6 @@ fn installed() !bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}else{
|
|
||||||
println("error in executing tailwindcss")
|
|
||||||
println(res)
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,22 @@ pub const version = '3.4.12'
|
|||||||
const singleton = false
|
const singleton = false
|
||||||
const default = true
|
const default = true
|
||||||
|
|
||||||
|
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
||||||
@[heap]
|
@[heap]
|
||||||
pub struct Tailwind {
|
pub struct Tailwind {
|
||||||
pub mut:
|
pub mut:
|
||||||
name string = 'default'
|
name string = 'default'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// your checking & initialization code if needed
|
||||||
fn obj_init(mycfg_ Tailwind) !Tailwind {
|
fn obj_init(mycfg_ Tailwind) !Tailwind {
|
||||||
mut mycfg := mycfg_
|
mut mycfg := mycfg_
|
||||||
return mycfg
|
return mycfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// called before start if done
|
||||||
fn configure() ! {
|
fn configure() ! {
|
||||||
|
// mut installer := get()!
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////NORMALLY NO NEED TO TOUCH
|
/////////////NORMALLY NO NEED TO TOUCH
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
!!hero_code.generate_installer
|
|
||||||
name:'tailwind4'
|
|
||||||
classname:'Tailwind'
|
|
||||||
singleton:0
|
|
||||||
templates:0
|
|
||||||
default:1
|
|
||||||
title:''
|
|
||||||
supported_platforms:''
|
|
||||||
reset:0
|
|
||||||
startupmanager:0
|
|
||||||
hasconfig:0
|
|
||||||
build:0
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# tailwind4
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
To get started
|
|
||||||
|
|
||||||
```vlang
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.installers.something. tailwind4
|
|
||||||
|
|
||||||
mut installer:= tailwind4.get()!
|
|
||||||
|
|
||||||
installer.start()!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## example heroscript
|
|
||||||
|
|
||||||
|
|
||||||
```hero
|
|
||||||
!!tailwind4.install
|
|
||||||
homedir: '/home/user/tailwind4'
|
|
||||||
username: 'admin'
|
|
||||||
password: 'secretpassword'
|
|
||||||
title: 'Some Title'
|
|
||||||
host: 'localhost'
|
|
||||||
port: 8888
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
module tailwind4
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.osal
|
|
||||||
import freeflowuniverse.herolib.core
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
import freeflowuniverse.herolib.installers.ulist
|
|
||||||
import os
|
|
||||||
//////////////////// following actions are not specific to instance of the object
|
|
||||||
|
|
||||||
// checks if a certain version or above is installed
|
|
||||||
fn installed() !bool {
|
|
||||||
res := os.execute('tailwindcss4 -h')
|
|
||||||
if res.exit_code == 0 {
|
|
||||||
r := res.output.split_into_lines().filter(it.contains('tailwindcss v'))
|
|
||||||
if r.len != 1 {
|
|
||||||
return error("couldn't parse tailwind4 version, expected 'tailwindcss v' on 1 row.\n${res.output}")
|
|
||||||
}
|
|
||||||
|
|
||||||
v := texttools.version(r[0].all_after(' '))
|
|
||||||
if v < texttools.version(version) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}else{
|
|
||||||
println("error in executing tailwindcss")
|
|
||||||
println(res)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the Upload List of the files
|
|
||||||
fn ulist_get() !ulist.UList {
|
|
||||||
// optionally build a UList which is all paths which are result of building, is then used e.g. in upload
|
|
||||||
return ulist.UList{}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn upload() ! {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn install() ! {
|
|
||||||
console.print_header('install tailwind4')
|
|
||||||
|
|
||||||
mut url := ''
|
|
||||||
if core.is_linux_arm()! {
|
|
||||||
url = 'https://github.com/tailwindlabs/tailwindcss/releases/download/v${version}/tailwindcss-linux-arm64'
|
|
||||||
} else if core.is_linux_intel()! {
|
|
||||||
url = 'https://github.com/tailwindlabs/tailwindcss/releases/download/v${version}/tailwindcss-linux-x64'
|
|
||||||
} else if core.is_osx_arm()! {
|
|
||||||
url = 'https://github.com/tailwindlabs/tailwindcss/releases/download/v${version}/tailwindcss-macos-arm64'
|
|
||||||
} else if core.is_osx_intel()! {
|
|
||||||
url = 'https://github.com/tailwindlabs/tailwindcss/releases/download/v${version}/tailwindcss-macos-x64'
|
|
||||||
} else {
|
|
||||||
return error('unsported platform')
|
|
||||||
}
|
|
||||||
|
|
||||||
mut dest := osal.download(
|
|
||||||
url: url
|
|
||||||
minsize_kb: 40000
|
|
||||||
// reset: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
osal.cmd_add(
|
|
||||||
cmdname: 'tailwind4'
|
|
||||||
source: dest.path
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
|
|
||||||
fn destroy() ! {}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
module tailwind4
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.playbook
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
import freeflowuniverse.herolib.sysadmin.startupmanager
|
|
||||||
import freeflowuniverse.herolib.osal.zinit
|
|
||||||
|
|
||||||
__global (
|
|
||||||
tailwind_global map[string]&Tailwind
|
|
||||||
tailwind_default string
|
|
||||||
)
|
|
||||||
|
|
||||||
/////////FACTORY
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct ArgsGet {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(args_ ArgsGet) !&Tailwind {
|
|
||||||
return &Tailwind{}
|
|
||||||
}
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct PlayArgs {
|
|
||||||
pub mut:
|
|
||||||
heroscript string // if filled in then plbook will be made out of it
|
|
||||||
plbook ?playbook.PlayBook
|
|
||||||
reset bool
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn play(args_ PlayArgs) ! {
|
|
||||||
mut args := args_
|
|
||||||
|
|
||||||
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
|
|
||||||
|
|
||||||
mut other_actions := plbook.find(filter: 'tailwind4.')!
|
|
||||||
for other_action in other_actions {
|
|
||||||
if other_action.name in ['destroy', 'install', 'build'] {
|
|
||||||
mut p := other_action.params
|
|
||||||
reset := p.get_default_false('reset')
|
|
||||||
if other_action.name == 'destroy' || reset {
|
|
||||||
console.print_debug('install action tailwind4.destroy')
|
|
||||||
destroy()!
|
|
||||||
}
|
|
||||||
if other_action.name == 'install' {
|
|
||||||
console.print_debug('install action tailwind4.install')
|
|
||||||
install()!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//////////////////////////# LIVE CYCLE MANAGEMENT FOR INSTALLERS ///////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
fn startupmanager_get(cat zinit.StartupManagerType) !startupmanager.StartupManager {
|
|
||||||
// unknown
|
|
||||||
// screen
|
|
||||||
// zinit
|
|
||||||
// tmux
|
|
||||||
// systemd
|
|
||||||
match cat {
|
|
||||||
.zinit {
|
|
||||||
console.print_debug('startupmanager: zinit')
|
|
||||||
return startupmanager.get(cat: .zinit)!
|
|
||||||
}
|
|
||||||
.systemd {
|
|
||||||
console.print_debug('startupmanager: systemd')
|
|
||||||
return startupmanager.get(cat: .systemd)!
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.print_debug('startupmanager: auto')
|
|
||||||
return startupmanager.get()!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct InstallArgs {
|
|
||||||
pub mut:
|
|
||||||
reset bool
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut self Tailwind) install(args InstallArgs) ! {
|
|
||||||
switch(self.name)
|
|
||||||
if args.reset || (!installed()!) {
|
|
||||||
install()!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut self Tailwind) destroy() ! {
|
|
||||||
switch(self.name)
|
|
||||||
destroy()!
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch instance to be used for tailwind4
|
|
||||||
pub fn switch(name string) {
|
|
||||||
tailwind_default = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct DefaultConfigArgs {
|
|
||||||
instance string = 'default'
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
module tailwind4
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.data.encoderhero
|
|
||||||
|
|
||||||
|
|
||||||
pub const version = '4.0.8'
|
|
||||||
const singleton = false
|
|
||||||
const default = true
|
|
||||||
|
|
||||||
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
|
|
||||||
@[heap]
|
|
||||||
pub struct Tailwind {
|
|
||||||
pub mut:
|
|
||||||
name string = 'default'
|
|
||||||
}
|
|
||||||
|
|
||||||
// your checking & initialization code if needed
|
|
||||||
fn obj_init(mycfg_ Tailwind) !Tailwind {
|
|
||||||
mut mycfg := mycfg_
|
|
||||||
return mycfg
|
|
||||||
}
|
|
||||||
|
|
||||||
// called before start if done
|
|
||||||
fn configure() ! {
|
|
||||||
// mut installer := get()!
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////NORMALLY NO NEED TO TOUCH
|
|
||||||
|
|
||||||
pub fn heroscript_dumps(obj Tailwind) !string {
|
|
||||||
return encoderhero.encode[Tailwind](obj)!
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn heroscript_loads(heroscript string) !Tailwind {
|
|
||||||
mut obj := encoderhero.decode[Tailwind](heroscript)!
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
@@ -52,7 +52,8 @@ pub fn (mut f DocusaurusFactory) get(args_ DSiteGetArgs) !&DocSite {
|
|||||||
|
|
||||||
// First, check if the new site args provides a configuration that can be written instead of template cfg dir
|
// First, check if the new site args provides a configuration that can be written instead of template cfg dir
|
||||||
if cfg := args.config {
|
if cfg := args.config {
|
||||||
cfg.write('${args.path}/cfg')!
|
panic("not implemented")
|
||||||
|
// cfg.write('${args.path}/cfg')!
|
||||||
} else {
|
} else {
|
||||||
// Then ensure cfg directory exists in src,
|
// Then ensure cfg directory exists in src,
|
||||||
if !os.exists('${args.path}/cfg') {
|
if !os.exists('${args.path}/cfg') {
|
||||||
@@ -70,7 +71,7 @@ pub fn (mut f DocusaurusFactory) get(args_ DSiteGetArgs) !&DocSite {
|
|||||||
if args.init{
|
if args.init{
|
||||||
mut template_cfg := template_path.dir_get('docs')!
|
mut template_cfg := template_path.dir_get('docs')!
|
||||||
template_cfg.copy(dest: '${args.path}/docs')!
|
template_cfg.copy(dest: '${args.path}/docs')!
|
||||||
} else{
|
}else{
|
||||||
return error("Can't find docs dir in chosen docusaurus location: ${args.path}")
|
return error("Can't find docs dir in chosen docusaurus location: ${args.path}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
module echarts
|
|
||||||
|
|
||||||
import json
|
|
||||||
import x.json2
|
|
||||||
|
|
||||||
pub struct Title {
|
|
||||||
pub:
|
|
||||||
text string @[json: 'text'; omitempty]
|
|
||||||
subtext string @[json: 'subtext'; omitempty]
|
|
||||||
left string @[json: 'left'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Tooltip {
|
|
||||||
pub:
|
|
||||||
trigger string @[json: 'trigger'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Legend {
|
|
||||||
pub:
|
|
||||||
data []string @[json: 'data'; omitempty]
|
|
||||||
orient string @[omitempty]
|
|
||||||
left string @[omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Grid {
|
|
||||||
pub:
|
|
||||||
left string @[json: 'left'; omitempty]
|
|
||||||
right string @[json: 'right'; omitempty]
|
|
||||||
bottom string @[json: 'bottom'; omitempty]
|
|
||||||
contain_label bool @[json: 'containLabel'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ToolboxFeature {
|
|
||||||
pub:
|
|
||||||
save_as_image map[string]string @[json: 'saveAsImage'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Toolbox {
|
|
||||||
pub:
|
|
||||||
feature ToolboxFeature @[json: 'feature'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct XAxis {
|
|
||||||
pub:
|
|
||||||
type_ string @[json: 'type'; omitempty]
|
|
||||||
boundary_gap bool @[json: 'boundaryGap'; omitempty]
|
|
||||||
data []string @[json: 'data'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct YAxis {
|
|
||||||
pub:
|
|
||||||
type_ string @[json: 'type'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Series {
|
|
||||||
pub:
|
|
||||||
name string @[json: 'name'; omitempty]
|
|
||||||
type_ string @[json: 'type'; omitempty]
|
|
||||||
stack string @[json: 'stack'; omitempty]
|
|
||||||
data []string @[json: 'data'; omitempty]
|
|
||||||
radius int @[omitempty]
|
|
||||||
emphasis Emphasis @[omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Emphasis {
|
|
||||||
pub:
|
|
||||||
item_style ItemStyle @[json: 'itemStyle'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ItemStyle {
|
|
||||||
pub:
|
|
||||||
shadow_blur int @[json: 'shadowBlur'; omitempty]
|
|
||||||
shadow_offset_x int @[json: 'shadowOffsetX'; omitempty]
|
|
||||||
shadow_color string @[json: 'shadowColor'; omitempty]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EChartsOption {
|
|
||||||
pub:
|
|
||||||
title Title @[json: 'title'; omitempty]
|
|
||||||
tooltip Tooltip @[json: 'tooltip'; omitempty]
|
|
||||||
legend Legend @[json: 'legend'; omitempty]
|
|
||||||
grid Grid @[json: 'grid'; omitempty]
|
|
||||||
toolbox Toolbox @[json: 'toolbox'; omitempty]
|
|
||||||
x_axis XAxis @[json: 'xAxis'; omitempty]
|
|
||||||
y_axis YAxis @[json: 'yAxis'; omitempty]
|
|
||||||
series []Series @[json: 'series'; omitempty]
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
module echarts
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
const option_json = '{"title":{"text":"Main Title","subtext":"Subtitle","left":"center"},"tooltip":{"trigger":"axis"},"legend":{"data":["Example1","Example2"]},"grid":{"left":"3%","right":"4%","bottom":"3%","containLabel":true},"xAxis":{"type":"category","data":["Jan","Feb","Mar"]},"yAxis":{"type":"value"},"series":[{"name":"Example1","type":"line","stack":"Total","data":["10","20","30"]},{"name":"Example2","type":"line","stack":"Total","data":["15","25","35"]}]}'
|
|
||||||
|
|
||||||
fn test_echarts() {
|
|
||||||
option := EChartsOption{
|
|
||||||
title: Title{
|
|
||||||
text: 'Main Title'
|
|
||||||
subtext: 'Subtitle'
|
|
||||||
left: 'center'
|
|
||||||
}
|
|
||||||
tooltip: Tooltip{
|
|
||||||
trigger: 'axis'
|
|
||||||
}
|
|
||||||
legend: Legend{
|
|
||||||
data: ['Example1', 'Example2']
|
|
||||||
}
|
|
||||||
grid: Grid{
|
|
||||||
left: '3%'
|
|
||||||
right: '4%'
|
|
||||||
bottom: '3%'
|
|
||||||
contain_label: true
|
|
||||||
}
|
|
||||||
toolbox: Toolbox{
|
|
||||||
feature: ToolboxFeature{
|
|
||||||
save_as_image: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x_axis: XAxis{
|
|
||||||
type_: 'category'
|
|
||||||
boundary_gap: false
|
|
||||||
data: ['Jan', 'Feb', 'Mar']
|
|
||||||
}
|
|
||||||
y_axis: YAxis{
|
|
||||||
type_: 'value'
|
|
||||||
}
|
|
||||||
series: [
|
|
||||||
Series{
|
|
||||||
name: 'Example1'
|
|
||||||
type_: 'line'
|
|
||||||
stack: 'Total'
|
|
||||||
data: ['10', '20', '30']
|
|
||||||
},
|
|
||||||
Series{
|
|
||||||
name: 'Example2'
|
|
||||||
type_: 'line'
|
|
||||||
stack: 'Total'
|
|
||||||
data: ['15', '25', '35']
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
assert json.encode(option) == option_json
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
module echarts
|
|
||||||
|
|
||||||
import x.json2 as json
|
|
||||||
|
|
||||||
pub fn (o EChartsOption) json() string {
|
|
||||||
return json.encode(o)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (o EChartsOption) mdx() string {
|
|
||||||
option := format_js_object(o, true)
|
|
||||||
return '<EChart option={${option}} />'
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (o EChartsOption) markdown() string {
|
|
||||||
option := format_js_object(o, true)
|
|
||||||
return '```echarts\n{${option}\n};\n```\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic function to format JavaScript-like objects
|
|
||||||
fn format_js_object[T](obj T, omitempty bool) string {
|
|
||||||
mut result := ''
|
|
||||||
result += '{'
|
|
||||||
|
|
||||||
$for field in T.fields {
|
|
||||||
field_name := if field.attrs.any(it.starts_with('json:')) {
|
|
||||||
field.attrs.filter(it.starts_with('json'))[0].all_after('json:').trim_space()
|
|
||||||
} else {
|
|
||||||
field.name
|
|
||||||
}
|
|
||||||
value := obj.$(field.name)
|
|
||||||
formatted_value := format_js_value(value, field.attrs.contains('omitempty'))
|
|
||||||
if formatted_value.trim_space() != '' || !omitempty {
|
|
||||||
result += '${field_name}: ${formatted_value.trim_space()}, '
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result += '}'
|
|
||||||
if result == '{}' && omitempty {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
return result.str().replace(', }', '}') // Remove trailing comma
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fully generic function to format any JS value
|
|
||||||
// TODO: improve code below, far from cleanest implementation
|
|
||||||
// currently is sufficient since only used in echart mdx export
|
|
||||||
fn format_js_value[T](value T, omitempty bool) string {
|
|
||||||
return $if T is string {
|
|
||||||
// is actually map
|
|
||||||
if value.str().starts_with('{') && value.str().ends_with('}') {
|
|
||||||
value
|
|
||||||
// map_any := json2.raw_decode(value.str()) or {'{}'}.as_map()
|
|
||||||
// println('debugzo21 ${map_any}')
|
|
||||||
// mut val := '{'
|
|
||||||
// for k, v in map_any {
|
|
||||||
// val += '${k}: ${format_js_value(v.str(), false)}'
|
|
||||||
// }
|
|
||||||
// val += '}'
|
|
||||||
// if val == '{}' && omitempty {
|
|
||||||
// return ''
|
|
||||||
// }
|
|
||||||
// val
|
|
||||||
} else {
|
|
||||||
val := '"${value}"'
|
|
||||||
if val == '""' && omitempty {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
val
|
|
||||||
}
|
|
||||||
} $else $if T is int {
|
|
||||||
if '${value}' == '0' && omitempty {
|
|
||||||
''
|
|
||||||
} else {
|
|
||||||
'${value}'
|
|
||||||
}
|
|
||||||
} $else $if T is f64 {
|
|
||||||
if '${value}' == '0.0' && omitempty {
|
|
||||||
''
|
|
||||||
} else {
|
|
||||||
'${value}'
|
|
||||||
}
|
|
||||||
} $else $if T is bool {
|
|
||||||
if '${value}' == 'false' && omitempty {
|
|
||||||
''
|
|
||||||
} else {
|
|
||||||
'${value}'
|
|
||||||
}
|
|
||||||
} $else $if T is $struct {
|
|
||||||
val := format_js_object(value, omitempty)
|
|
||||||
if val == '' && omitempty {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
val
|
|
||||||
} $else $if T is $array {
|
|
||||||
mut arr := '['
|
|
||||||
for i in 0 .. value.len {
|
|
||||||
if i != 0 {
|
|
||||||
arr += ', '
|
|
||||||
}
|
|
||||||
val := format_js_value(value[i], omitempty)
|
|
||||||
if val.starts_with('"{') && val.ends_with('}"') {
|
|
||||||
arr += val.trim('"')
|
|
||||||
} else if val.starts_with('"\'') && val.ends_with('\'"') {
|
|
||||||
arr += val.trim('"')
|
|
||||||
} else if val.trim('"').trim_space().f64() != 0 {
|
|
||||||
arr += val.trim('"').trim_space()
|
|
||||||
} else if val.trim('"').trim_space() == '0' || val.trim('"').trim_space() == '0.0' {
|
|
||||||
arr += '0'
|
|
||||||
} else {
|
|
||||||
arr += val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arr += ']'
|
|
||||||
if omitempty && arr == '[]' {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
arr
|
|
||||||
} $else {
|
|
||||||
'null'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import os
|
|
||||||
import strings
|
|
||||||
|
|
||||||
pub fn (mut site DocSite) clean(args ErrorArgs) ! {
|
|
||||||
toclean := '
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
babel.config.js
|
|
||||||
|
|
||||||
# Production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
.docusaurus
|
|
||||||
.cache-loader
|
|
||||||
|
|
||||||
# Misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
bun.lockb
|
|
||||||
bun.lock
|
|
||||||
|
|
||||||
yarn.lock
|
|
||||||
|
|
||||||
build.sh
|
|
||||||
build_dev.sh
|
|
||||||
build-dev.sh
|
|
||||||
develop.sh
|
|
||||||
install.sh
|
|
||||||
|
|
||||||
package.json
|
|
||||||
package-lock.json
|
|
||||||
pnpm-lock.yaml
|
|
||||||
|
|
||||||
sidebars.ts
|
|
||||||
|
|
||||||
tsconfig.json
|
|
||||||
'
|
|
||||||
|
|
||||||
//TODO: need better way how to deal with this
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
// Footer config structures
|
|
||||||
pub struct FooterItem {
|
|
||||||
pub mut:
|
|
||||||
label string
|
|
||||||
to string
|
|
||||||
href string
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct FooterLink {
|
|
||||||
pub mut:
|
|
||||||
title string
|
|
||||||
items []FooterItem
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Footer {
|
|
||||||
pub mut:
|
|
||||||
style string = 'dark'
|
|
||||||
links []FooterLink
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Main {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
title string = 'A Test Site'
|
|
||||||
// tagline string
|
|
||||||
url string = 'http://localhost/testsite'
|
|
||||||
// url_home string
|
|
||||||
// base_url string = '/' @[json: 'baseUrl']
|
|
||||||
// image string = 'img/tf_graph.png' @[required]
|
|
||||||
build_dest []string @[json: 'buildDest']
|
|
||||||
build_dest_dev []string @[json: 'buildDestDev']
|
|
||||||
content []ContentItem
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navbar config structures
|
|
||||||
pub struct NavbarItem {
|
|
||||||
pub mut:
|
|
||||||
href string
|
|
||||||
label string
|
|
||||||
position string
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Navbar {
|
|
||||||
pub mut:
|
|
||||||
title string
|
|
||||||
items []NavbarItem
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combined config structure
|
|
||||||
pub struct Config {
|
|
||||||
pub mut:
|
|
||||||
footer Footer
|
|
||||||
main Main
|
|
||||||
navbar Navbar
|
|
||||||
}
|
|
||||||
|
|
||||||
//pulled from e.g. git and linked to a destination in the astro build location
|
|
||||||
pub struct ContentItem {
|
|
||||||
pub mut:
|
|
||||||
url string
|
|
||||||
dest string
|
|
||||||
replacer map[string]string //items we want to replace
|
|
||||||
}
|
|
||||||
|
|
||||||
// load_config loads all configuration from the specified directory
|
|
||||||
pub fn load_config(cfg_dir string) !Config {
|
|
||||||
// Ensure the config directory exists
|
|
||||||
if !os.exists(cfg_dir) {
|
|
||||||
return error('Config directory ${cfg_dir} does not exist')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load and parse footer config
|
|
||||||
footer_content := os.read_file(os.join_path(cfg_dir, 'footer.json'))!
|
|
||||||
footer := json.decode(Footer, footer_content)!
|
|
||||||
|
|
||||||
// Load and parse main config
|
|
||||||
main_config_path := os.join_path(cfg_dir, 'main.json')
|
|
||||||
main_content := os.read_file(main_config_path)!
|
|
||||||
main := json.decode(Main, main_content) or {
|
|
||||||
eprintln('main.json in ${cfg_dir} is not in the right format please fix.\nError: ${err}')
|
|
||||||
println('
|
|
||||||
|
|
||||||
## EXAMPLE OF A GOOD ONE:
|
|
||||||
|
|
||||||
- note the list for buildDest and buildDestDev
|
|
||||||
- note its the full path where the html is pushed too
|
|
||||||
|
|
||||||
{
|
|
||||||
"title": "ThreeFold Web4",
|
|
||||||
"tagline": "ThreeFold Web4",
|
|
||||||
"url": "https://docs.threefold.io",
|
|
||||||
"url_home": "docs/introduction",
|
|
||||||
"image": "img/tf_graph.png",
|
|
||||||
"buildDest":["root@info.ourworld.tf:/root/hero/www/info/tfgrid4"],
|
|
||||||
"buildDestDev":["root@info.ourworld.tf:/root/hero/www/infodev/tfgrid4"]
|
|
||||||
|
|
||||||
}
|
|
||||||
')
|
|
||||||
exit(99)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load and parse navbar config
|
|
||||||
navbar_content := os.read_file(os.join_path(cfg_dir, 'navbar.json'))!
|
|
||||||
navbar := json.decode(Navbar, navbar_content)!
|
|
||||||
|
|
||||||
return Config{
|
|
||||||
footer: footer
|
|
||||||
main: main
|
|
||||||
navbar: navbar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (c Config) write(path string) ! {
|
|
||||||
mut footer_file := pathlib.get_file(path: '${path}/footer.json', create: true)!
|
|
||||||
footer_file.write(json.encode(c.footer))!
|
|
||||||
mut main_file := pathlib.get_file(path: '${path}/main.json', create: true)!
|
|
||||||
main_file.write(json.encode(c.main))!
|
|
||||||
mut navbar_file := pathlib.get_file(path: '${path}/navbar.json', create: true)!
|
|
||||||
navbar_file.write(json.encode(c.navbar))!
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import freeflowuniverse.herolib.develop.gittools
|
|
||||||
|
|
||||||
@[heap]
|
|
||||||
pub struct StarlightFactory {
|
|
||||||
pub mut:
|
|
||||||
sites []&DocSite @[skip; str: skip]
|
|
||||||
path_build pathlib.Path
|
|
||||||
// path_publish pathlib.Path
|
|
||||||
args StarlightArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct StarlightArgs {
|
|
||||||
pub mut:
|
|
||||||
// publish_path string
|
|
||||||
build_path string
|
|
||||||
production bool
|
|
||||||
update bool
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(args_ StarlightArgs) !&StarlightFactory {
|
|
||||||
mut args := args_
|
|
||||||
if args.build_path == '' {
|
|
||||||
args.build_path = '${os.home_dir()}/hero/var/starlight'
|
|
||||||
}
|
|
||||||
// if args.publish_path == ""{
|
|
||||||
// args.publish_path = "${os.home_dir()}/hero/var/starlight/publish"
|
|
||||||
// }
|
|
||||||
mut ds := &StarlightFactory{
|
|
||||||
args: args_
|
|
||||||
path_build: pathlib.get_dir(path: args.build_path, create: true)!
|
|
||||||
// path_publish: pathlib.get_dir(path: args_.publish_path, create: true)!
|
|
||||||
}
|
|
||||||
|
|
||||||
ds.template_install(install:true,template_update:args.update,delete:true)!
|
|
||||||
|
|
||||||
return ds
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
pub struct SiteError {
|
|
||||||
Error
|
|
||||||
pub mut:
|
|
||||||
path string
|
|
||||||
msg string
|
|
||||||
cat ErrorCat
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ErrorCat {
|
|
||||||
unknown
|
|
||||||
image_double
|
|
||||||
file_double
|
|
||||||
file_not_found
|
|
||||||
image_not_found
|
|
||||||
page_double
|
|
||||||
page_not_found
|
|
||||||
sidebar
|
|
||||||
circular_import
|
|
||||||
def
|
|
||||||
summary
|
|
||||||
include
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.osal.screen
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
import freeflowuniverse.herolib.core.base
|
|
||||||
import freeflowuniverse.herolib.develop.gittools
|
|
||||||
import json
|
|
||||||
import freeflowuniverse.herolib.osal
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
|
|
||||||
@[heap]
|
|
||||||
pub struct DocSite {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
url string
|
|
||||||
path_src pathlib.Path
|
|
||||||
path_build pathlib.Path
|
|
||||||
// path_publish pathlib.Path
|
|
||||||
args SiteGetArgs
|
|
||||||
errors []SiteError
|
|
||||||
config Config
|
|
||||||
factory &StarlightFactory @[skip; str: skip] // Reference to the parent
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn (mut s DocSite) build() ! {
|
|
||||||
s.generate()!
|
|
||||||
osal.exec(
|
|
||||||
cmd: '
|
|
||||||
cd ${s.path_build.path}
|
|
||||||
bash build.sh
|
|
||||||
'
|
|
||||||
retry: 0
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut s DocSite) build_dev_publish() ! {
|
|
||||||
s.generate()!
|
|
||||||
osal.exec(
|
|
||||||
cmd: '
|
|
||||||
cd ${s.path_build.path}
|
|
||||||
bash build_dev_publish.sh
|
|
||||||
'
|
|
||||||
retry: 0
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut s DocSite) build_publish()! {
|
|
||||||
s.generate()!
|
|
||||||
osal.exec(
|
|
||||||
cmd: '
|
|
||||||
cd ${s.path_build.path}
|
|
||||||
bash build_publish.sh
|
|
||||||
'
|
|
||||||
retry: 0
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut s DocSite) dev()! {
|
|
||||||
s.clean()!
|
|
||||||
s.generate()!
|
|
||||||
|
|
||||||
// Create screen session for starlight development server
|
|
||||||
mut screen_name := 'starlight'
|
|
||||||
mut sf := screen.new()!
|
|
||||||
|
|
||||||
// Add and start a new screen session
|
|
||||||
mut scr := sf.add(
|
|
||||||
name: screen_name
|
|
||||||
cmd: '/bin/bash'
|
|
||||||
start: true
|
|
||||||
attach: false
|
|
||||||
reset: true
|
|
||||||
)!
|
|
||||||
|
|
||||||
// Send commands to the screen session
|
|
||||||
scr.cmd_send('cd ${s.path_build.path}')!
|
|
||||||
scr.cmd_send('bash develop.sh')!
|
|
||||||
|
|
||||||
// Print instructions for user
|
|
||||||
console.print_header(' Starlight Development Server')
|
|
||||||
console.print_item('Development server is running in a screen session.')
|
|
||||||
console.print_item('To view the server output:')
|
|
||||||
console.print_item(' 1. Attach to screen: screen -r ${screen_name}')
|
|
||||||
console.print_item(' 2. To detach from screen: Press Ctrl+A then D')
|
|
||||||
console.print_item(' 3. To list all screens: screen -ls')
|
|
||||||
console.print_item('The site content is on::')
|
|
||||||
console.print_item(' 1. location of documents: ${s.path_src.path}/docs')
|
|
||||||
if osal.cmd_exists('code') {
|
|
||||||
console.print_item(' 2. We opened above dir in vscode.')
|
|
||||||
osal.exec(cmd: 'code ${s.path_src.path}/docs')!
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the watcher in a separate thread
|
|
||||||
// mut tf:=spawn watch_docs(docs_path, s.path_src.path, s.path_build.path)
|
|
||||||
// tf.wait()!
|
|
||||||
println('\n')
|
|
||||||
|
|
||||||
if s.args.watch_changes {
|
|
||||||
docs_path := '${s.path_src.path}/docs'
|
|
||||||
watch_docs(docs_path, s.path_src.path, s.path_build.path)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct ErrorArgs {
|
|
||||||
pub mut:
|
|
||||||
path string
|
|
||||||
msg string
|
|
||||||
cat ErrorCat
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut site DocSite) error(args ErrorArgs) {
|
|
||||||
// path2 := pathlib.get(args.path)
|
|
||||||
e := SiteError{
|
|
||||||
path: args.path
|
|
||||||
msg: args.msg
|
|
||||||
cat: args.cat
|
|
||||||
}
|
|
||||||
site.errors << e
|
|
||||||
console.print_stderr(args.msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_item(item string)!{
|
|
||||||
item2:=item.trim_space().trim("/").trim_space().all_after_last("/")
|
|
||||||
if ["internal","infodev","info","dev","friends","dd","web"].contains(item2){
|
|
||||||
return error("destination path is wrong, cannot be: ${item}")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut site DocSite) check() ! {
|
|
||||||
for item in site.config.main.build_dest{
|
|
||||||
check_item(item)!
|
|
||||||
}
|
|
||||||
for item in site.config.main.build_dest_dev{
|
|
||||||
check_item(item)!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut site DocSite) generate() ! {
|
|
||||||
console.print_header(' site generate: ${site.name} on ${site.path_build.path}')
|
|
||||||
console.print_header(' site source on ${site.path_src.path}')
|
|
||||||
site.check()!
|
|
||||||
site.template_install()!
|
|
||||||
|
|
||||||
// Now copy all directories that exist in src to build
|
|
||||||
for item in ['src', 'static', 'cfg', 'public'] {
|
|
||||||
if os.exists('${site.path_src.path}/${item}') {
|
|
||||||
mut aa := site.path_src.dir_get(item)!
|
|
||||||
aa.copy(dest: '${site.path_build.path}/${item}')!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for item in ['docs'] {
|
|
||||||
if os.exists('${site.path_src.path}/${item}') {
|
|
||||||
mut aa := site.path_src.dir_get(item)!
|
|
||||||
aa.copy(dest: '${site.path_build.path}/${item}', delete: true)!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut site DocSite) template_install() ! {
|
|
||||||
mut gs := gittools.new()!
|
|
||||||
|
|
||||||
site.factory.template_install(template_update:false, install:false, delete:false)!
|
|
||||||
|
|
||||||
cfg := site.config
|
|
||||||
|
|
||||||
mut myhome:="\$\{HOME\}" //for usage in bash
|
|
||||||
|
|
||||||
profile_include := osal.profile_path_source()!.replace(os.home_dir(),myhome)
|
|
||||||
|
|
||||||
mydir:=site.path_build.path.replace(os.home_dir(),myhome)
|
|
||||||
|
|
||||||
for item in ['src', 'static'] {
|
|
||||||
mut aa := site.path_src.dir_get(item) or {continue}
|
|
||||||
aa.copy(dest: '${site.factory.path_build.path}/${item}', delete:false)!
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
develop := $tmpl('templates/develop.sh')
|
|
||||||
build := $tmpl('templates/build.sh')
|
|
||||||
build_dev_publish := $tmpl('templates/build_dev_publish.sh')
|
|
||||||
build_publish := $tmpl('templates/build_publish.sh')
|
|
||||||
|
|
||||||
mut develop_ := site.path_build.file_get_new('develop.sh')!
|
|
||||||
develop_.template_write(develop, true)!
|
|
||||||
develop_.chmod(0o700)!
|
|
||||||
|
|
||||||
mut build_ := site.path_build.file_get_new('build.sh')!
|
|
||||||
build_.template_write(build, true)!
|
|
||||||
build_.chmod(0o700)!
|
|
||||||
|
|
||||||
mut build_publish_ := site.path_build.file_get_new('build_publish.sh')!
|
|
||||||
build_publish_.template_write(build_publish, true)!
|
|
||||||
build_publish_.chmod(0o700)!
|
|
||||||
|
|
||||||
mut build_dev_publish_ := site.path_build.file_get_new('build_dev_publish.sh')!
|
|
||||||
build_dev_publish_.template_write(build_dev_publish, true)!
|
|
||||||
build_dev_publish_.chmod(0o700)!
|
|
||||||
|
|
||||||
mut develop2_ := site.path_src.file_get_new('develop.sh')!
|
|
||||||
develop2_.template_write(develop, true)!
|
|
||||||
develop2_.chmod(0o700)!
|
|
||||||
|
|
||||||
mut build2_ := site.path_src.file_get_new('build.sh')!
|
|
||||||
build2_.template_write(build, true)!
|
|
||||||
build2_.chmod(0o700)!
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import os
|
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
import freeflowuniverse.herolib.develop.gittools
|
|
||||||
import freeflowuniverse.herolib.ui.console
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
pub struct SiteGetArgs {
|
|
||||||
pub mut:
|
|
||||||
name string
|
|
||||||
nameshort string
|
|
||||||
path string
|
|
||||||
url string
|
|
||||||
publish_path string
|
|
||||||
build_path string
|
|
||||||
production bool
|
|
||||||
watch_changes bool = true
|
|
||||||
update bool
|
|
||||||
init bool //means create new one if needed
|
|
||||||
deploykey string
|
|
||||||
config ?Config
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut f StarlightFactory) get(args_ SiteGetArgs) !&DocSite {
|
|
||||||
console.print_header(' Starlight: ${args_.name}')
|
|
||||||
mut args := args_
|
|
||||||
|
|
||||||
if args.build_path.len == 0 {
|
|
||||||
args.build_path = '${f.path_build.path}'
|
|
||||||
}
|
|
||||||
// if args.publish_path.len == 0 {
|
|
||||||
// args.publish_path = '${f.path_publish.path}/${args.name}'
|
|
||||||
|
|
||||||
// coderoot:"${os.home_dir()}/hero/var/publishcode"
|
|
||||||
mut gs := gittools.new(ssh_key_path: args.deploykey)!
|
|
||||||
|
|
||||||
if args.url.len > 0 {
|
|
||||||
args.path = gs.get_path(url: args.url)!
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.path.trim_space() == "" {
|
|
||||||
args.path = os.getwd()
|
|
||||||
}
|
|
||||||
args.path = args.path.replace('~', os.home_dir())
|
|
||||||
|
|
||||||
mut r := gs.get_repo(
|
|
||||||
url: 'https://github.com/freeflowuniverse/starlight_template.git'
|
|
||||||
)!
|
|
||||||
mut template_path := r.patho()!
|
|
||||||
|
|
||||||
// First, check if the new site args provides a configuration that can be written instead of template cfg dir
|
|
||||||
if cfg := args.config {
|
|
||||||
cfg.write('${args.path}/cfg')!
|
|
||||||
} else {
|
|
||||||
// Then ensure cfg directory exists in src,
|
|
||||||
if !os.exists('${args.path}/cfg') {
|
|
||||||
if args.init{
|
|
||||||
// else copy config from template
|
|
||||||
mut template_cfg := template_path.dir_get('cfg')!
|
|
||||||
template_cfg.copy(dest: '${args.path}/cfg')!
|
|
||||||
}else{
|
|
||||||
return error("Can't find cfg dir in chosen starlight location: ${args.path}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !os.exists('${args.path}/src') {
|
|
||||||
if args.init{
|
|
||||||
mut template_cfg := template_path.dir_get('src')!
|
|
||||||
template_cfg.copy(dest: '${args.path}/src')!
|
|
||||||
} else{
|
|
||||||
return error("Can't find src dir in chosen starlight location: ${args.path}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mut myconfig := load_config('${args.path}/cfg')!
|
|
||||||
|
|
||||||
if args.name == '' {
|
|
||||||
args.name = myconfig.main.name
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.name.len==0{
|
|
||||||
return error("name for a site cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.nameshort.len == 0 {
|
|
||||||
args.nameshort = args.name
|
|
||||||
}
|
|
||||||
args.nameshort = texttools.name_fix(args.nameshort)
|
|
||||||
|
|
||||||
mut ds := DocSite{
|
|
||||||
name: args.name
|
|
||||||
url: args.url
|
|
||||||
path_src: pathlib.get_dir(path: args.path, create: false)!
|
|
||||||
path_build: f.path_build
|
|
||||||
// path_publish: pathlib.get_dir(path: args.publish_path, create: true)!
|
|
||||||
args: args
|
|
||||||
config: myconfig
|
|
||||||
factory: &f
|
|
||||||
}
|
|
||||||
|
|
||||||
ds.check()!
|
|
||||||
|
|
||||||
f.sites << &ds
|
|
||||||
|
|
||||||
return &ds
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.develop.gittools
|
|
||||||
import freeflowuniverse.herolib.osal
|
|
||||||
import freeflowuniverse.herolib.installers.web.bun
|
|
||||||
import freeflowuniverse.herolib.installers.web.tailwind
|
|
||||||
import os
|
|
||||||
|
|
||||||
@[params]
|
|
||||||
struct TemplateInstallArgs{
|
|
||||||
template_update bool = true
|
|
||||||
install bool
|
|
||||||
delete bool = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut self StarlightFactory) template_install(args TemplateInstallArgs) ! {
|
|
||||||
mut gs := gittools.new()!
|
|
||||||
|
|
||||||
mut r := gs.get_repo(
|
|
||||||
url: 'https://github.com/freeflowuniverse/starlight_template.git'
|
|
||||||
pull: args.template_update
|
|
||||||
)!
|
|
||||||
mut template_path := r.patho()!
|
|
||||||
|
|
||||||
for item in ['public', 'src'] {
|
|
||||||
mut aa := template_path.dir_get(item) or {continue} //skip if not exist
|
|
||||||
aa.copy(dest: '${self.path_build.path}/${item}', delete: args.delete)!
|
|
||||||
}
|
|
||||||
|
|
||||||
for item in ['package.json', 'tsconfig.json', 'astro.config.mjs'] {
|
|
||||||
src_path := os.join_path(template_path.path, item)
|
|
||||||
dest_path := os.join_path(self.path_build.path, item)
|
|
||||||
os.cp(src_path, dest_path) or {
|
|
||||||
return error('Failed to copy ${item} to build path: ${err}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.install{
|
|
||||||
// install bun
|
|
||||||
mut installer := bun.get()!
|
|
||||||
installer.install()!
|
|
||||||
|
|
||||||
mut installer2 := tailwind.get()!
|
|
||||||
installer2.install()!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
osal.exec(
|
|
||||||
cmd: '
|
|
||||||
${osal.profile_path_source_and()!}
|
|
||||||
export PATH=/tmp/starlight_build/node_modules/.bin:${os.home_dir()}/.bun/bin/:??PATH
|
|
||||||
cd ${self.path_build.path}
|
|
||||||
bun install
|
|
||||||
'
|
|
||||||
)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "??{script_dir}"
|
|
||||||
|
|
||||||
echo "Docs directory: ??script_dir"
|
|
||||||
|
|
||||||
cd "${mydir}"
|
|
||||||
|
|
||||||
export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH
|
|
||||||
|
|
||||||
rm -rf ${site.path_build.path}/build/
|
|
||||||
|
|
||||||
${profile_include}
|
|
||||||
|
|
||||||
bun run build
|
|
||||||
|
|
||||||
mkdir -p ${site.args.publish_path.trim_right("/")}
|
|
||||||
echo SYNC TO ${site.args.publish_path.trim_right("/")}
|
|
||||||
rsync -rv --delete ${site.path_build.path}/build/ ${site.args.publish_path.trim_right("/")}/
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "??{script_dir}"
|
|
||||||
|
|
||||||
|
|
||||||
echo "Docs directory: ??script_dir"
|
|
||||||
|
|
||||||
cd "${mydir}"
|
|
||||||
|
|
||||||
export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH
|
|
||||||
|
|
||||||
rm -rf ${site.path_build.path}/build/
|
|
||||||
|
|
||||||
${profile_include}
|
|
||||||
|
|
||||||
bun run build
|
|
||||||
|
|
||||||
@for dest in cfg.main.build_dest_dev
|
|
||||||
rsync -rv --delete ${site.path_build.path}/build/ ${dest.trim_right("/")}/
|
|
||||||
@end
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "??{script_dir}"
|
|
||||||
|
|
||||||
echo "Docs directory: ??script_dir"
|
|
||||||
|
|
||||||
cd "${mydir}"
|
|
||||||
|
|
||||||
export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH
|
|
||||||
|
|
||||||
rm -rf ${site.path_build.path}/build/
|
|
||||||
|
|
||||||
${profile_include}
|
|
||||||
|
|
||||||
bun run build
|
|
||||||
|
|
||||||
@for dest in cfg.main.build_dest
|
|
||||||
rsync -rv --delete ${site.path_build.path}/build/ ${dest.trim_right("/")}/
|
|
||||||
@end
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
script_dir="???cd "???dirname "??{BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
cd "??{script_dir}"
|
|
||||||
|
|
||||||
echo "Docs directory: ??script_dir"
|
|
||||||
|
|
||||||
cd "${mydir}"
|
|
||||||
|
|
||||||
export PATH=${site.path_build.path}/node_modules/.bin:??{HOME}/.bun/bin/:??PATH
|
|
||||||
|
|
||||||
${profile_include}
|
|
||||||
|
|
||||||
bun dev
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
module starlight
|
|
||||||
|
|
||||||
import freeflowuniverse.herolib.osal.notifier
|
|
||||||
import os
|
|
||||||
|
|
||||||
fn watch_docs(docs_path string, path_src string, path_build string) ! {
|
|
||||||
mut n := notifier.new('docsite_watcher') or {
|
|
||||||
eprintln('Failed to create watcher: ${err}')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n.args['path_src'] = path_src
|
|
||||||
n.args['path_build'] = path_build
|
|
||||||
|
|
||||||
// Add watch with captured args
|
|
||||||
n.add_watch(docs_path, fn (event notifier.NotifyEvent, path string, args map[string]string) {
|
|
||||||
handle_file_change(event, path, args) or { eprintln('Error handling file change: ${err}') }
|
|
||||||
})!
|
|
||||||
|
|
||||||
n.start()!
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle_file_change processes file system events
|
|
||||||
fn handle_file_change(event notifier.NotifyEvent, path string, args map[string]string) ! {
|
|
||||||
file_base := os.base(path)
|
|
||||||
is_dir := os.is_dir(path)
|
|
||||||
|
|
||||||
// Skip files starting with #
|
|
||||||
if file_base.starts_with('#') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// For files (not directories), check extensions
|
|
||||||
if !is_dir {
|
|
||||||
ext := os.file_ext(path).to_lower()
|
|
||||||
if ext !in ['.md', '.png', '.jpeg', '.jpg'] {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get relative path from docs directory
|
|
||||||
rel_path := path.replace('${args['path_src']}/docs/', '')
|
|
||||||
dest_path := '${args['path_build']}/docs/${rel_path}'
|
|
||||||
|
|
||||||
match event {
|
|
||||||
.create, .modify {
|
|
||||||
if is_dir {
|
|
||||||
// For directories, just ensure they exist
|
|
||||||
os.mkdir_all(dest_path) or {
|
|
||||||
return error('Failed to create directory ${dest_path}: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: ${rel_path}')
|
|
||||||
} else {
|
|
||||||
// For files, ensure parent directory exists and copy
|
|
||||||
os.mkdir_all(os.dir(dest_path)) or {
|
|
||||||
return error('Failed to create directory ${os.dir(dest_path)}: ${err}')
|
|
||||||
}
|
|
||||||
os.cp(path, dest_path) or {
|
|
||||||
return error('Failed to copy ${path} to ${dest_path}: ${err}')
|
|
||||||
}
|
|
||||||
println('Updated: ${rel_path}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.delete {
|
|
||||||
if os.exists(dest_path) {
|
|
||||||
if is_dir {
|
|
||||||
os.rmdir_all(dest_path) or {
|
|
||||||
return error('Failed to delete directory ${dest_path}: ${err}')
|
|
||||||
}
|
|
||||||
println('Deleted directory: ${rel_path}')
|
|
||||||
} else {
|
|
||||||
os.rm(dest_path) or { return error('Failed to delete ${dest_path}: ${err}') }
|
|
||||||
println('Deleted: ${rel_path}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.rename {
|
|
||||||
// For rename events, fswatch provides the new path in the event
|
|
||||||
// The old path is already removed, so we just need to handle the new path
|
|
||||||
if is_dir {
|
|
||||||
os.mkdir_all(dest_path) or {
|
|
||||||
return error('Failed to create directory ${dest_path}: ${err}')
|
|
||||||
}
|
|
||||||
println('Renamed directory to: ${rel_path}')
|
|
||||||
} else {
|
|
||||||
os.mkdir_all(os.dir(dest_path)) or {
|
|
||||||
return error('Failed to create directory ${os.dir(dest_path)}: ${err}')
|
|
||||||
}
|
|
||||||
os.cp(path, dest_path) or {
|
|
||||||
return error('Failed to copy ${path} to ${dest_path}: ${err}')
|
|
||||||
}
|
|
||||||
println('Renamed to: ${rel_path}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -181,6 +181,7 @@ systemd_process_test.v
|
|||||||
data/graphdb
|
data/graphdb
|
||||||
data/radixtree
|
data/radixtree
|
||||||
clients/livekit
|
clients/livekit
|
||||||
|
core/playcmds
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user