diff --git a/content/bash_prompt.rst b/content/bash_prompt.rst
index 9f4a721..4be8b05 100644
--- a/content/bash_prompt.rst
+++ b/content/bash_prompt.rst
@@ -31,14 +31,13 @@
PS1='\[\033[0;96m\]$(__git_ps1 "(%s) ")\[\033[0;92m\]\w \[\033[0;31m\]$(__exit_code_ps1 $?)\[\033[0;15m\]\$ '
fi
-На GitHub можно найти много всяких скриптов или даже целых "фремворков" типа `этого`_
-или `отдельные скрипты`_ для Git. А вы знали, что скрипт для PS1 и так `поставляется`_
-в пакете Git? Не вижу смысла не использовать его, если только вам не надо
-как-то иначе работать с PS1.
+На GitHub можно найти много всяких скриптов или даже целых "фремворков" типа
+`этого`_ или `отдельные скрипты`_ для Git. Однако, скрипт для PS1 и так
+`поставляется`_ в пакете Git.
-Помимо **git-prompt.sh** я использую функцию ``__exit_code_ps1()``, которая просто
-печатает число, если оно не равно нулю. Это очень удобно — всегда видишь код выхода
-предыдущей запущенной команды, порой очень помогает при отладке скриптов.
+Помимо **git-prompt.sh** я использую функцию ``__exit_code_ps1()``, которая
+просто печатает число, если оно не равно нулю. Это очень удобно — всегда
+видишь код выхода команды. Порой это очень помогает при отладке скриптов.
В итоге всё это дело у меня выглядит вот так:
diff --git a/content/do_boring_backups.rst b/content/do_boring_backups.rst
new file mode 100644
index 0000000..75497ae
--- /dev/null
+++ b/content/do_boring_backups.rst
@@ -0,0 +1,246 @@
+:title: Делаем скучные бэкапы
+:date: 8 Oct 22
+
+=====================
+Делаем скучные бэкапы
+=====================
+
+Бэкапы, в самом деле, нескучные (:
+
+Мотивацией для написания очередного велосипеда стало то, что даже для, казалось
+бы, готовых инструментов приходится писать обвязки на шелле.
+
+В какой-то момент мне надоело писать баш-портянки для бэкапа и я написал одну
+большую, которая умеет сразу много всего. Знакомьтесь — **boring_backup**.
+
+> **Пакеты для Debian/Ubuntu, Arch и сорцы**
+`здесь `_.
+
+Сначала о том, чем он *НЕ* является:
+
+* Тупым линейным скриптом с вызовом tar и mysqldump.
+* Полным аналогом `restic `_ и похожих утилит.
+* POSIX-совместимым скриптом. Хотелось бы, конечно, но это достаточно больно.
+ Может быть когда-нибудь я перепишу код и оно заработает на dash/ash и прочих
+ шеллах, но это будет уже другой скрипт.
+
+**boring_backup** — это библиотека функций Bash и интерфейс командной строки
+для скриптов резервного копирования.
+
+Вот что в ней есть:
+
+* Обработка ошибок (wow!)
+* Логирование в файл или syslog (WOW!)
+* Парсер URI практически целиком совместимый с RFC 3986!
+* Готовые функции для запаковки файлов (tar) и снятия SQL-дампов (MySQL/MariaDB
+ и PostgreSQL)
+
+Логика работы утилиты крайне проста. Есть два массива данных — список URI
+исходных ресурсов, которые надо бэкапить и список URI хранилищ, куда предстоит
+бэкапиться.
+
+Поддерживается определённый набор схем URI, каждую схему обрабатывает свой
+обработчик.
+
+Cамый простой скрипт резервного копирования, который можно написать с
+помощью **boring_backup** выглядит так:
+
+.. code-block:: shell
+
+ sources=(/home/john)
+ targets=(/var/backups)
+
+Сохраним это в файл под именем `test`.
+
+Здесь очевидно дира /home/john будет скопирована в /var/backups, однако cперва
+она будет запакована в архив tar без сжатия. Запускаем бэкап:
+
+.. code-block:: shell
+
+ boring_backup test
+
+Итого получим файл /var/backups/test_john_2022.10.08-1230.tar. Если мы хотим
+сжать архив, то добавим компрессию:
+
+.. code-block:: shell
+
+ sources=(/home/john)
+ targets=(/var/backups)
+ compression=xz
+
+Здесь значение переменной ``compression`` будет интерпретироваться так же как
+tar интерпретирует расширение архива с опцией ``--auto-compress``. То есть
+поддерживаются все утилиты, которыми tar умеет сжимать. Если переменная пуста
+или содержит что-то не то, то сжатие будет отключено.
+
+Надо исключить лишние файлы из архива? Легко!
+
+.. code-block:: shell
+
+ sources=(/home/john)
+ targets=(/var/backups)
+ compression=xz
+ tar_exclude=(.cache foo bar)
+
+Для передачи опций для tar есть переменная ``tar_options`` в которой можно
+переопределить умолчание — ``-acf``.
+
+Теперь добавим базу данных MySQL в бэкап. Записывается URI в формате, который
+ещё называют DSN (data source name):
+
+.. code-block:: shell
+
+ sources=(
+ /home/john
+ mysql://test_usr:%2C%23s%7BRGqH@localhost/test_db
+ )
+ targets=(/var/backups)
+ compression=xz
+ tar_exclude=(.cache foo bar)
+
+Обратите внимание, что пароль пользователя закодирован, чтобы не возникло
+коллизий из-за наличия в нём спецсимволов. Закодировать пароль крайне просто,
+вот пример на Perl, который можно выполнить прямо в командной строке:
+
+.. code-block:: perl
+
+ echo ',#s{RGqH' | perl -MURI::Escape -wlne 'print uri_escape $_'
+
+Теперь в /var/backups окажется два файла — test_john_2022.10.08-1230.tar.xz
+и test_test_db_2022.10.08-1230.sql.xz. Сваливать все файлы в одну директорию
+может быть неудобно, поэтому давайте сохранять файлы в папки по датам.
+
+.. code-block:: shell
+
+ sources=(
+ /home/john
+ mysql://test_usr:%2C%23s%7BRGqH@localhost/test_db
+ )
+ today=$(date +%Y-%m-%d) # в результате даст дату yyyy-mm-dd
+ make_target_dir=yes
+ targets=(/var/backups/$today)
+ compression=xz
+ tar_exclude=(.cache foo bar)
+
+Теперь пути до файлов будут выглядеть так:
+/var/backups/2022-10-08/test_john_2022.10.08-1230.tar.xz.
+
+Теперь поробуем передать эти файлы ещё и на удалённое хранилище. Допустим на
+S3-совместимое объектное хранилище. Сперва придётся установить и настроить
+утилиту s3cmd, затем скрипт примет вид:
+
+.. code-block:: shell
+
+ sources=(
+ /home/john
+ mysql://test_usr:%2C%23s%7BRGqH@localhost/test_db
+ )
+ today=$(date +%Y-%m-%d)
+ make_target_dir=yes
+ targets=(
+ /var/backups/$today
+ s3://my_bucket/$today
+ )
+ compression=xz
+ tar_exclude=(.cache foo bar)
+
+**boring_backup** сначала сделает бэкап и сохранит его в /var/backups и только
+затем передаст файлы в S3-хранилище (или любое другое). Можно указывать любое
+количество URI в targets и также в sources. Особенностью targets является то,
+что требуется хотя бы один URI со схемой `file`, куда будут сохраняться
+локальные бэкапы. Просто путь без указания схемы тоже считается за file.
+Следующие три записи полностью эквивалентны:
+
+* /var/backups
+* file:/var/backups
+* file:///var/backups
+
+Пойдём дальше.
+
+Мы можем обрабатывать ошибки. Допишем в скрипт функцию `on_error`, которая
+будет нам отправлять письмо с фрагментом лога и текстом ошибки.
+
+.. code-block:: shell
+
+ sources=(
+ /home/john
+ mysql://test_usr:%2C%23s%7BRGqH@localhost/test_db
+ )
+ today=$(date +%Y-%m-%d)
+ make_target_dir=yes
+ targets=(
+ /var/backups/$today
+ s3://my_bucket/$today
+ )
+ compression=xz
+ tar_exclude=(.cache foo bar)
+
+ email=me@example.com
+ on_error() {
+ local log_fragment err_message
+ err_message="$*"
+ log_fragment="$(grep -n 'Backup started' "$log_file" | tail -1 |
+ cut -d ':' -f 1 | xargs -I {} tail -n +{} "$log_file")"
+ printf \
+ 'Текст ошибки:\n%s\n\nЛог последнего бэкапа:\n%s' \
+ "$err_message" "$log_fragment" |
+ mail -s "$HOSTNAME: Ошибка при выполнении бэкапа" "$email"
+ }
+
+Также мы можем после копирование в удалённое хранилище удалять локальный
+бэкап. Список всех созданных за текущее выполнение скрипта бэкапов хранится
+в массиве ``backups``.
+
+.. code-block:: shell
+
+ finalise() {
+ log -p "\tClean up local backups"
+ log -p "\tFiles to delete: ${backups[@]}"
+ rm -rv -- "${backups[@]}"
+ log -p "\tLocal backups deleted"
+ }
+
+Это лишь небольшая часть того, что можно реализовать. Полная документация
+есть `в виде мануала `_.
+
+Отмечу ещё несколько важных фич:
+
+* Стандартную логику можно полностью переопределить, в том числе выкинуть
+ необходимость сохранять локальный бэкап. Достаточно описать функцию с
+ именем `backup`.
+* Можно выполнять действия перед бэкапом в функции `prepare` и после бэкапа
+ — `finalise`.
+
+Есть и вещи, которые вам не понравятся, но решение которых вероятно появится
+в слудующих версиях **boring_backup**:
+
+* Концепция репозитория бэкапов не поддерживается. Файлы просто загружается
+ туда куда вы укажете.
+* Из коробки можно делать только полные бэкапы.
+* Нет ротации бэкапов. Её нужно реализовывать самостоятельно.
+* Из коробки нет шифрования. GPG и кастомная функция `backup` вам в помощь.
+
+В заключение дам ещё один пример скрипта для бэкапа приложения Gitea.
+Попробуйте сами разобраться что тут происходит.
+
+.. code-block:: shell
+
+ sources=(.) # just pass validation
+ targets=(.)
+ today="$(date +%d_%b_%Y)"
+ s3cmd_config=~/.s3cfg
+ prepare() {
+ systemctl stop gitea.service
+ sleep 5
+ }
+ backup() {
+ log -p "Dumping Gitea"
+ su -c "/usr/local/bin/gitea dump -c /etc/gitea/app.ini \
+ -f /home/git/.cache/gitea_dump.zip" - git 2>> "$log_file"
+ backups+=(/home/git/.cache/gitea_dump.zip)
+ tgt_s3cmd s3://mybucket/backups/gitea-$today
+ }
+ finalise() {
+ systemctl start gitea.service
+ rm -rv -- "${backups[@]}"
+ }
diff --git a/content/posixish_functions.rst b/content/posixish_functions.rst
new file mode 100644
index 0000000..b8bad93
--- /dev/null
+++ b/content/posixish_functions.rst
@@ -0,0 +1,189 @@
+:title: Несколько полезных функций на Bourne Shell
+:date: 18 Oct 22
+
+==========================================
+Несколько полезных функций на Bourne Shell
+==========================================
+
+Функции, описанные здесь можно применять в любой POSIX-совместимой оболочке.
+Протестировано в **dash**. Примеры на этой странице будут обновляться.
+
+**1. Парсер аргументов в стиле GNU**
+
+.. code-block:: shell
+
+ optval() {
+ # GNU-style command line options parser
+ #
+ # Usage: optval "$1" "$2"
+ #
+ # Set variables:
+ # OPT - option name
+ # VAL - value
+ # SFT - value for shift command
+
+ OPT="${1%%=*}"; VAL="${1#*=}"; SFT=1
+
+ if [ "$OPT" = "$VAL" ]; then
+ if [ -n "$2" ] && [ "${2#"${2%%?}"}" != "-" ]; then
+ VAL="$2"; SFT=2
+ else
+ unset VAL
+ fi
+ fi
+ if [ -z "$VAL" ]; then
+ echo Missing argument for option "$OPT" >&2
+ exit 1
+ fi
+ }
+
+Эта функция позволит надёжно распарсить опции с обязательным аргументом. Ей
+по силам:
+
+* **-o** *value*
+* **--option**\ =\ *value*
+* **--option**\ =\ *name=value*
+* **-option** *value*
+
+..и вариации.
+
+Непосредственно в POSIX-оболочке работать с парсером будет тяжко из-за
+отсутсвия массивов. Единственный доступный массив **$@** здесь занят. NULL в
+качестве разделителя для списка позиционных аргументов использовать не
+получится, поэтому ниже пример с двоеточием. В **Bash** таких проблем нет.
+
+.. code-block:: shell
+
+ while [ "$#" -ne 0 ]; do
+ case "$1" in
+ -h|--help)
+ echo "Usage: $0 [-chv] [--] arguments"
+ exit 0
+ ;;
+ -v|--version)
+ echo 1.0.0
+ exit 0
+ ;;
+ -c|-c=*|--config|--config=*)
+ optval "$1" "$2"
+ config="$VAL"
+ shift "$SFT"
+ ;;
+ --) # end of options processing
+ shift
+ break
+ ;;
+ -*)
+ echo Unknown option "$1" >&2
+ exit 1
+ ;;
+ *) # Save positional arguments
+ # In Bash better use ARGS+=("$1") instead
+ ARGS="$1":"$ARGS"
+ shift
+ esac
+ done
+
+ # Set positional arguments. It is not needed in Bash
+ # In Bash you can use ARGS array directly or use set -- "${ARGS[@]}"
+ OLDIFS="$IFS"; IFS=:
+ # $arg must be unquoted!
+ # shellcheck disable=SC2086
+ for arg in $ARGS; do set -- $arg "$@"; done
+ IFS="$OLDIFS"
+
+ echo Config: "$config"
+ echo Positional arguments:
+ for arg in "$@"; do; echo : "$arg"; done
+
+**2. Интерактивный диалог**
+
+.. code-block:: shell
+
+ confirm() {
+ # Confirmation interactive dialog
+ #
+ # Usage: confirm "message"
+
+ [ -n "$ASSUME_NO" ] && return 1
+ [ -n "$ASSUME_YES" ] && return 0
+
+ while true; do
+ printf '%s (y/n) ' "$*"
+ read -r REPLY
+ case "$REPLY" in
+ [Yy]|[Yy][Ee][Ss]) return 0;;
+ [Nn]|[Nn][Oo]) return 1;;
+ *) echo Please, answer y or n;;
+ esac
+ done
+ }
+
+Здесь предусмотрены переменные ``ASSUME_NO`` и ``ASSUME_YES`` для
+неинтерактивного режима. Автоматическое "Нет" и "Да" соответственно. Пример:
+
+.. code-block:: shell
+
+ if confirm 'Proceed?'; then
+ echo OK
+ else
+ echo Abort
+ fi
+
+**3. Версия Python**
+
+Просто напечатает в STDOUT версию интерпретатора Python.
+
+.. code-block:: shell
+
+ python_version() {
+ # Get Python interpreter version in format 'major.minor.micro' e.g. '3.10.2'
+ # Works with Python 2.x and 3.x.
+ #
+ # Usage: python_version []
+
+ "${1:-python}" -c "import sys; v=sys.version_info; \
+ print('%s.%s.%s' % (v.major, v.minor, v.micro))"
+ }
+
+**4. Открыть редактор по умолчанию**
+
+.. code-block:: shell
+
+ default_editor() {
+ # Edit file in user's default editor
+ #
+ # Usage: default_editor file
+
+ if [ -n "$EDITOR" ]; then
+ "$EDITOR" "$@"
+ return 0
+ fi
+
+ if hash select-editor >/dev/null 2>&1; then
+ if [ -f ~/.selected_editor ]; then
+ . ~/.selected_editor
+ else
+ select-editor
+ . ~/.selected_editor
+ fi
+ "$SELECTED_EDITOR" "$@"
+ return 0
+ fi
+
+ if hash editor >/dev/null 2>&1; then
+ editor "$@"
+ return 0
+ else
+ vi "$@" # fallback to vi
+ return 0
+ fi
+ }
+
+**5. Простой URL-decoder**
+
+.. code-block:: shell
+
+ unescape_uri() {
+ echo "$1" | sed 's/%/\\\\x/g' | xargs printf
+ }
diff --git a/content/projects.rst b/content/projects.rst
index 59eb395..a35a58e 100644
--- a/content/projects.rst
+++ b/content/projects.rst
@@ -8,7 +8,8 @@
* `rSW `_ -- генератор статических сайтов из reStructuredText
* tui.sh -- библиотека элементов TUI для POSIX-совместимых оболочек
-* boring-backup -- Bash фреймворк для скриптов резервного копирования
+* `boring-backup `_ -- Bash
+ фреймворк для скриптов резервного копирования
* `imgs `_ -- минималистичный хостинг картинок
* `vk-toot `_ -- кросспостер VK -> Mastodon
* `piglet `_ -- клиент DNS API Porkbun
diff --git a/content/terminal_muxers.rst b/content/terminal_muxers.rst
new file mode 100644
index 0000000..d36c6e9
--- /dev/null
+++ b/content/terminal_muxers.rst
@@ -0,0 +1,253 @@
+:title: Терминальные мультиплексоры
+:date: 19 Oct 22
+
+===========================
+Терминальные мультиплексоры
+===========================
+
+Терминальный мультиплексор — это утилита, которая позволяет запускать
+несколько сессий оболочки в рамках одной сессии и также отсоединять
+собственную сессию от оболочки.
+
+Для пояснения зачем это нужно я процитирую русскую Википедию:
+
+ Это полезно для работы с несколькими программами из командной строки, а
+ также для запуска программ на удаленном сервере.
+
+Возможно звучит сложно, но всё проще чем кажется. В этой заметке я попробую
+наглядно объяснить концепцию и приёмы работы с мультиплексорами терминала.
+
+Сейчас распространено два мультиплексора терминала:
+
+* `GNU screen `_
+* `tmux `_
+
+Рано или поздно вы с одним из них столкнётесь.
+
+Матчасть
+========
+
+Вот основные концепции, которые использются в упомянутых утилитах.
+
+Сессия
+ Непосредственно сессия мультиплексора терминала. Сессий может быть
+ множество, их можно создавать, закрывать, отсоединяться от них или
+ присоединяться, задать имя.
+
+Окно
+ В любой сессии мультиплекcора есть как минимум одно окно. Если закрыть
+ последнее окно, то закроется вся сессия. В каждом окне запускается новая
+ сессия командной оболочки (Bash, Zsh и пр.).
+
+ Окон также может быть множество. К ним применимы те же действия, что и к
+ сессиям, кроме отсоединения.
+
+ Между окнами можно переключаться с помощью горячих клавиш.
+
+Область экрана (или панель)
+ Каждое окно можно разбить на несколько областей. В каждой области будет
+ запущена новая сессия оболочки.
+
+ Делить окно можно по вертикали и горизонтали. Размеры областей обычно
+ можно отрегулировать. По умолчанию экран делится пополам.
+
+ Здесь надо отметить, что в **screen** понятия окно и область экрана
+ смешаны. То есть область экрана приравнивается к отдельному окну, хотя
+ они и отображаются на одном экране.
+
+Итак, я подключаюсь к серверу по SSH с целью запустить там какую-то команду.
+После запуска команды я намерен отключиться от сервера. При этом команда
+должна продолжить выполняться. Я также должен увидеть результат её выполнения,
+когда вновь подключусь к серверу.
+
+**1.** Подключаюсь по SSH:
+
+.. code-block:: text
+
+ localhost server
+ +------------+ +------------------+
+ | ssh_client |----->| ssh_session |
+ +------------+ +------------------+
+ (you are here)----->| shell_session(0) |
+ +------------------+
+
+После успешного подключения по SSH я окажусь в сессии оболочки — **(0)**.
+При этом если оборвётся SSH-сессия, то я потеряю всё что в ней было.
+
+**2.** Как только я запущу **screen** картинка примет следующий вид:
+
+.. code-block:: text
+
+ localhost server
+ +------------+ +------------------+
+ | ssh_client |----->| ssh_session |
+ +------------+ +------------------+
+ | shell_session(0) |
+ +------------------+
+ | screen_session |
+ +------------------+
+ (you are here)----->| shell_session(1) |
+ +------------------+
+
+**3.** Для примера запущу **vim**:
+
+.. code-block:: text
+
+ localhost server
+ +------------+ +------------------+
+ | ssh_client |----->| ssh_session |
+ +------------+ +------------------+
+ | shell_session(0) |
+ +------------------+
+ | screen_session |
+ +------------------+
+ (you are here)----->| shell_session(1) |
+ +------------------+
+ | vim |
+ +------------------+
+
+**4.** Теперь я отключусь (detach) от сессии **screen**:
+
+.. code-block:: text
+
+ localhost server
+ +------------+ +------------------+ +------------------+
+ | ssh_client |----->| ssh_session | | screen_session |
+ +------------+ +------------------+ +------------------+
+ (you are here)----->| shell_session(0) | | shell_session(1) |
+ +------------------+ +------------------+
+ | vim |
+ +------------------+
+
+Сессия **screen** отсоединилась от оболочки **(0)**. При этом оболочка **(1)**
+продолжает работать. Сейчас можно отключиться от SSH.
+
+Когда я подключусь к серверу вновь, то окажусть в ситуации как на схеме 4, но
+после выполнения команды **screen -r** всё вернётся к состоянию как на схеме 3
+и я смогу продолжить работу в **vim**.
+
+.. admonition:: Как набирать комбинации клавиш
+
+ Вначале набрается модификатор, например ``Ctrl+a``, затем надо отпустить
+ клавиши и нажать на клавишу команды, например ``?``.
+
+screen
+======
+
+**screen** просто работает. Он минималистичен и прост в использовании.
+
+Запуск новой сессии:
+
+.. code-block:: text
+
+ screen [-S имя_сессии]
+
+Подключиться к запущенной сессии:
+
+.. code-block:: text
+
+ screen -r [имя_сессии]
+
+=============== ============================================================
+Комбинация Действие
+=============== ============================================================
+Ctrl+a ? Показать справку
+Ctrl+a : Открыть приглашение для ввода команд screen
+Ctrl+a " Список окон
+Ctrl+a 0 Открыть окно номер 0
+Ctrl+a A Переименовать текущее окно
+Ctrl+a a Отправить комбинацию Ctrl+a в текущее окно
+Ctrl+a c Создать новое окно (с оболочкой)
+Ctrl+a S Разделить текущую область по горизонтали
+Ctrl+a | Разделить текущую область по вертикали
+Ctrl+a tab Перевести фокус ввода на следующую область
+Ctrl+a Ctrl+a Переключиться между текущей и предыдущей областью
+Ctrl+a Esc Перейти в режим копирования (используйте Enter для выделения
+ текста). Также скроллинг терминала.
+Ctrl+a ] Вставить текст
+Ctrl+a Q Закрыть все окна, кроме текущего
+Ctrl+a X Закрыть текущую область
+Ctrl+a d Отсоединиться от текущей сессии
+=============== ============================================================
+
+При разделении экрана на области появится новая пустая область. Нужно
+переключить на неё фокус и запустить новое окно с оболочкой ``Ctrl+a`` ``c``.
+
+Конфигурация **screen** хранится в файле **~/.screenrc**. Я для себя написал
+совсем простой конфиг, который тем не менее показывает всё что действительно
+нужно.
+
+.. code-block:: unixconfig
+
+ startup_message off
+ hardstatus alwayslastline
+ hardstatus string '%S: %-w%>(%n %t)%{-}%+w%<'
+
+.. image:: https://i.nxhs.cloud/Swl.png
+ :alt: Terminal with screen
+
+Материалы по **screen**:
+
+* `GNU screen usage `_
+* `GNU screen - ArchWiki `_
+
+tmux
+====
+
+**tmux** — это более новороченное решение. Он умеет почти всё то же что
+**screen** + имеет дополнительные фичи вроде управления мышью.
+
+Запустить новую сессию:
+
+.. code-block:: text
+
+ tmux [new -s имя_сессии]
+
+Подключиться к сессии:
+
+.. code-block:: text
+
+ tmux a [-t имя_сессии]
+
+=============== ============================================================
+Комбинация Действие
+=============== ============================================================
+Ctrl+b c Создать новое окно
+Ctrl+b , Переименовать окно
+Ctrl+b w Список окон
+Ctrl+b " Разделить текущую область по горизотали
+ctrl+b % Разделить текущую область по вертикали
+Ctrl+b x Закрыть текущую панель
+Ctrl+b [ Перейти в режим копирования текста + скроллинг
+Ctrl+b d Отсоединиться от сессии
+=============== ============================================================
+
+Мой **~/.tmux.conf**:
+
+.. code-block:: unixconfig
+
+ set -g mouse on
+ set -g history-limit 5000
+
+ # vim style controls
+ bind h select-pane -L
+ bind j select-pane -D
+ bind k select-pane -U
+ bind l select-pane -R
+
+ bind -r H resize-pane -L 10
+ bind -r J resize-pane -D 10
+ bind -r K resize-pane -U 10
+ bind -r L resize-pane -R 10
+
+ # Copy / paste
+ bind b list-buffers
+ bind B show-buffer
+ bind P paste-buffer
+
+ bind-key -T copy-mode-vi v send-keys -X begin-selection
+ bind-key -T copy-mode-vi y send-keys -X copy-selection
+ bind-key -T copy-mode-vi r send-keys -X rectangle-toggle
+
+ bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel \
+ "xclip -i -f -selection primary | xclip -i -selection clipboard"
diff --git a/layouts/base.jinja2 b/layouts/base.jinja2
index fe15f5c..9302f42 100644
--- a/layouts/base.jinja2
+++ b/layouts/base.jinja2
@@ -15,13 +15,6 @@
{% for link in site.links %}
diff --git a/layouts/index.jinja2 b/layouts/index.jinja2
index 517910e..c5ca38c 100644
--- a/layouts/index.jinja2
+++ b/layouts/index.jinja2
@@ -4,10 +4,13 @@
{% for post in aggr.posts %}
-
- {{ post.title }}
- — {{ post.date }}
+ {{ post.title }}
+ {{ post.date }}
{% endfor %}
+
{% endblock %}
diff --git a/settings.toml b/settings.toml
index 1209942..4b3e5c6 100644
--- a/settings.toml
+++ b/settings.toml
@@ -10,28 +10,30 @@ name = 'Ещё один сайт про cисадмиство и програм
title = 'Nixhacks'
datetime_format = "%d %b %y"
- [[site.links]]
- title = '[home]'
- url = '/'
- [[site.links]]
- title = '[projects]'
- url = '/projects.html'
- [[site.links]]
- title = '[code]'
- url = 'https://git.nxhs.cloud/ge/'
- target = '_blank'
- [[site.links]]
- title = '[kb]'
- url = '/kb/'
- target = '_blank'
- [[site.links]]
- title = '[markdown]'
- url = '/writing/'
- target = '_blank'
- [[site.links]]
- title = '[my ip]'
- url = '/cgi-bin/ip.cgi'
- target = '_blank'
+[[site.links]]
+title = '[home]'
+url = '/'
+[[site.links]]
+title = '[software]'
+url = '/projects.html'
+[[site.links]]
+title = '[knowledge base]'
+url = '/kb/'
+target = '_blank'
+[[site.links]]
+title = '[manpages]'
+url = '/cgi-bin/man.cgi'
+[[site.links]]
+title = '[code]'
+url = 'https://git.nxhs.cloud/ge/'
+target = '_blank'
+[[site.links]]
+title = '[markdown]'
+url = '/writing/'
+target = '_blank'
+[[site.links]]
+title = '[my ip]'
+url = '/cgi-bin/ip.cgi'
[site.footer]
text = ''
diff --git a/static/css/style.css b/static/css/style.css
index 7a4394d..a5cc30d 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -1,3 +1,10 @@
+@font-face {
+ font-family: 'Inconsolata-LGC';
+ src: local('Inconsolata-LGC'),
+ url(/fonts/Inconsolata-LGC.woff2) format('woff2'),
+ url(/fonts/Inconsolata-LGC.woff) format('woff');
+ font-display: swap;
+}
:root {
--b: #000;
--w: #fff;
@@ -10,12 +17,17 @@
--a: #0b6adc;
}
}
+@media screen and (max-width: 840px) {
+ img {
+ width: 100%;
+ }
+}
body {
color: var(--b);
- background-color: var(--w);
+ background: var(--w);
padding: 1rem;
max-width: 79ch;
- font-family: 'Source Code Pro', monospace;
+ font-family: 'Inconsolata-LGC', monospace;
font-size: 16px;
line-height: 1.3;
}
@@ -54,19 +66,16 @@ section#posts {
ul#posts li {
list-style: none;
padding: 0;
- margin: .5rem 0;
+ margin: 1rem 0;
}
ul#posts {
padding: 0;
}
-dt { /* Defenition list title */
+dt {
font-weight: bold;
}
-span.meta {
- font-size: 80%;
-}
-span.docutils.literal { /* Inline literal */
- background-color: #d0d7de;
+span.docutils.literal {
+ background: #d0d7de;
border-radius: 4px;
padding: 0 4px 2px 4px;
font-family: monospace;
@@ -89,7 +98,7 @@ pre > code {
}
blockquote {
padding: 0 1em;
- border-left: .25em solid #d0d7de;
+ border-left: .25em solid var(--b);
}
table, th, td {
border: 1px solid;
@@ -99,7 +108,7 @@ table, th, td {
}
th {
font-weight: 600;
- background-color: #d0d7de;
+ background: #ebeff2;
}
td p {
margin: .5rem 0;
@@ -107,12 +116,20 @@ td p {
hr {
color: #d0d7de;
}
+img {
+ max-width: 79ch;
+}
.admonition {
padding: .5rem 1rem;
+ border-radius: 8px;
+ background: #ebeff2;
}
.admonition-title {
font-weight: bold;
}
+.admonition-title::before {
+ content: '🛈 ';
+}
.highlight {
border-radius: 8px;
}
diff --git a/static/fonts/Inconsolata-LGC.woff b/static/fonts/Inconsolata-LGC.woff
new file mode 100644
index 0000000..01d2f89
Binary files /dev/null and b/static/fonts/Inconsolata-LGC.woff differ
diff --git a/static/fonts/Inconsolata-LGC.woff2 b/static/fonts/Inconsolata-LGC.woff2
new file mode 100644
index 0000000..139bd70
Binary files /dev/null and b/static/fonts/Inconsolata-LGC.woff2 differ
diff --git a/static/img/DEC_VT100_terminal.jpg b/static/img/DEC_VT100_terminal.jpg
index 50a3729..f4fb7be 100644
Binary files a/static/img/DEC_VT100_terminal.jpg and b/static/img/DEC_VT100_terminal.jpg differ