move new versino from volta website

This commit is contained in:
Laila van Reenen 2025-07-21 19:42:32 +02:00
parent 5f22cdf06f
commit 02049b6d33
Signed by: LailaTheElf
GPG Key ID: 8A3EF0226518C12D
20 changed files with 1036 additions and 1113 deletions

4
.gitignore vendored
View File

@ -1,2 +1,2 @@
/__pycache__
/public
/target
/html

264
Cargo.lock generated Normal file
View File

@ -0,0 +1,264 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "arraydeque"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "encoding_rs"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
"cfg-if",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "getopts"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
dependencies = [
"unicode-width",
]
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [
"foldhash",
]
[[package]]
name = "hashlink"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
dependencies = [
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "memo-map"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b"
[[package]]
name = "minijinja"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e60ac08614cc09062820e51d5d94c2fce16b94ea4e5003bb81b99a95f84e876"
dependencies = [
"memo-map",
"self_cell",
"serde",
"serde_json",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pulldown-cmark"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0"
dependencies = [
"bitflags",
"getopts",
"memchr",
"pulldown-cmark-escape",
"serde",
"unicase",
]
[[package]]
name = "pulldown-cmark-escape"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "self_cell"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-width"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
[[package]]
name = "webTemplate"
version = "0.2.0"
dependencies = [
"hashlink",
"minijinja",
"pulldown-cmark",
"regex",
"yaml-rust2",
]
[[package]]
name = "yaml-rust2"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7"
dependencies = [
"arraydeque",
"encoding_rs",
"hashlink",
]

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "webTemplate"
version = "0.2.0"
edition = "2024"
[dependencies]
hashlink = "0.10.0"
minijinja = { version = "2.10.2", features = ["loader", "builtins", "json"] }
pulldown-cmark = { version = "0.13.0", features = ["serde"] }
regex = "1.11.1"
yaml-rust2 = "0.10.2"

View File

@ -1,10 +0,0 @@
FROM python:3
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
copy . .
CMD [ "python", "./render.py" ]

View File

@ -1,31 +0,0 @@
import yaml
from render import listPages
firstItem = True
def headerGen(item, root):
global firstItem
if(root and not firstItem):
separator = '<span class="menuSeparator">|</span>'
else:
separator = ''
firstItem = False
if(not ('klickable' in item and item['klickable'] is False)):
item['lable'] = '<a href="' + item["url"] + '">' + item["lable"] + '</a>'
if('sub' not in item):
return '<li>' + separator + '<span class="menuLable">' + item["lable"] + '</span></li>'
data = ''
subicon = ''
if(root):
subicon = ' <svg class="menuDropIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><path d="M1,3.7L5,8L9,3.7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" fill="none" stroke="#eee"></path></svg>'
data += ' data-hassub="true"'
html = '<li' + data + '>' + separator + '<span class="menuLable sub">' + item["lable"] + subicon + '</span><ul class="submenu">'
html += listPages(item['sub'], item['url'], headerGen)
return html + '</ul></li>'

139
index.py
View File

@ -1,139 +0,0 @@
import os
import yaml
import shutil
from jinja2 import Template
import sass
from page import Page
pages = []
# SRC_DIR = "/home/mreenen/Documents/git/kladjes"
SRC_DIR = os.path.abspath("/home/mreenen/Documents/git/kladjes")
TARGET_DIR = os.path.abspath("public")
def renderPage(page, template, header):
html = page.renderPage(template, header, SRC_DIR)
path = TARGET_DIR + page.url
if (path.split('.')[-1] != "html"):
path = os.path.abspath(path + '/index.html')
pathDir = os.path.dirname(path)
fileName = os.path.basename(path)
os.makedirs(pathDir, exist_ok=True)
with open(path, 'w') as file:
file.write(html)
del html
if (page.assets is not None):
print("assets found")
for asset in page.assets:
srcAssets = SRC_DIR + asset
destAssets = TARGET_DIR + asset
if(not os.path.isdir(destAssets)):
os.mkdir(destAssets)
print(f"asset: {asset}")
for f in os.listdir(srcAssets):
print(f"f: {f}")
if ((f[0] != '.') and os.path.isfile(srcAssets + "/" + f)):
print("copy: " + asset + "/" + f)
shutil.copyfile(srcAssets + "/" + f, destAssets + "/" + f)
def findPageInList(list, url):
i = 0
while (i < len(list)):
if (list[i].url == url):
return i
i += 1
return -1
def render(rootPage, template, header):
done = False
isSub = False
lastPage = []
curPage = 0
while not done:
if (curPage == 0):
page = rootPage
else:
pageIndex = curPage - 1
page = None
if (pageIndex < len(rootPage.subPages)):
page = rootPage.subPages[pageIndex]
else:
pageIndex -= len(rootPage.subPages)
if (pageIndex < len(rootPage.sidebar)):
page = rootPage.sidebar[pageIndex]
if (page is not None):
if (page.clickable):
renderPage(page, template, header)
if ((curPage != 0) and
((len(page.subPages) > 0) or (len(page.sidebar) > 0))):
rootPage = page
lastPage.append(curPage)
curPage = 1
else:
curPage += 1
else:
if (len(lastPage) > 0):
curPage = lastPage.pop() + 1
rootPage = rootPage.parrent
else:
done = True
print(f"done")
def index(fileName):
global pages
with open(SRC_DIR + '/index.yaml', 'r') as file:
index = yaml.safe_load(file)
if ('sitemap' not in index):
print("ERROR: /index.yaml is missing a sitemap")
else:
pages = []
for item in index["sitemap"]:
pages.append(Page(item, None))
for page in pages:
page.scan(SRC_DIR)
print("START INDEX")
for p in pages:
print(p)
print("END INDEX")
seperator = False
header = ""
for p in pages:
if (seperator):
header += "<span class='menuSeparator'>|</span>"
header += p.topMenuHTML()
seperator = True
os.makedirs(TARGET_DIR, exist_ok=True)
with open(TARGET_DIR + '/header.html', 'w') as file:
file.write(header);
with open('template.html') as file:
template = Template(file.read())
for p in pages:
render(p, template, header)
with open('style.scss', 'r') as file:
css = sass.compile(string=file.read())
with open(TARGET_DIR + '/style.css', 'w') as file:
file.write(css)
del css
if __name__ == '__main__':
index('index.yml')

181
index.yml
View File

@ -1,181 +0,0 @@
---
- lable: Home
url: /
# - lable: Niet vergeten
# url: /niet-vergeten.html
- lable: HR
sub:
- lable: ANE10
url: ANE10
sidebar:
- lable: Index
url: .
- lable: Dingen
url: dingen.html
- lable: lab 3
url: lab-3.html
- lable: lab 5
url: lab-5.html
- lable: ANE10-2
url: ANE10-2
sidebar:
- lable: Index
url: .
- lable: Bi Polar Junction Transistor
url: bjt.html
- lable: Field Effect Transistor
url: fet.html
- lable: DIS10
sidebar:
- lable: index
url: .
- lable: handigheidjes
url: handigheidjes.html
- lable: opgavens
url: opgaves.html
- lable: 3.1 theorie
url: week-3.1.html
- lable: 3.2 theorie
url: week-3.2.html
- lable: 3.3 theorie
url: week-3.3.html
- lable: 3.4 lab
url: week-3.4.html
- lable: 3.5 theorie
url: week-3.5.html
# - lable: 3.7 lab
# url: week-3.7.html
- lable: 4.1 lab
url: week-4.1.html
- lable: 4.3 lab
url: week-4.3.html
- lable: 4.4 theorie
url: week-4.4.html
- lable: 4.5 theorie
url: week-4.5.html
- lable: 4.7 lab
url: week-5.5.html
- lable: EPS20
sidebar:
- lable: index
url: .
- lable: week 1
url: week-1.html
- lable: lab 1
url: lab-1.html
- lable: week 2
url: week-2.html
- lable: Argief (beta)
url: argief
sidebar:
# year 1
- lable: WIS10 #empty
clickable: False
- lable: ELE10 #empty
clickable: False
- lable: PEE10 #empty
clickable: False
- lable: ELE20
sub:
- opamp
- lable: WIS20
sidebar:
- lable: formuleblad
url: formuleblad.html
- lable: diffrencieren
url: diffrencieeren.html
- lable: supsitutsie
url: supsitutsie.html
- lable: breuksplitsen
url: breuksplitsen.html
- lable: parsiele int.
url: parsiel.html
- lable: fourier transform
url: fourier-transformatie.html
- lable: divrenciaal vergeleiking
url: difrenciaal-vergeleiking.html
- lable: 2de Orde DV
url: 2de-orde-dv.html
- EMS10 #empty
- PEE20 #empty
- lable: EPS10
sub:
- buckboost
- electiesh-en-machnetich-veld
- lab-1
- lab-2
- lab-3
- lab-4
- lab-5
- lab-6
# year 2
- lable: REG10
url: REG10-2
sidebar:
- lable: Index
url: .
- lable: Laplace
url: laplace.html
- lable: Voorbeelden
url: voorbeelden.html
- lable: Eigenschappen
url: eigenschappen.html
- lable: P-Regelaar
url: pregelaar.html
- lable: polen en nulpunten
url: polen-en-nulpunten.html
- lable: Formuleblad
url: formuleblad.html
- lable: Lab 1
url: lab1.html
- lable: Lab 2
url: lab2.html
- lable: Lab 3
url: lab3.html
- EMS20 #empyt
- PEE30 #empty
#- ANE10
#- DIS10
#- EPS20
- PEE40 #empty
- lable: TEL10
sidebar:
- lable: index
url: .
# - lable: week 4.1
# url: week-1.html
- lable: 4.2 theorie
url: week-2.html
- lable: 4.3 lab
url: week-3.html
- lable: 4.5 theorie
url: week-5.html
- lable: Fablab making
url: fablab-making.html
- lable: Toki Pona
url: tokipona
# - lable: Projecten
# klickable: False
# url: projecten
# sub:
# - lable: MR Function Board
# url: mr-fn-board
# sidebar:
# - lable: blog
# url: blog.html
# - lable: Button layout
# url: button-layout.html
# - lable: PCB
# url: pcb.html
# - lable: Puzzel Game
# url: puzzel-game

211
page.py
View File

@ -1,211 +0,0 @@
import os
import yaml
from renderMD import renderMD
class Page:
lable = ''
url = ''
clickable = True
subPages = []
sidebar = []
parrent = None
assets = None
def __init__(self, item, parrent):
self.subPages = []
self.sidebar = []
self.parrent = parrent
if (parrent is None):
root = ''
else:
root = parrent.url
if (type(item) is str):
self.lable = item
self.url = os.path.abspath(root + '/' + item)
self.clickable = True
elif (type(item) is dict):
if('url' not in item):
if(('klickable' in item) and (item['klickable'] is False)):
self.url = root # still set the url for sub pages
else:
self.url = root + '/' + item['lable']
else:
self.url = item['url']
if(self.url == ''):
self.url = '/'
if(self.url[0] != '/'):
self.url = root + '/' + self.url
self.url = os.path.abspath(self.url)
if('lable' not in item):
self.lable = item['url'].split('/')[-1]
else:
self.lable = item['lable']
self.lable = self.lable.replace('<', '&lt;')
self.lable = self.lable.replace('>', '&gt;')
if ('sub' in item):
for subItem in item['sub']:
self.subPages.append(Page(subItem, self))
if ('sidebar' in item):
for subItem in item['sidebar']:
self.sidebar.append(Page(subItem, self))
else:
self.sidebar = []
if ('klickable' in item):
self.clickable = item["klickable"]
else:
print('ERROR: Page.__init__(): invalid index itemtype ' + type(item))
def scan(self, srcDir):
if ((self.parrent is None) or (self.url != self.parrent.url)):
indexFile = os.path.abspath(srcDir + self.url + '/index.yaml')
print(f"INFO: Page.scan(): indexFile: {indexFile}")
index = None
if (os.path.isfile(indexFile)):
with open(indexFile, 'r') as file:
index = yaml.safe_load(file)
if (index is not None):
if ('subPages' in index):
print(f"INFO: Page.scan(): subpages found for {self.url}")
for item in index["subPages"]:
page = Page(item, self)
self.subPages.append(page)
page.scan(srcDir)
if ('sidebar' in index):
print(f"INFO: Page.scan(): sidemenu found for {self.url}")
for item in index["sidebar"]:
page = Page(item, self)
self.sidebar.append(page)
page.scan(srcDir)
if ('assets' in index):
print(f"INFO: Page.scan(): assets found for {self.url}")
self.assets = index["assets"]
i = 0
while (i < len(self.assets)):
if(self.assets[i][0] != '/'):
self.assets[i] = self.url + '/' + self.assets[i]
self.assets[i] = os.path.abspath(self.assets[i])
i += 1
def getSidebar(self):
if ((len(self.sidebar) == 0) and (self.parrent is not None)):
return self.parrent.getSidebar()
else:
return self.sidebar
def __str__(self):
return self.toString(0)
def toString(self, indents):
indent = " "*indents
str = f"{indent} - lable: {self.lable}\n"
str += f"{indent} url: {self.url}\n"
if (self.clickable):
str += f"{indent} clickable: True\n"
else:
str += f"{indent} clickable: False\n"
if (self.assets is not None):
str += f"{indent} assets: {len(self.assets)}\n"
for asset in self.assets:
str += f"{indent} - {asset}\n"
if (len(self.subPages) > 0):
str += f"{indent} sub: {len(self.subPages)}\n"
for page in self.subPages:
str += page.toString(indents + 1)
if (len(self.sidebar) > 0):
str += f"{indent} sidebar:\n"
for page in self.sidebar:
str += page.toString(indents + 1)
return str
def topMenuHTML(self):
if (len(self.subPages) == 0):
propertys = ''
if (self.clickable):
html = f"<span class='menuLable'><a href='{self.url}'>{self.lable}</a></span>"
else:
html = f"<span class='menuLable'>{self.lable}</span>"
else:
subicon = " <svg class='menuDropIcon' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'><path d='M1,3.7L5,8L9,3.7' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' fill='none' stroke='#eee'></path></svg>"
propertys = " data-hassub='true'"
propertys += " class=\"sub\""
if (self.clickable):
html = f"<span class='menuLable'><a href='{self.url}'>{self.lable}</a>{subicon}</span><ul>"
else:
html = f"<span class='menuLable'>{self.lable}{subicon}</span><ul>"
for page in self.subPages:
html += page.topMenuHTML()
html += "</ul>"
return f"<li{propertys}>{html}</li>"
def sidebarHTML(self):
if (len(self.subPages) == 0):
propertys = ""
if (self.clickable):
html = f"<span class='menuLable'><a href='{self.url}'>{self.lable}</a></span>"
else:
html = f"<span class='menuLable'>{self.lable}</span>"
else:
propertys = " data-hassub='true'"
propertys += " class=\"sub\""
if (self.clickable):
html = f"<span class='menuLable'><a href='{self.url}'>{self.lable}</a></span><ul>"
else:
html = f"<span class='menuLable'>{self.lable}</span><ul>"
for page in self.subPages:
html += page.sidebarHTML()
html += "</ul>"
return f"<li{propertys}>{html}</li>"
def renderPage(self, template, header, srcDir):
html = ""
if (os.path.isdir(srcDir + self.url)):
if(self.url[-1] == '/'):
srcFile = self.url + 'index.md'
else:
srcFile = self.url + '/index.md'
elif (len(self.url.split('.')) > 1 and len(self.url.split('.')[-1]) < 5):
srcFile = '.'.join(self.url.split('.')[0:-1]) + '.md'
else:
srcFile = self.url + '.md'
if(not os.path.isfile(srcDir + srcFile)):
print(f"ERROR: faild to find srouce file for page {self.lable} ({self.url})")
else:
print('md: ' + srcFile)
if(len(self.getSidebar()) != 0):
sidebar = '<aside><nav><ul id="sidemenu">'
for item in self.getSidebar():
sidebar += item.sidebarHTML()
sidebar += '</ul></nav></aside>'
else:
sidebar = ""
html = template.render(
header = header,
sidebar = sidebar,
body = renderMD(srcDir + srcFile)
)
return html

179
render.py
View File

@ -1,179 +0,0 @@
import os
import yaml
import shutil
from jinja2 import Template
# from header import headerGen
from renderMD import renderMD
from renderSCSS import renderSCSS
with open('template.html', 'r') as file:
template = Template(file.read())
srcDir = '/source'
destDir = '/target'
firstItem = True
def headerGen(item, root):
global firstItem
if(root and not firstItem):
separator = '<span class="menuSeparator">|</span>'
else:
separator = ''
firstItem = False
lable = item['lable']
if(not ('klickable' in item and item['klickable'] is False)):
lable = '<a href="' + item["url"] + '">' + item["lable"] + '</a>'
if('sub' not in item):
return '<li>' + separator + '<span class="menuLable">' + lable + '</span></li>'
data = ''
subicon = ''
if(root):
subicon = ' <svg class="menuDropIcon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"><path d="M1,3.7L5,8L9,3.7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" fill="none" stroke="#eee"></path></svg>'
data += ' data-hassub="true"'
html = '<li' + data + '>' + separator + '<span class="menuLable sub">' + lable + subicon + '</span><ul class="submenu">'
html += listPages(item['sub'], item['url'], headerGen)
return html + '</ul></li>'
def sidebarGen(item, root):
if(not ('klickable' in item and item['klickable'] is False)):
item['lable'] = '<a href="' + item["url"] + '">' + item["lable"] + '</a>'
if('sub' not in item):
return '<li><span class="menuLable">' + item["lable"] + '</span></li>'
html = '<li><span class="menuLable sub">' + item["lable"] + '</span><ul class="submenu">'
html += listPages(item['sub'], item['url'], sidebarGen)
return html + '</ul></li>'
def listPages(items, parentUrl, cb):
root = False
if(firstItem):
root = True
html = ""
for item in items:
if(type(item) is dict):
if('url' not in item):
if('lable' not in item):
print('url or lable reqired')
continue
if('klickable' in item and item['klickable'] is False):
item["url"] = parentUrl
else:
item["url"] = parentUrl + '/' + item["lable"]
if(item['url'] == ''):
item['url'] = '/'
if(item["url"][0] != '/'):
item["url"] = parentUrl + '/' + item["url"]
#WARNING: XSS vanrable!
if('lable' not in item):
item["lable"] = item["url"].split('/')[-1]
# if(not ('klickable' in item and item['klickable'] is False)):
html += cb(item, root)
continue
if(type(item) is str):
html += cb({"lable": item, "url": parentUrl + '/' + item}, root)
continue
return html
sidebar = ''
def renderPage(item, root):
global srcDir, destDir, header, template, sidebar
html = ''
srcFile = item['url']
if(os.path.isdir(srcDir + item['url'])):
if(item['url'][-1] == '/'):
srcFile = item['url'] + 'index.md'
else:
srcFile = item['url'] + '/index.md'
elif(len(item['url'].split('.')) > 1 and len(item['url'].split('.')[-1]) < 5):
srcFile = '.'.join(item['url'].split('.')[0:-1]) + '.md'
if(not os.path.isfile(srcDir + srcFile)):
print('faild to find path to page for ' + item['url'])
else:
print('md: ' + srcFile)
if(not os.path.isdir(destDir + '/'.join(srcFile.split('/')[0:-1]))):
os.mkdir(destDir + '/'.join(srcFile.split('/')[0:-1]))
if('sidebar' in item):
sidebar = '<aside><nav><ul id="sidemenu">'
sidebar += listPages(item['sidebar'], item['url'], sidebarGen)
sidebar += '</ul></nav></aside>'
target = open(destDir + '.'.join(srcFile.split('.')[0:-1]) + '.html', "w")
target.write(template.render(
header = header,
sidebar = sidebar,
body = renderMD(srcDir + srcFile)
))
target.close()
if('sidebar' in item):
listPages(item['sidebar'], item['url'], renderPage)
sidebar = ''
if('sub' in item):
listPages(item['sub'], item['url'], renderPage)
return ''
def copyAssets(d):
global srcDir, destDir
for f in os.listdir(srcDir + d):
if f == '.git':
continue
if(os.path.isdir(srcDir + d + f)):
if(not os.path.isdir(destDir + d + f)):
os.mkdir(destDir + d + f)
copyAssets(d + f + '/')
continue
ext = f.split('.')[-1]
if(ext == 'md'):
# print('md: ' + d + f)
# target = open(destDir + d + '.'.join(f.split('.')[0:-1]) + '.html', "w")
# target.write(template.render(
# header = header,
# body = renderMD(srcDir + d + f)
# ))
# target.close()
continue
if(ext == 'scss'):
print('scss: ' + d + f)
target = open(destDir + d + '.'.join(f.split('.')[0:-1]) + '.css', "w")
target.write(renderSCSS(srcDir + d + f))
target.close()
continue
print("copy: " + d + f)
shutil.copyfile(srcDir + d + f, destDir + d + f)
if(__name__ == '__main__'):
with open('index.yml', 'r') as file:
index = yaml.safe_load(file)
header = '<header><nav><ul id="menu">'
header += listPages(index, '', headerGen)
header += '</ul></nav></header>'
copyAssets('/')
listPages(index, '', renderPage)

View File

@ -1,10 +0,0 @@
#!/bin/bash
cd $HOME/webTemplate
rm -r ../public/*
python3 render.py
# cd ../public/ && python3 -m http.server 8000

View File

@ -1,43 +0,0 @@
import html
from misaka import Markdown, HtmlRenderer
import latex2mathml.converter
import re
def renderLatex(code):
code = code.strip()
code = code.replace("=>", "\\Rightarrow")
code = code.replace("->", "\\rightarrow")
code = code.replace("\\(", "\\left(")
code = code.replace("\\)", "\\right)")
code = code.replace("*", "\\cdot")
a = re.search(r"{[^{}]*} */ *{[^{}]*}", code)
while a is not None:
newcode = code[:a.span()[0]]
newcode += "\\frac"
newcode += re.sub(r'}[ ]*/[ ]*{', '}{', code[a.span()[0] : a.span()[1]])
newcode += code[a.span()[1]:]
code = newcode
a = re.search(r"{[^{}}]*} */ *{[^{}}]*}", code)
return latex2mathml.converter.convert(code)
class HighlighterRenderer(HtmlRenderer):
def codespan(self, code):
lang = code.split(' ')[0]
if(lang == 'tex'):
return '{}\n'.format(renderLatex(' '.join(code.split(' ')[1:]).strip()))
return '<code>{}</code>\n'.format(code.strip())
def blockcode(self, code, lang):
if(lang == 'latex/equetion'):
return '<p data-latex="{}">{}</p>\n'.format(code.strip(), renderLatex(code))
return '<pre><code class="{}">{}</code></pre>\n'.format(lang.replace('/', '-'), code)
renderer = HighlighterRenderer()
md = Markdown(renderer, extensions=('fenced-code','tables'))
def renderMD(src):
srcFile = open(src, 'r')
html = md(srcFile.read())
srcFile.close()
return html

View File

@ -1,13 +0,0 @@
code = """li {
display: block;
}
"""
import sass
def renderSCSS(src):
srcFile = open(src, 'r')
# print(src)
css = sass.compile(string=srcFile.read())
srcFile.close()
return css

View File

@ -1,5 +0,0 @@
pyyaml
jinja2
misaka
latex2mathml
libsass

View File

@ -1,50 +0,0 @@
import yaml
def parseHTML(item):
if(not ('klickable' in item and item['klickable'] is False)):
item['lable'] = '<a href="' + item["url"] + '">' + item["lable"] + '</a>'
if('sub' not in item):
return '<li><span class="menuLable">' + item["lable"] + '</span></li>'
html = '<li><span class="menuLable sub">' + item["lable"] + '</span><ul class="submenu">'
html += genHTML(item['sub'], item['url'])
return html + '</ul></li>'
def genHTML(items, parentUrl):
html = ""
for item in items:
if(type(item) is dict):
if('url' not in item):
if('lable' not in item):
print('url or lable reqired')
continue
if('klickable' in item and item['klickable'] is False):
item["url"] = parentUrl
else:
item["url"] = parentUrl + '/' + item["lable"]
if(item["url"][0] != '/'):
item["url"] = parentUrl + '/' + item["url"]
#WARNING: XSS vanrable!
if('lable' not in item):
item["lable"] = item["url"].split('/')[-1]
html += parseHTML(item)
continue
if(type(item) is str):
html += parseHTML({"lable": item, "url": parentUrl + '/' + item})
continue
return html
def getSidebar(items, parentUrl):
html = '<aside><nav><ul id="sidemenu">'
html += genHTML(items, parentUrl)
html += '</ul></nav></aside>'
return html

64
src/main.rs Normal file
View File

@ -0,0 +1,64 @@
use std::{path::Path};
use std::fs;
use minijinja::Environment;
mod render;
use render::{Renderer, IndexItem, build_jinja_env, Template};
const SRC_PATH: &'static str = "../src";
const OUT_PATH: &'static str = "./html/";
fn render_index(out_path: &Path, index: &IndexItem, cur_path: &Path, site_index: &Renderer, jinja_env: Option<&Environment>) {
let dest_path: &Path = &cur_path.join(&index.friendly);
println!("dest_path: {:?}", dest_path);
let jinja_env = match jinja_env {
Some(env) => env,
None => &build_jinja_env(Template::index(&site_index.path.join("templates"))),
};
match &index.src {
Some(src) => {
let friendly = match index.friendly.len() {
0 => String::from("index"),
_ => index.friendly.clone()
};
if !index.is_asset {
let dest_path: &Path = &cur_path.join(format!("{}.{}", friendly, index.target_extention));
match site_index.render_page(index, &site_index.site, jinja_env) {
Some(html) => {
let _ = fs::write(dest_path, html);
},
None => todo!(),
};
}
else {
let _ = fs::copy(src, &cur_path.join(friendly));
}
},
None => {
let _ = fs::create_dir(dest_path);
},
};
for page in &index.sub_pages {
render_index(out_path, page, dest_path, &site_index, Some(&jinja_env));
}
}
fn main() {
let src_path = Path::new(SRC_PATH);
let out_path = Path::new(OUT_PATH);
let index = Renderer::index(src_path).unwrap();
println!("");
println!("index:");
println!("{:?}", index);
let _ = fs::create_dir_all(out_path);
render_index(out_path, &index.site, out_path, &index, None);
}

118
src/render.rs Normal file
View File

@ -0,0 +1,118 @@
mod index;
mod parse_md;
use std::{collections::BTreeMap, fs, path::Path};
use minijinja::{context, Environment, Value};
use index::indexer;
use parse_md::parse_md;
use yaml_rust2::Yaml;
pub use index::indexer::{IndexItem, Template};
pub fn build_jinja_env<'a>(templates: Vec<Box<indexer::Template>>) -> Environment<'a> {
build_jinja_env_dir(templates)
}
fn build_jinja_env_dir<'a>(templates: Vec<Box<indexer::Template>>) -> Environment<'a> {
let mut env: Environment = Environment::new();
for template in templates {
match template.src {
Some(src) => {
match env.add_template_owned(template.name.clone(), src) {
Ok(_) => {},
Err(err) => println!("ERROR: failt to add template \"{}\" ({err:?})", template.name),
}
},
None => {
for plate in template.sub_templates {
if let Some(src) = plate.src {
let name = format!("{}/{}", template.name.clone(), plate.name.clone());
match env.add_template_owned(name.clone(), src) {
Ok(_) => println!("template name: {name}"),
Err(err) => println!("ERROR: failt to add template \"{name}\" ({err:?})"),
}
}
}
}
};
}
env
}
#[derive(Debug)]
pub struct Renderer {
// jinja_env: Environment<'a>,
pub site: IndexItem,
pub path: Box<Path>
}
impl Renderer {
pub fn index(path: &Path) -> Option<Renderer> {
if let Some(site_index) = IndexItem::index(&path) {
Some(Renderer {
site: site_index,
path: Box::from(path)
})
}
else {
None
}
}
fn convert_yaml(yaml: Yaml) -> Value {
match yaml {
Yaml::Real(val) => Value::from(val),
Yaml::Integer(val) => Value::from(val),
Yaml::String(val) => Value::from(val),
Yaml::Boolean(val) => Value::from(val),
Yaml::Array(val) => {
let mut list: Vec<Value> = Vec::new();
for item in val {
list.push(Renderer::convert_yaml(item));
}
Value::from(list)
},
Yaml::Hash(val) => {
let mut list: BTreeMap<String, Value> = BTreeMap::new();
for (key, val) in val {
if let Yaml::String(key) = key {
list.insert(key, Renderer::convert_yaml(val));
}
else {
todo!()
}
}
Value::from(list)
},
Yaml::Alias(val) => Value::from(val),
Yaml::Null => Value::UNDEFINED,
Yaml::BadValue => Value::UNDEFINED,
}
}
pub fn render_page(&self, page: &IndexItem, index: &IndexItem, jinja_env: &Environment) -> Option<String> {
if page.src == None {
return None;
}
match fs::read_to_string(page.src.as_ref().unwrap()) {
Ok(content) => {
let split = indexer::split_params(content);
match parse_md(split.md, jinja_env) {
Some(md) => {
let template = jinja_env.get_template(&page.template).unwrap();
let html = template.render(context! {
index => index.to_minijinja(),
is_error_not_found => false,
url => page.get_url(),
body => md,
params => Renderer::convert_yaml(split.yaml)
}).unwrap();
Some(html)
},
None => None
}
},
Err(_) => None,
}
}
}

354
src/render/index.rs Normal file
View File

@ -0,0 +1,354 @@
pub mod indexer {
use std::{collections::BTreeMap, ffi::OsString, fs, path::Path};
use hashlink::LinkedHashMap;
use regex::Regex;
use yaml_rust2::{Yaml, YamlLoader};
#[derive(Debug)]
pub struct IndexItem {
pub src: Option<String>,
pub is_asset: bool,
pub path: String,
pub title: String,
pub friendly: String,
pub public: bool,
pub template: String,
pub sub_pages: Vec<Box<IndexItem>>,
pub target_extention: String,
}
impl IndexItem {
fn new() -> IndexItem {
IndexItem {
src: None,
is_asset: false,
path: String::from("/"),
title: String::from(""),
friendly: String::from(""),
public: false,
sub_pages: Vec::new(),
template: String::from("default"),
target_extention: String::from("html"),
}
}
pub fn index(path: &Path) -> Option<IndexItem> {
IndexItem::scan_entry(path, String::from("/"))
}
fn scan_entry(path: &Path, last_url: String) -> Option<IndexItem> {
let mut item = IndexItem::new();
if let Some(file_name) = path.file_name() {
if let Some(file_name) = file_name.to_str() {
let file_name = String::from(file_name);
if path.is_dir() {
if file_name != "templates" {
item.friendly = file_name.clone();
item.path = last_url.clone();
let url = if last_url == String::from("/") {
format!("/{file_name}")
} else {
format!("{}/{file_name}", last_url.clone())
};
let index_path = path.join("index.md");
match IndexItem::scan_entry(&index_path, last_url.clone()) {
Some(mut item) => {
let sub_pages = IndexItem::scan_dir(&path, last_url);
item.sub_pages = sub_pages.0;
item.public = sub_pages.1;
return Some(item);
},
None => {
let sub_pages = IndexItem::scan_dir(&path, url);
item.sub_pages = sub_pages.0;
item.public = sub_pages.1;
return Some(item);
}
}
}
}
else if path.is_file() {
item.src = match path.to_str() {
Some(path) => Some(String::from(path)),
None => None,
};
if item.src == None || file_name.len() < 4 {
return None;
}
let extention: String = file_name.clone().drain(file_name.len()-3..).collect();
if extention == ".md" {
let friendly = file_name.clone().drain(..file_name.len()-3).collect();
item.friendly = if friendly == "index" {
String::from("")
} else {
friendly
};
item.path = last_url;
let md = fs::read_to_string(path);
match md {
Ok(md) => {
let params = split_params(md).yaml;
IndexItem::parse_params(&mut item, params);
return Some(item)
},
Err(_) => todo!()
}
}
else {
item.is_asset = true;
item.friendly = file_name.clone();
item.path = last_url;
return Some(item);
}
}
return None
}
}
None
}
fn scan_dir(path: &Path, last_url: String) -> (Vec<Box<IndexItem>>, bool) {
let mut items: Vec<Box<IndexItem>> = Vec::new();
let mut has_public: bool = false;
let list = fs::read_dir(path);
match list {
Ok(list) => {
for entry in list {
match entry {
Ok(entry) => {
if entry.file_name() != OsString::from("index.md") {
let item = IndexItem::scan_entry(&entry.path(), last_url.clone());
if let Some(i) = item {
if i.public {
has_public = true;
}
items.push(Box::from(i));
}
}
},
Err(_) => todo!()
}
}
},
Err(_) => todo!()
};
items.sort_by(|i, c| i.friendly.cmp(&c.friendly));
(items, has_public)
}
fn parse_params(item: &mut IndexItem, params: Yaml) {
if let Yaml::Hash(params) = params {
if let Some(value) = params.get(&Yaml::from(Yaml::String(String::from("title")))) {
if let Yaml::String(title) = value {
item.title = title.clone();
}
};
if let Some(value) = params.get(&Yaml::from(Yaml::String(String::from("public")))) {
if let Yaml::Boolean(public) = value {
item.public = public.clone();
}
};
if let Some(value) = params.get(&Yaml::from(Yaml::String(String::from("template")))) {
if let Yaml::String(template) = value {
item.template = template.clone();
}
};
if let Some(value) = params.get(&Yaml::from(Yaml::String(String::from("target_extention")))) {
if let Yaml::String(target_extention) = value {
item.target_extention = target_extention.clone();
}
};
}
}
pub fn get_url(&self) -> String {
if self.src != None {
if self.path.ends_with("/") {
format!("{}{}", self.path, self.friendly)
} else {
format!("{}/{}", self.path, self.friendly)
}
}
else {
String::from("")
}
}
pub fn to_minijinja(&self) -> minijinja::Value {
let mut list: BTreeMap<String, minijinja::Value> = BTreeMap::new();
list.insert(String::from("path"), minijinja::Value::from(self.path.clone()));
list.insert(String::from("title"), minijinja::Value::from(self.title.clone()));
list.insert(String::from("friendly"), minijinja::Value::from(self.friendly.clone()));
list.insert(String::from("public"), minijinja::Value::from(self.public.clone()));
list.insert(String::from("template"), minijinja::Value::from(self.template.clone()));
list.insert(String::from("url"), minijinja::Value::from(self.get_url()));
list.insert(String::from("sub_pages"), minijinja::Value::from({
let mut list: Vec<minijinja::Value> = Vec::new();
for page in self.sub_pages.iter() {
list.push(page.to_minijinja());
};
list
}));
minijinja::Value::from(list)
}
}
#[derive(Debug)]
pub struct Template {
pub name: String,
pub extention: String,
pub path: Option<String>,
pub src: Option<String>,
pub sub_templates: Vec<Box<Template>>
}
impl Template {
fn new() -> Template {
Template {
name: String::new(),
extention: String::new(),
path: None,
src: None,
sub_templates: Vec::new()
}
}
pub fn index(path: &Path) -> Vec<Box<Template>> {
match Template::scan_template(path) {
Some(templates) => templates.sub_templates,
None => Vec::new(),
}
}
fn scan_template(path: &Path) -> Option<Template> {
let mut item = Template::new();
if let Some(file_name) = path.file_name() {
if let Some(file_name) = file_name.to_str() {
let file_name = String::from(file_name);
if path.is_dir() {
item.name = file_name;
item.sub_templates = Template::scan_dir(&path);
return Some(item);
}
else if path.is_file() {
item.path = match path.to_str() {
Some(path) => Some(String::from(path)),
None => None,
};
if item.path == None {
return None;
}
let path = item.path.clone().unwrap();
item.src = match fs::read_to_string(path) {
Ok(src) => Some(src),
Err(_) => None,
};
let re= Regex::new(r"\.").unwrap();
let mut matches = re.captures_iter(&file_name);
if let Some(dot) = matches.next() {
let dot = dot.get(0).unwrap();
item.extention = file_name.clone().drain(dot.start()..).collect();
item.name = file_name.clone().drain(..dot.start()).collect();
return Some(item);
}
else {
item.name = file_name;
return Some(item);
}
}
return None
}
}
None
}
fn scan_dir(path: &Path) -> Vec<Box<Template>> {
let mut items: Vec<Box<Template>> = Vec::new();
let list = fs::read_dir(path);
match list {
Ok(list) => {
for entry in list {
match entry {
Ok(entry) => {
let item = Template::scan_template(&entry.path());
if let Some(i) = item {
items.push(Box::from(i));
}
},
Err(_) => todo!()
}
}
},
Err(_) => todo!()
};
items.sort_by(|i, c| i.name.cmp(&c.name));
items
}
}
pub struct SplitMd {
pub yaml: Yaml,
pub md: String
}
fn join_yaml(yaml: Vec<Yaml>) -> Yaml {
let mut out: LinkedHashMap<Yaml, Yaml> = LinkedHashMap::new();
for yaml in yaml {
match yaml {
yaml_rust2::Yaml::Hash(map) => {
for yaml in map.iter() {
out.insert(yaml.0.clone(), yaml.1.clone());
}
},
_ => {
todo!();
},
}
}
Yaml::Hash(out)
}
pub fn split_params(md: String) -> SplitMd {
let re= Regex::new(r"---").unwrap();
let mut matches = re.captures_iter(&md);
if let Some(first_dashes) = matches.next() {
let first_dashes = first_dashes.get(0).unwrap();
if first_dashes.start() == 0 {
if let Some(second_dashes) = matches.next() {
let second_dashes = second_dashes.get(0).unwrap();
let yaml: String = md.clone().drain(first_dashes.end()..second_dashes.start()).collect();
let markdown: String = md.clone().drain(second_dashes.end()..).collect();
if yaml.len() > 0 {
match YamlLoader::load_from_str(&yaml) {
Ok(yaml) => {
return SplitMd {
yaml: join_yaml(yaml),
md: markdown,
};
},
Err(_) => {}
}
}
}
}
}
SplitMd {
yaml: Yaml::BadValue,
md: md,
}
}
}

223
src/render/parse_md.rs Normal file
View File

@ -0,0 +1,223 @@
use std::{collections::BTreeMap};
use pulldown_cmark::{Event, Options, Parser, Tag};
use minijinja::{context, Environment, Value};
use regex::Regex;
pub fn parse_md(md: String, jinja_env: &Environment) -> Option<String> {
let mut options = Options::empty();
options.insert(Options::ENABLE_TABLES);
options.insert(Options::ENABLE_FOOTNOTES);
options.insert(Options::ENABLE_STRIKETHROUGH);
options.insert(Options::ENABLE_TASKLISTS);
// options.insert(Options::ENABLE_SMART_PUNCTUATION);
// options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
options.insert(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS);
// options.insert(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS);
// options.insert(Options::ENABLE_OLD_FOOTNOTES);
options.insert(Options::ENABLE_MATH);
options.insert(Options::ENABLE_GFM);
options.insert(Options::ENABLE_DEFINITION_LIST);
options.insert(Options::ENABLE_SUPERSCRIPT);
options.insert(Options::ENABLE_SUBSCRIPT);
options.insert(Options::ENABLE_WIKILINKS);
let md = md_preprocessor(&md, jinja_env);
// let mut parser = Parser::new_ext("", options);
// let mut in_heading_two: Vec<Event> = Vec::new();
let parser = Parser::new_ext(&md, options).filter_map(|event| {
match &event {
Event::Start(tag) => {
match tag {
Tag::Paragraph => Some(event),
Tag::Heading { level, id, classes, attrs } => Some(event),
Tag::BlockQuote(_block_quote_kind) => Some(event),
Tag::CodeBlock(_code_block_kind) => Some(event),
Tag::HtmlBlock => Some(event),
Tag::List(_) => Some(event),
Tag::Item => Some(event),
Tag::FootnoteDefinition(_cow_str) => Some(event),
Tag::DefinitionList => Some(event),
Tag::DefinitionListTitle => Some(event),
Tag::DefinitionListDefinition => Some(event),
Tag::Table(_alignments) => Some(event),
Tag::TableHead => Some(event),
Tag::TableRow => Some(event),
Tag::TableCell => Some(event),
Tag::Emphasis => Some(event),
Tag::Strong => Some(event),
Tag::Strikethrough => Some(event),
Tag::Superscript => Some(event),
Tag::Subscript => Some(event),
Tag::Link { link_type, dest_url, title, id } => {
let re = Regex::new(r"^%(?<tags>[^%]+)%").unwrap();
// let out = .unwrap();
let (url, args) = match re.captures(&dest_url) {
Some(args) => {
(dest_url.strip_prefix(args.get(0).unwrap().as_str()).unwrap().to_string(), parse_args(args.name("tags").unwrap().as_str()))
},
None => (dest_url.to_string(), Value::UNDEFINED)
};
match render_component("link_start", context! {
link_type => link_type,
dest_url => url,
title => title,
id => id,
args => args
}, jinja_env)
{
Some(html) => Some(Event::Html(html.into())),
None => Some(event),
}
},
Tag::Image { link_type, dest_url, title, id } => Some(event),
Tag::MetadataBlock(_metadata_block_kind) => Some(event),
}
},
Event::End(_tag_end) => Some(event),
Event::Text(_cow_str) => Some(event),
Event::Code(_cow_str) => Some(event),
Event::InlineMath(_cow_str) => Some(event),
Event::DisplayMath(_cow_str) => Some(event),
Event::Html(_cow_str) => Some(event),
Event::InlineHtml(_cow_str) => Some(event),
Event::FootnoteReference(_cow_str) => Some(event),
Event::SoftBreak => Some(event),
Event::HardBreak => Some(event),
Event::Rule => Some(event),
Event::TaskListMarker(_) => Some(event),
}
});
let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, parser);
Some(html)
}
fn md_preprocessor(md: &String, jinja_env: &Environment) -> String {
let re = Regex::new(r"\{\% *(.*)\((.*)\).*\%\}").unwrap();
let out = re.replace_all(md, |capture: &regex::Captures<'_>| {
let template = &capture[1];
let args = &capture[2];
match render_component(template, context! { args => parse_args(args)}, jinja_env){
Some(html) => html,
None => String::from("")
}
});
String::from(out)
}
fn render_component(component: &str, args: Value, jinja_env: &Environment) -> Option<String> {
println!(" tag found: {component} <- {args}");
match jinja_env.get_template(&format!("components/{component}")) {
Ok(ding) => {
// match ding.render(context! { args => args }) {
match ding.render(args) {
Ok(html) => Some(html),
Err(err) => {
println!("ERROR: faild to render template: {component} ({err:?})");
None
},
}
},
Err(_) => {
println!("ERROR: could not find template: {component}");
Some("".to_string())
},
}
}
const NUMBER: i8 = 0;
const STRING: i8 = 1;
const OBJECT: i8 = 2;
const BOOLEAN: i8 = 3;
const UNDEFINED: i8 = 4;
fn parse_args(args: &str) -> Value {
let mut arg_type: i8 = UNDEFINED;
let mut hash: BTreeMap<String, Value> = BTreeMap::new();
let mut list: Vec<Value> = Vec::new();
for arg in args.split(",") {
if (arg.starts_with("\"") && arg.ends_with("\""))
|| (arg.starts_with("'") && arg.ends_with("'"))
{
if arg_type == UNDEFINED || arg_type == STRING {
let value = arg.strip_prefix(&['\'', '"']).unwrap().strip_suffix(&['\'', '"']).unwrap();
list.push(Value::from(value));
arg_type = STRING;
} else {
println!("ERROR: parse_md: syntax error in template include arguments (inconsistant type; {arg_type} != {STRING})");
return Value::UNDEFINED;
}
} else {
let mut obj: Vec<&str> = arg.split("=").collect();
if obj.len() > 1 {
if arg_type == UNDEFINED || arg_type == OBJECT {
if obj[0].len() > 0 {
arg_type = OBJECT;
let key = obj.remove(0);
hash.insert(key.to_string(), parse_args(&obj.join("=")));
} else {
println!("ERROR: parse_md: syntax error in template include arguments (empty key for key-value type)");
return Value::UNDEFINED;
}
} else {
println!("ERROR: parse_md: syntax error in template include arguments (inconsistant type; {arg_type} != {OBJECT})");
return Value::UNDEFINED;
}
} else {
match String::from(arg).parse::<f64>() {
Ok(number) => {
if arg_type == UNDEFINED || arg_type == NUMBER {
list.push(Value::from(number));
arg_type = NUMBER;
} else {
println!("ERROR: parse_md: syntax error in template include arguments (inconsistant type; {arg_type} != {NUMBER})");
return Value::UNDEFINED;
}
},
Err(_) => {
let arg_lower = arg.to_lowercase();
if arg_lower == "true" || arg_lower == "false" {
if arg_type == UNDEFINED || arg_type == BOOLEAN {
list.push(Value::from(arg_lower == "true"));
arg_type = BOOLEAN;
} else {
println!("ERROR: parse_md: syntax error in template include arguments (inconsistant type; {arg_type} != {BOOLEAN})");
return Value::UNDEFINED;
}
} else {
if arg_type == UNDEFINED || arg_type == OBJECT {
if obj[0].len() > 0 {
hash.insert(obj[0].to_string(), Value::from(true));
arg_type = OBJECT;
}
} else {
println!("ERROR: parse_md: syntax error in template include arguments (inconsistant type; {arg_type} != {OBJECT})");
return Value::UNDEFINED;
}
}
},
};
}
}
}
match &list[..] {
[value] => {
match arg_type {
NUMBER => value.to_owned(),
STRING => value.to_owned(),
BOOLEAN => value.to_owned(),
_ => Value::UNDEFINED
}
},
[_, _, ..] => Value::from(list),
[] => match arg_type {
OBJECT => Value::from(hash),
_ => Value::UNDEFINED
},
}
}

