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 @@
-

- {% if page.template == 'index.jinja2' %} - {{ site.title }} - {% else %} - {{ site.title }} / {{ page.title }}

- {% endif %} -

{% 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 @@

+
+ {{ html | safe }} +
{% 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