Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35eac48dcd | |||
| ef16ed4c88 | |||
| 30b7681234 | |||
| 13b6106efd | |||
| 831b081fc7 | |||
| fde423a32b | |||
| 55a82f75a9 | |||
| f85abd43c4 | |||
| 0f66ebf52a | |||
| 55a515ccd5 | |||
| de8cbefb8e | |||
| cc7fee573f | |||
| 137be9579a | |||
| 5afd0170e5 | |||
| 5a2053cfb4 |
@@ -2,9 +2,12 @@ name: Publish kewt-git to AUR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'packaging/AUR/PKGBUILD.git'
|
||||
- 'packaging/AUR/.SRCINFO.git'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-aur-git:
|
||||
|
||||
@@ -3,6 +3,7 @@ name: Release Standalone Builder
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -103,3 +104,34 @@ jobs:
|
||||
commit_email: ${{ github.actor }}@users.noreply.github.com
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: "Update kewt-bin to ${{ github.ref_name }}"
|
||||
|
||||
publish-homebrew:
|
||||
runs-on: local
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Render Formula and push to GitHub
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${TAG#v}"
|
||||
|
||||
curl -sL -o kewt-binary \
|
||||
"https://git.krzak.org/N0VA/kewt/releases/download/${TAG}/kewt"
|
||||
CHECKSUM=$(sha256sum kewt-binary | awk '{print $1}')
|
||||
rm -f kewt-binary
|
||||
|
||||
git clone https://x-access-token:${{ secrets.GH_RELEASE_TOKEN }}@github.com/n0va-bot/homebrew-tap.git brew-work || true
|
||||
mkdir -p brew-work/Formula
|
||||
sed -e "s/VERSION_PLACEHOLDER/${VERSION}/g" \
|
||||
-e "s/SHA256SUM_PLACEHOLDER/${CHECKSUM}/g" \
|
||||
packaging/homebrew/kewt.rb.template > brew-work/Formula/kewt.rb
|
||||
|
||||
cd brew-work
|
||||
[ -d .git ] || { git init && git checkout --orphan main && git remote add origin https://x-access-token:${{ secrets.GH_RELEASE_TOKEN }}@github.com/n0va-bot/homebrew-tap.git; }
|
||||
git add Formula/kewt.rb
|
||||
git config user.name "${{ github.actor }}"
|
||||
git config user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
git commit -m "Update kewt to ${TAG}" || echo "No changes to commit"
|
||||
git push -u origin main
|
||||
|
||||
19
Makefile
Normal file
19
Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
all: kewt
|
||||
|
||||
kewt:
|
||||
./tools/build-standalone.sh
|
||||
|
||||
install: kewt
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
install -m 755 kewt $(DESTDIR)$(BINDIR)/kewt
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/kewt
|
||||
|
||||
clean:
|
||||
rm -f kewt
|
||||
|
||||
.PHONY: all install uninstall clean
|
||||
18
README.md
18
README.md
@@ -10,22 +10,12 @@
|
||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||
|
||||
|
||||
## Quick Install
|
||||
|
||||
```sh
|
||||
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/download/latest/kewt
|
||||
chmod +x kewt
|
||||
```
|
||||
|
||||
On Arch Linux, _kewt_ is available on the AUR:
|
||||
|
||||
- [kewt-bin](https://aur.archlinux.org/packages/kewt-bin) — prebuilt standalone binary from the latest release
|
||||
- [kewt-git](https://aur.archlinux.org/packages/kewt-git) — built from the latest git source
|
||||
## [Installation](https://kewt.krzak.org/#Installation)
|
||||
|
||||
## Contributing
|
||||
|
||||
Either through a pull request to the **home** repository ([N0VA/kewt](https://git.krzak.org/N0VA/kewt)) or by sending a patch to my email address ([n0va@krzak.org](mailto:n0va@krzak.org))
|
||||
Either open an issue or pull request on the **home** repository ([N0VA/kewt](https://git.krzak.org/N0VA/kewt)) or message me on my email address ([n0va@krzak.org](mailto:n0va@krzak.org?subject=%5Bkewt%5D%20something)) with the subjectline being `[kewt] something`.
|
||||
|
||||
## Credits
|
||||
## License
|
||||
|
||||
- _kew_ css style adapted from _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||
ISC
|
||||
|
||||
@@ -22,7 +22,19 @@ END {
|
||||
sub(/^\[!/, "", kind)
|
||||
sub(/\]$/, "", kind)
|
||||
lkind = tolower(kind)
|
||||
if (lkind == "note" || lkind == "tip" || lkind == "important" || lkind == "warning" || lkind == "caution") {
|
||||
is_valid = 0
|
||||
if (custom_admonitions != "") {
|
||||
n = split(tolower(custom_admonitions), adms, ",")
|
||||
for (idx = 1; idx <= n; idx++) {
|
||||
adm = adms[idx]
|
||||
sub(/^[ \t]+/, "", adm)
|
||||
sub(/[ \t]+$/, "", adm)
|
||||
if (lkind == adm) { is_valid = 1; break }
|
||||
}
|
||||
} else if (lkind == "note" || lkind == "tip" || lkind == "important" || lkind == "warning" || lkind == "caution") {
|
||||
is_valid = 1
|
||||
}
|
||||
if (is_valid) {
|
||||
print "<div class=\"admonition admonition-" lkind "\">"
|
||||
print "<p class=\"admonition-title\">" cap(lkind) "</p>"
|
||||
has_body = 0
|
||||
|
||||
46
awk/frontmatter.awk
Normal file
46
awk/frontmatter.awk
Normal file
@@ -0,0 +1,46 @@
|
||||
BEGIN {
|
||||
state = "start"
|
||||
}
|
||||
{
|
||||
if (state == "start") {
|
||||
if ($0 == "---") {
|
||||
state = "in_fm"
|
||||
next
|
||||
} else {
|
||||
state = "body"
|
||||
print
|
||||
next
|
||||
}
|
||||
}
|
||||
if (state == "in_fm") {
|
||||
if ($0 == "---") {
|
||||
state = "body"
|
||||
next
|
||||
}
|
||||
line = $0
|
||||
if (line ~ /^[[:space:]]*$/ || line ~ /^[[:space:]]*#/) next
|
||||
if (line !~ /=/) next
|
||||
|
||||
key = line
|
||||
val = line
|
||||
sub(/=.*/, "", key)
|
||||
sub(/[^=]*=/, "", val)
|
||||
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", val)
|
||||
|
||||
if (val ~ /^".*"$/) {
|
||||
val = substr(val, 2, length(val) - 2)
|
||||
gsub(/\\"/, "\"", val)
|
||||
} else if (val ~ /^'.*'$/) {
|
||||
val = substr(val, 2, length(val) - 2)
|
||||
gsub(/\\'/, "'", val)
|
||||
}
|
||||
|
||||
if (fm_out != "") {
|
||||
print key "=" val >> fm_out
|
||||
}
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
@@ -3,8 +3,14 @@ function strip_markdown(s) {
|
||||
gsub(/[*_`~]/, "", s)
|
||||
gsub(/[\[\]]/, "", s)
|
||||
gsub(/\([^\)]*\)/, "", s)
|
||||
s = tolower(s)
|
||||
gsub(/[^a-z0-9 -]/, "", s)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
|
||||
gsub(/[[:space:]]+/, "-", s)
|
||||
gsub(/-{2,}/, "-", s)
|
||||
gsub(/^-+|-+$/, "", s)
|
||||
if (length(s) > 80) s = substr(s, 1, 80)
|
||||
gsub(/-+$/, "", s)
|
||||
return s
|
||||
}
|
||||
function print_header(line) {
|
||||
|
||||
@@ -48,7 +48,26 @@ BEGIN {
|
||||
}
|
||||
}
|
||||
|
||||
print "<li>" content "</li>"
|
||||
has_checkbox = 0
|
||||
if (content ~ /^\[[ \t]\] /) {
|
||||
has_checkbox = 1
|
||||
is_checked = 0
|
||||
sub(/^\[[ \t]\] /, "", content)
|
||||
} else if (content ~ /^\[[xX]\] /) {
|
||||
has_checkbox = 1
|
||||
is_checked = 1
|
||||
sub(/^\[[xX]\] /, "", content)
|
||||
}
|
||||
|
||||
if (has_checkbox) {
|
||||
if (is_checked) {
|
||||
print "<li class=\"task-list-item\"><input type=\"checkbox\" class=\"task-list-item-checkbox\" checked disabled> " content "</li>"
|
||||
} else {
|
||||
print "<li class=\"task-list-item\"><input type=\"checkbox\" class=\"task-list-item-checkbox\" disabled> " content "</li>"
|
||||
}
|
||||
} else {
|
||||
print "<li>" content "</li>"
|
||||
}
|
||||
} else {
|
||||
while (depth > 0) {
|
||||
print "</" cur_type[depth] ">"
|
||||
|
||||
@@ -204,7 +204,7 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
||||
}
|
||||
if (is_audio_ext(ext)) return "<audio controls src=\"" src "\"></audio>"
|
||||
if (is_video_ext(ext)) return "<video controls src=\"" src "\"></video>"
|
||||
return "<iframe src=\"" src "\"></iframe>"
|
||||
return "<iframe src=\"" src "\" allowfullscreen></iframe>"
|
||||
}
|
||||
|
||||
if (is_image_ext(ext)) {
|
||||
@@ -223,7 +223,29 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
||||
}
|
||||
}
|
||||
|
||||
return "<iframe src=\"" src "\"></iframe>"
|
||||
return "<iframe src=\"" src "\" allowfullscreen></iframe>"
|
||||
}
|
||||
|
||||
function render_typed_embed(etype, src, alt, has_alt, local_path, content) {
|
||||
if (etype == "i") {
|
||||
if (has_alt) return "<img alt=\"" alt "\" src=\"" src "\" />"
|
||||
return "<img src=\"" src "\" />"
|
||||
}
|
||||
if (etype == "v") return "<video controls src=\"" src "\"></video>"
|
||||
if (etype == "a") return "<audio controls src=\"" src "\"></audio>"
|
||||
if (etype == "f") return "<iframe src=\"" src "\" allowfullscreen></iframe>"
|
||||
if (etype == "e") {
|
||||
if (!is_global_url(src)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
}
|
||||
return render_embed(src, alt, has_alt, 1)
|
||||
}
|
||||
return render_embed(src, alt, has_alt, 0)
|
||||
}
|
||||
|
||||
function extract_attr(tag, attr, pat, m, token) {
|
||||
@@ -319,7 +341,7 @@ function apply_td_vertical_align(line, out, rest, seg, td_tag, img_tag, after
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, pre, post, repl) {
|
||||
function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, embed_type, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /<img[^>]*\/?>/)) {
|
||||
@@ -329,7 +351,10 @@ function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, p
|
||||
src = extract_attr(tag, "src")
|
||||
alt = extract_attr(tag, "alt")
|
||||
force_inline_tag = extract_attr(tag, "data-force-inline")
|
||||
if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
|
||||
embed_type = extract_attr(tag, "data-embed-type")
|
||||
if (embed_type != "") {
|
||||
repl = render_typed_embed(embed_type, src, alt, (alt != ""))
|
||||
} else if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
|
||||
# Preserve hand-written <img> attributes (style/class/etc) for normal images.
|
||||
repl = tag
|
||||
} else {
|
||||
|
||||
@@ -20,9 +20,11 @@ function mask_html_tags(s, out, rest, start, len, tag, token) {
|
||||
return out rest
|
||||
}
|
||||
|
||||
function restore_html_tags(s, i) {
|
||||
function restore_html_tags(s, i, val) {
|
||||
for (i = 1; i <= html_tag_count; i++) {
|
||||
gsub(html_tag_token[i], html_tag_value[i], s)
|
||||
val = html_tag_value[i]
|
||||
gsub(/&/, "\\\\&", val)
|
||||
gsub(html_tag_token[i], val, s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -58,6 +60,36 @@ function restore_html_tags(s, i) {
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
|
||||
# typed embeds: !i, !v, !a, !f, !e
|
||||
while (match(line, /![ivafe]\[[^\]]*\]\([^\)]+ "[^"]*"\)/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
token = substr(line, start, len)
|
||||
etype = substr(token, 2, 1)
|
||||
match(token, /\[[^\]]*\]/); alt = substr(token, RSTART + 1, RLENGTH - 2)
|
||||
match(token, /"[^"]*"/); etitle = substr(token, RSTART + 1, RLENGTH - 2)
|
||||
match(token, /\([^\)]+/); inner = substr(token, RSTART + 1, RLENGTH - 1)
|
||||
sub(/[[:space:]]*"[^"]*"/, "", inner); src = inner
|
||||
repl = "<img data-embed-type=\"" etype "\" alt=\"" alt "\" src=\"" src "\" title=\"" etitle "\" />"
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
while (match(line, /![ivafe]\[[^\]]*\]\([^\)]+\)/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
token = substr(line, start, len)
|
||||
etype = substr(token, 2, 1)
|
||||
match(token, /\[[^\]]*\]/); alt = substr(token, RSTART + 1, RLENGTH - 2)
|
||||
match(token, /\([^\)]+/); src = substr(token, RSTART + 1, RLENGTH - 1)
|
||||
repl = "<img data-embed-type=\"" etype "\" alt=\"" alt "\" src=\"" src "\" />"
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
while (match(line, /![ivafe]\[[^\]]+\]/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
token = substr(line, start, len)
|
||||
etype = substr(token, 2, 1)
|
||||
src = substr(token, 4, len - 4)
|
||||
repl = "<img data-embed-type=\"" etype "\" src=\"" src "\" />"
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
|
||||
# force-inline image syntax (double bang)
|
||||
while (match(line, /!!\[[^\]]*\]\([^\)]+ "[^"]*"\)/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
|
||||
268
kewt.sh
268
kewt.sh
@@ -33,9 +33,17 @@ awk_dir="$script_dir/awk"
|
||||
KEWT_TMPDIR=$(mktemp -d "/tmp/kewt_run.XXXXXX")
|
||||
trap 'rm -rf "$KEWT_TMPDIR"' EXIT HUP INT TERM
|
||||
|
||||
ensure_root_defaults() {
|
||||
if [ ! -f "./site.conf" ]; then
|
||||
cat > "./site.conf" <<'EOF'
|
||||
|
||||
|
||||
create_new_site() {
|
||||
new_title="$1"
|
||||
new_dir="site"
|
||||
[ -n "$new_title" ] && new_dir="$new_title"
|
||||
|
||||
[ -e "$new_dir" ] && die "Target '$new_dir' already exists."
|
||||
|
||||
mkdir -p "$new_dir"
|
||||
cat > "$new_dir/site.conf" <<'EOF'
|
||||
title = "kewt"
|
||||
style = "kewt"
|
||||
dir_indexes = true
|
||||
@@ -60,11 +68,10 @@ base_url = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
custom_admonitions = ""
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ ! -f "./template.html" ]; then
|
||||
cat > "./template.html" <<'EOF'
|
||||
cat > "$new_dir/template.html" <<'EOF'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -88,20 +95,6 @@ EOF
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
create_new_site() {
|
||||
new_title="$1"
|
||||
new_dir="site"
|
||||
[ -n "$new_title" ] && new_dir="$new_title"
|
||||
|
||||
[ -e "$new_dir" ] && die "Target '$new_dir' already exists."
|
||||
|
||||
ensure_root_defaults
|
||||
|
||||
mkdir -p "$new_dir"
|
||||
cp "./site.conf" "$new_dir/site.conf"
|
||||
printf "# _kewt_ website\n" > "$new_dir/index.md"
|
||||
|
||||
if [ -n "$new_title" ]; then
|
||||
@@ -114,6 +107,7 @@ create_new_site() {
|
||||
|
||||
create_new_post() {
|
||||
post_src_dir="$1"
|
||||
post_user_title="$2"
|
||||
|
||||
target_dir="$post_src_dir"
|
||||
if [ -n "$posts_dir" ]; then
|
||||
@@ -133,7 +127,12 @@ create_new_post() {
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
|
||||
touch "$file_path"
|
||||
post_date_val="$(date "+%Y-%m-%d %H:%M")"
|
||||
if [ -n "$post_user_title" ]; then
|
||||
printf -- '---\ntitle = "%s"\ndate = "%s"\ndraft = false\n---\n# %s\n' "$post_user_title" "$post_date_val" "$post_user_title" > "$file_path"
|
||||
else
|
||||
printf -- '---\ndate = "%s"\ndraft = false\n---\n' "$post_date_val" > "$file_path"
|
||||
fi
|
||||
|
||||
echo "Created new post at '$file_path'."
|
||||
exit 0
|
||||
@@ -173,6 +172,7 @@ base_url = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
custom_admonitions = ""
|
||||
CONFEOF
|
||||
|
||||
# Update site.conf
|
||||
@@ -269,6 +269,10 @@ while [ $# -gt 0 ]; do
|
||||
;;
|
||||
--post)
|
||||
post_mode="true"
|
||||
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
||||
post_title="$2"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--update)
|
||||
update_dir="."
|
||||
@@ -307,7 +311,7 @@ done
|
||||
|
||||
[ "$new_mode" = "true" ] && create_new_site "$new_title"
|
||||
|
||||
ensure_root_defaults
|
||||
|
||||
|
||||
if [ -z "$src" ]; then
|
||||
if [ "$post_mode" = "true" ] && [ -f "./site.conf" ]; then
|
||||
@@ -454,6 +458,7 @@ base_url=""
|
||||
generate_feed="false"
|
||||
feed_file="rss.xml"
|
||||
posts_dir=""
|
||||
custom_admonitions=""
|
||||
|
||||
load_config() {
|
||||
[ -f "$1" ] || return
|
||||
@@ -505,6 +510,7 @@ load_config() {
|
||||
generate_feed) generate_feed="$val" ;;
|
||||
feed_file) feed_file="${val#/}" ;;
|
||||
posts_dir) posts_dir="${val#/}" ;;
|
||||
custom_admonitions) custom_admonitions="$val" ;;
|
||||
esac
|
||||
done < "$1"
|
||||
}
|
||||
@@ -516,7 +522,7 @@ if [ -n "$posts_dir" ]; then
|
||||
HIDE_ARGS="$HIDE_ARGS -o -path '$src/$posts_dir/*'"
|
||||
fi
|
||||
|
||||
[ "$post_mode" = "true" ] && create_new_post "$src"
|
||||
[ "$post_mode" = "true" ] && create_new_post "$src" "$post_title"
|
||||
|
||||
asset_version=""
|
||||
if [ "$versioning" = "true" ]; then
|
||||
@@ -538,6 +544,24 @@ escape_html_attr() {
|
||||
-e 's/>/\>/g'
|
||||
}
|
||||
|
||||
parse_frontmatter() {
|
||||
_fm_file="$1"
|
||||
_fm_out="$KEWT_TMPDIR/fm_vals.txt"
|
||||
: > "$_fm_out"
|
||||
awk -v fm_out="$_fm_out" -f "$awk_dir/frontmatter.awk" "$_fm_file" > /dev/null
|
||||
fm_title=""
|
||||
fm_date=""
|
||||
fm_draft=""
|
||||
while IFS='=' read -r _fk _fv; do
|
||||
case "$_fk" in
|
||||
title) fm_title="$_fv" ;;
|
||||
date) fm_date="$_fv" ;;
|
||||
draft) fm_draft="$_fv" ;;
|
||||
esac
|
||||
done < "$_fm_out"
|
||||
rm -f "$_fm_out"
|
||||
}
|
||||
|
||||
nav_links_html() {
|
||||
[ -n "$nav_links" ] || return
|
||||
|
||||
@@ -584,7 +608,33 @@ nav_links_html() {
|
||||
|
||||
template="$src/template.html"
|
||||
[ -f "$template" ] || template="./template.html"
|
||||
[ -f "$template" ] || die "Template '$template' not found."
|
||||
if [ ! -f "$template" ]; then
|
||||
template="$KEWT_TMPDIR/default_template.html"
|
||||
cat > "$template" <<'EOF'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{TITLE}}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
||||
{{HEAD_EXTRA}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{HEADER_BRAND}}</h1>
|
||||
</header>
|
||||
|
||||
<nav id="side-bar">{{NAV}}</nav>
|
||||
|
||||
<article>{{CONTENT}}</article>
|
||||
<footer>{{FOOTER}}</footer>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
fi
|
||||
|
||||
[ -d "$out" ] && rm -rf "$out"
|
||||
mkdir -p "$out"
|
||||
@@ -643,7 +693,7 @@ render_markdown() {
|
||||
if [ "$rel_dir_of_file" = "$posts_dir" ]; then
|
||||
temp_post_with_backlink="$KEWT_TMPDIR/post_with_backlink.md"
|
||||
printf "[< Back](index.html)\n\n" > "$temp_post_with_backlink"
|
||||
cat "$file" >> "$temp_post_with_backlink"
|
||||
awk -f "$awk_dir/frontmatter.awk" "$file" >> "$temp_post_with_backlink"
|
||||
content_file="$temp_post_with_backlink"
|
||||
fi
|
||||
fi
|
||||
@@ -693,11 +743,21 @@ render_markdown() {
|
||||
fi
|
||||
head_extra=""
|
||||
if [ -n "$favicon_src" ]; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
if echo "$favicon_src" | grep -q "^http"; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
elif echo "$favicon_src" | grep -q "^/"; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
else
|
||||
head_extra="<link rel=\"icon\" href=\"/$favicon_src\" />"
|
||||
fi
|
||||
fi
|
||||
|
||||
parse_frontmatter "$file"
|
||||
|
||||
page_title="$title"
|
||||
if [ "$generate_page_title" = "true" ] && [ -n "$file" ] && [ -f "$file" ]; then
|
||||
if [ -n "$fm_title" ]; then
|
||||
page_title="$fm_title - $title"
|
||||
elif [ "$generate_page_title" = "true" ] && [ -n "$file" ] && [ -f "$file" ]; then
|
||||
if [ "$is_home" = "true" ] && [ -n "$home_name" ]; then
|
||||
page_title="$home_name - $title"
|
||||
else
|
||||
@@ -715,7 +775,19 @@ render_markdown() {
|
||||
fi
|
||||
fi
|
||||
|
||||
ENABLE_HEADER_LINKS="$enable_header_links" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$content_file" | AWK_CURRENT_URL="$current_url" AWK_TITLE="$page_title" AWK_NAV="$nav" AWK_FOOTER="$footer" AWK_STYLE_PATH="${style_path}${asset_version}" AWK_HEADER_BRAND="$header_brand" AWK_HEAD_EXTRA="$head_extra" awk -f "$awk_dir/render_template.awk" "$local_template"
|
||||
ENABLE_HEADER_LINKS="$enable_header_links" CUSTOM_ADMONITIONS="$custom_admonitions" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$content_file" | AWK_CURRENT_URL="$current_url" AWK_TITLE="$page_title" AWK_NAV="$nav" AWK_FOOTER="$footer" AWK_STYLE_PATH="${style_path}${asset_version}" AWK_HEADER_BRAND="$header_brand" AWK_HEAD_EXTRA="$head_extra" awk -f "$awk_dir/render_template.awk" "$local_template"
|
||||
}
|
||||
|
||||
needs_rebuild() {
|
||||
src_file="$1"
|
||||
out_file="$2"
|
||||
[ ! -f "$out_file" ] && return 0
|
||||
[ "$src_file" -nt "$out_file" ] && return 0
|
||||
[ -f "./site.conf" ] && [ "./site.conf" -nt "$out_file" ] && return 0
|
||||
[ -f "$src/site.conf" ] && [ "$src/site.conf" -nt "$out_file" ] && return 0
|
||||
[ -f "$template" ] && [ "$template" -nt "$out_file" ] && return 0
|
||||
[ -f "$script_dir/styles/$style.css" ] && [ "$script_dir/styles/$style.css" -nt "$out_file" ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "Building site from '$src' to '$out'..."
|
||||
@@ -728,9 +800,13 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
mkdir -p "$out_dir"
|
||||
|
||||
if [ -f "$dir/styles.css" ]; then
|
||||
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||
if needs_rebuild "$dir/styles.css" "$out_dir/styles.css"; then
|
||||
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||
fi
|
||||
elif [ -f "$dir/style.css" ]; then
|
||||
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||
if needs_rebuild "$dir/style.css" "$out_dir/styles.css"; then
|
||||
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||
fi
|
||||
fi
|
||||
|
||||
[ "$dir_indexes" != "true" ] && continue
|
||||
@@ -747,7 +823,9 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url="/$rel_dir/index.html"
|
||||
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
if needs_rebuild "$md_file" "$out_dir/index.html"; then
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
@@ -774,30 +852,69 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
label="${name%.md}"
|
||||
|
||||
# Try to get first heading
|
||||
post_h=$(grep -m 1 '^# ' "$entry" | sed 's/^# *//')
|
||||
if [ -n "$post_h" ]; then
|
||||
post_h=$(echo "$post_h" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||
# Parse frontmatter for date/title/draft
|
||||
parse_frontmatter "$entry"
|
||||
[ "$fm_draft" = "true" ] && continue
|
||||
|
||||
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||
# For posts add date and time
|
||||
# Try to get first heading
|
||||
post_h="$fm_title"
|
||||
if [ -z "$post_h" ]; then
|
||||
post_h=$(grep -m 1 '^# ' "$entry" | sed 's/^# *//')
|
||||
if [ -n "$post_h" ]; then
|
||||
post_h=$(echo "$post_h" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||
fi
|
||||
fi
|
||||
|
||||
is_post_entry="false"
|
||||
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||
is_post_entry="true"
|
||||
fi
|
||||
|
||||
if [ -n "$post_h" ]; then
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
# Use frontmatter date if available, else parse from filename
|
||||
if [ -n "$fm_date" ]; then
|
||||
p_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
p_time=""
|
||||
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
p_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
else
|
||||
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
p_time="00:00"
|
||||
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
fi
|
||||
if [ -n "$p_time" ]; then
|
||||
label="$post_h - $p_date $p_time"
|
||||
else
|
||||
label="$post_h - $p_date"
|
||||
fi
|
||||
else
|
||||
label="$post_h"
|
||||
fi
|
||||
elif [ "$is_post_entry" = "true" ]; then
|
||||
# No heading; use date
|
||||
if [ -n "$fm_date" ]; then
|
||||
p_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
p_time=""
|
||||
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
p_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
if [ -n "$p_time" ]; then
|
||||
label="$p_date $p_time"
|
||||
else
|
||||
label="$p_date"
|
||||
fi
|
||||
else
|
||||
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
p_time="00:00"
|
||||
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
label="$post_h - $p_date $p_time"
|
||||
else
|
||||
label="$post_h"
|
||||
label="$p_date $p_time"
|
||||
fi
|
||||
elif [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||
# No heading and date and time for posts
|
||||
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
p_time="00:00"
|
||||
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
label="$p_date $p_time"
|
||||
fi
|
||||
echo "- [$label](${name%.md}.html)" >> "$temp_index"
|
||||
else
|
||||
@@ -807,12 +924,14 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url="/$rel_dir/index.html"
|
||||
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
if needs_rebuild "$dir" "$out_dir/index.html"; then
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
rm "$temp_index"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -f "$out/styles.css" ] && [ -f "$script_dir/styles/$style.css" ]; then
|
||||
if [ -f "$script_dir/styles/$style.css" ] && needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then
|
||||
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
|
||||
fi
|
||||
|
||||
@@ -842,11 +961,20 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while
|
||||
fi
|
||||
|
||||
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
|
||||
# Skip draft files
|
||||
parse_frontmatter "$file"
|
||||
if [ "$fm_draft" = "true" ]; then
|
||||
continue
|
||||
fi
|
||||
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
|
||||
out_file="$out/${rel_path%.md}.html"
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
if needs_rebuild "$file" "$out_file"; then
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
fi
|
||||
else
|
||||
cp "$file" "$out/$rel_path"
|
||||
if needs_rebuild "$file" "$out/$rel_path"; then
|
||||
cp "$file" "$out/$rel_path"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -895,20 +1023,34 @@ if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
printf ' <description>%s</description>\n' "$title" >> "$feed_path"
|
||||
printf ' <lastBuildDate>%s</lastBuildDate>\n' "$build_date" >> "$feed_path"
|
||||
|
||||
find "$src" -type f -name '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*.md' -print | LC_ALL=C sort -r | while IFS= read -r post_file; do
|
||||
find "$src" -type f -name '*.md' -path "*${posts_dir:-__no_posts__}*" -print | LC_ALL=C sort -r | while IFS= read -r post_file; do
|
||||
post_basename=$(basename "$post_file" .md)
|
||||
# Extract YYYY-MM-DD
|
||||
post_date=$(echo "$post_basename" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
|
||||
# Extract HH:MM if present (e.g., 2026-03-17-10:30 or 2026-03-17-10:30_1)
|
||||
post_time="00:00"
|
||||
if echo "$post_basename" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
post_time=$(echo "$post_basename" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
# Parse frontmatter
|
||||
parse_frontmatter "$post_file"
|
||||
[ "$fm_draft" = "true" ] && continue
|
||||
|
||||
# Use frontmatter date, fallback to filename
|
||||
if [ -n "$fm_date" ]; then
|
||||
post_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
post_time="00:00"
|
||||
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
post_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
else
|
||||
post_date=$(echo "$post_basename" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||
post_time="00:00"
|
||||
if echo "$post_basename" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||
post_time=$(echo "$post_basename" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||
fi
|
||||
fi
|
||||
|
||||
post_slug=$(echo "$post_basename" | sed -e 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}//' -e 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}//' -e 's/^[_\-]//')
|
||||
|
||||
post_heading=$(grep -m 1 '^# ' "$post_file" | sed 's/^# *//')
|
||||
post_heading="$fm_title"
|
||||
if [ -z "$post_heading" ]; then
|
||||
post_heading=$(grep -m 1 '^# ' "$post_file" | sed 's/^# *//')
|
||||
fi
|
||||
if [ -z "$post_heading" ]; then
|
||||
if [ -n "$post_slug" ] && ! echo "$post_slug" | grep -q '^[0-9]\+$'; then
|
||||
post_heading=$(echo "$post_slug" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)}1')
|
||||
@@ -917,7 +1059,7 @@ if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
fi
|
||||
fi
|
||||
post_heading=$(echo "$post_heading" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||
post_title="$post_heading - $post_date $post_time"
|
||||
feed_post_title="$post_heading - $post_date $post_time"
|
||||
|
||||
rel_path="${post_file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
@@ -926,6 +1068,8 @@ if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
pub_year=$(echo "$post_date" | cut -d- -f1)
|
||||
pub_month=$(echo "$post_date" | cut -d- -f2)
|
||||
pub_day=$(echo "$post_date" | cut -d- -f3)
|
||||
# zero-padded
|
||||
pub_day=$(printf '%02d' "${pub_day#0}")
|
||||
case "$pub_month" in
|
||||
01) pub_mon="Jan" ;; 02) pub_mon="Feb" ;; 03) pub_mon="Mar" ;;
|
||||
04) pub_mon="Apr" ;; 05) pub_mon="May" ;; 06) pub_mon="Jun" ;;
|
||||
@@ -935,7 +1079,7 @@ if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
pub_date="${pub_day} ${pub_mon} ${pub_year} ${post_time}:00 +0000"
|
||||
|
||||
printf ' <item>\n' >> "$feed_path"
|
||||
printf ' <title>%s</title>\n' "$post_title" >> "$feed_path"
|
||||
printf ' <title>%s</title>\n' "$feed_post_title" >> "$feed_path"
|
||||
printf ' <link>%s</link>\n' "$post_url" >> "$feed_path"
|
||||
printf ' <guid>%s</guid>\n' "$post_url" >> "$feed_path"
|
||||
printf ' <pubDate>%s</pubDate>\n' "$pub_date" >> "$feed_path"
|
||||
|
||||
20
markdown.sh
20
markdown.sh
@@ -15,10 +15,15 @@ sed_inplace() {
|
||||
fi
|
||||
}
|
||||
|
||||
temp_file="/tmp/markdown.$$.md"
|
||||
temp_file="${KEWT_TMPDIR:-/tmp}/markdown.$$.md"
|
||||
cat "$@" > "$temp_file"
|
||||
|
||||
trap 'rm -f "$temp_file" "$temp_file.tmp"' EXIT INT TERM
|
||||
trap 'rm -f "$temp_file" "$temp_file.tmp" "$temp_file.fm"' EXIT INT TERM
|
||||
|
||||
# Frontmatter
|
||||
fm_file="$temp_file.fm"
|
||||
: > "$fm_file"
|
||||
awk -v fm_out="$fm_file" -f "$awk_dir/frontmatter.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
# Mask
|
||||
awk -f "$awk_dir/mask_inline_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
@@ -40,14 +45,19 @@ done
|
||||
sed_inplace "/^\[[^\]]*\]: */d" "$temp_file"
|
||||
|
||||
# Blocks
|
||||
sed_inplace "s/^>!\[/> [!/g" "$temp_file"
|
||||
sed_inplace "s/^>\[!/> [!/g" "$temp_file"
|
||||
|
||||
loop_count=0
|
||||
max_iterations=100
|
||||
while grep '^>' "$temp_file" >/dev/null; do
|
||||
awk -f "$awk_dir/blockquote.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
loop_count=$((loop_count + 1))
|
||||
if [ "$loop_count" -gt "$max_iterations" ]; then
|
||||
echo "Warning: Blockquote processing exceeded $max_iterations iterations on $1. Breaking to prevent infinite loop." >&2
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
awk -f "$awk_dir/blockquote_to_admonition.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -v custom_admonitions="$CUSTOM_ADMONITIONS" -f "$awk_dir/blockquote_to_admonition.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/fenced_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/indented_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
7
package.json
Normal file
7
package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "kewt",
|
||||
"description": "A minimalist static site generator inspired by werc",
|
||||
"global": "true",
|
||||
"install": "make install",
|
||||
"scripts": ["kewt"]
|
||||
}
|
||||
16
packaging/homebrew/kewt.rb.template
Normal file
16
packaging/homebrew/kewt.rb.template
Normal file
@@ -0,0 +1,16 @@
|
||||
class Kewt < Formula
|
||||
desc "Minimalist static site generator inspired by werc"
|
||||
homepage "https://kewt.krzak.org"
|
||||
url "https://github.com/n0va-bot/kewt/releases/download/vVERSION_PLACEHOLDER/kewt"
|
||||
sha256 "SHA256SUM_PLACEHOLDER"
|
||||
license "ISC"
|
||||
version "VERSION_PLACEHOLDER"
|
||||
|
||||
def install
|
||||
bin.install "kewt"
|
||||
end
|
||||
|
||||
test do
|
||||
system "#{bin}/kewt", "--version"
|
||||
end
|
||||
end
|
||||
@@ -14,12 +14,15 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
||||
## Features
|
||||
|
||||
- No dependencies
|
||||
- Frontmatter support (title, date, draft)
|
||||
- Supports many embed types
|
||||
- Automatic css variable replacement for older browsers
|
||||
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
||||
- Typed embeds: `\!i`, `\!v`, `\!a`, `\!f`, `\!e`
|
||||
- Inline html support
|
||||
- MFM `$font` and `\<plain>` tags
|
||||
- Admonition support (that's what the blocks like the warning block below are called)
|
||||
- GFM Admonition support (that's what the blocks like the warning block below are called)
|
||||
- Task list support (`- [ ]`, `- [x]`)
|
||||
- RSS/Feed generation and Sitemap support
|
||||
- Post creation via `--post`
|
||||
- Automatic 404 page generation
|
||||
@@ -30,20 +33,58 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
||||
|
||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||
|
||||
***
|
||||
|
||||
## Installation
|
||||
|
||||
You can clone the repository to use `kewt.sh` directly, or you can download the standalone executable, which bundles all dependencies into a single file:
|
||||
### Standalone
|
||||
|
||||
```sh
|
||||
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/download/latest/kewt
|
||||
chmod +x kewt
|
||||
```
|
||||
|
||||
On Arch Linux, _kewt_ is available on the AUR:
|
||||
### From source
|
||||
|
||||
```sh
|
||||
git clone https://git.krzak.org/N0VA/kewt.git
|
||||
cd kewt
|
||||
```
|
||||
|
||||
#### Building
|
||||
|
||||
```sh
|
||||
make
|
||||
```
|
||||
|
||||
#### Installing
|
||||
|
||||
```sh
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Package Managers
|
||||
|
||||
#### AUR
|
||||
|
||||
- [kewt-bin](https://aur.archlinux.org/packages/kewt-bin) — prebuilt standalone binary from the latest release
|
||||
- [kewt-git](https://aur.archlinux.org/packages/kewt-git) — built from the latest git source
|
||||
|
||||
#### Homebrew
|
||||
|
||||
```sh
|
||||
brew tap n0va-bot/tap
|
||||
brew install kewt
|
||||
```
|
||||
|
||||
#### bpkg
|
||||
|
||||
```sh
|
||||
bpkg install n0va-bot/kewt
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
@@ -57,9 +98,9 @@ On Arch Linux, _kewt_ is available on the AUR:
|
||||
|
||||
`--new [title]` creates a new site directory with a copied `site.conf` and a default `index.md`.
|
||||
|
||||
`--post` creates a new empty markdown file in the configured `posts_dir` with the current date and time as the name.
|
||||
`--post [title]` creates a new markdown file in the configured `posts_dir` with the current date/time as the name and creates the default frontmatter.
|
||||
|
||||
## site.conf
|
||||
### site.conf
|
||||
|
||||
```conf
|
||||
title = "kewt"
|
||||
@@ -86,6 +127,7 @@ generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
enable_header_links = true
|
||||
custom_admonitions = ""
|
||||
```
|
||||
|
||||
- `title` site title
|
||||
@@ -112,14 +154,15 @@ enable_header_links = true
|
||||
- `feed_file` filename for the generated RSS feed (default: "rss.xml")
|
||||
- `posts_dir` directory name containing posts (e.g., "posts"). Enables reverse-chronological sorting, title headings in indexes, and automatic backlinks.
|
||||
- `enable_header_links` turns markdown section headings into clickable anchor links (default: true)
|
||||
- `custom_admonitions` comma separated list of custom admonitions
|
||||
|
||||
## Ignores
|
||||
### Ignores
|
||||
|
||||
- `.kewtignore`: Files/directories to ignore. If empty, the whole directory gets ignored
|
||||
- `.kewthide`: Files/directories to hide from navigation but still process. Same empty rules as with ignore
|
||||
- `.kewtpreserve`: Files/directories to copy but not convert markdown to html. Same empty rules again
|
||||
|
||||
## Embeds
|
||||
### Embeds
|
||||
|
||||
- `\![link]`:
|
||||
- local image/audio/video files are embedded as media tags
|
||||
@@ -128,10 +171,30 @@ enable_header_links = true
|
||||
- other global links are embedded as `<iframe>`
|
||||
- `\` works the same, with `alt` used for images
|
||||
- `\!![]` and `\!` force inline local file contents
|
||||
- **Typed Embeds**: Force specific output regardless of extension:
|
||||
- `\!i[link]` or `\!i[alt](link)`: **I**mage
|
||||
- `\!v[link]`: **V**ideo
|
||||
- `\!a[link]`: **A**udio
|
||||
- `\!f[link]`: I**f**rame
|
||||
- `\!e[link]`: Inline/**e**mbed text/code file directly
|
||||
|
||||
## Credits
|
||||
### Frontmatter
|
||||
|
||||
- _kew_ css style adapted from _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||
You can set metadata for a page using a `site.conf`-style frontmatter block at the very top of `.md` files:
|
||||
|
||||
```conf
|
||||
---
|
||||
title = "Custom Page Title"
|
||||
date = "2026-03-23 11:32"
|
||||
draft = false
|
||||
---
|
||||
```
|
||||
|
||||
- `title`: Overrides the page title, post name in index links, and RSS `<title>`.
|
||||
- `date`: Overrides the post date and time. Supports `YYYY-MM-DD` and `YYYY-MM-DD HH:MM` (or `HH-MM`).
|
||||
- `draft`: If `true`, the file is excluded from HTML generation
|
||||
|
||||
***
|
||||
|
||||
>[!WARNING]
|
||||
>The base that all of this is built upon was coded at night, while sleepy and a bit sick, and after walking for about 4 hours around a forest, so...
|
||||
|
||||
@@ -19,3 +19,4 @@ error_page = "not_found.html"
|
||||
versioning = true
|
||||
enable_header_links = true
|
||||
base_url = "https://kewt.krzak.org"
|
||||
custom_admonitions = ""
|
||||
|
||||
@@ -273,4 +273,13 @@ hr {
|
||||
margin-right: 20px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.task-list-item-checkbox {
|
||||
margin: 0 0.2em 0.25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ exit $?
|
||||
#==PAYLOAD==
|
||||
EOF
|
||||
|
||||
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "standalone")
|
||||
VERSION=$(git describe --tags 2>/dev/null || echo "standalone")
|
||||
tmpbuild=$(mktemp -d)
|
||||
cp -r "$REPO_ROOT/kewt.sh" "$REPO_ROOT/markdown.sh" "$REPO_ROOT/awk" "$REPO_ROOT/styles" "$tmpbuild/"
|
||||
sed -e "s/kewt version git/kewt version $VERSION/" "$tmpbuild/kewt.sh" > "$tmpbuild/kewt.sh.tmp" && mv "$tmpbuild/kewt.sh.tmp" "$tmpbuild/kewt.sh"
|
||||
|
||||
Reference in New Issue
Block a user