View File

@ -1,215 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto&family=Ubuntu&display=swap');
$main-color: #222;
$second-color: #333;
$oposit-color: #eee;
$accent-color: #7d7;
body {
margin: 0;
padding: 0;
overflow-x: hidden;
background-color: $main-color;
color: $oposit-color;
font-family: 'Roboto', sans-serif;
}
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
text-align: right;
height: 50px;
background: linear-gradient(180deg, $main-color 0%, $main-color 80%, rgba(0,0,0,0) 100%);
#headerLogo{
padding-left: 50px;
height: 50px;
}
ul {
list-style: none;
padding: 0 50px 0 0;
margin: 0;
display: inline-block;
user-select: none;
font-family: 'Ubuntu', sans-serif;
font-size: 18px;
.menuSeparator,
.menuLable{
display: inline-block;
}
li.sub .menuLable{
padding: 15px 5px;
a {
display: inline-block;
padding: 0;
}
}
li {
display: inline-block;
}
li.open {
.menuLable {
background-color: $second-color;
}
ul {
display: block;
ul {
display: none;
}
}
}
ul{
display: none;
position: absolute;
text-align: center;
padding: 0;
// margin-left: 10px;
background-color: $second-color;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
li {
display: block;
}
}
}
.menuDropIcon{
width: 15px;
}
a {
padding: 15px 5px;
color: $accent-color;
text-decoration: none;
&:hover{
color: $accent-color;
text-decoration: underline;
}
}
li a{
display: block;
}
}
aside{
height: 100vh;
position: fixed;
top: 0;
left: 0;
overflow-y: auto;
ul{
list-style: none;
padding: 50px 0 0 0;
margin: 0;
display: block;
user-select: none;
font-family: 'Ubuntu', sans-serif;
font-size: 18px;
.menuLable {
height: 50px;
}
ul {
padding: 0 0 0 25px;
}
}
a {
width: 150px;
display: block;
padding: 15px 5px;
color: $accent-color;
text-decoration: none;
&:hover{
color: $accent-color;
background-color: $second-color;
text-decoration: underline;
}
}
}
main {
padding: 0 calc(50vw - 250px) 50px;
max-width: 500px;
a {
color: $accent-color;
text-decoration: none;
&:hover{
text-decoration: underline;
}
}
a.btn {
padding: 5px;
border: 1px $accent-color solid;
border-radius: 5px;
&:hover{
color: $main-color;
background-color: $accent-color;
text-decoration: none;
}
}
img, video {
width: 100%;
}
}
#bigPicture {
display: none;
position: fixed;
top: 50vh;
width: 100vw;
transform: translateY(-50%);
&.show{
display: block;
}
&.tall{
width: auto;
height: 100vh;
left: 50vw;
transform: translateY(-50%) translateX(-50%);
}
}
@media only screen and (max-width: 850px){
main {
padding-left: 175px;
}
}
@media only screen and (max-width: 555px){
main {
width: auto;
padding: 0 28px 50px;
}
}
h1, h2, h3, h4, h5, h6{
font-family: 'Ubuntu', sans-serif;
}
h1 { padding-top: 50px; font-size: 40px; }
h2 { padding-top: 40px; font-size: 30px; }
h3 { padding-top: 10px; font-size: 20px; }

View File

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/style.css">
<title>Kladjes</title>
</head>
<body>
<header>
<nav>
<ul class="menu">
{{ header }}
</ul>
</nav>
</header>
{{ sidebar }}
<main>
{{ body }}
</main>
<script src="/scripts/app.js"></script>
</body>
</html>