8 Commits

Author SHA1 Message Date
77c0b29b4c fix gitea actions missing go
All checks were successful
Lint / shellcheck (push) Successful in 16s
Release Standalone Builder / build (release) Successful in 1m8s
2026-03-11 08:01:26 +01:00
9ae965662c Actions! Work!
Some checks failed
Lint / shellcheck (push) Successful in 48s
Release Standalone Builder / build (release) Failing after 6s
2026-03-11 07:51:31 +01:00
9ced2af562 Actions
Some checks failed
Lint / shellcheck (push) Has been cancelled
2026-03-11 07:42:45 +01:00
f407b1c4af extra navbar links 2026-03-09 20:05:42 +01:00
bee12ce8c1 Update markdown_inline.awk 2026-03-09 10:40:30 +01:00
696dc1c142 Fix building from ./ 2026-03-08 19:28:06 +01:00
c2416e731b Turn off antialiasing in the default style 2026-03-08 17:54:10 +01:00
01fd55001a html tag fix 2026-03-08 17:44:28 +01:00
14 changed files with 265 additions and 17 deletions

16
.gitea/workflows/lint.yml Normal file
View File

@@ -0,0 +1,16 @@
name: Lint
on:
push:
branches: [ main, master ]
pull_request:
jobs:
shellcheck:
runs-on: local
steps:
- uses: actions/checkout@v4
- name: Install Shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Run Shellcheck
run: shellcheck kewt.sh markdown.sh tools/build-standalone.sh || true

View File

@@ -0,0 +1,29 @@
name: Release Standalone Builder
on:
release:
types: [published]
jobs:
build:
runs-on: local
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build standalone executable
run: |
chmod +x tools/build-standalone.sh
./tools/build-standalone.sh
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Upload Release Asset
uses: https://gitea.com/actions/release-action@main
with:
files: |-
kewt
api_key: '${{secrets.GITEA_TOKEN}}'

16
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Lint
on:
push:
branches: [ main, master ]
pull_request:
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Run Shellcheck
run: shellcheck kewt.sh markdown.sh tools/build-standalone.sh || true

25
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Release Standalone Builder
on:
release:
types: [published]
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build standalone executable
run: |
chmod +x tools/build-standalone.sh
./tools/build-standalone.sh
- name: Upload Release Asset
uses: softprops/action-gh-release@v2
with:
files: kewt

1
.gitignore vendored
View File

@@ -1 +1,2 @@
out/ out/
kewt

View File

@@ -41,6 +41,8 @@ flatten = false
order = "" order = ""
home_name = "Home" home_name = "Home"
show_home_in_nav = true show_home_in_nav = true
nav_links = ""
nav_extra = ""
footer = "made with <a href="https://kewt.krzak.org">kewt</a>" footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
logo = "" logo = ""
display_logo = false display_logo = false
@@ -57,6 +59,8 @@ favicon = ""
- `order` comma separated file/directory name list to order the sidebar (alphabetical by default) - `order` comma separated file/directory name list to order the sidebar (alphabetical by default)
- `home_name` text for the home link in navigation (default: "Home") - `home_name` text for the home link in navigation (default: "Home")
- `show_home_in_nav` show home link in navigation (default: true) - `show_home_in_nav` show home link in navigation (default: true)
- `nav_links` comma separated extra nav links, as bare URLs or Markdown links like `[Label](https://example.com)`
- `nav_extra` raw HTML appended inside the `<nav>` after the generated link list
- `footer` footer html/text shown at the bottom of pages - `footer` footer html/text shown at the bottom of pages
- `logo` logo image path (used in header if enabled) - `logo` logo image path (used in header if enabled)
- `display_logo` show logo in header - `display_logo` show logo in header

View File

