Compare commits
19 Commits
v1.4.2
...
2a03859390
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a03859390 | |||
| b65c4c6665 | |||
| 90d8e25b70 | |||
| 8814c12480 | |||
| 0c0f249226 | |||
| b29a5274e1 | |||
| 00f4bbb5f0 | |||
| 7fe204f9f9 | |||
| 81d3caff45 | |||
| 100979d28a | |||
| d35ddcf487 | |||
| aac9198878 | |||
| 99e1f5dd24 | |||
| 3970c6eb47 | |||
| 3075719963 | |||
| 0379d38234 | |||
| b22897135e | |||
| 185cf769c3 | |||
| b2acd26660 |
@@ -3,7 +3,11 @@ name: Lint
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, master]
|
branches: [main, master]
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**.sh'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
shellcheck:
|
shellcheck:
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,2 @@
|
|||||||
out/
|
out/
|
||||||
kewt
|
kewt
|
||||||
site.conf
|
|
||||||
template.html
|
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -1,5 +1,6 @@
|
|||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BINDIR = $(PREFIX)/bin
|
BINDIR = $(PREFIX)/bin
|
||||||
|
ZSHCOMPDIR ?= $(PREFIX)/share/zsh/site-functions
|
||||||
|
|
||||||
all: kewt
|
all: kewt
|
||||||
|
|
||||||
@@ -9,9 +10,12 @@ kewt:
|
|||||||
install: kewt
|
install: kewt
|
||||||
install -d $(DESTDIR)$(BINDIR)
|
install -d $(DESTDIR)$(BINDIR)
|
||||||
install -m 755 kewt $(DESTDIR)$(BINDIR)/kewt
|
install -m 755 kewt $(DESTDIR)$(BINDIR)/kewt
|
||||||
|
install -d $(DESTDIR)$(ZSHCOMPDIR)
|
||||||
|
install -m 644 packaging/zsh/_kewt $(DESTDIR)$(ZSHCOMPDIR)/_kewt
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f $(DESTDIR)$(BINDIR)/kewt
|
rm -f $(DESTDIR)$(BINDIR)/kewt
|
||||||
|
rm -f $(DESTDIR)$(ZSHCOMPDIR)/_kewt
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f kewt
|
rm -f kewt
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||||
|
|
||||||
|
|
||||||
## [Installation](https://kewt.krzak.org/#installation)
|
## [Installation](https://kewt.krzak.org/docs/installation)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
62
awk/definition_lists.awk
Normal file
62
awk/definition_lists.awk
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
BEGIN {
|
||||||
|
in_dl = 0
|
||||||
|
has_prev = 0
|
||||||
|
prev_line = ""
|
||||||
|
in_pre = 0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if ($0 ~ /^<pre>/) {
|
||||||
|
if (!in_pre) in_pre = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_pre && $0 ~ /^:[ \t]+[^ \t]/) {
|
||||||
|
if (!in_dl) {
|
||||||
|
in_dl = 1
|
||||||
|
print "<dl>"
|
||||||
|
print "<dt>" prev_line "</dt>"
|
||||||
|
has_prev = 0
|
||||||
|
} else {
|
||||||
|
if (has_prev && prev_line != "") {
|
||||||
|
print "<dt>" prev_line "</dt>"
|
||||||
|
has_prev = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def_text = $0
|
||||||
|
sub(/^:[ \t]+/, "", def_text)
|
||||||
|
print "<dd>" def_text "</dd>"
|
||||||
|
|
||||||
|
if ($0 ~ /<\/pre>/) {
|
||||||
|
if (in_pre) in_pre = 0
|
||||||
|
}
|
||||||
|
next
|
||||||
|
} else {
|
||||||
|
if (in_dl) {
|
||||||
|
if ($0 == "") {
|
||||||
|
# End of definition list
|
||||||
|
print "</dl>"
|
||||||
|
in_dl = 0
|
||||||
|
print ""
|
||||||
|
has_prev = 0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_prev) {
|
||||||
|
print prev_line
|
||||||
|
}
|
||||||
|
prev_line = $0
|
||||||
|
has_prev = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($0 ~ /<\/pre>/) {
|
||||||
|
if (in_pre) in_pre = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if (in_dl) {
|
||||||
|
print "</dl>"
|
||||||
|
} else {
|
||||||
|
if (has_prev) {
|
||||||
|
print prev_line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
857
awk/emoji.awk
Normal file
857
awk/emoji.awk
Normal file
@@ -0,0 +1,857 @@
|
|||||||
|
BEGIN {
|
||||||
|
map[":+1:"] = "👍"
|
||||||
|
map[":100:"] = "💯"
|
||||||
|
map[":1234:"] = "🔢"
|
||||||
|
map[":8ball:"] = "🎱"
|
||||||
|
map[":a:"] = "🅰️"
|
||||||
|
map[":ab:"] = "🆎"
|
||||||
|
map[":abc:"] = "🔤"
|
||||||
|
map[":abcd:"] = "🔡"
|
||||||
|
map[":accept:"] = "🉑"
|
||||||
|
map[":aerial_tramway:"] = "🚡"
|
||||||
|
map[":airplane:"] = "✈️"
|
||||||
|
map[":alarm_clock:"] = "⏰"
|
||||||
|
map[":alien:"] = "👽"
|
||||||
|
map[":ambulance:"] = "🚑"
|
||||||
|
map[":anchor:"] = "⚓"
|
||||||
|
map[":angel:"] = "👼"
|
||||||
|
map[":anger:"] = "💢"
|
||||||
|
map[":angry:"] = "😠"
|
||||||
|
map[":anguished:"] = "😧"
|
||||||
|
map[":ant:"] = "🐜"
|
||||||
|
map[":apple:"] = "🍎"
|
||||||
|
map[":aquarius:"] = "♒"
|
||||||
|
map[":aries:"] = "♈"
|
||||||
|
map[":arrow_backward:"] = "◀️"
|
||||||
|
map[":arrow_double_down:"] = "⏬"
|
||||||
|
map[":arrow_double_up:"] = "⏫"
|
||||||
|
map[":arrow_down:"] = "⬇️"
|
||||||
|
map[":arrow_down_small:"] = "🔽"
|
||||||
|
map[":arrow_forward:"] = "▶️"
|
||||||
|
map[":arrow_heading_down:"] = "⤵️"
|
||||||
|
map[":arrow_heading_up:"] = "⤴️"
|
||||||
|
map[":arrow_left:"] = "⬅️"
|
||||||
|
map[":arrow_lower_left:"] = "↙️"
|
||||||
|
map[":arrow_lower_right:"] = "↘️"
|
||||||
|
map[":arrow_right:"] = "➡️"
|
||||||
|
map[":arrow_right_hook:"] = "↪️"
|
||||||
|
map[":arrow_up:"] = "⬆️"
|
||||||
|
map[":arrow_up_down:"] = "↕️"
|
||||||
|
map[":arrow_up_small:"] = "🔼"
|
||||||
|
map[":arrow_upper_left:"] = "↖️"
|
||||||
|
map[":arrow_upper_right:"] = "↗️"
|
||||||
|
map[":arrows_clockwise:"] = "🔃"
|
||||||
|
map[":arrows_counterclockwise:"] = "🔄"
|
||||||
|
map[":art:"] = "🎨"
|
||||||
|
map[":articulated_lorry:"] = "🚛"
|
||||||
|
map[":astonished:"] = "😲"
|
||||||
|
map[":atm:"] = "🏧"
|
||||||
|
map[":b:"] = "🅱️"
|
||||||
|
map[":baby:"] = "👶"
|
||||||
|
map[":baby_bottle:"] = "🍼"
|
||||||
|
map[":baby_chick:"] = "🐤"
|
||||||
|
map[":baby_symbol:"] = "🚼"
|
||||||
|
map[":baggage_claim:"] = "🛄"
|
||||||
|
map[":balloon:"] = "🎈"
|
||||||
|
map[":ballot_box_with_check:"] = "☑️"
|
||||||
|
map[":bamboo:"] = "🎍"
|
||||||
|
map[":banana:"] = "🍌"
|
||||||
|
map[":bangbang:"] = "‼️"
|
||||||
|
map[":bank:"] = "🏦"
|
||||||
|
map[":bar_chart:"] = "📊"
|
||||||
|
map[":barber:"] = "💈"
|
||||||
|
map[":baseball:"] = "⚾"
|
||||||
|
map[":basketball:"] = "🏀"
|
||||||
|
map[":bath:"] = "🛀"
|
||||||
|
map[":bathtub:"] = "🛁"
|
||||||
|
map[":battery:"] = "🔋"
|
||||||
|
map[":bear:"] = "🐻"
|
||||||
|
map[":beer:"] = "🍺"
|
||||||
|
map[":beers:"] = "🍻"
|
||||||
|
map[":beetle:"] = "🪲"
|
||||||
|
map[":beginner:"] = "🔰"
|
||||||
|
map[":bell:"] = "🔔"
|
||||||
|
map[":bento:"] = "🍱"
|
||||||
|
map[":bicyclist:"] = "🚴"
|
||||||
|
map[":bike:"] = "🚲"
|
||||||
|
map[":bikini:"] = "👙"
|
||||||
|
map[":bird:"] = "🐦"
|
||||||
|
map[":birthday:"] = "🎂"
|
||||||
|
map[":black_circle:"] = "⚫"
|
||||||
|
map[":black_joker:"] = "🃏"
|
||||||
|
map[":black_nib:"] = "✒️"
|
||||||
|
map[":black_square_button:"] = "🔲"
|
||||||
|
map[":blossom:"] = "🌼"
|
||||||
|
map[":blowfish:"] = "🐡"
|
||||||
|
map[":blue_book:"] = "📘"
|
||||||
|
map[":blue_car:"] = "🚙"
|
||||||
|
map[":blue_heart:"] = "💙"
|
||||||
|
map[":blush:"] = "😊"
|
||||||
|
map[":boar:"] = "🐗"
|
||||||
|
map[":boat:"] = "⛵"
|
||||||
|
map[":bomb:"] = "💣"
|
||||||
|
map[":book:"] = "📖"
|
||||||
|
map[":bookmark:"] = "🔖"
|
||||||
|
map[":bookmark_tabs:"] = "📑"
|
||||||
|
map[":books:"] = "📚"
|
||||||
|
map[":boom:"] = "💥"
|
||||||
|
map[":boot:"] = "👢"
|
||||||
|
map[":bouquet:"] = "💐"
|
||||||
|
map[":bow:"] = "🙇"
|
||||||
|
map[":bowling:"] = "🎳"
|
||||||
|
map[":boy:"] = "👦"
|
||||||
|
map[":bread:"] = "🍞"
|
||||||
|
map[":bride_with_veil:"] = "👰♀️"
|
||||||
|
map[":bridge_at_night:"] = "🌉"
|
||||||
|
map[":briefcase:"] = "💼"
|
||||||
|
map[":broken_heart:"] = "💔"
|
||||||
|
map[":bug:"] = "🐛"
|
||||||
|
map[":bulb:"] = "💡"
|
||||||
|
map[":bullettrain_front:"] = "🚅"
|
||||||
|
map[":bullettrain_side:"] = "🚄"
|
||||||
|
map[":bus:"] = "🚌"
|
||||||
|
map[":busstop:"] = "🚏"
|
||||||
|
map[":bust_in_silhouette:"] = "👤"
|
||||||
|
map[":busts_in_silhouette:"] = "👥"
|
||||||
|
map[":cactus:"] = "🌵"
|
||||||
|
map[":cake:"] = "🍰"
|
||||||
|
map[":calendar:"] = "📆"
|
||||||
|
map[":calling:"] = "📲"
|
||||||
|
map[":camel:"] = "🐫"
|
||||||
|
map[":camera:"] = "📷"
|
||||||
|
map[":cancer:"] = "♋"
|
||||||
|
map[":candy:"] = "🍬"
|
||||||
|
map[":capital_abcd:"] = "🔠"
|
||||||
|
map[":capricorn:"] = "♑"
|
||||||
|
map[":car:"] = "🚗"
|
||||||
|
map[":card_index:"] = "📇"
|
||||||
|
map[":carousel_horse:"] = "🎠"
|
||||||
|
map[":cat2:"] = "🐈"
|
||||||
|
map[":cat:"] = "🐱"
|
||||||
|
map[":cd:"] = "💿"
|
||||||
|
map[":chart:"] = "💹"
|
||||||
|
map[":chart_with_downwards_trend:"] = "📉"
|
||||||
|
map[":chart_with_upwards_trend:"] = "📈"
|
||||||
|
map[":checkered_flag:"] = "🏁"
|
||||||
|
map[":cherries:"] = "🍒"
|
||||||
|
map[":cherry_blossom:"] = "🌸"
|
||||||
|
map[":chestnut:"] = "🌰"
|
||||||
|
map[":chicken:"] = "🐔"
|
||||||
|
map[":children_crossing:"] = "🚸"
|
||||||
|
map[":chocolate_bar:"] = "🍫"
|
||||||
|
map[":christmas_tree:"] = "🎄"
|
||||||
|
map[":church:"] = "⛪"
|
||||||
|
map[":cinema:"] = "🎦"
|
||||||
|
map[":circus_tent:"] = "🎪"
|
||||||
|
map[":city_sunrise:"] = "🌇"
|
||||||
|
map[":city_sunset:"] = "🌆"
|
||||||
|
map[":cl:"] = "🆑"
|
||||||
|
map[":clap:"] = "👏"
|
||||||
|
map[":clapper:"] = "🎬"
|
||||||
|
map[":clipboard:"] = "📋"
|
||||||
|
map[":clock1030:"] = "🕥"
|
||||||
|
map[":clock10:"] = "🕙"
|
||||||
|
map[":clock1130:"] = "🕦"
|
||||||
|
map[":clock11:"] = "🕚"
|
||||||
|
map[":clock1230:"] = "🕧"
|
||||||
|
map[":clock12:"] = "🕛"
|
||||||
|
map[":clock130:"] = "🕜"
|
||||||
|
map[":clock1:"] = "🕐"
|
||||||
|
map[":clock230:"] = "🕝"
|
||||||
|
map[":clock2:"] = "🕑"
|
||||||
|
map[":clock330:"] = "🕞"
|
||||||
|
map[":clock3:"] = "🕒"
|
||||||
|
map[":clock430:"] = "🕟"
|
||||||
|
map[":clock4:"] = "🕓"
|
||||||
|
map[":clock530:"] = "🕠"
|
||||||
|
map[":clock5:"] = "🕔"
|
||||||
|
map[":clock630:"] = "🕡"
|
||||||
|
map[":clock6:"] = "🕕"
|
||||||
|
map[":clock730:"] = "🕢"
|
||||||
|
map[":clock7:"] = "🕖"
|
||||||
|
map[":clock830:"] = "🕣"
|
||||||
|
map[":clock8:"] = "🕗"
|
||||||
|
map[":clock930:"] = "🕤"
|
||||||
|
map[":clock9:"] = "🕘"
|
||||||
|
map[":closed_book:"] = "📕"
|
||||||
|
map[":closed_lock_with_key:"] = "🔐"
|
||||||
|
map[":closed_umbrella:"] = "🌂"
|
||||||
|
map[":cloud:"] = "☁️"
|
||||||
|
map[":clubs:"] = "♣️"
|
||||||
|
map[":cn:"] = "🇨🇳"
|
||||||
|
map[":cocktail:"] = "🍸"
|
||||||
|
map[":coffee:"] = "☕"
|
||||||
|
map[":cold_sweat:"] = "😰"
|
||||||
|
map[":computer:"] = "💻"
|
||||||
|
map[":confetti_ball:"] = "🎊"
|
||||||
|
map[":confounded:"] = "😖"
|
||||||
|
map[":confused:"] = "😕"
|
||||||
|
map[":congratulations:"] = "㊗️"
|
||||||
|
map[":construction:"] = "🚧"
|
||||||
|
map[":construction_worker:"] = "👷"
|
||||||
|
map[":convenience_store:"] = "🏪"
|
||||||
|
map[":cookie:"] = "🍪"
|
||||||
|
map[":cool:"] = "🆒"
|
||||||
|
map[":cop:"] = "👮"
|
||||||
|
map[":copyright:"] = "©️"
|
||||||
|
map[":corn:"] = "🌽"
|
||||||
|
map[":couple:"] = "👫"
|
||||||
|
map[":couple_with_heart:"] = "💑"
|
||||||
|
map[":couplekiss:"] = "💏"
|
||||||
|
map[":cow2:"] = "🐄"
|
||||||
|
map[":cow:"] = "🐮"
|
||||||
|
map[":credit_card:"] = "💳"
|
||||||
|
map[":crocodile:"] = "🐊"
|
||||||
|
map[":crossed_flags:"] = "🎌"
|
||||||
|
map[":crown:"] = "👑"
|
||||||
|
map[":cry:"] = "😢"
|
||||||
|
map[":crying_cat_face:"] = "😿"
|
||||||
|
map[":crystal_ball:"] = "🔮"
|
||||||
|
map[":cupid:"] = "💘"
|
||||||
|
map[":curly_loop:"] = "➰"
|
||||||
|
map[":currency_exchange:"] = "💱"
|
||||||
|
map[":curry:"] = "🍛"
|
||||||
|
map[":custard:"] = "🍮"
|
||||||
|
map[":customs:"] = "🛃"
|
||||||
|
map[":cyclone:"] = "🌀"
|
||||||
|
map[":dancer:"] = "💃"
|
||||||
|
map[":dancers:"] = "👯"
|
||||||
|
map[":dango:"] = "🍡"
|
||||||
|
map[":dart:"] = "🎯"
|
||||||
|
map[":dash:"] = "💨"
|
||||||
|
map[":date:"] = "📅"
|
||||||
|
map[":de:"] = "🇩🇪"
|
||||||
|
map[":deciduous_tree:"] = "🌳"
|
||||||
|
map[":department_store:"] = "🏬"
|
||||||
|
map[":diamond_shape_with_a_dot_inside:"] = "💠"
|
||||||
|
map[":diamonds:"] = "♦️"
|
||||||
|
map[":disappointed:"] = "😞"
|
||||||
|
map[":disappointed_relieved:"] = "😥"
|
||||||
|
map[":dizzy:"] = "💫"
|
||||||
|
map[":dizzy_face:"] = "😵"
|
||||||
|
map[":do_not_litter:"] = "🚯"
|
||||||
|
map[":dog2:"] = "🐕"
|
||||||
|
map[":dog:"] = "🐶"
|
||||||
|
map[":dollar:"] = "💵"
|
||||||
|
map[":dolls:"] = "🎎"
|
||||||
|
map[":dolphin:"] = "🐬"
|
||||||
|
map[":door:"] = "🚪"
|
||||||
|
map[":doughnut:"] = "🍩"
|
||||||
|
map[":dragon:"] = "🐉"
|
||||||
|
map[":dragon_face:"] = "🐲"
|
||||||
|
map[":dress:"] = "👗"
|
||||||
|
map[":dromedary_camel:"] = "🐪"
|
||||||
|
map[":droplet:"] = "💧"
|
||||||
|
map[":dvd:"] = "📀"
|
||||||
|
map[":ear:"] = "👂"
|
||||||
|
map[":ear_of_rice:"] = "🌾"
|
||||||
|
map[":earth_africa:"] = "🌍"
|
||||||
|
map[":earth_americas:"] = "🌎"
|
||||||
|
map[":earth_asia:"] = "🌏"
|
||||||
|
map[":egg:"] = "🥚"
|
||||||
|
map[":eggplant:"] = "🍆"
|
||||||
|
map[":eight:"] = "8️⃣"
|
||||||
|
map[":eight_pointed_black_star:"] = "✴️"
|
||||||
|
map[":eight_spoked_asterisk:"] = "✳️"
|
||||||
|
map[":electric_plug:"] = "🔌"
|
||||||
|
map[":elephant:"] = "🐘"
|
||||||
|
map[":email:"] = "📧"
|
||||||
|
map[":end:"] = "🔚"
|
||||||
|
map[":envelope:"] = "✉️"
|
||||||
|
map[":es:"] = "🇪🇸"
|
||||||
|
map[":euro:"] = "💶"
|
||||||
|
map[":european_castle:"] = "🏰"
|
||||||
|
map[":european_post_office:"] = "🏤"
|
||||||
|
map[":evergreen_tree:"] = "🌲"
|
||||||
|
map[":exclamation:"] = "❗"
|
||||||
|
map[":expressionless:"] = "😑"
|
||||||
|
map[":eyeglasses:"] = "👓"
|
||||||
|
map[":eyes:"] = "👀"
|
||||||
|
map[":factory:"] = "🏭"
|
||||||
|
map[":fallen_leaf:"] = "🍂"
|
||||||
|
map[":family:"] = "👪"
|
||||||
|
map[":fast_forward:"] = "⏩"
|
||||||
|
map[":fax:"] = "📠"
|
||||||
|
map[":fearful:"] = "😨"
|
||||||
|
map[":feet:"] = "🐾"
|
||||||
|
map[":ferris_wheel:"] = "🎡"
|
||||||
|
map[":file_folder:"] = "📁"
|
||||||
|
map[":fire:"] = "🔥"
|
||||||
|
map[":fire_engine:"] = "🚒"
|
||||||
|
map[":fireworks:"] = "🎆"
|
||||||
|
map[":first_quarter_moon:"] = "🌓"
|
||||||
|
map[":first_quarter_moon_with_face:"] = "🌛"
|
||||||
|
map[":fish:"] = "🐟"
|
||||||
|
map[":fish_cake:"] = "🍥"
|
||||||
|
map[":fishing_pole_and_fish:"] = "🎣"
|
||||||
|
map[":fist:"] = "✊"
|
||||||
|
map[":five:"] = "5️⃣"
|
||||||
|
map[":flags:"] = "🎏"
|
||||||
|
map[":flashlight:"] = "🔦"
|
||||||
|
map[":floppy_disk:"] = "💾"
|
||||||
|
map[":flower_playing_cards:"] = "🎴"
|
||||||
|
map[":flushed:"] = "😳"
|
||||||
|
map[":foggy:"] = "🌁"
|
||||||
|
map[":football:"] = "🏈"
|
||||||
|
map[":fork_and_knife:"] = "🍴"
|
||||||
|
map[":fountain:"] = "⛲"
|
||||||
|
map[":four:"] = "4️⃣"
|
||||||
|
map[":four_leaf_clover:"] = "🍀"
|
||||||
|
map[":fr:"] = "🇫🇷"
|
||||||
|
map[":free:"] = "🆓"
|
||||||
|
map[":fried_shrimp:"] = "🍤"
|
||||||
|
map[":fries:"] = "🍟"
|
||||||
|
map[":frog:"] = "🐸"
|
||||||
|
map[":frowning:"] = "😦"
|
||||||
|
map[":fuelpump:"] = "⛽"
|
||||||
|
map[":full_moon:"] = "🌕"
|
||||||
|
map[":full_moon_with_face:"] = "🌝"
|
||||||
|
map[":game_die:"] = "🎲"
|
||||||
|
map[":gem:"] = "💎"
|
||||||
|
map[":gemini:"] = "♊"
|
||||||
|
map[":ghost:"] = "👻"
|
||||||
|
map[":gift:"] = "🎁"
|
||||||
|
map[":gift_heart:"] = "💝"
|
||||||
|
map[":girl:"] = "👧"
|
||||||
|
map[":globe_with_meridians:"] = "🌐"
|
||||||
|
map[":goat:"] = "🐐"
|
||||||
|
map[":golf:"] = "⛳"
|
||||||
|
map[":grapes:"] = "🍇"
|
||||||
|
map[":green_apple:"] = "🍏"
|
||||||
|
map[":green_book:"] = "📗"
|
||||||
|
map[":green_heart:"] = "💚"
|
||||||
|
map[":grey_exclamation:"] = "❕"
|
||||||
|
map[":grey_question:"] = "❔"
|
||||||
|
map[":grimacing:"] = "😬"
|
||||||
|
map[":grin:"] = "😁"
|
||||||
|
map[":grinning:"] = "😀"
|
||||||
|
map[":guardsman:"] = "💂♂️"
|
||||||
|
map[":guitar:"] = "🎸"
|
||||||
|
map[":gun:"] = "🔫"
|
||||||
|
map[":haircut:"] = "💇"
|
||||||
|
map[":hamburger:"] = "🍔"
|
||||||
|
map[":hammer:"] = "🔨"
|
||||||
|
map[":hamster:"] = "🐹"
|
||||||
|
map[":handbag:"] = "👜"
|
||||||
|
map[":hankey:"] = "💩"
|
||||||
|
map[":hash:"] = "#️⃣"
|
||||||
|
map[":hatched_chick:"] = "🐥"
|
||||||
|
map[":hatching_chick:"] = "🐣"
|
||||||
|
map[":headphones:"] = "🎧"
|
||||||
|
map[":hear_no_evil:"] = "🙉"
|
||||||
|
map[":heart:"] = "❤️"
|
||||||
|
map[":heart_decoration:"] = "💟"
|
||||||
|
map[":heart_eyes:"] = "😍"
|
||||||
|
map[":heart_eyes_cat:"] = "😻"
|
||||||
|
map[":heartbeat:"] = "💓"
|
||||||
|
map[":hearts:"] = "♥️"
|
||||||
|
map[":heavy_check_mark:"] = "✔️"
|
||||||
|
map[":heavy_division_sign:"] = "➗"
|
||||||
|
map[":heavy_dollar_sign:"] = "💲"
|
||||||
|
map[":heavy_minus_sign:"] = "➖"
|
||||||
|
map[":heavy_multiplication_x:"] = "✖️"
|
||||||
|
map[":heavy_plus_sign:"] = "➕"
|
||||||
|
map[":helicopter:"] = "🚁"
|
||||||
|
map[":herb:"] = "🌿"
|
||||||
|
map[":hibiscus:"] = "🌺"
|
||||||
|
map[":high_brightness:"] = "🔆"
|
||||||
|
map[":high_heel:"] = "👠"
|
||||||
|
map[":honey_pot:"] = "🍯"
|
||||||
|
map[":horse:"] = "🐴"
|
||||||
|
map[":horse_racing:"] = "🏇"
|
||||||
|
map[":hospital:"] = "🏥"
|
||||||
|
map[":hotel:"] = "🏨"
|
||||||
|
map[":hotsprings:"] = "♨️"
|
||||||
|
map[":hourglass:"] = "⌛"
|
||||||
|
map[":hourglass_flowing_sand:"] = "⏳"
|
||||||
|
map[":house:"] = "🏠"
|
||||||
|
map[":house_with_garden:"] = "🏡"
|
||||||
|
map[":hushed:"] = "😯"
|
||||||
|
map[":ice_cream:"] = "🍨"
|
||||||
|
map[":icecream:"] = "🍦"
|
||||||
|
map[":id:"] = "🆔"
|
||||||
|
map[":ideograph_advantage:"] = "🉐"
|
||||||
|
map[":imp:"] = "👿"
|
||||||
|
map[":inbox_tray:"] = "📥"
|
||||||
|
map[":incoming_envelope:"] = "📨"
|
||||||
|
map[":information_desk_person:"] = "💁"
|
||||||
|
map[":information_source:"] = "ℹ️"
|
||||||
|
map[":innocent:"] = "😇"
|
||||||
|
map[":interrobang:"] = "⁉️"
|
||||||
|
map[":iphone:"] = "📱"
|
||||||
|
map[":it:"] = "🇮🇹"
|
||||||
|
map[":izakaya_lantern:"] = "🏮"
|
||||||
|
map[":jack_o_lantern:"] = "🎃"
|
||||||
|
map[":japan:"] = "🗾"
|
||||||
|
map[":japanese_castle:"] = "🏯"
|
||||||
|
map[":japanese_goblin:"] = "👺"
|
||||||
|
map[":japanese_ogre:"] = "👹"
|
||||||
|
map[":jeans:"] = "👖"
|
||||||
|
map[":joy:"] = "😂"
|
||||||
|
map[":joy_cat:"] = "😹"
|
||||||
|
map[":jp:"] = "🇯🇵"
|
||||||
|
map[":key:"] = "🔑"
|
||||||
|
map[":keycap_ten:"] = "🔟"
|
||||||
|
map[":kimono:"] = "👘"
|
||||||
|
map[":kiss:"] = "💋"
|
||||||
|
map[":kissing:"] = "😗"
|
||||||
|
map[":kissing_cat:"] = "😽"
|
||||||
|
map[":kissing_closed_eyes:"] = "😚"
|
||||||
|
map[":kissing_heart:"] = "😘"
|
||||||
|
map[":kissing_smiling_eyes:"] = "😙"
|
||||||
|
map[":koala:"] = "🐨"
|
||||||
|
map[":koko:"] = "🈁"
|
||||||
|
map[":kr:"] = "🇰🇷"
|
||||||
|
map[":large_blue_diamond:"] = "🔷"
|
||||||
|
map[":large_orange_diamond:"] = "🔶"
|
||||||
|
map[":last_quarter_moon:"] = "🌗"
|
||||||
|
map[":last_quarter_moon_with_face:"] = "🌜"
|
||||||
|
map[":laughing:"] = "😆"
|
||||||
|
map[":leaves:"] = "🍃"
|
||||||
|
map[":ledger:"] = "📒"
|
||||||
|
map[":left_luggage:"] = "🛅"
|
||||||
|
map[":left_right_arrow:"] = "↔️"
|
||||||
|
map[":leftwards_arrow_with_hook:"] = "↩️"
|
||||||
|
map[":lemon:"] = "🍋"
|
||||||
|
map[":leo:"] = "♌"
|
||||||
|
map[":leopard:"] = "🐆"
|
||||||
|
map[":libra:"] = "♎"
|
||||||
|
map[":light_rail:"] = "🚈"
|
||||||
|
map[":link:"] = "🔗"
|
||||||
|
map[":lips:"] = "👄"
|
||||||
|
map[":lipstick:"] = "💄"
|
||||||
|
map[":lock:"] = "🔒"
|
||||||
|
map[":lock_with_ink_pen:"] = "🔏"
|
||||||
|
map[":lollipop:"] = "🍭"
|
||||||
|
map[":loop:"] = "➿"
|
||||||
|
map[":loudspeaker:"] = "📢"
|
||||||
|
map[":love_hotel:"] = "🏩"
|
||||||
|
map[":love_letter:"] = "💌"
|
||||||
|
map[":low_brightness:"] = "🔅"
|
||||||
|
map[":m:"] = "Ⓜ️"
|
||||||
|
map[":mag:"] = "🔍"
|
||||||
|
map[":mag_right:"] = "🔎"
|
||||||
|
map[":mahjong:"] = "🀄"
|
||||||
|
map[":mailbox:"] = "📫"
|
||||||
|
map[":mailbox_closed:"] = "📪"
|
||||||
|
map[":mailbox_with_mail:"] = "📬"
|
||||||
|
map[":mailbox_with_no_mail:"] = "📭"
|
||||||
|
map[":man:"] = "👨"
|
||||||
|
map[":man_with_gua_pi_mao:"] = "👲"
|
||||||
|
map[":man_with_turban:"] = "👳♂️"
|
||||||
|
map[":mans_shoe:"] = "👞"
|
||||||
|
map[":maple_leaf:"] = "🍁"
|
||||||
|
map[":mask:"] = "😷"
|
||||||
|
map[":massage:"] = "💆"
|
||||||
|
map[":meat_on_bone:"] = "🍖"
|
||||||
|
map[":mega:"] = "📣"
|
||||||
|
map[":melon:"] = "🍈"
|
||||||
|
map[":memo:"] = "📝"
|
||||||
|
map[":mens:"] = "🚹"
|
||||||
|
map[":metal:"] = "🤘"
|
||||||
|
map[":metro:"] = "🚇"
|
||||||
|
map[":microphone:"] = "🎤"
|
||||||
|
map[":microscope:"] = "🔬"
|
||||||
|
map[":milky_way:"] = "🌌"
|
||||||
|
map[":minibus:"] = "🚐"
|
||||||
|
map[":minidisc:"] = "💽"
|
||||||
|
map[":mobile_phone_off:"] = "📴"
|
||||||
|
map[":money_with_wings:"] = "💸"
|
||||||
|
map[":moneybag:"] = "💰"
|
||||||
|
map[":monkey:"] = "🐒"
|
||||||
|
map[":monkey_face:"] = "🐵"
|
||||||
|
map[":monorail:"] = "🚝"
|
||||||
|
map[":mortar_board:"] = "🎓"
|
||||||
|
map[":mount_fuji:"] = "🗻"
|
||||||
|
map[":mountain_bicyclist:"] = "🚵"
|
||||||
|
map[":mountain_cableway:"] = "🚠"
|
||||||
|
map[":mountain_railway:"] = "🚞"
|
||||||
|
map[":mouse2:"] = "🐁"
|
||||||
|
map[":mouse:"] = "🐭"
|
||||||
|
map[":movie_camera:"] = "🎥"
|
||||||
|
map[":moyai:"] = "🗿"
|
||||||
|
map[":muscle:"] = "💪"
|
||||||
|
map[":mushroom:"] = "🍄"
|
||||||
|
map[":musical_keyboard:"] = "🎹"
|
||||||
|
map[":musical_note:"] = "🎵"
|
||||||
|
map[":musical_score:"] = "🎼"
|
||||||
|
map[":mute:"] = "🔇"
|
||||||
|
map[":nail_care:"] = "💅"
|
||||||
|
map[":name_badge:"] = "📛"
|
||||||
|
map[":necktie:"] = "👔"
|
||||||
|
map[":negative_squared_cross_mark:"] = "❎"
|
||||||
|
map[":neutral_face:"] = "😐"
|
||||||
|
map[":new:"] = "🆕"
|
||||||
|
map[":new_moon:"] = "🌑"
|
||||||
|
map[":new_moon_with_face:"] = "🌚"
|
||||||
|
map[":newspaper:"] = "📰"
|
||||||
|
map[":ng:"] = "🆖"
|
||||||
|
map[":nine:"] = "9️⃣"
|
||||||
|
map[":no_bell:"] = "🔕"
|
||||||
|
map[":no_bicycles:"] = "🚳"
|
||||||
|
map[":no_entry:"] = "⛔"
|
||||||
|
map[":no_entry_sign:"] = "🚫"
|
||||||
|
map[":no_good:"] = "🙅"
|
||||||
|
map[":no_mobile_phones:"] = "📵"
|
||||||
|
map[":no_mouth:"] = "😶"
|
||||||
|
map[":no_pedestrians:"] = "🚷"
|
||||||
|
map[":no_smoking:"] = "🚭"
|
||||||
|
map[":nose:"] = "👃"
|
||||||
|
map[":notebook:"] = "📓"
|
||||||
|
map[":notebook_with_decorative_cover:"] = "📔"
|
||||||
|
map[":notes:"] = "🎶"
|
||||||
|
map[":nut_and_bolt:"] = "🔩"
|
||||||
|
map[":o2:"] = "🅾️"
|
||||||
|
map[":o:"] = "⭕"
|
||||||
|
map[":ocean:"] = "🌊"
|
||||||
|
map[":octopus:"] = "🐙"
|
||||||
|
map[":oden:"] = "🍢"
|
||||||
|
map[":office:"] = "🏢"
|
||||||
|
map[":ok:"] = "🆗"
|
||||||
|
map[":ok_hand:"] = "👌"
|
||||||
|
map[":ok_woman:"] = "🙆♀️"
|
||||||
|
map[":older_man:"] = "👴"
|
||||||
|
map[":older_woman:"] = "👵"
|
||||||
|
map[":on:"] = "🔛"
|
||||||
|
map[":oncoming_automobile:"] = "🚘"
|
||||||
|
map[":oncoming_bus:"] = "🚍"
|
||||||
|
map[":oncoming_police_car:"] = "🚔"
|
||||||
|
map[":oncoming_taxi:"] = "🚖"
|
||||||
|
map[":one:"] = "1️⃣"
|
||||||
|
map[":open_file_folder:"] = "📂"
|
||||||
|
map[":open_hands:"] = "👐"
|
||||||
|
map[":open_mouth:"] = "😮"
|
||||||
|
map[":ophiuchus:"] = "⛎"
|
||||||
|
map[":orange_book:"] = "📙"
|
||||||
|
map[":outbox_tray:"] = "📤"
|
||||||
|
map[":ox:"] = "🐂"
|
||||||
|
map[":page_facing_up:"] = "📄"
|
||||||
|
map[":page_with_curl:"] = "📃"
|
||||||
|
map[":pager:"] = "📟"
|
||||||
|
map[":palm_tree:"] = "🌴"
|
||||||
|
map[":panda_face:"] = "🐼"
|
||||||
|
map[":paperclip:"] = "📎"
|
||||||
|
map[":parking:"] = "🅿️"
|
||||||
|
map[":part_alternation_mark:"] = "〽️"
|
||||||
|
map[":partly_sunny:"] = "⛅"
|
||||||
|
map[":passport_control:"] = "🛂"
|
||||||
|
map[":paw_prints:"] = "🐾"
|
||||||
|
map[":peach:"] = "🍑"
|
||||||
|
map[":pear:"] = "🍐"
|
||||||
|
map[":pencil2:"] = "✏️"
|
||||||
|
map[":pencil:"] = "📝"
|
||||||
|
map[":penguin:"] = "🐧"
|
||||||
|
map[":pensive:"] = "😔"
|
||||||
|
map[":performing_arts:"] = "🎭"
|
||||||
|
map[":persevere:"] = "😣"
|
||||||
|
map[":pig2:"] = "🐖"
|
||||||
|
map[":pig:"] = "🐷"
|
||||||
|
map[":pig_nose:"] = "🐽"
|
||||||
|
map[":pill:"] = "💊"
|
||||||
|
map[":pineapple:"] = "🍍"
|
||||||
|
map[":pisces:"] = "♓"
|
||||||
|
map[":pizza:"] = "🍕"
|
||||||
|
map[":point_down:"] = "👇"
|
||||||
|
map[":point_left:"] = "👈"
|
||||||
|
map[":point_right:"] = "👉"
|
||||||
|
map[":point_up:"] = "☝️"
|
||||||
|
map[":point_up_2:"] = "👆"
|
||||||
|
map[":police_car:"] = "🚓"
|
||||||
|
map[":poodle:"] = "🐩"
|
||||||
|
map[":poop:"] = "💩"
|
||||||
|
map[":post_office:"] = "🏣"
|
||||||
|
map[":postal_horn:"] = "📯"
|
||||||
|
map[":postbox:"] = "📮"
|
||||||
|
map[":potable_water:"] = "🚰"
|
||||||
|
map[":pouch:"] = "👝"
|
||||||
|
map[":poultry_leg:"] = "🍗"
|
||||||
|
map[":pound:"] = "💷"
|
||||||
|
map[":pouting_cat:"] = "😾"
|
||||||
|
map[":pray:"] = "🙏"
|
||||||
|
map[":princess:"] = "👸"
|
||||||
|
map[":punch:"] = "👊"
|
||||||
|
map[":purple_heart:"] = "💜"
|
||||||
|
map[":purse:"] = "👛"
|
||||||
|
map[":pushpin:"] = "📌"
|
||||||
|
map[":put_litter_in_its_place:"] = "🚮"
|
||||||
|
map[":question:"] = "❓"
|
||||||
|
map[":rabbit2:"] = "🐇"
|
||||||
|
map[":rabbit:"] = "🐰"
|
||||||
|
map[":racehorse:"] = "🐎"
|
||||||
|
map[":radio:"] = "📻"
|
||||||
|
map[":radio_button:"] = "🔘"
|
||||||
|
map[":rage:"] = "😡"
|
||||||
|
map[":railway_car:"] = "🚃"
|
||||||
|
map[":rainbow:"] = "🌈"
|
||||||
|
map[":raised_hand:"] = "✋"
|
||||||
|
map[":raised_hands:"] = "🙌"
|
||||||
|
map[":raising_hand:"] = "🙋"
|
||||||
|
map[":ram:"] = "🐏"
|
||||||
|
map[":ramen:"] = "🍜"
|
||||||
|
map[":rat:"] = "🐀"
|
||||||
|
map[":recycle:"] = "♻️"
|
||||||
|
map[":red_car:"] = "🚗"
|
||||||
|
map[":red_circle:"] = "🔴"
|
||||||
|
map[":registered:"] = "®️"
|
||||||
|
map[":relaxed:"] = "☺️"
|
||||||
|
map[":relieved:"] = "😌"
|
||||||
|
map[":repeat:"] = "🔁"
|
||||||
|
map[":repeat_one:"] = "🔂"
|
||||||
|
map[":restroom:"] = "🚻"
|
||||||
|
map[":revolving_hearts:"] = "💞"
|
||||||
|
map[":rewind:"] = "⏪"
|
||||||
|
map[":ribbon:"] = "🎀"
|
||||||
|
map[":rice:"] = "🍚"
|
||||||
|
map[":rice_ball:"] = "🍙"
|
||||||
|
map[":rice_cracker:"] = "🍘"
|
||||||
|
map[":rice_scene:"] = "🎑"
|
||||||
|
map[":ring:"] = "💍"
|
||||||
|
map[":rocket:"] = "🚀"
|
||||||
|
map[":roller_coaster:"] = "🎢"
|
||||||
|
map[":rooster:"] = "🐓"
|
||||||
|
map[":rose:"] = "🌹"
|
||||||
|
map[":rotating_light:"] = "🚨"
|
||||||
|
map[":round_pushpin:"] = "📍"
|
||||||
|
map[":rowboat:"] = "🚣"
|
||||||
|
map[":ru:"] = "🇷🇺"
|
||||||
|
map[":rugby_football:"] = "🏉"
|
||||||
|
map[":runner:"] = "🏃"
|
||||||
|
map[":running_shirt_with_sash:"] = "🎽"
|
||||||
|
map[":sa:"] = "🈂️"
|
||||||
|
map[":sagittarius:"] = "♐"
|
||||||
|
map[":sailboat:"] = "⛵"
|
||||||
|
map[":sake:"] = "🍶"
|
||||||
|
map[":sandal:"] = "👡"
|
||||||
|
map[":santa:"] = "🎅"
|
||||||
|
map[":satellite:"] = "📡"
|
||||||
|
map[":satisfied:"] = "😆"
|
||||||
|
map[":saxophone:"] = "🎷"
|
||||||
|
map[":school:"] = "🏫"
|
||||||
|
map[":school_satchel:"] = "🎒"
|
||||||
|
map[":scissors:"] = "✂️"
|
||||||
|
map[":scorpius:"] = "♏"
|
||||||
|
map[":scream:"] = "😱"
|
||||||
|
map[":scream_cat:"] = "🙀"
|
||||||
|
map[":scroll:"] = "📜"
|
||||||
|
map[":seat:"] = "💺"
|
||||||
|
map[":secret:"] = "㊙️"
|
||||||
|
map[":see_no_evil:"] = "🙈"
|
||||||
|
map[":seedling:"] = "🌱"
|
||||||
|
map[":seven:"] = "7️⃣"
|
||||||
|
map[":shaved_ice:"] = "🍧"
|
||||||
|
map[":sheep:"] = "🐑"
|
||||||
|
map[":shell:"] = "🐚"
|
||||||
|
map[":ship:"] = "🚢"
|
||||||
|
map[":shirt:"] = "👕"
|
||||||
|
map[":shit:"] = "💩"
|
||||||
|
map[":shower:"] = "🚿"
|
||||||
|
map[":signal_strength:"] = "📶"
|
||||||
|
map[":six:"] = "6️⃣"
|
||||||
|
map[":six_pointed_star:"] = "🔯"
|
||||||
|
map[":ski:"] = "🎿"
|
||||||
|
map[":skull:"] = "💀"
|
||||||
|
map[":sleeping:"] = "😴"
|
||||||
|
map[":sleepy:"] = "😪"
|
||||||
|
map[":slot_machine:"] = "🎰"
|
||||||
|
map[":small_blue_diamond:"] = "🔹"
|
||||||
|
map[":small_orange_diamond:"] = "🔸"
|
||||||
|
map[":small_red_triangle:"] = "🔺"
|
||||||
|
map[":small_red_triangle_down:"] = "🔻"
|
||||||
|
map[":smile:"] = "😄"
|
||||||
|
map[":smile_cat:"] = "😸"
|
||||||
|
map[":smiley:"] = "😃"
|
||||||
|
map[":smiley_cat:"] = "😺"
|
||||||
|
map[":smiling_imp:"] = "😈"
|
||||||
|
map[":smirk:"] = "😏"
|
||||||
|
map[":smirk_cat:"] = "😼"
|
||||||
|
map[":smoking:"] = "🚬"
|
||||||
|
map[":snail:"] = "🐌"
|
||||||
|
map[":snake:"] = "🐍"
|
||||||
|
map[":snowboarder:"] = "🏂"
|
||||||
|
map[":snowflake:"] = "❄️"
|
||||||
|
map[":snowman:"] = "⛄"
|
||||||
|
map[":sob:"] = "😭"
|
||||||
|
map[":soccer:"] = "⚽"
|
||||||
|
map[":soon:"] = "🔜"
|
||||||
|
map[":sos:"] = "🆘"
|
||||||
|
map[":sound:"] = "🔉"
|
||||||
|
map[":space_invader:"] = "👾"
|
||||||
|
map[":spades:"] = "♠️"
|
||||||
|
map[":spaghetti:"] = "🍝"
|
||||||
|
map[":sparkler:"] = "🎇"
|
||||||
|
map[":sparkles:"] = "✨"
|
||||||
|
map[":sparkling_heart:"] = "💖"
|
||||||
|
map[":speak_no_evil:"] = "🙊"
|
||||||
|
map[":speaker:"] = "🔈"
|
||||||
|
map[":speech_balloon:"] = "💬"
|
||||||
|
map[":speedboat:"] = "🚤"
|
||||||
|
map[":star2:"] = "🌟"
|
||||||
|
map[":star:"] = "⭐"
|
||||||
|
map[":stars:"] = "🌠"
|
||||||
|
map[":station:"] = "🚉"
|
||||||
|
map[":statue_of_liberty:"] = "🗽"
|
||||||
|
map[":steam_locomotive:"] = "🚂"
|
||||||
|
map[":stew:"] = "🍲"
|
||||||
|
map[":straight_ruler:"] = "📏"
|
||||||
|
map[":strawberry:"] = "🍓"
|
||||||
|
map[":stuck_out_tongue:"] = "😛"
|
||||||
|
map[":stuck_out_tongue_closed_eyes:"] = "😝"
|
||||||
|
map[":stuck_out_tongue_winking_eye:"] = "😜"
|
||||||
|
map[":sun_with_face:"] = "🌞"
|
||||||
|
map[":sunflower:"] = "🌻"
|
||||||
|
map[":sunglasses:"] = "😎"
|
||||||
|
map[":sunny:"] = "☀️"
|
||||||
|
map[":sunrise:"] = "🌅"
|
||||||
|
map[":sunrise_over_mountains:"] = "🌄"
|
||||||
|
map[":surfer:"] = "🏄"
|
||||||
|
map[":sushi:"] = "🍣"
|
||||||
|
map[":suspension_railway:"] = "🚟"
|
||||||
|
map[":sweat:"] = "😓"
|
||||||
|
map[":sweat_drops:"] = "💦"
|
||||||
|
map[":sweat_smile:"] = "😅"
|
||||||
|
map[":sweet_potato:"] = "🍠"
|
||||||
|
map[":swimmer:"] = "🏊"
|
||||||
|
map[":symbols:"] = "🔣"
|
||||||
|
map[":syringe:"] = "💉"
|
||||||
|
map[":tada:"] = "🎉"
|
||||||
|
map[":tanabata_tree:"] = "🎋"
|
||||||
|
map[":tangerine:"] = "🍊"
|
||||||
|
map[":taurus:"] = "♉"
|
||||||
|
map[":taxi:"] = "🚕"
|
||||||
|
map[":tea:"] = "🍵"
|
||||||
|
map[":telephone:"] = "☎️"
|
||||||
|
map[":telephone_receiver:"] = "📞"
|
||||||
|
map[":telescope:"] = "🔭"
|
||||||
|
map[":tennis:"] = "🎾"
|
||||||
|
map[":tent:"] = "⛺"
|
||||||
|
map[":thought_balloon:"] = "💭"
|
||||||
|
map[":three:"] = "3️⃣"
|
||||||
|
map[":thumbsdown:"] = "👎"
|
||||||
|
map[":thumbsup:"] = "👍"
|
||||||
|
map[":ticket:"] = "🎫"
|
||||||
|
map[":tiger2:"] = "🐅"
|
||||||
|
map[":tiger:"] = "🐯"
|
||||||
|
map[":tired_face:"] = "😫"
|
||||||
|
map[":tm:"] = "™️"
|
||||||
|
map[":toilet:"] = "🚽"
|
||||||
|
map[":tokyo_tower:"] = "🗼"
|
||||||
|
map[":tomato:"] = "🍅"
|
||||||
|
map[":tongue:"] = "👅"
|
||||||
|
map[":top:"] = "🔝"
|
||||||
|
map[":tophat:"] = "🎩"
|
||||||
|
map[":tractor:"] = "🚜"
|
||||||
|
map[":traffic_light:"] = "🚥"
|
||||||
|
map[":train2:"] = "🚆"
|
||||||
|
map[":train:"] = "🚋"
|
||||||
|
map[":tram:"] = "🚊"
|
||||||
|
map[":triangular_flag_on_post:"] = "🚩"
|
||||||
|
map[":triangular_ruler:"] = "📐"
|
||||||
|
map[":trident:"] = "🔱"
|
||||||
|
map[":triumph:"] = "😤"
|
||||||
|
map[":trolleybus:"] = "🚎"
|
||||||
|
map[":trophy:"] = "🏆"
|
||||||
|
map[":tropical_drink:"] = "🍹"
|
||||||
|
map[":tropical_fish:"] = "🐠"
|
||||||
|
map[":truck:"] = "🚚"
|
||||||
|
map[":trumpet:"] = "🎺"
|
||||||
|
map[":tulip:"] = "🌷"
|
||||||
|
map[":turtle:"] = "🐢"
|
||||||
|
map[":tv:"] = "📺"
|
||||||
|
map[":twisted_rightwards_arrows:"] = "🔀"
|
||||||
|
map[":two:"] = "2️⃣"
|
||||||
|
map[":two_men_holding_hands:"] = "👬"
|
||||||
|
map[":two_women_holding_hands:"] = "👭"
|
||||||
|
map[":u5272:"] = "🈹"
|
||||||
|
map[":u5408:"] = "🈴"
|
||||||
|
map[":u55b6:"] = "🈺"
|
||||||
|
map[":u6307:"] = "🈯"
|
||||||
|
map[":u6708:"] = "🈷️"
|
||||||
|
map[":u6709:"] = "🈶"
|
||||||
|
map[":u6e80:"] = "🈵"
|
||||||
|
map[":u7121:"] = "🈚"
|
||||||
|
map[":u7533:"] = "🈸"
|
||||||
|
map[":u7981:"] = "🈲"
|
||||||
|
map[":u7a7a:"] = "🈳"
|
||||||
|
map[":umbrella:"] = "☔"
|
||||||
|
map[":unamused:"] = "😒"
|
||||||
|
map[":underage:"] = "🔞"
|
||||||
|
map[":unlock:"] = "🔓"
|
||||||
|
map[":up:"] = "🆙"
|
||||||
|
map[":us:"] = "🇺🇸"
|
||||||
|
map[":v:"] = "✌️"
|
||||||
|
map[":vertical_traffic_light:"] = "🚦"
|
||||||
|
map[":vhs:"] = "📼"
|
||||||
|
map[":vibration_mode:"] = "📳"
|
||||||
|
map[":video_camera:"] = "📹"
|
||||||
|
map[":video_game:"] = "🎮"
|
||||||
|
map[":violin:"] = "🎻"
|
||||||
|
map[":virgo:"] = "♍"
|
||||||
|
map[":volcano:"] = "🌋"
|
||||||
|
map[":vs:"] = "🆚"
|
||||||
|
map[":walking:"] = "🚶"
|
||||||
|
map[":waning_crescent_moon:"] = "🌘"
|
||||||
|
map[":waning_gibbous_moon:"] = "🌖"
|
||||||
|
map[":warning:"] = "⚠️"
|
||||||
|
map[":watch:"] = "⌚"
|
||||||
|
map[":water_buffalo:"] = "🐃"
|
||||||
|
map[":watermelon:"] = "🍉"
|
||||||
|
map[":wave:"] = "👋"
|
||||||
|
map[":wavy_dash:"] = "〰️"
|
||||||
|
map[":waxing_crescent_moon:"] = "🌒"
|
||||||
|
map[":waxing_gibbous_moon:"] = "🌔"
|
||||||
|
map[":wc:"] = "🚾"
|
||||||
|
map[":weary:"] = "😩"
|
||||||
|
map[":wedding:"] = "💒"
|
||||||
|
map[":whale2:"] = "🐋"
|
||||||
|
map[":whale:"] = "🐳"
|
||||||
|
map[":wheelchair:"] = "♿"
|
||||||
|
map[":white_check_mark:"] = "✅"
|
||||||
|
map[":white_circle:"] = "⚪"
|
||||||
|
map[":white_flower:"] = "💮"
|
||||||
|
map[":white_square_button:"] = "🔳"
|
||||||
|
map[":wind_chime:"] = "🎐"
|
||||||
|
map[":wine_glass:"] = "🍷"
|
||||||
|
map[":wink:"] = "😉"
|
||||||
|
map[":wolf:"] = "🐺"
|
||||||
|
map[":woman:"] = "👩"
|
||||||
|
map[":womans_clothes:"] = "👚"
|
||||||
|
map[":womans_hat:"] = "👒"
|
||||||
|
map[":womens:"] = "🚺"
|
||||||
|
map[":worried:"] = "😟"
|
||||||
|
map[":wrench:"] = "🔧"
|
||||||
|
map[":x:"] = "❌"
|
||||||
|
map[":yellow_heart:"] = "💛"
|
||||||
|
map[":yen:"] = "💴"
|
||||||
|
map[":yum:"] = "😋"
|
||||||
|
map[":zap:"] = "⚡"
|
||||||
|
map[":zero:"] = "0️⃣"
|
||||||
|
map[":zzz:"] = "💤"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if ($0 ~ /<pre>/) in_pre = 1
|
||||||
|
if (!in_pre) {
|
||||||
|
code_count = 0
|
||||||
|
line = $0
|
||||||
|
out = ""
|
||||||
|
while (match(line, /<code>[^<]*<\/code>/)) {
|
||||||
|
code_count++
|
||||||
|
code_store[code_count] = substr(line, RSTART, RLENGTH)
|
||||||
|
out = out substr(line, 1, RSTART - 1) "\034EC" code_count "\034"
|
||||||
|
line = substr(line, RSTART + RLENGTH)
|
||||||
|
}
|
||||||
|
out = out line
|
||||||
|
|
||||||
|
for (sc in map) {
|
||||||
|
if (index(out, sc)) {
|
||||||
|
gsub(sc, map[sc], out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i <= code_count; i++) {
|
||||||
|
gsub("\034EC" i "\034", code_store[i], out)
|
||||||
|
}
|
||||||
|
$0 = out
|
||||||
|
}
|
||||||
|
if ($0 ~ /<\/pre>/) in_pre = 0
|
||||||
|
print
|
||||||
|
}
|
||||||
51
awk/footnotes.awk
Normal file
51
awk/footnotes.awk
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
BEGIN { fn_count = 0 }
|
||||||
|
|
||||||
|
# Match [^id]: text
|
||||||
|
/^\[\^[a-zA-Z0-9_-]+\]:/ {
|
||||||
|
id_start = index($0, "[^") + 2
|
||||||
|
id_end = index($0, "]:")
|
||||||
|
id = substr($0, id_start, id_end - id_start)
|
||||||
|
text = substr($0, id_end + 2)
|
||||||
|
# Trim leading space
|
||||||
|
sub(/^[ \t]+/, "", text)
|
||||||
|
|
||||||
|
fn_ids[++fn_count] = id
|
||||||
|
fn_texts[id] = text
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
lines[++line_count] = $0
|
||||||
|
}
|
||||||
|
|
||||||
|
END {
|
||||||
|
for (i = 1; i <= line_count; i++) {
|
||||||
|
line = lines[i]
|
||||||
|
|
||||||
|
for (j = 1; j <= fn_count; j++) {
|
||||||
|
id = fn_ids[j]
|
||||||
|
marker = "[^" id "]"
|
||||||
|
repl = "<sup><a href=\"#fn:" id "\" id=\"fnref:" id "\">" id "</a></sup>"
|
||||||
|
|
||||||
|
while ((pos = index(line, marker)) > 0) {
|
||||||
|
line = substr(line, 1, pos - 1) repl substr(line, pos + length(marker))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print line
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fn_count > 0) {
|
||||||
|
print "<hr />"
|
||||||
|
print "<section class=\"footnotes\">"
|
||||||
|
print "<ol>"
|
||||||
|
for (j = 1; j <= fn_count; j++) {
|
||||||
|
id = fn_ids[j]
|
||||||
|
text = fn_texts[id]
|
||||||
|
print "<li id=\"fn:" id "\">"
|
||||||
|
print "<p>" text " <a href=\"#fnref:" id "\" class=\"reversefootnote\">↩</a></p>"
|
||||||
|
print "</li>"
|
||||||
|
}
|
||||||
|
print "</ol>"
|
||||||
|
print "</section>"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,46 @@ function title_from_name(name) {
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_title(path, default_title, full_path, line, title, in_fm) {
|
||||||
|
full_path = src "/" path
|
||||||
|
if (path !~ /\.md$/) {
|
||||||
|
full_path = full_path "/index.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
title = ""
|
||||||
|
in_fm = 0
|
||||||
|
while ((getline line < full_path) > 0) {
|
||||||
|
if (line ~ /^---[[:space:]]*$/) {
|
||||||
|
if (in_fm == 0) {
|
||||||
|
in_fm = 1
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in_fm) {
|
||||||
|
if (line ~ /^[[:space:]]*title[[:space:]]*=/) {
|
||||||
|
sub(/^[[:space:]]*title[[:space:]]*=[[:space:]]*/, "", line)
|
||||||
|
if (line ~ /^".*"$/) {
|
||||||
|
title = substr(line, 2, length(line) - 2)
|
||||||
|
} else if (line ~ /^'.*'$/) {
|
||||||
|
title = substr(line, 2, length(line) - 2)
|
||||||
|
} else {
|
||||||
|
title = line
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(full_path)
|
||||||
|
|
||||||
|
if (title != "") return title
|
||||||
|
return default_title
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function compare_paths(p1, p2, parts1, parts2, n1, n2, i, name1, name2, lname1, lname2, w1, w2) {
|
function compare_paths(p1, p2, parts1, parts2, n1, n2, i, name1, name2, lname1, lname2, w1, w2) {
|
||||||
n1 = split(p1, parts1, "/")
|
n1 = split(p1, parts1, "/")
|
||||||
n2 = split(p2, parts2, "/")
|
n2 = split(p2, parts2, "/")
|
||||||
@@ -132,7 +172,7 @@ END {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
printf "<li><a href=\"/%sindex.html\">%s</a><ul>\n", dir_path, title_from_name(parts[i])
|
printf "<li><a href=\"/%sindex.html\">%s</a><ul>\n", dir_path, get_title(this_d, title_from_name(parts[i]))
|
||||||
opened_levels[++depth] = i
|
opened_levels[++depth] = i
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +185,7 @@ END {
|
|||||||
if (parts[n] != "index.md" && !is_single) {
|
if (parts[n] != "index.md" && !is_single) {
|
||||||
path = "/" rel
|
path = "/" rel
|
||||||
gsub(/\.md$/, ".html", path)
|
gsub(/\.md$/, ".html", path)
|
||||||
printf "<li><a href=\"%s\">%s</a></li>\n", path, title_from_name(parts[n])
|
printf "<li><a href=\"%s\">%s</a></li>\n", path, get_title(rel, title_from_name(parts[n]))
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_n = n
|
prev_n = n
|
||||||
|
|||||||
@@ -97,6 +97,23 @@ function read_file(path, out, line, rc) {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function read_file_or_render_md(path, ext, cmd, content, line, rc) {
|
||||||
|
content = ""
|
||||||
|
if (ext == "md") {
|
||||||
|
cmd = "sh \"" script_dir "/markdown.sh\" \"" path "\""
|
||||||
|
while ((cmd | getline line) > 0) {
|
||||||
|
content = content line "\n"
|
||||||
|
}
|
||||||
|
close(cmd)
|
||||||
|
} else {
|
||||||
|
while ((rc = getline line < path) > 0) {
|
||||||
|
content = content line "\n"
|
||||||
|
}
|
||||||
|
close(path)
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
function escape_html(s, t) {
|
function escape_html(s, t) {
|
||||||
t = s
|
t = s
|
||||||
gsub(/&/, "\\&", t)
|
gsub(/&/, "\\&", t)
|
||||||
@@ -189,7 +206,8 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
|||||||
if (force_inline && !is_global_url(src)) {
|
if (force_inline && !is_global_url(src)) {
|
||||||
local_path = resolve_local_path(src)
|
local_path = resolve_local_path(src)
|
||||||
if (local_path != "") {
|
if (local_path != "") {
|
||||||
content = read_file(local_path)
|
ext = ext_of(src)
|
||||||
|
content = read_file_or_render_md(local_path, ext)
|
||||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
@@ -217,7 +235,7 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
|||||||
if (is_inline_text_ext(ext)) {
|
if (is_inline_text_ext(ext)) {
|
||||||
local_path = resolve_local_path(src)
|
local_path = resolve_local_path(src)
|
||||||
if (local_path != "") {
|
if (local_path != "") {
|
||||||
content = read_file(local_path)
|
content = read_file_or_render_md(local_path, ext)
|
||||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
@@ -238,7 +256,7 @@ function render_typed_embed(etype, src, alt, has_alt, local_path, content) {
|
|||||||
if (!is_global_url(src)) {
|
if (!is_global_url(src)) {
|
||||||
local_path = resolve_local_path(src)
|
local_path = resolve_local_path(src)
|
||||||
if (local_path != "") {
|
if (local_path != "") {
|
||||||
content = read_file(local_path)
|
content = read_file_or_render_md(local_path, ext_of(src))
|
||||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,5 +230,7 @@ function restore_html_tags(s, i, val) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gsub(/<a href="https?:\/\/[^"]*"/, "& rel=\"noopener noreferrer\"", line)
|
||||||
|
|
||||||
print line
|
print line
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function mask_plain(s, t) {
|
|||||||
gsub(/\$/, "\034P8\034", t)
|
gsub(/\$/, "\034P8\034", t)
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
BEGIN { in_plain = 0 }
|
BEGIN { in_plain = 0; in_script_style = 0 }
|
||||||
{
|
{
|
||||||
line = $0
|
line = $0
|
||||||
out = ""
|
out = ""
|
||||||
@@ -48,5 +48,41 @@ BEGIN { in_plain = 0 }
|
|||||||
in_plain = 0
|
in_plain = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print out
|
tmp_line = out
|
||||||
|
out2 = ""
|
||||||
|
while (1) {
|
||||||
|
if (!in_script_style) {
|
||||||
|
pos_script = match(tolower(tmp_line), /<script([ >]|$)/)
|
||||||
|
script_start = RSTART; script_len = RLENGTH
|
||||||
|
pos_style = match(tolower(tmp_line), /<style([ >]|$)/)
|
||||||
|
style_start = RSTART; style_len = RLENGTH
|
||||||
|
|
||||||
|
if (pos_script == 0 && pos_style == 0) {
|
||||||
|
out2 = out2 tmp_line
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (pos_script > 0 && (pos_style == 0 || pos_script < pos_style)) {
|
||||||
|
out2 = out2 substr(tmp_line, 1, script_start + script_len - 1)
|
||||||
|
tmp_line = substr(tmp_line, script_start + script_len)
|
||||||
|
in_script_style = 1
|
||||||
|
end_tag = "</script>"
|
||||||
|
} else {
|
||||||
|
out2 = out2 substr(tmp_line, 1, style_start + style_len - 1)
|
||||||
|
tmp_line = substr(tmp_line, style_start + style_len)
|
||||||
|
in_script_style = 1
|
||||||
|
end_tag = "</style>"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pos_end = match(tolower(tmp_line), end_tag)
|
||||||
|
if (pos_end == 0) {
|
||||||
|
out2 = out2 mask_plain(tmp_line)
|
||||||
|
tmp_line = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out2 = out2 mask_plain(substr(tmp_line, 1, RSTART - 1)) substr(tmp_line, RSTART, RLENGTH)
|
||||||
|
tmp_line = substr(tmp_line, RSTART + RLENGTH)
|
||||||
|
in_script_style = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print out2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ BEGIN {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if ($0 ~ /^<pre>/) in_pre = 1
|
if ($0 ~ /<pre>/) in_pre = 1
|
||||||
|
|
||||||
if (in_pre) {
|
if (in_pre) {
|
||||||
if (in_p) { print "</p>"; in_p = 0 }
|
if (in_p) { print "</p>"; in_p = 0 }
|
||||||
@@ -13,7 +13,16 @@ BEGIN {
|
|||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($0 ~ /^<\/?(div|table|p|[ou]l|h[1-6]|[bh]r|blockquote|li|hr)/) {
|
if ($0 ~ /^<\/?(div|table|p|[ou]l|h[1-6]|[bh]r|blockquote|li|hr|section|article|nav|aside|header|footer|dl|dt|dd|script|style|iframe|details|summary|figure|figcaption|audio|video|picture)/) {
|
||||||
|
if (in_p) {
|
||||||
|
print "</p>"
|
||||||
|
in_p = 0
|
||||||
|
}
|
||||||
|
print
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($0 ~ /^[\t ]*!([a-zA-Z])?\[[^\]]*\](\([^)]*\))?[\t ]*$/ || $0 ~ /^[\t ]*!!?\[[^\]]*\](\([^)]*\))?[\t ]*$/) {
|
||||||
if (in_p) {
|
if (in_p) {
|
||||||
print "</p>"
|
print "</p>"
|
||||||
in_p = 0
|
in_p = 0
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ BEGIN {
|
|||||||
style_path = ENVIRON["AWK_STYLE_PATH"]
|
style_path = ENVIRON["AWK_STYLE_PATH"]
|
||||||
head_extra = ENVIRON["AWK_HEAD_EXTRA"]
|
head_extra = ENVIRON["AWK_HEAD_EXTRA"]
|
||||||
header_brand = ENVIRON["AWK_HEADER_BRAND"]
|
header_brand = ENVIRON["AWK_HEADER_BRAND"]
|
||||||
|
lang = ENVIRON["AWK_LANG"]
|
||||||
|
version = ENVIRON["AWK_VERSION"]
|
||||||
|
content_warning = ENVIRON["AWK_CONTENT_WARNING"]
|
||||||
if (current_url != "") {
|
if (current_url != "") {
|
||||||
nav = replace_all(nav, "href=\"" current_url "\"", "href=\"" current_url "\" class=\"current-page\"")
|
nav = replace_all(nav, "href=\"" current_url "\"", "href=\"" current_url "\" class=\"current-page\"")
|
||||||
}
|
}
|
||||||
@@ -24,11 +27,13 @@ BEGIN {
|
|||||||
{
|
{
|
||||||
line = $0
|
line = $0
|
||||||
line = replace_all(line, "{{TITLE}}", title)
|
line = replace_all(line, "{{TITLE}}", title)
|
||||||
|
line = replace_all(line, "{{LANG}}", lang)
|
||||||
line = replace_all(line, "{{NAV}}", nav)
|
line = replace_all(line, "{{NAV}}", nav)
|
||||||
line = replace_all(line, "{{FOOTER}}", footer)
|
line = replace_all(line, "{{FOOTER}}", footer)
|
||||||
line = replace_all(line, "{{CSS}}", style_path)
|
line = replace_all(line, "{{CSS}}", style_path)
|
||||||
line = replace_all(line, "{{HEAD_EXTRA}}", head_extra)
|
line = replace_all(line, "{{HEAD_EXTRA}}", head_extra)
|
||||||
line = replace_all(line, "{{HEADER_BRAND}}", header_brand)
|
line = replace_all(line, "{{HEADER_BRAND}}", header_brand)
|
||||||
|
line = replace_all(line, "{{VERSION}}", version)
|
||||||
|
|
||||||
pos = index(line, "{{CONTENT}}")
|
pos = index(line, "{{CONTENT}}")
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
|
|||||||
50
awk/toc.awk
Normal file
50
awk/toc.awk
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
BEGIN {
|
||||||
|
toc_str = "<ol class=\"toc\">\n"
|
||||||
|
has_toc = 0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
lines[++n] = $0
|
||||||
|
if ($0 ~ /<pre>/) in_pre = 1
|
||||||
|
if (!in_pre && $0 ~ /\{\{TOC\}\}/) {
|
||||||
|
has_toc = 1
|
||||||
|
toc_lines[n] = 1
|
||||||
|
}
|
||||||
|
if ($0 ~ /<\/pre>/) in_pre = 0
|
||||||
|
if (match($0, /<h[23][^>]*>/)) {
|
||||||
|
tag_len = RLENGTH
|
||||||
|
title_start = RSTART + tag_len
|
||||||
|
title_str = substr($0, title_start)
|
||||||
|
title_end = index(title_str, "</h")
|
||||||
|
if (title_end > 0) {
|
||||||
|
title = substr(title_str, 1, title_end - 1)
|
||||||
|
gsub(/<[^>]+>/, "", title)
|
||||||
|
|
||||||
|
# extract id
|
||||||
|
id_start = match($0, /id="[^"]*"/)
|
||||||
|
if (id_start > 0) {
|
||||||
|
id_str = substr($0, id_start + 4)
|
||||||
|
id_end = index(id_str, "\"")
|
||||||
|
id = substr(id_str, 1, id_end - 1)
|
||||||
|
|
||||||
|
# what tag? level
|
||||||
|
level = substr($0, match($0, /<h[23]/) + 2, 1)
|
||||||
|
|
||||||
|
if (level == "2") {
|
||||||
|
toc_str = toc_str "<li class=\"toc-h2\"><a href=\"#" id "\">" title "</a></li>\n"
|
||||||
|
} else if (level == "3") {
|
||||||
|
toc_str = toc_str "<li class=\"toc-h3\"><a href=\"#" id "\">" title "</a></li>\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
toc_str = toc_str "</ol>"
|
||||||
|
for (i = 1; i <= n; i++) {
|
||||||
|
if (has_toc && toc_lines[i] && lines[i] ~ /^[[:space:]]*\{\{TOC\}\}[[:space:]]*$/) {
|
||||||
|
toc_lines[i] = 0 # Mark as processed if we want, but not strictly needed
|
||||||
|
sub(/\{\{TOC\}\}/, toc_str, lines[i])
|
||||||
|
}
|
||||||
|
print lines[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
708
lib/builder.sh
Normal file
708
lib/builder.sh
Normal file
@@ -0,0 +1,708 @@
|
|||||||
|
needs_rebuild() {
|
||||||
|
src_file="$1"
|
||||||
|
out_file="$2"
|
||||||
|
[ ! -f "$out_file" ] && return 0
|
||||||
|
[ "$src_file" -nt "$out_file" ] && return 0
|
||||||
|
[ -f "./site.conf" ] && [ "./site.conf" -nt "$out_file" ] && return 0
|
||||||
|
[ -f "$src/site.conf" ] && [ "$src/site.conf" -nt "$out_file" ] && return 0
|
||||||
|
[ -f "$template" ] && [ "$template" -nt "$out_file" ] && return 0
|
||||||
|
[ -f "$script_dir/styles/$style.css" ] && [ "$script_dir/styles/$style.css" -nt "$out_file" ] && return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
build_site() {
|
||||||
|
echo "Building site from '$src' to '$out'..."
|
||||||
|
|
||||||
|
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
|
||||||
|
rel_dir="${dir#"$src"}"
|
||||||
|
rel_dir="${rel_dir#/}"
|
||||||
|
[ -z "$rel_dir" ] && rel_dir="."
|
||||||
|
out_dir="$out/$rel_dir"
|
||||||
|
mkdir -p "$out_dir"
|
||||||
|
|
||||||
|
if [ -f "$dir/styles.css" ]; then
|
||||||
|
if needs_rebuild "$dir/styles.css" "$out_dir/styles.css"; then
|
||||||
|
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||||
|
fi
|
||||||
|
elif [ -f "$dir/style.css" ]; then
|
||||||
|
if needs_rebuild "$dir/style.css" "$out_dir/styles.css"; then
|
||||||
|
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$dir_indexes" != "true" ] && continue
|
||||||
|
|
||||||
|
has_custom_index="false"
|
||||||
|
has_list="false"
|
||||||
|
if [ -f "$dir/index.md" ]; then
|
||||||
|
has_custom_index="true"
|
||||||
|
if grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$dir/index.md" 2>/dev/null; then
|
||||||
|
has_list="true"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$has_custom_index" = "false" ] || [ "$has_list" = "true" ]; then
|
||||||
|
is_posts_dir="false"
|
||||||
|
if [ -n "$posts_dir" ] && { [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; }; then
|
||||||
|
is_posts_dir="true"
|
||||||
|
fi
|
||||||
|
if [ "$single_file_index" = "true" ] && [ "$is_posts_dir" = "false" ] && [ "$has_list" = "false" ]; then
|
||||||
|
md_count=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md" | wc -l)
|
||||||
|
if [ "$md_count" -eq 1 ]; then
|
||||||
|
md_file=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md")
|
||||||
|
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||||
|
target_url="/$rel_dir/index.html"
|
||||||
|
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||||
|
if needs_rebuild "$md_file" "$out_dir/index.html"; then
|
||||||
|
parse_frontmatter "$md_file"
|
||||||
|
if [ -n "$fm_content_warning" ]; then
|
||||||
|
content_out_file="$out_dir/content.html"
|
||||||
|
content_rel_url="/$rel_dir/content.html"
|
||||||
|
[ "$rel_dir" = "." ] && content_rel_url="/content.html"
|
||||||
|
|
||||||
|
is_cw_content_page="true"
|
||||||
|
render_markdown "$md_file" "$is_home" "$target_url" > "$content_out_file"
|
||||||
|
is_cw_content_page="false"
|
||||||
|
|
||||||
|
generate_content_warning_page "$fm_title" "$fm_content_warning" "$content_rel_url" "$target_url" "$out_dir/index.html" "false"
|
||||||
|
else
|
||||||
|
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
temp_index="$KEWT_TMPDIR/index.md"
|
||||||
|
temp_list="$KEWT_TMPDIR/list.md"
|
||||||
|
: > "$temp_list"
|
||||||
|
|
||||||
|
if [ "$has_custom_index" = "false" ]; then
|
||||||
|
display_dir="${rel_dir#.}"
|
||||||
|
[ -z "$display_dir" ] && display_dir="/"
|
||||||
|
echo "# Index of $display_dir" > "$temp_index"
|
||||||
|
echo "" >> "$temp_index"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
sort_args=""
|
||||||
|
# If this is the posts dir reverse
|
||||||
|
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||||
|
sort_args="-r"
|
||||||
|
fi
|
||||||
|
|
||||||
|
temp_entries="$KEWT_TMPDIR/entries_$$.txt"
|
||||||
|
: > "$temp_entries"
|
||||||
|
|
||||||
|
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | while read -r entry; do
|
||||||
|
name="${entry##*/}"
|
||||||
|
case "$name" in
|
||||||
|
template.html|site.conf|style.css|index.md) continue ;;
|
||||||
|
esac
|
||||||
|
if [ -d "$entry" ]; then
|
||||||
|
echo "${name}|- [${name}/](${name}/index.html)" >> "$temp_entries"
|
||||||
|
elif [ "${entry%.md}" != "$entry" ]; then
|
||||||
|
label="${name%.md}"
|
||||||
|
|
||||||
|
# Parse frontmatter for date/title/draft
|
||||||
|
parse_frontmatter "$entry"
|
||||||
|
[ "$fm_draft" = "true" ] && continue
|
||||||
|
|
||||||
|
# Try to get first heading
|
||||||
|
post_h="$fm_title"
|
||||||
|
if [ -z "$post_h" ]; then
|
||||||
|
post_h=$(grep -m 1 '^# ' "$entry" | sed 's/^# *//')
|
||||||
|
if [ -n "$post_h" ]; then
|
||||||
|
post_h=$(echo "$post_h" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
is_post_entry="false"
|
||||||
|
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||||
|
is_post_entry="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$post_h" ]; then
|
||||||
|
if [ "$is_post_entry" = "true" ]; then
|
||||||
|
# Use frontmatter date if available, else parse from filename
|
||||||
|
if [ -n "$fm_date" ]; then
|
||||||
|
p_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
p_time=""
|
||||||
|
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
p_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
p_time="00:00"
|
||||||
|
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -n "$p_time" ]; then
|
||||||
|
label="$post_h - $p_date $p_time"
|
||||||
|
else
|
||||||
|
label="$post_h - $p_date"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
label="$post_h"
|
||||||
|
fi
|
||||||
|
elif [ "$is_post_entry" = "true" ]; then
|
||||||
|
# No heading; use date
|
||||||
|
if [ -n "$fm_date" ]; then
|
||||||
|
p_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
p_time=""
|
||||||
|
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
p_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
if [ -n "$p_time" ]; then
|
||||||
|
label="$p_date $p_time"
|
||||||
|
else
|
||||||
|
label="$p_date"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
p_time="00:00"
|
||||||
|
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
label="$p_date $p_time"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$is_post_entry" = "true" ]; then
|
||||||
|
sort_key="${p_date} ${p_time}"
|
||||||
|
else
|
||||||
|
sort_key="$name"
|
||||||
|
fi
|
||||||
|
echo "${sort_key}|- [$label](${name%.md}.html)|$name|${name%.md}.html" >> "$temp_entries"
|
||||||
|
else
|
||||||
|
echo "${name}|- [$name]($name)|$name|$name" >> "$temp_entries"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$is_posts_dir" = "true" ]; then
|
||||||
|
LC_ALL=C sort $sort_args "$temp_entries" > "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||||
|
cut -d'|' -f2 "$KEWT_TMPDIR/sorted_entries_$$.txt" >> "$temp_list"
|
||||||
|
mkdir -p "$KEWT_TMPDIR/prevnext"
|
||||||
|
awk -F'|' '
|
||||||
|
{
|
||||||
|
name[NR] = $3
|
||||||
|
url[NR] = $4
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
for(i=1; i<=NR; i++) {
|
||||||
|
prev_str = ""
|
||||||
|
next_str = ""
|
||||||
|
if(i > 1) {
|
||||||
|
next_str = "[Next >](" url[i-1] ")"
|
||||||
|
}
|
||||||
|
if(i < NR) {
|
||||||
|
prev_str = "[< Previous](" url[i+1] ")"
|
||||||
|
}
|
||||||
|
if (prev_str != "" || next_str != "") {
|
||||||
|
out = "'"$KEWT_TMPDIR"'/prevnext/" name[i]
|
||||||
|
printf "%s|%s\n", prev_str, next_str > out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||||
|
rm -f "$KEWT_TMPDIR/sorted_entries_$$.txt"
|
||||||
|
else
|
||||||
|
LC_ALL=C sort $sort_args "$temp_entries" | cut -d'|' -f2 >> "$temp_list"
|
||||||
|
fi
|
||||||
|
rm -f "$temp_entries"
|
||||||
|
|
||||||
|
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||||
|
target_url="/$rel_dir/index.html"
|
||||||
|
[ "$rel_dir" = "." ] && target_url="/index.html"
|
||||||
|
|
||||||
|
num_items=$(wc -l < "$temp_list")
|
||||||
|
if [ "$is_posts_dir" = "true" ] && [ -n "$posts_per_page" ] && [ "$posts_per_page" -gt 0 ] && [ "$num_items" -gt "$posts_per_page" ]; then
|
||||||
|
num_pages=$(( (num_items + posts_per_page - 1) / posts_per_page ))
|
||||||
|
for p in $(seq 1 $num_pages); do
|
||||||
|
chunk_list="$KEWT_TMPDIR/chunk.md"
|
||||||
|
start_line=$(( (p - 1) * posts_per_page + 1 ))
|
||||||
|
tail -n +$start_line "$temp_list" | head -n "$posts_per_page" > "$chunk_list"
|
||||||
|
|
||||||
|
base_url_dir="$(dirname "$target_url")"
|
||||||
|
[ "$base_url_dir" = "/" ] && base_url_dir=""
|
||||||
|
|
||||||
|
nav_html="<div class=\"pagination\">"
|
||||||
|
if [ "$p" -gt 1 ]; then
|
||||||
|
if [ "$p" -eq 2 ]; then
|
||||||
|
nav_html="$nav_html <a href=\"$base_url_dir/index.html\" class=\"prev-page\">« Prev</a> "
|
||||||
|
else
|
||||||
|
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p-1))/index.html\" class=\"prev-page\">« Prev</a> "
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
nav_html="$nav_html <span class=\"page-number\">Page $p of $num_pages</span> "
|
||||||
|
if [ "$p" -lt "$num_pages" ]; then
|
||||||
|
nav_html="$nav_html <a href=\"$base_url_dir/page/$((p+1))/index.html\" class=\"next-page\">Next »</a> "
|
||||||
|
fi
|
||||||
|
nav_html="$nav_html</div>"
|
||||||
|
|
||||||
|
echo "" >> "$chunk_list"
|
||||||
|
echo "$nav_html" >> "$chunk_list"
|
||||||
|
|
||||||
|
temp_index_p="$KEWT_TMPDIR/index_p$p.md"
|
||||||
|
if [ "$has_custom_index" = "false" ]; then
|
||||||
|
display_dir="${rel_dir#.}"
|
||||||
|
[ -z "$display_dir" ] && display_dir="/"
|
||||||
|
echo "# Index of $display_dir" > "$temp_index_p"
|
||||||
|
echo "" >> "$temp_index_p"
|
||||||
|
else
|
||||||
|
: > "$temp_index_p"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$has_custom_index" = "true" ]; then
|
||||||
|
awk '
|
||||||
|
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||||
|
while((getline line < "'"$chunk_list"'") > 0) print line
|
||||||
|
close("'"$chunk_list"'")
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{ print }
|
||||||
|
' "$dir/index.md" >> "$temp_index_p"
|
||||||
|
else
|
||||||
|
cat "$chunk_list" >> "$temp_index_p"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$p" -eq 1 ]; then
|
||||||
|
out_file="$out_dir/index.html"
|
||||||
|
target_url_p="$target_url"
|
||||||
|
else
|
||||||
|
out_file="$out_dir/page/$p/index.html"
|
||||||
|
target_url_p="$base_url_dir/page/$p/index.html"
|
||||||
|
mkdir -p "$(dirname "$out_file")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
render_markdown "$temp_index_p" "$is_home" "$target_url_p" > "$out_file"
|
||||||
|
rm -f "$temp_index_p" "$chunk_list"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
if [ "$has_custom_index" = "true" ]; then
|
||||||
|
awk '
|
||||||
|
/^[[:space:]]*\{\{LIST\}\}[[:space:]]*$/ {
|
||||||
|
while((getline line < "'"$temp_list"'") > 0) print line
|
||||||
|
close("'"$temp_list"'")
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{ print }
|
||||||
|
' "$dir/index.md" > "$temp_index"
|
||||||
|
else
|
||||||
|
cat "$temp_list" >> "$temp_index"
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_rebuild="false"
|
||||||
|
needs_rebuild "$dir" "$out_dir/index.html" && do_rebuild="true"
|
||||||
|
[ "$has_custom_index" = "true" ] && needs_rebuild "$dir/index.md" "$out_dir/index.html" && do_rebuild="true"
|
||||||
|
|
||||||
|
if [ "$do_rebuild" = "false" ] && [ -f "$out_dir/index.html" ]; then
|
||||||
|
for _child in "$dir"/*; do
|
||||||
|
[ -e "$_child" ] || continue
|
||||||
|
if [ "$_child" -nt "$out_dir/index.html" ]; then
|
||||||
|
do_rebuild="true"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$do_rebuild" = "true" ]; then
|
||||||
|
if [ "$has_custom_index" = "true" ]; then
|
||||||
|
parse_frontmatter "$dir/index.md"
|
||||||
|
else
|
||||||
|
fm_content_warning=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$fm_content_warning" ]; then
|
||||||
|
content_out_file="$out_dir/content.html"
|
||||||
|
content_rel_url="/$rel_dir/content.html"
|
||||||
|
[ "$rel_dir" = "." ] && content_rel_url="/content.html"
|
||||||
|
|
||||||
|
is_cw_content_page="true"
|
||||||
|
render_markdown "$temp_index" "$is_home" "$target_url" > "$content_out_file"
|
||||||
|
is_cw_content_page="false"
|
||||||
|
|
||||||
|
generate_content_warning_page "$fm_title" "$fm_content_warning" "$content_rel_url" "$target_url" "$out_dir/index.html" "false"
|
||||||
|
else
|
||||||
|
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f "$temp_index" "$temp_list"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -f "$script_dir/styles/$style.css" ] && needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then
|
||||||
|
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
|
||||||
|
rel_path="${file#"$src"}"
|
||||||
|
rel_path="${rel_path#/}"
|
||||||
|
dir_rel=$(dirname "$rel_path")
|
||||||
|
out_dir="$out/$dir_rel"
|
||||||
|
|
||||||
|
case "${file##*/}" in
|
||||||
|
template.html|site.conf|style.css|styles.css) continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
is_preserved=0
|
||||||
|
if [ -n "$(eval "find \"$file\" \( $PRESERVE_ARGS \) -print")" ]; then
|
||||||
|
is_preserved=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
is_posts_dir_2="false"
|
||||||
|
if [ -n "$posts_dir" ] && { [ "$dir_rel" = "$posts_dir" ] || [ "./$dir_rel" = "$posts_dir" ]; }; then
|
||||||
|
is_posts_dir_2="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ] && [ "$is_posts_dir_2" = "false" ]; then
|
||||||
|
md_count=$(find "$(dirname "$file")" ! -name "$(basename "$(dirname "$file")")" -prune -name "*.md" | wc -l)
|
||||||
|
[ "$md_count" -eq 1 ] && continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
|
||||||
|
# Skip draft files
|
||||||
|
parse_frontmatter "$file"
|
||||||
|
if [ "$fm_draft" = "true" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
|
||||||
|
out_file="$out/${rel_path%.md}.html"
|
||||||
|
if needs_rebuild "$file" "$out_file"; then
|
||||||
|
if [ -n "$fm_content_warning" ]; then
|
||||||
|
content_out_file="$out/${rel_path%.md}-content.html"
|
||||||
|
content_rel_url="/${rel_path%.md}-content.html"
|
||||||
|
orig_rel_url="/${rel_path%.md}.html"
|
||||||
|
|
||||||
|
is_cw_content_page="true"
|
||||||
|
render_markdown "$file" "$is_home" "$orig_rel_url" > "$content_out_file"
|
||||||
|
is_cw_content_page="false"
|
||||||
|
|
||||||
|
generate_content_warning_page "$fm_title" "$fm_content_warning" "$content_rel_url" "$orig_rel_url" "$out_file" "false"
|
||||||
|
else
|
||||||
|
render_markdown "$file" "$is_home" > "$out_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if needs_rebuild "$file" "$out/$rel_path"; then
|
||||||
|
cp "$file" "$out/$rel_path"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$error_page" ] && [ ! -f "$out/$error_page" ]; then
|
||||||
|
temp_404="$KEWT_TMPDIR/404_gen.md"
|
||||||
|
echo "# 404 - Not Found" > "$temp_404"
|
||||||
|
echo "" >> "$temp_404"
|
||||||
|
echo "The requested page could not be found." >> "$temp_404"
|
||||||
|
render_markdown "$temp_404" "false" "/$error_page" > "$out/$error_page"
|
||||||
|
rm -f "$temp_404"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$base_url" ]; then
|
||||||
|
sitemap_file="$out/sitemap.xml"
|
||||||
|
base_url="${base_url%/}"
|
||||||
|
today=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
|
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$sitemap_file"
|
||||||
|
printf '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n' >> "$sitemap_file"
|
||||||
|
|
||||||
|
find "$out" -type f -name "*.html" -print | sort | while IFS= read -r html_file; do
|
||||||
|
rel_url="${html_file#"$out"}"
|
||||||
|
|
||||||
|
# Don't include 404 in the sitemap (duh)
|
||||||
|
[ "${rel_url#/}" = "$error_page" ] && continue
|
||||||
|
|
||||||
|
{
|
||||||
|
printf ' <url>\n'
|
||||||
|
printf ' <loc>%s%s</loc>\n' "$base_url" "$rel_url"
|
||||||
|
printf ' <lastmod>%s</lastmod>\n' "$today"
|
||||||
|
printf ' </url>\n'
|
||||||
|
} >> "$sitemap_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '</urlset>\n' >> "$sitemap_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||||
|
feed_path="$out/$feed_file"
|
||||||
|
base_url_feed="${base_url%/}"
|
||||||
|
build_date=$(date -u '+%a, %d %b %Y %H:%M:%S +0000')
|
||||||
|
|
||||||
|
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$feed_path"
|
||||||
|
{
|
||||||
|
printf '<rss version="2.0">\n'
|
||||||
|
printf ' <channel>\n'
|
||||||
|
printf ' <title>%s</title>\n' "$title"
|
||||||
|
printf ' <link>%s</link>\n' "$base_url_feed"
|
||||||
|
printf ' <description>%s</description>\n' "$title"
|
||||||
|
printf ' <lastBuildDate>%s</lastBuildDate>\n' "$build_date"
|
||||||
|
} >> "$feed_path"
|
||||||
|
|
||||||
|
temp_feed_files="$KEWT_TMPDIR/feed_files_$$.txt"
|
||||||
|
: > "$temp_feed_files"
|
||||||
|
|
||||||
|
find "$src" -type f -name '*.md' -path "*${posts_dir:-__no_posts__}*" -print | while IFS= read -r post_file; do
|
||||||
|
post_basename=$(basename "$post_file" .md)
|
||||||
|
# Parse frontmatter to get date
|
||||||
|
parse_frontmatter "$post_file"
|
||||||
|
[ "$fm_draft" = "true" ] && continue
|
||||||
|
if [ -n "$fm_date" ]; then
|
||||||
|
post_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
post_time="00:00"
|
||||||
|
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
post_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
post_date=$(echo "$post_basename" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
post_time="00:00"
|
||||||
|
if echo "$post_basename" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
post_time=$(echo "$post_basename" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "${post_date} ${post_time}|${post_file}" >> "$temp_feed_files"
|
||||||
|
done
|
||||||
|
|
||||||
|
LC_ALL=C sort -r "$temp_feed_files" | cut -d'|' -f2- | while IFS= read -r post_file; do
|
||||||
|
post_basename=$(basename "$post_file" .md)
|
||||||
|
|
||||||
|
# Parse frontmatter
|
||||||
|
parse_frontmatter "$post_file"
|
||||||
|
[ "$fm_draft" = "true" ] && continue
|
||||||
|
|
||||||
|
# Use frontmatter date, fallback to filename
|
||||||
|
if [ -n "$fm_date" ]; then
|
||||||
|
post_date=$(echo "$fm_date" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
post_time="00:00"
|
||||||
|
if echo "$fm_date" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
post_time=$(echo "$fm_date" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}[ T_-]\?\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
post_date=$(echo "$post_basename" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
post_time="00:00"
|
||||||
|
if echo "$post_basename" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
post_time=$(echo "$post_basename" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
post_slug=$(echo "$post_basename" | sed -e 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}//' -e 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}//' -e 's/^[_\-]//')
|
||||||
|
|
||||||
|
post_heading="$fm_title"
|
||||||
|
if [ -z "$post_heading" ]; then
|
||||||
|
post_heading=$(grep -m 1 '^# ' "$post_file" | sed 's/^# *//')
|
||||||
|
fi
|
||||||
|
if [ -z "$post_heading" ]; then
|
||||||
|
if [ -n "$post_slug" ] && ! echo "$post_slug" | grep -q '^[0-9]\+$'; then
|
||||||
|
post_heading=$(echo "$post_slug" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)}1')
|
||||||
|
else
|
||||||
|
post_heading="Post"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
post_heading=$(echo "$post_heading" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||||
|
feed_post_title="$post_heading - $post_date $post_time"
|
||||||
|
|
||||||
|
rel_path="${post_file#"$src"}"
|
||||||
|
rel_path="${rel_path#/}"
|
||||||
|
post_url="$base_url_feed/${rel_path%.md}.html"
|
||||||
|
|
||||||
|
if date -u -d "$post_date $post_time" '+%a, %d %b %Y %H:%M:%S +0000' >/dev/null 2>&1; then
|
||||||
|
pub_date=$(date -u -d "$post_date $post_time" '+%a, %d %b %Y %H:%M:%S +0000')
|
||||||
|
else
|
||||||
|
pub_year=$(echo "$post_date" | cut -d- -f1)
|
||||||
|
pub_month=$(echo "$post_date" | cut -d- -f2)
|
||||||
|
pub_day=$(echo "$post_date" | cut -d- -f3)
|
||||||
|
# zero-padded
|
||||||
|
pub_day=$(printf '%02d' "${pub_day#0}")
|
||||||
|
case "$pub_month" in
|
||||||
|
01) pub_mon="Jan" ;; 02) pub_mon="Feb" ;; 03) pub_mon="Mar" ;;
|
||||||
|
04) pub_mon="Apr" ;; 05) pub_mon="May" ;; 06) pub_mon="Jun" ;;
|
||||||
|
07) pub_mon="Jul" ;; 08) pub_mon="Aug" ;; 09) pub_mon="Sep" ;;
|
||||||
|
10) pub_mon="Oct" ;; 11) pub_mon="Nov" ;; 12) pub_mon="Dec" ;;
|
||||||
|
esac
|
||||||
|
pub_date="Mon, ${pub_day} ${pub_mon} ${pub_year} ${post_time}:00 +0000"
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
printf ' <item>\n'
|
||||||
|
printf ' <title>%s</title>\n' "$feed_post_title"
|
||||||
|
printf ' <link>%s</link>\n' "$post_url"
|
||||||
|
printf ' <guid>%s</guid>\n' "$post_url"
|
||||||
|
printf ' <pubDate>%s</pubDate>\n' "$pub_date"
|
||||||
|
printf ' </item>\n'
|
||||||
|
} >> "$feed_path"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf ' </channel>\n' >> "$feed_path"
|
||||||
|
printf '</rss>\n' >> "$feed_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$generate_search" = "true" ] || [ "$generate_tags" = "true" ]; then
|
||||||
|
if [ "$generate_search" = "true" ]; then
|
||||||
|
printf '[\n' > "$out/search.json"
|
||||||
|
fi
|
||||||
|
first_search_item="true"
|
||||||
|
temp_tags="$KEWT_TMPDIR/tags_$$.txt"
|
||||||
|
: > "$temp_tags"
|
||||||
|
|
||||||
|
eval "find \"$src\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print" | sort | while IFS= read -r md_file; do
|
||||||
|
is_index="false"
|
||||||
|
[ "$(basename "$md_file")" = "index.md" ] && is_index="true"
|
||||||
|
|
||||||
|
rel_path="${md_file#"$src"}"
|
||||||
|
rel_path="${rel_path#/}"
|
||||||
|
if [ "$is_index" = "true" ]; then
|
||||||
|
if [ "$rel_path" = "index.md" ]; then
|
||||||
|
md_url="/index.html"
|
||||||
|
else
|
||||||
|
md_url="/${rel_path%/index.md}/index.html"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
md_url="/${rel_path%.md}.html"
|
||||||
|
if [ "$single_file_index" = "true" ]; then
|
||||||
|
dir_of_file="$(dirname "$md_file")"
|
||||||
|
rel_dir_of_file="${dir_of_file#"$src"}"
|
||||||
|
rel_dir_of_file="${rel_dir_of_file#/}"
|
||||||
|
[ -z "$rel_dir_of_file" ] && rel_dir_of_file="."
|
||||||
|
|
||||||
|
is_posts_dir_search="false"
|
||||||
|
if [ -n "$posts_dir" ] && { [ "$rel_dir_of_file" = "$posts_dir" ] || [ "./$rel_dir_of_file" = "$posts_dir" ]; }; then
|
||||||
|
is_posts_dir_search="true"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$is_posts_dir_search" = "false" ] && [ ! -f "$dir_of_file/index.md" ]; then
|
||||||
|
md_count_search=$(find "$dir_of_file" ! -name "$(basename "$dir_of_file")" -prune -name "*.md" | wc -l)
|
||||||
|
if [ "$md_count_search" -eq 1 ]; then
|
||||||
|
if [ "$rel_dir_of_file" = "." ]; then
|
||||||
|
md_url="/index.html"
|
||||||
|
else
|
||||||
|
md_url="/$rel_dir_of_file/index.html"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
parse_frontmatter "$md_file"
|
||||||
|
[ "$fm_draft" = "true" ] && continue
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
289
lib/generator.sh
Normal file
289
lib/generator.sh
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
SEARCH_FORM_FOOTER='<form class="kewt-search-footer" action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form>'
|
||||||
|
SEARCH_FORM_HEADER='<form class="kewt-search-header" action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form>'
|
||||||
|
SEARCH_FORM_NAV='<div class="kewt-search-nav"><form action="/search.html" method="get"><input type="text" name="q" placeholder="Search..." required><button type="submit">Go</button></form></div>'
|
||||||
|
|
||||||
|
generate_nav() {
|
||||||
|
dinfo=$(eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -print" | sort | AWK_SRC="$1" awk -f "$awk_dir/collect_dir_info.awk")
|
||||||
|
find_cmd="find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print"
|
||||||
|
if [ -n "$posts_dir" ] && [ -d "$1/$posts_dir" ]; then
|
||||||
|
find_cmd="$find_cmd && echo \"$1/$posts_dir/index.md\""
|
||||||
|
fi
|
||||||
|
eval "$find_cmd" | sort -u | AWK_SRC="$1" AWK_SINGLE_FILE_INDEX="$single_file_index" AWK_FLATTEN="$flatten" AWK_ORDER="$order" AWK_HOME_NAME="$home_name" AWK_SHOW_HOME_IN_NAV="$show_home_in_nav" AWK_DINFO="$dinfo" awk -f "$awk_dir/generate_sidebar.awk"
|
||||||
|
}
|
||||||
|
escape_html_text() {
|
||||||
|
printf '%s' "$1" | sed \
|
||||||
|
-e 's/&/\&/g' \
|
||||||
|
-e 's/</\</g' \
|
||||||
|
-e 's/>/\>/g'
|
||||||
|
}
|
||||||
|
escape_html_attr() {
|
||||||
|
printf '%s' "$1" | sed \
|
||||||
|
-e 's/&/\&/g' \
|
||||||
|
-e 's/"/\"/g' \
|
||||||
|
-e 's/</\</g' \
|
||||||
|
-e 's/>/\>/g'
|
||||||
|
}
|
||||||
|
parse_frontmatter() {
|
||||||
|
_fm_file="$1"
|
||||||
|
_fm_out="$KEWT_TMPDIR/fm_vals.txt"
|
||||||
|
: > "$_fm_out"
|
||||||
|
awk -v fm_out="$_fm_out" -f "$awk_dir/frontmatter.awk" "$_fm_file" > /dev/null
|
||||||
|
fm_title=""
|
||||||
|
fm_date=""
|
||||||
|
fm_draft=""
|
||||||
|
fm_description=""
|
||||||
|
fm_content_warning=""
|
||||||
|
fm_tags=""
|
||||||
|
while IFS='=' read -r _fk _fv; do
|
||||||
|
case "$_fk" in
|
||||||
|
title) fm_title="$_fv" ;;
|
||||||
|
date) fm_date="$_fv" ;;
|
||||||
|
draft) fm_draft="$_fv" ;;
|
||||||
|
description) fm_description="$_fv" ;;
|
||||||
|
content_warning) fm_content_warning="$_fv" ;;
|
||||||
|
tags) fm_tags="$_fv" ;;
|
||||||
|
esac
|
||||||
|
done < "$_fm_out"
|
||||||
|
rm -f "$_fm_out"
|
||||||
|
}
|
||||||
|
nav_links_html() {
|
||||||
|
[ -n "$nav_links" ] || return
|
||||||
|
|
||||||
|
old_ifs=$IFS
|
||||||
|
set -f
|
||||||
|
IFS=','
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
set -- $nav_links
|
||||||
|
IFS=$old_ifs
|
||||||
|
set +f
|
||||||
|
|
||||||
|
[ $# -gt 0 ] || return
|
||||||
|
|
||||||
|
printf '<ul class="nav-extra-links">\n'
|
||||||
|
for raw_link in "$@"; do
|
||||||
|
link=$(printf '%s' "$raw_link" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||||
|
[ -n "$link" ] || continue
|
||||||
|
|
||||||
|
case "$link" in
|
||||||
|
\[*\]\(*\))
|
||||||
|
label=${link#\[}
|
||||||
|
label=${label%%\]*}
|
||||||
|
link_url=${link#*](}
|
||||||
|
link_url=${link_url%)}
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
link_url=$link
|
||||||
|
label=$(printf '%s' "$link" | sed \
|
||||||
|
-e 's|^[A-Za-z][A-Za-z0-9+.-]*://||' \
|
||||||
|
-e 's|/$||')
|
||||||
|
[ -n "$label" ] || label="$link"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -n "$link_url" ] || continue
|
||||||
|
[ -n "$label" ] || label="$link_url"
|
||||||
|
|
||||||
|
link_attr=$(escape_html_attr "$link_url")
|
||||||
|
label_text=$(escape_html_text "$label")
|
||||||
|
printf '<li><a href="%s">%s</a></li>\n' "$link_attr" "$label_text"
|
||||||
|
done
|
||||||
|
printf '</ul>'
|
||||||
|
}
|
||||||
|
find_closest() {
|
||||||
|
target="$1"
|
||||||
|
start_dir="$2"
|
||||||
|
curr="$start_dir"
|
||||||
|
while [ "$curr" != "$src" ] && [ "$curr" != "." ] && [ "$curr" != "/" ]; do
|
||||||
|
if [ -f "$curr/$target" ]; then
|
||||||
|
echo "$curr/$target"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
curr=$(dirname "$curr")
|
||||||
|
done
|
||||||
|
if [ -f "$src/$target" ]; then
|
||||||
|
echo "$src/$target"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
copy_style_with_resolved_vars() {
|
||||||
|
src_style="$1"
|
||||||
|
out_style="$2"
|
||||||
|
awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style"
|
||||||
|
}
|
||||||
|
render_markdown() {
|
||||||
|
file="$1"
|
||||||
|
is_home="$2"
|
||||||
|
url_override="$3"
|
||||||
|
|
||||||
|
if [ -n "$url_override" ]; then
|
||||||
|
current_url="$url_override"
|
||||||
|
else
|
||||||
|
rel_path="${file#"$src"}"
|
||||||
|
rel_path="${rel_path#/}"
|
||||||
|
current_url="/${rel_path%.md}.html"
|
||||||
|
fi
|
||||||
|
|
||||||
|
content_file="$file"
|
||||||
|
if [ -n "$posts_dir" ] && [ "$file" != "$src/$posts_dir/index.md" ]; then
|
||||||
|
rel_dir_of_url=$(dirname "$current_url")
|
||||||
|
rel_dir_of_url="${rel_dir_of_url#/}"
|
||||||
|
if { [ "$rel_dir_of_url" = "$posts_dir" ] || [ "./$rel_dir_of_url" = "$posts_dir" ]; } && [ "$(basename "$current_url")" != "index.html" ]; then
|
||||||
|
temp_post_with_backlink="$KEWT_TMPDIR/post_with_backlink_$$.md"
|
||||||
|
printf "[< Back](index.html)\n\n" > "$temp_post_with_backlink"
|
||||||
|
awk -f "$awk_dir/frontmatter.awk" "$file" >> "$temp_post_with_backlink"
|
||||||
|
|
||||||
|
post_md_name="$(basename "$current_url" .html).md"
|
||||||
|
prevnext_file="$KEWT_TMPDIR/prevnext/$post_md_name"
|
||||||
|
if [ -f "$prevnext_file" ]; then
|
||||||
|
IFS='|' read -r prev_str next_str < "$prevnext_file"
|
||||||
|
|
||||||
|
printf "\n\n---\n<div class=\"post-nav\">\n" >> "$temp_post_with_backlink"
|
||||||
|
if [ -n "$prev_str" ]; then
|
||||||
|
printf "<span class=\"prev-post\">%s</span>\n" "$prev_str" >> "$temp_post_with_backlink"
|
||||||
|
fi
|
||||||
|
if [ -n "$next_str" ]; then
|
||||||
|
printf "<span class=\"next-post\">%s</span>\n" "$next_str" >> "$temp_post_with_backlink"
|
||||||
|
fi
|
||||||
|
printf "</div>\n" >> "$temp_post_with_backlink"
|
||||||
|
fi
|
||||||
|
content_file="$temp_post_with_backlink"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
||||||
|
[ -z "$local_template" ] && local_template="$template"
|
||||||
|
|
||||||
|
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
||||||
|
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
||||||
|
if [ -n "$closest_style_src" ]; then
|
||||||
|
style_rel_to_src="${closest_style_src#"$src"/}"
|
||||||
|
case "$closest_style_src" in
|
||||||
|
"$src/styles.css") style_rel_to_src="styles.css" ;;
|
||||||
|
"$src/style.css") style_rel_to_src="style.css" ;;
|
||||||
|
esac
|
||||||
|
style_path="/${style_rel_to_src%styles.css}"
|
||||||
|
style_path="${style_path%style.css}styles.css"
|
||||||
|
else
|
||||||
|
style_path="/styles.css"
|
||||||
|
fi
|
||||||
|
|
||||||
|
logo_html=""
|
||||||
|
if [ "$display_logo" = "true" ] && [ -n "$logo" ]; then
|
||||||
|
logo_html="<img class=\"site-logo\" src=\"$logo\" alt=\"$title\" />"
|
||||||
|
fi
|
||||||
|
|
||||||
|
brand_text=""
|
||||||
|
if [ "$display_title" = "true" ]; then
|
||||||
|
brand_text="$title"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$logo_html" ] && [ -n "$brand_text" ]; then
|
||||||
|
header_brand="<a href=\"/index.html\">$logo_html $brand_text</a>"
|
||||||
|
elif [ -n "$logo_html" ]; then
|
||||||
|
header_brand="<a href=\"/index.html\">$logo_html</a>"
|
||||||
|
elif [ -n "$brand_text" ]; then
|
||||||
|
header_brand="<a href=\"/index.html\">$brand_text</a>"
|
||||||
|
else
|
||||||
|
header_brand="<a href=\"/index.html\">$title</a>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
favicon_src=""
|
||||||
|
if [ "$logo_as_favicon" = "true" ] && [ -n "$logo" ]; then
|
||||||
|
favicon_src="$logo"
|
||||||
|
elif [ -n "$favicon" ]; then
|
||||||
|
favicon_src="$favicon"
|
||||||
|
fi
|
||||||
|
head_extra=""
|
||||||
|
if [ -n "$favicon_src" ]; then
|
||||||
|
if echo "$favicon_src" | grep -q "^http"; then
|
||||||
|
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||||
|
elif echo "$favicon_src" | grep -q "^/"; then
|
||||||
|
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||||
|
else
|
||||||
|
head_extra="<link rel=\"icon\" href=\"/$favicon_src\" />"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
parse_frontmatter "$file"
|
||||||
|
|
||||||
|
page_title="$title"
|
||||||
|
if [ -n "$fm_title" ]; then
|
||||||
|
page_title="$fm_title - $title"
|
||||||
|
elif [ "$generate_page_title" = "true" ] && [ -n "$file" ] && [ -f "$file" ]; then
|
||||||
|
if [ "$is_home" = "true" ] && [ -n "$home_name" ]; then
|
||||||
|
page_title="$home_name - $title"
|
||||||
|
else
|
||||||
|
first_heading=$(grep -m 1 '^# ' "$file" | sed 's/^# *//; s/ *$//')
|
||||||
|
if [ -n "$first_heading" ]; then
|
||||||
|
first_heading=$(echo "$first_heading" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||||
|
page_title="$first_heading - $title"
|
||||||
|
else
|
||||||
|
basename_no_ext=$(basename "$file" .md)
|
||||||
|
if [ "$basename_no_ext" != "index" ] && [ "$basename_no_ext" != "404_gen" ]; then
|
||||||
|
cap_basename=$(echo "$basename_no_ext" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
|
||||||
|
page_title="$cap_basename - $title"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
head_extra_og="<meta property=\"og:title\" content=\"$(escape_html_attr "$page_title")\" />"
|
||||||
|
if [ -n "$fm_description" ]; then
|
||||||
|
head_extra_og="$head_extra_og
|
||||||
|
<meta property=\"og:description\" content=\"$(escape_html_attr "$fm_description")\" />"
|
||||||
|
fi
|
||||||
|
og_url="${base_url%/}${current_url}"
|
||||||
|
head_extra_og="$head_extra_og
|
||||||
|
<meta property=\"og:url\" content=\"$(escape_html_attr "$og_url")\" />"
|
||||||
|
|
||||||
|
if [ -n "$head_extra" ]; then
|
||||||
|
head_extra="$head_extra
|
||||||
|
$head_extra_og"
|
||||||
|
else
|
||||||
|
head_extra="$head_extra_og"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$is_cw_content_page" = "true" ] && [ "$cw_hide_url" = "true" ]; then
|
||||||
|
head_extra="$head_extra
|
||||||
|
<script>window.history.replaceState(null, '', '$current_url');</script>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
final_footer="$footer"
|
||||||
|
if [ "$search_in_footer" = "true" ]; then
|
||||||
|
final_footer="$footer $SEARCH_FORM_FOOTER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
final_nav="$nav"
|
||||||
|
final_header_brand="$header_brand"
|
||||||
|
if [ "$search_in_header" = "true" ]; then
|
||||||
|
final_header_brand="$header_brand $SEARCH_FORM_HEADER"
|
||||||
|
final_nav="$SEARCH_FORM_NAV
|
||||||
|
$nav"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENABLE_HEADER_LINKS="$enable_header_links" CUSTOM_ADMONITIONS="$custom_admonitions" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$content_file" | AWK_LANG="$lang" AWK_CURRENT_URL="$current_url" AWK_TITLE="$page_title" AWK_NAV="$final_nav" AWK_FOOTER="$final_footer" AWK_STYLE_PATH="${style_path}" AWK_HEADER_BRAND="$final_header_brand" AWK_HEAD_EXTRA="$head_extra" AWK_VERSION="$asset_version" AWK_CONTENT_WARNING="$fm_content_warning" awk -f "$awk_dir/render_template.awk" "$local_template"
|
||||||
|
}
|
||||||
|
generate_content_warning_page() {
|
||||||
|
_fm_title="$1"
|
||||||
|
_fm_content_warning="$2"
|
||||||
|
_content_rel_url="$3"
|
||||||
|
_target_url="$4"
|
||||||
|
_out_file="$5"
|
||||||
|
_is_home="$6"
|
||||||
|
|
||||||
|
_temp_cw="$KEWT_TMPDIR/cw_$$.md"
|
||||||
|
_cw_text="${_fm_content_warning}"
|
||||||
|
[ "$_cw_text" = "true" ] && _cw_text="This content may be sensitive."
|
||||||
|
|
||||||
|
cat <<EOF > "$_temp_cw"
|
||||||
|
---
|
||||||
|
title = "$_fm_title"
|
||||||
|
---
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> **Content Warning:** $_cw_text
|
||||||
|
|
||||||
|
<a href="$(basename "$_content_rel_url")" class="cw-button">Reveal Content</a>
|
||||||
|
EOF
|
||||||
|
render_markdown "$_temp_cw" "$_is_home" "$_target_url" > "$_out_file"
|
||||||
|
rm -f "$_temp_cw"
|
||||||
|
}
|
||||||
44
lib/search.js
Normal file
44
lib/search.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var params = new URLSearchParams(window.location.search);
|
||||||
|
var query = params.get('q');
|
||||||
|
var box = document.getElementById('search-box');
|
||||||
|
var resultsContainer = document.getElementById('search-results-list');
|
||||||
|
|
||||||
|
if (box && query) box.value = query;
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
resultsContainer.innerHTML = '<p>Enter a search term above.</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/search.json')
|
||||||
|
.then(function(response) { return response.json(); })
|
||||||
|
.then(function(data) {
|
||||||
|
var q = query.toLowerCase();
|
||||||
|
var results = data.filter(function(item) {
|
||||||
|
return item.title.toLowerCase().indexOf(q) !== -1 ||
|
||||||
|
item.content.toLowerCase().indexOf(q) !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
var esc = query.replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
resultsContainer.innerHTML = '<p>No results found for "<strong>' + esc + '</strong>".</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<p>Found ' + results.length + ' result(s) for "<strong>' + esc + '</strong>":</p>';
|
||||||
|
results.forEach(function(result) {
|
||||||
|
var snippet = result.content.substring(0, 200);
|
||||||
|
if (result.content.length > 200) snippet += '...';
|
||||||
|
html += '<div class="search-result">';
|
||||||
|
html += '<a href="' + result.url + '">' + result.title + '</a>';
|
||||||
|
if (snippet) html += '<p>' + snippet + '</p>';
|
||||||
|
html += '</div>';
|
||||||
|
});
|
||||||
|
resultsContainer.innerHTML = html;
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
resultsContainer.innerHTML = '<p>Error loading search index.</p>';
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -62,13 +62,20 @@ awk -f "$awk_dir/fenced_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_f
|
|||||||
awk -f "$awk_dir/indented_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/indented_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -v enable_header_links="$ENABLE_HEADER_LINKS" -f "$awk_dir/headers.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -v enable_header_links="$ENABLE_HEADER_LINKS" -f "$awk_dir/headers.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
|
awk -f "$awk_dir/definition_lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
|
|
||||||
|
# TOC
|
||||||
|
awk -f "$awk_dir/toc.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
|
# Footnotes
|
||||||
|
awk -f "$awk_dir/footnotes.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
|
|
||||||
# Spacing
|
# Spacing
|
||||||
awk -f "$awk_dir/breaks.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/breaks.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/paragraphs.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/paragraphs.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
|
|
||||||
# Inline styles
|
# Inline styles
|
||||||
|
awk -f "$awk_dir/emoji.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/markdown_inline.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/markdown_inline.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -v script_dir="$script_dir" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
||||||
rm "$temp_file"
|
rm "$temp_file"
|
||||||
|
|||||||
@@ -26,4 +26,6 @@ build() {
|
|||||||
package() {
|
package() {
|
||||||
cd "${pkgname}"
|
cd "${pkgname}"
|
||||||
install -Dm755 kewt "${pkgdir}/usr/bin/kewt"
|
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() {
|
package() {
|
||||||
install -Dm755 "${srcdir}/${pkgname}-${pkgver}.sh" "${pkgdir}/usr/bin/kewt"
|
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
|
def install
|
||||||
bin.install "kewt"
|
bin.install "kewt"
|
||||||
|
generate_completions_from_executable(bin/"kewt", "--dump-zsh-completions", shells: [:zsh])
|
||||||
end
|
end
|
||||||
|
|
||||||
test do
|
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 "$@"
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# Heaven
|
|
||||||
|
|
||||||
| --- | --- |
|
|
||||||
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
|
||||||
@@ -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.
|
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.
|
||||||
|
|||||||
5
site/depths/index.md
Normal file
5
site/depths/index.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Depths
|
||||||
|
|
||||||
|
This is a custom index for a directory
|
||||||
|
|
||||||
|
{{LIST}}
|
||||||
85
site/docs/configuration.md
Normal file
85
site/docs/configuration.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
---
|
||||||
|
title = "Configuration"
|
||||||
|
---
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
## site.conf
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
- `title` - site title
|
||||||
|
- `style` - style file name from `./styles` (without `.css`)
|
||||||
|
- `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
|
||||||
|
- `order` - comma separated file/directory name list to order the sidebar (alphabetical by default)
|
||||||
|
- `home_name` - text for the home link in navigation (default: "Home")
|
||||||
|
- `show_home_in_nav` - show home link in navigation (default: true)
|
||||||
|
- `nav_links` - comma separated extra nav links, as bare URLs or Markdown links like `[Label](https://example.com)`
|
||||||
|
- `nav_extra` - raw HTML appended inside the `<nav>` after the generated link list
|
||||||
|
- `footer` - footer html/text shown at the bottom of pages
|
||||||
|
- `logo` - logo image path (used in header if enabled)
|
||||||
|
- `display_logo` - show logo in header
|
||||||
|
- `display_title` - show title text in header
|
||||||
|
- `logo_as_favicon` - use `logo` as favicon
|
||||||
|
- `favicon` - explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
|
||||||
|
- `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.
|
||||||
|
- `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
|
||||||
|
|
||||||
|
- `.kewtignore` - files/directories to ignore completely. If the file is empty, the whole directory gets ignored.
|
||||||
|
- `.kewthide` - files/directories to hide from navigation but still process. Same empty-file rules as `.kewtignore`.
|
||||||
|
- `.kewtpreserve` - files/directories to copy as-is without converting markdown to HTML. Same empty-file rules again.
|
||||||
24
site/docs/embeds.md
Normal file
24
site/docs/embeds.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title = "Embeds"
|
||||||
|
---
|
||||||
|
# Embeds
|
||||||
|
|
||||||
|
- `\![link]`:
|
||||||
|
- local image/audio/video files are embedded as media tags
|
||||||
|
- local text/code files are inlined directly
|
||||||
|
- global image/audio/video links are embedded as media tags
|
||||||
|
- other global links are embedded as `<iframe>`
|
||||||
|
- `\` works the same, with `alt` used for images
|
||||||
|
- `\!![link]` and `\!` force inline local file contents
|
||||||
|
|
||||||
|
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||||
|
|
||||||
|
## Typed Embeds
|
||||||
|
|
||||||
|
Force specific output regardless of extension:
|
||||||
|
|
||||||
|
- `\!i[link]` or `\!i[alt](link)` - **I**mage
|
||||||
|
- `\!v[link]` - **V**ideo
|
||||||
|
- `\!a[link]` - **A**udio
|
||||||
|
- `\!f[link]` - I**f**rame
|
||||||
|
- `\!e[link]` - Inline/**e**mbed text/code file directly
|
||||||
22
site/docs/frontmatter.md
Normal file
22
site/docs/frontmatter.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title = "Frontmatter"
|
||||||
|
---
|
||||||
|
# Frontmatter
|
||||||
|
|
||||||
|
You can set metadata for a page using a `site.conf`-style frontmatter block at the very top of `.md` files:
|
||||||
|
|
||||||
|
```conf
|
||||||
|
---
|
||||||
|
title = "Custom Page Title"
|
||||||
|
date = "2026-03-23 11:32"
|
||||||
|
draft = false
|
||||||
|
description = "A short page summary"
|
||||||
|
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. 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.
|
||||||
6
site/docs/index.md
Normal file
6
site/docs/index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title = "Documentation"
|
||||||
|
---
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
{{LIST}}
|
||||||
45
site/docs/installation.md
Normal file
45
site/docs/installation.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
title = "Installation"
|
||||||
|
---
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Standalone
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/download/latest/kewt
|
||||||
|
chmod +x kewt
|
||||||
|
```
|
||||||
|
## From source
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://git.krzak.org/N0VA/kewt.git
|
||||||
|
cd kewt
|
||||||
|
```
|
||||||
|
### Building
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
```
|
||||||
|
### Installing
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
## Package Managers
|
||||||
|
|
||||||
|
### AUR
|
||||||
|
|
||||||
|
- [kewt-bin](https://aur.archlinux.org/packages/kewt-bin) - prebuilt standalone binary from the latest release
|
||||||
|
- [kewt-git](https://aur.archlinux.org/packages/kewt-git) - built from the latest git source
|
||||||
|
|
||||||
|
### Homebrew
|
||||||
|
|
||||||
|
```sh
|
||||||
|
brew tap n0va-bot/tap
|
||||||
|
brew install kewt
|
||||||
|
```
|
||||||
|
### bpkg
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bpkg install n0va-bot/kewt
|
||||||
|
```
|
||||||
41
site/docs/markdown.md
Normal file
41
site/docs/markdown.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title = "Markdown Extensions"
|
||||||
|
---
|
||||||
|
# Markdown Extensions
|
||||||
|
|
||||||
|
## Directory Index Customisation
|
||||||
|
|
||||||
|
By default, directories without an `index.md` get an auto-generated index page listing their contents.
|
||||||
|
|
||||||
|
If you create your own `index.md` in a directory, you can still include the auto-generated file list by using the `{{LIST}}` placeholder:
|
||||||
|
|
||||||
|
```md
|
||||||
|
# Blog
|
||||||
|
|
||||||
|
This is my blog. The posts are below. The top-most one is the most recent.
|
||||||
|
|
||||||
|
{{LIST}}
|
||||||
|
```
|
||||||
|
The `{{LIST}}` placeholder is replaced with the autogenerated file list.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
`{{TOC}}` auto-generates a nested heading list with clickable anchors.
|
||||||
|
|
||||||
|
## Footnotes
|
||||||
|
|
||||||
|
Full support for `[^id]` footnotes and `[^id]: text` definitions. They render as a numbered `<section>` at the bottom of the page.
|
||||||
|
|
||||||
|
## Definition Lists
|
||||||
|
|
||||||
|
Definition lists use the standard syntax:
|
||||||
|
|
||||||
|
```md
|
||||||
|
Term
|
||||||
|
: Definition
|
||||||
|
```
|
||||||
|
This renders as `<dl><dt>Term</dt><dd>Definition</dd></dl>`. Multiple definitions per term are supported.
|
||||||
|
|
||||||
|
## Emoji Shortcodes
|
||||||
|
|
||||||
|
Standard GitHub/MkDocs emoji shortcodes like `:smile:`, `:fire:`, `:rocket:` are automatically replaced with their Unicode emoji equivalents. Shortcodes inside code blocks are left as-is.
|
||||||
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)
|
||||||
19
site/docs/templates.md
Normal file
19
site/docs/templates.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
title = "Templates"
|
||||||
|
---
|
||||||
|
# Templates
|
||||||
|
|
||||||
|
When customizing `template.html`, the placeholders available are:
|
||||||
|
- `{{CONTENT}}` - the generated content
|
||||||
|
- `{{TITLE}}` - the generated title
|
||||||
|
- `{{NAV}}` - the generated navigation
|
||||||
|
- `{{FOOTER}}` - the configured footer
|
||||||
|
- `{{VERSION}}` - the cache-busting string from `versioning = true` (e.g. `?v=12345678`). Safe to use even if versioning is **disabled** (it will be empty).
|
||||||
|
- `{{CSS}}` - the configured CSS file path
|
||||||
|
- `{{LANG}}` - the configured document language
|
||||||
|
- `{{HEAD_EXTRA}}` - meta-tags
|
||||||
|
- `{{HEADER_BRAND}}` - header rendering the name and/or logo
|
||||||
|
|
||||||
|
## 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.
|
||||||
23
site/docs/usage.md
Normal file
23
site/docs/usage.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title = "Usage"
|
||||||
|
---
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kewt --help
|
||||||
|
kewt --version
|
||||||
|
kewt --new [title]
|
||||||
|
kewt --post [title]
|
||||||
|
kewt --generate-template [path]
|
||||||
|
kewt --update [dir]
|
||||||
|
kewt --from <src> --to <out>
|
||||||
|
kewt [src] [out]
|
||||||
|
kewt --watch
|
||||||
|
kewt --serve [port]
|
||||||
|
```
|
||||||
|
- `--new [title]` creates a new site directory with a default `site.conf`, `template.html`, and `index.md`.
|
||||||
|
- `--post [title]` creates a new markdown file in the configured `posts_dir` with the current date/time as the filename and default frontmatter.
|
||||||
|
- `--generate-template [path]` writes the default `template.html` to the given path (defaults to `template.html` in the current directory).
|
||||||
|
- `--update [dir]` adds any missing keys to `site.conf` and checks `template.html` against the latest default.
|
||||||
|
- `--watch` (`-w`) watches for file changes in the source directory and rebuilds automatically.
|
||||||
|
- `--serve` (`-s`) starts a local HTTP server (python3 or busybox) in the output directory after building. Use with the port number to specify the port. Composable with `--watch`.
|
||||||
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 149 KiB |
12
site/heaven/index.md
Normal file
12
site/heaven/index.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
title = "Heaven"
|
||||||
|
content_warning = "This page may contain CSS"
|
||||||
|
---
|
||||||
|
# Heaven
|
||||||
|
|
||||||
|
Told you
|
||||||
|
|
||||||
|
Probably should've mentioned the catgirl too
|
||||||
|
|
||||||
|
| --- | --- |
|
||||||
|
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |
|
||||||
176
site/index.md
176
site/index.md
@@ -14,7 +14,7 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- No dependencies
|
- No dependencies
|
||||||
- Frontmatter support (title, date, draft)
|
- Frontmatter support (title, date, draft, description)
|
||||||
- Supports many embed types
|
- Supports many embed types
|
||||||
- Automatic css variable replacement for older browsers
|
- Automatic css variable replacement for older browsers
|
||||||
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
||||||
@@ -30,171 +30,15 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
|||||||
- Code block classes for use with external libraries like highlight.js or prism.js (both tested)
|
- Code block classes for use with external libraries like highlight.js or prism.js (both tested)
|
||||||
- Clickable markdown header anchors
|
- Clickable markdown header anchors
|
||||||
- Mobile responsive layout
|
- Mobile responsive layout
|
||||||
|
- Customisable directory index pages with `{{LIST}}`
|
||||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
- Open Graph meta tags from frontmatter
|
||||||
|
- Auto-generated Table of Contents via `{{TOC}}`
|
||||||
|
- Footnotes (`[^id]`)
|
||||||
|
- Definition lists
|
||||||
|
- Emoji shortcodes (`:smile:`, `:fire:`, etc.)
|
||||||
|
- Post pagination
|
||||||
|
- `--watch` and `--serve` modes for development
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
## Installation
|
## [Quickstart guide](/docs/quickstart.html)
|
||||||
|
|
||||||
### Standalone
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/download/latest/kewt
|
|
||||||
chmod +x kewt
|
|
||||||
```
|
|
||||||
|
|
||||||
### From source
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://git.krzak.org/N0VA/kewt.git
|
|
||||||
cd kewt
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Building
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Installing
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Package Managers
|
|
||||||
|
|
||||||
#### AUR
|
|
||||||
|
|
||||||
- [kewt-bin](https://aur.archlinux.org/packages/kewt-bin) — prebuilt standalone binary from the latest release
|
|
||||||
- [kewt-git](https://aur.archlinux.org/packages/kewt-git) — built from the latest git source
|
|
||||||
|
|
||||||
#### Homebrew
|
|
||||||
|
|
||||||
```sh
|
|
||||||
brew tap n0va-bot/tap
|
|
||||||
brew install kewt
|
|
||||||
```
|
|
||||||
|
|
||||||
#### bpkg
|
|
||||||
|
|
||||||
```sh
|
|
||||||
bpkg install n0va-bot/kewt
|
|
||||||
```
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./kewt.sh --help
|
|
||||||
./kewt.sh --version
|
|
||||||
./kewt.sh --new [title]
|
|
||||||
./kewt.sh --post
|
|
||||||
./kewt.sh --from <src> --to <out>
|
|
||||||
./kewt.sh [src] [out]
|
|
||||||
```
|
|
||||||
|
|
||||||
`--new [title]` creates a new site directory with a copied `site.conf` and a default `index.md`.
|
|
||||||
|
|
||||||
`--post [title]` creates a new markdown file in the configured `posts_dir` with the current date/time as the name and creates the default frontmatter.
|
|
||||||
|
|
||||||
### site.conf
|
|
||||||
|
|
||||||
```conf
|
|
||||||
title = "kewt"
|
|
||||||
style = "kewt"
|
|
||||||
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
|
|
||||||
base_url = ""
|
|
||||||
generate_feed = false
|
|
||||||
feed_file = "rss.xml"
|
|
||||||
posts_dir = ""
|
|
||||||
enable_header_links = true
|
|
||||||
custom_admonitions = ""
|
|
||||||
```
|
|
||||||
|
|
||||||
- `title` site title
|
|
||||||
- `style` style file name from `./styles` (without `.css`)
|
|
||||||
- `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
|
|
||||||
- `order` comma separated file/directory name list to order the sidebar (alphabetical by default)
|
|
||||||
- `home_name` text for the home link in navigation (default: "Home")
|
|
||||||
- `show_home_in_nav` show home link in navigation (default: true)
|
|
||||||
- `nav_links` comma separated extra nav links, as bare URLs or Markdown links like `[Label](https://example.com)`
|
|
||||||
- `nav_extra` raw HTML appended inside the `<nav>` after the generated link list
|
|
||||||
- `footer` footer html/text shown at the bottom of pages
|
|
||||||
- `logo` logo image path (used in header if enabled)
|
|
||||||
- `display_logo` show logo in header
|
|
||||||
- `display_title` show title text in header
|
|
||||||
- `logo_as_favicon` use `logo` as favicon
|
|
||||||
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
|
|
||||||
- `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)
|
|
||||||
- `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.
|
|
||||||
- `enable_header_links` turns markdown section headings into clickable anchor links (default: true)
|
|
||||||
- `custom_admonitions` comma separated list of custom admonitions
|
|
||||||
|
|
||||||
### Ignores
|
|
||||||
|
|
||||||
- `.kewtignore`: Files/directories to ignore. If empty, the whole directory gets ignored
|
|
||||||
- `.kewthide`: Files/directories to hide from navigation but still process. Same empty rules as with ignore
|
|
||||||
- `.kewtpreserve`: Files/directories to copy but not convert markdown to html. Same empty rules again
|
|
||||||
|
|
||||||
### Embeds
|
|
||||||
|
|
||||||
- `\![link]`:
|
|
||||||
- local image/audio/video files are embedded as media tags
|
|
||||||
- local text/code files are inlined directly
|
|
||||||
- global image/audio/video links are embedded as media tags
|
|
||||||
- other global links are embedded as `<iframe>`
|
|
||||||
- `\` works the same, with `alt` used for images
|
|
||||||
- `\!![]` and `\!` force inline local file contents
|
|
||||||
- **Typed Embeds**: Force specific output regardless of extension:
|
|
||||||
- `\!i[link]` or `\!i[alt](link)`: **I**mage
|
|
||||||
- `\!v[link]`: **V**ideo
|
|
||||||
- `\!a[link]`: **A**udio
|
|
||||||
- `\!f[link]`: I**f**rame
|
|
||||||
- `\!e[link]`: Inline/**e**mbed text/code file directly
|
|
||||||
|
|
||||||
### Frontmatter
|
|
||||||
|
|
||||||
You can set metadata for a page using a `site.conf`-style frontmatter block at the very top of `.md` files:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
---
|
|
||||||
title = "Custom Page Title"
|
|
||||||
date = "2026-03-23 11:32"
|
|
||||||
draft = false
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
- `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
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
>[!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...
|
|
||||||
@@ -9,7 +9,7 @@ display_logo = false
|
|||||||
display_title = true
|
display_title = true
|
||||||
logo_as_favicon = false
|
logo_as_favicon = false
|
||||||
favicon = "favicon.ico"
|
favicon = "favicon.ico"
|
||||||
order = ""
|
order = "Home, docs, depths, heaven"
|
||||||
home_name = "Home"
|
home_name = "Home"
|
||||||
show_home_in_nav = true
|
show_home_in_nav = true
|
||||||
nav_links = ""
|
nav_links = ""
|
||||||
@@ -20,3 +20,11 @@ versioning = true
|
|||||||
enable_header_links = true
|
enable_header_links = true
|
||||||
base_url = "https://kewt.krzak.org"
|
base_url = "https://kewt.krzak.org"
|
||||||
custom_admonitions = ""
|
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
|
||||||
254
styles/kewt.css
254
styles/kewt.css
@@ -48,6 +48,9 @@ header h1 {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: var(--fg-heading);
|
color: var(--fg-heading);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-logo {
|
.site-logo {
|
||||||
@@ -219,6 +222,22 @@ pre code {
|
|||||||
border-color: var(--adm-caution-border);
|
border-color: var(--adm-caution-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cw-button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: var(--bg-deep);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
color: var(--fg);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cw-button:hover {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--bg);
|
||||||
|
border-color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
padding-top: 60px;
|
padding-top: 60px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
@@ -252,8 +271,28 @@ hr {
|
|||||||
border-top: 1px solid var(--code-border);
|
border-top: 1px solid var(--code-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-toggle,
|
||||||
|
.nav-toggle-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-toggle-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--fg-heading);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
#side-bar {
|
#side-bar {
|
||||||
|
display: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: auto;
|
top: auto;
|
||||||
left: auto;
|
left: auto;
|
||||||
@@ -264,6 +303,10 @@ hr {
|
|||||||
margin: 0 20px 20px 20px;
|
margin: 0 20px 20px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-toggle:checked~#side-bar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
article {
|
article {
|
||||||
margin: 0 20px 0 20px;
|
margin: 0 20px 0 20px;
|
||||||
}
|
}
|
||||||
@@ -283,3 +326,214 @@ hr {
|
|||||||
margin: 0 0.2em 0.25em -1.6em;
|
margin: 0 0.2em 0.25em -1.6em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kewt-search-page {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-page input[type="text"] {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-page input[type="text"]:focus {
|
||||||
|
border-color: var(--fg-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-page button {
|
||||||
|
padding: 8px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--bg-deep);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-page button:hover {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result {
|
||||||
|
margin: 16px 0;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--code-bg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result a {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--fg-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result p {
|
||||||
|
margin: 6px 0 0 0;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer search */
|
||||||
|
.kewt-search-footer {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-footer input[type="text"] {
|
||||||
|
padding: 3px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-footer input[type="text"]:focus {
|
||||||
|
border-color: var(--fg-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-footer button {
|
||||||
|
padding: 3px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--bg-deep);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-footer button:hover {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-header {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 4px;
|
||||||
|
margin-left: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-header input[type="text"] {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-header input[type="text"]:focus {
|
||||||
|
border-color: var(--fg-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-header button {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--bg-deep);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-header button:hover {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav {
|
||||||
|
display: none;
|
||||||
|
padding: 8px 0 12px 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-bottom: 1px solid var(--code-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav form {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav input[type="text"] {
|
||||||
|
flex: 1;
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav input[type="text"]:focus {
|
||||||
|
border-color: var(--fg-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav button {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--bg-deep);
|
||||||
|
color: var(--fg);
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav button:hover {
|
||||||
|
background: var(--fg);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.kewt-search-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-nav {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-page {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-page button {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-footer {
|
||||||
|
display: flex;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kewt-search-footer input[type="text"] {
|
||||||
|
flex: 1;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,10 +28,10 @@ EOF
|
|||||||
|
|
||||||
VERSION=$(git describe --tags 2>/dev/null || echo "standalone")
|
VERSION=$(git describe --tags 2>/dev/null || echo "standalone")
|
||||||
tmpbuild=$(mktemp -d)
|
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"
|
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"
|
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"
|
rm -rf "$tmpbuild"
|
||||||
|
|
||||||
chmod +x "$OUT_FILE"
|
chmod +x "$OUT_FILE"
|
||||||
|
|||||||
Reference in New Issue
Block a user