Docs as Code для художественной литературы. Делаем творческий сайт ребенка с помощью MkDocs

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!


Docs as Сode — подход к работе с текстами, подразумевающий написание текста как кода:


  • в простом текстовом редакторе или IDE;
  • с использованием системы контроля версий;
  • с CI / CD / Code Review.

В настоящее время Docs as Code широко применяется при работе с технической документацией, давая техническим писателям и проектным командам массу удобств и преимуществ.


Но что если пойти дальше, попробовать такой подход не с техническими, а с художественными текстами? Что если автор — не технарь и не айтишник? Просто юный начинающий писатель, который пробует писать прозу и стихи ручкой на бумаге, и надеется познакомить широкую публику со своим творчеством?


В этой статье я расскажу о таком эксперименте (забегая вперед, удачном). Моей дочери 11 лет, она пишет сказки, стихи и рассказы. Чтобы поддержать ее увлечение, я помог ей создать литературный сайт, используя подход Docs as Code. Она успешно освоила основы Markdown и Git. Сейчас она самостоятельно публикует новые произведения и обновляет новости на своем сайте https://lib-beliakova.github.io/.


Продемонстрированные далее файлы и конфиги можно также посмотреть в репозитории https://github.com/lib-beliakova/lib-beliakova.github.io/.


Инструменты


Docs as code — не конкретный набор инструментов, а скорее "философия". Выбор инструментов самый широкий. Недолго думая, я взял то, с чем недавно имел дело по работе (я технический писатель).


  • Markdown — язык разметки.
  • Git — система контроля версий.
  • VS Code — IDE, текстовый редактор.
  • MkDocs — генератор статических веб-сайтов из набора Markdown-документов.
  • Material for MkDocs — продвинутая тема оформления для MkDocs.
  • Docker — контейнеризатор приложений (для упрощения запуска MkDocs).
  • GitHub — всем известный веб-сервис для хостинга Git-репозиториев, также предоставляющий бесплатный хостинг статических сайтов GitHub Pages и систему автоматизации сборки GitHub Actions.

Создание проекта MkDocs


Минимальный "проект" MkDocs выглядит так:


.
├─ docs/
│  └─ index.md
└─ mkdocs.yml

Его можно создать командой mkdocs new или просто добавить необходимые файлы вручную (если MkDocs пока не установлен). Управление параметрами проекта MkDocs осуществляется в конфигурационном файле mkdocs.yaml в корневой папке проекта. Его первоначальное содержимое может быть таким:


site_name: Литературное творчество Беляковой Анастасии
nav:
  - index.md

Этого уже достаточно, чтобы собрать минимальный сайт. Но, перед переходом к сборке, я бы хотел рассмотреть еще несколько предварительных действий.


Подключение темы оформления Material


Нужная нам тема подключается так:


theme:
  name: material

Русификация


В шаблоне сайта имеется несколько локализуемых строчек, например "search", "previous", "next". Их можно перевести на русский, добавив плагин i18n (internationalization) c единственным языком ru.


plugins:
  - i18n:
      docs_structure: suffix
      default_language: ru
      languages:
        ru:
          name: Russian
          build: true
          site_name: "Литературное творчество Беляковой Анастасии"

Встроенный поиск


На сайт можно добавить поиск:


plugins:
  - search

Настройка локальной сборки сайта в Docker


Пора посмотреть что получилось. Чтобы не засорять ноутбук ребенка MkDocs-ом, Питоном, и прочими зависимостями, засорим его Docker'ом воспользуемся Docker'ом (пусть заодно чуть-чуть с контейнеризацией познакомится). Пишем такой нехитрый Containerfile:


FROM docker.io/library/python:alpine
WORKDIR /tmp
COPY ./requirements.txt .
RUN apk add --update --no-cache --virtual .build-deps gcc musl-dev &&\
    apk add --no-cache git ca-certificates curl &&\
    pip install --no-cache-dir --requirement ./requirements.txt &&\
    apk del .build-deps &&\
    rm ./requirements.txt
