Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3075719963 | |||
| 0379d38234 | |||
| b22897135e | |||
| 185cf769c3 | |||
| b2acd26660 | |||
| 4069bafd52 | |||
| 9dbd41392e | |||
| 35eac48dcd | |||
| ef16ed4c88 | |||
| 30b7681234 | |||
| 13b6106efd | |||
| 831b081fc7 | |||
| fde423a32b | |||
| 55a82f75a9 | |||
| f85abd43c4 | |||
| 0f66ebf52a | |||
| 55a515ccd5 | |||
| de8cbefb8e | |||
| cc7fee573f | |||
| 137be9579a | |||
| 5afd0170e5 | |||
| 5a2053cfb4 | |||
| 2fc3d6fc6f | |||
| c5a9355871 | |||
| b8cd129c47 | |||
| 5e033a65e7 | |||
| 5bf2e2abe5 | |||
| 3a2056ff8f | |||
| fd829a3f22 | |||
| bad02decba | |||
| 2aef6ec4a1 | |||
| 78eac182dc | |||
| b7382a20ab | |||
| d8cf07ee2a | |||
| 76e2ae0117 | |||
| 2e331b5d9a | |||
| 9f5d1089a2 | |||
| 9ccba8fd4e | |||
| 95679abd85 | |||
| 8b1e793510 | |||
| 19f96553d9 | |||
| 1a7525a857 | |||
| e7d90d18e8 | |||
| 4019d2721d | |||
| b58604a4cf | |||
| 99e805b180 | |||
| 62075dea4a |
38
.gitea/workflows/publish-aur-git.yml
Normal file
38
.gitea/workflows/publish-aur-git.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Publish kewt-git to AUR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'packaging/AUR/PKGBUILD.git'
|
||||
- 'packaging/AUR/.SRCINFO.git'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-aur-git:
|
||||
runs-on: local
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Arch Linux environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pacman-package-manager curl jq || true
|
||||
|
||||
- name: Prepare AUR files
|
||||
run: |
|
||||
mkdir -p aur-work
|
||||
cp packaging/AUR/PKGBUILD.git aur-work/PKGBUILD
|
||||
cp packaging/AUR/.SRCINFO.git aur-work/.SRCINFO
|
||||
|
||||
- name: Publish to AUR
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
|
||||
with:
|
||||
pkgname: kewt-git
|
||||
pkgbuild: ./aur-work/PKGBUILD
|
||||
commit_username: ${{ github.actor }}
|
||||
commit_email: ${{ github.actor }}@users.noreply.github.com
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: "Update kewt-git to ${{ github.sha }}"
|
||||
@@ -3,6 +3,7 @@ name: Release Standalone Builder
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -32,12 +33,24 @@ jobs:
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
|
||||
# Fetch release body from Gitea
|
||||
RELEASE_BODY=$(curl -sL \
|
||||
"https://git.krzak.org/api/v1/repos/N0VA/kewt/releases/tags/${TAG}" \
|
||||
| jq -r '.body // ""')
|
||||
|
||||
# Build JSON payload
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg name "Release $TAG" \
|
||||
--arg body "$RELEASE_BODY" \
|
||||
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
|
||||
|
||||
# Create the release on GitHub
|
||||
curl -sL -X POST \
|
||||
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/n0va-bot/kewt/releases" \
|
||||
-d "{\"tag_name\":\"${TAG}\",\"name\":\"${TAG}\",\"draft\":false,\"prerelease\":false}" || true
|
||||
-d "$PAYLOAD" || true
|
||||
|
||||
# Get the release ID
|
||||
RELEASE_ID=$(curl -sL \
|
||||
@@ -78,25 +91,9 @@ jobs:
|
||||
-e "s/SHA256SUM_PLACEHOLDER/${CHECKSUM}/g" \
|
||||
packaging/AUR/PKGBUILD.template > aur-work/PKGBUILD
|
||||
|
||||
cat > aur-work/.SRCINFO << SRCEOF
|
||||
pkgbase = kewt-bin
|
||||
pkgdesc = A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||
pkgver = ${VERSION}
|
||||
pkgrel = 1
|
||||
url = https://git.krzak.org/N0VA/kewt
|
||||
arch = any
|
||||
license = MIT
|
||||
depends = sh
|
||||
provides = kewt
|
||||
conflicts = kewt
|
||||
conflicts = kewt-git
|
||||
source = kewt-bin-${VERSION}.sh::https://git.krzak.org/N0VA/kewt/releases/download/v${VERSION}/kewt
|
||||
sha256sums = ${CHECKSUM}
|
||||
|
||||
pkgname = kewt-bin
|
||||
SRCEOF
|
||||
# Remove leading whitespace from heredoc
|
||||
sed -i 's/^ //' aur-work/.SRCINFO
|
||||
sed -e "s/VERSION_PLACEHOLDER/${VERSION}/g" \
|
||||
-e "s/SHA256SUM_PLACEHOLDER/${CHECKSUM}/g" \
|
||||
packaging/AUR/.SRCINFO.template > aur-work/.SRCINFO
|
||||
|
||||
- name: Publish to AUR
|
||||
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
|
||||
@@ -107,3 +104,34 @@ jobs:
|
||||
commit_email: ${{ github.actor }}@users.noreply.github.com
|
||||
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
commit_message: "Update kewt-bin to ${{ github.ref_name }}"
|
||||
|
||||
publish-homebrew:
|
||||
runs-on: local
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Render Formula and push to GitHub
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${TAG#v}"
|
||||
|
||||
curl -sL -o kewt-binary \
|
||||
"https://git.krzak.org/N0VA/kewt/releases/download/${TAG}/kewt"
|
||||
CHECKSUM=$(sha256sum kewt-binary | awk '{print $1}')
|
||||
rm -f kewt-binary
|
||||
|
||||
git clone https://x-access-token:${{ secrets.GH_RELEASE_TOKEN }}@github.com/n0va-bot/homebrew-tap.git brew-work || true
|
||||
mkdir -p brew-work/Formula
|
||||
sed -e "s/VERSION_PLACEHOLDER/${VERSION}/g" \
|
||||
-e "s/SHA256SUM_PLACEHOLDER/${CHECKSUM}/g" \
|
||||
packaging/homebrew/kewt.rb.template > brew-work/Formula/kewt.rb
|
||||
|
||||
cd brew-work
|
||||
[ -d .git ] || { git init && git checkout --orphan main && git remote add origin https://x-access-token:${{ secrets.GH_RELEASE_TOKEN }}@github.com/n0va-bot/homebrew-tap.git; }
|
||||
git add Formula/kewt.rb
|
||||
git config user.name "${{ github.actor }}"
|
||||
git config user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
git commit -m "Update kewt to ${TAG}" || echo "No changes to commit"
|
||||
git push -u origin main
|
||||
|
||||
34
LICENSE
Normal file
34
LICENSE
Normal file
@@ -0,0 +1,34 @@
|
||||
ISC License
|
||||
|
||||
Copyright 2026 N0\A
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software
|
||||
for any purpose with or without fee is hereby granted, provided
|
||||
that the above copyright notice and this permission notice appear
|
||||
in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
This project incorporates code (CSS style) from the 'kew' project,
|
||||
which is also licensed under the ISC License: Copyright (c) 2026 uint23
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
19
Makefile
Normal file
19
Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
|
||||
all: kewt
|
||||
|
||||
kewt:
|
||||
./tools/build-standalone.sh
|
||||
|
||||
install: kewt
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
install -m 755 kewt $(DESTDIR)$(BINDIR)/kewt
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/kewt
|
||||
|
||||
clean:
|
||||
rm -f kewt
|
||||
|
||||
.PHONY: all install uninstall clean
|
||||
105
README.md
105
README.md
@@ -1,106 +1,21 @@
|
||||
# _kewt_
|
||||
# 
|
||||
### Pronounced "cute"
|
||||
|
||||
***
|
||||
|
||||
# [Go to the website](https://kewt.krzak.org)
|
||||
|
||||
***
|
||||
|
||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||
|
||||
It's meant to be a static site generator, like _[kew](https://github.com/uint23/kew)_ but use only default (POSIX) tooling, like _[werc](http://werc.cat-v.org/)_ (and definitely unlike _[kew](https://github.com/uint23/kew)_)
|
||||
|
||||
## Features
|
||||
## [Installation](https://kewt.krzak.org/#installation)
|
||||
|
||||
- No dependencies
|
||||
- Supports many embed types
|
||||
- Automatic css variable replacement for older browsers
|
||||
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
||||
- Inline html support
|
||||
- MFM `$font` and `\<plain>` tags
|
||||
- Admonition support (that's what the blocks like the warning block below are called)
|
||||
## Contributing
|
||||
|
||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||
Either open an issue or pull request on the **home** repository ([N0VA/kewt](https://git.krzak.org/N0VA/kewt)) or message me on my email address ([n0va@krzak.org](mailto:n0va@krzak.org?subject=%5Bkewt%5D%20something)) with the subjectline being `[kewt] something`.
|
||||
|
||||
## Installation
|
||||
## License
|
||||
|
||||
You can clone the repository to use `kewt.sh` directly, or you can download the standalone executable, which bundles all dependencies into a single file:
|
||||
|
||||
```sh
|
||||
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/latest/download/kewt
|
||||
chmod +x kewt
|
||||
```
|
||||
|
||||
On Arch Linux, _kewt_ is available on the 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
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
./kewt.sh --help
|
||||
./kewt.sh --new [title]
|
||||
./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`.
|
||||
|
||||
## 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 = ""
|
||||
```
|
||||
|
||||
- `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)
|
||||
|
||||
## 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
|
||||
|
||||
## Credits
|
||||
|
||||
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||
|
||||
>[!WARNING]
|
||||
>Most of this was coded at night, while sleepy and a bit sick, and after walking for about 4 hours around a forest, so...
|
||||
ISC
|
||||
|
||||
@@ -22,7 +22,19 @@ END {
|
||||
sub(/^\[!/, "", kind)
|
||||
sub(/\]$/, "", kind)
|
||||
lkind = tolower(kind)
|
||||
if (lkind == "note" || lkind == "tip" || lkind == "important" || lkind == "warning" || lkind == "caution") {
|
||||
is_valid = 0
|
||||
if (custom_admonitions != "") {
|
||||
n = split(tolower(custom_admonitions), adms, ",")
|
||||
for (idx = 1; idx <= n; idx++) {
|
||||
adm = adms[idx]
|
||||
sub(/^[ \t]+/, "", adm)
|
||||
sub(/[ \t]+$/, "", adm)
|
||||
if (lkind == adm) { is_valid = 1; break }
|
||||
}
|
||||
} else if (lkind == "note" || lkind == "tip" || lkind == "important" || lkind == "warning" || lkind == "caution") {
|
||||
is_valid = 1
|
||||
}
|
||||
if (is_valid) {
|
||||
print "<div class=\"admonition admonition-" lkind "\">"
|
||||
print "<p class=\"admonition-title\">" cap(lkind) "</p>"
|
||||
has_body = 0
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
BEGIN {
|
||||
src = ENVIRON["AWK_SRC"]
|
||||
slen = length(src)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
BEGIN { in_fence = 0; first_line = 0 }
|
||||
BEGIN { in_fence = 0; first_line = 0; code_tag = "<code>" }
|
||||
{
|
||||
if (!in_fence && $0 ~ /^```/) {
|
||||
in_fence = 1
|
||||
first_line = 1
|
||||
lang = $0
|
||||
sub(/^```[[:space:]]*/, "", lang)
|
||||
sub(/[[:space:]]*$/, "", lang)
|
||||
if (lang != "") {
|
||||
code_tag = "<code class=\"language-" lang "\">"
|
||||
} else {
|
||||
code_tag = "<code>"
|
||||
}
|
||||
next
|
||||
}
|
||||
if (in_fence && $0 ~ /^```[[:space:]]*$/) {
|
||||
if (first_line) printf "%s", "<pre>" code_tag
|
||||
print "</code></pre>"
|
||||
in_fence = 0
|
||||
next
|
||||
@@ -14,8 +23,12 @@ BEGIN { in_fence = 0; first_line = 0 }
|
||||
gsub(/&/, "\\&"); gsub(/</, "\\<"); gsub(/>/, "\\>")
|
||||
if (first_line) {
|
||||
first_line = 0
|
||||
if ($0 == "") next
|
||||
print "<pre><code>" $0
|
||||
printf "%s", "<pre>" code_tag
|
||||
if ($0 == "") {
|
||||
print ""
|
||||
next
|
||||
}
|
||||
print $0
|
||||
} else {
|
||||
print
|
||||
}
|
||||
@@ -24,5 +37,8 @@ BEGIN { in_fence = 0; first_line = 0 }
|
||||
}
|
||||
}
|
||||
END {
|
||||
if (in_fence) print "</code></pre>"
|
||||
if (in_fence) {
|
||||
if (first_line) printf "%s", "<pre>" code_tag
|
||||
print "</code></pre>"
|
||||
}
|
||||
}
|
||||
|
||||
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>"
|
||||
}
|
||||
}
|
||||
46
awk/frontmatter.awk
Normal file
46
awk/frontmatter.awk
Normal file
@@ -0,0 +1,46 @@
|
||||
BEGIN {
|
||||
state = "start"
|
||||
}
|
||||
{
|
||||
if (state == "start") {
|
||||
if ($0 == "---") {
|
||||
state = "in_fm"
|
||||
next
|
||||
} else {
|
||||
state = "body"
|
||||
print
|
||||
next
|
||||
}
|
||||
}
|
||||
if (state == "in_fm") {
|
||||
if ($0 == "---") {
|
||||
state = "body"
|
||||
next
|
||||
}
|
||||
line = $0
|
||||
if (line ~ /^[[:space:]]*$/ || line ~ /^[[:space:]]*#/) next
|
||||
if (line !~ /=/) next
|
||||
|
||||
key = line
|
||||
val = line
|
||||
sub(/=.*/, "", key)
|
||||
sub(/[^=]*=/, "", val)
|
||||
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", val)
|
||||
|
||||
if (val ~ /^".*"$/) {
|
||||
val = substr(val, 2, length(val) - 2)
|
||||
gsub(/\\"/, "\"", val)
|
||||
} else if (val ~ /^'.*'$/) {
|
||||
val = substr(val, 2, length(val) - 2)
|
||||
gsub(/\\'/, "'", val)
|
||||
}
|
||||
|
||||
if (fm_out != "") {
|
||||
print key "=" val >> fm_out
|
||||
}
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
@@ -33,6 +33,13 @@ function compare_paths(p1, p2, parts1, parts2, n1, n2, i, name1, name2, lname
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
src = ENVIRON["AWK_SRC"]
|
||||
single_file_index = ENVIRON["AWK_SINGLE_FILE_INDEX"]
|
||||
flatten = ENVIRON["AWK_FLATTEN"]
|
||||
order = ENVIRON["AWK_ORDER"]
|
||||
home_name = ENVIRON["AWK_HOME_NAME"]
|
||||
show_home_in_nav = ENVIRON["AWK_SHOW_HOME_IN_NAV"]
|
||||
dinfo = ENVIRON["AWK_DINFO"]
|
||||
n_dlines = split(dinfo, dlines, "\n")
|
||||
for (i = 1; i <= n_dlines; i++) {
|
||||
if (split(dlines[i], dparts, "|") == 3) {
|
||||
|
||||
@@ -3,23 +3,32 @@ function strip_markdown(s) {
|
||||
gsub(/[*_`~]/, "", s)
|
||||
gsub(/[\[\]]/, "", s)
|
||||
gsub(/\([^\)]*\)/, "", s)
|
||||
s = tolower(s)
|
||||
gsub(/[^a-z0-9 -]/, "", s)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
|
||||
gsub(/[[:space:]]+/, "-", s)
|
||||
gsub(/-{2,}/, "-", s)
|
||||
gsub(/^-+|-+$/, "", s)
|
||||
if (length(s) > 80) s = substr(s, 1, 80)
|
||||
gsub(/-+$/, "", s)
|
||||
return s
|
||||
}
|
||||
function print_header(line) {
|
||||
if (line ~ /^# /) {
|
||||
sub(/^# /, "", line); print "<h1 id=\"" strip_markdown(line) "\">" line "</h1>"
|
||||
} else if (line ~ /^## /) {
|
||||
sub(/^## /, "", line); print "<h2 id=\"" strip_markdown(line) "\">" line "</h2>"
|
||||
} else if (line ~ /^### /) {
|
||||
sub(/^### /, "", line); print "<h3 id=\"" strip_markdown(line) "\">" line "</h3>"
|
||||
} else if (line ~ /^#### /) {
|
||||
sub(/^#### /, "", line); print "<h4 id=\"" strip_markdown(line) "\">" line "</h4>"
|
||||
} else if (line ~ /^##### /) {
|
||||
sub(/^##### /, "", line); print "<h5 id=\"" strip_markdown(line) "\">" line "</h5>"
|
||||
} else if (line ~ /^###### /) {
|
||||
sub(/^###### /, "", line); print "<h6 id=\"" strip_markdown(line) "\">" line "</h6>"
|
||||
tag = ""
|
||||
if (line ~ /^# /) { tag = "h1"; sub(/^# /, "", line) }
|
||||
else if (line ~ /^## /) { tag = "h2"; sub(/^## /, "", line) }
|
||||
else if (line ~ /^### /) { tag = "h3"; sub(/^### /, "", line) }
|
||||
else if (line ~ /^#### /) { tag = "h4"; sub(/^#### /, "", line) }
|
||||
else if (line ~ /^##### /) { tag = "h5"; sub(/^##### /, "", line) }
|
||||
else if (line ~ /^###### /) { tag = "h6"; sub(/^###### /, "", line) }
|
||||
|
||||
if (tag != "") {
|
||||
id = strip_markdown(line)
|
||||
if (enable_header_links == "true") {
|
||||
print "<" tag " id=\"" id "\"><a href=\"#" id "\" class=\"header-anchor\">" line "</a></" tag ">"
|
||||
} else {
|
||||
print "<" tag " id=\"" id "\">" line "</" tag ">"
|
||||
}
|
||||
} else {
|
||||
print line
|
||||
}
|
||||
@@ -29,7 +38,7 @@ BEGIN {
|
||||
in_pre = 0
|
||||
}
|
||||
{
|
||||
if ($0 ~ /^<pre><code>/) {
|
||||
if ($0 ~ /^<pre><code/) {
|
||||
in_pre = 1
|
||||
if (has_prev && prev != "") { print_header(prev); has_prev = 0 }
|
||||
print
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
BEGIN { in_code = 0 }
|
||||
/^ | / {
|
||||
if (!in_code) { print "<pre><code>"; in_code = 1 }
|
||||
sub(/^ | /, "", $0)
|
||||
gsub(/&/, "\\&"); gsub(/</, "\\<"); gsub(/>/, "\\>")
|
||||
print; next
|
||||
BEGIN { in_code = 0; in_html_pre = 0 }
|
||||
{
|
||||
if ($0 ~ /<pre>/) in_html_pre = 1
|
||||
if ($0 ~ /<\/pre>/) { in_html_pre = 0; if (in_code) { print "</code></pre>"; in_code = 0 }; print; next }
|
||||
|
||||
if (!in_html_pre && $0 ~ /^(\t| )/) {
|
||||
if (!in_code) { printf "%s", "<pre><code>"; in_code = 1 }
|
||||
sub(/^(\t| )/, "", $0)
|
||||
gsub(/&/, "\\&"); gsub(/</, "\\<"); gsub(/>/, "\\>")
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
if (in_code) {
|
||||
print "</code></pre>"
|
||||
in_code = 0
|
||||
}
|
||||
print
|
||||
}
|
||||
{ if (in_code) { print "</code></pre>"; in_code = 0 } print }
|
||||
END { if (in_code) print "</code></pre>" }
|
||||
|
||||
@@ -48,7 +48,26 @@ BEGIN {
|
||||
}
|
||||
}
|
||||
|
||||
print "<li>" content "</li>"
|
||||
has_checkbox = 0
|
||||
if (content ~ /^\[[ \t]\] /) {
|
||||
has_checkbox = 1
|
||||
is_checked = 0
|
||||
sub(/^\[[ \t]\] /, "", content)
|
||||
} else if (content ~ /^\[[xX]\] /) {
|
||||
has_checkbox = 1
|
||||
is_checked = 1
|
||||
sub(/^\[[xX]\] /, "", content)
|
||||
}
|
||||
|
||||
if (has_checkbox) {
|
||||
if (is_checked) {
|
||||
print "<li class=\"task-list-item\"><input type=\"checkbox\" class=\"task-list-item-checkbox\" checked disabled> " content "</li>"
|
||||
} else {
|
||||
print "<li class=\"task-list-item\"><input type=\"checkbox\" class=\"task-list-item-checkbox\" disabled> " content "</li>"
|
||||
}
|
||||
} else {
|
||||
print "<li>" content "</li>"
|
||||
}
|
||||
} else {
|
||||
while (depth > 0) {
|
||||
print "</" cur_type[depth] ">"
|
||||
|
||||
@@ -204,7 +204,7 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
||||
}
|
||||
if (is_audio_ext(ext)) return "<audio controls src=\"" src "\"></audio>"
|
||||
if (is_video_ext(ext)) return "<video controls src=\"" src "\"></video>"
|
||||
return "<iframe src=\"" src "\"></iframe>"
|
||||
return "<iframe src=\"" src "\" allowfullscreen></iframe>"
|
||||
}
|
||||
|
||||
if (is_image_ext(ext)) {
|
||||
@@ -223,7 +223,29 @@ function render_embed(src, alt, has_alt, force_inline, ext, local_path, conte
|
||||
}
|
||||
}
|
||||
|
||||
return "<iframe src=\"" src "\"></iframe>"
|
||||
return "<iframe src=\"" src "\" allowfullscreen></iframe>"
|
||||
}
|
||||
|
||||
function render_typed_embed(etype, src, alt, has_alt, local_path, content) {
|
||||
if (etype == "i") {
|
||||
if (has_alt) return "<img alt=\"" alt "\" src=\"" src "\" />"
|
||||
return "<img src=\"" src "\" />"
|
||||
}
|
||||
if (etype == "v") return "<video controls src=\"" src "\"></video>"
|
||||
if (etype == "a") return "<audio controls src=\"" src "\"></audio>"
|
||||
if (etype == "f") return "<iframe src=\"" src "\" allowfullscreen></iframe>"
|
||||
if (etype == "e") {
|
||||
if (!is_global_url(src)) {
|
||||
local_path = resolve_local_path(src)
|
||||
if (local_path != "") {
|
||||
content = read_file(local_path)
|
||||
if (content ~ /\n$/) sub(/\n$/, "", content)
|
||||
return content
|
||||
}
|
||||
}
|
||||
return render_embed(src, alt, has_alt, 1)
|
||||
}
|
||||
return render_embed(src, alt, has_alt, 0)
|
||||
}
|
||||
|
||||
function extract_attr(tag, attr, pat, m, token) {
|
||||
@@ -319,7 +341,7 @@ function apply_td_vertical_align(line, out, rest, seg, td_tag, img_tag, after
|
||||
return out rest
|
||||
}
|
||||
|
||||
function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, pre, post, repl) {
|
||||
function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, embed_type, pre, post, repl) {
|
||||
out = ""
|
||||
rest = line
|
||||
while (match(rest, /<img[^>]*\/?>/)) {
|
||||
@@ -329,7 +351,10 @@ function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, p
|
||||
src = extract_attr(tag, "src")
|
||||
alt = extract_attr(tag, "alt")
|
||||
force_inline_tag = extract_attr(tag, "data-force-inline")
|
||||
if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
|
||||
embed_type = extract_attr(tag, "data-embed-type")
|
||||
if (embed_type != "") {
|
||||
repl = render_typed_embed(embed_type, src, alt, (alt != ""))
|
||||
} else if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
|
||||
# Preserve hand-written <img> attributes (style/class/etc) for normal images.
|
||||
repl = tag
|
||||
} else {
|
||||
|
||||
@@ -20,9 +20,11 @@ function mask_html_tags(s, out, rest, start, len, tag, token) {
|
||||
return out rest
|
||||
}
|
||||
|
||||
function restore_html_tags(s, i) {
|
||||
function restore_html_tags(s, i, val) {
|
||||
for (i = 1; i <= html_tag_count; i++) {
|
||||
gsub(html_tag_token[i], html_tag_value[i], s)
|
||||
val = html_tag_value[i]
|
||||
gsub(/&/, "\\\\&", val)
|
||||
gsub(html_tag_token[i], val, s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -58,6 +60,36 @@ function restore_html_tags(s, i) {
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
|
||||
# typed embeds: !i, !v, !a, !f, !e
|
||||
while (match(line, /![ivafe]\[[^\]]*\]\([^\)]+ "[^"]*"\)/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
token = substr(line, start, len)
|
||||
etype = substr(token, 2, 1)
|
||||
match(token, /\[[^\]]*\]/); alt = substr(token, RSTART + 1, RLENGTH - 2)
|
||||
match(token, /"[^"]*"/); etitle = substr(token, RSTART + 1, RLENGTH - 2)
|
||||
match(token, /\([^\)]+/); inner = substr(token, RSTART + 1, RLENGTH - 1)
|
||||
sub(/[[:space:]]*"[^"]*"/, "", inner); src = inner
|
||||
repl = "<img data-embed-type=\"" etype "\" alt=\"" alt "\" src=\"" src "\" title=\"" etitle "\" />"
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
while (match(line, /![ivafe]\[[^\]]*\]\([^\)]+\)/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
token = substr(line, start, len)
|
||||
etype = substr(token, 2, 1)
|
||||
match(token, /\[[^\]]*\]/); alt = substr(token, RSTART + 1, RLENGTH - 2)
|
||||
match(token, /\([^\)]+/); src = substr(token, RSTART + 1, RLENGTH - 1)
|
||||
repl = "<img data-embed-type=\"" etype "\" alt=\"" alt "\" src=\"" src "\" />"
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
while (match(line, /![ivafe]\[[^\]]+\]/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
token = substr(line, start, len)
|
||||
etype = substr(token, 2, 1)
|
||||
src = substr(token, 4, len - 4)
|
||||
repl = "<img data-embed-type=\"" etype "\" src=\"" src "\" />"
|
||||
line = substr(line, 1, start - 1) repl substr(line, start + len)
|
||||
}
|
||||
|
||||
# force-inline image syntax (double bang)
|
||||
while (match(line, /!!\[[^\]]*\]\([^\)]+ "[^"]*"\)/)) {
|
||||
start = RSTART; len = RLENGTH
|
||||
@@ -198,5 +230,7 @@ function restore_html_tags(s, i) {
|
||||
}
|
||||
}
|
||||
|
||||
gsub(/<a href="https?:\/\/[^"]*"/, "& rel=\"noopener noreferrer\"", line)
|
||||
|
||||
print line
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ BEGIN {
|
||||
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)/) {
|
||||
if (in_p) {
|
||||
print "</p>"
|
||||
in_p = 0
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
function replace_all(text, token, value, pos, token_len) {
|
||||
function replace_all(text, token, value, pos, token_len, res) {
|
||||
token_len = length(token)
|
||||
res = ""
|
||||
while ((pos = index(text, token)) > 0) {
|
||||
text = substr(text, 1, pos - 1) value substr(text, pos + token_len)
|
||||
res = res substr(text, 1, pos - 1) value
|
||||
text = substr(text, pos + token_len)
|
||||
}
|
||||
return res text
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
current_url = ENVIRON["AWK_CURRENT_URL"]
|
||||
nav = ENVIRON["AWK_NAV"]
|
||||
title = ENVIRON["AWK_TITLE"]
|
||||
footer = ENVIRON["AWK_FOOTER"]
|
||||
style_path = ENVIRON["AWK_STYLE_PATH"]
|
||||
head_extra = ENVIRON["AWK_HEAD_EXTRA"]
|
||||
header_brand = ENVIRON["AWK_HEADER_BRAND"]
|
||||
if (current_url != "") {
|
||||
nav = replace_all(nav, "href=\"" current_url "\"", "href=\"" current_url "\" class=\"current-page\"")
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
BEGIN { done = 0 }
|
||||
BEGIN {
|
||||
new_title = ENVIRON["AWK_NEW_TITLE"]
|
||||
done = 0
|
||||
}
|
||||
/^title[[:space:]]*=/ {
|
||||
print "title = \"" new_title "\""
|
||||
done = 1
|
||||
|
||||
21
icon.svg
Normal file
21
icon.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
||||
<defs>
|
||||
<linearGradient id="bg-grad" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#4a3b69"/>
|
||||
<stop offset="100%" stop-color="#352654"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="512" height="512" rx="80" ry="80" fill="url(#bg-grad)"/>
|
||||
<text
|
||||
x="256"
|
||||
y="296"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="central"
|
||||
font-family="Georgia, 'Times New Roman', Times, serif"
|
||||
font-size="220"
|
||||
font-weight="bold"
|
||||
font-style="italic"
|
||||
fill="#debfff"
|
||||
letter-spacing="-8"
|
||||
>kewt</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 639 B |
764
kewt.sh
764
kewt.sh
@@ -11,13 +11,23 @@ usage() {
|
||||
Usage: $invoked_as [--from <src>] [--to <out>]
|
||||
$invoked_as [src] [out]
|
||||
$invoked_as --new [title]
|
||||
$invoked_as --update [dir]
|
||||
$invoked_as --post
|
||||
$invoked_as --generate-template
|
||||
$invoked_as --version
|
||||
$invoked_as --help
|
||||
|
||||
Options:
|
||||
--help Show this help message.
|
||||
--new [title] Create a new site directory (default: site)
|
||||
--from <src> Source directory (default: site)
|
||||
--to <out> Output directory (default: out)
|
||||
--help Show this help message.
|
||||
--new [title] Create a new site directory (default: site)
|
||||
--update [dir] Update site.conf and template.html with latest defaults (defaults to current directory)
|
||||
--post Create a new empty post file in the configured posts_dir with current date and time as name
|
||||
--generate-template [path] Generate a new template file at <path> (default: template.html)
|
||||
--version Show version information.
|
||||
--from <src> Source directory (default: site)
|
||||
--to <out> Output directory (default: out)
|
||||
--watch, -w Watch for file changes and rebuild automatically.
|
||||
--serve, -s [port] Start a local HTTP server after building (default port: 8000).
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -25,12 +35,10 @@ script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
|
||||
awk_dir="$script_dir/awk"
|
||||
|
||||
KEWT_TMPDIR=$(mktemp -d "/tmp/kewt_run.XXXXXX")
|
||||
trap 'rm -rf "$KEWT_TMPDIR"' EXIT HUP INT TERM
|
||||
trap 'rm -rf "$KEWT_TMPDIR"' EXIT
|
||||
trap 'exit 0' HUP INT TERM
|
||||
|
||||
ensure_root_defaults() {
|
||||
if [ ! -f "./site.conf" ]; then
|
||||
cat > "./site.conf" <<'EOF'
|
||||
title = "kewt"
|
||||
DEFAULT_CONF='title = "kewt"
|
||||
style = "kewt"
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
@@ -40,7 +48,7 @@ home_name = "Home"
|
||||
show_home_in_nav = true
|
||||
nav_links = ""
|
||||
nav_extra = ""
|
||||
footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
|
||||
footer = "made with <a href=\"https://kewt.krzak.org\">kewt</a>"
|
||||
logo = ""
|
||||
display_logo = false
|
||||
display_title = true
|
||||
@@ -49,15 +57,19 @@ favicon = ""
|
||||
generate_page_title = true
|
||||
error_page = "not_found.html"
|
||||
versioning = false
|
||||
EOF
|
||||
fi
|
||||
enable_header_links = true
|
||||
base_url = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
posts_per_page = 12
|
||||
custom_admonitions = ""'
|
||||
|
||||
if [ ! -f "./template.html" ]; then
|
||||
cat > "./template.html" <<'EOF'
|
||||
<!doctype html>
|
||||
DEFAULT_TMPL='<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{TITLE}}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
||||
@@ -65,8 +77,10 @@ EOF
|
||||
</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>
|
||||
@@ -74,9 +88,17 @@ EOF
|
||||
<article>{{CONTENT}}</article>
|
||||
<footer>{{FOOTER}}</footer>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
fi
|
||||
</html>'
|
||||
|
||||
|
||||
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() {
|
||||
@@ -86,27 +108,114 @@ create_new_site() {
|
||||
|
||||
[ -e "$new_dir" ] && die "Target '$new_dir' already exists."
|
||||
|
||||
ensure_root_defaults
|
||||
|
||||
mkdir -p "$new_dir"
|
||||
cp "./site.conf" "$new_dir/site.conf"
|
||||
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 -v new_title="$new_title" -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"
|
||||
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 = false\n---\n# %s\n' "$post_user_title" "$post_date_val" "$post_user_title" > "$file_path"
|
||||
else
|
||||
printf -- '---\ndate = "%s"\ndraft = false\n---\n' "$post_date_val" > "$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
|
||||
}
|
||||
|
||||
src=""
|
||||
out=""
|
||||
new_mode="false"
|
||||
new_title=""
|
||||
post_mode="false"
|
||||
post_title=""
|
||||
positional_count=0
|
||||
watch_mode="false"
|
||||
serve_mode="false"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -121,6 +230,33 @@ while [ $# -gt 0 ]; do
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--version|-v)
|
||||
echo "kewt version git"
|
||||
exit 0
|
||||
;;
|
||||
--post)
|
||||
post_mode="true"
|
||||
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
||||
post_title="$2"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--generate-template)
|
||||
generate_template_path="template.html"
|
||||
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
||||
generate_template_path="$2"
|
||||
shift
|
||||
fi
|
||||
generate_template "$generate_template_path"
|
||||
;;
|
||||
--update)
|
||||
update_dir="."
|
||||
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
||||
update_dir="$2"
|
||||
shift
|
||||
fi
|
||||
update_site "$update_dir"
|
||||
;;
|
||||
--from)
|
||||
[ $# -lt 2 ] && die "--from requires a value."
|
||||
src="$2"
|
||||
@@ -131,6 +267,16 @@ while [ $# -gt 0 ]; do
|
||||
out="$2"
|
||||
shift
|
||||
;;
|
||||
--watch|-w)
|
||||
watch_mode="true"
|
||||
;;
|
||||
--serve|-s)
|
||||
serve_mode="true"
|
||||
if [ $# -ge 2 ] && echo "$2" | grep -qE '^[0-9]+$'; then
|
||||
serve_port="$2"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--*)
|
||||
die "Unknown option: $1"
|
||||
;;
|
||||
@@ -150,9 +296,15 @@ done
|
||||
|
||||
[ "$new_mode" = "true" ] && create_new_site "$new_title"
|
||||
|
||||
ensure_root_defaults
|
||||
|
||||
[ -z "$src" ] && src="site"
|
||||
|
||||
if [ -z "$src" ]; then
|
||||
if [ "$post_mode" = "true" ] && [ -f "./site.conf" ]; then
|
||||
src="."
|
||||
else
|
||||
src="site"
|
||||
fi
|
||||
fi
|
||||
[ -z "$out" ] && out="out"
|
||||
|
||||
src="${src%/}"
|
||||
@@ -258,8 +410,12 @@ done < "$KEWT_TMPDIR/kewt_preserve"
|
||||
rm -f "$KEWT_TMPDIR/kewt_preserve"
|
||||
|
||||
generate_nav() {
|
||||
dinfo=$(eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -print" | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
|
||||
eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print" | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v home_name="$home_name" -v show_home_in_nav="$show_home_in_nav" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
|
||||
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"
|
||||
}
|
||||
|
||||
title="kewt"
|
||||
@@ -282,6 +438,13 @@ 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=""
|
||||
|
||||
load_config() {
|
||||
[ -f "$1" ] || return
|
||||
@@ -298,13 +461,19 @@ load_config() {
|
||||
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=${val#\'}; val=${val%\'} ;;
|
||||
\"*\")
|
||||
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" ;;
|
||||
style) style="${val#/}" ;;
|
||||
dir_indexes) dir_indexes="$val" ;;
|
||||
single_file_index) single_file_index="$val" ;;
|
||||
flatten) flatten="$val" ;;
|
||||
@@ -314,14 +483,21 @@ load_config() {
|
||||
nav_links) nav_links="$val" ;;
|
||||
nav_extra) nav_extra="$val" ;;
|
||||
footer) footer="$val" ;;
|
||||
logo) logo="$val" ;;
|
||||
logo) logo="${val#/}" ;;
|
||||
display_logo) display_logo="$val" ;;
|
||||
display_title) display_title="$val" ;;
|
||||
logo_as_favicon) logo_as_favicon="$val" ;;
|
||||
favicon) favicon="$val" ;;
|
||||
favicon) favicon="${val#/}" ;;
|
||||
generate_page_title) generate_page_title="$val" ;;
|
||||
error_page) error_page="$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" ;;
|
||||
esac
|
||||
done < "$1"
|
||||
}
|
||||
@@ -329,6 +505,12 @@ load_config() {
|
||||
load_config "./site.conf"
|
||||
load_config "$src/site.conf"
|
||||
|
||||
if [ -n "$posts_dir" ]; then
|
||||
HIDE_ARGS="$HIDE_ARGS -o -path '$src/$posts_dir/*'"
|
||||
fi
|
||||
|
||||
[ "$post_mode" = "true" ] && create_new_post "$src" "$post_title"
|
||||
|
||||
asset_version=""
|
||||
if [ "$versioning" = "true" ]; then
|
||||
asset_version="?v=$(date +%s)"
|
||||
@@ -349,6 +531,26 @@ escape_html_attr() {
|
||||
-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=""
|
||||
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" ;;
|
||||
esac
|
||||
done < "$_fm_out"
|
||||
rm -f "$_fm_out"
|
||||
}
|
||||
|
||||
nav_links_html() {
|
||||
[ -n "$nav_links" ] || return
|
||||
|
||||
@@ -395,7 +597,10 @@ nav_links_html() {
|
||||
|
||||
template="$src/template.html"
|
||||
[ -f "$template" ] || template="./template.html"
|
||||
[ -f "$template" ] || die "Template '$template' not found."
|
||||
if [ ! -f "$template" ]; then
|
||||
template="$KEWT_TMPDIR/default_template.html"
|
||||
printf '%s\n' "$DEFAULT_TMPL" > "$template"
|
||||
fi
|
||||
|
||||
[ -d "$out" ] && rm -rf "$out"
|
||||
mkdir -p "$out"
|
||||
@@ -435,6 +640,30 @@ copy_style_with_resolved_vars() {
|
||||
|
||||
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
|
||||
dir_of_file=$(dirname "$file")
|
||||
rel_dir_of_file="${dir_of_file#"$src"}"
|
||||
rel_dir_of_file="${rel_dir_of_file#/}"
|
||||
if [ "$rel_dir_of_file" = "$posts_dir" ]; 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"
|
||||
content_file="$temp_post_with_backlink"
|
||||
fi
|
||||
fi
|
||||
|
||||
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
||||
[ -z "$local_template" ] && local_template="$template"
|
||||
|
||||
@@ -480,26 +709,70 @@ render_markdown() {
|
||||
fi
|
||||
head_extra=""
|
||||
if [ -n "$favicon_src" ]; then
|
||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||
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 [ "$generate_page_title" = "true" ] && [ -n "$file" ] && [ -f "$file" ]; then
|
||||
first_heading=$(grep -m 1 '^# ' "$file" | sed 's/^# *//; s/ *$//')
|
||||
if [ -n "$first_heading" ]; then
|
||||
page_title="$first_heading - $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
|
||||
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"
|
||||
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
|
||||
|
||||
MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$file" | awk -v title="$page_title" -v nav="$nav" -v footer="$footer" -v style_path="${style_path}${asset_version}" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
|
||||
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
|
||||
|
||||
ENABLE_HEADER_LINKS="$enable_header_links" CUSTOM_ADMONITIONS="$custom_admonitions" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$content_file" | AWK_CURRENT_URL="$current_url" AWK_TITLE="$page_title" AWK_NAV="$nav" AWK_FOOTER="$footer" AWK_STYLE_PATH="${style_path}${asset_version}" AWK_HEADER_BRAND="$header_brand" AWK_HEAD_EXTRA="$head_extra" awk -f "$awk_dir/render_template.awk" "$local_template"
|
||||
}
|
||||
|
||||
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
|
||||
@@ -510,47 +783,237 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
||||
mkdir -p "$out_dir"
|
||||
|
||||
if [ -f "$dir/styles.css" ]; then
|
||||
copy_style_with_resolved_vars "$dir/styles.css" "$out_dir/styles.css"
|
||||
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
|
||||
copy_style_with_resolved_vars "$dir/style.css" "$out_dir/styles.css"
|
||||
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
|
||||
|
||||
if [ ! -f "$dir/index.md" ]; then
|
||||
if [ "$single_file_index" = "true" ]; then
|
||||
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")
|
||||
render_markdown "$md_file" > "$out_dir/index.html"
|
||||
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
|
||||
render_markdown "$md_file" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
temp_index="$KEWT_TMPDIR/index.md"
|
||||
display_dir="${rel_dir#.}"
|
||||
[ -z "$display_dir" ] && display_dir="/"
|
||||
echo "# Index of $display_dir" > "$temp_index"
|
||||
echo "" >> "$temp_index"
|
||||
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | sort | while read -r entry; do
|
||||
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
|
||||
|
||||
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | LC_ALL=C sort $sort_args | while read -r entry; do
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
template.html|site.conf|style.css|index.md) continue ;;
|
||||
esac
|
||||
if [ -d "$entry" ]; then
|
||||
echo "- [${name}/](${name}/index.html)" >> "$temp_index"
|
||||
echo "- [${name}/](${name}/index.html)" >> "$temp_list"
|
||||
elif [ "${entry%.md}" != "$entry" ]; then
|
||||
echo "- [${name%.md}](${name%.md}.html)" >> "$temp_index"
|
||||
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
|
||||
echo "- [$label](${name%.md}.html)" >> "$temp_list"
|
||||
else
|
||||
echo "- [$name]($name)" >> "$temp_index"
|
||||
echo "- [$name]($name)" >> "$temp_list"
|
||||
fi
|
||||
done
|
||||
render_markdown "$temp_index" > "$out_dir/index.html"
|
||||
rm "$temp_index"
|
||||
|
||||
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" = "true" ]; then
|
||||
render_markdown "$temp_index" "$is_home" "$target_url" > "$out_dir/index.html"
|
||||
fi
|
||||
fi
|
||||
rm -f "$temp_index" "$temp_list"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -f "$out/styles.css" ] && [ -f "$script_dir/styles/$style.css" ]; then
|
||||
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
|
||||
|
||||
@@ -564,21 +1027,40 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while
|
||||
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
|
||||
|
||||
if [ "$single_file_index" = "true" ] && [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ] && [ ! -f "$(dirname "$file")/index.md" ]; then
|
||||
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"
|
||||
render_markdown "$file" > "$out_file"
|
||||
if needs_rebuild "$file" "$out_file"; then
|
||||
render_markdown "$file" "$is_home" > "$out_file"
|
||||
fi
|
||||
else
|
||||
cp "$file" "$out/$rel_path"
|
||||
if needs_rebuild "$file" "$out/$rel_path"; then
|
||||
cp "$file" "$out/$rel_path"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -587,8 +1069,164 @@ if [ -n "$error_page" ] && [ ! -f "$out/$error_page" ]; then
|
||||
echo "# 404 - Not Found" > "$temp_404"
|
||||
echo "" >> "$temp_404"
|
||||
echo "The requested page could not be found." >> "$temp_404"
|
||||
render_markdown "$temp_404" > "$out/$error_page"
|
||||
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"
|
||||
|
||||
find "$src" -type f -name '*.md' -path "*${posts_dir:-__no_posts__}*" -print | LC_ALL=C sort -r | 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
|
||||
|
||||
echo "Build complete."
|
||||
}
|
||||
|
||||
build_site
|
||||
|
||||
if [ "$serve_mode" = "true" ]; then
|
||||
port="${serve_port:-8000}"
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
python3 -m http.server "$port" -d "$out" >/dev/null 2>&1 &
|
||||
server_pid=$!
|
||||
echo "Serving '$out' on http://localhost:$port (python3)"
|
||||
elif command -v busybox >/dev/null 2>&1; then
|
||||
busybox httpd -f -p "$port" -h "$out" >/dev/null 2>&1 &
|
||||
server_pid=$!
|
||||
echo "Serving '$out' on http://localhost:$port (busybox)"
|
||||
else
|
||||
die "Neither python3 nor busybox httpd is available to serve."
|
||||
fi
|
||||
|
||||
trap 'kill $server_pid 2>/dev/null; rm -rf "$KEWT_TMPDIR"' EXIT
|
||||
trap 'kill $server_pid 2>/dev/null; exit 0' HUP INT TERM
|
||||
fi
|
||||
|
||||
if [ "$watch_mode" = "true" ]; then
|
||||
echo "Watching for changes in '$src'..."
|
||||
touch "$KEWT_TMPDIR/watch_mark"
|
||||
while true; do
|
||||
sleep 1
|
||||
changed="$(find "$src" -type f -newer "$KEWT_TMPDIR/watch_mark" 2>/dev/null | head -n 1)"
|
||||
[ -z "$changed" ] && [ -f "site.conf" ] && [ "site.conf" -nt "$KEWT_TMPDIR/watch_mark" ] && changed="site.conf"
|
||||
[ -z "$changed" ] && [ -f "$src/site.conf" ] && [ "$src/site.conf" -nt "$KEWT_TMPDIR/watch_mark" ] && changed="$src/site.conf"
|
||||
[ -z "$changed" ] && [ -f "$template" ] && [ "$template" -nt "$KEWT_TMPDIR/watch_mark" ] && changed="$template"
|
||||
[ -z "$changed" ] && [ -d "$script_dir/styles" ] && changed="$(find "$script_dir/styles" -type f -newer "$KEWT_TMPDIR/watch_mark" 2>/dev/null | head -n 1)"
|
||||
|
||||
if [ -n "$changed" ]; then
|
||||
echo ""
|
||||
echo "Change detected, rebuilding..."
|
||||
build_site
|
||||
touch "$KEWT_TMPDIR/watch_mark"
|
||||
fi
|
||||
done
|
||||
elif [ "$serve_mode" = "true" ]; then
|
||||
wait "$server_pid"
|
||||
fi
|
||||
|
||||
29
markdown.sh
29
markdown.sh
@@ -15,10 +15,15 @@ sed_inplace() {
|
||||
fi
|
||||
}
|
||||
|
||||
temp_file="/tmp/markdown.$$.md"
|
||||
temp_file="${KEWT_TMPDIR:-/tmp}/markdown.$$.md"
|
||||
cat "$@" > "$temp_file"
|
||||
|
||||
trap 'rm -f "$temp_file" "$temp_file.tmp"' EXIT INT TERM
|
||||
trap 'rm -f "$temp_file" "$temp_file.tmp" "$temp_file.fm"' EXIT INT TERM
|
||||
|
||||
# Frontmatter
|
||||
fm_file="$temp_file.fm"
|
||||
: > "$fm_file"
|
||||
awk -v fm_out="$fm_file" -f "$awk_dir/frontmatter.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
|
||||
# Mask
|
||||
awk -f "$awk_dir/mask_inline_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
@@ -40,25 +45,37 @@ done
|
||||
sed_inplace "/^\[[^\]]*\]: */d" "$temp_file"
|
||||
|
||||
# Blocks
|
||||
sed_inplace "s/^>!\[/> [!/g" "$temp_file"
|
||||
sed_inplace "s/^>\[!/> [!/g" "$temp_file"
|
||||
|
||||
loop_count=0
|
||||
max_iterations=100
|
||||
while grep '^>' "$temp_file" >/dev/null; do
|
||||
awk -f "$awk_dir/blockquote.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
loop_count=$((loop_count + 1))
|
||||
if [ "$loop_count" -gt "$max_iterations" ]; then
|
||||
echo "Warning: Blockquote processing exceeded $max_iterations iterations on $1. Breaking to prevent infinite loop." >&2
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
awk -f "$awk_dir/blockquote_to_admonition.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -v custom_admonitions="$CUSTOM_ADMONITIONS" -f "$awk_dir/blockquote_to_admonition.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/fenced_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/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"
|
||||
|
||||
# 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
|
||||
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"
|
||||
|
||||
# Inline styles
|
||||
awk -f "$awk_dir/emoji.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -f "$awk_dir/markdown_inline.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||
awk -v input_file="$1" -v site_root="$MARKDOWN_SITE_ROOT" -v fallback_file="$MARKDOWN_FALLBACK_FILE" -f "$awk_dir/markdown_embed.awk" "$temp_file"
|
||||
rm "$temp_file"
|
||||
|
||||
7
package.json
Normal file
7
package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "kewt",
|
||||
"description": "A minimalist static site generator inspired by werc",
|
||||
"global": "true",
|
||||
"install": "make install",
|
||||
"scripts": ["kewt"]
|
||||
}
|
||||
16
packaging/AUR/.SRCINFO.git
Normal file
16
packaging/AUR/.SRCINFO.git
Normal file
@@ -0,0 +1,16 @@
|
||||
pkgbase = kewt-git
|
||||
pkgdesc = A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||
pkgver = r0.0000000
|
||||
pkgrel = 3
|
||||
url = https://kewt.krzak.org
|
||||
arch = any
|
||||
license = ISC
|
||||
makedepends = git
|
||||
depends = sh
|
||||
provides = kewt
|
||||
conflicts = kewt
|
||||
conflicts = kewt-bin
|
||||
source = kewt-git::git+https://git.krzak.org/N0VA/kewt.git
|
||||
sha256sums = SKIP
|
||||
|
||||
pkgname = kewt-git
|
||||
15
packaging/AUR/.SRCINFO.template
Normal file
15
packaging/AUR/.SRCINFO.template
Normal file
@@ -0,0 +1,15 @@
|
||||
pkgbase = kewt-bin
|
||||
pkgdesc = A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||
pkgver = VERSION_PLACEHOLDER
|
||||
pkgrel = 2
|
||||
url = https://kewt.krzak.org
|
||||
arch = any
|
||||
license = ISC
|
||||
depends = sh
|
||||
provides = kewt
|
||||
conflicts = kewt
|
||||
conflicts = kewt-git
|
||||
source = kewt-bin-VERSION_PLACEHOLDER.sh::https://git.krzak.org/N0VA/kewt/releases/download/vVERSION_PLACEHOLDER/kewt
|
||||
sha256sums = SHA256SUM_PLACEHOLDER
|
||||
|
||||
pkgname = kewt-bin
|
||||
@@ -1,16 +1,16 @@
|
||||
# Maintainer: n0va <n0va@krzak.org>
|
||||
pkgname=kewt-git
|
||||
pkgver=r0.0000000
|
||||
pkgrel=1
|
||||
pkgrel=2
|
||||
pkgdesc="A minimalist, 100% POSIX, static site generator inspired by werc and kew"
|
||||
arch=('any')
|
||||
url="https://git.krzak.org/N0VA/kewt"
|
||||
license=('MIT')
|
||||
url="https://kewt.krzak.org"
|
||||
license=('ISC')
|
||||
makedepends=('git')
|
||||
depends=('sh')
|
||||
provides=('kewt')
|
||||
conflicts=('kewt' 'kewt-bin')
|
||||
source=("${pkgname}::git+${url}.git")
|
||||
source=("${pkgname}::git+https://git.krzak.org/N0VA/kewt.git")
|
||||
sha256sums=('SKIP')
|
||||
|
||||
pkgver() {
|
||||
|
||||
@@ -4,12 +4,12 @@ pkgver=VERSION_PLACEHOLDER
|
||||
pkgrel=1
|
||||
pkgdesc="A minimalist, 100% POSIX, static site generator inspired by werc and kew"
|
||||
arch=('any')
|
||||
url="https://git.krzak.org/N0VA/kewt"
|
||||
license=('MIT')
|
||||
url="https://kewt.krzak.org"
|
||||
license=('ISC')
|
||||
depends=('sh')
|
||||
provides=('kewt')
|
||||
conflicts=('kewt' 'kewt-git')
|
||||
source=("${pkgname}-${pkgver}.sh::${url}/releases/download/v${pkgver}/kewt")
|
||||
source=("${pkgname}-${pkgver}.sh::https://git.krzak.org/N0VA/kewt/releases/download/v${pkgver}/kewt")
|
||||
sha256sums=('SHA256SUM_PLACEHOLDER')
|
||||
|
||||
build() {
|
||||
|
||||
16
packaging/homebrew/kewt.rb.template
Normal file
16
packaging/homebrew/kewt.rb.template
Normal file
@@ -0,0 +1,16 @@
|
||||
class Kewt < Formula
|
||||
desc "Minimalist static site generator inspired by werc"
|
||||
homepage "https://kewt.krzak.org"
|
||||
url "https://github.com/n0va-bot/kewt/releases/download/vVERSION_PLACEHOLDER/kewt"
|
||||
sha256 "SHA256SUM_PLACEHOLDER"
|
||||
license "ISC"
|
||||
version "VERSION_PLACEHOLDER"
|
||||
|
||||
def install
|
||||
bin.install "kewt"
|
||||
end
|
||||
|
||||
test do
|
||||
system "#{bin}/kewt", "--version"
|
||||
end
|
||||
end
|
||||
16
site.conf
16
site.conf
@@ -1,16 +0,0 @@
|
||||
title = "kewt"
|
||||
style = "kewt"
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
|
||||
logo = ""
|
||||
display_logo = false
|
||||
display_title = true
|
||||
logo_as_favicon = true
|
||||
favicon = ""
|
||||
order = ""
|
||||
home_name = "Home"
|
||||
show_home_in_nav = true
|
||||
nav_links = ""
|
||||
nav_extra = ""
|
||||
61
site/Docs/configuration.md
Normal file
61
site/Docs/configuration.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Configuration
|
||||
|
||||
## Dot Files
|
||||
|
||||
- `.kewtignore` - files/directories to ignore completely. If the file is empty, the whole directory gets ignored.
|
||||
- `.kewthide` - files/directories to hide from navigation but still process. Same empty-file rules as `.kewtignore`.
|
||||
- `.kewtpreserve` - files/directories to copy as-is without converting markdown to HTML. Same empty-file rules again.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
You can set metadata for a page using a `site.conf`-style frontmatter block at the very top of `.md` files:
|
||||
|
||||
```conf
|
||||
---
|
||||
title = "Custom Page Title"
|
||||
date = "2026-03-23 11:32"
|
||||
draft = false
|
||||
description = "A short page summary"
|
||||
---
|
||||
```
|
||||
- `title` - overrides the page title, post name in index links, and RSS `<title>`.
|
||||
- `date` - overrides the post date and time. Supports `YYYY-MM-DD` and `YYYY-MM-DD HH:MM` (or `HH-MM`).
|
||||
- `draft` - if `true`, the file is excluded from HTML generation.
|
||||
- `description` - page description, used for Open Graph `og:description` meta tag.
|
||||
|
||||
## Directory Index Customisation
|
||||
|
||||
By default, directories without an `index.md` get an auto-generated index page listing their contents.
|
||||
|
||||
If you create your own `index.md` in a directory, you can still include the auto-generated file list by using the `{{LIST}}` placeholder:
|
||||
|
||||
```md
|
||||
# Blog
|
||||
|
||||
This is my blog. The posts are below. The top-most one is the most recent.
|
||||
|
||||
{{LIST}}
|
||||
```
|
||||
The `{{LIST}}` tag will be replaced with the generated list of links to child pages and files, exactly as in case the custom index didn't exist.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
You can auto-generate a Table of Contents by placing `{{TOC}}` anywhere in your markdown file. It collects all `h2` and `h3` headings and generates an ordered list with anchor links.
|
||||
|
||||
## Footnotes
|
||||
|
||||
Footnotes use the `[^id]` syntax inline and `[^id]: text` for definitions at the bottom of the file. They are rendered as a numbered `<section>` at the end of the page.
|
||||
|
||||
## Definition Lists
|
||||
|
||||
Definition lists use the standard syntax:
|
||||
|
||||
```md
|
||||
Term
|
||||
: Definition
|
||||
```
|
||||
This renders as `<dl><dt>Term</dt><dd>Definition</dd></dl>`. Multiple definitions per term are supported.
|
||||
|
||||
## Emoji Shortcodes
|
||||
|
||||
Standard GitHub/MkDocs emoji shortcodes like `:smile:`, `:fire:`, `:rocket:` are automatically replaced with their Unicode emoji equivalents. Shortcodes inside code blocks are left as-is.
|
||||
21
site/Docs/embeds.md
Normal file
21
site/Docs/embeds.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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
|
||||
3
site/Docs/index.md
Normal file
3
site/Docs/index.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Documentation
|
||||
|
||||
{{LIST}}
|
||||
42
site/Docs/installation.md
Normal file
42
site/Docs/installation.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 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
|
||||
```
|
||||
77
site/Docs/usage.md
Normal file
77
site/Docs/usage.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# 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`.
|
||||
|
||||
## 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
|
||||
enable_header_links = true
|
||||
base_url = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
posts_per_page = 12
|
||||
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.
|
||||
- `posts_per_page` - number of posts per page in paginated post indexes (default: 12). Set to 0 to disable pagination.
|
||||
- `enable_header_links` - turns markdown section headings into clickable anchor links (default: true)
|
||||
- `custom_admonitions` - comma separated list of custom admonitions
|
||||
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}}
|
||||
BIN
site/favicon.ico
Normal file
BIN
site/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
113
site/index.md
113
site/index.md
@@ -1,8 +1,12 @@
|
||||
# _kewt_
|
||||
### Pronounced "cute"
|
||||
|
||||
***
|
||||
|
||||
# [Go to the repo](https://git.krzak.org/N0VA/kewt)
|
||||
|
||||
***
|
||||
|
||||
_kewt_ is a minimalist ssg inspired by _[werc](http://werc.cat-v.org/)_ and _[kew](https://github.com/uint23/kew)_
|
||||
|
||||
It's meant to be a static site generator, like _[kew](https://github.com/uint23/kew)_ but use only default (POSIX) tooling, like _[werc](http://werc.cat-v.org/)_ (and definitely unlike _[kew](https://github.com/uint23/kew)_)
|
||||
@@ -10,97 +14,32 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
||||
## Features
|
||||
|
||||
- No dependencies
|
||||
- Frontmatter support (title, date, draft, description)
|
||||
- Supports many embed types
|
||||
- Automatic css variable replacement for older browsers
|
||||
- Automatic inlining and embedding of many filetypes with `\![link]` or `\`
|
||||
- Typed embeds: `\!i`, `\!v`, `\!a`, `\!f`, `\!e`
|
||||
- Inline html support
|
||||
- MFM `$font` and `\<plain>` tags
|
||||
- Admonition support (that's what the blocks like the warning block below are called)
|
||||
- GFM Admonition support (that's what the blocks like the warning block below are called)
|
||||
- Task list support (`- [ ]`, `- [x]`)
|
||||
- RSS/Feed generation and Sitemap support
|
||||
- Post creation via `--post`
|
||||
- Automatic 404 page generation
|
||||
- `?v=n` support for cache busting
|
||||
- Code block classes for use with external libraries like highlight.js or prism.js (both tested)
|
||||
- Clickable markdown header anchors
|
||||
- Mobile responsive layout
|
||||
- Customisable directory index pages with `{{LIST}}`
|
||||
- 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
|
||||
|
||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||
***
|
||||
|
||||
## Installation
|
||||
|
||||
You can clone the repository to use `kewt.sh` directly, or you can download the standalone executable, which bundles all dependencies into a single file:
|
||||
|
||||
```sh
|
||||
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/latest/download/kewt
|
||||
chmod +x kewt
|
||||
```
|
||||
|
||||
On Arch Linux, _kewt_ is available on the 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
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
./kewt.sh --help
|
||||
./kewt.sh --new [title]
|
||||
./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`.
|
||||
|
||||
## 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 = ""
|
||||
```
|
||||
|
||||
- `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)
|
||||
|
||||
## 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
|
||||
|
||||
## Credits
|
||||
|
||||
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||
|
||||
>![WARNING]
|
||||
>Most of this was coded at night, while sleepy and a bit sick, and after walking for about 4 hours around a forest, so...
|
||||
> [!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...
|
||||
|
||||
@@ -3,10 +3,23 @@ style = "kewt"
|
||||
dir_indexes = true
|
||||
single_file_index = true
|
||||
flatten = false
|
||||
footer = "<a href="https://kewt.krzak.org"><img src="/button.gif" /></a>"
|
||||
footer = "<a href=\"https://kewt.krzak.org\"><img src=\"/button.gif\" /></a>"
|
||||
logo = ""
|
||||
display_logo = false
|
||||
display_title = true
|
||||
logo_as_favicon = true
|
||||
favicon = ""
|
||||
order = ""
|
||||
logo_as_favicon = false
|
||||
favicon = "favicon.ico"
|
||||
order = "Home, Docs, depths, Heaven"
|
||||
home_name = "Home"
|
||||
show_home_in_nav = true
|
||||
nav_links = ""
|
||||
nav_extra = ""
|
||||
generate_page_title = true
|
||||
error_page = "not_found.html"
|
||||
versioning = true
|
||||
enable_header_links = true
|
||||
base_url = "https://kewt.krzak.org"
|
||||
custom_admonitions = ""
|
||||
generate_feed = false
|
||||
feed_file = "rss.xml"
|
||||
posts_dir = ""
|
||||
138
styles/kewt.css
138
styles/kewt.css
@@ -1,25 +1,28 @@
|
||||
:root {
|
||||
--bg: #646c7f;
|
||||
--fg: #fffde0;
|
||||
--fg-link: #fff18f;
|
||||
--code-bg: #32394a;
|
||||
--code-border: #8f95a4;
|
||||
--code-fg: #fffde0;
|
||||
--code-sel: #fff18f;
|
||||
--code-prop: #ffd27f;
|
||||
--code-val: #cde7ff;
|
||||
--code-var: #b9ffbe;
|
||||
--code-com: #d0d0d0;
|
||||
--adm-note-bg: #3f5666;
|
||||
--adm-note-border: #a8d8ff;
|
||||
--adm-tip-bg: #3f664c;
|
||||
--adm-tip-border: #b9ffbe;
|
||||
--adm-important-bg: #5a4a6c;
|
||||
--adm-important-border: #e4c7ff;
|
||||
--adm-warning-bg: #6b5539;
|
||||
--adm-warning-border: #ffe0a8;
|
||||
--adm-caution-bg: #6f3f3f;
|
||||
--adm-caution-border: #ffb4b4;
|
||||
--bg: #4a3b69;
|
||||
--bg-deep: #352654;
|
||||
--fg: #fbf5ff;
|
||||
--fg-muted: #c8b9df;
|
||||
--fg-link: #dfaeff;
|
||||
--fg-heading: #debfff;
|
||||
--code-bg: #31234c;
|
||||
--code-border: #8060af;
|
||||
--code-fg: #fbf5ff;
|
||||
--code-sel: #ffef99;
|
||||
--code-prop: #ffdfba;
|
||||
--code-val: #cae2ff;
|
||||
--code-var: #caffc2;
|
||||
--code-com: #b8aac8;
|
||||
--adm-note-bg: #353866;
|
||||
--adm-note-border: #b8c5ff;
|
||||
--adm-tip-bg: #295246;
|
||||
--adm-tip-border: #aeffda;
|
||||
--adm-important-bg: #533076;
|
||||
--adm-important-border: #f4d9ff;
|
||||
--adm-warning-bg: #634631;
|
||||
--adm-warning-border: #ffe2bd;
|
||||
--adm-caution-bg: #662d43;
|
||||
--adm-caution-border: #ffc4d5;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -29,18 +32,22 @@ body {
|
||||
color: var(--fg);
|
||||
font-family: serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.2;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 20px;
|
||||
padding-bottom: 0;
|
||||
border-bottom: 1px solid var(--code-border);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
margin: 0;
|
||||
font-size: 35px;
|
||||
font-weight: normal;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
color: var(--fg-heading);
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
@@ -57,18 +64,26 @@ header a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
header a:hover {
|
||||
color: var(--bg-deep);
|
||||
background: var(--fg);
|
||||
}
|
||||
|
||||
#side-bar {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
padding-left: 20px;
|
||||
margin-right: 14px;
|
||||
border-right: 1px solid var(--code-border);
|
||||
padding-right: 7px;
|
||||
}
|
||||
|
||||
.side-title {
|
||||
font-size: 25px;
|
||||
margin: 20px 0 8px 0;
|
||||
color: var(--fg);
|
||||
color: var(--fg-heading);
|
||||
}
|
||||
|
||||
#side-bar ul {
|
||||
@@ -87,6 +102,14 @@ a {
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
#side-bar a.current-page {
|
||||
font-weight: bold;
|
||||
color: var(--fg);
|
||||
border-left: 3px solid var(--fg-link);
|
||||
padding-left: 7px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: var(--fg);
|
||||
color: var(--bg);
|
||||
@@ -100,7 +123,7 @@ article {
|
||||
h3 {
|
||||
margin-top: 30px;
|
||||
font-size: 25px;
|
||||
color: var(--fg);
|
||||
color: var(--fg-heading);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@@ -197,10 +220,11 @@ pre code {
|
||||
}
|
||||
|
||||
footer {
|
||||
padding-top: 80px;
|
||||
padding-top: 60px;
|
||||
font-style: italic;
|
||||
font-size: 17px;
|
||||
margin-bottom: 20px;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
|
||||
article,
|
||||
@@ -220,3 +244,65 @@ footer img {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
margin: 24px 0;
|
||||
border: 0;
|
||||
border-top: 1px solid var(--code-border);
|
||||
}
|
||||
|
||||
.nav-toggle, .nav-toggle-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@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 {
|
||||
display: none;
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
width: auto;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--code-border);
|
||||
padding: 0 0 20px 0;
|
||||
margin: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
.nav-toggle:checked ~ #side-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
article {
|
||||
margin: 0 20px 0 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.task-list-item-checkbox {
|
||||
margin: 0 0.2em 0.25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>{{TITLE}}</title>
|
||||
|
||||
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
||||
{{HEAD_EXTRA}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{HEADER_BRAND}}</h1>
|
||||
</header>
|
||||
|
||||
<nav id="side-bar">{{NAV}}</nav>
|
||||
|
||||
<article>{{CONTENT}}</article>
|
||||
<footer>{{FOOTER}}</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -26,7 +26,13 @@ exit $?
|
||||
#==PAYLOAD==
|
||||
EOF
|
||||
|
||||
tar -cz -C "$REPO_ROOT" kewt.sh markdown.sh awk styles >> "$OUT_FILE"
|
||||
VERSION=$(git describe --tags 2>/dev/null || echo "standalone")
|
||||
tmpbuild=$(mktemp -d)
|
||||
cp -r "$REPO_ROOT/kewt.sh" "$REPO_ROOT/markdown.sh" "$REPO_ROOT/awk" "$REPO_ROOT/styles" "$tmpbuild/"
|
||||
sed -e "s/kewt version git/kewt version $VERSION/" "$tmpbuild/kewt.sh" > "$tmpbuild/kewt.sh.tmp" && mv "$tmpbuild/kewt.sh.tmp" "$tmpbuild/kewt.sh"
|
||||
chmod +x "$tmpbuild/kewt.sh" "$tmpbuild/markdown.sh"
|
||||
tar -cz -C "$tmpbuild" kewt.sh markdown.sh awk styles >> "$OUT_FILE"
|
||||
rm -rf "$tmpbuild"
|
||||
|
||||
chmod +x "$OUT_FILE"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user