Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21dc56aa6d | |||
| 7df5daaa6c | |||
| 4f74dd5fe0 | |||
| 0751849492 | |||
| 009877ae76 | |||
| 69bd5832e7 | |||
| b525a5f1c2 | |||
| 2a03859390 | |||
| b65c4c6665 | |||
| 90d8e25b70 | |||
| 8814c12480 | |||
| 0c0f249226 | |||
| b29a5274e1 | |||
| 00f4bbb5f0 | |||
| 7fe204f9f9 | |||
| 81d3caff45 |
14
.gitattributes
vendored
Normal file
14
.gitattributes
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
* text=auto eol=lf
|
||||
*.sh text eol=lf
|
||||
*.awk text eol=lf
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.js text eol=lf
|
||||
*.md text eol=lf
|
||||
*.conf text eol=lf
|
||||
*.json text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.png binary
|
||||
*.gif binary
|
||||
*.svg text eol=lf
|
||||
*.ico binary
|
||||
@@ -3,7 +3,11 @@ name: Lint
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
paths:
|
||||
- '**.sh'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.sh'
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
|
||||
4
Makefile
4
Makefile
@@ -1,5 +1,6 @@
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
ZSHCOMPDIR ?= $(PREFIX)/share/zsh/site-functions
|
||||
|
||||
all: kewt
|
||||
|
||||
@@ -9,9 +10,12 @@ kewt:
|
||||
install: kewt
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
install -m 755 kewt $(DESTDIR)$(BINDIR)/kewt
|
||||
install -d $(DESTDIR)$(ZSHCOMPDIR)
|
||||
install -m 644 packaging/zsh/_kewt $(DESTDIR)$(ZSHCOMPDIR)/_kewt
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/kewt
|
||||
rm -f $(DESTDIR)$(ZSHCOMPDIR)/_kewt
|
||||
|
||||
clean:
|
||||
rm -f kewt
|
||||
|
||||
@@ -375,6 +375,13 @@ function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, e
|
||||
} else if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
|
||||
# Preserve hand-written <img> attributes (style/class/etc) for normal images.
|
||||
repl = tag
|
||||
} else if (force_inline_tag != "" && !is_global_url(src) && is_inline_text_ext(ext_of(src))) {
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl != "") {
|
||||
repl = "<pre><code>" repl "</code></pre>"
|
||||
} else {
|
||||
repl = render_embed(src, alt, (alt != ""), 1)
|
||||
}
|
||||
} else {
|
||||
repl = render_embed(src, alt, (alt != ""), (force_inline_tag != ""))
|
||||
}
|
||||
@@ -399,7 +406,12 @@ function rewrite_double_bang_with_parens(line, out, rest, token, inside, src,
|
||||
src = substr(inside, sep + 2)
|
||||
sub(/\)$/, "", src)
|
||||
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl != "") {
|
||||
repl = "<pre><code>" repl "</code></pre>"
|
||||
} else {
|
||||
repl = render_embed(src, alt, (alt != ""), 1)
|
||||
}
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
@@ -416,7 +428,12 @@ function rewrite_double_bang_bare(line, out, rest, token, src, pre, post, rep
|
||||
src = token
|
||||
sub(/^!!\[/, "", src)
|
||||
sub(/\]$/, "", src)
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl != "") {
|
||||
repl = "<pre><code>" repl "</code></pre>"
|
||||
} else {
|
||||
repl = render_embed(src, "", 0, 1)
|
||||
}
|
||||
out = out pre repl
|
||||
rest = post
|
||||
}
|
||||
@@ -576,6 +593,75 @@ function restore_plain_markers(line) {
|
||||
return line
|
||||
}
|
||||
|
||||
function break_code_double_bang(line, out, rest, pstart, pend, code_content, token, pre, post, inside, sep, src, alt, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (1) {
|
||||
pstart = index(rest, "<code>")
|
||||
if (pstart == 0) {
|
||||
out = out rest
|
||||
break
|
||||
}
|
||||
out = out substr(rest, 1, pstart - 1)
|
||||
rest = substr(rest, pstart)
|
||||
pend = index(substr(rest, 7), "</code>")
|
||||
if (pend == 0) {
|
||||
out = out rest
|
||||
break
|
||||
}
|
||||
pend = pend + 6
|
||||
code_content = substr(rest, 7, pend - 7)
|
||||
rest = substr(rest, pend + 7)
|
||||
if (match(code_content, /!!\[[^\]]*\]\([^)]*\)/)) {
|
||||
token = substr(code_content, RSTART, RLENGTH)
|
||||
pre = substr(code_content, 1, RSTART - 1)
|
||||
post = substr(code_content, RSTART + RLENGTH)
|
||||
inside = token
|
||||
sub(/^!!\[/, "", inside)
|
||||
sep = index(inside, "](")
|
||||
alt = substr(inside, 1, sep - 1)
|
||||
src = substr(inside, sep + 2)
|
||||
sub(/\)$/, "", src)
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl != "") {
|
||||
repl = "<pre><code>" repl "</code></pre>"
|
||||
} else {
|
||||
repl = render_embed(src, alt, (alt != ""), 1)
|
||||
}
|
||||
if (repl == "") {
|
||||
out = out "<code>" code_content "</code>"
|
||||
} else {
|
||||
if (pre != "") out = out "<code>" pre "</code>"
|
||||
out = out repl
|
||||
if (post != "") out = out "<code>" post "</code>"
|
||||
}
|
||||
} else if (match(code_content, /!!\[[^\]]+\]/)) {
|
||||
token = substr(code_content, RSTART, RLENGTH)
|
||||
pre = substr(code_content, 1, RSTART - 1)
|
||||
post = substr(code_content, RSTART + RLENGTH)
|
||||
src = token
|
||||
sub(/^!!\[/, "", src)
|
||||
sub(/\]$/, "", src)
|
||||
repl = render_code_include(src, 1)
|
||||
if (repl != "") {
|
||||
repl = "<pre><code>" repl "</code></pre>"
|
||||
} else {
|
||||
repl = render_embed(src, "", 0, 1)
|
||||
}
|
||||
if (repl == "") {
|
||||
out = out "<code>" code_content "</code>"
|
||||
} else {
|
||||
if (pre != "") out = out "<code>" pre "</code>"
|
||||
out = out repl
|
||||
if (post != "") out = out "<code>" post "</code>"
|
||||
}
|
||||
} else {
|
||||
out = out "<code>" code_content "</code>"
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
input_dir = dirname_of(input_file)
|
||||
in_pre_code = 0
|
||||
@@ -601,6 +687,9 @@ BEGIN {
|
||||
|
||||
line = apply_td_vertical_align(line)
|
||||
line = restore_plain_markers(line)
|
||||
if (!(in_pre_code || start_pre)) {
|
||||
line = break_code_double_bang(line)
|
||||
}
|
||||
print line
|
||||
|
||||
if (start_pre && !end_pre) {
|
||||
|
||||
@@ -46,14 +46,9 @@ function mask(s, t) {
|
||||
while (match(substr(line, p), /`+/)) {
|
||||
pstart = p + RSTART - 1
|
||||
plen = RLENGTH
|
||||
if (plen >= 3) {
|
||||
out = out substr(line, p, pstart - p + plen)
|
||||
p = pstart + plen
|
||||
continue
|
||||
}
|
||||
|
||||
# Found 1 or 2 backticks at pstart
|
||||
# Search for closing marker
|
||||
# Found backtick sequence at pstart
|
||||
# Search for closing marker of same length
|
||||
marker = substr(line, pstart, plen)
|
||||
tail = substr(line, pstart + plen)
|
||||
mpos = index(tail, marker)
|
||||
@@ -69,9 +64,20 @@ function mask(s, t) {
|
||||
# Found match!
|
||||
content = substr(tail, 1, mpos - 1)
|
||||
out = out substr(line, p, pstart - p)
|
||||
if (plen == 2 && substr(content, 1, 1) == " " && substr(content, length(content), 1) == " ") {
|
||||
if (plen >= 2 && substr(content, 1, 1) == " " && substr(content, length(content), 1) == " ") {
|
||||
content = substr(content, 2, length(content) - 2)
|
||||
}
|
||||
if (content ~ /!!\[/) {
|
||||
_rb_test = content
|
||||
gsub(/!!\[[^\]]*\]\([^)]*\)/, "", _rb_test)
|
||||
gsub(/!!\[[^\]]+\]/, "", _rb_test)
|
||||
gsub(/[[:space:]]+/, "", _rb_test)
|
||||
if (_rb_test == "") {
|
||||
out = out content
|
||||
p = pstart + plen + mpos + plen - 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
out = out "<code>" mask(content) "</code>"
|
||||
p = pstart + plen + mpos + plen - 1
|
||||
} else {
|
||||
|
||||
@@ -50,7 +50,7 @@ END {
|
||||
in_pre = 0
|
||||
i = 1
|
||||
while (i <= count) {
|
||||
if (lines[i] ~ /^<pre><code>/) {
|
||||
if (lines[i] ~ /^<pre><code/) {
|
||||
in_pre = 1
|
||||
print lines[i]
|
||||
i++
|
||||
|
||||
BIN
button.gif
BIN
button.gif
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 1.5 KiB |
49
button.svg
Normal file
49
button.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="88"
|
||||
height="31"
|
||||
viewBox="0 0 88 31"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id="bg-grad" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#4a3b69" />
|
||||
<stop offset="100%" stop-color="#352654" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="88" height="31" fill="#000" />
|
||||
<rect x="1" y="1" width="86" height="29" fill="url(#bg-grad)" />
|
||||
<text
|
||||
x="5"
|
||||
y="12"
|
||||
font-family="Georgia, 'Times New Roman', Times, serif"
|
||||
font-size="10"
|
||||
font-weight="bold"
|
||||
fill="#debfff"
|
||||
style="letter-spacing: -0.5px;"
|
||||
>made</text>
|
||||
<text
|
||||
x="5"
|
||||
y="22"
|
||||
font-family="Georgia, 'Times New Roman', Times, serif"
|
||||
font-size="10"
|
||||
font-weight="bold"
|
||||
fill="#debfff"
|
||||
style="letter-spacing: -0.5px;"
|
||||
>with</text>
|
||||
<text
|
||||
x="80"
|
||||
y="16"
|
||||
text-anchor="end"
|
||||
dominant-baseline="central"
|
||||
font-family="Georgia, 'Times New Roman', Times, serif"
|
||||
font-size="28"
|
||||
font-weight="bold"
|
||||
font-style="italic"
|
||||
fill="#debfff"
|
||||
letter-spacing="-2"
|
||||
>kewt</text>
|
||||
<rect x="1" y="1" width="86" height="1" fill="#ffffff" fill-opacity="0.2" />
|
||||
<rect x="1" y="2" width="1" height="27" fill="#ffffff" fill-opacity="0.2" />
|
||||
<rect x="1" y="29" width="86" height="1" fill="#000000" fill-opacity="0.4" />
|
||||
<rect x="86" y="2" width="1" height="28" fill="#000000" fill-opacity="0.4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
725
lib/builder.sh
Normal file
725
lib/builder.sh
Normal file
@@ -0,0 +1,725 @@
|
||||
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
|
||||
[ -f "$script_dir/styles/$style.root.css" ] && [ "$script_dir/styles/$style.root.css" -nt "$out_file" ] && return 0
|
||||
[ -f "$src/styles.root.css" ] && [ "$src/styles.root.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|styles.root.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 "$src/styles.css" ] && [ ! -f "$src/style.css" ]; then
|
||||
if [ -f "$src/styles.root.css" ]; then
|
||||
_base_css="$script_dir/styles/$style.css"
|
||||
[ ! -f "$_base_css" ] && _base_css="$script_dir/styles/kewt.css"
|
||||
if [ ! -f "$out/styles.css" ] || [ "$src/styles.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
|
||||
merge_root_style "$src/styles.root.css" "$_base_css" "$out/styles.css"
|
||||
fi
|
||||
elif [ -f "$script_dir/styles/$style.css" ]; then
|
||||
if 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
|
||||
elif [ -f "$script_dir/styles/$style.root.css" ]; then
|
||||
_base_css="$script_dir/styles/kewt.css"
|
||||
if [ ! -f "$out/styles.css" ] || [ "$script_dir/styles/$style.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
|
||||
merge_root_style "$script_dir/styles/$style.root.css" "$_base_css" "$out/styles.css"
|
||||
fi
|
||||
fi
|
||||
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|styles.root.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
|
||||
|
||||
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
|
||||
if [ -z "$fm_content_warning" ] || [ "$include_cw_pages_in_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
|
||||
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."
|
||||
}
|
||||
142
lib/commands.sh
Normal file
142
lib/commands.sh
Normal file
@@ -0,0 +1,142 @@
|
||||
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, --init [title] Create a new site directory (default: site)
|
||||
--clean Clean the output directory before building (default).
|
||||
--no-clean Do not clean the output directory before building.
|
||||
--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
|
||||
}
|
||||
162
lib/config.sh
Normal file
162
lib/config.sh
Normal file
@@ -0,0 +1,162 @@
|
||||
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
|
||||
include_cw_pages_in_search = 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"
|
||||
include_cw_pages_in_search="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" ;;
|
||||
include_cw_pages_in_search) include_cw_pages_in_search="$val" ;;
|
||||
esac
|
||||
done < "$1"
|
||||
}
|
||||
310
lib/generator.sh
Normal file
310
lib/generator.sh
Normal file
@@ -0,0 +1,310 @@
|
||||
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"
|
||||
}
|
||||
merge_root_style() {
|
||||
root_file="$1"
|
||||
base_css="$2"
|
||||
out_file="$3"
|
||||
{
|
||||
cat "$root_file"
|
||||
awk '
|
||||
BEGIN { in_root = 0; brace_depth = 0 }
|
||||
/^:root[[:space:]]*\{/ { in_root = 1; brace_depth = 1; next }
|
||||
in_root {
|
||||
for (i = 1; i <= length($0); i++) {
|
||||
c = substr($0, i, 1)
|
||||
if (c == "{") brace_depth++
|
||||
if (c == "}") { brace_depth--; if (brace_depth == 0) { in_root = 0; next } }
|
||||
}
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$base_css"
|
||||
} | awk -f "$awk_dir/replace_variables.awk" > "$out_file"
|
||||
}
|
||||
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"
|
||||
}
|
||||
54
lib/search.js
Normal file
54
lib/search.js
Normal file
@@ -0,0 +1,54 @@
|
||||
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>";
|
||||
});
|
||||
});
|
||||
@@ -3,5 +3,7 @@
|
||||
"description": "A minimalist static site generator inspired by werc",
|
||||
"global": "true",
|
||||
"install": "make install",
|
||||
"scripts": ["kewt"]
|
||||
"scripts": [
|
||||
"kewt"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -26,4 +26,6 @@ build() {
|
||||
package() {
|
||||
cd "${pkgname}"
|
||||
install -Dm755 kewt "${pkgdir}/usr/bin/kewt"
|
||||
install -d "${pkgdir}/usr/share/zsh/site-functions"
|
||||
"${pkgdir}/usr/bin/kewt" --dump-zsh-completions > "${pkgdir}/usr/share/zsh/site-functions/_kewt"
|
||||
}
|
||||
|
||||
@@ -18,4 +18,6 @@ build() {
|
||||
|
||||
package() {
|
||||
install -Dm755 "${srcdir}/${pkgname}-${pkgver}.sh" "${pkgdir}/usr/bin/kewt"
|
||||
install -d "${pkgdir}/usr/share/zsh/site-functions"
|
||||
"${pkgdir}/usr/bin/kewt" --dump-zsh-completions > "${pkgdir}/usr/share/zsh/site-functions/_kewt"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ class Kewt < Formula
|
||||
|
||||
def install
|
||||
bin.install "kewt"
|
||||
generate_completions_from_executable(bin/"kewt", "--dump-zsh-completions", shells: [:zsh])
|
||||
end
|
||||
|
||||
test do
|
||||
|
||||
25
packaging/zsh/_kewt
Normal file
25
packaging/zsh/_kewt
Normal file
@@ -0,0 +1,25 @@
|
||||
#compdef kewt
|
||||
|
||||
_kewt() {
|
||||
local -a args
|
||||
args=(
|
||||
'--help[Show help message]'
|
||||
'(-h)--help[Show help message]'
|
||||
'(-)--new[Create a new site directory]'
|
||||
'(-)--init[Create a new site directory (alias for --new)]'
|
||||
'(-)--clean[Clean the output directory before building]'
|
||||
'(-)--no-clean[Do not clean the output directory before building]'
|
||||
'(-)--update[Update site.conf and template.html with latest defaults]'
|
||||
'(-)--post[Create a new empty post file in the configured posts_dir]'
|
||||
'(-)--generate-template[Generate a new template file]'
|
||||
'(-v --version)'{-v,--version}'[Show version information]'
|
||||
'--from[Source directory]:directory:_directories'
|
||||
'--to[Output directory]:directory:_directories'
|
||||
'(-w --watch)'{-w,--watch}'[Watch for file changes and rebuild automatically]'
|
||||
'(-s --serve)'{-s,--serve}'[Start a local HTTP server after building]::port:'
|
||||
)
|
||||
|
||||
_arguments -S -C $args '*: :_directories'
|
||||
}
|
||||
|
||||
_kewt "$@"
|
||||
BIN
site/button.gif
BIN
site/button.gif
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -2,6 +2,8 @@
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus malesuada lacus eu ligula semper pharetra. Cras viverra volutpat massa nec sagittis. Aliquam fringilla quam ut tincidunt ultricies. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed id sagittis nisi. Aenean vitae urna justo. Vivamus dictum eros ac mi convallis, blandit hendrerit nunc sagittis. Mauris feugiat neque quis justo molestie, vel pulvinar diam faucibus. Ut tempus magna sit amet ex pharetra mollis. Ut elementum metus metus, id consectetur est tempor eget. Proin sed nibh tincidunt, porttitor elit non, blandit ligula. Ut condimentum accumsan lobortis. Nullam nec tempus leo, sit amet iaculis erat. Donec rutrum, orci in elementum varius, nisl elit rutrum nunc, in lacinia lorem enim non enim.
|
||||
|
||||
@@ -8,6 +8,8 @@ title = "Configuration"
|
||||
```conf
|
||||
title = "kewt"
|
||||
style = "kewt"
|
||||
lang = "en"
|
||||
draft_by_default = false
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
@@ -33,9 +35,17 @@ 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
|
||||
include_cw_pages_in_search = false
|
||||
```
|
||||
- `title` - site title
|
||||
- `style` - style file name from `./styles` (without `.css`)
|
||||
- `style` - style name from the built-in `styles/` directory. See [Theming](theming.md)
|
||||
- `lang` - document language, used for the `<html lang="...">` attribute (default: "en")
|
||||
- `draft_by_default` - default value for the `draft` frontmatter field in new posts created (default: false)
|
||||
- `dir_indexes` - generate directory index pages when missing `index.md`
|
||||
- `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
|
||||
@@ -53,14 +63,20 @@ cw_hide_url = true
|
||||
- `generate_page_title` - automatically generate title text from the first markdown heading or filename (default: true)
|
||||
- `error_page` - filename for the generated 404 error page (default: "not_found.html", empty to disable)
|
||||
- `versioning` - append a version query parameter (`?v=timestamp`) to css asset urls to bypass cache (default: false)
|
||||
- `enable_header_links` - turns markdown section headings into clickable anchor links (default: true)
|
||||
- `base_url` - absolute URL of the site, used for sitemap and RSS feed generation
|
||||
- `generate_feed` - enable RSS feed generation (requires `base_url`)
|
||||
- `feed_file` - filename for the generated RSS feed (default: "rss.xml")
|
||||
- `posts_dir` - directory name containing posts (e.g., "posts"). Enables reverse-chronological sorting, title headings in indexes, and automatic backlinks.
|
||||
- `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)
|
||||
- `generate_tags` - generate tag index pages from post frontmatter (requires `posts_dir`)
|
||||
- `tags_dir` - directory name for generated tag pages (default: "tags")
|
||||
- `generate_search` - generate a `search.json` index for client-side search
|
||||
- `search_in_footer` - include a search box in the page footer (requires `generate_search`)
|
||||
- `search_in_header` - include a search box in the page header (requires `generate_search`)
|
||||
- `include_cw_pages_in_search` - include content warning pages in the search index (default: false)
|
||||
|
||||
## Dot Files
|
||||
|
||||
|
||||
@@ -13,6 +13,14 @@ title = "Embeds"
|
||||
|
||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||
|
||||
## Reality-Breaking Embeds
|
||||
|
||||
`\!![link]` and `\!` work even inside inline code blocks. If the content between backticks consists only of `\!![]` embeds, the embed triggers and the content is inlined instead of being rendered as code.
|
||||
|
||||
```
|
||||
`!![/file.sh]`
|
||||
```
|
||||
|
||||
## Typed Embeds
|
||||
|
||||
Force specific output regardless of extension:
|
||||
|
||||
@@ -11,10 +11,12 @@ title = "Custom Page Title"
|
||||
date = "2026-03-23 11:32"
|
||||
draft = false
|
||||
description = "A short page summary"
|
||||
tags = "example, tutorial"
|
||||
---
|
||||
```
|
||||
- `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.
|
||||
- `draft` - if `true`, the file is excluded from HTML generation. If not set, uses the `draft_by_default` config value.
|
||||
- `description` - page description, used for Open Graph `og:description` meta tag.
|
||||
- `tags` - comma separated list of tags. Used for tag index generation when `generate_tags` is enabled in `site.conf`.
|
||||
- `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.
|
||||
|
||||
@@ -39,3 +39,82 @@ This renders as `<dl><dt>Term</dt><dd>Definition</dd></dl>`. Multiple definition
|
||||
## 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.
|
||||
|
||||
## Pipe Tables
|
||||
|
||||
Tables use the GitHub-style syntax:
|
||||
|
||||
```md
|
||||
| Header 1 | Header 2 |
|
||||
|---|---|
|
||||
| cell 1 | cell 2 |
|
||||
| cell 3 | cell 4 |
|
||||
```
|
||||
Column alignment is set with colons in the separator:
|
||||
|
||||
```md
|
||||
| Left | Center | Right |
|
||||
|:---|:---:|---:|
|
||||
| a | b | c |
|
||||
```
|
||||
Tables can drop the header row and with a separator:
|
||||
|
||||
```md
|
||||
|---|---|
|
||||
| a | b |
|
||||
```
|
||||
## Blockquotes
|
||||
|
||||
Standard Markdown blockquote syntax using `>`:
|
||||
|
||||
```md
|
||||
> This is a blockquote.
|
||||
> It can span multiple lines.
|
||||
```
|
||||
### Admonitions
|
||||
|
||||
Blockquotes that start with a type tag become styled admonition blocks. Five built-in types are supported: `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`.
|
||||
|
||||
```md
|
||||
> [!NOTE]
|
||||
> This is a note admonition.
|
||||
```
|
||||
Custom admonition types can be added via the `custom_admonitions` config option in `site.conf`.
|
||||
|
||||
## Task Lists
|
||||
|
||||
GFM-style task lists are supported inside **both** ordered and unordered lists:
|
||||
|
||||
```md
|
||||
- [ ] Unchecked item
|
||||
- [x] Checked item
|
||||
- Normal item
|
||||
|
||||
1. [ ] Unchecked item
|
||||
2. [x] Checked item
|
||||
3. Normal item
|
||||
```
|
||||
## Reference Links
|
||||
|
||||
Markdown reference-style links and images are supported:
|
||||
|
||||
```md
|
||||
[link text][ref]
|
||||
|
||||
[ref]: https://example.com "Optional title"
|
||||
|
||||
![alt text][img-ref]
|
||||
|
||||
[img-ref]: /image.png "Optional title"
|
||||
```
|
||||
## Plain Text Blocks
|
||||
|
||||
Content inside `<plain>...</plain>` tags is rendered without any Markdown processing
|
||||
|
||||
## MFM Font Syntax
|
||||
|
||||
Misskey-style font syntax is supported for inline font family changes:
|
||||
|
||||
- `$[font.serif text]` - serif font
|
||||
- `$[font.mono text]` - monospace font
|
||||
- `$[font.sans text]` - sans-serif font
|
||||
|
||||
32
site/docs/quickstart.md
Normal file
32
site/docs/quickstart.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title = "Quickstart"
|
||||
---
|
||||
# Quickstart
|
||||
|
||||
## Creating a site
|
||||
|
||||
```sh
|
||||
kewt --new mysite
|
||||
cd mysite
|
||||
```
|
||||
This creates a directory with a `site.conf`, `template.html`, and `index.md`.
|
||||
|
||||
## Writing content
|
||||
|
||||
Edit `index.md` (or any `.md` file) and just write markdown as usual. Files in subdirectories are added to the navigation automatically.
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
kewt src out # Replace with the directories you want
|
||||
```
|
||||
Reads from `src` and writes static HTML to `out`.
|
||||
|
||||
## Previewing
|
||||
|
||||
```sh
|
||||
kewt --serve
|
||||
```
|
||||
Builds the site and starts a local HTTP server. Use `--watch` with `--serve` to rebuild automatically on file changes.
|
||||
|
||||
## That's it, if you want to do anything more, look at [the documentation](/docs)
|
||||
@@ -13,3 +13,7 @@ When customizing `template.html`, the placeholders available are:
|
||||
- `{{LANG}}` - the configured document language
|
||||
- `{{HEAD_EXTRA}}` - meta-tags
|
||||
- `{{HEADER_BRAND}}` - header rendering the name and/or logo
|
||||
|
||||
## Search
|
||||
|
||||
When `generate_search` is enabled, kewt embeds a search bar into pages based on the `search_in_header` and `search_in_footer` config options. The search uses a `search.json` index generated at build time and a client-side JS script. No external dependencies are required.
|
||||
|
||||
60
site/docs/theming.md
Normal file
60
site/docs/theming.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title = "Theming"
|
||||
---
|
||||
# Theming
|
||||
|
||||
*kewt* has a few colour palettes built-in. Set the `style` option in `site.conf` to a theme name to apply it.
|
||||
|
||||
## Built-in Themes
|
||||
|
||||
| Theme | `style` value | Dark/Light |
|
||||
|---|---|---|
|
||||
| Kewt (default) | `kewt` | Light |
|
||||
| Kewt Light | `kewt-light` | Light |
|
||||
| Nord | `nord` | Dark |
|
||||
| Nord Light | `nord-light` | Light |
|
||||
| Monokai | `mono` | Dark |
|
||||
| Monokai Light | `mono-light` | Light |
|
||||
| One Dark | `onedark` | Dark |
|
||||
| One Light | `onelight` | Light |
|
||||
| Rose Pine | `rosepine` | Dark |
|
||||
| Rose Pine Light | `rosepine-light` | Light |
|
||||
| Solarized | `solarized` | Light |
|
||||
| Solarized Dark | `solarized-dark` | Dark |
|
||||
|
||||
```conf
|
||||
style = "kewt-light"
|
||||
```
|
||||
## How It Works
|
||||
|
||||
Each theme is a `.root.css` file containing a `:root` block with CSS custom properties. At build time, *kewt* merges the theme's variables with the base `kewt.css` stylesheet. The base `:root` block is stripped out and replaced with the theme's variables.
|
||||
|
||||
## Style Resolution
|
||||
|
||||
*kewt* resolves styles in this priority order (highest wins):
|
||||
|
||||
1. `site/styles.css` - a full custom stylesheet in your site directory. Overrides everything.
|
||||
2. `site/styles.root.css` - custom `:root` variables merged with the built-in `kewt.css` base.
|
||||
3. built-in `<style>.css` - a full stylesheet matching the `style` config value.
|
||||
4. built-in `<style>.root.css` - `:root` variables merged with `kewt.css`.
|
||||
|
||||
If none of these exist, the unmodified `kewt.css` is used
|
||||
|
||||
## Custom Themes
|
||||
|
||||
To create a custom colour theme, place a `styles.root.css` file in your site directory. The file should contain only a `:root` block with the CSS variables you want to override:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg: #1a1b26;
|
||||
--fg: #c0caf5;
|
||||
--fg-link: #7aa2f7;
|
||||
--fg-heading: #c0caf5;
|
||||
--code-bg: #24283b;
|
||||
}
|
||||
```
|
||||
Any variables not overridden will fall back to the defaults in `kewt.css`. The `:root` block in the base stylesheet is automatically removed to prevent conflicts.
|
||||
|
||||
## Per-Directory Styles
|
||||
|
||||
Subdirectories can have their own `styles.css` or `styles.root.css` that apply only to pages in that directory. Per-directory styles follow the same priority.
|
||||
@@ -20,4 +20,6 @@ kewt --serve [port]
|
||||
- `--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.
|
||||
- `--clean` cleans the output directory before building (default behavior).
|
||||
- `--no-clean` does not clean the output directory before building. Useful with `--watch` to avoid clearing output on every rebuild.
|
||||
- `--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`.
|
||||
|
||||
@@ -9,4 +9,4 @@ Told you
|
||||
Probably should've mentioned the catgirl too
|
||||
|
||||
| --- | --- |
|
||||
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||
| ```!![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||
|
||||
@@ -41,5 +41,4 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
||||
|
||||
***
|
||||
|
||||
> [!WARNING]
|
||||
> The base that all of this is built upon was coded at night, while sleepy and a bit sick, and after walking for about 4 hours around a forest, so...
|
||||
## [Quickstart guide](/docs/quickstart.html)
|
||||
|
||||
@@ -23,3 +23,8 @@ custom_admonitions = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
generate_tags = false
|
||||
generate_search = true
|
||||
search_in_footer = true
|
||||
search_in_header = true
|
||||
include_cw_pages_in_search = false
|
||||
|
||||
27
styles/kewt-light.root.css
Normal file
27
styles/kewt-light.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #f5f0ff;
|
||||
--bg-deep: #e8dffa;
|
||||
--fg: #2d1b4e;
|
||||
--fg-muted: #7a6898;
|
||||
--fg-link: #7b3fba;
|
||||
--fg-heading: #3d2466;
|
||||
--code-bg: #e8dffa;
|
||||
--code-border: #d0c0e8;
|
||||
--code-fg: #2d1b4e;
|
||||
--code-sel: #b8860b;
|
||||
--code-prop: #6f42c1;
|
||||
--code-val: #0366d6;
|
||||
--code-var: #22863a;
|
||||
--code-com: #7a6898;
|
||||
--adm-note-bg: #e0e0f8;
|
||||
--adm-note-border: #5b6fc4;
|
||||
--adm-tip-bg: #ddf0dd;
|
||||
--adm-tip-border: #46a758;
|
||||
--adm-important-bg: #eeddf5;
|
||||
--adm-important-border: #8b5fc7;
|
||||
--adm-warning-bg: #f5f0dd;
|
||||
--adm-warning-border: #b8860b;
|
||||
--adm-caution-bg: #f5dddd;
|
||||
--adm-caution-border: #c44569;
|
||||
--thead-bg: #e8dffa;
|
||||
}
|
||||
244
styles/kewt.css
244
styles/kewt.css
@@ -23,6 +23,7 @@
|
||||
--adm-warning-border: #ffe2bd;
|
||||
--adm-caution-bg: #662d43;
|
||||
--adm-caution-border: #ffc4d5;
|
||||
--thead-bg: #3d2d5c;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -48,6 +49,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 +222,7 @@ pre code {
|
||||
background: var(--adm-caution-bg);
|
||||
border-color: var(--adm-caution-border);
|
||||
}
|
||||
|
||||
.cw-button {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
@@ -267,6 +272,32 @@ hr {
|
||||
border-top: 1px solid var(--code-border);
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
thead {
|
||||
border-bottom: 2px solid var(--code-border);
|
||||
background: var(--thead-bg);
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px 12px;
|
||||
border-top: 1px solid var(--code-border);
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background: var(--bg-deep);
|
||||
}
|
||||
|
||||
.nav-toggle,
|
||||
.nav-toggle-label {
|
||||
display: none;
|
||||
@@ -299,7 +330,7 @@ hr {
|
||||
margin: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.nav-toggle:checked~#side-bar {
|
||||
.nav-toggle:checked ~ #side-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -322,3 +353,214 @@ hr {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
27
styles/mono-light.root.css
Normal file
27
styles/mono-light.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #f5f5f5;
|
||||
--bg-deep: #e8e8e8;
|
||||
--fg: #1a1a1a;
|
||||
--fg-muted: #808080;
|
||||
--fg-link: #333333;
|
||||
--fg-heading: #000000;
|
||||
--code-bg: #e8e8e8;
|
||||
--code-border: #c0c0c0;
|
||||
--code-fg: #1a1a1a;
|
||||
--code-sel: #555555;
|
||||
--code-prop: #333333;
|
||||
--code-val: #666666;
|
||||
--code-var: #444444;
|
||||
--code-com: #999999;
|
||||
--adm-note-bg: #e0e0ee;
|
||||
--adm-note-border: #8888aa;
|
||||
--adm-tip-bg: #e0eee0;
|
||||
--adm-tip-border: #88aa88;
|
||||
--adm-important-bg: #eee0ee;
|
||||
--adm-important-border: #aa88aa;
|
||||
--adm-warning-bg: #eeeed0;
|
||||
--adm-warning-border: #aaaa78;
|
||||
--adm-caution-bg: #eee0e0;
|
||||
--adm-caution-border: #aa8888;
|
||||
--thead-bg: #e8e8e8;
|
||||
}
|
||||
27
styles/mono.root.css
Normal file
27
styles/mono.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #1a1a1a;
|
||||
--bg-deep: #111111;
|
||||
--fg: #d4d4d4;
|
||||
--fg-muted: #808080;
|
||||
--fg-link: #e0e0e0;
|
||||
--fg-heading: #ffffff;
|
||||
--code-bg: #0d0d0d;
|
||||
--code-border: #404040;
|
||||
--code-fg: #d4d4d4;
|
||||
--code-sel: #b8b8b8;
|
||||
--code-prop: #e0e0e0;
|
||||
--code-val: #a0a0a0;
|
||||
--code-var: #c8c8c8;
|
||||
--code-com: #585858;
|
||||
--adm-note-bg: #1a1a2e;
|
||||
--adm-note-border: #5a5a8a;
|
||||
--adm-tip-bg: #1a2e1a;
|
||||
--adm-tip-border: #5a8a5a;
|
||||
--adm-important-bg: #2e1a2e;
|
||||
--adm-important-border: #8a5a8a;
|
||||
--adm-warning-bg: #2e2e1a;
|
||||
--adm-warning-border: #8a8a5a;
|
||||
--adm-caution-bg: #2e1a1a;
|
||||
--adm-caution-border: #8a5a5a;
|
||||
--thead-bg: #111111;
|
||||
}
|
||||
27
styles/nord-light.root.css
Normal file
27
styles/nord-light.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #eceff4;
|
||||
--bg-deep: #d8dee9;
|
||||
--fg: #2e3440;
|
||||
--fg-muted: #4c566a;
|
||||
--fg-link: #5e81ac;
|
||||
--fg-heading: #3b4252;
|
||||
--code-bg: #d8dee9;
|
||||
--code-border: #c5cdd9;
|
||||
--code-fg: #2e3440;
|
||||
--code-sel: #d08770;
|
||||
--code-prop: #5e81ac;
|
||||
--code-val: #8fbcbb;
|
||||
--code-var: #a3be8c;
|
||||
--code-com: #4c566a;
|
||||
--adm-note-bg: #d8dee9;
|
||||
--adm-note-border: #5e81ac;
|
||||
--adm-tip-bg: #e0ebd8;
|
||||
--adm-tip-border: #a3be8c;
|
||||
--adm-important-bg: #e5dbe8;
|
||||
--adm-important-border: #b48ead;
|
||||
--adm-warning-bg: #ede5d6;
|
||||
--adm-warning-border: #ebcb8b;
|
||||
--adm-caution-bg: #eddcdc;
|
||||
--adm-caution-border: #bf616a;
|
||||
--thead-bg: #d8dee9;
|
||||
}
|
||||
27
styles/nord.root.css
Normal file
27
styles/nord.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #2e3440;
|
||||
--bg-deep: #242933;
|
||||
--fg: #d8dee9;
|
||||
--fg-muted: #a5b0c1;
|
||||
--fg-link: #88c0d0;
|
||||
--fg-heading: #eceff4;
|
||||
--code-bg: #3b4252;
|
||||
--code-border: #4c566a;
|
||||
--code-fg: #d8dee9;
|
||||
--code-sel: #ebcb8b;
|
||||
--code-prop: #8fbcbb;
|
||||
--code-val: #81a1c1;
|
||||
--code-var: #a3be8c;
|
||||
--code-com: #616e88;
|
||||
--adm-note-bg: #3b4252;
|
||||
--adm-note-border: #88c0d0;
|
||||
--adm-tip-bg: #3b4340;
|
||||
--adm-tip-border: #a3be8c;
|
||||
--adm-important-bg: #3b4044;
|
||||
--adm-important-border: #b48ead;
|
||||
--adm-warning-bg: #3b4038;
|
||||
--adm-warning-border: #ebcb8b;
|
||||
--adm-caution-bg: #3b3840;
|
||||
--adm-caution-border: #bf616a;
|
||||
--thead-bg: #3b4252;
|
||||
}
|
||||
27
styles/onedark.root.css
Normal file
27
styles/onedark.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #282c34;
|
||||
--bg-deep: #21252b;
|
||||
--fg: #abb2bf;
|
||||
--fg-muted: #636d83;
|
||||
--fg-link: #61afef;
|
||||
--fg-heading: #c8ccd4;
|
||||
--code-bg: #21252b;
|
||||
--code-border: #3e4451;
|
||||
--code-fg: #abb2bf;
|
||||
--code-sel: #e5c07b;
|
||||
--code-prop: #e06c75;
|
||||
--code-val: #56b6c2;
|
||||
--code-var: #98c379;
|
||||
--code-com: #5c6370;
|
||||
--adm-note-bg: #2c313c;
|
||||
--adm-note-border: #61afef;
|
||||
--adm-tip-bg: #2c3a30;
|
||||
--adm-tip-border: #98c379;
|
||||
--adm-important-bg: #33303c;
|
||||
--adm-important-border: #c678dd;
|
||||
--adm-warning-bg: #3a352c;
|
||||
--adm-warning-border: #e5c07b;
|
||||
--adm-caution-bg: #3a2c2e;
|
||||
--adm-caution-border: #e06c75;
|
||||
--thead-bg: #21252b;
|
||||
}
|
||||
27
styles/onelight.root.css
Normal file
27
styles/onelight.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #fafafa;
|
||||
--bg-deep: #eaeaeb;
|
||||
--fg: #383a42;
|
||||
--fg-muted: #a0a1a7;
|
||||
--fg-link: #4078f2;
|
||||
--fg-heading: #383a42;
|
||||
--code-bg: #eaeaeb;
|
||||
--code-border: #d4d4d5;
|
||||
--code-fg: #383a42;
|
||||
--code-sel: #986801;
|
||||
--code-prop: #e45649;
|
||||
--code-val: #0184bc;
|
||||
--code-var: #50a14f;
|
||||
--code-com: #a0a1a7;
|
||||
--adm-note-bg: #e8eefa;
|
||||
--adm-note-border: #4078f2;
|
||||
--adm-tip-bg: #e8f5e8;
|
||||
--adm-tip-border: #50a14f;
|
||||
--adm-important-bg: #f2e8f5;
|
||||
--adm-important-border: #a626a4;
|
||||
--adm-warning-bg: #f5f0e0;
|
||||
--adm-warning-border: #986801;
|
||||
--adm-caution-bg: #fae8e8;
|
||||
--adm-caution-border: #e45649;
|
||||
--thead-bg: #eaeaeb;
|
||||
}
|
||||
27
styles/rosepine-light.root.css
Normal file
27
styles/rosepine-light.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #faf4ed;
|
||||
--bg-deep: #f2e9e1;
|
||||
--fg: #575279;
|
||||
--fg-muted: #9893a5;
|
||||
--fg-link: #907aa9;
|
||||
--fg-heading: #286983;
|
||||
--code-bg: #f2e9e1;
|
||||
--code-border: #dfdad9;
|
||||
--code-fg: #575279;
|
||||
--code-sel: #ea9d34;
|
||||
--code-prop: #b4637a;
|
||||
--code-val: #56949f;
|
||||
--code-var: #286983;
|
||||
--code-com: #9893a5;
|
||||
--adm-note-bg: #f0e8f5;
|
||||
--adm-note-border: #907aa9;
|
||||
--adm-tip-bg: #e8f0ee;
|
||||
--adm-tip-border: #56949f;
|
||||
--adm-important-bg: #f0e8f0;
|
||||
--adm-important-border: #907aa9;
|
||||
--adm-warning-bg: #f5f0e0;
|
||||
--adm-warning-border: #ea9d34;
|
||||
--adm-caution-bg: #f5e5e8;
|
||||
--adm-caution-border: #b4637a;
|
||||
--thead-bg: #f2e9e1;
|
||||
}
|
||||
27
styles/rosepine.root.css
Normal file
27
styles/rosepine.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #191724;
|
||||
--bg-deep: #13111e;
|
||||
--fg: #e0def4;
|
||||
--fg-muted: #908caa;
|
||||
--fg-link: #c4a7e7;
|
||||
--fg-heading: #ebbcba;
|
||||
--code-bg: #1f1d2e;
|
||||
--code-border: #26233a;
|
||||
--code-fg: #e0def4;
|
||||
--code-sel: #f6c177;
|
||||
--code-prop: #eb6f92;
|
||||
--code-val: #9ccfd8;
|
||||
--code-var: #31748f;
|
||||
--code-com: #6e6a86;
|
||||
--adm-note-bg: #1f1d2e;
|
||||
--adm-note-border: #c4a7e7;
|
||||
--adm-tip-bg: #1a2332;
|
||||
--adm-tip-border: #9ccfd8;
|
||||
--adm-important-bg: #2a1f2e;
|
||||
--adm-important-border: #c4a7e7;
|
||||
--adm-warning-bg: #2a251f;
|
||||
--adm-warning-border: #f6c177;
|
||||
--adm-caution-bg: #2a1f22;
|
||||
--adm-caution-border: #eb6f92;
|
||||
--thead-bg: #1f1d2e;
|
||||
}
|
||||
27
styles/solarized-dark.root.css
Normal file
27
styles/solarized-dark.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #002b36;
|
||||
--bg-deep: #001e26;
|
||||
--fg: #839496;
|
||||
--fg-muted: #586e75;
|
||||
--fg-link: #268bd2;
|
||||
--fg-heading: #93a1a1;
|
||||
--code-bg: #073642;
|
||||
--code-border: #094959;
|
||||
--code-fg: #839496;
|
||||
--code-sel: #d33682;
|
||||
--code-prop: #268bd2;
|
||||
--code-val: #2aa198;
|
||||
--code-var: #859900;
|
||||
--code-com: #586e75;
|
||||
--adm-note-bg: #073642;
|
||||
--adm-note-border: #268bd2;
|
||||
--adm-tip-bg: #07382e;
|
||||
--adm-tip-border: #2aa198;
|
||||
--adm-important-bg: #2a0736;
|
||||
--adm-important-border: #d33682;
|
||||
--adm-warning-bg: #363007;
|
||||
--adm-warning-border: #b58900;
|
||||
--adm-caution-bg: #360a07;
|
||||
--adm-caution-border: #cb4b16;
|
||||
--thead-bg: #073642;
|
||||
}
|
||||
27
styles/solarized.root.css
Normal file
27
styles/solarized.root.css
Normal file
@@ -0,0 +1,27 @@
|
||||
:root {
|
||||
--bg: #fdf6e3;
|
||||
--bg-deep: #eee8d5;
|
||||
--fg: #657b83;
|
||||
--fg-muted: #93a1a1;
|
||||
--fg-link: #268bd2;
|
||||
--fg-heading: #586e75;
|
||||
--code-bg: #eee8d5;
|
||||
--code-border: #d3cbb7;
|
||||
--code-fg: #657b83;
|
||||
--code-sel: #d33682;
|
||||
--code-prop: #268bd2;
|
||||
--code-val: #2aa198;
|
||||
--code-var: #859900;
|
||||
--code-com: #93a1a1;
|
||||
--adm-note-bg: #eee8d5;
|
||||
--adm-note-border: #268bd2;
|
||||
--adm-tip-bg: #e8f5e0;
|
||||
--adm-tip-border: #859900;
|
||||
--adm-important-bg: #f0e8f5;
|
||||
--adm-important-border: #6c71c4;
|
||||
--adm-warning-bg: #fdf0e3;
|
||||
--adm-warning-border: #b58900;
|
||||
--adm-caution-bg: #fde8e8;
|
||||
--adm-caution-border: #dc322f;
|
||||
--thead-bg: #eee8d5;
|
||||
}
|
||||
@@ -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