WORKDIR /d
CMD ["mkdocs", "serve", "-a", "0.0.0.0:8000"]

Добавляем список зависимостей в файл requirements.txt:


mkdocs>=1.2.1
mkdocs-material>=7.1.0
mkdocs-static-i18n

Собираем образ:


docker build -t lib-beliakova -f Containerfile .

Запускаем встроенный веб-сервер MkDocs:


docker run --rm -itp 8000:8000 -v "$(pwd):/d" lib-beliakova

Теперь можно увидеть получившийся сайтик в браузере по ссылке http://localhost:8000/. Если изменить любой файл в проекте и сохраниться, MkDocs немедленно подхватит изменения, пересоберет сайт, а страница в браузере сразу обновится. Очень удобно — можно править Markdown-документы, время от времени сохраняться, и сразу видеть результаты в окне браузера.


Наполнение контентом


Тут я уже уступил место за клавиатурой юной писательнице, показав азы Markdown — как добавлять заголовки, разбивать текст на параграфы и вставлять цитаты.


# Иван-дворник и царевна-птица

В одном царстве был, как положено, царь. Ну и конечно же, у этого царя были подданные, крестьяне, земледельцы, ремесленники.

У одного крестьянина было три сына. И младшего звали Иваном. 
Жили они бедно, сыновья в школу не ходили и в институт с университетом тоже. 

И выросли ребята взрослые. И задумался младший Иван, кем ему работать. 
Идёт Иван по улице. Вдруг смотрит -- объявление.

> Срочно требуется царский дворник!

Пришёл Иван к царю на собеседование, спрашивает:

...

Результат: https://lib-beliakova.github.io/tales/Ivan-dvornik/


Также сразу показал команду запуска MkDocs в Docker.


Дочка справилась с перепечатыванием своих бумажных рукописей в markdown без особых затруднений. Оказалось, что это не сложнее работы с Word на школьных уроках информатики. Нужно добавлять по markdown-файлику для каждого произведения в проект в папку docs, и не забывать "регистрировать" каждый фвйл в mkdoc.yaml в секции nav:


nav:
  - index.md
  - news.md
  - Рассказы:
     - stories/index.md
     - stories/summer.md
     - stories/under-our-house.md
     - stories/preparation-for-festival.md
     - stories/zastroyschiki.md
    // ...
  - Сказки:
     - tales/index.md
     - tales/Ivan-dvornik.md
     - tales/Masha-rasteryasha.md
     - tales/rainbow-lama.md
  - Сказки:
     - tales/index.md
     - tales/Ivan-dvornik.md
     - tales/Masha-rasteryasha.md
     - tales/rainbow-lama.md
  - Фанфики:
     - fanfics/index.md
     - fanfics/stolen-sun.md
     - fanfics/smeshariki.md
     // ...

Тут пришлось познакомить юную писательницу с азами YAML, хорошо что VS Code подсвечивает ошибки.


Названия для каждого пункта можно не писать, MkDocs автоматически достает их из Markdown (использует первый заголовок).


У нас "узлы" оглавления ассоциируются с индексными страницами index.md — это как бы "лендинги", чтобы можно было дать кому-то ссылку на "все рассказы", "все сказки" или "все фанфики". По-умолчанию MkDocs Material так не делает (узлы оглавления просто являются контейнерами документов, не имея своего контента). Чтобы получить нужное поведение, достаточно добавить в конфиг парметр navigation.indexes:


theme:
    name: material
    features:
      - navigation.indexes

При желании, можно еще включить опцию navigation.expand (рядом с navigation.indexes) — в этом случае оглавление будет сразу отображаться в развернутом виде.


Также можно сделать названия, отличные от заголовков внутри файлов. Нам это не потребовалось, но вот пример из документации MkDocs Material:


nav:
  - Section:
    - section/index.md 
    - Page 1: section/page-1.md
    ...
    - Page n: section/page-n.md

Публикация в GitHub Pages с использованием GitHub Actions


