Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81d3caff45 | |||
| 100979d28a | |||
| d35ddcf487 | |||
| aac9198878 | |||
| 99e1f5dd24 | |||
| 3970c6eb47 |
@@ -10,7 +10,7 @@
|
||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||
|
||||
|
||||
## [Installation](https://kewt.krzak.org/#installation)
|
||||
## [Installation](https://kewt.krzak.org/docs/installation)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -4,6 +4,46 @@ function title_from_name(name) {
|
||||
return name
|
||||
}
|
||||
|
||||
function get_title(path, default_title, full_path, line, title, in_fm) {
|
||||
full_path = src "/" path
|
||||
if (path !~ /\.md$/) {
|
||||
full_path = full_path "/index.md"
|
||||
}
|
||||
|
||||
title = ""
|
||||
in_fm = 0
|
||||
while ((getline line < full_path) > 0) {
|
||||
if (line ~ /^---[[:space:]]*$/) {
|
||||
if (in_fm == 0) {
|
||||
in_fm = 1
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (in_fm) {
|
||||
if (line ~ /^[[:space:]]*title[[:space:]]*=/) {
|
||||
sub(/^[[:space:]]*title[[:space:]]*=[[:space:]]*/, "", line)
|
||||
if (line ~ /^".*"$/) {
|
||||
title = substr(line, 2, length(line) - 2)
|
||||
} else if (line ~ /^'.*'$/) {
|
||||
title = substr(line, 2, length(line) - 2)
|
||||
} else {
|
||||
title = line
|
||||
}
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
close(full_path)
|
||||
|
||||
if (title != "") return title
|
||||
return default_title
|
||||
}
|
||||
|
||||
|
||||
function compare_paths(p1, p2, parts1, parts2, n1, n2, i, name1, name2, lname1, lname2, w1, w2) {
|
||||
n1 = split(p1, parts1, "/")
|
||||
n2 = split(p2, parts2, "/")
|
||||
@@ -132,7 +172,7 @@ END {
|
||||
continue
|
||||
}
|
||||
|
||||
printf "<li><a href=\"/%sindex.html\">%s</a><ul>\n", dir_path, title_from_name(parts[i])
|
||||
printf "<li><a href=\"/%sindex.html\">%s</a><ul>\n", dir_path, get_title(this_d, title_from_name(parts[i]))
|
||||
opened_levels[++depth] = i
|
||||
}
|
||||
|
||||
@@ -145,7 +185,7 @@ END {
|
||||
if (parts[n] != "index.md" && !is_single) {
|
||||
path = "/" rel
|
||||
gsub(/\.md$/, ".html", path)
|
||||
printf "<li><a href=\"%s\">%s</a></li>\n", path, title_from_name(parts[n])
|
||||
printf "<li><a href=\"%s\">%s</a></li>\n", path, get_title(rel, title_from_name(parts[n]))
|
||||
}
|
||||
|
||||
prev_n = n
|
||||
|
||||
@@ -97,6 +97,23 @@ function read_file(path, out, line, rc) {
|
||||
return out
|
||||
}
|
||||
|
||||
function read_file_or_render_md(path, ext, cmd, content, line, rc) {
|
||||
content = ""
|
||||
if (ext == "md") {
|
||||
cmd = "sh \"" script_dir "/markdown.sh\" \"" path "\""
|
||||
while ((cmd | getline line) > 0) {
|
||||
content = content line "\n"
|
||||
}
|
||||
close(cmd)
|
||||
} else {
|
||||
while ((rc = getline line < path) > 0) {
|
||||
content = content line "\n"
|
||||
}
|
||||
close(path)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
function escape_html(s, t) {
|
||||
t = s
|
||||
gsub(/&/, "\\&", t)
|
||||
@@ -189,7 +206,8 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
||||
if (force_inline && !is_global_url(src)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
ext = ext_of(src)
|
||||
content = read_file_or_render_md(local_path, ext)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
@@ -217,7 +235,7 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
||||
if (is_inline_text_ext(ext)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
content = read_file_or_render_md(local_path, ext)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
@@ -238,7 +256,7 @@ function render_typed_embed(etype, src, alt, has_alt, local_path, content) {
|
||||
if (!is_global_url(src)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
content = read_file_or_render_md(local_path, ext_of(src))
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ function mask_plain(s, t) {
|
||||
gsub(/\$/, "\034P8\034", t)
|
||||
return t
|
||||
}
|
||||
BEGIN { in_plain = 0 }
|
||||
BEGIN { in_plain = 0; in_script_style = 0 }
|
||||
{
|
||||
line = $0
|
||||
out = ""
|
||||
@@ -48,5 +48,41 @@ BEGIN { in_plain = 0 }
|
||||
in_plain = 0
|
||||
}
|
||||
}
|
||||
print out
|
||||
tmp_line = out
|
||||
out2 = ""
|
||||
while (1) {
|
||||
if (!in_script_style) {
|
||||
pos_script = match(tolower(tmp_line), /<script([ >]|$)/)
|
||||
script_start = RSTART; script_len = RLENGTH
|
||||
pos_style = match(tolower(tmp_line), /<style([ >]|$)/)
|
||||
style_start = RSTART; style_len = RLENGTH
|
||||
|
||||
if (pos_script == 0 && pos_style == 0) {
|
||||
out2 = out2 tmp_line
|
||||
break
|
||||
}
|
||||
if (pos_script > 0 && (pos_style == 0 || pos_script < pos_style)) {
|
||||
out2 = out2 substr(tmp_line, 1, script_start + script_len - 1)
|
||||
tmp_line = substr(tmp_line, script_start + script_len)
|
||||
in_script_style = 1
|
||||
end_tag = "</script>"
|
||||
} else {
|
||||
out2 = out2 substr(tmp_line, 1, style_start + style_len - 1)
|
||||
tmp_line = substr(tmp_line, style_start + style_len)
|
||||
in_script_style = 1
|
||||
end_tag = "</style>"
|
||||
}
|
||||
} else {
|
||||
pos_end = match(tolower(tmp_line), end_tag)
|
||||
if (pos_end == 0) {
|
||||
out2 = out2 mask_plain(tmp_line)
|
||||
tmp_line = ""
|
||||
break
|
||||
}
|
||||
out2 = out2 mask_plain(substr(tmp_line, 1, RSTART - 1)) substr(tmp_line, RSTART, RLENGTH)
|
||||
tmp_line = substr(tmp_line, RSTART + RLENGTH)
|
||||
in_script_style = 0
|
||||
}
|
||||
}
|
||||
print out2
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ BEGIN {
|
||||
}
|
||||
|
||||
{
|
||||
if ($0 ~ /^<pre>/) in_pre = 1
|
||||
if ($0 ~ /<pre>/) in_pre = 1
|
||||
|
||||
if (in_pre) {
|
||||
if (in_p) { print "</p>"; in_p = 0 }
|
||||
@@ -13,7 +13,16 @@ BEGIN {
|
||||
next
|
||||
}
|
||||
|
||||
if ($0 ~ /^<\/?(div|table|p|[ou]l|h[1-6]|[bh]r|blockquote|li|hr|section|article|nav|aside|header|footer|dl|dt|dd)/) {
|
||||
if ($0 ~ /^<\/?(div|table|p|[ou]l|h[1-6]|[bh]r|blockquote|li|hr|section|article|nav|aside|header|footer|dl|dt|dd|script|style|iframe|details|summary|figure|figcaption|audio|video|picture)/) {
|
||||
if (in_p) {
|
||||
print "</p>"
|
||||
in_p = 0
|
||||
}
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
if ($0 ~ /^[\t ]*!([a-zA-Z])?\[[^\]]*\](\([^)]*\))?[\t ]*$/ || $0 ~ /^[\t ]*!!?\[[^\]]*\](\([^)]*\))?[\t ]*$/) {
|
||||
if (in_p) {
|
||||
print "</p>"
|
||||
in_p = 0
|
||||
|
||||
@@ -16,6 +16,9 @@ BEGIN {
|
||||
style_path = ENVIRON["AWK_STYLE_PATH"]
|
||||
head_extra = ENVIRON["AWK_HEAD_EXTRA"]
|
||||
header_brand = ENVIRON["AWK_HEADER_BRAND"]
|
||||
lang = ENVIRON["AWK_LANG"]
|
||||
version = ENVIRON["AWK_VERSION"]
|
||||
content_warning = ENVIRON["AWK_CONTENT_WARNING"]
|
||||
if (current_url != "") {
|
||||
nav = replace_all(nav, "href=\"" current_url "\"", "href=\"" current_url "\" class=\"current-page\"")
|
||||
}
|
||||
@@ -24,11 +27,13 @@ BEGIN {
|
||||
{
|
||||
line = $0
|
||||
line = replace_all(line, "{{TITLE}}", title)
|
||||
line = replace_all(line, "{{LANG}}", lang)
|
||||
line = replace_all(line, "{{NAV}}", nav)
|
||||
line = replace_all(line, "{{FOOTER}}", footer)
|
||||
line = replace_all(line, "{{CSS}}", style_path)
|
||||
line = replace_all(line, "{{HEAD_EXTRA}}", head_extra)
|
||||
line = replace_all(line, "{{HEADER_BRAND}}", header_brand)
|
||||
line = replace_all(line, "{{VERSION}}", version)
|
||||
|
||||
pos = index(line, "{{CONTENT}}")
|
||||
if (pos > 0) {
|
||||
|
||||
148
kewt.sh
148
kewt.sh
@@ -40,6 +40,8 @@ trap 'exit 0' HUP INT TERM
|
||||
|
||||
DEFAULT_CONF='title = "kewt"
|
||||
style = "kewt"
|
||||
lang = "en"
|
||||
draft_by_default = false
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
@@ -63,16 +65,17 @@ generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
posts_per_page = 12
|
||||
custom_admonitions = ""'
|
||||
custom_admonitions = ""
|
||||
cw_hide_url = true'
|
||||
|
||||
DEFAULT_TMPL='<!doctype html>
|
||||
<html>
|
||||
<html lang="{{LANG}}">
|
||||
<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" />
|
||||
<link rel="stylesheet" href="{{CSS}}{{VERSION}}" type="text/css" />
|
||||
{{HEAD_EXTRA}}
|
||||
</head>
|
||||
|
||||
@@ -145,9 +148,9 @@ create_new_post() {
|
||||
|
||||
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"
|
||||
printf -- '---\ntitle = "%s"\ndate = "%s"\ndraft = %s\n---\n# %s\n' "$post_user_title" "$post_date_val" "$draft_by_default" "$post_user_title" > "$file_path"
|
||||
else
|
||||
printf -- '---\ndate = "%s"\ndraft = false\n---\n' "$post_date_val" > "$file_path"
|
||||
printf -- '---\ndate = "%s"\ndraft = %s\n---\n' "$post_date_val" "$draft_by_default" > "$file_path"
|
||||
fi
|
||||
|
||||
echo "Created new post at '$file_path'."
|
||||
@@ -420,6 +423,8 @@ generate_nav() {
|
||||
|
||||
title="kewt"
|
||||
style="kewt"
|
||||
lang="en"
|
||||
draft_by_default="false"
|
||||
footer="made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
||||
dir_indexes="true"
|
||||
single_file_index="true"
|
||||
@@ -445,6 +450,7 @@ feed_file="rss.xml"
|
||||
posts_dir=""
|
||||
posts_per_page="12"
|
||||
custom_admonitions=""
|
||||
cw_hide_url="true"
|
||||
|
||||
load_config() {
|
||||
[ -f "$1" ] || return
|
||||
@@ -498,6 +504,9 @@ load_config() {
|
||||
posts_dir) posts_dir="${val#/}" ;;
|
||||
posts_per_page) posts_per_page="$val" ;;
|
||||
custom_admonitions) custom_admonitions="$val" ;;
|
||||
cw_hide_url) cw_hide_url="$val" ;;
|
||||
lang) lang="$val" ;;
|
||||
draft_by_default) draft_by_default="$val" ;;
|
||||
esac
|
||||
done < "$1"
|
||||
}
|
||||
@@ -540,12 +549,14 @@ parse_frontmatter() {
|
||||
fm_date=""
|
||||
fm_draft=""
|
||||
fm_description=""
|
||||
fm_content_warning=""
|
||||
while IFS='=' read -r _fk _fv; do
|
||||
case "$_fk" in
|
||||
title) fm_title="$_fv" ;;
|
||||
date) fm_date="$_fv" ;;
|
||||
draft) fm_draft="$_fv" ;;
|
||||
description) fm_description="$_fv" ;;
|
||||
content_warning) fm_content_warning="$_fv" ;;
|
||||
esac
|
||||
done < "$_fm_out"
|
||||
rm -f "$_fm_out"
|
||||
@@ -756,8 +767,39 @@ render_markdown() {
|
||||
else
|
||||
head_extra="$head_extra_og"
|
||||
fi
|
||||
|
||||
if [ "$is_cw_content_page" = "true" ] && [ "$cw_hide_url" = "true" ]; then
|
||||
head_extra="$head_extra
|
||||
<script>window.history.replaceState(null, '', '$current_url');</script>"
|
||||
fi
|
||||
|
||||
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"
|
||||
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_LANG="$lang" 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_VERSION="$asset_version" AWK_CONTENT_WARNING="$fm_content_warning" awk -f "$awk_dir/render_template.awk" "$local_template"
|
||||
}
|
||||
|
||||
generate_content_warning_page() {
|
||||
_fm_title="$1"
|
||||
_fm_content_warning="$2"
|
||||
_content_rel_url="$3"
|
||||
_target_url="$4"
|
||||
_out_file="$5"
|
||||
_is_home="$6"
|
||||
|
||||
_temp_cw="$KEWT_TMPDIR/cw_$$.md"
|
||||
_cw_text="${_fm_content_warning}"
|
||||
[ "$_cw_text" = "true" ] && _cw_text="This content may be sensitive."
|
||||
|
||||
cat <<EOF > "$_temp_cw"
|
||||
---
|
||||
title = "$_fm_title"
|
||||
---
|
||||
|
||||
> [!CAUTION]
|
||||
> **Content Warning:** $_cw_text
|
||||
|
||||
<a href="$(basename "$_content_rel_url")" class="cw-button">Reveal Content</a>
|
||||
EOF
|
||||
render_markdown "$_temp_cw" "$_is_home" "$_target_url" > "$_out_file"
|
||||
rm -f "$_temp_cw"
|
||||
}
|
||||
|
||||
needs_rebuild() {
|
||||
@@ -816,7 +858,20 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
target_url="/$rel_dir/index.html"
|
||||
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||
if needs_rebuild "$md_file" "$out_dir/index.html"; then
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
parse_frontmatter "$md_file"
|
||||
if [ -n "$fm_content_warning" ]; then
|
||||
content_out_file="$out_dir/content.html"
|
||||
content_rel_url="/$rel_dir/content.html"
|
||||
[ "$rel_dir" = "." ] && content_rel_url="/content.html"
|
||||
|
||||
is_cw_content_page="true"
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$content_out_file"
|
||||
is_cw_content_page="false"
|
||||
|
||||
generate_content_warning_page "$fm_title" "$fm_content_warning" "$content_rel_url" "$target_url" "$out_dir/index.html" "false"
|
||||
else
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
@@ -840,13 +895,16 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
sort_args="-r"
|
||||
fi
|
||||
|
||||
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | LC_ALL=C sort $sort_args | while read -r entry; do
|
||||
temp_entries="$KEWT_TMPDIR/entries_$$.txt"
|
||||
: > "$temp_entries"
|
||||
|
||||
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | while read -r entry; do
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
template.html|site.conf|style.css|index.md) continue ;;
|
||||
esac
|
||||
if [ -d "$entry" ]; then
|
||||
echo "- [${name}/](${name}/index.html)" >> "$temp_list"
|
||||
echo "${name}|- [${name}/](${name}/index.html)" >> "$temp_entries"
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
label="${name%.md}"
|
||||
|
||||
@@ -914,12 +972,20 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
label="$p_date $p_time"
|
||||
fi
|
||||
fi
|
||||
echo "- [$label](${name%.md}.html)" >> "$temp_list"
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
sort_key="${p_date} ${p_time}"
|
||||
else
|
||||
sort_key="$name"
|
||||
fi
|
||||
echo "${sort_key}|- [$label](${name%.md}.html)" >> "$temp_entries"
|
||||
else
|
||||
echo "- [$name]($name)" >> "$temp_list"
|
||||
echo "${name}|- [$name]($name)" >> "$temp_entries"
|
||||
fi
|
||||
done
|
||||
|
||||
LC_ALL=C sort $sort_args "$temp_entries" | cut -d'|' -f2- >> "$temp_list"
|
||||
rm -f "$temp_entries"
|
||||
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url="/$rel_dir/index.html"
|
||||
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||
@@ -1006,7 +1072,25 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
[ "$has_custom_index" = "true" ] && needs_rebuild "$dir/index.md" "$out_dir/index.html" && do_rebuild="true"
|
||||
|
||||
if [ "$do_rebuild" = "true" ]; then
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
if [ "$has_custom_index" = "true" ]; then
|
||||
parse_frontmatter "$dir/index.md"
|
||||
else
|
||||
fm_content_warning=""
|
||||
fi
|
||||
|
||||
if [ -n "$fm_content_warning" ]; then
|
||||
content_out_file="$out_dir/content.html"
|
||||
content_rel_url="/$rel_dir/content.html"
|
||||
[ "$rel_dir" = "." ] && content_rel_url="/content.html"
|
||||
|
||||
is_cw_content_page="true"
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$content_out_file"
|
||||
is_cw_content_page="false"
|
||||
|
||||
generate_content_warning_page "$fm_title" "$fm_content_warning" "$content_rel_url" "$target_url" "$out_dir/index.html" "false"
|
||||
else
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
rm -f "$temp_index" "$temp_list"
|
||||
@@ -1055,7 +1139,19 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while
|
||||
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
|
||||
out_file="$out/${rel_path%.md}.html"
|
||||
if needs_rebuild "$file" "$out_file"; then
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
if [ -n "$fm_content_warning" ]; then
|
||||
content_out_file="$out/${rel_path%.md}-content.html"
|
||||
content_rel_url="/${rel_path%.md}-content.html"
|
||||
orig_rel_url="/${rel_path%.md}.html"
|
||||
|
||||
is_cw_content_page="true"
|
||||
render_markdown "$file" "$is_home" "$orig_rel_url" > "$content_out_file"
|
||||
is_cw_content_page="false"
|
||||
|
||||
generate_content_warning_page "$fm_title" "$fm_content_warning" "$content_rel_url" "$orig_rel_url" "$out_file" "false"
|
||||
else
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if needs_rebuild "$file" "$out/$rel_path"; then
|
||||
@@ -1113,7 +1209,31 @@ if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
printf ' <lastBuildDate>%s</lastBuildDate>\n' "$build_date"
|
||||
} >> "$feed_path"
|
||||
|
||||
find "$src" -type f -name '*.md' -path "*${posts_dir:-__no_posts__}*" -print | LC_ALL=C sort -r | while IFS= read -r post_file; do
|
||||
temp_feed_files="$KEWT_TMPDIR/feed_files_$$.txt"
|
||||
: > "$temp_feed_files"
|
||||
|
||||
find "$src" -type f -name '*.md' -path "*${posts_dir:-__no_posts__}*" -print | while IFS= read -r post_file; do
|
||||
post_basename=$(basename "$post_file" .md)
|
||||
# Parse frontmatter to get date
|
||||
parse_frontmatter "$post_file"
|
||||
[ "$fm_draft" = "true" ] && continue
|
||||
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
|
||||
echo "${post_date} ${post_time}|${post_file}" >> "$temp_feed_files"
|
||||
done
|
||||
|
||||
LC_ALL=C sort -r "$temp_feed_files" | cut -d'|' -f2- | while IFS= read -r post_file; do
|
||||
post_basename=$(basename "$post_file" .md)
|
||||
|
||||
# Parse frontmatter
|
||||
|
||||
@@ -77,5 +77,5 @@ awk -f "$awk_dir/paragraphs.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_fi
|
||||
# Inline styles
|
||||
awk -f "$awk_dir/emoji.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/markdown_inline.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
||||
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -v script_dir="$script_dir" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
||||
rm "$temp_file"
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# Configuration
|
||||
|
||||
## Dot Files
|
||||
|
||||
- `.kewtignore` - files/directories to ignore completely. If the file is empty, the whole directory gets ignored.
|
||||
- `.kewthide` - files/directories to hide from navigation but still process. Same empty-file rules as `.kewtignore`.
|
||||
- `.kewtpreserve` - files/directories to copy as-is without converting markdown to HTML. Same empty-file rules again.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
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
|
||||
description = "A short page summary"
|
||||
---
|
||||
```
|
||||
- `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.
|
||||
- `description` - page description, used for Open Graph `og:description` meta tag.
|
||||
|
||||
## Directory Index Customisation
|
||||
|
||||
By default, directories without an `index.md` get an auto-generated index page listing their contents.
|
||||
|
||||
If you create your own `index.md` in a directory, you can still include the auto-generated file list by using the `{{LIST}}` placeholder:
|
||||
|
||||
```md
|
||||
# Blog
|
||||
|
||||
This is my blog. The posts are below. The top-most one is the most recent.
|
||||
|
||||
{{LIST}}
|
||||
```
|
||||
The `{{LIST}}` tag will be replaced with the generated list of links to child pages and files, exactly as in case the custom index didn't exist.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
You can auto-generate a Table of Contents by placing `{{TOC}}` anywhere in your markdown file. It collects all `h2` and `h3` headings and generates an ordered list with anchor links.
|
||||
|
||||
## Footnotes
|
||||
|
||||
Footnotes use the `[^id]` syntax inline and `[^id]: text` for definitions at the bottom of the file. They are rendered as a numbered `<section>` at the end of the page.
|
||||
|
||||
## Definition Lists
|
||||
|
||||
Definition lists use the standard syntax:
|
||||
|
||||
```md
|
||||
Term
|
||||
: Definition
|
||||
```
|
||||
This renders as `<dl><dt>Term</dt><dd>Definition</dd></dl>`. Multiple definitions per term are supported.
|
||||
|
||||
## Emoji Shortcodes
|
||||
|
||||
Standard GitHub/MkDocs emoji shortcodes like `:smile:`, `:fire:`, `:rocket:` are automatically replaced with their Unicode emoji equivalents. Shortcodes inside code blocks are left as-is.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Documentation
|
||||
|
||||
{{LIST}}
|
||||
@@ -1,4 +0,0 @@
|
||||
# Heaven
|
||||
|
||||
| --- | --- |
|
||||
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||
@@ -1,23 +1,7 @@
|
||||
# Usage
|
||||
|
||||
```sh
|
||||
kewt --help
|
||||
kewt --version
|
||||
kewt --new [title]
|
||||
kewt --post [title]
|
||||
kewt --generate-template [path]
|
||||
kewt --update [dir]
|
||||
kewt --from <src> --to <out>
|
||||
kewt [src] [out]
|
||||
kewt --watch
|
||||
kewt --serve [port]
|
||||
```
|
||||
- `--new [title]` creates a new site directory with a default `site.conf`, `template.html`, and `index.md`.
|
||||
- `--post [title]` creates a new markdown file in the configured `posts_dir` with the current date/time as the filename and default frontmatter.
|
||||
- `--generate-template [path]` writes the default `template.html` to the given path (defaults to `template.html` in the current directory).
|
||||
- `--update [dir]` adds any missing keys to `site.conf` and checks `template.html` against the latest default.
|
||||
- `--watch` (`-w`) watches for file changes in the source directory and rebuilds automatically.
|
||||
- `--serve` (`-s`) starts a local HTTP server (python3 or busybox) in the output directory after building. Use with the port number to specify the port. Composable with `--watch`.
|
||||
---
|
||||
title = "Configuration"
|
||||
---
|
||||
# Configuration
|
||||
|
||||
## site.conf
|
||||
|
||||
@@ -48,6 +32,7 @@ feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
posts_per_page = 12
|
||||
custom_admonitions = ""
|
||||
cw_hide_url = true
|
||||
```
|
||||
- `title` - site title
|
||||
- `style` - style file name from `./styles` (without `.css`)
|
||||
@@ -75,3 +60,10 @@ custom_admonitions = ""
|
||||
- `posts_per_page` - number of posts per page in paginated post indexes (default: 12). Set to 0 to disable pagination.
|
||||
- `enable_header_links` - turns markdown section headings into clickable anchor links (default: true)
|
||||
- `custom_admonitions` - comma separated list of custom admonitions
|
||||
- `cw_hide_url` - embeds non-breaking JS to replace the URL in the browser bar on content warning pages (default: true)
|
||||
|
||||
## Dot Files
|
||||
|
||||
- `.kewtignore` - files/directories to ignore completely. If the file is empty, the whole directory gets ignored.
|
||||
- `.kewthide` - files/directories to hide from navigation but still process. Same empty-file rules as `.kewtignore`.
|
||||
- `.kewtpreserve` - files/directories to copy as-is without converting markdown to HTML. Same empty-file rules again.
|
||||
@@ -1,3 +1,6 @@
|
||||
---
|
||||
title = "Embeds"
|
||||
---
|
||||
# Embeds
|
||||
|
||||
- `\![link]`:
|
||||
20
site/docs/frontmatter.md
Normal file
20
site/docs/frontmatter.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title = "Frontmatter"
|
||||
---
|
||||
# Frontmatter
|
||||
|
||||
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
|
||||
description = "A short page summary"
|
||||
---
|
||||
```
|
||||
- `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.
|
||||
- `description` - page description, used for Open Graph `og:description` meta tag.
|
||||
- `content_warning` - if set, creates an interstitial warning page that the user must click through. If set to `true` uses a generic warning, otherwise uses your string.
|
||||
6
site/docs/index.md
Normal file
6
site/docs/index.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title = "Documentation"
|
||||
---
|
||||
# Documentation
|
||||
|
||||
{{LIST}}
|
||||
@@ -1,3 +1,6 @@
|
||||
---
|
||||
title = "Installation"
|
||||
---
|
||||
# Installation
|
||||
|
||||
## Standalone
|
||||
41
site/docs/markdown.md
Normal file
41
site/docs/markdown.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title = "Markdown Extensions"
|
||||
---
|
||||
# Markdown Extensions
|
||||
|
||||
## Directory Index Customisation
|
||||
|
||||
By default, directories without an `index.md` get an auto-generated index page listing their contents.
|
||||
|
||||
If you create your own `index.md` in a directory, you can still include the auto-generated file list by using the `{{LIST}}` placeholder:
|
||||
|
||||
```md
|
||||
# Blog
|
||||
|
||||
This is my blog. The posts are below. The top-most one is the most recent.
|
||||
|
||||
{{LIST}}
|
||||
```
|
||||
The `{{LIST}}` placeholder is replaced with the autogenerated file list.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
`{{TOC}}` auto-generates a nested heading list with clickable anchors.
|
||||
|
||||
## Footnotes
|
||||
|
||||
Full support for `[^id]` footnotes and `[^id]: text` definitions. They render as a numbered `<section>` at the bottom of the page.
|
||||
|
||||
## Definition Lists
|
||||
|
||||
Definition lists use the standard syntax:
|
||||
|
||||
```md
|
||||
Term
|
||||
: Definition
|
||||
```
|
||||
This renders as `<dl><dt>Term</dt><dd>Definition</dd></dl>`. Multiple definitions per term are supported.
|
||||
|
||||
## Emoji Shortcodes
|
||||
|
||||
Standard GitHub/MkDocs emoji shortcodes like `:smile:`, `:fire:`, `:rocket:` are automatically replaced with their Unicode emoji equivalents. Shortcodes inside code blocks are left as-is.
|
||||
15
site/docs/templates.md
Normal file
15
site/docs/templates.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title = "Templates"
|
||||
---
|
||||
# Templates
|
||||
|
||||
When customizing `template.html`, the placeholders available are:
|
||||
- `{{CONTENT}}` - the generated content
|
||||
- `{{TITLE}}` - the generated title
|
||||
- `{{NAV}}` - the generated navigation
|
||||
- `{{FOOTER}}` - the configured footer
|
||||
- `{{VERSION}}` - the cache-busting string from `versioning = true` (e.g. `?v=12345678`). Safe to use even if versioning is **disabled** (it will be empty).
|
||||
- `{{CSS}}` - the configured CSS file path
|
||||
- `{{LANG}}` - the configured document language
|
||||
- `{{HEAD_EXTRA}}` - meta-tags
|
||||
- `{{HEADER_BRAND}}` - header rendering the name and/or logo
|
||||
23
site/docs/usage.md
Normal file
23
site/docs/usage.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title = "Usage"
|
||||
---
|
||||
# Usage
|
||||
|
||||
```sh
|
||||
kewt --help
|
||||
kewt --version
|
||||
kewt --new [title]
|
||||
kewt --post [title]
|
||||
kewt --generate-template [path]
|
||||
kewt --update [dir]
|
||||
kewt --from <src> --to <out>
|
||||
kewt [src] [out]
|
||||
kewt --watch
|
||||
kewt --serve [port]
|
||||
```
|
||||
- `--new [title]` creates a new site directory with a default `site.conf`, `template.html`, and `index.md`.
|
||||
- `--post [title]` creates a new markdown file in the configured `posts_dir` with the current date/time as the filename and default frontmatter.
|
||||
- `--generate-template [path]` writes the default `template.html` to the given path (defaults to `template.html` in the current directory).
|
||||
- `--update [dir]` adds any missing keys to `site.conf` and checks `template.html` against the latest default.
|
||||
- `--watch` (`-w`) watches for file changes in the source directory and rebuilds automatically.
|
||||
- `--serve` (`-s`) starts a local HTTP server (python3 or busybox) in the output directory after building. Use with the port number to specify the port. Composable with `--watch`.
|
||||
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 149 KiB |
12
site/heaven/index.md
Normal file
12
site/heaven/index.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title = "Heaven"
|
||||
content_warning = "This page may contain CSS"
|
||||
---
|
||||
# Heaven
|
||||
|
||||
Told you
|
||||
|
||||
Probably should've mentioned the catgirl too
|
||||
|
||||
| --- | --- |
|
||||
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||
@@ -9,7 +9,7 @@ display_logo = false
|
||||
display_title = true
|
||||
logo_as_favicon = false
|
||||
favicon = "favicon.ico"
|
||||
order = "Home, Docs, depths, Heaven"
|
||||
order = "Home, docs, depths, heaven"
|
||||
home_name = "Home"
|
||||
show_home_in_nav = true
|
||||
nav_links = ""
|
||||
|
||||
@@ -218,6 +218,21 @@ pre code {
|
||||
background: var(--adm-caution-bg);
|
||||
border-color: var(--adm-caution-border);
|
||||
}
|
||||
.cw-button {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
background: var(--bg-deep);
|
||||
border: 1px solid var(--code-border);
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cw-button:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
border-color: var(--fg);
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-top: 60px;
|
||||
@@ -252,7 +267,8 @@ hr {
|
||||
border-top: 1px solid var(--code-border);
|
||||
}
|
||||
|
||||
.nav-toggle, .nav-toggle-label {
|
||||
.nav-toggle,
|
||||
.nav-toggle-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -283,7 +299,7 @@ hr {
|
||||
margin: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.nav-toggle:checked ~ #side-bar {
|
||||
.nav-toggle:checked~#side-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user