commit d14a3f95a788037deec337d967404cf23413753b Author: ge Date: Fri Sep 30 17:54:01 2022 +0300 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1a77ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +TODO.* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..29882ae --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +# Makefile for reSW + +CONTENTDIR = content +STATICDIR = static +BUILDDIR = build +CONFIG = settings.toml +SERVER ?= + +all: build + +help: + @echo 'Build rSW site' + @echo + @echo 'Available targets:' + @echo + @echo ' build build HTML' + @echo ' serve run local HTTP server' + @echo ' css generate Pygments stylesheet' + @echo + @echo 'Run make without target to build html' + +build: + test -d "$(BUILDDIR)" && rm -rf "$(BUILDDIR)" || true + rsw build -c "$(CONFIG)" + +serve: + python -m http.server --directory $(BUILDDIR)/ --bind 0.0.0.0 8080 + +css: + mkdir -pv "$(STATICDIR)"/css/pygments + pygmentize -f html -S $(filter-out $@,$(MAKECMDGOALS)) -a .highlight \ + > "$(STATICDIR)"/css/pygments/$(filter-out $@,$(MAKECMDGOALS)).css + +publish: + rsync --verbose -r $(BUILDDIR)/ $(SERVER):/srv/http/nixhacks.net/public/ + +%: + @: + +.PHONY: all help build serve css publish diff --git a/content/bash_prompt.rst b/content/bash_prompt.rst new file mode 100644 index 0000000..9f4a721 --- /dev/null +++ b/content/bash_prompt.rst @@ -0,0 +1,52 @@ +:title: Функциональный и простой Bash prompt +:date: 10 Jul 22 + +==================================== +Функциональный и простой Bash prompt +==================================== + +О промптинге в Bash написано очень много, в этой заметке я просто покажу +лаконичный и функциональный вариант приглашения командной строки. + +Вот фрагмент из моего **~/.bashrc**: + +.. code-block:: shell + + # Command line prompt + # Print a non-zero exit code + __exit_code_ps1() { + if [ "$1" -ne 0 ]; then + echo "$1 " + fi + } + + PS1='\[\033[0;92m\]\w \[\033[0;31m\]$(__exit_code_ps1 $?)\[\033[0;15m\]\$ ' + + # Git prompt + # See /usr/share/git/completion/git-prompt.sh or download from: + # https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh + if [ -f /usr/share/git/completion/git-prompt.sh ]; then + . /usr/share/git/completion/git-prompt.sh + GIT_PS1_SHOWDIRTYSTATE=1 + 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. + +Помимо **git-prompt.sh** я использую функцию ``__exit_code_ps1()``, которая просто +печатает число, если оно не равно нулю. Это очень удобно — всегда видишь код выхода +предыдущей запущенной команды, порой очень помогает при отладке скриптов. + +В итоге всё это дело у меня выглядит вот так: + +.. image:: https://i.nxhs.cloud/ovD.png + +.. Links + +.. _этого: https://github.com/ohmybash/oh-my-bash +.. _отдельные скрипты: https://github.com/magicmonty/bash-git-prompt +.. _поставляется: https://github.com/git/git/tree/master/contrib/completion> + diff --git a/content/index.rst b/content/index.rst new file mode 100644 index 0000000..5c5ec09 --- /dev/null +++ b/content/index.rst @@ -0,0 +1,4 @@ +:title: Ещё один сайт про cисадмиство и программирование +:date: 1 Jan 70 +:type: page +:template: index.jinja2 diff --git a/content/projects.rst b/content/projects.rst new file mode 100644 index 0000000..8b6e0ec --- /dev/null +++ b/content/projects.rst @@ -0,0 +1,18 @@ +:title: Projects +:date: 1 Jan 70 +:type: page + +======= +Проекты +======= + +* `rSW `_ -- генератор статических сайтов из reStructuredText +* tui.sh -- библиотека элементов TUI для POSIX-совместимых оболочек +* boring-backup -- Bash фреймворк для скриптов резервного копирования +* `imgs `_ -- минималистичный хостинг картинок +* `vk-toot `_ -- кросспостер VK -> Mastodon +* `piglet `_ -- клиент DNS API Porkbun +* roadwarrior -- менеджер подключений к VPN и прокси +* `n! ` -- консольный менеджер заметок + +Остальное есть в `Gitea `_. diff --git a/content/python_oop.rst b/content/python_oop.rst new file mode 100644 index 0000000..6307ef0 --- /dev/null +++ b/content/python_oop.rst @@ -0,0 +1,59 @@ +:title: Питонячье ООП на уточках +:date: 24 Jul 21 + +======================== +Питонячье ООП на уточках +======================== + +Это весьма скупая на информацию напоминалка о работе с классами. + +* `Class definitions`_ +* `ООП на Python: концепции, принципы и примеры реализации`_ + +**duck.py**: + +.. code-block:: python + + # Объявляем класс "птица". От него в последствии будет унаследован + # класс "уточка". Здесь задаются общие характеристики для всех птиц + # и метод "летать". Аргумент `self` – это ссылка на объект, в контексте + # которого вызывается метод. Она обязательна для методов классов. + + class Bird(object): + can_fly = True + wings = 2 + def fly(self): + print('I\'m flying!') + + # Уточка это уже конкретный биологический вид (опускам породы и + # виды уточек). + # У неё есть имя и цвет (хотя цвет мог быть и у абстрактной птицы). + # Также уточка обладает уникальным методом "сказать кря". + + class Duck(Bird): + name = 'Duck' + color = 'Yellow' + def say_quack(self): + print('quack!') + +Как этим пользоваться: + +.. code-block:: text + + >>> import duck + >>> bird = duck.Bird() + >>> duck = duck.Duck() + >>> bird.fly() + I'm flying! + >>> duck.fly() + I'm flying! + >>> duck.say_quack() + quack! + >>> duck.name = 'Cool Duck' + >>> duck.name + 'Cool Duck' + +.. Links + +.. _ООП на Python\: концепции, принципы и примеры реализации: https://proglib.io/p/python-oop/ +.. _Class definitions: https://docs.python.org/3/reference/compound_stmts.html#class-definitions diff --git a/content/this_blog.rst b/content/this_blog.rst new file mode 100644 index 0000000..673aaa3 --- /dev/null +++ b/content/this_blog.rst @@ -0,0 +1,339 @@ +:title: Как я написал этот блог +:date: 5 Aug 22 +:type: page + +.. note:: + + Статья малось устарела и не соответсвует текущей действительности. + +======================= +Как я написал этот блог +======================= + +Я очень давно планировал написать блог. Именно что написать CMS для ведения +блога. Если погуглить, то CMS для этих целей сотни на разных языках с разным +позиционированием и фичами. Но мне хотелось своего. + +Первые попытки создать сайт были на `Flask`_, особым успехом не увенчались, +но это была хорошая практика и я немного прокачал свой уровень программирования. + +Для блога я не хотел использовать базу данных. Вместо этого я хранил статьи в +исходном Markdown, а метаданные по каждой статье писал в отдельный JSON, который +заменял мне БД. С тем же успехом я мог бы использовать SQLite, но мне нужно было +хранить контент в text/plain. + +Блог даже работал, но мне хотелось сделать админку, чтобы можно было писать +прямо в браузере. На этой части разработка заглохла и я окончательно запутался в +импортах в Python) + +Прошло ещё много времени, я вернулся к идее, но решил сделать ставку на простоту +и начал писать на `Bottle`_. Здесь разработка остановилась не дойдя даже до +варианта хоть как-то работающей системы. Причины скорее всего надо искать в +лени. + +Устав я взял `CMS Bludit`_ и стал вести свои редкие заметки в нём. Решение не +самое плохое, но и не супер отличное. Для простой кастомизации футера пришлось +городить костыли. + +.. _Flask: https://flask.palletsprojects.com/ +.. _Bottle: https://bottlepy.org/ +.. _CMS Bludit: https://www.bludit.com/ru/ + +Статика VS динамика +=================== + +Преимуществ у статических сайтов много. Об этом уже много написано. Мне больше +всего в статических сайтах нравится то, что: + +* роутингом запросов не управляет какой-либо скрипт, я могу свободно + распоряжаться тем, что у меня есть в DocumentRoot; +* не нужно настраивать application-сервер и реверс-прокси на него. + +С редактированием сайта чуть сложнее, так как сперва надо внести изменения +локально, а затем заменить изменившиеся файлы на сервере. Но и тут есть +какие-то решения. + +Я рассматривал уже готовые генераторы статических сайтов, но у них всех были +фатальные недостатки: + +* они сложные и требуют изучения документации чтобы начать писать; +* написать собственную тему задача весьма непростая; +* они написаны не мной. + +Здесь и рождается мой велосипед, которому я не придумал названия. Буду называть +его `генератором`. + +*re*\ **Structured**\ *Text* +============================ + +Я очень долго писал на Markdown, но в один момент понял, что возможностей языка +стало нехватать. Тут пришёлся очень кстати `Python-Markdown`_ к которому можно +было прикручивать `расширения`_. Одно даже сам `написал`_. + +Постепенно я пришёл к `reStructuredText`_. Все приколюхи, которые в нём есть +существуют не за счёт расширения синтаксиса плагинами, а заложены в спецификации. +Функциональности из коробки хватает чтобы писать даже `формулы`_. + +Такие вещи делаются через `роли` и `директивы`. Некоторые готовые директивы есть +в системе документации `Sphinx`_. Их я собираюсь потихоньку скопировать к себе. + +Ещё одно большое преимущество перед Markdown состоит в том, что reStructuredText +поддерживает атрибуты. Вот как это выглядит: + +.. code-block:: rst + + :title: Как я написал этот блог + :date: 5 Aug 2022 + +Это нативная фича, которую можно использовать в статьях для добавления метаданных. + +По сути это место, где можно сделать настройки, касающиеся отдельно взятой статьи. + +Большинство генераторов статических сайтов колхозят нечно подобное в Markdown, делая +исходник статьи непригодным для парсинга другими инструментами. Это причина по +которой в старой реализации блога мне пришлось использовать JSON для метаданных. + +.. _Sphinx: https://www.sphinx-doc.org/en/master/index.html +.. _Python-Markdown: https://python-markdown.github.io/ +.. _расширения: https://python-markdown.github.io/extensions/ +.. _написал: https://pypi.org/project/markdown-alerts/ +.. _rST: https://docutils.sourceforge.io/rst.html +.. _формулы: https://docutils.sourceforge.io/docs/ref/rst/mathematics.html + +Обзор +===== + +Сайт состоит из следующих частей. + +rst_blg.py + Непосредственно код генератора. Сейчас там всего около 200 строк без учёта + комментариев. + +requirements.txt + Список зависимостей. Стараюсь держать минимум. Пока что там: `docutils`, + `jinja2`, `toml` и `pygments`. + +settings.toml + Файл с настройками. Здесь можно переопределить практически всё. + +Makefile + Через него запускаются команды для сборки сайта и некоторые другие. Не + является обязательным, но с ним удобнее. + +layouts + Директория для шаблонов Jinja2. + +assets + Директория для хранения статических файлов, будь то CSS, JS или изображения. + Всё что нужно для визуального оформления страниц. Внутренняя структура + каталога может быть произвольной. + +content + Директория с исходниками статей в reStructuredText. Сюда можно также + положить любые файлы и директории. + +build + В эту директорию копируются ассеты, файлы и сгенерированный HTML. + +Статьи, шаблоны и ассеты могут быть оформлены абсолютно любым образом. Скрипту +безразлично что собирать. Пути и имена всех директорий можно переопределить в +settings.toml. + +Исходники с небольшой инструкцией я положил сюда: https://git.nxhs.cloud/ge/blog + +Написание постов +================ + +Тут всё предельно просто. Пишем файл и кладём в директорию `content`. В файле +должны быть указаны обязательные атрибуты ``:title:`` и ``:date:``. + +Блог пока не умеет работать с вложенной структурой статей. Поэтому всё будет +свалено в кучу в корневую директорию сайта. + +Все статьи добавляются в список постов и отображаются на главной странице. + +Для того, чтобы сделать "отдельную" страницу, надо добавить атрибут +``:not_a_post:``. + +================= ======= =========== ==================================== +Атрибут Тип Умолчание Описание +================= ======= =========== ==================================== +``:title:`` Строка Заголовок статьи +``:date:`` Дата Дата публикации, формат задаётся в + settings.toml +``:not_a_post:`` Флаг для одиночных страниц +================= ======= =========== ==================================== + +Темы оформления +=============== + +Никаких готовых тем. Пишем CSS вручную. Генератору сайтов всё равно что там +будет. От уровеня представления он никак не зависит. Так что можно писать любые +шаблоны и стили для них. + +Отдельно можно задать тему для блоков кода. Список тем для Pygments с превью +есть на странице https://pygments.org/styles/ + +Чтобы поменять тему, например, на `default` надо выполнить команду: + +.. code-block:: shell + + make css default + # или + pygmentize -f html -S default -a .highlight > assets/css/pygments/default.css + +Затем поменять значение ``theme`` в секции ``pygments`` settings.toml. + +.. code-block:: toml + + [pygments] + theme = 'default' + +Шаблоны +======== + +Шаблоны можно писать какие угодно. Для этогой сайта я написал три файла. +Приведу их без лишних строк. + +**base.j2** + +.. code-block:: html+jinja + + + + + + + + + + + {{ page_title }} | {{ site_title }} + + + +
+