Инициализируем git-репозиторий, делаем первый commit, закидываем репозиторий на GitHub и подключаем GitHub Pages (тут не буду вдаваться в подробности, все стандартно).


Добавляем файлик .github/workflows /mkdocs.yml с инструкциями по сборке и деплою сайта для GitHub Actions:


name: mkdocs
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: 3.x
      - run: pip install mkdocs-material mkdocs-static-i18n
      - run: mkdocs gh-deploy --force

Теперь юная писательница может делать commit и push в VS Code, и через несколько минут изменения автоматически появляются на сайте.


Если скучно ждать, можно следить за процессом в разделе Actions репозитория на GitHub.


Google Analytics


Первые вопросы дочки после публикации сайта — смотрит ли кто-то её творчество? Сколько людей посмотрело? К счастью, в MkDocs есть встроенная поддержка GA, добавить счетчик на сайт можно парой строчек в конфиге:


extra:
    analytics:
        provider: google
        property: G-9ZRQD3E6FD

Разумеется, надо сперва зарегистрировать "property" на https://analytics.google.com/ и подтвердить "права владения" сайтом. Для этого есть несколько вариантов, самым простым мне показалось закинуть сгенерированный Гуглом файлик в корень сайта. Никакие дополнительные "приседания" для этого не нужны. Достаточно положить файл в папку docs и он автоматически окажется в корневой папке сайта.


Через день-другой в Google Analytics уже можно будет смотреть отчеты.


Форма обратной связи


Ожидаемо, следующий вопрос юной писательницы — нравится ли кому-то её творчество? Вдруг не нравится? Сбор обратной связи можно легко организовать через ту же Google Analytics:


extra:
    analytics:
        provider: google
        property: G-9ZRQD3E6FD
        feedback:
          title: Вам нравится эта страница?
          ratings:
            - icon: material/thumb-up-outline
              name: Да
              data: 1
              note: Спасибо! Я рада, что вам понравилось.
            - icon: material/thumb-down-outline
              name: Нет
              data: 0
              note: Жаль, что вам не понравилось...

В результате, внизу каждой странички появляется такая разметка:


<form class="md-feedback" name="feedback">
    <fieldset>
      <legend class="md-feedback__title">
        Вам нравится эта страница?
      </legend>
      <div class="md-feedback__inner">
        <div class="md-feedback__list">
            <button class="md-feedback__icon md-icon" type="submit" title="Да" data-md-value="1">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 9v12H1V9h4m4 12a2 2 0 0 1-2-2V9c0-.55.22-1.05.59-1.41L14.17 1l1.06 1.06c.27.27.44.64.44 1.05l-.03.32L14.69 8H21a2 2 0 0 1 2 2v2c0 .26-.05.5-.14.73l-3.02 7.05C19.54 20.5 18.83 21 18 21H9m0-2h9.03L21 12v-2h-8.79l1.13-5.32L9 9.03V19Z"></path></svg>
            </button>    
            <button class="md-feedback__icon md-icon" type="submit" title="Нет" data-md-value="0">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 15V3h4v12h-4M15 3a2 2 0 0 1 2 2v10c0 .55-.22 1.05-.59 1.41L9.83 23l-1.06-1.06c-.27-.27-.44-.64-.44-1.06l.03-.31.95-4.57H3a2 2 0 0 1-2-2v-2c0-.26.05-.5.14-.73l3.02-7.05C4.46 3.5 5.17 3 6 3h9m0 2H5.97L3 12v2h8.78l-1.13 5.32L15 14.97V5Z"></path></svg>
            </button>
        </div>
        <div class="md-feedback__note">
            <div data-md-value="1" hidden="">

              Спасибо! Я рада, что вам понравилось.
            </div>
            <div data-md-value="0" hidden="">

              Жаль, что вам не понравилось...
            </div>  
        </div>
      </div>
    </fieldset>
  </form>

В браузере это выглядит и работает так:



Результаты можно смотреть в GA, если добавить отчет по инструкции. Инструкция надежно спрятана в документации MkDocs под спойлером How to visualize the collected feedback ratings :-)


