summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile24
-rw-r--r--bootstrap.sh285
-rw-r--r--compile_flags.txt4
-rwxr-xr-xmain.c3351
4 files changed, 3664 insertions, 0 deletions
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..15f1596
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,24 @@
+FROM nginx:alpine
+
+RUN apk add git-daemon cgit certbot certbot-nginx fcgiwrap apache2-utils gcc musl-dev
+RUN adduser -D -h /srv/git git
+
+WORKDIR /usr/build/
+COPY bootstrap.sh .
+COPY main.c .
+COPY static ./static
+
+RUN DEBIAN_FRONTEND=noninteractive sh bootstrap.sh
+
+RUN printf "\
+ git daemon --reuseaddr --base-path=/srv/git/ /srv/git/ &\n\
+ fcgiwrap -f &\n\
+ nginx &\n\
+ wait -n\n\
+ exit $?\
+" >> /usr/local/bin/entrypoint
+
+# ssh, http, https, git
+EXPOSE 22 80 443 9418
+
+ENTRYPOINT [ "entrypoint" ]
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100644
index 0000000..b0e9eff
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,285 @@
+echo "[ 1/9] Setting up Git"
+
+if ! id git >/dev/null 2>&1; then
+ useradd -m -b /srv git
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] Git user already created."
+fi
+
+GIT_HOME=$( grep git /etc/passwd | cut -d: -f6 )
+
+if [ "$GIT_HOME" = "" ]; then
+ echo "[ERROR] No git home!"
+ exit 1
+fi
+
+echo "[INFO] Git home: $GIT_HOME"
+
+if ! command -v git >/dev/null 2>&1; then
+ apt-get install -y git
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] Git already installed."
+fi
+
+if ls /usr/lib/git-core/git-http-backend >/dev/null 2>&1; then
+ GIT_HTTP_BACKEND=/usr/lib/git-core/git-http-backend
+elif ls /usr/libexec/git-core/git-http-backend >/dev/null 2>&1; then
+ GIT_HTTP_BACKEND=/usr/libexec/git-core/git-http-backend
+else
+ echo "[ERROR] git-http-backend not found!"
+ exit 1
+fi
+
+echo "[INFO] git-http-backend: $GIT_HTTP_BACKEND"
+
+echo "[ 2/9] Setting up cgit"
+
+if [ ! command -v /usr/share/webapps/cgit/cgit.cgi >/dev/null 2>&1 ] &&
+ [ ! command -v /usr/lib/cgit/cgit.cgi >/dev/null 2>&1 ]; then
+ apt-get install -y cgit
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] cgit already installed."
+fi
+
+if command -v /usr/share/webapps/cgit/cgit.cgi >/dev/null 2>&1; then
+ CGIT_CGI=/usr/share/webapps/cgit/cgit.cgi
+elif command -v /usr/lib/cgit/cgit.cgi >/dev/null 2>&1; then
+ CGIT_CGI=/usr/lib/cgit/cgit.cgi
+else
+ echo "[ERROR] cgit not found!"
+ exit 1
+fi
+
+echo "[INFO] Found cgit: $CGIT_CGI"
+
+echo "[ 3/9] Setting up nginx"
+
+if ! command -v nginx >/dev/null 2>&1; then
+ apt-get install -y nginx
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] nginx already installed."
+fi
+
+echo "[ 4/9] Setting up certbot"
+
+if ! command -v certbot >/dev/null 2>&1; then
+ apt-get install -y python3-certbot-nginx
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] certbot already installed."
+fi
+
+echo "[ 5/9] Setting up fcgiwrap"
+
+if ! command -v fcgiwrap >/dev/null 2>&1; then
+ apt-get install -y fcgiwrap
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] fcgiwrap already installed."
+fi
+
+echo "[ 6/9] Setting up htpasswd"
+
+if ! command -v htpasswd >/dev/null 2>&1; then
+ apt-get install -y apache2-utils
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] htpasswd already installed."
+fi
+
+echo "[ 7/9] Setting up GCC"
+
+if ! command -v gcc >/dev/null 2>&1; then
+ apt-get install -y gcc
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[SKIP] GCC already installed."
+fi
+
+echo "[ 8/9] Compiling and setting up CGI program"
+
+COMPILE="\
+ -Wno-old-style-declaration \
+ -Wno-missing-field-initializers -Wno-missing-braces \
+ -Wall -Wextra -Werror -pedantic -mshstk \
+ -O3 -o main main.c"
+
+SAN=-fsanitize=address,undefined,leak
+
+if gcc $SAN $COMPILE >/dev/null 2>&1; then
+ gcc $SAN $COMPILE
+ [ $? -eq 0 ] || exit $?
+else
+ echo "[INFO] Sanitizers are disabled."
+ gcc $COMPILE
+ [ $? -eq 0 ] || exit $?
+fi
+
+chown root:root main
+[ $? -eq 0 ] || exit $?
+
+mv -f main /srv/
+[ $? -eq 0 ] || exit $?
+
+mv -f static /srv/static
+[ $? -eq 0 ] || exit $?
+
+echo "[ 9/9] Configuring"
+
+if [ ! -d /etc/nginx/sites-available ]; then
+ mkdir /etc/nginx/sites-available
+ [ $? -eq 0 ] || exit $?
+fi
+
+if [ ! -d /etc/nginx/sites-enabled ]; then
+ mkdir /etc/nginx/sites-enabled
+ [ $? -eq 0 ] || exit $?
+fi
+
+if [ ! -f /etc/nginx/sites-enabled/default ]; then
+ ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
+ [ $? -eq 0 ] || exit $?
+fi
+
+cat <<EOF >/etc/nginx/sites-available/default
+server {
+ listen 80;
+ listen [::]:80;
+
+ server_name _;
+
+ location ~ ^/git_write/ {
+ rewrite ^/git_write/(.*) /\$1 break;
+
+ auth_basic "Git";
+ auth_basic_user_file $GIT_HOME.htpasswd;
+
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $GIT_HTTP_BACKEND;
+ fastcgi_param GIT_PROJECT_ROOT $GIT_HOME;
+ fastcgi_param PATH_INFO \$uri;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ }
+
+ location ~ ^/git_read/ {
+ rewrite ^/git_read/(.*) /\$1 break;
+
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $GIT_HTTP_BACKEND;
+ fastcgi_param GIT_PROJECT_ROOT $GIT_HOME;
+ fastcgi_param PATH_INFO \$uri;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ }
+
+ location ~ \\.git {
+ if (\$arg_service = git-receive-pack) {
+ rewrite /(.*) /git_write/\$1 last;
+ }
+ if (\$uri ~ ^/.*/git-receive-pack\$) {
+ rewrite /(.*) /git_write/\$1 last;
+ }
+ if (\$arg_service = git-upload-pack) {
+ rewrite /(.*) /git_read/\$1 last;
+ }
+ if (\$uri ~ ^/.*/git-upload-pack\$) {
+ rewrite /(.*) /git_read/\$1 last;
+ }
+ }
+
+ location ^~ /git/ {
+ rewrite ^/git/(.*) /\$1 break;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $CGIT_CGI;
+ fastcgi_param PATH_INFO \$uri;
+ fastcgi_param QUERY_STRING \$args;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ }
+
+ location ~* \\.(txt|asc|htm|css|svg|jpg|png|gif|ico|woff|woff2|js|wasm|mp3)\$ {
+ rewrite ^/(.*) /static/plain/\$1 break;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $CGIT_CGI;
+ fastcgi_param PATH_INFO \$uri;
+ fastcgi_param QUERY_STRING \$args;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ }
+
+ location / {
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME /srv/main.cgi;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ }
+}
+EOF
+[ $? -eq 0 ] || exit $?
+
+echo "[INFO] Written /etc/nginx/sites-available/default:"
+
+cat /etc/nginx/sites-available/default
+
+cat <<EOF >/etc/nginx/mime.types
+types {
+ text/plain txt;
+ text/plain asc;
+ text/html htm;
+ text/css css;
+ image/svg+xml svg;
+ image/jpeg jpg;
+ image/png png;
+ image/gif gif;
+ image/x-icon ico;
+ application/font-woff woff;
+ application/font-woff2 woff2;
+ application/javascript js;
+ application/wasm wasm;
+ audio/mpeg mp3;
+}
+EOF
+[ $? -eq 0 ] || exit $?
+
+echo "[INFO] Written /etc/nginx/mime.types:"
+
+cat /etc/nginx/mime.types
+
+cat <<EOF >/etc/nginx/nginx.conf
+user git;
+worker_processes 1;
+pid /run/nginx.pid;
+include /etc/nginx/modules-enabled/*.conf;
+
+events {
+ worker_connections 768;
+}
+
+http {
+ sendfile on;
+ tcp_nopush on;
+ types_hash_max_size 2048;
+ server_names_hash_bucket_size 256;
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_prefer_server_ciphers on;
+
+ access_log /var/log/nginx/access.log;
+ error_log /var/log/nginx/error.log;
+
+ gzip on;
+
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/sites-enabled/*;
+}
+EOF
+[ $? -eq 0 ] || exit $?
+
+echo "[INFO] Written /etc/nginx/nginx.conf:"
+
+cat /etc/nginx/nginx.conf
+
+echo "[INFO] All done!"
diff --git a/compile_flags.txt b/compile_flags.txt
new file mode 100644
index 0000000..1a27fb0
--- /dev/null
+++ b/compile_flags.txt
@@ -0,0 +1,4 @@
+-Wall
+-Wextra
+-Werror
+-pedantic
diff --git a/main.c b/main.c
new file mode 100755
index 0000000..975d9fd
--- /dev/null
+++ b/main.c
@@ -0,0 +1,3351 @@
+#if 0 /*
+#/ ================================================================
+#/
+#/ main.c
+#/
+#/ CGI program to render HTTP-only web site.
+#/
+#/ ----------------------------------------------------------------
+#/
+#/ Qualities
+#/
+#/ - Single source file
+#/ - No JavaScript
+#/ - No external dependencies
+#/ - No dynamic memory management
+#/ - Simple bootstrap script
+#/ - Not a single LLM-generated line of code
+#/
+#/ NOTE
+#/
+#/ - At the start of this file you can see a shell script
+#/ that will compile and install the program into `/srv/`
+#/
+#/ TODO
+#/
+#/ - CGI program
+#/ - Hierarchical tags.
+#/ - Blacklist and throttling.
+#/ - Wrap other CGI programs.
+#/ - CI terminal
+#/ - Convay's Game of Life with `<meta http-equiv="refresh" content="1" >`
+#/ - Bootstrap script
+#/ - ssh
+#/ - git config
+#/ - highlight
+#/ - tor, onion service
+#/ - daemons
+#/ - ufw
+#/
+#/ ================================================================
+
+USR=git
+if [ -n "$1" ]; then
+ USR=$1
+fi
+SRC=${0##*./}
+BIN=${SRC%.*}.cgi
+export REQUEST_METHOD=GET
+export DOCUMENT_URI=/theme
+gcc \
+ -Wno-old-style-declaration \
+ -Wno-missing-field-initializers -Wno-missing-braces \
+ -Wimplicit-fallthrough=0 \
+ -Wall -Wextra -Werror -pedantic \
+ -fsanitize=address,undefined,leak -mshstk -O3 \
+ -o $BIN $SRC \
+ && ./$BIN \
+ && sudo chown root:root $BIN \
+ && sudo mv -f $BIN /srv/
+exit $? # */
+#endif
+
+// ================================================================
+//
+// Headers
+//
+// ================================================================
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+// ================================================================
+//
+// Configuration
+//
+// ================================================================
+
+#define COOKIE_THEME "theme"
+
+#define FORM_NAME "submit"
+#define FORM_ITEM_DATA "data"
+
+enum {
+ DEFAULT_SCALE = 100,
+ DEFAULT_WIDTH = 42,
+
+ SCALE_MIN = 1,
+ SCALE_MAX = 1000,
+ WIDTH_MIN = 20,
+ WIDTH_MAX = 100,
+
+ MAX_REQUEST_METHOD_SIZE = 8,
+ MAX_REQUEST_SIZE = 512,
+ MAX_COOKIE_SIZE = 512,
+ MAX_CONTENT_TYPE_SIZE = 64,
+};
+
+// ================================================================
+//
+// Basic definitions
+//
+// ================================================================
+
+typedef unsigned char u8;
+typedef int i32;
+typedef unsigned int u32;
+typedef long long i64;
+typedef unsigned long long u64;
+typedef signed char b8;
+typedef char c8;
+
+typedef struct {
+ c8 const *family;
+ c8 const *file;
+ c8 const *weight;
+ c8 const *style;
+} Font;
+
+typedef struct {
+ c8 const *text;
+ c8 const *href;
+ c8 const *target;
+} Link;
+
+typedef struct {
+ i32 type;
+ i32 color;
+ i32 background;
+ i32 width;
+ i32 layout;
+ i32 icon;
+ i32 action;
+ i32 padding_top;
+ i32 scale;
+ i32 line_height;
+ b8 nowrap;
+ b8 unselectable;
+ c8 const *text;
+ c8 const *href;
+ c8 const *image;
+} Tag;
+
+typedef struct {
+ c8 const *id;
+ c8 const *id_parent;
+ c8 const *title;
+ Tag * content;
+} Doc;
+
+typedef struct {
+ c8 const *method;
+ c8 const *uri;
+ i32 index;
+} Location;
+
+typedef struct {
+ c8 const *alias;
+ c8 const *url;
+} Redirect;
+
+// ================================================================
+//
+// Content data
+//
+// ================================================================
+
+#define INSERT_THEME ((c8 const *) 1)
+
+enum {
+ SECONDS_IN_MONTH = 2629746,
+
+ DOC_404 = 0,
+ DOC_MAIN,
+ DOC_ABOUT,
+ DOC_MUSIC,
+ DOC_PROJECTS,
+ DOC_BLOG,
+ DOC_PGP,
+ DOC_LINKS,
+ DOC_THEME,
+ DOC_LINKS_MONERO,
+ DOC_LINKS_BITCOIN,
+ DOC_SAW,
+
+ NUM_DOCS,
+
+ LAYOUT_NONE = 0,
+ LAYOUT_LEFT,
+ LAYOUT_CENTER,
+ LAYOUT_RIGHT,
+ LAYOUT_FLOAT_LEFT,
+ LAYOUT_FLOAT_CENTER,
+ LAYOUT_FLOAT_RIGHT,
+ LAYOUT_MARGIN_LEFT,
+ LAYOUT_MARGIN_RIGHT,
+
+ ICON_NONE = 0,
+ ICON_REGULAR,
+ ICON_SOLID,
+ ICON_BRAND,
+
+ TAG_PLAIN = 0,
+ TAG_RAW,
+ TAG_END,
+ TAG_BEGIN,
+ TAG_CAPTION,
+ TAG_SUBTITLE,
+ TAG_BLOCK,
+ TAG_CODE,
+ TAG_CLIPBOARD,
+ TAG_BLACK_FLAG,
+ TAG_SUBMIT_TEXT,
+
+ ACTION_BLANK = 0,
+ ACTION_SELF,
+ ACTION_SUBMIT,
+
+ // Color IDs
+
+ COLOR_NONE = 0,
+ COLOR_FOREGROUND_TEXT,
+ COLOR_FOREGROUND_HOVER,
+ COLOR_FOREGROUND_LINK,
+ COLOR_FOREGROUND_NAV,
+ COLOR_FOREGROUND_TITLE,
+ COLOR_FOREGROUND_DIM,
+ COLOR_FOREGROUND_FADE,
+ COLOR_FOREGROUND_HEAD,
+ COLOR_FOREGROUND_FOOT,
+ COLOR_FOREGROUND_SPOT,
+ COLOR_BACKGROUND_NAV,
+ COLOR_BACKGROUND_FAR,
+ COLOR_BACKGROUND_FADE,
+ COLOR_BACKGROUND_NEAR,
+
+ COLOR_RAINBOW_LIGHT,
+ COLOR_RAINBOW_DARK,
+
+ COLORS_MAX,
+
+ THEME_GUATTARI = 0,
+ THEME_MONERO,
+ THEME_BLACK,
+ THEME_WHITE,
+ THEME_PINK,
+ THEME_LIGHT,
+};
+
+static c8 const *RAINBOW_LIGHT[] = {
+ "ff3050",
+ "ff8080",
+ "e7d000",
+ "00ff00",
+ "00ffff",
+ "00ff80",
+ "a0b0ff",
+ "ff00ff",
+};
+
+static c8 const *RAINBOW_DARK[] = {
+ "7f1020",
+ "7f4040",
+ "736800",
+ "007f00",
+ "007f7f",
+ "007f40",
+ "40407f",
+ "7f007f",
+};
+
+#define NUM_COLORS ((i32) (sizeof COLORS / sizeof *COLORS))
+
+static c8 COLORS[][7] = {
+ [COLOR_NONE] = "d4d4d4",
+ [COLOR_FOREGROUND_TEXT] = "d4d4d4",
+ [COLOR_FOREGROUND_HOVER] = "e8e4d0",
+ [COLOR_FOREGROUND_LINK] = "a090a0",
+ [COLOR_FOREGROUND_NAV] = "a090a0",
+ [COLOR_FOREGROUND_TITLE] = "d4d4d4",
+ [COLOR_FOREGROUND_DIM] = "707070",
+ [COLOR_FOREGROUND_FADE] = "707070",
+ [COLOR_FOREGROUND_HEAD] = "6b6770",
+ [COLOR_FOREGROUND_FOOT] = "b0b0b0",
+ [COLOR_FOREGROUND_SPOT] = "a090a0",
+ [COLOR_BACKGROUND_NAV] = "222222",
+ [COLOR_BACKGROUND_FAR] = "19171a",
+ [COLOR_BACKGROUND_FADE] = "17151a",
+ [COLOR_BACKGROUND_NEAR] = "0a0a0a",
+};
+
+static c8 THEMES[][NUM_COLORS][7] = {
+ [THEME_GUATTARI] = {
+ "d4d4d4", // none
+ "d4d4d4", // text
+ "e8e4d0", // hover
+ "a090a0", // link
+ "a090a0", // nav
+ "d4d4d4", // title
+ "707070", // dim
+ "707070", // fade
+ "6b6770", // head
+ "b0b0b0", // foot
+ "a090a0", // spot
+ "222222", // back nav
+ "19171a", // back far
+ "17151a", // back fade
+ "0a0a0a", // back near
+ },
+ [THEME_MONERO] = {
+ "e0e0e0", // none
+ "e0e0e0", // text
+ "ffe0a0", // hover
+ "909090", // link
+ "e07010", // nav
+ "c0b0a0", // title
+ "878067", // dim
+ "877757", // fade
+ "877757", // head
+ "878067", // foot
+ "878067", // spot
+ "222222", // back nav
+ "201710", // back far
+ "2c2007", // back fade
+ "000000", // back near
+ },
+ [THEME_BLACK] = {
+ "ffffff", // none
+ "ffffff", // text
+ "ffffff", // hover
+ "b0b0b0", // link
+ "b0b0b0", // nav
+ "ffffff", // title
+ "808080", // dim
+ "ffffff", // fade
+ "ffffff", // head
+ "ffffff", // foot
+ "ffffff", // spot
+ "000000", // back nav
+ "000000", // back far
+ "000000", // back fade
+ "000000", // back near
+ },
+ [THEME_WHITE] = {
+ "000000", // none
+ "000000", // text
+ "000000", // hover
+ "404040", // link
+ "404040", // nav
+ "000000", // title
+ "808080", // dim
+ "000000", // fade
+ "000000", // head
+ "000000", // foot
+ "000000", // spot
+ "ffffff", // back nav
+ "ffffff", // back far
+ "ffffff", // back fade
+ "ffffff", // back near
+ },
+ [THEME_PINK] = {
+ "d4d4d4", // none
+ "d4d4d4", // text
+ "f0a0f0", // hover
+ "909090", // link
+ "d040d0", // nav
+ "b0b0b0", // title
+ "707070", // dim
+ "707070", // fade
+ "707070", // head
+ "6b6770", // foot
+ "d040d0", // spot
+ "222222", // back nav
+ "0a0a0a", // back far
+ "301030", // back fade
+ "19171a", // back near
+ },
+ [THEME_LIGHT] = {
+ "040404", // none
+ "040404", // text
+ "0a0a0a", // hover
+ "474747", // link
+ "305040", // nav
+ "101010", // title
+ "808080", // dim
+ "707070", // fade
+ "707070", // head
+ "707070", // foot
+ "305040", // spot
+ "d0e0e0", // back nav
+ "c0c0c0", // back far
+ "d0e0e0", // back fade
+ "f7f7f7", // back near
+ },
+};
+
+#define COLOR_MAP_SIZE ((i32) (sizeof COLOR_MAP / sizeof *COLOR_MAP))
+
+static i32 COLOR_MAP[] = {
+ [COLOR_NONE] = COLOR_FOREGROUND_TEXT,
+ [COLOR_FOREGROUND_TEXT] = COLOR_FOREGROUND_TEXT,
+ [COLOR_FOREGROUND_HOVER] = COLOR_FOREGROUND_HOVER,
+ [COLOR_FOREGROUND_LINK] = COLOR_FOREGROUND_LINK,
+ [COLOR_FOREGROUND_NAV] = COLOR_FOREGROUND_NAV,
+ [COLOR_FOREGROUND_TITLE] = COLOR_FOREGROUND_TITLE,
+ [COLOR_FOREGROUND_DIM] = COLOR_FOREGROUND_DIM,
+ [COLOR_FOREGROUND_FADE] = COLOR_FOREGROUND_FADE,
+ [COLOR_FOREGROUND_HEAD] = COLOR_FOREGROUND_HEAD,
+ [COLOR_FOREGROUND_FOOT] = COLOR_FOREGROUND_FOOT,
+ [COLOR_FOREGROUND_SPOT] = COLOR_FOREGROUND_SPOT,
+ [COLOR_BACKGROUND_NAV] = COLOR_BACKGROUND_NAV,
+ [COLOR_BACKGROUND_FAR] = COLOR_BACKGROUND_FAR,
+ [COLOR_BACKGROUND_FADE] = COLOR_BACKGROUND_FADE,
+ [COLOR_BACKGROUND_NEAR] = COLOR_BACKGROUND_NEAR,
+ [COLOR_RAINBOW_LIGHT] = COLOR_RAINBOW_LIGHT,
+ [COLOR_RAINBOW_DARK] = COLOR_RAINBOW_DARK,
+};
+
+static i32 SCALE = DEFAULT_SCALE;
+static i32 WIDTH = DEFAULT_WIDTH;
+
+static Redirect redirects[] = {
+ {
+ .alias = "/contact",
+ .url = "https://t.me/mitryz",
+ }, {
+ .alias = "/github",
+ .url = "https://github.com/automainint",
+ }, {
+ .alias = "/gitlab",
+ .url = "https://gitlab.com/automainint",
+ }, {
+ .alias = "/manifold",
+ .url = "https://manifold.markets/deleuze",
+ }, {
+ .alias = "/youtube",
+ .url = "https://youtube.com/@MityaSelivanov",
+ }, {
+ .alias = "/odysee",
+ .url = "https://odysee.com/@angsthotep:d",
+ }, {
+ .alias = "/twitch",
+ .url = "https://twitch.tv/angsthotep",
+ }, {
+ .alias = "/soundcloud",
+ .url = "https://soundcloud.com/angsthotep",
+ }, {
+ .alias = "/bandcamp",
+ .url = "https://angsthotep.bandcamp.com/",
+ }, {
+ .alias = "/spotify",
+ .url = "https://open.spotify.com/artist/0rr6un5mTZBZSDymK7QDWb",
+ }, {
+ .alias = "/telegram",
+ .url = "https://t.me/eterna",
+ }, {
+ .alias = "/discord",
+ .url = "https://discord.gg/jstDS8x",
+ }, {
+ .alias = "/twitter",
+ .url = "https://twitter.com/angsthotep",
+ }, {
+ .alias = "/linkedin",
+ .url = "https://linkedin.com/in/guattari",
+ }, {
+ .alias = "/instagram",
+ .url = "https://instagram.com/anotherdeleuze",
+ }, {
+ .alias = "/facebook",
+ .url = "https://facebook.com/eternaforgeso",
+ }, {
+ .alias = "/reddit",
+ .url = "https://reddit.com/u/angsthotep",
+ }, {
+ .alias = "/simplexchat",
+ .url =
+ "https://simplex.chat/contact/#/"
+ "?v=1-4&smp=smp%3A%2F%"
+ "2Fhejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA%3D%"
+ "40smp16.simplex.im%2F8r8DH3EY7nAy1iS3LONVU9Wjc3TiVHxT%"
+ "23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAc_"
+ "Do6A3LfIKd49my6sv324pikcN3y9pFRQLzFG3rA1U%253D%26srv%"
+ "3Dp3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6"
+ "qd.onion",
+ },
+};
+
+static Location locs[] = {
+ {
+ .method = "GET",
+ .uri = "/",
+ .index = DOC_MAIN,
+ }, {
+ .method = "GET",
+ .uri = "/about",
+ .index = DOC_ABOUT,
+ }, {
+ .method = "GET",
+ .uri = "/music",
+ .index = DOC_MUSIC,
+ }, {
+ .method = "GET",
+ .uri = "/projects",
+ .index = DOC_PROJECTS,
+ }, {
+ .method = "GET",
+ .uri = "/blog",
+ .index = DOC_BLOG,
+ }, {
+ .method = "GET",
+ .uri = "/pgp",
+ .index = DOC_PGP,
+ }, {
+ .method = "GET",
+ .uri = "/links",
+ .index = DOC_LINKS,
+ }, {
+ .method = "GET",
+ .uri = "/theme",
+ .index = DOC_THEME,
+ }, {
+ .method = "GET",
+ .uri = "/links/monero",
+ .index = DOC_LINKS_MONERO,
+ }, {
+ .method = "GET",
+ .uri = "/links/bitcoin",
+ .index = DOC_LINKS_BITCOIN,
+ }, {
+ .method = "GET",
+ .uri = "/saw",
+ .index = DOC_SAW,
+ }
+};
+
+static Font fonts[] = {
+ {
+ .family = "Lithos Pro",
+ .file = "lithos_pro_black.woff2",
+ .weight = "900",
+ .style = "normal",
+ }, {
+ .family = "Verdana",
+ .file = "verdana.woff2",
+ .weight = "normal",
+ .style = "normal",
+ }, {
+ .family = "Verdana",
+ .file = "verdana_bold.woff2",
+ .weight = "bold",
+ .style = "normal",
+ }, {
+ .family = "Verdana",
+ .file = "verdana_italic.woff2",
+ .weight = "normal",
+ .style = "italic",
+ }, {
+ .family = "Segoe UI",
+ .file = "segoe_ui.woff2",
+ .weight = "normal",
+ .style = "normal",
+ }, {
+ .family = "Segoe UI",
+ .file = "segoe_ui_bold.woff2",
+ .weight = "bold",
+ .style = "normal",
+ }, {
+ .family = "Segoe UI",
+ .file = "segoe_ui_italic.woff2",
+ .weight = "normal",
+ .style = "italic",
+ }, {
+ .family = "Icon",
+ .file = "fa_regular_400.woff2",
+ .weight = "normal",
+ .style = "normal",
+ }, {
+ .family = "Icon Solid",
+ .file = "fa_solid_900.woff2",
+ .weight = "normal",
+ .style = "normal",
+ }, {
+ .family = "Brand",
+ .file = "fa_brands_400.woff2",
+ .weight = "normal",
+ .style = "normal",
+ },
+};
+
+static Link navigation_left[] = {
+ {
+ .text = "Main",
+ .href = "/",
+ .target = "_self",
+ }, {
+ .text = "About Me",
+ .href = "/about",
+ .target = "_self",
+ }, {
+ .text = "Music",
+ .href = "/music",
+ .target = "_self",
+ }, {
+ .text = "Projects",
+ .href = "/projects",
+ .target = "_self",
+ }, {
+ .text = "Blog",
+ .href = "/blog",
+ .target = "_self",
+ }, {
+ .text = "Git",
+ .href = "/git",
+ .target = "_blank",
+ },
+};
+
+static Link navigation_right[] = {
+ {
+ .text = "<span style='font-family:\"Icon Solid\";'>&#63024;</span> PGP",
+ .href = "/pgp",
+ .target = "_self",
+ }, {
+ .text = "Links",
+ .href = "/links",
+ .target = "_self",
+ },
+};
+
+static Doc docs[NUM_DOCS] = {
+ [DOC_404] = {
+ .id = "404",
+ .title = "Page Not Found",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .text = "404",
+ }, {
+ .type = TAG_END,
+ .text = "Page Not Found",
+ },
+ },
+ },
+ [DOC_MAIN] = {
+ .id = NULL,
+ .title = "Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_BLOCK,
+ .layout = LAYOUT_CENTER,
+ .padding_top = 40,
+ .scale = 100,
+ .href = "/2024_shadows.jpg",
+ .image = "/2024_shadows_small.jpg",
+ }, {
+ .type = TAG_SUBTITLE,
+ .layout = LAYOUT_CENTER,
+ .scale = 150,
+ .text = "New album - coming soon...",
+ }, {
+ .type = TAG_BLOCK,
+ .padding_top = 80,
+ .text = "Monero accepted here!",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .scale = 34,
+ .href = "https://monerica.com",
+ .image = "/MCEP.svg",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }
+ },
+ },
+ [DOC_ABOUT] = {
+ .id = "about",
+ .title = "About Me - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .layout = LAYOUT_CENTER,
+ .text = "Dungeonstep",
+ }, {
+ .text = " Hello! My name is Mitya Selivanov, aka Dmitry Babushkin. <br>"
+ " I&#39;m a ",
+ }, {
+ .type = TAG_BLACK_FLAG,
+ .text = "&nbsp;freelance computer programmer and musician. "
+ " Inspired to build decentralization and cooperation technologies. <br>",
+ }, {
+ .layout = LAYOUT_RIGHT,
+ .image = "/pepe.gif",
+ }, {
+ .type = TAG_CAPTION,
+ .layout = LAYOUT_CENTER,
+ .text = "Interested in",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#62786; ",
+ }, {
+ .text = "Semantic compression (see ",
+ }, {
+ .text = "Casey&#39;s post",
+ .href = "https://caseymuratori.com/blog_0015",
+ }, {
+ .text = ") <br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#62171; ",
+ }, {
+ .text = "Low-level programming <br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#61549; ",
+ }, {
+ .text = "Performance engineering <br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#61671; ",
+ }, {
+ .text = "Prediction markets (see ",
+ }, {
+ .text = "Scott Alexander&#39;s FAQ",
+ .href = "https://www.astralcodexten.com/p/prediction-market-faq",
+ }, {
+ .text = ") <br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#61977; ",
+ }, {
+ .text = "Blockchain &amp; ",
+ }, {
+ .nowrap = 1,
+ .text = "Proof-of-Work",
+ }, {
+ .text = " <br><br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#61742; ",
+ }, {
+ .text = "Puzzle games <br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#61441; ",
+ }, {
+ .text = "Music synthesis <br>",
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .width = 140,
+ .icon = ICON_SOLID,
+ .text = "&#62038; ",
+ }, {
+ .text = "Meeting new people <br>",
+ }, {
+ .type = TAG_CAPTION,
+ .layout = LAYOUT_CENTER,
+ .text = "I use",
+ }, {
+ .width = 120,
+ .icon = ICON_SOLID,
+ .text = "&#61444; ",
+ }, {
+ .type = TAG_CODE,
+ .text = "C"
+ }, {
+ .text = ", ",
+ }, {
+ .width = 120,
+ .icon = ICON_SOLID,
+ .text = "&#58745; ",
+ }, {
+ .type = TAG_CODE,
+ .text = "snake_case",
+ }, {
+ .text = ", ",
+ }, {
+ .width = 120,
+ .icon = ICON_SOLID,
+ .text = "&#61788; ",
+ }, {
+ .type = TAG_CODE,
+ .text = "helix",
+ .href = "https://helix-editor.com/",
+ }, {
+ .text = ", ",
+ }, {
+ .width = 120,
+ .icon = ICON_BRAND,
+ .text = "&#61820; ",
+ }, {
+ .type = TAG_CODE,
+ .text = "Linux",
+ }, {
+ .text = "<br> ",
+ }, {
+ .type = TAG_CODE,
+ .text = "Alacritty",
+ .href = "https://github.com/alacritty/alacritty",
+ }, {
+ .text = ", WebAssembly, OpenGL, Vulkan <br>",
+ }, {
+ .type = TAG_CODE,
+ .text = "nginx",
+ .href = "https://hg.nginx.org/nginx",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "wolfSSL",
+ .href = "https://github.com/wolfSSL/wolfssl",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "stb",
+ .href = "https://github.com/nothings/stb",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "sokol",
+ .href = "https://github.com/floooh/sokol",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "nanovg",
+ .href = "https://github.com/memononen/nanovg",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "miniaudio",
+ .href = "https://github.com/mackron/miniaudio",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "gf",
+ .href = "https://github.com/nakst/gf",
+ }, {
+ .text = " <br>",
+ }, {
+ .type = TAG_CAPTION,
+ .layout = LAYOUT_CENTER,
+ .text = "My music alias",
+ }, {
+ .nowrap = 1,
+ .text = "Guattari &ndash; electronic",
+ }, {
+ .type = TAG_END,
+ .text = " music, mostly dungeon synth, witch house; and black metal. "
+ " Feel free to use my music in your projects. "
+ " All music is licensed under a Creative Commons License (Attribution). "
+ " Contact me if you need additional information. ",
+ },
+ },
+ },
+ [DOC_MUSIC] = {
+ .id = "music",
+ .title = "Music - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_BLOCK,
+ .layout = LAYOUT_CENTER,
+ .padding_top = 40,
+ .scale = 100,
+ .href = "/2021_accelerationism.jpg",
+ .image = "/2021_accelerationism_small.jpg",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_FADE,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 80,
+ .padding_top = -110,
+ .text = "2021",
+ }, {
+ .type = TAG_SUBTITLE,
+ .layout = LAYOUT_CENTER,
+ .text = "Guattari &ndash; Accelerationism",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .scale = 80,
+ .text = "Apple / iTunes",
+ .href = "https://music.apple.com/us/album/accelerationism-ep/1548020164",
+ }, {
+ .type = TAG_CODE,
+ .text = " | ",
+ }, {
+ .scale = 80,
+ .text = "Spotify",
+ .href = "https://open.spotify.com/album/5N6tZYohr2mrmr0XelAwlK?si=RzcBdOAKRWKozDYJ91iREA",
+ }, {
+ .type = TAG_CODE,
+ .text = " | ",
+ }, {
+ .scale = 80,
+ .text = "Deezer",
+ .href = "https://deezer.page.link/PXS8q1jEqECa79rEA",
+ }, {
+ .type = TAG_END,
+ }, {
+ .line_height = 40,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;1.",
+ }, {
+ .text = "Willing Core",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/FwFcJqiRFh8",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2021_accelerationism/01_willing_core.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;2.",
+ }, {
+ .text = "Desert Cult",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/MxkNiq6JnfE",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2021_accelerationism/02_desert_cult.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;3.",
+ }, {
+ .text = "Artificial Womb",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/hTeYtI4ECJ4",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2021_accelerationism/03_artificial_womb.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;4.",
+ }, {
+ .text = "Phenotypic Revolution",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/FxDsTCq5LvE",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2021_accelerationism/04_phenotypic_revolution.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;5.",
+ }, {
+ .text = "Above The Sky",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/K4BIiTCNvy4",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2021_accelerationism/05_above_the_sky.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;6.",
+ }, {
+ .text = "Elimination",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/OiTB5KRBuI0",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2021_accelerationism/06_elimination.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BLOCK,
+ .layout = LAYOUT_CENTER,
+ .padding_top = 40,
+ .scale = 100,
+ .href = "/2020_unalbum.jpg",
+ .image = "/2020_unalbum_small.jpg",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_FADE,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 80,
+ .padding_top = -110,
+ .text = "2020",
+ }, {
+ .type = TAG_SUBTITLE,
+ .layout = LAYOUT_CENTER,
+ .text = "Guattari &ndash; Anti-Album",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .scale = 80,
+ .text = "YouTube",
+ .href = "https://www.youtube.com/playlist?list=PLcgDcJaNt0a-wSyZdGARyF3OMAqOQY36J",
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;1.",
+ }, {
+ .text = "Casual Track 018",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/Uhveh_67zyQ",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/01_casual_track_018.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;2.",
+ }, {
+ .text = "Casual Track 019",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/JEQf2cvjOko",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/02_casual_track_019.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;3.",
+ }, {
+ .text = "Casual Track 020",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/FYu2_kSUnMc",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/03_casual_track_020.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;4.",
+ }, {
+ .text = "Casual Track 021",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/2oP4CfHWFqE",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/04_casual_track_021.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;5.",
+ }, {
+ .text = "Casual Track 022",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/-WDp3EWTjrc",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/05_casual_track_022.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;6.",
+ }, {
+ .text = "Casual Track 023",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/pKuf9PkWhko",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/06_casual_track_023.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;7.",
+ }, {
+ .text = "Casual Track 024",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/XwE493hj4z4",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/07_casual_track_024.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;8.",
+ }, {
+ .text = "Casual Track 025",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/XKCEUS7yzFU",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/08_casual_track_025.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;9.",
+ }, {
+ .text = "Casual Track 026",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/-MbUQp3hbqM",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2020_unalbum/09_casual_track_026.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BLOCK,
+ .layout = LAYOUT_CENTER,
+ .padding_top = 40,
+ .scale = 100,
+ .href = "/2016_determine.jpg",
+ .image = "/2016_determine_small.jpg",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_FADE,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 80,
+ .padding_top = -110,
+ .text = "2016",
+ }, {
+ .type = TAG_SUBTITLE,
+ .layout = LAYOUT_CENTER,
+ .text = "Guattari &ndash; Determine",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .scale = 80,
+ .text = "YouTube",
+ .href = "https://youtu.be/GfUYhgaBzik",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;1.",
+ }, {
+ .text = "Introduction",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/Rwyd4idcR2A",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/01_introduction.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;2.",
+ }, {
+ .text = "Fugue in Eb minor",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/VgZ8jjuzLow",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/02_fugue_in_eb_minor.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;3.",
+ }, {
+ .text = "Lonely Harp",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/IufFOFv9vwA",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/03_lonely_harp.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;4.",
+ }, {
+ .type = TAG_CODE,
+ .text = ".SILENT",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/docPTZi6EOI",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/04_dot_silent.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;5.",
+ }, {
+ .text = "Interlude",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/QnzE7Knzx8Q",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/05_interlude.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;6.",
+ }, {
+ .text = "96 cycles of C minor",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/QgU65MvM50M",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/06_96_cycles_of_c_minor.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;7.",
+ }, {
+ .text = "Vast",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/NLNJZCMMbVY",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/07_vast.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .background = COLOR_BACKGROUND_FADE,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;8.",
+ }, {
+ .text = "Machinery",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/DWzFyCOwGaI",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/08_machinery.mp3",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .line_height = 160,
+ }, {
+ .width = 180,
+ .unselectable = 1,
+ .text = "&nbsp;9.",
+ }, {
+ .text = "Epilogue",
+ }, {
+ .width = 100,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .icon = ICON_BRAND,
+ .line_height = 80,
+ .padding_top = 55,
+ .text = "&#62513;",
+ .href = "https://youtu.be/38DaJiwZvtM",
+ }, {
+ .width = 240,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .text = "mp3",
+ .href = "/git/music/plain/2016_determine/09_epilogue.mp3",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ },
+ },
+ },
+ [DOC_PROJECTS] = {
+ .id = "projects",
+ .title = "Projects - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .layout = LAYOUT_CENTER,
+ .text = "Saw",
+ }, {
+ .text = "A new experimental music sequencer and audio editor. <br><br>"
+ "There is a lot of things I don't like in currently available music software. "
+ "Some of them are: bad performance, instability (bugs and crashes), complicated system environment requirements, inconvenient UI, lack of features, closedness, and, most important, bad standards. "
+ "I decided to make a new music sequencer from scratch, so I can experiment with it and find better ways to make music. <br><br>"
+ "Code is cross-platform and written in C. <br><br>",
+ }, {
+ .text = "Used libraries: ",
+ }, {
+ .type = TAG_CODE,
+ .text = "kit",
+ .href = "/git/kit",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "stb",
+ .href = "https://github.com/nothings/stb",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "miniaudio",
+ .href = "https://github.com/mackron/miniaudio",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "nanovg",
+ .href = "https://github.com/memononen/nanovg",
+ }, {
+ .text = ", ",
+ }, {
+ .type = TAG_CODE,
+ .text = "sokol",
+ .href = "https://github.com/floooh/sokol",
+ }, {
+ .text = ". <br><br>",
+ }, {
+ .width = 120,
+ .icon = ICON_SOLID,
+ .text = "&#61459; ",
+ }, {
+ .text = "Source code",
+ .href = "/git/saw",
+ }, {
+ .text = " - Git repository <br>",
+ }, {
+ .width = 120,
+ .icon = ICON_SOLID,
+ .text = "&#61671; ",
+ }, {
+ .text = "Try out",
+ .href = "/saw",
+ }, {
+ .text = " - run the prototype in the browser <br>",
+ }, {
+ .width = 120,
+ .icon = ICON_SOLID,
+ .text = "&#62038; ",
+ }, {
+ .text = "Project page",
+ .href = "https://handmade.network/p/432/saw",
+ }, {
+ .type = TAG_END,
+ .text = " on Handmade Network",
+ },
+ },
+ },
+ [DOC_BLOG] = {
+ .id = "blog",
+ .title = "Blog - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .color = COLOR_RAINBOW_LIGHT,
+ .text = "Work in progress...",
+ }, {
+ .type = TAG_END,
+ },
+ },
+ },
+ [DOC_PGP] = {
+ .id = "pgp",
+ .title = "PGP - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .text = "Authenticity",
+ }, {
+ .text = "I use the corresponding private key to sign all my commits:<br><br>",
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/guattari_tech.asc",
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#61572; ",
+ }, {
+ .text = "Public key",
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = " ",
+ }, {
+ .type = TAG_CODE,
+ .text = "52D474B70D8FD302",
+ }, {
+ .text = "<br><br>",
+ }, {
+ .type = TAG_CODE,
+ .color = COLOR_FOREGROUND_DIM,
+ .line_height = 95,
+ .text = "-----BEGIN PGP PUBLIC KEY BLOCK-----<br>"
+ "<br>"
+ "mQINBGMquWABEAC6kQRcWblxfTGgJgXo4E7rbjOr0kQHpV3aygoclJM4jPRjQHBh<br>"
+ "N9R/OktTm7D+yMMP/4cFXkIjjabR8ZhbUwW9k1I44cGilGiViSA3sNfcILrvdMVU<br>"
+ "oQzlvl3Wy82Fv6b24hufsNGIbvru+FIDg5ZATMjX1Ux3CYO5u8KwE0iTzZ/3Dyis<br>"
+ "1aiGMTZ9VSQwPD3ekI/KyRauS9ZhwPQKK69QjcUzRE3owWdaWrPo95Z1jEgejRib<br>"
+ "vYtHHGqml4KRQLStA9AtQ/CtTqq2C1yh6t+ngmR+wWrKzz5gVdX/PfLs+OBiLjx2<br>"
+ "27g9eR420+Tmt42Q+CZpJ+VnnVk27m9Lnze4L4k1vl4dAJZoa2f/X15lNu1oXil+<br>"
+ "qdTSzLlRg1PBpTfHizNO/VJu8nteMPgHVb4Ag9OVO5hZ+bxcVtIPVEbe6MoHMsCL<br>"
+ "lxn4JdzHlgnRBxloYnQqIrqx1js9XGdEzXXfXHy1oNzhnxtJMAwDgVx+D2lLFQVz<br>"
+ "cU7FNb3CSL5Fn9zrEoxD70VqTB5PJVelrADpJ0ROqow0bhLsUXdcJfDSHAbkbQ97<br>"
+ "6BunaAyHtkORV0a3rr+XRG8xa9SpOF9JgyP0K52hnC0CGv2aWtfTJewtfQRl4zYB<br>"
+ "o5KA2z2xvddpAOgmkRa9tPWNK8kEjukAVg2l6ngNwg/EOoFOUPNJEL4E7wARAQAB<br>"
+ "tCtNaXR5YSBTZWxpdmFub3YgPGF1dG9tYWluaW50QGd1YXR0YXJpLnRlY2g+iQJO<br>"
+ "BBMBCgA4FiEE2byBPMDrN6gtw8qnUtR0tw2P0wIFAmMquWACGwMFCwkIBwIGFQoJ<br>"
+ "CAsCBBYCAwECHgECF4AACgkQUtR0tw2P0wLcVA/8DYgBfr2qWIV/6CHD1G1+ipHJ<br>"
+ "+iMyVEzx/dY7pVFFysNecE+Esr4Ns9lrufuSOIPS+EX6mR9GBjie1WJuvVG3xop7<br>"
+ "PhMx90Fr+ZOOVB/Y18K8Xae4ZT9BdYkE1bnGjBgSv8GACSzGyZsKPuP8h5DVFH9w<br>"
+ "PAJW0/0u/Tcd78M1loMUw8HF+g0f4uLi78yWpCIaxfrEu/shVcbload5K4VOfxOJ<br>"
+ "Zjjii6rNiRyfYtAqK0ZMcNE0sviMQZUx20S/wbAm2SQR8DYTlJZbsRXuk4PRCzkP<br>"
+ "92NDUiPwfR2MvlOf3UgH402ashamOT1EAUv4aXAnHxyYVni5AVMmX46ANBTvn0Uc<br>"
+ "oB1ktmz2v4/xDoyWd4F9ojBl2+1Fp6MQsJp/YDlDt7EL0SAXQOF4uXlCuKv6fZAm<br>"
+ "dOByhwMC9Ulq3F3HKxEcG8/VWxtnJPNFHn0F+aJEvCzHqtmCRRZRFB4zKlwebKck<br>"
+ "GvqAocW+Oq/c/SDQlz9lAHy/XM4kttR8eXs0T3frsoiphpx5tgk0EdG0NP/hHs+S<br>"
+ "KHPczqPoZ0wkoycSL+qtwbONYzD6z9GszC4oncodkCSRlKSgpvYWmYho4ag17bMI<br>"
+ "Ebhqx7fzkHj83y4hgMWLVlJML0tuno5pBcWqNQ+4oylIpeRdwBUmQrnE5+OaDIcs<br>"
+ "7+upq7PvGULuxpRGtkC5Ag0EYyq5YAEQALmvs8MQ/wPTWM9JkPKdIvMC4JErW86j<br>"
+ "ZGxNdxZHFVibMvwPrCMbD3zDwEv8/xKDD6UM+p50phiSem2APDJAQET8BM+yOoaS<br>"
+ "xquV0XCqbQxdSt50ZeY17+tMtDevBwKdqtmGtZSQUvALH7t7QgkmogMtcLUH7PI/<br>"
+ "ROyaC9fqUY7h5RNgnD22hE9qgUQbAv3Ylo4LM0KeJoT7BpWt9qDm7TMuIkJIARJq<br>"
+ "17teaJYjp34B4aWXA9yJTW/q9q4gsElgMel5HiV1sOXHOnCxPtfDah/HdlinMG/p<br>"
+ "wdaELC2WreBhdxycsFMHBtNDbXyHzrPF3uh0CwqGcAe8u+SribGpF16vfvCRReMP<br>"
+ "I8Rb5JhR3hVkKgcmMYPgvGwM2GQmWhCLRiPwQBVm0hiV0Dvdw0O39didpI3VzF5m<br>"
+ "zPnvC0Jn204WbWyfPZTV//2NrjQ7NSjJza2bmsStDa0pLxC7RtkfLC+hsWbRFtxA<br>"
+ "JBGZ2vzpdTK2Qe5dP/X8863MrqbAtP85n1mqTCXIO8/DR6gGjzoTZH1Q3s51T4nn<br>"
+ "6A0VfjROFdI4lRlCEuAuq4CovK1IimgECjumAOz6Wc0xkT4BlyJe5rYGUuxyhQAv<br>"
+ "gChxUY0Hfu2ClhaJoQfotPsKl17NuLKZjwyUyKdv42lgvt81wE465Mqd4kmpGHse<br>"
+ "ecuCng8xfBPvABEBAAGJAjYEGAEKACAWIQTZvIE8wOs3qC3DyqdS1HS3DY/TAgUC<br>"
+ "Yyq5YAIbDAAKCRBS1HS3DY/TAqHUEACzNsshshlgCvdtzr4R1wW5PzPzyzJh5aKv<br>"
+ "oFKQsT8Nok6qSt+Ft4teK0m9BqQvxSKvCnyLC98GAIuQk1CP97156mM9Z2k8w67O<br>"
+ "TsHPDO7KFdwN0+mfLD6u+qt1qm8a3GQI/SAqpgv6uiA7sJibwsDyfoMRwRAxU42k<br>"
+ "dZJwZzzMeWqCg0iesonS8272vCKVqhnCgH6/9VUYk3tzNW2wLW6bB5vEYIfs1w/B<br>"
+ "hrMZPG9mEcG+DG7IAFeM6jf7zpU8gxNb/CnWkYpdKPafn83oX1i7qiZ/3R/YbqsV<br>"
+ "6+sKNcSFDE7KSFflmPGudlC0BuWIaNvCfIXSNurhGBm5eibjxsR5DQ+MnMGzNAVy<br>"
+ "BxXjFeJMrKEPz+ZLAYqCYCNft94i3vQNMpWiwpzytzxRldTA+B3PIA/qSwtmY57f<br>"
+ "WYmojDm4GJCcOAY923zZselGlm1EY6fibM0RKj6fY3sLDQheg1Us+zn9tsCczMk+<br>"
+ "19K6fgS286Ni9fpe5emhmveePCwPRwGhXaDFzZkm4Ir2KW4erZAWlZaaso/LT1XR<br>"
+ "8vukISTIUJzwy4+Tk36W0ibhS7Vt+tPUOJVlBYbiXE8Vd4R9x2FQp/5wgRHMmcXu<br>"
+ "UAoG9LPzHaoPfGK65awP787kFA+NUz39NVvaIw1f8qs9HPVkFoAhLOpEQ4yQ+gS8<br>"
+ "c+F4YwNAaA==<br>"
+ "=YwR7<br>"
+ "-----END PGP PUBLIC KEY BLOCK-----<br>",
+ }, {
+ .type = TAG_END,
+ },
+ },
+ },
+ [DOC_LINKS] = {
+ .id = "links",
+ .title = "Links - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_LEFT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "For tip",
+ }, {
+ .text = "<br>",
+ }, {
+ .layout = LAYOUT_RIGHT,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_LEFT,
+ }, {
+ .action = ACTION_SELF,
+ .href = "/links/monero",
+ .text = "Monero",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_LEFT,
+ }, {
+ .action = ACTION_SELF,
+ .href = "/links/bitcoin",
+ .text = "Bitcoin",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "Mirrors",
+ }, {
+ .text = "<br><br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .nowrap = 1,
+ .href = "https://guattari.tech/links",
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#61612;",
+ }, {
+ .text = "https://guattari.tech",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .nowrap = 1,
+ .href = "http://guattari22ojifbuvudvd4ceurbcqfvfkxjlul5ax6pyquhg4c3chbyd.onion/links",
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62724;",
+ }, {
+ .text = "http://guattari22...onion",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_LEFT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "Code",
+ }, {
+ .text = "<br><br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/github",
+ }, {
+ .text = "GitHub",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/gitlab",
+ }, {
+ .text = "GitLab",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 60,
+ .unselectable = 1,
+ .text = "Prediction<br>markets",
+ }, {
+ .text = "<br><br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/manifold",
+ }, {
+ .width = 600,
+ .text = "Manifold",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_LEFT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "Video",
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/youtube",
+ }, {
+ .text = "YouTube",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/odysee",
+ }, {
+ .text = "Odysee",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/twitch",
+ }, {
+ .text = "Twitch",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "Audio",
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/soundcloud",
+ }, {
+ .text = "SoundCloud",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/bandcamp",
+ }, {
+ .text = "Bandcamp",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/spotify",
+ }, {
+ .text = "Spotify",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_LEFT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "Chat",
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/simplexchat",
+ }, {
+ .text = "SimpleX Chat",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/telegram",
+ }, {
+ .text = "Telegram",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/discord",
+ }, {
+ .text = "Discord",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_CAPTION,
+ .color = COLOR_FOREGROUND_DIM,
+ .layout = LAYOUT_MARGIN_RIGHT,
+ .scale = 70,
+ .unselectable = 1,
+ .text = "Social",
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/twitter",
+ }, {
+ .text = "Twitter",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/linkedin",
+ }, {
+ .text = "Linkedin",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_CENTER,
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_BLANK,
+ .href = "/reddit",
+ }, {
+ .text = "Reddit",
+ }, {
+ .type = TAG_END,
+ }, {
+ .type = TAG_END,
+ }, {
+ .text = "<br>",
+ }, {
+ .type = TAG_END,
+ },
+ },
+ },
+ [DOC_THEME] = {
+ .id = "theme",
+ .title = "Theme - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_CAPTION,
+ .color = COLOR_RAINBOW_LIGHT,
+ .layout = LAYOUT_CENTER,
+ .text = "Theme Setup",
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?guattari"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Guattari",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .action = ACTION_SELF,
+ .href = "/theme?wide"
+ }, {
+ .text = "Wide",
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&nbsp;&#xe209;",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?monero"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Monero",
+ }, {
+ .type = TAG_END,
+ .text = "<br>",
+ }, {
+ .type = TAG_BEGIN,
+ .layout = LAYOUT_FLOAT_RIGHT,
+ .action = ACTION_SELF,
+ .href = "/theme?narrow"
+ }, {
+ .text = "Narrow",
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&nbsp;&#xf3fb;",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?black"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Black",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?white"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "White",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?pink"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Pink",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?light"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Light",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?random"
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Random",
+ }, {
+ .type = TAG_END,
+ .text = "<br>"
+ }, {
+ .type = TAG_BEGIN,
+ .action = ACTION_SELF,
+ .href = "/theme?madness"
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .icon = ICON_SOLID,
+ .width = 140,
+ .text = "&#62783; ",
+ }, {
+ .text = "Madness",
+ }, {
+ .type = TAG_END,
+ .text = "<br><br>"
+ }, {
+ .type = TAG_SUBMIT_TEXT,
+ .text = INSERT_THEME,
+ }, {
+ .text = "<br>"
+ }, {
+ .icon = ICON_SOLID,
+ .width = 140,
+ .padding_top = 30,
+ .text = "&#62890;"
+ }, {
+ .action = ACTION_SUBMIT,
+ .text = "Apply",
+ .href = "/theme",
+ }, {
+ .type = TAG_CAPTION,
+ .layout = LAYOUT_CENTER,
+ .text = "Preview",
+ }, {
+ .color = COLOR_NONE,
+ .text = "DEFAULT <br>"
+ }, {
+ .color = COLOR_FOREGROUND_TEXT,
+ .text = "FOREGROUND TEXT <br>"
+ }, {
+ .color = COLOR_FOREGROUND_HOVER,
+ .text = "FOREGROUND HOVER <br>"
+ }, {
+ .color = COLOR_FOREGROUND_LINK,
+ .text = "FOREGROUND LINK <br>"
+ }, {
+ .color = COLOR_FOREGROUND_TITLE,
+ .text = "FOREGROUND TITLE <br>"
+ }, {
+ .color = COLOR_FOREGROUND_DIM,
+ .text = "FOREGROUND DIM <br>"
+ }, {
+ .color = COLOR_FOREGROUND_FADE,
+ .text = "FOREGROUND FADE <br>"
+ }, {
+ .color = COLOR_FOREGROUND_HEAD,
+ .text = "FOREGROUND HEAD <br>"
+ }, {
+ .color = COLOR_FOREGROUND_FOOT,
+ .text = "FOREGROUND FOOT <br>"
+ }, {
+ .color = COLOR_FOREGROUND_SPOT,
+ .text = "FOREGROUND SPOT <br>"
+ }, {
+ .background = COLOR_BACKGROUND_NAV,
+ .text = "BACKGROUND NAV <br>"
+ }, {
+ .background = COLOR_BACKGROUND_FAR,
+ .text = "BACKGROUND FAR <br>"
+ }, {
+ .background = COLOR_BACKGROUND_FADE,
+ .text = "BACKGROUND FADE <br>"
+ }, {
+ .background = COLOR_BACKGROUND_NEAR,
+ .text = "BACKGROUND NEAR <br>"
+ }, {
+ .color = COLOR_RAINBOW_LIGHT,
+ .text = "RAINBOW LIGHT <br>"
+ }, {
+ .type = TAG_END,
+ .color = COLOR_RAINBOW_DARK,
+ .text = "RAINBOW DARK"
+ },
+ },
+ },
+ [DOC_LINKS_MONERO] = {
+ .id = "monero",
+ .id_parent = "links",
+ .title = "Monero - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .text = "<br>Monero<br><br>"
+ }, {
+ .type = TAG_CODE,
+ .text = "8BuZuaXHH3d3hCRoAvyF9Z9tFS55A7B1mPDCCsdeMosV11DBvWb3XB54yE6mWwh35RcZe4RaaXnwiK5se3qn1d9H2X6Sfqd",
+ }, {
+ .type = TAG_END,
+ },
+ },
+ },
+ [DOC_LINKS_BITCOIN] = {
+ .id = "bitcoin",
+ .id_parent = "links",
+ .title = "Bitcoin - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .text = "<br>Bitcoin<br><br>"
+ }, {
+ .type = TAG_CODE,
+ .scale = 120,
+ .text = "bc1qhk0uezw98wxwweaeelwh8z0znk6jsl4qd5l8v7",
+ }, {
+ .type = TAG_END,
+ },
+ },
+ },
+ [DOC_SAW] = {
+ .id = "saw",
+ .title = "Saw - Guattari Tech",
+ .content = (Tag[]) {
+ {
+ .type = TAG_RAW,
+ .text = "<!DOCTYPE html>"
+ "<html style='height:100%;overflow:hidden;'>"
+ "<head>"
+ "<meta charset='utf-8'>"
+ "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>"
+ "<link rel='icon' type='image/gif' href='/favicon.gif' />"
+ "<title>Saw - Guattari Tech</title>"
+ "</head>"
+ "<body style='margin:0;height:100%;overflow:hidden;background-color:#505050;'>"
+ "<canvas style='margin:0;width:100%;height:100%;' id='canvas' oncontextmenu='event.preventDefault()'></canvas>"
+ "<script type='text/javascript'>"
+ "var Module = {"
+ "canvas: (function() { return document.getElementById('canvas'); })()"
+ "};"
+ "</script>"
+ "<script src='/saw/saw.js'></script>"
+ "</body>"
+ "</html>",
+ }
+ }
+ }
+};
+
+c8 const *headlines[] = {
+ "There is no need to fear or hope, but only to look for new weapons.",
+ "Handmade software in the age of LLMs.",
+ "Decentralization, engineering, and music.",
+ "Simplicity, reliability, and performance.",
+ "Accessibility, utility, and self custody.",
+ "Joy of programming.",
+ "Dark atmospheric music for Nomads.",
+ "A renaissance of the Pythagorean mindset.",
+};
+
+// ================================================================
+//
+// Procedures
+//
+// ================================================================
+
+c8 const *get_color(i32 index) {
+ static i64 rainbow_index = -1;
+ if (rainbow_index == -1)
+ rainbow_index = rand();
+
+ i32 n = index < 0 || index >= COLOR_MAP_SIZE ? COLOR_MAP[COLOR_NONE] : COLOR_MAP[index];
+ if (n == COLOR_NONE)
+ return COLORS[COLOR_FOREGROUND_TEXT];
+ if (n == COLOR_RAINBOW_LIGHT)
+ return RAINBOW_LIGHT[(rainbow_index++) % (sizeof RAINBOW_LIGHT / sizeof *RAINBOW_LIGHT)];
+ if (n == COLOR_RAINBOW_DARK)
+ return RAINBOW_DARK[(rainbow_index++) % (sizeof RAINBOW_DARK / sizeof *RAINBOW_DARK)];
+ if (n < 0 || n >= NUM_COLORS)
+ return COLORS[COLOR_NONE];
+ return COLORS[index];
+}
+
+i32 unpack_hex(c8 h) {
+ if (h >= '0' && h <= '9')
+ return h - '0';
+ if (h >= 'a' && h <= 'f')
+ return 10 + (h - 'a');
+ if (h >= 'A' && h <= 'F')
+ return 10 + (h - 'A');
+ return 0;
+}
+
+i32 color_brightness(c8 const *color) {
+ i32 rgb[3] = {
+ unpack_hex(color[0]) * 16 + unpack_hex(color[1]),
+ unpack_hex(color[2]) * 16 + unpack_hex(color[3]),
+ unpack_hex(color[4]) * 16 + unpack_hex(color[5]),
+ };
+ return rgb[0] + rgb[1] + rgb[2];
+}
+
+b8 is_dark_color(i32 index) {
+ i32 n =
+ index < 0 || index >= COLOR_MAP_SIZE
+ ? COLOR_MAP[COLOR_NONE]
+ : COLOR_MAP[index];
+
+ if (n == COLOR_RAINBOW_LIGHT)
+ return 0;
+ if (n == COLOR_RAINBOW_DARK)
+ return 1;
+
+ i32 bg = color_brightness(COLORS[COLOR_BACKGROUND_NEAR]);
+ if (n < 0 || n >= (i32)(sizeof COLORS / sizeof *COLORS))
+ return 127 * 3 < bg;
+
+ i32 fg = color_brightness(COLORS[n]);
+ return fg < bg;
+}
+
+void print_theme() {
+ printf("%d // scale&#10;", SCALE);
+ printf("%d // width&#10;&#10;", WIDTH);
+
+ for (i32 i = 0; i < COLOR_MAP_SIZE; ++i) {
+ if (COLOR_MAP[i] == COLOR_RAINBOW_LIGHT)
+ printf("rainbow_light");
+ else if (COLOR_MAP[i] == COLOR_RAINBOW_DARK)
+ printf("rainbow_dark");
+ else if (COLOR_MAP[i] == i) {
+ for (i32 j = 0; j < 6; ++j)
+ printf("%c", COLORS[i][j]);
+ } else {
+ printf("%d", COLOR_MAP[i]);
+ }
+ if (i == COLOR_NONE)
+ printf(" // default color");
+ if (i == COLOR_FOREGROUND_TEXT)
+ printf(" // foreground colors");
+ if (i == COLOR_BACKGROUND_NAV)
+ printf(" // background colors");
+ if (i == COLOR_RAINBOW_LIGHT)
+ printf(" // special colors");
+ printf("&#10;");
+ }
+}
+
+void print_text(c8 const *text, i32 color) {
+ if (text == INSERT_THEME)
+ print_theme();
+ else if (text != NULL) {
+ i32 mapped_color = COLOR_MAP[color];
+ if (mapped_color == COLOR_RAINBOW_LIGHT || mapped_color == COLOR_RAINBOW_DARK) {
+ for (i32 j = 0; text[j] != '\0'; ++j) {
+ if (text[j] == ' ')
+ printf(" ");
+ else if (text[j] == '<') {
+ for (; text[j] != '\0' && text[j] != '>'; ++j)
+ printf("%c", text[j]);
+ if (text[j] == '\0')
+ break;
+ printf("%c", text[j]);
+ } else if (text[j] == '&') {
+ for (; text[j] != '\0' && text[j] != ';'; ++j)
+ printf("%c", text[j]);
+ if (text[j] == '\0')
+ break;
+ printf("%c", text[j]);
+ } else
+ printf(
+ "<span style='color:#%s;'>%c</span>",
+ get_color(mapped_color),
+ text[j]
+ );
+ }
+ } else
+ printf("%s", text);
+ }
+}
+
+void print_element(Tag *t, b8 is_on_top, i32 default_color, i32 margin, b8 *is_link, b8 *need_styling) {
+ *is_link = (t->href != NULL && t->action != ACTION_SUBMIT);
+ b8 is_form = (t->href != NULL && t->action == ACTION_SUBMIT);
+
+ *need_styling = (t->type != TAG_PLAIN &&
+ t->type != TAG_BEGIN &&
+ t->type != TAG_END) ||
+ t->color != COLOR_NONE ||
+ t->background != COLOR_NONE ||
+ t->width > 0 ||
+ t->layout != LAYOUT_NONE ||
+ t->padding_top != 0 ||
+ t->line_height != 0 ||
+ t->nowrap ||
+ t->unselectable ||
+ margin != LAYOUT_NONE ||
+ *is_link;
+
+ if (*need_styling) {
+ if (*is_link)
+ printf("<a target='%s' href='%s' style='"
+ "text-decoration:underline;"
+ "text-decoration-style:dashed;",
+ t->action != ACTION_BLANK ? "_self" : "_blank",
+ t->href);
+ else
+ printf("<span style='");
+
+ if (t->unselectable)
+ printf("-webkit-user-select:none;-ms-user-select:none;user-select:none;");
+
+ if (margin == LAYOUT_MARGIN_LEFT)
+ printf("float:left;");
+ if (margin == LAYOUT_MARGIN_RIGHT)
+ printf("float:right;");
+ if (margin != LAYOUT_NONE && margin != t->layout)
+ printf("visibility:hidden;");
+
+ i32 padding_top = t->padding_top;
+
+ if (t->type == TAG_CAPTION) {
+ if (is_on_top)
+ padding_top += 68;
+ else
+ padding_top += 160;
+ }
+ if (t->type == TAG_SUBTITLE)
+ padding_top += 40;
+
+ if (padding_top != 0)
+ printf("padding-top:%.2fem;", .01f * padding_top);
+
+ if (t->type == TAG_BLOCK ||
+ t->line_height != 0 ||
+ t->layout == LAYOUT_LEFT ||
+ t->layout == LAYOUT_RIGHT ||
+ t->layout == LAYOUT_CENTER)
+ printf("display:block;");
+
+ if (t->line_height != 0)
+ printf("line-height:%.2f;", .01f * t->line_height);
+
+ if (t->icon != ICON_NONE)
+ printf("-webkit-user-select:none;-ms-user-select:none;user-select:none;"
+ "font-family:\"%s\";",
+ ( t->icon == ICON_SOLID ? "Icon Solid"
+ : t->icon == ICON_BRAND ? "Brand"
+ : "Icon"));
+
+ switch (t->type) {
+ case TAG_CAPTION:
+ printf("font-family:\"Lithos Pro\";"
+ "font-size:1.2em;font-weight:900;"
+ "letter-spacing:0.2em;"
+ "display:block;"
+ "padding-bottom:0.2em;");
+ break;
+
+ case TAG_SUBTITLE:
+ printf("font-family:\"Verdana\";"
+ "font-size:1.2em;"
+ "display:block;");
+ break;
+
+ case TAG_CODE:
+ if (*is_link)
+ printf("font-weight:bold;");
+ else
+ printf("font-family:monospace;font-size:0.9em;");
+ break;
+
+ default:;
+ }
+
+ if (t->nowrap)
+ printf("white-space:nowrap;");
+
+ if (t->color != COLOR_NONE)
+ printf("color:#%s;", get_color(t->color));
+
+ if (t->background != COLOR_NONE)
+ printf("background-color:#%s;", get_color(t->background));
+
+ if (t->width > 0) {
+ if (t->type != TAG_CAPTION)
+ printf("display:inline-block;");
+ printf("width:%.2fem;", .01f * t->width);
+ }
+
+ switch (t->layout) {
+ case LAYOUT_LEFT:
+ printf("text-align:left;");
+ break;
+
+ case LAYOUT_CENTER:
+ printf("text-align:center;");
+ break;
+
+ case LAYOUT_RIGHT:
+ printf("text-align:right;");
+ break;
+
+ case LAYOUT_FLOAT_LEFT:
+ printf("float:left;");
+ break;
+
+ case LAYOUT_FLOAT_CENTER:
+ printf("float:center;");
+ break;
+
+ case LAYOUT_FLOAT_RIGHT:
+ printf("float:right;");
+ break;
+
+ default:;
+ }
+
+ printf("'>");
+ }
+
+ if (t->image != NULL) {
+ printf("<img");
+ if (t->scale != 0)
+ printf(" width='%d%%'", t->scale);
+ switch (t->layout) {
+ case LAYOUT_LEFT:
+ printf(" align=\"left\"");
+ break;
+
+ case LAYOUT_CENTER:
+ printf(" align=\"center\"");
+ break;
+
+ case LAYOUT_RIGHT:
+ printf(" align=\"right\"");
+ break;
+
+ default:;
+ }
+ printf(" src=\"%s\"", t->image);
+ printf(">");
+ }
+
+ if (t->type == TAG_BLACK_FLAG) {
+ if (is_dark_color(t->color))
+ printf("<span style='"
+ "-webkit-user-select:none;-ms-user-select:none;user-select:none;"
+ "font-family:\"Icon Solid\";'>"
+ "&#61476;</span>");
+ else
+ printf("<span style='"
+ "-webkit-user-select:none;-ms-user-select:none;user-select:none;"
+ "font-family:\"Icon\";'>"
+ "&#61476;</span>");
+ }
+
+ if (is_form)
+ printf("<form style='display:inline-block;'"
+ " method='get' action='%s' id='"
+ FORM_NAME
+ "'>",
+ t->href);
+
+ if (t->type == TAG_SUBMIT_TEXT)
+ printf("<textarea style='font-familty:monospace;"
+ "color:#%s;"
+ "background-color:#%s;"
+ "resize:none;"
+ "width:100%%;'"
+ " rows='21' cols='40' spellcheck='false'"
+ " name='"
+ FORM_ITEM_DATA
+ "' form='"
+ FORM_NAME
+ "'>",
+ get_color(COLOR_FOREGROUND_TEXT),
+ get_color(COLOR_BACKGROUND_FAR));
+
+ if (*is_link && t->type == TAG_CODE)
+ printf("<span style='font-family:monospace;font-weight:normal;font-size:1.1em;'>");
+
+ if (t->text != NULL && t->scale != 0)
+ printf("<span style='font-size:%.2fem;'>", .01f * t->scale);
+
+ if (is_form)
+ printf("<input type='submit' style='"
+ "border:none;cursor:pointer;"
+ "font-size:1em;'"
+ " value='%s'></form>",
+ t->text);
+ else if (*is_link) {
+ if (t->text != NULL)
+ printf("%s", t->text);
+ } else
+ print_text(t->text,
+ t->color == COLOR_NONE ? default_color : t->color);
+
+ if (t->text != NULL && t->scale != 0)
+ printf("</span>");
+
+ if (*is_link && t->type == TAG_CODE)
+ printf("</span>");
+
+ if (t->type == TAG_SUBMIT_TEXT)
+ printf("</textarea>");
+}
+
+void close_element(b8 is_link, b8 need_styling) {
+ if (need_styling) {
+ if (is_link)
+ printf("</a>");
+ else
+ printf("</span>");
+ }
+}
+
+i64 print_tags(Tag *tags, i64 i) {
+ static i32 current_color = COLOR_NONE;
+
+ for (;; ++i) {
+ b8 is_link, need_styling;
+
+ if (tags[i].layout == LAYOUT_MARGIN_LEFT ||
+ tags[i].layout == LAYOUT_MARGIN_RIGHT) {
+ print_element(
+ tags + i,
+ i == 0,
+ current_color,
+ LAYOUT_MARGIN_LEFT,
+ &is_link,
+ &need_styling);
+ close_element(is_link, need_styling);
+
+ print_element(
+ tags + i,
+ i == 0,
+ current_color,
+ LAYOUT_MARGIN_RIGHT,
+ &is_link,
+ &need_styling);
+ close_element(is_link, need_styling);
+
+ if (tags[i].type == TAG_END)
+ break;
+ } else {
+ print_element(
+ tags + i,
+ i == 0,
+ current_color,
+ LAYOUT_NONE,
+ &is_link,
+ &need_styling);
+
+ i64 skip = 0;
+
+ if (tags[i].type == TAG_BEGIN) {
+ i32 prev_color = current_color;
+ if (tags[i].color != COLOR_NONE)
+ current_color = tags[i].color;
+ skip = print_tags(tags, i + 1);
+ current_color = prev_color;
+ }
+
+ close_element(is_link, need_styling);
+
+ if (tags[i].type == TAG_BEGIN)
+ i = skip - 1;
+ else if (tags[i].type == TAG_END)
+ break;
+ }
+ }
+
+ return i + 1;
+}
+
+i64 year() {
+ time_t t;
+ time(&t);
+ struct tm *exp = gmtime(&t);
+ return 1900 + exp->tm_year;
+}
+
+void print_time_gmt(i64 add_seconds) {
+ static c8 const *const WEEK[] = { "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat" };
+
+ static c8 const *const MONTHS[] = { "Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Now", "Dec" };
+
+ time_t t;
+ time(&t);
+ t += add_seconds;
+ struct tm *exp = gmtime(&t);
+
+ printf("%s, %02d %s %d %02d:%02d:%02d GMT", WEEK[exp->tm_wday],
+ exp->tm_mday, MONTHS[exp->tm_mon], 1900 + exp->tm_year,
+ exp->tm_hour, exp->tm_min, exp->tm_sec);
+}
+
+c8 const *getenv_or_empty(c8 const *name) {
+ c8 const *s = getenv(name);
+ return s == NULL ? "" : s;
+}
+
+b8 str_eq(c8 const *a, c8 const *a_end, c8 const *b, c8 const *b_end) {
+ i64 a_len = a_end == NULL ? (i64) strlen(a) : (i64) (a_end - a);
+ i64 b_len = b_end == NULL ? (i64) strlen(b) : (i64) (b_end - b);
+ if (a_len == b_len)
+ return memcmp(a, b, a_len) == 0;
+ return 0;
+}
+
+b8 str_eq_with_slash(c8 const *a, c8 const *b) {
+ i64 a_len = strlen(a);
+ i64 b_len = strlen(b);
+ if (a_len == b_len)
+ return memcmp(a, b, a_len) == 0;
+ if (a_len == b_len + 1 && a_len > 1 && a[a_len - 1] == '/')
+ return memcmp(a, b, b_len) == 0;
+ return 0;
+}
+
+i32 check_color(i32 c) {
+ if (c >= 0 && c < COLORS_MAX)
+ return c;
+ return COLOR_NONE;
+}
+
+b8 is_hex(c8 const *s, c8 const *end) {
+ for (i32 i = 0; s + i < end; ++i) {
+ if (s[i] >= '0' && s[i] <= '9')
+ continue;
+ if (s[i] >= 'a' && s[i] <= 'f')
+ continue;
+ return 0;
+ }
+ return 1;
+}
+
+c8 check_hex(c8 h) {
+ if ((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f'))
+ return h;
+ return '0';
+}
+
+b8 is_escaped_eol(c8 const *s) {
+ return s[0] == '%' &&
+ s[1] == '0' &&
+ (s[2] == 'D' || s[2] == 'A');
+}
+
+b8 is_escaped_comment(c8 const *s) {
+ return
+ s[0] == '%' && s[1] == '2' && s[2] == 'F' &&
+ s[3] == '%' && s[4] == '2' && s[5] == 'F';
+}
+
+void skip_spaces(c8 const *s, i32 *n) {
+ for (;;) {
+ if (s[*n] == '+' || s[*n] == ' ' || s[*n] == '\n' || s[*n] == '\r')
+ ++*n;
+ else if (is_escaped_eol(s + *n))
+ *n += 3;
+ else if (is_escaped_comment(s + *n)) {
+ *n += 6;
+ while (!is_escaped_eol(s + *n) && s[*n] != '\n' && s[*n] != '\r')
+ ++*n;
+ } else
+ return;
+ }
+}
+
+void setup_theme_cookie(c8 const *cookie, c8 const *query) {
+ // Parse cookie values
+ //
+ {
+ c8 const *val = cookie;
+ c8 const *val_end = NULL;
+
+ while (*val != '\0') {
+ while (*val == ' ' || *val == ';') ++val;
+ val_end = val;
+ while (*val_end != ';' && *val_end != '\0') ++val_end;
+
+ if (val_end - val >= 7 + COLOR_MAP_SIZE + NUM_COLORS * 6 &&
+ str_eq(val, val + sizeof COOKIE_THEME, COOKIE_THEME "=", NULL)) {
+ // Decode colors data from the cookie
+ //
+ i32 n = sizeof COOKIE_THEME;
+ SCALE = unpack_hex(val[n]) * 0x100 +
+ unpack_hex(val[n + 1]) * 0x10 +
+ unpack_hex(val[n + 2]);
+ WIDTH = unpack_hex(val[n + 3]) * 0x100 +
+ unpack_hex(val[n + 4]) * 0x10 +
+ unpack_hex(val[n + 5]);
+ n += 6;
+ for (i32 i = 0; i < COLOR_MAP_SIZE; ++i)
+ COLOR_MAP[i] = check_color(
+ (unpack_hex(val[n + i * 2]) << 4) |
+ unpack_hex(val[n + i * 2 + 1])
+ );
+ for (i32 i = 0; i < NUM_COLORS; ++i) {
+ i32 k = n + COLOR_MAP_SIZE * 2 + i * 6;
+ COLORS[i][0] = check_hex(val[k]);
+ COLORS[i][1] = check_hex(val[k + 1]);
+ COLORS[i][2] = check_hex(val[k + 2]);
+ COLORS[i][3] = check_hex(val[k + 3]);
+ COLORS[i][4] = check_hex(val[k + 4]);
+ COLORS[i][5] = check_hex(val[k + 5]);
+ }
+
+ // We don't care about other cookies.
+ break;
+ }
+
+ val = val_end;
+ }
+ }
+
+ // Handle the query
+ //
+ {
+ #define DEFAULT_MAP \
+ do { \
+ for (i32 i = 0;i < COLOR_MAP_SIZE; ++i) \
+ COLOR_MAP[i] = i; \
+ } while (0)
+
+ if (str_eq(query, NULL, "wide", NULL)) {
+ WIDTH = 100;
+ } else if (str_eq(query, NULL, "narrow", NULL)) {
+ WIDTH = 42;
+
+ } else if (str_eq(query, NULL, "guattari", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ memcpy(COLORS[i], THEMES[THEME_GUATTARI][i], 6);
+
+ } else if (str_eq(query, NULL, "monero", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ memcpy(COLORS[i], THEMES[THEME_MONERO][i], 6);
+
+ } else if (str_eq(query, NULL, "black", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ memcpy(COLORS[i], THEMES[THEME_BLACK][i], 6);
+
+ } else if (str_eq(query, NULL, "white", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ memcpy(COLORS[i], THEMES[THEME_WHITE][i], 6);
+
+ } else if (str_eq(query, NULL, "pink", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ memcpy(COLORS[i], THEMES[THEME_PINK][i], 6);
+
+ } else if (str_eq(query, NULL, "light", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ memcpy(COLORS[i], THEMES[THEME_LIGHT][i], 6);
+
+ } else if (str_eq(query, NULL, "random", NULL)) {
+ DEFAULT_MAP;
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ for (i32 j = 0; j < 6; ++j)
+ COLORS[i][j] = (rand() % 16)["0123456789abcdef"];
+
+ } else if (str_eq(query, NULL, "madness", NULL)) {
+ for (i32 i = 0; i < COLOR_BACKGROUND_NAV; ++i)
+ COLOR_MAP[i] = COLOR_RAINBOW_LIGHT;
+ for (i32 i = COLOR_BACKGROUND_NAV; i <= COLOR_BACKGROUND_NEAR; ++i)
+ COLOR_MAP[i] = COLOR_RAINBOW_DARK;
+ COLOR_MAP[COLOR_RAINBOW_LIGHT] = COLOR_RAINBOW_LIGHT;
+ COLOR_MAP[COLOR_RAINBOW_DARK] = COLOR_RAINBOW_DARK;
+
+ } else if (strlen(query) > 5 &&
+ str_eq(query, query + 5, "data=", NULL)) {
+ i32 n = 5;
+ i32 k = 0;
+ i32 val;
+
+ skip_spaces(query, &n);
+ if (sscanf(query + n, "%d%n", &val, &k) == 1) {
+ SCALE = val;
+ n += k;
+ }
+
+ skip_spaces(query, &n);
+ if (sscanf(query + n, "%d%n", &val, &k) == 1) {
+ WIDTH = val;
+ n += k;
+ }
+
+ c8 color[6];
+ for (i32 i = 0; i < COLOR_MAP_SIZE; ++i) {
+ skip_spaces(query, &n);
+ c8 c;
+ if (sscanf(query + n, "rainbow_ligh%c%n", &c, &k) == 1 &&
+ c == 't') {
+ COLOR_MAP[i] = COLOR_RAINBOW_LIGHT;
+ n += k;
+ } else if (sscanf(query + n, "rainbow_dar%c%n", &c, &k) == 1 &&
+ c == 'k') {
+ COLOR_MAP[i] = COLOR_RAINBOW_DARK;
+ n += k;
+ } else if (sscanf(query + n, "%c%c%c%c%c%c%n",
+ color, color + 1, color + 2,
+ color + 3, color + 4, color + 5,
+ &k) == 6 && is_hex(color, color + 6)) {
+ COLOR_MAP[i] = i;
+ for (i32 j = 0; j < 6; ++j)
+ COLORS[i][j] = check_hex(color[j]);
+ n += k;
+ } else if (sscanf(query + n, "%d%n", &val, &k) == 1) {
+ COLOR_MAP[i] = check_color(val);
+ n += k;
+ } else
+ break;
+ }
+ }
+
+ #undef DEFAULT_MAP
+ }
+
+ // Clamp values
+ //
+ {
+ if (SCALE < SCALE_MIN)
+ SCALE = SCALE_MIN;
+ if (SCALE > SCALE_MAX)
+ SCALE = SCALE_MAX;
+ if (WIDTH < WIDTH_MIN)
+ WIDTH = WIDTH_MIN;
+ if (WIDTH > WIDTH_MAX)
+ WIDTH = WIDTH_MAX;
+ }
+
+ // Setup new cookie values
+ //
+ {
+ // Encode colors data into the cookie
+ //
+ printf("Set-Cookie: " COOKIE_THEME "=");
+ printf("%03x%03x", SCALE, WIDTH);
+ for (i32 i = 0; i < COLOR_MAP_SIZE; ++i)
+ printf("%02x", COLOR_MAP[i]);
+ for (i32 i = 0; i < NUM_COLORS; ++i)
+ printf("%c%c%c%c%c%c",
+ COLORS[i][0],
+ COLORS[i][1],
+ COLORS[i][2],
+ COLORS[i][3],
+ COLORS[i][4],
+ COLORS[i][5]);
+ printf("; Path=/; SameSite=Lax; Expires=");
+ print_time_gmt(6 * SECONDS_IN_MONTH);
+ printf("; HttpOnly\n");
+ }
+}
+
+i32 main(i32 argc, c8 **argv) {
+ (void) argc;
+ (void) argv;
+
+ struct timespec time_begin;
+
+ timespec_get(&time_begin, TIME_UTC);
+ srand(time_begin.tv_sec ^ time_begin.tv_nsec);
+
+ c8 const *request_method = getenv_or_empty("REQUEST_METHOD");
+ c8 const *request_uri = getenv_or_empty("REQUEST_URI");
+ c8 const *query_string = getenv_or_empty("QUERY_STRING");
+ c8 const *document_uri = getenv_or_empty("DOCUMENT_URI");
+ c8 const *http_cookie = getenv_or_empty("HTTP_COOKIE");
+ c8 const *content_type = getenv_or_empty("CONTENT_TYPE");
+
+ if ( strlen(request_method) > MAX_REQUEST_METHOD_SIZE ||
+ strlen(request_uri) > MAX_REQUEST_SIZE ||
+ strlen(query_string) > MAX_REQUEST_SIZE ||
+ strlen(document_uri) > MAX_REQUEST_SIZE ||
+ strlen(http_cookie) > MAX_COOKIE_SIZE ||
+ strlen(content_type) > MAX_CONTENT_TYPE_SIZE ||
+ (!str_eq(request_method, NULL, "GET", NULL) &&
+ !str_eq(request_method, NULL, "POST", NULL))) {
+ printf("Status: 500 Internal Server Error\n");
+ return 0;
+ }
+
+ // TODO Check blacklist and throttling
+
+ if (0) {
+ printf("Status: 400 Forbidden\n");
+ return 0;
+ }
+
+ if (0) {
+ printf("Status: 429 Too Many Requests\n");
+ return 0;
+ }
+
+ i32 doc = DOC_404;
+
+ for (i64 i = 0; i < (i64)(sizeof redirects / sizeof *redirects); ++i)
+ if (str_eq_with_slash(document_uri, redirects[i].alias)) {
+ printf("Status: 301 Redirect\n");
+ printf("Location: %s\n", redirects[i].url);
+ printf("Content-Type: text/html\n");
+ printf("\n");
+ printf("Redirect: <a href='%s'>%s</a>\n", redirects[i].url, redirects[i].url);
+ return 0;
+ }
+
+ for (i64 i = 0; i < (i64)(sizeof locs / sizeof *locs); ++i)
+ if (strcmp(request_method, locs[i].method) == 0 &&
+ str_eq_with_slash(document_uri, locs[i].uri)) {
+ doc = locs[i].index;
+ break;
+ }
+
+ if (doc == DOC_404)
+ printf("Status: 404 Page Not Found\n");
+ else
+ printf("Status: 200 OK\n");
+
+ setup_theme_cookie(http_cookie, query_string);
+
+ printf("Content-Type: text/html\n");
+ printf("\n");
+
+ if (docs[doc].content[0].type == TAG_RAW) {
+ printf("%s", docs[doc].content[0].text);
+ return 0;
+ }
+
+ printf("<!DOCTYPE html><html><head>");
+ {
+ printf("<meta charset='utf-8'>");
+ printf("<meta name='viewport' "
+ "content='width=device-width,initial-scale=1'>");
+ printf(
+ "<link rel='icon' type='image/gif' href='/favicon.gif'>");
+ printf("<title> %s </title>", docs[doc].title);
+ printf("<style>");
+ for (i64 i = 0; i < (i64)(sizeof fonts / sizeof *fonts); ++i)
+ printf("@font-face{font-family:'%s';src:"
+ "url('/%s')format('woff2');"
+ "font-weight:%s;font-style:%s;}",
+ fonts[i].family, fonts[i].file, fonts[i].weight,
+ fonts[i].style);
+ printf(".centered{min-height:100vh;background-color:#%s;}",
+ get_color(COLOR_BACKGROUND_NEAR));
+ if (WIDTH > 0 && WIDTH < 100)
+ printf("@media(min-width:%dem)"
+ "{.centered{margin:auto;width:%dem;}}",
+ WIDTH,
+ WIDTH);
+ i32 scaled_nav = (35 * SCALE + 50) / 100;
+ printf(
+ "@media(min-width:%dem){"
+ ".navigation_left{float:left;}"
+ ".navigation_right{float:right;}"
+ ".foot_left{width:10em;float:left;}"
+ ".foot_right{width:10em;float:right;}"
+ "}"
+ "a{color:#%s;}"
+ "a:hover{color:#%s;}"
+ "input[type=submit]{"
+ "background-color:#%s;"
+ "color:#%s;}"
+ "input[type=submit]:hover{"
+ "color:#%s;}"
+ ".navigation_left a{color:#%s;}"
+ ".navigation_right a{color:#%s;}"
+ ".navigation_left a:hover{color:#%s;}"
+ ".navigation_right a:hover{color:#%s;}"
+ "</style>",
+ scaled_nav,
+ get_color(COLOR_FOREGROUND_LINK),
+ get_color(COLOR_FOREGROUND_HOVER),
+ get_color(COLOR_BACKGROUND_NEAR),
+ get_color(COLOR_FOREGROUND_LINK),
+ get_color(COLOR_FOREGROUND_HOVER),
+ get_color(COLOR_FOREGROUND_NAV),
+ get_color(COLOR_FOREGROUND_NAV),
+ get_color(COLOR_FOREGROUND_HOVER),
+ get_color(COLOR_FOREGROUND_HOVER)
+ );
+ }
+ printf("</head><body "
+ "style='font-family:\"Segoe UI\";"
+ "padding:0;margin:0;overflow-x:hidden;"
+ "background-color:#%s;color:#%s;'>"
+ "<div class='centered'>"
+ "<div style='padding:0.4em;font-size:%d.%dem;'>",
+ get_color(COLOR_BACKGROUND_FAR),
+ get_color(COLOR_FOREGROUND_TEXT),
+ SCALE / 100,
+ SCALE % 100);
+ {
+ printf("<div style='min-height:90vh;'>");
+ {
+ printf("<div "
+ "style='font-family:\"Lithos Pro\";"
+ "font-weight:900;font-size:3.3em;letter-spacing:0.1em;"
+ "color:#%s;"
+ "'> ",
+ get_color(COLOR_FOREGROUND_TITLE));
+ print_text("Guattari", COLOR_FOREGROUND_TITLE);
+ printf(" <span style='color:#%s;'>",
+ get_color(COLOR_FOREGROUND_SPOT));
+ print_text("Tech", COLOR_FOREGROUND_SPOT);
+ printf("</span> </div>");
+ printf(
+ "<div "
+ "style='font-family:\"Verdana\";font-size:1.7em;color:#%s;min-height:3em;"
+ "padding-left:0.5em;padding-right:2em;padding-top:0;padding-bottom:0.1em;"
+ "text-align:right;"
+ "-webkit-user-select:none;-ms-user-select:none;user-select:none;'> ",
+ get_color(COLOR_FOREGROUND_HEAD));
+ print_text(headlines[rand() % (sizeof headlines / sizeof *headlines)], COLOR_FOREGROUND_HEAD);
+ printf(" </div>");
+
+ printf("<ul "
+ "style='list-style-type:none;overflow:hidden;margin:0;"
+ "padding:0;"
+ "background-color:#%s;'>",
+ get_color(COLOR_BACKGROUND_NAV));
+ for (i64 i = 0; i < (i64)(sizeof navigation_left / sizeof *navigation_left); ++i)
+ printf("<li class='navigation_left'><a "
+ "style='font-weight:bold;text-decoration:none;"
+ "display:block;padding:0.5em;text-align:center;' "
+ "href='%s' "
+ "target='%s'>%s</a></li>",
+ navigation_left[i].href,
+ navigation_left[i].target,
+ navigation_left[i].text);
+ for (i64 i = sizeof navigation_right / sizeof *navigation_right - 1; i >= 0; --i)
+ printf("<li class='navigation_right'><a "
+ "style='font-weight:bold;text-decoration:none;"
+ "display:block;padding:0.5em;text-align:center;' "
+ "href='%s' "
+ "target='%s'>%s</a></li>",
+ navigation_right[i].href,
+ navigation_right[i].target,
+ navigation_right[i].text);
+ printf("</ul>");
+ printf("<div "
+ "style='padding-left:1.4em;padding-right:1.4em;font-size:1.1em;'>");
+ print_tags(docs[doc].content, 0);
+ printf("</div>");
+ }
+ printf("</div><div "
+ "style='padding-top:0.8em;color:#%s;font-size:0.8em;"
+ "text-align:center;'>",
+ get_color(COLOR_FOREGROUND_FOOT));
+ {
+ if (docs[doc].id != NULL) {
+ printf("<span class='foot_left'> <a "
+ "style='font-weight:bold;text-decoration:none;' "
+ "href='/'>root</a>");
+ if (docs[doc].id_parent != NULL)
+ printf(" &#47; <a "
+ "style='font-weight:bold;text-decoration:none;' "
+ "href='/%s'>%s</a>",
+ docs[doc].id_parent,
+ docs[doc].id_parent);
+ printf(" &#47; %s </span>",
+ docs[doc].id);
+ } else
+ printf("<span class='foot_left'> root </span>");
+ printf(
+ "<span style='font-family:\"Icon\";'>&copy;</span> %lld "
+ "Mitya Selivanov | <span "
+ "style='font-family:\"Brand\";'>&#62695;</span> CC-BY-4.0 ",
+ year());
+ printf("<span class='foot_right'><a "
+ "style='font-weight:bold;text-decoration:none;' "
+ "href='/theme'><span "
+ "style='font-family:\"Icon Solid\";'>&#127912;</span> "
+ "<span style='color:#%s;'>T</span>heme</a></span>",
+ get_color(COLOR_RAINBOW_LIGHT));
+ }
+ printf("</div>");
+ }
+ printf("</div></div></body></html>");
+
+ struct timespec time_end;
+ timespec_get(&time_end, TIME_UTC);
+ u64 delta_ns = 1000000000 * (time_end.tv_sec - time_begin.tv_sec) + (time_end.tv_nsec - time_begin.tv_nsec);
+ u64 delta_micros = (delta_ns + 500) / 1000;
+ printf("<script type='text/javascript'>"
+ "console.log('Page rendered in %lld.%02lld ms');"
+ "</script>",
+ delta_micros / 1000,
+ (delta_micros / 10) % 100);
+ printf("\n");
+
+ return 0;
+}