@@ -2,11 +2,36 @@ BEGIN {
in_pre = 0 in_pre = 0
} }
function mask_html_tags(s, out, rest, start, len, tag, token) {
out = ""
rest = s
html_tag_count = 0
while (match(rest, /<[^>]+>/)) {
out = out substr(rest, 1, RSTART - 1)
start = RSTART
len = RLENGTH
tag = substr(rest, start, len)
html_tag_count++
html_tag_token[html_tag_count] = "\034HT" html_tag_count "\034"
html_tag_value[html_tag_count] = tag
out = out html_tag_token[html_tag_count]
rest = substr(rest, start + len)
}
return out rest
}
function restore_html_tags(s, i) {
for (i = 1; i <= html_tag_count; i++) {
gsub(html_tag_token[i], html_tag_value[i], s)
}
return s
}
{ {
if ($0 ~ /<pre>/) { if ($0 ~ /<pre>/) {
in_pre = 1 in_pre = 1
} }
if (in_pre) { if (in_pre) {
print print
if ($0 ~ /<\/pre>/) { if ($0 ~ /<\/pre>/) {
@@ -114,6 +139,8 @@ BEGIN {
line = substr(line, 1, start - 1) "<span style=\"font-family: sans-serif;\">" content "</span>" substr(line, start + len) line = substr(line, 1, start - 1) "<span style=\"font-family: sans-serif;\">" content "</span>" substr(line, start + len)
} }
line = mask_html_tags(line)
# Bold, Italic, Strikethrough (BRE-like logic in AWK) # Bold, Italic, Strikethrough (BRE-like logic in AWK)
# Strong Bold ** # Strong Bold **
while (match(line, /\*\*[^*]+\*\*/)) { while (match(line, /\*\*[^*]+\*\*/)) {
@@ -152,11 +179,13 @@ BEGIN {
line = substr(line, 1, start - 1) repl substr(line, start + len) line = substr(line, 1, start - 1) repl substr(line, start + len)
} }
line = restore_html_tags(line)
# special characters # special characters
if (line !~ /&[A-Za-z0-9#]+;/) { if (line !~ /&[A-Za-z0-9#]+;/) {
gsub(/&/, "&amp;", line) gsub(/&/, "&amp;", line)
} }
p = 1 p = 1
while (match(substr(line, p), /</)) { while (match(substr(line, p), /</)) {
start = p + RSTART - 1 start = p + RSTART - 1

96
kewt.sh
View File

@@ -20,7 +20,7 @@ Options:
EOF EOF
} }
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
awk_dir="$script_dir/awk" awk_dir="$script_dir/awk"
ensure_root_defaults() { ensure_root_defaults() {
@@ -34,6 +34,8 @@ flatten = false
order = "" order = ""
home_name = "Home" home_name = "Home"
show_home_in_nav = true show_home_in_nav = true
nav_links = ""
nav_extra = ""
footer = "made with <a href="https://kewt.krzak.org">kewt</a>" footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
logo = "" logo = ""
display_logo = false display_logo = false
@@ -91,10 +93,7 @@ create_new_site() {
exit 0 exit 0
} }
generate_nav() {
dinfo=$(find "$1" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -print | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
find "$1" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -name "*.md" -print | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
}
src="" src=""
out="" out=""
@@ -131,9 +130,9 @@ while [ $# -gt 0 ]; do
*) *)
positional_count=$((positional_count + 1)) positional_count=$((positional_count + 1))
if [ "$positional_count" -eq 1 ]; then if [ "$positional_count" -eq 1 ]; then
[ -z "$src" ] && src="$1" || die "Source already set (use either positional or --from)." if [ -z "$src" ]; then src="$1"; else die "Source already set (use either positional or --from)."; fi
elif [ "$positional_count" -eq 2 ]; then elif [ "$positional_count" -eq 2 ]; then
[ -z "$out" ] && out="$1" || die "Output already set (use either positional or --to)." if [ -z "$out" ]; then out="$1"; else die "Output already set (use either positional or --to)."; fi
else else
die "Too many positional arguments." die "Too many positional arguments."
fi fi
@@ -154,7 +153,7 @@ out="${out%/}"
[ -d "$src" ] || die "Source directory '$src' does not exist." [ -d "$src" ] || die "Source directory '$src' does not exist."
IGNORE_ARGS="-name '.kewtignore' -o -name '.*'" IGNORE_ARGS="-name '.kewtignore' -o -path '$src/.*'"
if [ -f "$src/.kewtignore" ]; then if [ -f "$src/.kewtignore" ]; then
while IFS= read -r line || [ -n "$line" ]; do while IFS= read -r line || [ -n "$line" ]; do
@@ -184,7 +183,7 @@ while read -r ki; do
done < "/tmp/kewt_ignore_$$" done < "/tmp/kewt_ignore_$$"
rm -f "/tmp/kewt_ignore_$$" rm -f "/tmp/kewt_ignore_$$"
HIDE_ARGS="-name '.kewtignore' -o -name '.kewthide' -o -name '.kewtpreserve' -o -name '.*'" HIDE_ARGS="-name '.kewtignore' -o -name '.kewthide' -o -name '.kewtpreserve' -o -path '$src/.*'"
if [ -f "$src/.kewthide" ]; then if [ -f "$src/.kewthide" ]; then
while IFS= read -r line || [ -n "$line" ]; do while IFS= read -r line || [ -n "$line" ]; do
@@ -258,6 +257,9 @@ flatten="false"
order="" order=""
home_name="Home" home_name="Home"
show_home_in_nav="true" show_home_in_nav="true"
nav_links=""
nav_extra=""
footer="made with <a href=\"https://kewt.krzak.org\">kewt</a>"
logo="" logo=""
display_logo="false" display_logo="false"
display_title="true" display_title="true"
@@ -292,6 +294,8 @@ load_config() {
order) order="$val" ;; order) order="$val" ;;
home_name) home_name="$val" ;; home_name) home_name="$val" ;;
show_home_in_nav) show_home_in_nav="$val" ;; show_home_in_nav) show_home_in_nav="$val" ;;
nav_links) nav_links="$val" ;;
nav_extra) nav_extra="$val" ;;
footer) footer="$val" ;; footer) footer="$val" ;;
logo) logo="$val" ;; logo) logo="$val" ;;
display_logo) display_logo="$val" ;; display_logo) display_logo="$val" ;;
@@ -305,6 +309,65 @@ load_config() {
load_config "./site.conf" load_config "./site.conf"
load_config "$src/site.conf" load_config "$src/site.conf"
escape_html_text() {
printf '%s' "$1" | sed \
-e 's/&/\&amp;/g' \
-e 's/</\&lt;/g' \
-e 's/>/\&gt;/g'
}
escape_html_attr() {
printf '%s' "$1" | sed \
-e 's/&/\&amp;/g' \
-e 's/"/\&quot;/g' \
-e 's/</\&lt;/g' \
-e 's/>/\&gt;/g'
}
nav_links_html() {
[ -n "$nav_links" ] || return
old_ifs=$IFS
set -f
IFS=','
# shellcheck disable=SC2086
set -- $nav_links
IFS=$old_ifs
set +f
[ $# -gt 0 ] || return
printf '<ul class="nav-extra-links">\n'
for raw_link in "$@"; do
link=$(printf '%s' "$raw_link" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
[ -n "$link" ] || continue
case "$link" in
\[*\]\(*\))
label=${link#\[}
label=${label%%\]*}
link_url=${link#*](}
link_url=${link_url%)}
;;
*)
link_url=$link
label=$(printf '%s' "$link" | sed \
-e 's|^[A-Za-z][A-Za-z0-9+.-]*://||' \
-e 's|/$||')
[ -n "$label" ] || label="$link"
;;
esac
[ -n "$link_url" ] || continue
[ -n "$label" ] || label="$link_url"
link_attr=$(escape_html_attr "$link_url")
label_text=$(escape_html_text "$label")
printf '<li><a href="%s">%s</a></li>\n' "$link_attr" "$label_text"
done
printf '</ul>'
}
template="$src/template.html" template="$src/template.html"
[ -f "$template" ] || template="./template.html" [ -f "$template" ] || template="./template.html"
[ -f "$template" ] || die "Template '$template' not found." [ -f "$template" ] || die "Template '$template' not found."
@@ -313,6 +376,15 @@ template="$src/template.html"
mkdir -p "$out" mkdir -p "$out"
nav=$(generate_nav "$src") nav=$(generate_nav "$src")
extra_links=$(nav_links_html)
if [ -n "$extra_links" ]; then
nav="$nav
$extra_links"
fi
if [ -n "$nav_extra" ]; then
nav="$nav
$nav_extra"
fi
find_closest() { find_closest() {
target="$1" target="$1"
@@ -344,7 +416,7 @@ render_markdown() {
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")") closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")") [ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
if [ -n "$closest_style_src" ]; then if [ -n "$closest_style_src" ]; then
style_rel_to_src="${closest_style_src#$src/}" style_rel_to_src="${closest_style_src#"$src"/}"
case "$closest_style_src" in case "$closest_style_src" in
"$src/styles.css") style_rel_to_src="styles.css" ;; "$src/styles.css") style_rel_to_src="styles.css" ;;
"$src/style.css") style_rel_to_src="style.css" ;; "$src/style.css") style_rel_to_src="style.css" ;;
@@ -392,7 +464,7 @@ render_markdown() {
echo "Building site from '$src' to '$out'..." echo "Building site from '$src' to '$out'..."
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
rel_dir="${dir#$src}" rel_dir="${dir#"$src"}"
rel_dir="${rel_dir#/}" rel_dir="${rel_dir#/}"
[ -z "$rel_dir" ] && rel_dir="." [ -z "$rel_dir" ] && rel_dir="."
out_dir="$out/$rel_dir" out_dir="$out/$rel_dir"
@@ -444,7 +516,7 @@ if [ ! -f "$out/styles.css" ] && [ -f "$script_dir/styles/$style.css" ]; then
fi fi
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
rel_path="${file#$src}" rel_path="${file#"$src"}"
rel_path="${rel_path#/}" rel_path="${rel_path#/}"
dir_rel=$(dirname "$rel_path") dir_rel=$(dirname "$rel_path")
out_dir="$out/$dir_rel" out_dir="$out/$dir_rel"

View File

@@ -1,16 +1,18 @@
#!/bin/sh #!/bin/sh
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
awk_dir="$script_dir/awk" awk_dir="$script_dir/awk"
sed_inplace() { sed_inplace() {
script="$1" script="$1"
file="$2" file="$2"
tmp="${file}.tmp.$$" tmp="${file}.tmp.$$"
sed "$script" "$file" > "$tmp" && mv "$tmp" "$file" || { if sed "$script" "$file" > "$tmp" && mv "$tmp" "$file"; then
return 0
else
rm -f "$tmp" rm -f "$tmp"
return 1 return 1
} fi
} }
temp_file="/tmp/markdown.$$.md" temp_file="/tmp/markdown.$$.md"

View File

@@ -10,3 +10,7 @@ display_title = true
logo_as_favicon = true logo_as_favicon = true
favicon = "" favicon = ""
order = "" order = ""
home_name = "Home"
show_home_in_nav = true
nav_links = ""
nav_extra = ""

View File

@@ -15,3 +15,5 @@ Sed dictum tortor at interdum dignissim. Nunc hendrerit sollicitudin elementum.
Cras vitae sapien egestas, blandit libero et, volutpat augue. Ut augue quam, sollicitudin quis libero laoreet, bibendum imperdiet massa. Duis sed venenatis risus. Praesent a est mollis, viverra erat quis, faucibus elit. Donec at sagittis est, non posuere nisi. Integer posuere pharetra dui in aliquam. Morbi vehicula eros in hendrerit aliquam. Duis in turpis vel mauris mattis convallis in id tortor. Cras et aliquam augue. Cras vitae sapien egestas, blandit libero et, volutpat augue. Ut augue quam, sollicitudin quis libero laoreet, bibendum imperdiet massa. Duis sed venenatis risus. Praesent a est mollis, viverra erat quis, faucibus elit. Donec at sagittis est, non posuere nisi. Integer posuere pharetra dui in aliquam. Morbi vehicula eros in hendrerit aliquam. Duis in turpis vel mauris mattis convallis in id tortor. Cras et aliquam augue.
Cras quis consectetur dolor, a sodales tortor. Vestibulum aliquam lacinia metus, sed viverra erat egestas in. Morbi interdum sapien sed bibendum maximus. Aenean accumsan pharetra libero dapibus aliquam. Etiam sodales purus posuere gravida ullamcorper. Vestibulum tincidunt, nibh a pulvinar aliquet, leo tortor pulvinar diam, ut viverra nunc elit bibendum nulla. Praesent vel pulvinar erat, eu efficitur magna. Mauris at consequat purus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce in dui quis nisi elementum aliquam. Proin eget justo sed est commodo accumsan. Suspendisse a feugiat tellus, eget gravida tellus. Cras quis consectetur dolor, a sodales tortor. Vestibulum aliquam lacinia metus, sed viverra erat egestas in. Morbi interdum sapien sed bibendum maximus. Aenean accumsan pharetra libero dapibus aliquam. Etiam sodales purus posuere gravida ullamcorper. Vestibulum tincidunt, nibh a pulvinar aliquet, leo tortor pulvinar diam, ut viverra nunc elit bibendum nulla. Praesent vel pulvinar erat, eu efficitur magna. Mauris at consequat purus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce in dui quis nisi elementum aliquam. Proin eget justo sed est commodo accumsan. Suspendisse a feugiat tellus, eget gravida tellus.
![https://www.youtube.com/embed/NvQD9E5Cq8A]

View File

@@ -41,6 +41,8 @@ flatten = false
order = "" order = ""
home_name = "Home" home_name = "Home"
show_home_in_nav = true show_home_in_nav = true
nav_links = ""
nav_extra = ""
footer = "made with <a href="https://kewt.krzak.org">kewt</a>" footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
logo = "" logo = ""
display_logo = false display_logo = false
@@ -57,6 +59,8 @@ favicon = ""
- `order` comma separated file/directory name list to order the sidebar (alphabetical by default) - `order` comma separated file/directory name list to order the sidebar (alphabetical by default)
- `home_name` text for the home link in navigation (default: "Home") - `home_name` text for the home link in navigation (default: "Home")
- `show_home_in_nav` show home link in navigation (default: true) - `show_home_in_nav` show home link in navigation (default: true)
- `nav_links` comma separated extra nav links, as bare URLs or Markdown links like `[Label](https://example.com)`
- `nav_extra` raw HTML appended inside the `<nav>` after the generated link list
- `footer` footer html/text shown at the bottom of pages - `footer` footer html/text shown at the bottom of pages
- `logo` logo image path (used in header if enabled) - `logo` logo image path (used in header if enabled)
- `display_logo` show logo in header - `display_logo` show logo in header

View File

@@ -208,3 +208,15 @@ footer {
margin-left: 240px; margin-left: 240px;
margin-top: 0px; margin-top: 0px;
} }
img {
image-rendering: auto;
image-rendering: crisp-edges;
image-rendering: pixelated;
image-rendering: -webkit-optimize-contrast;
}
footer img {
display: inline-block;
vertical-align: top;
}

32
tools/build-standalone.sh Normal file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
set -e
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
if [ ! -f "$REPO_ROOT/kewt.sh" ]; then
echo "kewt.sh not found. Run from the repository root or tools/."
exit 1
fi
OUT_FILE="$REPO_ROOT/kewt"
cat << 'EOF' > "$OUT_FILE"
#!/bin/sh
tmpdir=$(mktemp -d "/tmp/kewt.XXXXXX")
trap 'rm -rf "$tmpdir"' EXIT HUP INT TERM
# Extract payload
sed '1,/^#==PAYLOAD==$/d' "$0" | tar -xz -C "$tmpdir"
# Pass control to the extracted script
exec "$tmpdir/kewt.sh" "$@"
#==PAYLOAD==
EOF
tar -cz -C "$REPO_ROOT" kewt.sh markdown.sh awk styles >> "$OUT_FILE"
chmod +x "$OUT_FILE"
echo "Generated standalone executable at $OUT_FILE"