This commit is contained in:
ge 2022-09-30 17:54:01 +03:00
commit d14a3f95a7
16 changed files with 905 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/
TODO.*

40
Makefile Normal file
View File

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

52
content/bash_prompt.rst Normal file
View File

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

4
content/index.rst Normal file
View File

@ -0,0 +1,4 @@
:title: Ещё один сайт про cисадмиство и программирование
:date: 1 Jan 70
:type: page
:template: index.jinja2

18
content/projects.rst Normal file
View File

@ -0,0 +1,18 @@
:title: Projects
:date: 1 Jan 70
:type: page
=======
Проекты
=======
* `rSW </rsw/>`_ -- генератор статических сайтов из reStructuredText
* tui.sh -- библиотека элементов TUI для POSIX-совместимых оболочек
* boring-backup -- Bash фреймворк для скриптов резервного копирования
* `imgs <https://git.nxhs.cloud/ge/imgs>`_ -- минималистичный хостинг картинок
* `vk-toot <https://git.nxhs.cloud/ge/vk-toot>`_ -- кросспостер VK -> Mastodon
* `piglet <https://git.hxhs.cloud/ge/piglet>`_ -- клиент DNS API Porkbun
* roadwarrior -- менеджер подключений к VPN и прокси
* `n! <https://git.nxhs.cloud/ge/n>` -- консольный менеджер заметок
Остальное есть в `Gitea <https://git.nxhs.cloud/ge/>`_.

59
content/python_oop.rst Normal file
View File

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

339
content/this_blog.rst Normal file
View File

@ -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
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="icon" href="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=">
<link rel="stylesheet" href="assets/css/pygments/{{ pygments_theme }}.css">
<link rel="stylesheet" href="assets/css/custom.css">
<title>{{ page_title }} | {{ site_title }}</title>
</head>
<body>
<header>
<p>
{% if posts %}
{{ site_title }}
{% else %}
<a href="/">{{ site_title }}</a> / {{ page_title }}</p>
{% endif %}
</p>
</header>
{% block content %}{% endblock %}
<footer>
<!-- Footer content -->
</footer>
</body>
</html>
**post.j2**
.. code-block:: html+jinja
{% extends "base.j2" %}
{% block content %}
<article>
{{ post | safe }}
<article>
{% endblock %}
**index.j2**
.. code-block:: html+jinja
{% extends "base.j2" %}
{% block content %}
<section>
<ul id="posts">
{% for post in posts %}
<li>
<a href="/{{ post['path'] }}">{{ post['title'] }}</a>
<span class="meta"> — {{ post['date'] }}</span>
</li>
{% endfor %}
</ul>
</section>
{% 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

View File

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

42
content/yt_sync.rst Normal file
View File

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

40
layouts/base.jinja2 Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta property="og:site_name" content="{{ site.name }}"/>
<meta property="og:title" content="{{ page.title }}"/>
<meta property="og:type" content="article" />
<meta property="og:image" content="{{ site.og.image }}"/>
<link rel="icon" href="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=">
<link rel="stylesheet" href="/css/pygments/{{ pygments_theme }}.css">
<link rel="stylesheet" href="/css/style.css">
<title>{{ page.title }} | {{ site.title }}</title>
</head>
<body>
<header>
<p>
{% if page.template == 'index.jinja2' %}
{{ site.title }}
{% else %}
<a href="/">{{ site.title }}</a> / {{ page.title }}</p>
{% endif %}
</p>
<p>
{% for link in site.links %}
<a href="{{ link.url }}" target="{{ link.target }}">
{{ link.title }}</a>
{% endfor %}
</p>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>{{ site.footer.text }}</p>
<p>{{ site.footer.copyright }}</p>
</footer>
</body>
</html>

13
layouts/index.jinja2 Normal file
View File

@ -0,0 +1,13 @@
{% extends "base.jinja2" %}
{% block content %}
<section id="posts">
<ul id="posts">
{% for post in aggr.posts %}
<li>
<a href="{{ post.path }}">{{ post.title }}</a>
<span class="meta"> — {{ post.date }}</span>
</li>
{% endfor %}
</ul>
</section>
{% endblock %}

6
layouts/post.jinja2 Normal file
View File

@ -0,0 +1,6 @@
{% extends "base.jinja2" %}
{% block content %}
<article>
{{ html | safe }}
<article>
{% endblock %}

41
settings.toml Normal file
View File

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

View File

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

122
static/css/style.css Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB