From e3cc1c1688320f502bd7e45277a433023b1d88c2 Mon Sep 17 00:00:00 2001
From: "N0\\A"
Date: Sun, 8 Mar 2026 14:18:45 +0100
Subject: [PATCH] Some fixes, .kewtignore, .kewtpreserve and .kewthide
---
README.md | 10 ++++
awk/generate_sidebar.awk | 5 +-
awk/headers.awk | 74 +++++++++++++----------
awk/paragraphs.awk | 2 +-
kewt.sh | 126 +++++++++++++++++++++++++++++++++++----
markdown.sh | 12 ++--
site/index.md | 10 ++++
7 files changed, 189 insertions(+), 50 deletions(-)
diff --git a/README.md b/README.md
index ccdb056..2b1ebdd 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,8 @@ dir_indexes = true
single_file_index = true
flatten = false
order = ""
+home_name = "Home"
+show_home_in_nav = true
footer = "made with kewt "
logo = ""
display_logo = false
@@ -53,6 +55,8 @@ favicon = ""
- `single_file_index` if a directory has one markdown file and no `index.md`, use that file as `index.html`
- `flatten` flatten sidebar directory levels
- `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")
+- `show_home_in_nav` show home link in navigation (default: true)
- `footer` footer html/text shown at the bottom of pages
- `logo` logo image path (used in header if enabled)
- `display_logo` show logo in header
@@ -60,6 +64,12 @@ favicon = ""
- `logo_as_favicon` use `logo` as favicon
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
+## 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
- `\![link]`:
diff --git a/awk/generate_sidebar.awk b/awk/generate_sidebar.awk
index 50ab4b3..ee9bead 100644
--- a/awk/generate_sidebar.awk
+++ b/awk/generate_sidebar.awk
@@ -80,8 +80,9 @@ END {
}
print ""
- if ("index.md" in all_paths) {
- print "Home "
+ if (show_home_in_nav == "true" && "index.md" in all_paths) {
+ if (home_name == "") home_name = "Home"
+ print "" home_name " "
}
depth = 0
diff --git a/awk/headers.awk b/awk/headers.awk
index 3968859..ceba83d 100644
--- a/awk/headers.awk
+++ b/awk/headers.awk
@@ -3,10 +3,27 @@ function strip_markdown(s) {
gsub(/[*_`~]/, "", s)
gsub(/[\[\]]/, "", s)
gsub(/\([^\)]*\)/, "", s)
- sub(/^[[:space:]]*/, "", s)
- sub(/[[:space:]]*$/, "", s)
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
+ gsub(/[[:space:]]+/, "-", s)
return s
}
+function print_header(line) {
+ if (line ~ /^# /) {
+ sub(/^# /, "", line); print "" line " "
+ } else if (line ~ /^## /) {
+ sub(/^## /, "", line); print "" line " "
+ } else if (line ~ /^### /) {
+ sub(/^### /, "", line); print "" line " "
+ } else if (line ~ /^#### /) {
+ sub(/^#### /, "", line); print "" line " "
+ } else if (line ~ /^##### /) {
+ sub(/^##### /, "", line); print "" line " "
+ } else if (line ~ /^###### /) {
+ sub(/^###### /, "", line); print "" line " "
+ } else {
+ print line
+ }
+}
BEGIN {
has_prev = 0
in_pre = 0
@@ -14,7 +31,7 @@ BEGIN {
{
if ($0 ~ /^ /) {
in_pre = 1
- if (has_prev && prev != "") { print prev; has_prev = 0 }
+ if (has_prev && prev != "") { print_header(prev); has_prev = 0 }
print
next
}
@@ -24,30 +41,32 @@ BEGIN {
next
}
- if ($0 ~ /^=+$/ && has_prev && prev != "" && prev !~ /^<[a-z]/) {
- print "" prev " "
- has_prev = 0
- } else if ($0 ~ /^-+$/ && has_prev && prev != "" && prev !~ /^<[a-z]/) {
- print "" prev " "
+ if ($0 ~ /^=+$/) {
+ if (has_prev && prev != "" && prev !~ /^<[a-z]/) {
+ print "" prev " "
+ has_prev = 0
+ } else {
+ if (has_prev) print_header(prev)
+ print $0
+ has_prev = 0
+ }
+ } else if ($0 ~ /^-+$/) {
+ if (has_prev && prev != "" && prev !~ /^<[a-z]/) {
+ print "" prev " "
+ has_prev = 0
+ } else {
+ if (has_prev) print_header(prev)
+ if (length($0) >= 3) print " "
+ else print $0
+ has_prev = 0
+ }
+ } else if ($0 ~ /^[*_]+$/ && length($0) >= 3) {
+ if (has_prev) print_header(prev)
+ print " "
has_prev = 0
} else {
if (has_prev) {
- line = prev
- if (line ~ /^# /) {
- sub(/^# /, "", line); print "" line " "
- } else if (line ~ /^## /) {
- sub(/^## /, "", line); print "" line " "
- } else if (line ~ /^### /) {
- sub(/^### /, "", line); print "" line " "
- } else if (line ~ /^#### /) {
- sub(/^#### /, "", line); print "" line " "
- } else if (line ~ /^##### /) {
- sub(/^##### /, "", line); print "" line " "
- } else if (line ~ /^###### /) {
- sub(/^###### /, "", line); print "" line " "
- } else {
- print prev
- }
+ print_header(prev)
}
prev = $0
has_prev = 1
@@ -55,11 +74,6 @@ BEGIN {
}
END {
if (has_prev) {
- line = prev
- if (line ~ /^# /) {
- sub(/^# /, "", line); print "" line " "
- } else {
- print prev
- }
+ print_header(prev)
}
}
diff --git a/awk/paragraphs.awk b/awk/paragraphs.awk
index ad4e2e8..f5b0ba2 100644
--- a/awk/paragraphs.awk
+++ b/awk/paragraphs.awk
@@ -13,7 +13,7 @@ BEGIN {
next
}
- if ($0 ~ /^<\/?(div|table|p|[ou]l|h[1-6]|[bh]r|blockquote|li)/) {
+ if ($0 ~ /^<\/?(div|table|p|[ou]l|h[1-6]|[bh]r|blockquote|li|hr)/) {
if (in_p) {
print "
"
in_p = 0
diff --git a/kewt.sh b/kewt.sh
index d4381ed..e803e74 100755
--- a/kewt.sh
+++ b/kewt.sh
@@ -32,6 +32,8 @@ dir_indexes = true
single_file_index = true
flatten = false
order = ""
+home_name = "Home"
+show_home_in_nav = true
footer = "made with kewt "
logo = ""
display_logo = false
@@ -147,8 +149,100 @@ ensure_root_defaults
[ -z "$src" ] && src="site"
[ -z "$out" ] && out="out"
+src="${src%/}"
+out="${out%/}"
+
[ -d "$src" ] || die "Source directory '$src' does not exist."
+IGNORE_ARGS="-name .git -o -name .kewtignore -o -name .*"
+
+if [ -f "$src/.kewtignore" ]; then
+ while IFS= read -r line || [ -n "$line" ]; do
+ case "$line" in
+ ''|'#'*) continue ;;
+ esac
+ pattern=$(echo "$line" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
+ [ -z "$pattern" ] && continue
+
+ pattern_clean="${pattern#/}"
+ pattern_clean="${pattern_clean%/}"
+
+ if echo "$pattern" | grep -q "/"; then
+ IGNORE_ARGS="$IGNORE_ARGS -o -path '$src/$pattern_clean'"
+ else
+ IGNORE_ARGS="$IGNORE_ARGS -o -name '$pattern_clean'"
+ fi
+ done < "$src/.kewtignore"
+fi
+
+for ki in $(find "$src" -name .kewtignore); do
+ d="${ki%/.kewtignore}"
+ if [ "$d" != "$src" ] && [ "$d" != "." ]; then
+ IGNORE_ARGS="$IGNORE_ARGS -o -path '$d'"
+ fi
+done
+
+HIDE_ARGS="-name .git -o -name .kewtignore -o -name .kewthide -o -name .kewtpreserve -o -name .*"
+
+if [ -f "$src/.kewthide" ]; then
+ while IFS= read -r line || [ -n "$line" ]; do
+ case "$line" in
+ ''|'#'*) continue ;;
+ esac
+ pattern=$(echo "$line" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
+ [ -z "$pattern" ] && continue
+
+ pattern_clean="${pattern#/}"
+ pattern_clean="${pattern_clean%/}"
+
+ if echo "$pattern" | grep -q "/"; then
+ HIDE_ARGS="$HIDE_ARGS -o -path '$src/$pattern_clean' -o -path '$src/$pattern_clean/*'"
+ else
+ HIDE_ARGS="$HIDE_ARGS -o -name '$pattern_clean'"
+ fi
+ done < "$src/.kewthide"
+fi
+
+for kh in $(find "$src" -name .kewthide); do
+ d="${kh%/.kewthide}"
+ if [ "$d" != "$src" ] && [ "$d" != "." ]; then
+ HIDE_ARGS="$HIDE_ARGS -o -path '$d' -o -path '$d/*'"
+ fi
+done
+
+PRESERVE_ARGS="-false"
+
+if [ -f "$src/.kewtpreserve" ]; then
+ while IFS= read -r line || [ -n "$line" ]; do
+ case "$line" in
+ ''|'#'*) continue ;;
+ esac
+ pattern=$(echo "$line" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
+ [ -z "$pattern" ] && continue
+
+ pattern_clean="${pattern#/}"
+ pattern_clean="${pattern_clean%/}"
+
+ if echo "$pattern" | grep -q "/"; then
+ PRESERVE_ARGS="$PRESERVE_ARGS -o -path '$src/$pattern_clean' -o -path '$src/$pattern_clean/*'"
+ else
+ PRESERVE_ARGS="$PRESERVE_ARGS -o -name '$pattern_clean'"
+ fi
+ done < "$src/.kewtpreserve"
+fi
+
+for kp in $(find "$src" -name .kewtpreserve); do
+ d="${kp%/.kewtpreserve}"
+ if [ "$d" != "$src" ] && [ "$d" != "." ]; then
+ PRESERVE_ARGS="$PRESERVE_ARGS -o -path '$d' -o -path '$d/*'"
+ fi
+done
+
+generate_nav() {
+ dinfo=$(eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -print" | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
+ eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print" | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v home_name="$home_name" -v show_home_in_nav="$show_home_in_nav" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
+}
+
title="kewt"
style="kewt"
footer="made with kewt "
@@ -156,6 +250,8 @@ dir_indexes="true"
single_file_index="true"
flatten="false"
order=""
+home_name="Home"
+show_home_in_nav="true"
logo=""
display_logo="false"
display_title="true"
@@ -188,6 +284,8 @@ load_config() {
single_file_index) single_file_index="$val" ;;
flatten) flatten="$val" ;;
order) order="$val" ;;
+ home_name) home_name="$val" ;;
+ show_home_in_nav) show_home_in_nav="$val" ;;
footer) footer="$val" ;;
logo) logo="$val" ;;
display_logo) display_logo="$val" ;;
@@ -282,14 +380,15 @@ render_markdown() {
head_extra=" "
fi
- MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="styles/$style.css" sh "$script_dir/markdown.sh" "$file" | awk -v title="$title" -v nav="$nav" -v footer="$footer" -v style_path="$style_path" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
+ MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$file" | awk -v title="$title" -v nav="$nav" -v footer="$footer" -v style_path="$style_path" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
}
echo "Building site from '$src' to '$out'..."
-find "$src" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -type d -print | sort | while read -r dir; do
- rel_dir="${dir#$src/}"
- [ "$dir" = "$src" ] && rel_dir="."
+eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
+ rel_dir="${dir#$src}"
+ rel_dir="${rel_dir#/}"
+ [ -z "$rel_dir" ] && rel_dir="."
out_dir="$out/$rel_dir"
mkdir -p "$out_dir"
@@ -334,12 +433,13 @@ find "$src" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -type d -print |
fi
done
-if [ ! -f "$out/styles.css" ] && [ -f "styles/$style.css" ]; then
- copy_style_with_resolved_vars "styles/$style.css" "$out/styles.css"
+if [ ! -f "$out/styles.css" ] && [ -f "$script_dir/styles/$style.css" ]; then
+ copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
fi
-find "$src" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -type f -print | sort | while IFS= read -r file; do
- rel_path="${file#$src/}"
+eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
+ rel_path="${file#$src}"
+ rel_path="${rel_path#/}"
dir_rel=$(dirname "$rel_path")
out_dir="$out/$dir_rel"
@@ -347,12 +447,17 @@ find "$src" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -type f -print |
template.html|site.conf|style.css|styles.css) continue ;;
esac
- if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ ! -f "$(dirname "$file")/index.md" ]; then
+ is_preserved=0
+ if [ -n "$(eval "find \"$file\" \( $PRESERVE_ARGS \) -print")" ]; then
+ is_preserved=1
+ fi
+
+ if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ]; then
md_count=$(find "$(dirname "$file")" ! -name "$(basename "$(dirname "$file")")" -prune -name "*.md" | wc -l)
[ "$md_count" -eq 1 ] && continue
fi
- if [ "${file%.md}" != "$file" ]; then
+ if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
out_file="$out/${rel_path%.md}.html"
render_markdown "$file" > "$out_file"
else
@@ -360,4 +465,5 @@ find "$src" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -type f -print |
fi
done
+
echo "Build complete."
diff --git a/markdown.sh b/markdown.sh
index 048eb14..8d7bf61 100755
--- a/markdown.sh
+++ b/markdown.sh
@@ -13,15 +13,17 @@ sed_inplace() {
}
}
-temp_file="/tmp/markdown.$$"
+temp_file="/tmp/markdown.$$.md"
cat "$@" > "$temp_file"
+trap 'rm -f "$temp_file" "$temp_file.tmp"' EXIT INT TERM
+
# Mask
awk -f "$awk_dir/mask_inline_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
awk -f "$awk_dir/mask_plain.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
# Reference links
-refs=$(cat "$@" | awk '/^\[[^\]]+\]: +/')
+refs=$(cat "$@" | awk '/^\[[^\]]+\]: */')
IFS='
'
for ref in $refs; do
@@ -33,7 +35,7 @@ for ref in $refs; do
sed_inplace "s|!\[$ref_id\]\[\]| |g" "$temp_file"
sed_inplace "s|\[$ref_id\]\[\]|$ref_id |g" "$temp_file"
done
-sed_inplace "/^\[[^\]]*\]: +/d" "$temp_file"
+sed_inplace "/^\[[^\]]*\]: */d" "$temp_file"
# Blocks
sed_inplace "s/^>!\[/> [!/g" "$temp_file"
@@ -50,10 +52,6 @@ awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_f
awk -f "$awk_dir/headers.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
awk -f "$awk_dir/lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
-sed_inplace "s/^\*\*\*+$/ /g" "$temp_file"
-sed_inplace "s/^---+$/ /g" "$temp_file"
-sed_inplace "s/^___+$/ /g" "$temp_file"
-
# Spacing
awk -f "$awk_dir/breaks.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
awk -f "$awk_dir/paragraphs.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
diff --git a/site/index.md b/site/index.md
index ded776a..51f2d1d 100644
--- a/site/index.md
+++ b/site/index.md
@@ -39,6 +39,8 @@ dir_indexes = true
single_file_index = true
flatten = false
order = ""
+home_name = "Home"
+show_home_in_nav = true
footer = "made with kewt "
logo = ""
display_logo = false
@@ -53,6 +55,8 @@ favicon = ""
- `single_file_index` if a directory has one markdown file and no `index.md`, use that file as `index.html`
- `flatten` flatten sidebar directory levels
- `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")
+- `show_home_in_nav` show home link in navigation (default: true)
- `footer` footer html/text shown at the bottom of pages
- `logo` logo image path (used in header if enabled)
- `display_logo` show logo in header
@@ -60,6 +64,12 @@ favicon = ""
- `logo_as_favicon` use `logo` as favicon
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
+## 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
- `\![link]`: