feat: modular building and search
This commit is contained in:
707
lib/builder.sh
Normal file
707
lib/builder.sh
Normal file
@@ -0,0 +1,707 @@
|
||||
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
|
||||
}
|
||||
|
||||
build_site() {
|
||||
echo "Building site from '$src' to '$out'..."
|
||||
|
||||
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"
|
||||
|
||||
if [ -f "$dir/styles.css" ]; then
|
||||
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
|
||||
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
|
||||
|
||||
has_custom_index="false"
|
||||
has_list="false"
|
||||
if [ -f "$dir/index.md" ]; then
|
||||
has_custom_index="true"
|
||||
if grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$dir/index.md" 2>/dev/null; then
|
||||
has_list="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$has_custom_index" = "false" ] || [ "$has_list" = "true" ]; then
|
||||
is_posts_dir="false"
|
||||
if [ -n "$posts_dir" ] && { [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; }; then
|
||||
is_posts_dir="true"
|
||||
fi
|
||||
if [ "$single_file_index" = "true" ] && [ "$is_posts_dir" = "false" ] && [ "$has_list" = "false" ]; then
|
||||
md_count=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md" | wc -l)
|
||||
if [ "$md_count" -eq 1 ]; then
|
||||
md_file=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md")
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url="/$rel_dir/index.html"
|
||||
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||
if needs_rebuild "$md_file" "$out_dir/index.html"; then
|
||||
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
|
||||
fi
|
||||
|
||||
temp_index="$KEWT_TMPDIR/index.md"
|
||||
temp_list="$KEWT_TMPDIR/list.md"
|
||||
: > "$temp_list"
|
||||
|
||||
if [ "$has_custom_index" = "false" ]; then
|
||||
display_dir="${rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index"
|
||||
echo "" >> "$temp_index"
|
||||
fi
|
||||
|
||||
|
||||
sort_args=""
|
||||
# If this is the posts dir reverse
|
||||
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||
sort_args="-r"
|
||||
fi
|
||||
|
||||
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}/](${name}/index.html)" >> "$temp_entries"
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
label="${name%.md}"
|
||||
|
||||
# Parse frontmatter for date/title/draft
|
||||
parse_frontmatter "$entry"
|
||||
[ "$fm_draft" = "true" ] && continue
|
||||
|
||||
# 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="$p_date $p_time"
|
||||
fi
|
||||
fi
|
||||
if [ "$is_post_entry" = "true" ]; then
|
||||
sort_key="${p_date} ${p_time}"
|
||||
else
|
||||
sort_key="$name"
|
||||
fi
|
||||
echo "${sort_key}|- [$label](${name%.md}.html)|$name|${name%.md}.html" >> "$temp_entries"
|
||||
else
|
||||
echo "${name}|- [$name]($name)|$name|$name" >> "$temp_entries"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$is_posts_dir" = "true" ]; then
|
||||
LC_ALL=C sort $sort_args "$temp_entries" > "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||
cut -d'|' -f2 "$KEWT_TMPDIR/sorted_entries_$$.txt" >> "$temp_list"
|
||||
mkdir -p "$KEWT_TMPDIR/prevnext"
|
||||
awk -F'|' '
|
||||
{
|
||||
name[NR] = $3
|
||||
url[NR] = $4
|
||||
}
|
||||
END {
|
||||
for(i=1; i<=NR; i++) {
|
||||
prev_str = ""
|
||||
next_str = ""
|
||||
if(i > 1) {
|
||||
next_str = "[Next >](" url[i-1] ")"
|
||||
}
|
||||
if(i < NR) {
|
||||
prev_str = "[< Previous](" url[i+1] ")"
|
||||
}
|
||||
if (prev_str != "" || next_str != "") {
|
||||
out = "'"$KEWT_TMPDIR"'/prevnext/" name[i]
|
||||
printf "%s|%s\n", prev_str, next_str > out
|
||||
}
|
||||
}
|
||||
}
|
||||
' "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||
rm -f "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||
else
|
||||
LC_ALL=C sort $sort_args "$temp_entries" | cut -d'|' -f2 >> "$temp_list"
|
||||
fi
|
||||
rm -f "$temp_entries"
|
||||
|
||||
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||
target_url="/$rel_dir/index.html"
|
||||
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||
|
||||
num_items=$(wc -l < "$temp_list")
|
||||
if [ "$is_posts_dir" = "true" ] && [ -n "$posts_per_page" ] && [ "$posts_per_page" -gt 0 ] && [ "$num_items" -gt "$posts_per_page" ]; then
|
||||
num_pages=$(( (num_items + posts_per_page - 1) / posts_per_page ))
|
||||
for p in $(seq 1 $num_pages); do
|
||||
chunk_list="$KEWT_TMPDIR/chunk.md"
|
||||
start_line=$(( (p - 1) * posts_per_page + 1 ))
|
||||
tail -n +$start_line "$temp_list" | head -n "$posts_per_page" > "$chunk_list"
|
||||
|
||||
base_url_dir="$(dirname "$target_url")"
|
||||
[ "$base_url_dir" = "/" ] && base_url_dir=""
|
||||
|
||||
nav_html="<div class=\"pagination\">"
|
||||
if [ "$p" -gt 1 ]; then
|
||||
if [ "$p" -eq 2 ]; then
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/index.html\" class=\"prev-page\">« Prev</a> "
|
||||
else
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p-1))/index.html\" class=\"prev-page\">« Prev</a> "
|
||||
fi
|
||||
fi
|
||||
nav_html="$nav_html <span class=\"page-number\">Page $p of $num_pages</span> "
|
||||
if [ "$p" -lt "$num_pages" ]; then
|
||||
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p+1))/index.html\" class=\"next-page\">Next »</a> "
|
||||
fi
|
||||
nav_html="$nav_html</div>"
|
||||
|
||||
echo "" >> "$chunk_list"
|
||||
echo "$nav_html" >> "$chunk_list"
|
||||
|
||||
temp_index_p="$KEWT_TMPDIR/index_p$p.md"
|
||||
if [ "$has_custom_index" = "false" ]; then
|
||||
display_dir="${rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index_p"
|
||||
echo "" >> "$temp_index_p"
|
||||
else
|
||||
: > "$temp_index_p"
|
||||
fi
|
||||
|
||||
if [ "$has_custom_index" = "true" ]; then
|
||||
awk '
|
||||
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||
while((getline line < "'"$chunk_list"'") > 0) print line
|
||||
close("'"$chunk_list"'")
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$dir/index.md" >> "$temp_index_p"
|
||||
else
|
||||
cat "$chunk_list" >> "$temp_index_p"
|
||||
fi
|
||||
|
||||
if [ "$p" -eq 1 ]; then
|
||||
out_file="$out_dir/index.html"
|
||||
target_url_p="$target_url"
|
||||
else
|
||||
out_file="$out_dir/page/$p/index.html"
|
||||
target_url_p="$base_url_dir/page/$p/index.html"
|
||||
mkdir -p "$(dirname "$out_file")"
|
||||
fi
|
||||
|
||||
render_markdown "$temp_index_p" "$is_home" "$target_url_p" > "$out_file"
|
||||
rm -f "$temp_index_p" "$chunk_list"
|
||||
done
|
||||
else
|
||||
if [ "$has_custom_index" = "true" ]; then
|
||||
awk '
|
||||
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||
while((getline line < "'"$temp_list"'") > 0) print line
|
||||
close("'"$temp_list"'")
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$dir/index.md" > "$temp_index"
|
||||
else
|
||||
cat "$temp_list" >> "$temp_index"
|
||||
fi
|
||||
|
||||
do_rebuild="false"
|
||||
needs_rebuild "$dir" "$out_dir/index.html" && do_rebuild="true"
|
||||
[ "$has_custom_index" = "true" ] && needs_rebuild "$dir/index.md" "$out_dir/index.html" && do_rebuild="true"
|
||||
|
||||
if [ "$do_rebuild" = "false" ] && [ -f "$out_dir/index.html" ]; then
|
||||
for _child in "$dir"/*; do
|
||||
[ -e "$_child" ] || continue
|
||||
if [ "$_child" -nt "$out_dir/index.html" ]; then
|
||||
do_rebuild="true"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$do_rebuild" = "true" ]; then
|
||||
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"
|
||||
fi
|
||||
done
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
|
||||
case "${file##*/}" in
|
||||
template.html|site.conf|style.css|styles.css) continue ;;
|
||||
esac
|
||||
|
||||
if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
|
||||
is_preserved=0
|
||||
if [ -n "$(eval "find \"$file\" \( $PRESERVE_ARGS \) -print")" ]; then
|
||||
is_preserved=1
|
||||
fi
|
||||
|
||||
is_posts_dir_2="false"
|
||||
if [ -n "$posts_dir" ] && { [ "$dir_rel" = "$posts_dir" ] || [ "./$dir_rel" = "$posts_dir" ]; }; then
|
||||
is_posts_dir_2="true"
|
||||
fi
|
||||
|
||||
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ] && [ "$is_posts_dir_2" = "false" ]; 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" ] && [ "$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"
|
||||
if needs_rebuild "$file" "$out_file"; then
|
||||
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
|
||||
cp "$file" "$out/$rel_path"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$error_page" ] && [ ! -f "$out/$error_page" ]; then
|
||||
temp_404="$KEWT_TMPDIR/404_gen.md"
|
||||
echo "# 404 - Not Found" > "$temp_404"
|
||||
echo "" >> "$temp_404"
|
||||
echo "The requested page could not be found." >> "$temp_404"
|
||||
render_markdown "$temp_404" "false" "/$error_page" > "$out/$error_page"
|
||||
rm -f "$temp_404"
|
||||
fi
|
||||
|
||||
if [ -n "$base_url" ]; then
|
||||
sitemap_file="$out/sitemap.xml"
|
||||
base_url="${base_url%/}"
|
||||
today=$(date +%Y-%m-%d)
|
||||
|
||||
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$sitemap_file"
|
||||
printf '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n' >> "$sitemap_file"
|
||||
|
||||
find "$out" -type f -name "*.html" -print | sort | while IFS= read -r html_file; do
|
||||
rel_url="${html_file#"$out"}"
|
||||
|
||||
# Don't include 404 in the sitemap (duh)
|
||||
[ "${rel_url#/}" = "$error_page" ] && continue
|
||||
|
||||
{
|
||||
printf ' <url>\n'
|
||||
printf ' <loc>%s%s</loc>\n' "$base_url" "$rel_url"
|
||||
printf ' <lastmod>%s</lastmod>\n' "$today"
|
||||
printf ' </url>\n'
|
||||
} >> "$sitemap_file"
|
||||
done
|
||||
|
||||
printf '</urlset>\n' >> "$sitemap_file"
|
||||
fi
|
||||
|
||||
if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||
feed_path="$out/$feed_file"
|
||||
base_url_feed="${base_url%/}"
|
||||
build_date=$(date -u '+%a, %d %b %Y %H:%M:%S +0000')
|
||||
|
||||
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$feed_path"
|
||||
{
|
||||
printf '<rss version="2.0">\n'
|
||||
printf ' <channel>\n'
|
||||
printf ' <title>%s</title>\n' "$title"
|
||||
printf ' <link>%s</link>\n' "$base_url_feed"
|
||||
printf ' <description>%s</description>\n' "$title"
|
||||
printf ' <lastBuildDate>%s</lastBuildDate>\n' "$build_date"
|
||||
} >> "$feed_path"
|
||||
|
||||
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
|
||||
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="$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')
|
||||
else
|
||||
post_heading="Post"
|
||||
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')
|
||||
feed_post_title="$post_heading - $post_date $post_time"
|
||||
|
||||
rel_path="${post_file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
post_url="$base_url_feed/${rel_path%.md}.html"
|
||||
|
||||
if date -u -d "$post_date $post_time" '+%a, %d %b %Y %H:%M:%S +0000' >/dev/null 2>&1; then
|
||||
pub_date=$(date -u -d "$post_date $post_time" '+%a, %d %b %Y %H:%M:%S +0000')
|
||||
else
|
||||
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" ;;
|
||||
07) pub_mon="Jul" ;; 08) pub_mon="Aug" ;; 09) pub_mon="Sep" ;;
|
||||
10) pub_mon="Oct" ;; 11) pub_mon="Nov" ;; 12) pub_mon="Dec" ;;
|
||||
esac
|
||||
pub_date="Mon, ${pub_day} ${pub_mon} ${pub_year} ${post_time}:00 +0000"
|
||||
fi
|
||||
|
||||
{
|
||||
printf ' <item>\n'
|
||||
printf ' <title>%s</title>\n' "$feed_post_title"
|
||||
printf ' <link>%s</link>\n' "$post_url"
|
||||
printf ' <guid>%s</guid>\n' "$post_url"
|
||||
printf ' <pubDate>%s</pubDate>\n' "$pub_date"
|
||||
printf ' </item>\n'
|
||||
} >> "$feed_path"
|
||||
done
|
||||
|
||||
printf ' </channel>\n' >> "$feed_path"
|
||||
printf '</rss>\n' >> "$feed_path"
|
||||
fi
|
||||
|
||||
if [ "$generate_search" = "true" ] || [ "$generate_tags" = "true" ]; then
|
||||
if [ "$generate_search" = "true" ]; then
|
||||
printf '[\n' > "$out/search.json"
|
||||
fi
|
||||
first_search_item="true"
|
||||
temp_tags="$KEWT_TMPDIR/tags_$$.txt"
|
||||
: > "$temp_tags"
|
||||
|
||||
eval "find \"$src\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print" | sort | while IFS= read -r md_file; do
|
||||
is_index="false"
|
||||
[ "$(basename "$md_file")" = "index.md" ] && is_index="true"
|
||||
|
||||
rel_path="${md_file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
if [ "$is_index" = "true" ]; then
|
||||
if [ "$rel_path" = "index.md" ]; then
|
||||
md_url="/index.html"
|
||||
else
|
||||
md_url="/${rel_path%/index.md}/index.html"
|
||||
fi
|
||||
else
|
||||
md_url="/${rel_path%.md}.html"
|
||||
if [ "$single_file_index" = "true" ]; then
|
||||
dir_of_file="$(dirname "$md_file")"
|
||||
rel_dir_of_file="${dir_of_file#"$src"}"
|
||||
rel_dir_of_file="${rel_dir_of_file#/}"
|
||||
[ -z "$rel_dir_of_file" ] && rel_dir_of_file="."
|
||||
|
||||
is_posts_dir_search="false"
|
||||
if [ -n "$posts_dir" ] && { [ "$rel_dir_of_file" = "$posts_dir" ] || [ "./$rel_dir_of_file" = "$posts_dir" ]; }; then
|
||||
is_posts_dir_search="true"
|
||||
fi
|
||||
|
||||
if [ "$is_posts_dir_search" = "false" ] && [ ! -f "$dir_of_file/index.md" ]; then
|
||||
md_count_search=$(find "$dir_of_file" ! -name "$(basename "$dir_of_file")" -prune -name "*.md" | wc -l)
|
||||
if [ "$md_count_search" -eq 1 ]; then
|
||||
if [ "$rel_dir_of_file" = "." ]; then
|
||||
md_url="/index.html"
|
||||
else
|
||||
md_url="/$rel_dir_of_file/index.html"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
parse_frontmatter "$md_file"
|
||||
[ "$fm_draft" = "true" ] && continue
|
||||
[ -n "$fm_content_warning" ] && continue
|
||||
|
||||
md_heading="$fm_title"
|
||||
if [ -z "$md_heading" ]; then
|
||||
md_heading=$(grep -m 1 '^# ' "$md_file" | sed 's/^# *//; s/ *$//')
|
||||
if [ -n "$md_heading" ]; then
|
||||
md_heading=$(echo "$md_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')
|
||||
fi
|
||||
fi
|
||||
if [ -z "$md_heading" ]; then
|
||||
basename_no_ext=$(basename "$md_file" .md)
|
||||
if [ "$basename_no_ext" != "index" ] && [ "$basename_no_ext" != "404_gen" ]; then
|
||||
md_heading=$(echo "$basename_no_ext" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
|
||||
else
|
||||
md_heading="$title - Page"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$generate_search" = "true" ]; then
|
||||
md_content=$(awk '{
|
||||
if (NR == 1 && $0 == "---") { in_fm = 1; next }
|
||||
if (in_fm && $0 == "---") { in_fm = 0; next }
|
||||
if (in_fm) next
|
||||
if ($0 ~ /^```/) { in_code = !in_code; next }
|
||||
if (in_code) next
|
||||
print
|
||||
}' "$md_file" | sed \
|
||||
-e 's/^#\{1,6\} //' \
|
||||
-e 's/\*\*\([^*]*\)\*\*/\1/g' \
|
||||
-e 's/\*\([^*]*\)\*/\1/g' \
|
||||
-e 's/__\([^_]*\)__/\1/g' \
|
||||
-e 's/_\([^_]*\)_/\1/g' \
|
||||
-e 's/`\([^`]*\)`/\1/g' \
|
||||
-e 's/\[\([^]]*\)](\([^)]*\))/\1/g' \
|
||||
-e 's/!\[\([^]]*\)](\([^)]*\))//g' \
|
||||
-e 's/^[[:space:]]*[-*+] //' \
|
||||
-e 's/^[[:space:]]*[0-9]\{1,\}\. //' \
|
||||
-e 's/^>[[:space:]]*//' \
|
||||
-e 's/<[^>]*>//g' \
|
||||
-e '/^[[:space:]]*$/d' \
|
||||
-e 's/|//g' \
|
||||
-e 's/^[[:space:]]*---[[:space:]]*$//' \
|
||||
| tr '\n' ' ' | sed -e 's/ */ /g' -e 's/\\/\\\\/g' -e 's/"/\\"/g' | head -c 500)
|
||||
if [ "$first_search_item" = "false" ]; then
|
||||
printf ',\n' >> "$out/search.json"
|
||||
fi
|
||||
printf ' {"url": "%s", "title": "%s", "content": "%s"}' "$md_url" "$md_heading" "$md_content" >> "$out/search.json"
|
||||
first_search_item="false"
|
||||
fi
|
||||
|
||||
if [ "$generate_tags" = "true" ] && [ -n "$fm_tags" ]; then
|
||||
old_ifs=$IFS
|
||||
IFS=','
|
||||
for tag in $fm_tags; do
|
||||
tag=$(echo "$tag" | sed 's/^[ \t]*//;s/[ \t]*$//')
|
||||
[ -z "$tag" ] && continue
|
||||
printf '%s|%s|%s\n' "$tag" "$md_url" "$md_heading" >> "$temp_tags"
|
||||
done
|
||||
IFS=$old_ifs
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$generate_search" = "true" ]; then
|
||||
printf '\n]\n' >> "$out/search.json"
|
||||
|
||||
cp "$script_dir/lib/search.js" "$out/search.js"
|
||||
|
||||
search_md="$KEWT_TMPDIR/search_$$.md"
|
||||
printf '%s\n' '# Search' '' \
|
||||
'<form class="kewt-search-page" action="/search.html" method="get">' \
|
||||
' <input type="text" id="search-box" name="q" placeholder="Search..." required>' \
|
||||
' <button type="submit">Search</button>' \
|
||||
'</form>' '' \
|
||||
'<div id="search-results-list">' \
|
||||
' <p>Loading...</p>' \
|
||||
'</div>' '' \
|
||||
'<script src="/search.js"></script>' > "$search_md"
|
||||
render_markdown "$search_md" "false" "/search.html" > "$out/search.html"
|
||||
rm -f "$search_md"
|
||||
fi
|
||||
|
||||
if [ "$generate_tags" = "true" ]; then
|
||||
tags_out_dir="$out/$tags_dir"
|
||||
mkdir -p "$tags_out_dir"
|
||||
|
||||
tags_index_md="$KEWT_TMPDIR/tags_index_$$.md"
|
||||
echo "# Tags" > "$tags_index_md"
|
||||
echo "" >> "$tags_index_md"
|
||||
|
||||
cut -d'|' -f1 "$temp_tags" | sort -u | while IFS= read -r tag; do
|
||||
tag_slug=$(echo "$tag" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
|
||||
|
||||
echo "- [$tag](/$(echo $tags_dir | sed 's|^\/||; s|\/$||')/$tag_slug.html)" >> "$tags_index_md"
|
||||
|
||||
tag_page_md="$KEWT_TMPDIR/tag_page_$$.md"
|
||||
echo "# Tag: $tag" > "$tag_page_md"
|
||||
echo "" >> "$tag_page_md"
|
||||
echo "Posts tagged with **$tag**:" >> "$tag_page_md"
|
||||
echo "" >> "$tag_page_md"
|
||||
|
||||
grep "^${tag}|" "$temp_tags" | while IFS='|' read -r _t t_url t_title; do
|
||||
echo "- [$t_title]($t_url)" >> "$tag_page_md"
|
||||
done
|
||||
|
||||
render_markdown "$tag_page_md" "false" "/$tags_dir/$tag_slug.html" > "$tags_out_dir/$tag_slug.html"
|
||||
rm -f "$tag_page_md"
|
||||
done
|
||||
|
||||
render_markdown "$tags_index_md" "false" "/$tags_dir/index.html" > "$tags_out_dir/index.html"
|
||||
rm -f "$tags_index_md"
|
||||
fi
|
||||
rm -f "$temp_tags"
|
||||
fi
|
||||
|
||||
echo "Build complete."
|
||||
}
|
||||
140
lib/commands.sh
Normal file
140
lib/commands.sh
Normal file
@@ -0,0 +1,140 @@
|
||||
usage() {
|
||||
invoked_as=$(basename "${KEWT_INVOKED_AS:-$0}")
|
||||
cat <<EOF
|
||||
Usage: $invoked_as [--from <src>] [--to <out>]
|
||||
$invoked_as [src] [out]
|
||||
$invoked_as --new [title]
|
||||
$invoked_as --update [dir]
|
||||
$invoked_as --post
|
||||
$invoked_as --generate-template
|
||||
$invoked_as --version
|
||||
$invoked_as --help
|
||||
|
||||
Options:
|
||||
--help Show this help message.
|
||||
--new [title] Create a new site directory (default: site)
|
||||
--update [dir] Update site.conf and template.html with latest defaults (defaults to current directory)
|
||||
--post Create a new empty post file in the configured posts_dir with current date and time as name
|
||||
--generate-template [path] Generate a new template file at <path> (default: template.html)
|
||||
--version Show version information.
|
||||
--from <src> Source directory (default: site)
|
||||
--to <out> Output directory (default: out)
|
||||
--watch, -w Watch for file changes and rebuild automatically.
|
||||
--serve, -s [port] Start a local HTTP server after building (default port: 8000).
|
||||
EOF
|
||||
}
|
||||
generate_template() {
|
||||
_gt_path="$1"
|
||||
[ -e "$_gt_path" ] && die "File '$_gt_path' already exists."
|
||||
_gt_dir=$(dirname "$_gt_path")
|
||||
[ -d "$_gt_dir" ] || mkdir -p "$_gt_dir"
|
||||
printf '%s\n' "$DEFAULT_TMPL" > "$_gt_path"
|
||||
echo "Generated template at '$_gt_path'."
|
||||
exit 0
|
||||
}
|
||||
|
||||
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"
|
||||
printf '%s\n' "$DEFAULT_CONF" > "$new_dir/site.conf"
|
||||
printf '%s\n' "$DEFAULT_TMPL" > "$new_dir/template.html"
|
||||
printf "# _kewt_ website\n" > "$new_dir/index.md"
|
||||
|
||||
if [ -n "$new_title" ]; then
|
||||
AWK_NEW_TITLE="$new_title" awk -f "$awk_dir/update_site_conf.awk" "$new_dir/site.conf" > "$new_dir/site.conf.tmp" && mv "$new_dir/site.conf.tmp" "$new_dir/site.conf"
|
||||
fi
|
||||
|
||||
echo "Created new site at '$new_dir'."
|
||||
exit 0
|
||||
}
|
||||
|
||||
create_new_post() {
|
||||
post_src_dir="$1"
|
||||
post_user_title="$2"
|
||||
|
||||
target_dir="$post_src_dir"
|
||||
if [ -n "$posts_dir" ]; then
|
||||
target_dir="$post_src_dir/$posts_dir"
|
||||
fi
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
base_filename="$(date +%Y-%m-%d-%H-%M)"
|
||||
filename="${base_filename}.md"
|
||||
file_path="$target_dir/$filename"
|
||||
|
||||
counter=1
|
||||
while [ -e "$file_path" ]; do
|
||||
filename="${base_filename}_${counter}.md"
|
||||
file_path="$target_dir/$filename"
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
|
||||
post_date_val="$(date "+%Y-%m-%d %H:%M")"
|
||||
if [ -n "$post_user_title" ]; then
|
||||
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 = %s\n---\n' "$post_date_val" "$draft_by_default" > "$file_path"
|
||||
fi
|
||||
|
||||
echo "Created new post at '$file_path'."
|
||||
exit 0
|
||||
}
|
||||
|
||||
update_site() {
|
||||
update_dir="${1:-.}"
|
||||
[ -d "$update_dir" ] || die "Directory '$update_dir' does not exist."
|
||||
|
||||
target_conf="$update_dir/site.conf"
|
||||
target_tmpl="$update_dir/template.html"
|
||||
|
||||
# Generate default site.conf
|
||||
default_conf="$KEWT_TMPDIR/default_site.conf"
|
||||
printf '%s\n' "$DEFAULT_CONF" > "$default_conf"
|
||||
|
||||
# Update site.conf
|
||||
if [ ! -f "$target_conf" ]; then
|
||||
echo "No site.conf found in '$update_dir'; nothing to update."
|
||||
else
|
||||
added=0
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
''|'#'*) continue ;;
|
||||
*=*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
key=$(printf '%s' "${line%%=*}" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
if ! grep -q "^[[:space:]]*${key}[[:space:]]*=" "$target_conf"; then
|
||||
printf '%s\n' "$line" >> "$target_conf"
|
||||
echo " Added: $key"
|
||||
added=$((added + 1))
|
||||
fi
|
||||
done < "$default_conf"
|
||||
if [ "$added" -eq 0 ]; then
|
||||
echo "site.conf is already up to date."
|
||||
else
|
||||
echo "Added $added new key(s) to '$target_conf'."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update template.html
|
||||
if [ -f "$target_tmpl" ]; then
|
||||
default_tmpl="$KEWT_TMPDIR/default_template.html"
|
||||
printf '%s\n' "$DEFAULT_TMPL" > "$default_tmpl"
|
||||
if cmp -s "$default_tmpl" "$target_tmpl" 2>/dev/null; then
|
||||
echo "template.html is already up to date."
|
||||
else
|
||||
cp "$default_tmpl" "${target_tmpl}.default"
|
||||
echo "template.html has local changes; saved latest default as '${target_tmpl}.default'."
|
||||
echo ""
|
||||
diff "$target_tmpl" "${target_tmpl}.default" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
}
|
||||
159
lib/config.sh
Normal file
159
lib/config.sh
Normal file
@@ -0,0 +1,159 @@
|
||||
DEFAULT_CONF='title = "kewt"
|
||||
style = "kewt"
|
||||
lang = "en"
|
||||
draft_by_default = false
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
order = ""
|
||||
home_name = "Home"
|
||||
show_home_in_nav = true
|
||||
nav_links = ""
|
||||
nav_extra = ""
|
||||
footer = "made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
||||
logo = ""
|
||||
display_logo = false
|
||||
display_title = true
|
||||
logo_as_favicon = true
|
||||
favicon = ""
|
||||
generate_page_title = true
|
||||
error_page = "not_found.html"
|
||||
versioning = false
|
||||
enable_header_links = true
|
||||
base_url = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
posts_per_page = 12
|
||||
custom_admonitions = ""
|
||||
cw_hide_url = true
|
||||
generate_tags = false
|
||||
tags_dir = "tags"
|
||||
generate_search = false
|
||||
search_in_footer = false
|
||||
search_in_header = false'
|
||||
|
||||
DEFAULT_TMPL='<!doctype 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}}{{VERSION}}" type="text/css" />
|
||||
{{HEAD_EXTRA}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="checkbox" id="nav-toggle" class="nav-toggle" aria-hidden="true" />
|
||||
<header>
|
||||
<h1>{{HEADER_BRAND}}</h1>
|
||||
<label for="nav-toggle" class="nav-toggle-label" aria-hidden="true">☰</label>
|
||||
</header>
|
||||
|
||||
<nav id="side-bar">{{NAV}}</nav>
|
||||
|
||||
<article>{{CONTENT}}</article>
|
||||
<footer>{{FOOTER}}</footer>
|
||||
</body>
|
||||
</html>'
|
||||
|
||||
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"
|
||||
flatten="false"
|
||||
order=""
|
||||
home_name="Home"
|
||||
show_home_in_nav="true"
|
||||
nav_links=""
|
||||
nav_extra=""
|
||||
footer="made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
||||
logo=""
|
||||
display_logo="false"
|
||||
display_title="true"
|
||||
logo_as_favicon="true"
|
||||
favicon=""
|
||||
generate_page_title="true"
|
||||
error_page="not_found.html"
|
||||
versioning="false"
|
||||
enable_header_links="true"
|
||||
base_url=""
|
||||
generate_feed="false"
|
||||
feed_file="rss.xml"
|
||||
posts_dir=""
|
||||
posts_per_page="12"
|
||||
custom_admonitions=""
|
||||
cw_hide_url="true"
|
||||
generate_tags="false"
|
||||
tags_dir="tags"
|
||||
generate_search="false"
|
||||
search_in_footer="false"
|
||||
search_in_header="false"
|
||||
|
||||
load_config() {
|
||||
[ -f "$1" ] || return
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
case "$line" in
|
||||
''|'#'*) continue ;;
|
||||
*=*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
|
||||
key=${line%%=*}
|
||||
val=${line#*=}
|
||||
|
||||
key=$(printf '%s' "$key" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
val=$(printf '%s' "$val" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||
case "$val" in
|
||||
\"*\")
|
||||
val=${val#\"}; val=${val%\"}
|
||||
val=$(printf '%s' "$val" | sed 's/\\"/\"/g; s/\\\\/\\/g')
|
||||
;;
|
||||
\'*\')
|
||||
val=${val#\'}; val=${val%\'}
|
||||
val=$(printf '%s' "$val" | sed "s/\\\\'/'/g; s/\\\\/\\/g")
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$key" in
|
||||
title) title="$val" ;;
|
||||
style) style="${val#/}" ;;
|
||||
dir_indexes) dir_indexes="$val" ;;
|
||||
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" ;;
|
||||
nav_links) nav_links="$val" ;;
|
||||
nav_extra) nav_extra="$val" ;;
|
||||
footer) footer="$val" ;;
|
||||
logo) logo="${val#/}" ;;
|
||||
display_logo) display_logo="$val" ;;
|
||||
display_title) display_title="$val" ;;
|
||||
logo_as_favicon) logo_as_favicon="$val" ;;
|
||||
favicon) favicon="${val#/}" ;;
|
||||
generate_page_title) generate_page_title="$val" ;;
|
||||
error_page) error_page="${val#/}" ;;
|
||||
versioning) versioning="$val" ;;
|
||||
enable_header_links) enable_header_links="$val" ;;
|
||||
base_url) base_url="$val" ;;
|
||||
generate_feed) generate_feed="$val" ;;
|
||||
feed_file) feed_file="${val#/}" ;;
|
||||
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" ;;
|
||||
generate_tags) generate_tags="$val" ;;
|
||||
tags_dir) tags_dir="${val#/}" ;;
|
||||
generate_search) generate_search="$val" ;;
|
||||
search_in_footer) search_in_footer="$val" ;;
|
||||
search_in_header) search_in_header="$val" ;;
|
||||
esac
|
||||
done < "$1"
|
||||
}
|
||||
289
lib/generator.sh
Normal file
289
lib/generator.sh
Normal file
@@ -0,0 +1,289 @@
|
||||
SEARCH_FORM_FOOTER='<form class="kewt-search-footer" action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form>'
|
||||
SEARCH_FORM_HEADER='<form class="kewt-search-header" action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form>'
|
||||
SEARCH_FORM_NAV='<div class="kewt-search-nav"><form action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form></div>'
|
||||
|
||||
generate_nav() {
|
||||
dinfo=$(eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -print" | sort | AWK_SRC="$1" awk -f "$awk_dir/collect_dir_info.awk")
|
||||
find_cmd="find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print"
|
||||
if [ -n "$posts_dir" ] && [ -d "$1/$posts_dir" ]; then
|
||||
find_cmd="$find_cmd && echo \"$1/$posts_dir/index.md\""
|
||||
fi
|
||||
eval "$find_cmd" | sort -u | AWK_SRC="$1" AWK_SINGLE_FILE_INDEX="$single_file_index" AWK_FLATTEN="$flatten" AWK_ORDER="$order" AWK_HOME_NAME="$home_name" AWK_SHOW_HOME_IN_NAV="$show_home_in_nav" AWK_DINFO="$dinfo" awk -f "$awk_dir/generate_sidebar.awk"
|
||||
}
|
||||
escape_html_text() {
|
||||
printf '%s' "$1" | sed \
|
||||
-e 's/&/\&/g' \
|
||||
-e 's/</\</g' \
|
||||
-e 's/>/\>/g'
|
||||
}
|
||||
escape_html_attr() {
|
||||
printf '%s' "$1" | sed \
|
||||
-e 's/&/\&/g' \
|
||||
-e 's/"/\"/g' \
|
||||
-e 's/</\</g' \
|
||||
-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=""
|
||||
fm_description=""
|
||||
fm_content_warning=""
|
||||
fm_tags=""
|
||||
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" ;;
|
||||
tags) fm_tags="$_fv" ;;
|
||||
esac
|
||||
done < "$_fm_out"
|
||||
rm -f "$_fm_out"
|
||||
}
|
||||
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>'
|
||||
}
|
||||
find_closest() {
|
||||
target="$1"
|
||||
start_dir="$2"
|
||||
curr="$start_dir"
|
||||
while [ "$curr" != "$src" ] && [ "$curr" != "." ] && [ "$curr" != "/" ]; do
|
||||
if [ -f "$curr/$target" ]; then
|
||||
echo "$curr/$target"
|
||||
return
|
||||
fi
|
||||
curr=$(dirname "$curr")
|
||||
done
|
||||
if [ -f "$src/$target" ]; then
|
||||
echo "$src/$target"
|
||||
fi
|
||||
}
|
||||
copy_style_with_resolved_vars() {
|
||||
src_style="$1"
|
||||
out_style="$2"
|
||||
awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style"
|
||||
}
|
||||
render_markdown() {
|
||||
file="$1"
|
||||
is_home="$2"
|
||||
url_override="$3"
|
||||
|
||||
if [ -n "$url_override" ]; then
|
||||
current_url="$url_override"
|
||||
else
|
||||
rel_path="${file#"$src"}"
|
||||
rel_path="${rel_path#/}"
|
||||
current_url="/${rel_path%.md}.html"
|
||||
fi
|
||||
|
||||
content_file="$file"
|
||||
if [ -n "$posts_dir" ] && [ "$file" != "$src/$posts_dir/index.md" ]; then
|
||||
rel_dir_of_url=$(dirname "$current_url")
|
||||
rel_dir_of_url="${rel_dir_of_url#/}"
|
||||
if { [ "$rel_dir_of_url" = "$posts_dir" ] || [ "./$rel_dir_of_url" = "$posts_dir" ]; } && [ "$(basename "$current_url")" != "index.html" ]; then
|
||||
temp_post_with_backlink="$KEWT_TMPDIR/post_with_backlink_$$.md"
|
||||
printf "[< Back](index.html)\n\n" > "$temp_post_with_backlink"
|
||||
awk -f "$awk_dir/frontmatter.awk" "$file" >> "$temp_post_with_backlink"
|
||||
|
||||
post_md_name="$(basename "$current_url" .html).md"
|
||||
prevnext_file="$KEWT_TMPDIR/prevnext/$post_md_name"
|
||||
if [ -f "$prevnext_file" ]; then
|
||||
IFS='|' read -r prev_str next_str < "$prevnext_file"
|
||||
|
||||
printf "\n\n---\n<div class=\"post-nav\">\n" >> "$temp_post_with_backlink"
|
||||
if [ -n "$prev_str" ]; then
|
||||
printf "<span class=\"prev-post\">%s</span>\n" "$prev_str" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
if [ -n "$next_str" ]; then
|
||||
printf "<span class=\"next-post\">%s</span>\n" "$next_str" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
printf "</div>\n" >> "$temp_post_with_backlink"
|
||||
fi
|
||||
content_file="$temp_post_with_backlink"
|
||||
fi
|
||||
fi
|
||||
|
||||
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
||||
[ -z "$local_template" ] && local_template="$template"
|
||||
|
||||
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
||||
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
||||
if [ -n "$closest_style_src" ]; then
|
||||
style_rel_to_src="${closest_style_src#"$src"/}"
|
||||
case "$closest_style_src" in
|
||||
"$src/styles.css") style_rel_to_src="styles.css" ;;
|
||||
"$src/style.css") style_rel_to_src="style.css" ;;
|
||||
esac
|
||||
style_path="/${style_rel_to_src%styles.css}"
|
||||
style_path="${style_path%style.css}styles.css"
|
||||
else
|
||||
style_path="/styles.css"
|
||||
fi
|
||||
|
||||
logo_html=""
|
||||
if [ "$display_logo" = "true" ] && [ -n "$logo" ]; then
|
||||
logo_html="<img class=\"site-logo\" src=\"$logo\" alt=\"$title\" />"
|
||||
fi
|
||||
|
||||
brand_text=""
|
||||
if [ "$display_title" = "true" ]; then
|
||||
brand_text="$title"
|
||||
fi
|
||||
|
||||
if [ -n "$logo_html" ] && [ -n "$brand_text" ]; then
|
||||
header_brand="<a href=\"/index.html\">$logo_html $brand_text</a>"
|
||||
elif [ -n "$logo_html" ]; then
|
||||
header_brand="<a href=\"/index.html\">$logo_html</a>"
|
||||
elif [ -n "$brand_text" ]; then
|
||||
header_brand="<a href=\"/index.html\">$brand_text</a>"
|
||||
else
|
||||
header_brand="<a href=\"/index.html\">$title</a>"
|
||||
fi
|
||||
|
||||
favicon_src=""
|
||||
if [ "$logo_as_favicon" = "true" ] && [ -n "$logo" ]; then
|
||||
favicon_src="$logo"
|
||||
elif [ -n "$favicon" ]; then
|
||||
favicon_src="$favicon"
|
||||
fi
|
||||
head_extra=""
|
||||
if [ -n "$favicon_src" ]; then
|
||||
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 [ -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
|
||||
first_heading=$(grep -m 1 '^# ' "$file" | sed 's/^# *//; s/ *$//')
|
||||
if [ -n "$first_heading" ]; then
|
||||
first_heading=$(echo "$first_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')
|
||||
page_title="$first_heading - $title"
|
||||
else
|
||||
basename_no_ext=$(basename "$file" .md)
|
||||
if [ "$basename_no_ext" != "index" ] && [ "$basename_no_ext" != "404_gen" ]; then
|
||||
cap_basename=$(echo "$basename_no_ext" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
|
||||
page_title="$cap_basename - $title"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
head_extra_og="<meta property=\"og:title\" content=\"$(escape_html_attr "$page_title")\" />"
|
||||
if [ -n "$fm_description" ]; then
|
||||
head_extra_og="$head_extra_og
|
||||
<meta property=\"og:description\" content=\"$(escape_html_attr "$fm_description")\" />"
|
||||
fi
|
||||
og_url="${base_url%/}${current_url}"
|
||||
head_extra_og="$head_extra_og
|
||||
<meta property=\"og:url\" content=\"$(escape_html_attr "$og_url")\" />"
|
||||
|
||||
if [ -n "$head_extra" ]; then
|
||||
head_extra="$head_extra
|
||||
$head_extra_og"
|
||||
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
|
||||
|
||||
final_footer="$footer"
|
||||
if [ "$search_in_footer" = "true" ]; then
|
||||
final_footer="$footer $SEARCH_FORM_FOOTER"
|
||||
fi
|
||||
|
||||
final_nav="$nav"
|
||||
final_header_brand="$header_brand"
|
||||
if [ "$search_in_header" = "true" ]; then
|
||||
final_header_brand="$header_brand $SEARCH_FORM_HEADER"
|
||||
final_nav="$SEARCH_FORM_NAV
|
||||
$nav"
|
||||
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_LANG="$lang" AWK_CURRENT_URL="$current_url" AWK_TITLE="$page_title" AWK_NAV="$final_nav" AWK_FOOTER="$final_footer" AWK_STYLE_PATH="${style_path}" AWK_HEADER_BRAND="$final_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"
|
||||
}
|
||||
44
lib/search.js
Normal file
44
lib/search.js
Normal file
@@ -0,0 +1,44 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var query = params.get('q');
|
||||
var box = document.getElementById('search-box');
|
||||
var resultsContainer = document.getElementById('search-results-list');
|
||||
|
||||
if (box && query) box.value = query;
|
||||
|
||||
if (!query) {
|
||||
resultsContainer.innerHTML = '<p>Enter a search term above.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/search.json')
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(data) {
|
||||
var q = query.toLowerCase();
|
||||
var results = data.filter(function(item) {
|
||||
return item.title.toLowerCase().indexOf(q) !== -1 ||
|
||||
item.content.toLowerCase().indexOf(q) !== -1;
|
||||
});
|
||||
|
||||
var esc = query.replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
if (results.length === 0) {
|
||||
resultsContainer.innerHTML = '<p>No results found for "<strong>' + esc + '</strong>".</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '<p>Found ' + results.length + ' result(s) for "<strong>' + esc + '</strong>":</p>';
|
||||
results.forEach(function(result) {
|
||||
var snippet = result.content.substring(0, 200);
|
||||
if (result.content.length > 200) snippet += '...';
|
||||
html += '<div class="search-result">';
|
||||
html += '<a href="' + result.url + '">' + result.title + '</a>';
|
||||
if (snippet) html += '<p>' + snippet + '</p>';
|
||||
html += '</div>';
|
||||
});
|
||||
resultsContainer.innerHTML = html;
|
||||
})
|
||||
.catch(function() {
|
||||
resultsContainer.innerHTML = '<p>Error loading search index.</p>';
|
||||
});
|
||||
});
|
||||
@@ -22,4 +22,8 @@ base_url = "https://kewt.krzak.org"
|
||||
custom_admonitions = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
posts_dir = ""
|
||||
generate_tags = false
|
||||
generate_search = true
|
||||
search_in_footer = true
|
||||
search_in_header = true
|
||||
215
styles/kewt.css
215
styles/kewt.css
@@ -48,6 +48,9 @@ header h1 {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
color: var(--fg-heading);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
@@ -218,6 +221,7 @@ pre code {
|
||||
background: var(--adm-caution-bg);
|
||||
border-color: var(--adm-caution-border);
|
||||
}
|
||||
|
||||
.cw-button {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
@@ -321,4 +325,215 @@ hr {
|
||||
.task-list-item-checkbox {
|
||||
margin: 0 0.2em 0.25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.kewt-search-page {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.kewt-search-page input[type="text"] {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
background: var(--code-bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.kewt-search-page input[type="text"]:focus {
|
||||
border-color: var(--fg-link);
|
||||
}
|
||||
|
||||
.kewt-search-page button {
|
||||
padding: 8px 20px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
background: var(--bg-deep);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kewt-search-page button:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
.search-result {
|
||||
margin: 16px 0;
|
||||
padding: 12px;
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.search-result a {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: var(--fg-link);
|
||||
}
|
||||
|
||||
.search-result p {
|
||||
margin: 6px 0 0 0;
|
||||
color: var(--fg-muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Footer search */
|
||||
.kewt-search-footer {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
margin-left: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.kewt-search-footer input[type="text"] {
|
||||
padding: 3px 8px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: var(--code-bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.kewt-search-footer input[type="text"]:focus {
|
||||
border-color: var(--fg-link);
|
||||
}
|
||||
|
||||
.kewt-search-footer button {
|
||||
padding: 3px 10px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: var(--bg-deep);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kewt-search-footer button:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
.kewt-search-header {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
vertical-align: middle;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.kewt-search-header input[type="text"] {
|
||||
padding: 4px 8px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: var(--code-bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.kewt-search-header input[type="text"]:focus {
|
||||
border-color: var(--fg-link);
|
||||
}
|
||||
|
||||
.kewt-search-header button {
|
||||
padding: 4px 10px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: var(--bg-deep);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kewt-search-header button:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
.kewt-search-nav {
|
||||
display: none;
|
||||
padding: 8px 0 12px 0;
|
||||
margin-bottom: 8px;
|
||||
border-bottom: 1px solid var(--code-border);
|
||||
}
|
||||
|
||||
.kewt-search-nav form {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.kewt-search-nav input[type="text"] {
|
||||
flex: 1;
|
||||
padding: 6px 8px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: var(--code-bg);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.kewt-search-nav input[type="text"]:focus {
|
||||
border-color: var(--fg-link);
|
||||
}
|
||||
|
||||
.kewt-search-nav button {
|
||||
padding: 6px 10px;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
background: var(--bg-deep);
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kewt-search-nav button:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.kewt-search-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.kewt-search-nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.kewt-search-page {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.kewt-search-page button {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.kewt-search-footer {
|
||||
display: flex;
|
||||
margin-left: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.kewt-search-footer input[type="text"] {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,10 @@ EOF
|
||||
|
||||
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/"
|
||||
cp -r "$REPO_ROOT/kewt.sh" "$REPO_ROOT/markdown.sh" "$REPO_ROOT/awk" "$REPO_ROOT/styles" "$REPO_ROOT/lib" "$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"
|
||||
chmod +x "$tmpbuild/kewt.sh" "$tmpbuild/markdown.sh"
|
||||
tar -cz -C "$tmpbuild" kewt.sh markdown.sh awk styles >> "$OUT_FILE"
|
||||
tar -cz -C "$tmpbuild" kewt.sh markdown.sh awk styles lib >> "$OUT_FILE"
|
||||
rm -rf "$tmpbuild"
|
||||
|
||||
chmod +x "$OUT_FILE"
|
||||
|
||||
Reference in New Issue
Block a user