Яндекс Метрика


У веб-аналитики от Яндекса есть замечательная штука, позволяющая подсматривать за действиями отдельных пользователей — Вебвизор. Поэтому, захотелось добавить также и счетчик от Яндекса на сайт. Интересно же посмотреть, как читатели взаимодействуют с текстом, как скроллят, на чем задерживают внимание...


MkDocs не поддерживает аналитику от Яндекса напрямую. Но это значит лишь то, что добавление Яндекса потребует лишь чуть больше телодвижений по сравнению с Google.


Надо лишь создать файл overrides/main.html и добавить в него код счетчика, выданный Яндексом:


{% extends "base.html" %}

{% block analytics %}
    {{ super() }}
    <!-- Yandex.Metrika counter - вставить сюда -->
{% endblock %}

И затем добавить папку с "оверрайдами" темы material в конфиге mkdocs.yaml:


theme:
    name: material
    custom_dir: overrides

В результате, счетчик Яндекса появляется на каждой странице сразу после кода счетчика Google (добавленного ранее по-простому).


Социальные кнопки


Их можно добавить так:


extra:
    social:
    - icon: fontawesome/brands/telegram 
      link: https://t.me/lib_beliakova
      name: Канал в Telegram
    - icon: fontawesome/brands/youtube 
      link: https://www.youtube.com/@a-tunes
      name: Канал на YouTube

Кнопки отображаются в футере сайта на каждой странице.


Admonition-блоки (note, warning, info, etc...)


Мы придумали репостить новости с сайта в канале Telegram канал, чтобы читателям было на что подписаться. А чтобы донести это до читателей, добавили вверху на странице новостей заметную ссылку в admonition-блоке:


# Новости