+ {% if posts %} + {{ site_title }} + {% else %} + {{ site_title }} / {{ page_title }}

+ {% endif %} +

+
+ + {% block content %}{% endblock %} + +
+ +
+ + + + +**post.j2** + +.. code-block:: html+jinja + + {% extends "base.j2" %} + {% block content %} + +
+ {{ post | safe }} +
+ + {% endblock %} + +**index.j2** + +.. code-block:: html+jinja + + {% extends "base.j2" %} + {% block content %} + +
+
    + {% for post in posts %} +
  • + {{ post['title'] }} + — {{ post['date'] }} +
  • + {% endfor %} +
+
+ + {% endblock %} + +settings.toml +============= + +Сначала я хотел использовать обычный INI, но мне нужно было получать из конфига +словарь. Немного подумал и выбрал TOML. Он отлично сериализируется в словарь, +визуально повторяет INI. + +**settings.toml** разделён на несколько секций. + +site + Данные касающиеся непосредственно сайта. + + title + Название сайта + + index_page_title + Заголовок главной страницы. Для всех остальных страниц заголовок + берётся из атрибута. + + datatime_format + Формат даты для атрибута ``:date:``. + +build + Параметры, используетмые при сборке. + + build_dir + Директория, куда будет сохранён собранный сайт. + + content_dir + Директория с исходниками статей. + + templates_dir + Директория с шаблонами Jinja2. + + assets_dir + Директория с ассетами. + +pygments + Параметры подсветки синтаксиса в блоках кода. + + theme + Стиль Pygments. + +docutils + Конфигурация для docutils. Здесь можно указать любые параметры, которые + есть здесь: https://docutils.sourceforge.io/docs/user/config.html + + Мне достаточно: + + .. code-block:: toml + + [docutils] + initial_header_level = 2 + section_self_link = true + syntax_highlight = 'short' + +Что ещё хочется сделать +======================= + +В перспективе я планирую добавить следующие фичи. + +- RSS +- OpenGraph +- Webmention +- Расширить возможности rST до уровня Sphinx +- Улучшить CSS +- Комментарии +- Кастомный лейаут для отдельных статей +- Поддержка вложенной структуры статей + +.. * https://jinja.palletsprojects.com/en/3.0.x/templates/ + * https://docutils.sourceforge.io/docs/user/config.html + diff --git a/content/todo_text_markup.rst b/content/todo_text_markup.rst new file mode 100644 index 0000000..0f725b7 --- /dev/null +++ b/content/todo_text_markup.rst @@ -0,0 +1,43 @@ +:title: Менеджер задач в текстовом файле +:date: 2 Sep 21 + +================================ +Менеджер задач в текстовом файле +================================ + +Кажется, я нашёл почти идеальную формулу для ведения списка задач. По сути я +придумал новый формат разметки текста, специализированный для списков задач. +Придумать для него название оказалось тяжело. Приложение Todo это второе по +популярности приложение после Hello World и все хорошие названия уже давно +заняты. Поэтому пусть будет просто **.todo**. + +Синтаксис выглядит таким образом + +.. code-block:: text + + - Uncompleted task (light blue) + + Completed task (green) + x Rejected task (red) + # Comment + \Marked text (yellow background)\ + `Code (magenta)` + Plain text + +.. image:: https://i.nxhs.cloud/MQ9.png + +Да, это все элементы синтаксиса. Предельно просто. + +Какие есть возможности (сравниваю с тем, что предлагают обычные +todo-приложения): + +- Не нужно устанавливать никакого дополнительного ПО или каждый раз открывать + громоздкий веб-интерфейс. Всё что надо это текстовый редактор. В моём случае + идеально подошёл **vim**. Написал для него `скрипт для подсветки синтаксиса`_. + При желании можно написать подсветку для других редакторов. +- Текстовая заметка и задача это одна сущность — один файл. +- Всё управление полностью с клавиатуры. +- Полная свобода включать в файл что угодно. Разумеется, текст, это не + специальный формат для встраивания изображений или других файлов, но можно + использовать элементы Markdown. + +.. _скрипт для подсветки синтаксиса: https://git.nxhs.cloud/ge/todolist-syntax diff --git a/content/yt_sync.rst b/content/yt_sync.rst new file mode 100644 index 0000000..e4c27d3 --- /dev/null +++ b/content/yt_sync.rst @@ -0,0 +1,42 @@ +:title: Синхронизация плейлистов YouTube +:date: 14 Aug 22 + +================================ +Синхронизация плейлистов YouTube +================================ + +YouTube, конечно, место прекрасное (кому как), но как и всё в этом бренном +мире видеоролики могут исчезнуть в любой момент. + +Отсюда есть только один выход — хранить все видео локально. Диск достаточного +объёма у меня имеется. + +С помощью `youtube-dl`_ или `yt-dlp`_ можно скачивать видео без регистрации и +SMS. + +Скачивать можно целыми плейлистами и разработчиками предусмотрена +возможность синхронизировать плейлист в YouTube с локальными файлами. + +Реализовано это весьма неочевидно. Ниже пример шелл-скрипта, с помощью которого +можно удобно синкать плейлист. Видео будут сохранены в директорию одноимённую +с плейлистом YouTube. С `шаблонами имён`_ можно поиграться. + +.. code-block:: shell + + #!/bin/sh + + echo 己龍 MUSIC VIDEO + yt-dlp --download-archive kiryu.txt \ + --format 'bv*+ba' \ + --output '%(playlist_title)s/%(title)s-%(id)s.%(ext)s' \ + 'https://youtube.com/playlist?list=PLg5luStJrusE-PLBGQhCkrzQt-BLLu3Fu' + +Скрипт можно запускать по крону и быть спокойным, что видео останутся с тобой. +А с домашним медиа-сервером становится совсем приятно. + +.. Links + +.. _youtube-dl: https://github.com/ytdl-org/youtube-dl +.. _yt-dlp: https://github.com/yt-dlp/yt-dlp +.. _шаблонами имён: https://github.com/yt-dlp/yt-dlp#output-template + diff --git a/layouts/base.jinja2 b/layouts/base.jinja2 new file mode 100644 index 0000000..fe15f5c --- /dev/null +++ b/layouts/base.jinja2 @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + {{ page.title }} | {{ site.title }} + + +
+

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