!!! info "Новости в Telegram"
    Чтобы не пропускать новости, подпишитесь на [канал сайта в Telegram :fontawesome-brands-telegram:](https://t.me/lib_beliakova).

## 22.02.2023: Издан сборник моих произведений
...

Результат: https://lib-beliakova.github.io/news/


Поддержка admonitions включается в mkdocs.yaml Markdown-расширением admonition:


markdown_extensions:
  - admonition

Такие блоки также можно использовать в художественных целях. Если указать пустой caption блока, получаем просто разноцветные рамочки.


Пёстрая и шумная толпа окружила царевну. Со всех сторон светились и мигали светодиодами вывески, одна зазывающая реклама пыталась перекричать другую.

!!! note ""
    Лётный клуб "Волшебный мир": откройте мир с высоты полёта ковра!

!!! info ""
    Аптека Кощея Бессмертного: эликсир молодости по доступной цене от отечественного товаропроизводителя!

!!! warning ""
    Кафе "Скатерть-Самобранка". Блюдо дня: каша из топора!

!!! tip ""
    Гостиница "Избушка на курьих ножках": накормим, напоим и спать уложим!

!!! example ""
    Агрокомплекс "У дедки". Наша репка большая-пребольшая! 

Результат: https://lib-beliakova.github.io/tales/Masha-rasteryasha/


Admonition еще можно использовать, чтобы спрятать отгадку загадки "под спойлер". Раскрываемый спойлер получается, если начать блок с трех знаков вопроса, вместо трех восклицательных знаков.


Сам вода водицей,  
А кипятка боится...

??? info "Отгадка"
    Лёд

Результат: https://lib-beliakova.github.io/riddles/


"Ночной" режим


Сейчас многие сайты заботятся о глазках читателей, предоставляя опциональную возможность читать светлый тест на темном фоне. В MkDocs Material это несложно сделать:


theme:
    name: material
    palette:
    - media: "(prefers-color-scheme: light)"
      scheme: default
      toggle:
        icon: material/brightness-4
        name: Темное оформление
    - media: "(prefers-color-scheme: dark)"
      scheme: slate
      toggle:
        icon: material/brightness-7
        name: Светлое оформление

Результат:



Ссылки-кнопки


MkDocs умеет добавлять "заметные" ссылки, которые выглядят как крупные кнопочки. Например, вот так добавляется кнопка "читать" со значком раскрытой книжки:


[:octicons-book-16: Читать](coast-of-poetry.md){ .md-button }

Результат: https://lib-beliakova.github.io/poems/


В MkDocs "из коробки" доступно более 10000 иконок.


Модификация CSS


Встроенные стили можно легко кастомизить. Для этого надо добавить CSS-файл со своими стилями и подключить его в mkdocs.yaml:


extra_css:
    - extra-styles-v1.css

Номер версии в имени файла — это небольшой хак. При изменениях в стилях можно менять номер, и таким образом избежать "застревания" закешированных старых стилей в браузерах читателей.


Вот так, например, можно настроить "под себя" внешний вид кнопок:


.md-typeset .md-button {
  border-radius: 0.3rem;
  font-weight: 400;
  font-size: 0.7rem;
  padding: 0.25em 0.5em;
  color: var(--link-color);
}

А так — подтюнить внешний вид ссылок, для "светлой" и "темной" тем оформления:


:root {
    --link-color: #115BC0;
}
.md-typeset a,
.md-typeset a:hover {
    color: var(--link-color);
}
.md-typeset a:hover:not(.md-button) {
    text-decoration: underline;
}
[data-md-color-scheme="slate"] {
    --link-color: #58a6ff;
}

Вставка HTML


Вебвизор показал, что мобильные пользователи не всегда находят иконку "гамбургера" для открытия оглавления. Чтобы им помочь, я решил добавить на стартовую страницу дополнительную навигацию. В Markdown можно добавлять фрагменты HTML, которые будут переноситься в итоговый HTML "как есть". Чтобы было красиво, решил попробовать такой подход.


HTML: https://github.com/lib-beliakova/lib-beliakova.github.io/blob/main/docs/index.md?plain=1#L5-L42


CSS: https://github.com/lib-beliakova/lib-beliakova.github.io/blob/main/docs/extra-styles-v1.css#L39-L62


Результат: https://lib-beliakova.github.io/



Прошу не бросаться тапками, это мой чуть ли не первый опыт хоть какой-то верстки (лучше помогите советом). Тем не менее, в вебвизоре видно, что посетители сайта стали этим пользоваться и переходить к другим разделам.


Немного SEO


Файл sitemap позволяет оптимизировать сканирование нашего сайта поисковыми системами. MkDocs автоматически добавляет файл sitemap.xml в корень сайта. Но, для того чтобы sitemap содержал корректные URLы страниц, нужно добавить в mkdocs.yaml базовый URL сайта:


site_url: https://lib-beliakova.github.io/

Чтобы поисковики смогли обнаружить наш sitemap, добавляем в папку docs файл robots.txt со ссылкой на sitemap.xml:


User-agent: *
Sitemap: https://lib-beliakova.github.io/sitemap.xml

Особенности использования Markdown в художественных текстах


Пунктуация для прямой речи (тире)


В отличие от технической документации, художественные тексты изобилуют прямой речью. Для правильного выделения прямой речи нужно использовать тире. Чтобы моя юная писательница не заморачивалась каждый раз со вставкой символа, отсутствующего на клавиатуре, я добавил в конфиге расширение smarty:


markdown_extensions:
  - smarty

Оно автоматически превращает двойные дефисы в короткое тире (en dash), а тройные — в длинное (em dash).


Пример текста в маркдауне:


# Однажды, под нашим домом...

-- Нинка! Ты не представляешь, что я нашёл!

Я вздрогнула от Серёгиного вопля.

-- Не представляю, как и то, зачем ты так орёшь.

-- Ну хватит нудеть! Пошли покажу!

Результат: https://lib-beliakova.github.io/stories/under-our-house/


"Типографские" кавычки и многоточия


Заодно, расширение smarty превращает "компьютерные" прямые кавычки в "типографские", а троеточия, набранные тремя отдельными точками — в один символ ellipsis. На первый взгляд это кажется ерундой, но по факту здорово облегчает жизнь, если захочется напечатать настоящую книгу в настоящей типографии (спойлер: захотелось, напечатали).


Переводы строк в стихах


Для оформления стихов нужно уметь вставлять переводы строк, не заворачивать же каждую стихотворную строку в отдельный параграф. На первый взгляд, тут непонятно как поступить, ведь одиночный перевод строки в Markdown превращается в пробел в HTML. А двойной перевод строки создает новый параграф <p>.


Тем не менее, есть три способа добиться получения <br> в итоговом HTML:


  1. Закончить строку в Markdown двумя пробелами.
  2. Закончить строку в Markdown символом \ (т.е как бы заэкранировать последующий перевод строки).
  3. Просто вставить <br> в Markdown.

Мы выбрали первый способ. Хоть он и менее очевидный, но он сохраняет человекочитаемость текста в редакторе.


Пример оформления стиха в Markdown:



Тут я использую скриншот из VS Code, чтобы было видно пробелы, создающие переводы строк.


Результат: https://lib-beliakova.github.io/poems/it-was-autumn/


Иллюстрации


Рисовать в семье никто не умеет. Поэтому выбор пал на художника, о котором все только и говорят последнее время — Midjourney. Десятидолларовой подписки хватило, чтобы сделать полсотни рисунков и проиллюстрировать каждый стишок и рассказик на сайте.


Про работу с Midjourney уже написано множество статей, поэтому не буду вдаваться в детали. Чтобы добиться единого стиля иллюстраций, подходящего к детской литературе, к каждому запросу добавляли "watercolor painting style". Получились как-бы-акварельные рисунки, которые потом и для "физической" книги пригодились.


Аудио


Возникла идея сделать аудиосказки, чтобы к читателям добавились также слушатели. В Markdown нет аналога тега <audio>, поэтому для вставки плеера пришлось использовать чистый HTML: https://github.com/lib-beliakova/lib-beliakova.github.io/blob/main/docs/tales/Masha-rasteryasha.md?plain=1#L3-L9


Результат: https://lib-beliakova.github.io/tales/Masha-rasteryasha/


В этом примере плеер "завернут" в нестандартный admоnition типа "audio". Это, разумеется, не важно для работы плеера. Сделано чисто из эстетических соображений, чтобы у плеера была рамочка, подпись и иконка с наушниками. Не буду здесь рассказывать про реализацию дополнительного типа admoinition, чтобы не растягивать и без того длинную статью. Если кому интересно, пишите, расскажу в комментариях.


Заключение


Оказалось, что "нетехнический" писатель тоже может эффективно использовать Docs as Code для быстрой публикации своих произведений. В то же время, многие технические писатели "старой закалки" с осторожностью присматриваются к этому подходу. Смелее, ничего страшного тут нет, даже ребенок справляется :-) Я уже давно не помогаю и не стою на подхвате. Добавляет новый файл, сохраняет, затем коммит, пуш… И готово, новый контент в продакшне!


Также хочу передать благодарность Владиславу Himura за то, что в свое время познакомил с MkDocs.

Источник: https://habr.com/ru/post/720584/


Интересные статьи

Интересные статьи

На сайты-агрегаторы люди ходят вовсе не за тем, чтобы смотреть рекламу, но огромная аудитория гарантирует владельцу возможность получать регулярный заработок. Насколько эффективно можно монетизировать...
Как безопасно и без лишнего труда открыть доступ к этим учетным данным и использовать их при разработке на Node.js? Ответу на этот вопрос посвящена эта статья.Содержание:Подключение к экземпляру Kafka...
В первой части мы сформулировали, из каких компонентов состоит система автодополнения, обсудили способы ее использования и требования к качеству. Теперь давайте разберемся, зачем там нужно машинное об...
Понимание того, как MySQL использует память, является ключом к настройке системы для достижения оптимальной производительности, также как и для устранения неполадок в случаях ненормальног...
Примерно год назад мы в DataLine запустили сервис для поиска и анализа уязвимостей в ИТ-приложениях. В основе сервиса – облачное решение Qualys, про работу которого мы уже рассказывал...