+ {% endif %} +

+

+ {% for link in site.links %} + + {{ link.title }} + {% endfor %} +

+
+
+ {% block content %}{% endblock %} +
+
+

{{ site.footer.text }}

+

{{ site.footer.copyright }}

+
+ + diff --git a/layouts/index.jinja2 b/layouts/index.jinja2 new file mode 100644 index 0000000..517910e --- /dev/null +++ b/layouts/index.jinja2 @@ -0,0 +1,13 @@ +{% extends "base.jinja2" %} +{% block content %} +
+
    + {% for post in aggr.posts %} +
  • + {{ post.title }} + — {{ post.date }} +
  • + {% endfor %} +
+
+{% endblock %} diff --git a/layouts/post.jinja2 b/layouts/post.jinja2 new file mode 100644 index 0000000..c6505c2 --- /dev/null +++ b/layouts/post.jinja2 @@ -0,0 +1,6 @@ +{% extends "base.jinja2" %} +{% block content %} +
+ {{ html | safe }} +
+{% endblock %} diff --git a/settings.toml b/settings.toml new file mode 100644 index 0000000..1209942 --- /dev/null +++ b/settings.toml @@ -0,0 +1,41 @@ +[defaults] +template = "post.jinja2" +type = "post" + +[pygments] +theme = "rrt" + +[site] +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.footer] +text = '' +copyright = '' + +[site.og] +image = 'https://nixhacks.net/img/DEC_VT100_terminal.jpg' diff --git a/static/css/pygments/rrt.css b/static/css/pygments/rrt.css new file mode 100644 index 0000000..5f8e958 --- /dev/null +++ b/static/css/pygments/rrt.css @@ -0,0 +1,84 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #0000ff } +.highlight { background: #000000; color: #dddddd } +.highlight .c { color: #00ff00 } /* Comment */ +.highlight .err { color: #dddddd } /* Error */ +.highlight .esc { color: #dddddd } /* Escape */ +.highlight .g { color: #dddddd } /* Generic */ +.highlight .k { color: #ff0000 } /* Keyword */ +.highlight .l { color: #dddddd } /* Literal */ +.highlight .n { color: #dddddd } /* Name */ +.highlight .o { color: #dddddd } /* Operator */ +.highlight .x { color: #dddddd } /* Other */ +.highlight .p { color: #dddddd } /* Punctuation */ +.highlight .ch { color: #00ff00 } /* Comment.Hashbang */ +.highlight .cm { color: #00ff00 } /* Comment.Multiline */ +.highlight .cp { color: #e5e5e5 } /* Comment.Preproc */ +.highlight .cpf { color: #00ff00 } /* Comment.PreprocFile */ +.highlight .c1 { color: #00ff00 } /* Comment.Single */ +.highlight .cs { color: #00ff00 } /* Comment.Special */ +.highlight .gd { color: #dddddd } /* Generic.Deleted */ +.highlight .ge { color: #dddddd } /* Generic.Emph */ +.highlight .gr { color: #dddddd } /* Generic.Error */ +.highlight .gh { color: #dddddd } /* Generic.Heading */ +.highlight .gi { color: #dddddd } /* Generic.Inserted */ +.highlight .go { color: #dddddd } /* Generic.Output */ +.highlight .gp { color: #dddddd } /* Generic.Prompt */ +.highlight .gs { color: #dddddd } /* Generic.Strong */ +.highlight .gu { color: #dddddd } /* Generic.Subheading */ +.highlight .gt { color: #dddddd } /* Generic.Traceback */ +.highlight .kc { color: #ff0000 } /* Keyword.Constant */ +.highlight .kd { color: #ff0000 } /* Keyword.Declaration */ +.highlight .kn { color: #ff0000 } /* Keyword.Namespace */ +.highlight .kp { color: #ff0000 } /* Keyword.Pseudo */ +.highlight .kr { color: #ff0000 } /* Keyword.Reserved */ +.highlight .kt { color: #ee82ee } /* Keyword.Type */ +.highlight .ld { color: #dddddd } /* Literal.Date */ +.highlight .m { color: #dddddd } /* Literal.Number */ +.highlight .s { color: #87ceeb } /* Literal.String */ +.highlight .na { color: #dddddd } /* Name.Attribute */ +.highlight .nb { color: #dddddd } /* Name.Builtin */ +.highlight .nc { color: #dddddd } /* Name.Class */ +.highlight .no { color: #7fffd4 } /* Name.Constant */ +.highlight .nd { color: #dddddd } /* Name.Decorator */ +.highlight .ni { color: #dddddd } /* Name.Entity */ +.highlight .ne { color: #dddddd } /* Name.Exception */ +.highlight .nf { color: #ffff00 } /* Name.Function */ +.highlight .nl { color: #dddddd } /* Name.Label */ +.highlight .nn { color: #dddddd } /* Name.Namespace */ +.highlight .nx { color: #dddddd } /* Name.Other */ +.highlight .py { color: #dddddd } /* Name.Property */ +.highlight .nt { color: #dddddd } /* Name.Tag */ +.highlight .nv { color: #eedd82 } /* Name.Variable */ +.highlight .ow { color: #dddddd } /* Operator.Word */ +.highlight .pm { color: #dddddd } /* Punctuation.Marker */ +.highlight .w { color: #dddddd } /* Text.Whitespace */ +.highlight .mb { color: #dddddd } /* Literal.Number.Bin */ +.highlight .mf { color: #dddddd } /* Literal.Number.Float */ +.highlight .mh { color: #dddddd } /* Literal.Number.Hex */ +.highlight .mi { color: #dddddd } /* Literal.Number.Integer */ +.highlight .mo { color: #dddddd } /* Literal.Number.Oct */ +.highlight .sa { color: #87ceeb } /* Literal.String.Affix */ +.highlight .sb { color: #87ceeb } /* Literal.String.Backtick */ +.highlight .sc { color: #87ceeb } /* Literal.String.Char */ +.highlight .dl { color: #87ceeb } /* Literal.String.Delimiter */ +.highlight .sd { color: #87ceeb } /* Literal.String.Doc */ +.highlight .s2 { color: #87ceeb } /* Literal.String.Double */ +.highlight .se { color: #87ceeb } /* Literal.String.Escape */ +.highlight .sh { color: #87ceeb } /* Literal.String.Heredoc */ +.highlight .si { color: #87ceeb } /* Literal.String.Interpol */ +.highlight .sx { color: #87ceeb } /* Literal.String.Other */ +.highlight .sr { color: #87ceeb } /* Literal.String.Regex */ +.highlight .s1 { color: #87ceeb } /* Literal.String.Single */ +.highlight .ss { color: #87ceeb } /* Literal.String.Symbol */ +.highlight .bp { color: #dddddd } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #ffff00 } /* Name.Function.Magic */ +.highlight .vc { color: #eedd82 } /* Name.Variable.Class */ +.highlight .vg { color: #eedd82 } /* Name.Variable.Global */ +.highlight .vi { color: #eedd82 } /* Name.Variable.Instance */ +.highlight .vm { color: #eedd82 } /* Name.Variable.Magic */ +.highlight .il { color: #dddddd } /* Literal.Number.Integer.Long */ diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..7a4394d --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,122 @@ +:root { + --b: #000; + --w: #fff; + --a: #0b6adc; +} +@media (prefers-color-scheme: dark) { + :root { + --b: #fff; + --w: #000; + --a: #0b6adc; + } +} +body { + color: var(--b); + background-color: var(--w); + padding: 1rem; + max-width: 79ch; + font-family: 'Source Code Pro', monospace; + font-size: 16px; + line-height: 1.3; +} +header { + margin: 2rem 0 auto; +} +footer { + margin-top: 2rem; +} +h1, h2, h3, h4, h5, h6 { + margin-top: 1rem; + margin-bottom: 1.2rem; + font-weight: 500; +} +h1 { font-size: 3.6rem; line-height: 1.2; letter-spacing: -.1rem;} +h2 { font-size: 3.0rem; line-height: 1.25; letter-spacing: -.1rem; } +h3 { font-size: 2.6rem; line-height: 1.3; letter-spacing: -.1rem; } +h4 { font-size: 2.0rem; line-height: 1.35; letter-spacing: -.08rem; } +h5 { font-size: 1.6rem; line-height: 1.5; letter-spacing: -.05rem; } +h6 { font-size: 1.2rem; line-height: 1.6; letter-spacing: 0; } +p { + margin: 1rem 0; +} +a { + color: var(--b); +} +a:hover { + color: var(--a); +} +ol, ul { + margin: 0; +} +section#posts { + margin-top: 2rem; +} +ul#posts li { + list-style: none; + padding: 0; + margin: .5rem 0; +} +ul#posts { + padding: 0; +} +dt { /* Defenition list title */ + font-weight: bold; +} +span.meta { + font-size: 80%; +} +span.docutils.literal { /* Inline literal */ + background-color: #d0d7de; + border-radius: 4px; + padding: 0 4px 2px 4px; + font-family: monospace; + font-size: 80%; +} +pre { + font-size: 85%; +} +code { + padding: .2rem .5rem; + margin: 0 .2rem; + white-space: nowrap; + background: #f1f1f1; + border: 1px solid #e1e1e1; +} +pre > code { + display: block; + padding: 1rem; + white-space: pre; +} +blockquote { + padding: 0 1em; + border-left: .25em solid #d0d7de; +} +table, th, td { + border: 1px solid; + border-collapse: collapse; + border-color: #afb4b9; + padding: 0 8px; +} +th { + font-weight: 600; + background-color: #d0d7de; +} +td p { + margin: .5rem 0; +} +hr { + color: #d0d7de; +} +.admonition { + padding: .5rem 1rem; +} +.admonition-title { + font-weight: bold; +} +.highlight { + border-radius: 8px; +} +.highlight pre { + padding: 1rem; + overflow-x: auto; +} diff --git a/static/img/DEC_VT100_terminal.jpg b/static/img/DEC_VT100_terminal.jpg new file mode 100644 index 0000000..50a3729 Binary files /dev/null and b/static/img/DEC_VT100_terminal.jpg differ