updates
143
classes.py
@@ -1,17 +1,12 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import markdown
|
import markdown
|
||||||
import frontmatter
|
import frontmatter
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from logger import logger
|
from logger import logger
|
||||||
from config import Config
|
from config import Config
|
||||||
|
from jinja_env import env, content_item_template, index_template
|
||||||
# Prepare template rendering engine
|
|
||||||
env = Environment(loader=FileSystemLoader(Config.TEMPLATE_DIR))
|
|
||||||
env.globals['header_image'] = Config.HEADER_IMAGE
|
|
||||||
index_template = env.get_template("index.html")
|
|
||||||
content_item_template = env.get_template("content_item.html")
|
|
||||||
|
|
||||||
class DefaultFrontmatterHeader:
|
class DefaultFrontmatterHeader:
|
||||||
def __init__(self, title):
|
def __init__(self, title):
|
||||||
@@ -24,35 +19,115 @@ class DefaultFrontmatterHeader:
|
|||||||
class ContentItemPrototype:
|
class ContentItemPrototype:
|
||||||
def render_content(self):
|
def render_content(self):
|
||||||
logger.debug(f"Rendering {self.source_filename} to {Config.OUTPUT_DIR}/{self.target_filename}")
|
logger.debug(f"Rendering {self.source_filename} to {Config.OUTPUT_DIR}/{self.target_filename}")
|
||||||
if self.image_src_file.exists():
|
if self.image_file.exists():
|
||||||
shutil.copyfile(self.image_src_file, Path(Config.OUTPUT_DIR) / self.image)
|
shutil.copyfile(self.image_src_file, Path(Config.OUTPUT_DIR) / self.image)
|
||||||
if self.custom_css_src_file.exists():
|
if self.css_file.exists():
|
||||||
shutil.copyfile(self.custom_css_src_file,Path(Config.OUTPUT_DIR) / self.custom_css)
|
shutil.copyfile(self.custom_css_src_file,Path(Config.OUTPUT_DIR) / self.custom_css)
|
||||||
if self.custom_js_src_file.exists():
|
if self.js_file.exists():
|
||||||
shutil.copyfile(self.custom_js_src_file, Path(Config.OUTPUT_DIR) / self.custom_js)
|
shutil.copyfile(self.custom_js_src_file, Path(Config.OUTPUT_DIR) / self.custom_js)
|
||||||
with self.content_output_path.open("w", encoding="utf-8") as f:
|
with self.target_filename.open("w", encoding="utf-8") as f:
|
||||||
f.write(content_item_template.render(content_item = self, page_title = self.title))
|
f.write(content_item_template.render(content_item = self, page_title = self.title))
|
||||||
|
|
||||||
def __init__(self, md_file):
|
def parse_content(self):
|
||||||
logger.debug(f"Parsing {md_file}")
|
try:
|
||||||
self.source_filename = md_file
|
self.source_filename = Path(self.source_filename)
|
||||||
self.slug = md_file.stem
|
self.subdir = self.source_filename.parent
|
||||||
self.target_filename = self.slug + ".html"
|
logger.debug(f"Parsing item {self.source_filename}")
|
||||||
self.data = frontmatter.load(md_file)
|
self.slug = self.source_filename.stem
|
||||||
self.html = markdown.markdown(self.data.content)
|
self.target_filename = Path(f"{Config.OUTPUT_DIR}/{self.source_filename.parent}/{self.source_filename.stem}.html")
|
||||||
self.preview = self.html[:300] + "<a href=" + f"{self.slug}.html" + ">... read more</a>"
|
self.data = frontmatter.load(self.source_filename)
|
||||||
self.title = self.data.get("title", self.slug)
|
self.html = markdown.markdown(self.data.content)
|
||||||
self.omit_second_title = self.data.get("omit_second_title", False)
|
self.url = "<a href=" + f"{self.subdir}/{self.slug}.html" + "> ...read more</a>"
|
||||||
self.date = self.data.get("data", "2000-01-01T00:00:00+03:00")
|
self.preview = self.html[:300] + self.url
|
||||||
self.content_output_path = Path(Config.OUTPUT_DIR) / f"{self.slug}.html"
|
self.title = self.data.get("title", self.slug)
|
||||||
self.image_src_file = Path(f"{Config.CONTENT_DIR}/{md_file.stem}.jpg")
|
self.omit_second_title = self.data.get("omit_second_title", False)
|
||||||
self.image = f"images/{md_file.stem}.jpg" if self.image_src_file.exists() else None
|
self.date = self.data.get("date", "2000-01-01T00:00:00+03:00")
|
||||||
self.custom_css_src_file = Path(f"{Config.CONTENT_DIR}/{md_file.stem}.css")
|
self.image_file = self.source_filename.stem + ".jpg"
|
||||||
self.custom_css = f"static/css/{md_file.stem}.css" if self.custom_css_src_file.exists() else None
|
self.css_file = self.source_filename.stem + ".css"
|
||||||
logger.debug(f"Custom CSS: {self.slug}: {self.custom_css}")
|
self.js_file = self.source_filename.stem + ".js"
|
||||||
self.custom_js_src_file = Path(f"{Config.CONTENT_DIR}/{md_file.stem}.js")
|
except Exception as e:
|
||||||
self.custom_js = f"static/js/{md_file.stem}.js" if self.custom_js_src_file.exists() else None
|
logger.error(e)
|
||||||
logger.debug(f"Custom JS: {self.slug}: {self.custom_js}")
|
|
||||||
# Special handling for the 'about' item
|
def create_content(self):
|
||||||
if self.slug == 'about':
|
with open(self.source_filename, mode="w", encoding="utf-8") as f:
|
||||||
self.image = 'static/about.jpg'
|
f.writelines([
|
||||||
|
"---\n",
|
||||||
|
self.default_frontmatter_header.title,
|
||||||
|
self.default_frontmatter_header.date,
|
||||||
|
self.default_frontmatter_header.description,
|
||||||
|
self.default_frontmatter_header.author,
|
||||||
|
self.default_frontmatter_header.omit_second_title,
|
||||||
|
"---\n"
|
||||||
|
])
|
||||||
|
|
||||||
|
def __init__(self, filename, title = None):
|
||||||
|
self.source_filename = filename
|
||||||
|
self.title = title or self.source_filename.stem
|
||||||
|
self.default_frontmatter_header = DefaultFrontmatterHeader(self.title)
|
||||||
|
|
||||||
|
class SitePrototype:
|
||||||
|
def init_site():
|
||||||
|
logger.info("Initializing new site")
|
||||||
|
content_dir = Path(Config.CONTENT_DIR)
|
||||||
|
static_dir = Path(Config.STATIC_DIR)
|
||||||
|
templates_dir = Path(Config.TEMPLATES_DIR)
|
||||||
|
images_dir = Path(f"{Config.STATIC_DIR}/images")
|
||||||
|
css_dir = Path(f"{Config.STATIC_DIR}/css")
|
||||||
|
js_dir = Path(f"{Config.STATIC_DIR}/js")
|
||||||
|
# Create directories
|
||||||
|
for subdir in [content_dir, static_dir, templates_dir, images_dir, css_dir, js_dir]:
|
||||||
|
os.makedirs(subdir, exist_ok=True)
|
||||||
|
# Create templates from literals
|
||||||
|
import templates
|
||||||
|
template_names = [t for t in dir(templates) if not t.startswith('_')]
|
||||||
|
for template_name in template_names:
|
||||||
|
template_content = getattr(templates, template_name)
|
||||||
|
with open(templates_dir / f"{template_name}.html", "w", encoding="utf8") as f:
|
||||||
|
f.write(template_content)
|
||||||
|
# Create static/about.md
|
||||||
|
|
||||||
|
|
||||||
|
def build_site(self):
|
||||||
|
# Recreate the output dir if needed
|
||||||
|
if output_dir.exists():
|
||||||
|
shutil.rmtree(output_dir)
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
|
# Create public subdirs
|
||||||
|
subdirs = [img_dir, css_dir, js_dir]
|
||||||
|
for subdir in subdirs:
|
||||||
|
subdir.mkdir(arents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Copy static files if exist
|
||||||
|
if static_dir.exists():
|
||||||
|
shutil.copytree(static_dir, output_dir / "static", dirs_exist_ok=True)
|
||||||
|
|
||||||
|
# Parse the content files
|
||||||
|
content_items = []
|
||||||
|
for md_file in content_dir.glob("*.md"):
|
||||||
|
current_content_item = ContentItemPrototype(md_file)
|
||||||
|
current_content_item.render_content()
|
||||||
|
content_items.append({
|
||||||
|
"slug": current_content_item.slug,
|
||||||
|
"title": current_content_item.title,
|
||||||
|
"date": current_content_item.date,
|
||||||
|
# "preview": markdown.markdown(current_content_item.preview),
|
||||||
|
"image": current_content_item.image,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Render the index file
|
||||||
|
with (output_dir / "index.html").open("w", encoding="utf-8") as f:
|
||||||
|
f.write(index_template.render(page_title = Config.MAIN_PAGE_TITLE, content_items=content_items))
|
||||||
|
|
||||||
|
# Render the about file
|
||||||
|
about_content = ContentItemPrototype(Path('static/about.md'))
|
||||||
|
about_content.render_content()
|
||||||
|
|
||||||
|
# Move 'robots.txt' into output_dir
|
||||||
|
shutil.copyfile(static_dir / 'robots.txt', output_dir / 'robots.txt')
|
||||||
|
|
||||||
|
logger.info(f"Created {len(content_items)} content items.")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.output_dir = Path(Config.OUTPUT_DIR)
|
||||||
|
|
||||||
@@ -7,10 +7,7 @@ class Config:
|
|||||||
APP_DESCRIPTION = "Simplistic static site generator"
|
APP_DESCRIPTION = "Simplistic static site generator"
|
||||||
APP_SRC_URL = f"https://git.exocortex.ru/Exocortex/{APP_NAME}"
|
APP_SRC_URL = f"https://git.exocortex.ru/Exocortex/{APP_NAME}"
|
||||||
OUTPUT_DIR = 'public'
|
OUTPUT_DIR = 'public'
|
||||||
OUTPUT_IMG_DIR = f"{OUTPUT_DIR}/images"
|
TEMPLATES_DIR = 'templates'
|
||||||
OUTPUT_CSS_DIR = f"{OUTPUT_DIR}/css"
|
|
||||||
OUTPUT_JS_DIR = f"{OUTPUT_DIR}/js"
|
|
||||||
TEMPLATE_DIR = 'templates'
|
|
||||||
CONTENT_DIR = 'content'
|
CONTENT_DIR = 'content'
|
||||||
STATIC_DIR = 'static'
|
STATIC_DIR = 'static'
|
||||||
HEADER_IMAGE = 'static/header.jpg'
|
HEADER_IMAGE = 'static/header.jpg'
|
||||||
9
content.bak/about.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: 'about'
|
||||||
|
date: '2025-06-08 19:18:24'
|
||||||
|
description:
|
||||||
|
author:
|
||||||
|
omit_second_title: 'False'
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
BIN
content.bak/galatea.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
82
content.bak/galatea.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
date: '2025-06-04T13:24:45+03:00'
|
||||||
|
title: 'Галатея'
|
||||||
|
---
|
||||||
|
|
||||||
|
Самозабвенно, руки в кровь сбивая
|
||||||
|
О грубый и холодный мрамор,
|
||||||
|
Мечту прекрасную ваяешь,
|
||||||
|
Достойную великих храмов
|
||||||
|
|
||||||
|
Бессмертный совершенный образ
|
||||||
|
Творишь, богами вдохновлённый,
|
||||||
|
Изгибы нежные рисует
|
||||||
|
Резец твой, музами ведомый
|
||||||
|
|
||||||
|
И вот она на пьедестале застыла,
|
||||||
|
Будто время встало
|
||||||
|
Хитона складки приоткрылись,
|
||||||
|
Бедро игриво обнажая
|
||||||
|
|
||||||
|
Зовет, не издавая звуков,
|
||||||
|
Рукою белоснежной манит
|
||||||
|
— Иль это только наважденье
|
||||||
|
Неверным разумом играет?
|
||||||
|
|
||||||
|
Слова любви, молитвы, слезы,
|
||||||
|
Подарки, жертвы и проклятья
|
||||||
|
|
||||||
|
— Все тщетно: это просто мрамор,
|
||||||
|
Глупец! Какое с камнем счастье?!
|
||||||
|
|
||||||
|
Прочь! Вон из дома! В храм скорее!
|
||||||
|
Пред Афродитой преклониться:
|
||||||
|
Гимн в камне высек для тебя я —
|
||||||
|
Так пусть он в плоть переродится!
|
||||||
|
|
||||||
|
Перед тобой разверзлись волны
|
||||||
|
— Пред ней скала пусть растворится
|
||||||
|
Ты всех богов в себя влюбляешь,
|
||||||
|
А я… в творенье рук влюбился!
|
||||||
|
|
||||||
|
Святые своды звон наполнил
|
||||||
|
И заискрился воздух нежно:
|
||||||
|
“Ты удивил меня, Художник”, —
|
||||||
|
Богиня молвила небрежно, —
|
||||||
|
|
||||||
|
“Ты силой чувств меня растрогал,
|
||||||
|
Развеял скуку дней бессчетных.
|
||||||
|
Я дам тебе то, что ты просишь,
|
||||||
|
Чтоб не погиб в мечтах бесплотных”
|
||||||
|
|
||||||
|
Он, словно громом пораженный,
|
||||||
|
Назад в беспамятстве вернулся
|
||||||
|
Упал в бессильи у порога
|
||||||
|
И в сон глубокий окунулся
|
||||||
|
|
||||||
|
Вот Гелий жаркими лучами
|
||||||
|
Сна наважденье прогоняет
|
||||||
|
Лицо рукой прикрыв от света,
|
||||||
|
Глаза ваятель открывает
|
||||||
|
|
||||||
|
Убранство скромного жилища
|
||||||
|
Окинул взором безнадежным
|
||||||
|
И видит: нимфа примеряет
|
||||||
|
Пред зеркалом свои одежды
|
||||||
|
|
||||||
|
Боясь спугнуть оживший морок,
|
||||||
|
Он молвил шёпотом, робея,
|
||||||
|
Не веря в чудо Афродиты:
|
||||||
|
“Взгляни в глаза мне, Галатея!”
|
||||||
|
|
||||||
|
Воздушно дева обернулась,
|
||||||
|
Рукою локоны поправив,
|
||||||
|
Прошла, словно не видя, мимо
|
||||||
|
Его в безмолвии оставив
|
||||||
|
|
||||||
|
“Постой! Моей ты страстью дышишь”,
|
||||||
|
Схватил её он за запястье…
|
||||||
|
|
||||||
|
— Все тщетно: это просто мрамор,
|
||||||
|
Глупец! Какое с камнем счастье?!
|
||||||
|
|
||||||
147
content.bak/giotto.md
Normal file
147
content.bak/giotto.md~
Normal file
BIN
content.bak/lone_raider.jpg
Normal file
|
After Width: | Height: | Size: 242 KiB |
99
content.bak/lone_raider.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
date: '2025-06-04T13:24:45+03:00'
|
||||||
|
title: 'Призрачный всадник'
|
||||||
|
---
|
||||||
|
|
||||||
|
Слова мои, как кисть, рисуют на холсте небес
|
||||||
|
Холмов высоких очертанья, острой гранью лес
|
||||||
|
Далекий горизонт разрезал пополам
|
||||||
|
Вглядись туда, и ты его увидишь там…
|
||||||
|
|
||||||
|
На тех холмах, в мерцающих лучах заката,
|
||||||
|
Огромный силуэт верхом, закован в латы
|
||||||
|
По ветру плещет длинный темный его плащ
|
||||||
|
— О нем я поведу мой долгий грустный сказ.
|
||||||
|
|
||||||
|
Когда луна секреты шепчет в ухо ночи,
|
||||||
|
И ветер легкий этот шепот вдаль уносит,
|
||||||
|
Тень рыцаря, одетого в холодный звездный свет,
|
||||||
|
На призрачном коне, пронзает взором бездну лет
|
||||||
|
|
||||||
|
В доспехах черных и холодных, словно лед
|
||||||
|
Безмолвно чрез века свое проклятие несет
|
||||||
|
Подписан кровью страшный договор печали,
|
||||||
|
Закована душа в броню из твердой стали
|
||||||
|
|
||||||
|
Он вел вперед войска под знаменем с крестом
|
||||||
|
Разил врагов своим безжалостным мечом
|
||||||
|
Геенной огненной кругом пылали битвы
|
||||||
|
И Смерть взимала дань, под стоны и молитвы
|
||||||
|
|
||||||
|
За Гордость бился он, за Долг, за Короля,
|
||||||
|
— И реки крови не могла впитать зе
|
||||||
|
Неистовым копьем освобождал он мавров души
|
||||||
|
Рыдали ангелы, не в силах крики слушать
|
||||||
|
|
||||||
|
Однажды, средь руин и сотен мертвых тел
|
||||||
|
Лик девы он прекрасной бездыханной разглядел
|
||||||
|
И сердце рыцаря на миг остановилось:
|
||||||
|
Красивее лица во снах ему не снилось!
|
||||||
|
|
||||||
|
“О! Мой Господь! Не за тебя ль всю жизнь я бился?!
|
||||||
|
И меч мой верный меж врагов твоих ярился
|
||||||
|
Твой светлый стяг чрез все пустыни мира нес,
|
||||||
|
Не ведая сомнений, жалости, и слез!
|
||||||
|
|
||||||
|
Молю тебя: прости, что жизнь забрал
|
||||||
|
У Ангела, мой Бог! Клянусь, ведь я не знал!”
|
||||||
|
В ответ лишь тишина — и едкий крик ворон…
|
||||||
|
Накрыл плащом и рядом лег, убитый горем, он
|
||||||
|
|
||||||
|
Вот ночь спустилась пологом тяжелым, душным
|
||||||
|
И небо замерцало россыпью жемчужной
|
||||||
|
Ленивый месяц белый томно на боку лежит
|
||||||
|
У стен разрушенных Христово войско спит
|
||||||
|
|
||||||
|
Средь минаретов и домов сгустились тени
|
||||||
|
Встал рыцарь возле девы на колени
|
||||||
|
Сжал лезвие меча своей ладонью грубой
|
||||||
|
Кровь пролилась в навеки сомкнутые губы
|
||||||
|
|
||||||
|
“Раз Бог не хочет дать мне то, о чем молю
|
||||||
|
Иными силами я деву оживлю!
|
||||||
|
Пусть Господа завет святой нарушу,
|
||||||
|
Отдам я Дьяволу мятущуюся душу!
|
||||||
|
|
||||||
|
И с трепетом ресниц раскрылись веки
|
||||||
|
Глаза блеснули, словно в полнолунье реки
|
||||||
|
Откинув плащ его, на локте поднялась
|
||||||
|
Вздохнула и к нему всем телом подалась
|
||||||
|
|
||||||
|
Всмотрелась в незнакомый гордый лик
|
||||||
|
И вдруг кинжал изогнутый в руке ее возник
|
||||||
|
Как молния, сверкнул, как ветра дуновенье
|
||||||
|
Застыл у горла шевалье он на мгновенье
|
||||||
|
|
||||||
|
И бархат неба вспыхнул ярким тут огнем
|
||||||
|
И стало вдруг светло на миг, как будто днем…
|
||||||
|
И алой кровью оросив алтарь руин,
|
||||||
|
Руками горло сжав, пал навзничь паладин
|
||||||
|
|
||||||
|
С тех самых пор, невольник клятвы страстной,
|
||||||
|
Скитается в ночи’, эпохам неподвластный
|
||||||
|
В лесах дремучих и на сумрачных холмах
|
||||||
|
В пустынях призрачных, в безмолвных страшных с
|
||||||
|
|
||||||
|
Без устали и без надежды смотрит вдаль
|
||||||
|
Фигура рыцаря того, закованная в сталь
|
||||||
|
И ветер тихо шепчет паладина имя,
|
||||||
|
Когда при свете звезд земля от жара стынет…
|
||||||
|
|
||||||
|
Слова мои, как кисть, рисуют на холсте небес
|
||||||
|
Холмов высоких очертанья, острой гранью лес
|
||||||
|
Далекий горизонт разрезал пополам
|
||||||
|
Вглядись туда, и ты его увидишь там…
|
||||||
|
|
||||||
|
На тех холмах, в тускнеющих лучах заката,
|
||||||
|
Огромный силуэт верхом, закован в латы
|
||||||
|
Навечно проклят он бродить по свету,
|
||||||
|
Покуда помнят менестрели песню эту
|
||||||
BIN
content.bak/persian_night.jpg
Normal file
|
After Width: | Height: | Size: 73 KiB |
113
content.bak/persian_night.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
date: '2025-06-04T13:21:41+03:00'
|
||||||
|
title: 'Персидская ночь'
|
||||||
|
---
|
||||||
|
|
||||||
|
Над куполами ветер тихо шелестит в ночи
|
||||||
|
По стенам вьются кружева хуруфа
|
||||||
|
И месяц возлежит на ложе из парчи
|
||||||
|
|
||||||
|
Спит Шахин-Шах на шелке среди наложниц юных
|
||||||
|
На лбу его разгладились морщины
|
||||||
|
А вдалеке задумчиво поет канун
|
||||||
|
|
||||||
|
Чернее тьмы, шайтана породившей,
|
||||||
|
Крадется ассасин
|
||||||
|
— Один
|
||||||
|
|
||||||
|
Ковры и шкуры эхо глушат в длинных коридорах
|
||||||
|
Свет факелов в углах играет тенью
|
||||||
|
Негромкий стража хрип и тихий шорох,
|
||||||
|
|
||||||
|
Когда изогнутая сталь, чуть взвизгнув, взрежет тьму,
|
||||||
|
Бесшумно на ковер осядет тело
|
||||||
|
Небрежно на пол уронив с главы чалму
|
||||||
|
|
||||||
|
Давно визирь коварный плел узоры козней злых
|
||||||
|
И вот решил: пора владыке на покой
|
||||||
|
К советам стал он глух и скуп на похвалы
|
||||||
|
|
||||||
|
Совсем забыл он о родне, неблагодарным стал
|
||||||
|
И не дает в карман припрятать злато
|
||||||
|
Где ж это видано?! Пусть сдохнет, как шакал!
|
||||||
|
|
||||||
|
Чернее тьмы, шайтана породившей,
|
||||||
|
Крадется ассасин
|
||||||
|
— Один
|
||||||
|
|
||||||
|
Не скрипнет дверь, лишь пес огромный дернет ухом
|
||||||
|
Подкупленных наложниц след простыл
|
||||||
|
Скользнул убийца внутрь бесплотным духом
|
||||||
|
|
||||||
|
Пес первым был убит — мелькнул кинжал скупым размахом
|
||||||
|
И ассасин клинок из тонкой стали
|
||||||
|
Занёс над сердцем беззащитным шаха
|
||||||
|
|
||||||
|
Остановилось время на мгновенье,
|
||||||
|
До смерти только вдох
|
||||||
|
— Один
|
||||||
|
|
||||||
|
У изголовья силуэтом загустел Эфир
|
||||||
|
Ужасным Индрой, демоном насилья
|
||||||
|
"Бессмертная" возникла — и сверкнул шамшир
|
||||||
|
|
||||||
|
Движеньем плавным в миг с кинжалом руку отсекла
|
||||||
|
Залив шелк простыней обильно кровью
|
||||||
|
Под подбородком нежно сталью провела
|
||||||
|
|
||||||
|
Встал Шахов Шах, в руке сжимая золоченый меч.
|
||||||
|
"Прости, мой Царь, что сон твой потревожен.
|
||||||
|
Пёс мертв, но жизнь твою смогла я уберечь"
|
||||||
|
|
||||||
|
"Прощенья не проси, ведь ты достойна высшей чести
|
||||||
|
Мой бедный пёс... Он верным другом был мне
|
||||||
|
А этот душегуб — лишь раб. Я жажду мести!"
|
||||||
|
|
||||||
|
Слуг, Дана, позови и всё здесь прикажи убрать
|
||||||
|
А тело отнеси к визирю в спальню
|
||||||
|
Изменнику бесчестному подбрось в кровать!
|
||||||
|
|
||||||
|
И поутру его ко мне на завтрак притащи — нагим
|
||||||
|
Ползти заставь его червем по полу
|
||||||
|
Когда закончу есть, поговорю я с ним
|
||||||
|
|
||||||
|
На площади дворца горит костер священный
|
||||||
|
Шипит, плюясь, огонь душистым маслом
|
||||||
|
Вот, в клетку заточён, доставлен пленный
|
||||||
|
|
||||||
|
Совсем нагой, в крови и нечистотах
|
||||||
|
Вчера лишь — господин!
|
||||||
|
— Один
|
||||||
|
|
||||||
|
Шах подошел к нему, нахмурился брезгливо
|
||||||
|
Над площадью тотчас повисла тишина
|
||||||
|
Все ждут. Время течет неторопливо
|
||||||
|
|
||||||
|
"Ну что ж, сатрап, не на того ты замахнулся!
|
||||||
|
Меня убить отправил ассасина!"
|
||||||
|
— Шах замолчал, недобро ухмыльнулся
|
||||||
|
|
||||||
|
"Мой приговор запомнят люди в целом мире!
|
||||||
|
Палач с тебя живого снимет кожу!
|
||||||
|
— Обтянут ею кресло для визирей"
|
||||||
|
|
||||||
|
"Воительница Дана, жизнь спасшая Царю!
|
||||||
|
За верность твою в дар прими мой меч—шамшир
|
||||||
|
На острой глади — золотом: "Благодарю!"
|
||||||
|
|
||||||
|
"Я все сказал. Пусть мой приказ исполнят."
|
||||||
|
Промолвил и ушел.
|
||||||
|
— Один
|
||||||
|
|
||||||
|
Над куполами ветер тихо шелестит в ночи
|
||||||
|
По стенам вьются кружева хуруфа
|
||||||
|
И месяц возлежит на ложе из парчи
|
||||||
|
|
||||||
|
На бортике фонтана — фигура грустная Царя
|
||||||
|
Сидит один и тяжело вздыхает
|
||||||
|
Потрескивает тихо масло фонаря...
|
||||||
|
|
||||||
|
Правителей судьба незавидна, увы.
|
||||||
|
Мечты о подвигах, любви и славе,
|
||||||
|
А наяву — интриги, козни, в чаше — яд
|
||||||
|
И голова болит от мыслей о державе
|
||||||
9
content.bak/test2.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: 'test2'
|
||||||
|
date: '2025-06-08 20:16:33'
|
||||||
|
description:
|
||||||
|
author:
|
||||||
|
omit_second_title: 'False'
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
BIN
content.bak/whole_summer.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
3
content.bak/whole_summer.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
console.log("Custom JS loaded");
|
||||||
|
}, false);
|
||||||
299
content.bak/whole_summer.md
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
---
|
||||||
|
title: "Целое лето"
|
||||||
|
omit_second_title: False
|
||||||
|
---
|
||||||
|
<small style="font-family: Lucida Console; font-size: 0.8em;">
|
||||||
|
By reason or force / Stripe summer / School’s out and so are you
|
||||||
|
</small>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
|
||||||
|
Далеко внизу лязг контейнеров, грохот техники и выкрики рабочих сливались в равномерный гул. Порт Вальпараисо был довольно тихим – в звуковом диапазоне. Его настоящий шум был электронным.
|
||||||
|
И уровень этого шума зашкаливал. Не только пристань для множества судов, от гигантских лайнеров до неприметных частных яхт без иллюминаторов – порт, с его скоростными соединениями, оптоволоконными линиями и спутниковыми аплинками, представлял собой огромный информационный хаб. Всем требовалась связь. Конечно, каждый хотел знать, о чем все говорят, и все понимали, что каждый пытается подслушивать. Порт Вальпараисо постоянно находился в центре пристального внимания. На него были направлены сотни антенн, сенсоров и сканеров. Так много, что один человек с портативным оборудованием легко оставался незамеченным.
|
||||||
|
Маленький наушник щелкнул, оживая, и голос Реджи произнес:
|
||||||
|
— Как успехи? Какие удивительные известия ждут нас в киберпространстве?
|
||||||
|
Его голос вызывал в воображении твидовый пиджак, английскую кепку, возможно, молоток для игры в крокет – голос человека, который владеет несколькими поместьями, и никогда их не посещает.
|
||||||
|
— Надо было взять бикини. — ответила Ольга, щурясь от жгущего солнца.
|
||||||
|
— Сейчас январь.
|
||||||
|
— И оказывается, здесь лето! Ты знал?
|
||||||
|
— Я забыл. — сказал Реджи.
|
||||||
|
— Я тоже. Вот про часовые пояса я всегда помню. — сказала Ольга.
|
||||||
|
— Часовые пояса еще хуже. Из-за часовых поясов я никогда не путешествую. — сказал Реджи.
|
||||||
|
— Правда?
|
||||||
|
— Правда. Менять часовой пояс вредно для мозга.
|
||||||
|
— Джетлаг? Проблемы со сном?
|
||||||
|
— Проблемы со _снами_, — уточнил Реджи. — Перемещение в другой часовой пояс нарушает сны.
|
||||||
|
— Это джетлаг.
|
||||||
|
— Это не джетлаг. Слушай, ты когда-нибудь задумывалась, как появились разные религии и почему одни так похожи на другие?
|
||||||
|
— Честно говоря, не особенно. Подожди.
|
||||||
|
Ольга склонилась над портативным терминалом, на экране которого мерцали строчки сообщений.
|
||||||
|
— Что там? — спросил Реджи.
|
||||||
|
— Ничего. Показалось.
|
||||||
|
— Очень жаль.
|
||||||
|
— Продолжай. Джетлаг и различные религии.
|
||||||
|
— Не джетлаг.
|
||||||
|
— Хорошо, не джетлаг.
|
||||||
|
— Так вот, часовые пояса влияют на наши сны. Тем, кто в одном часовом поясе, снятся схожие сны.
|
||||||
|
— Ты только что это выдумал?
|
||||||
|
— Это наука, друг мой.
|
||||||
|
— Наука утверждает, что люди в одном часовом поясе видят одни и те же сны?
|
||||||
|
— Структурно схожие, не одинаковые.
|
||||||
|
— И почему так происходит?
|
||||||
|
— Ну, предположим, сны, которые нам снятся, представляют собой бесконечное счётное множество.
|
||||||
|
— Что?
|
||||||
|
— Бесконечное счётное множество. Плохая связь? Я пропадаю?
|
||||||
|
Ольга засмеялась.
|
||||||
|
— Нет, ты не пропадаешь. Продолжай.
|
||||||
|
— Отобразим это множество на время суток, когда мы спим. Тогда часовой пояс становится порождающим элементом. Это особый элемент, который...
|
||||||
|
— Генератор.
|
||||||
|
— Генератор. — согласился Реджи.
|
||||||
|
— Я знаю, что такое генератор в алгебраической группе.
|
||||||
|
— Я знаю, что ты знаешь, но повторить лишний раз стоило. Так вот, этот элемент создаёт всю группу через операцию, скажем, сновидения. Ты же согласна, что сны – это не просто случайный процесс, а трансформации, да?
|
||||||
|
— Да-а. — протянула Ольга.
|
||||||
|
Она пошевелилась, горячая кровельная черепица скрипнула.
|
||||||
|
Ольга снова посмотрела на терминал и сказала:
|
||||||
|
— Подожди.
|
||||||
|
— Наверно, лучше не отвлекать тебя. — сказал Реджи.
|
||||||
|
— Наверно. — согласилась Ольга, и потом добавила — Но мы поговорим о снах еще.
|
||||||
|
— Конечно. — сказал Реджи.
|
||||||
|
Наушник замолчал.
|
||||||
|
Она снова сгорбилась над экраном и прокрутила вверх и вниз сообщения, которыми суда обменивались с портом. Сплошной электронный шум, как и бывает в сетях с высоким трафиком. Многие системы были настроены торопливо, сервисными командами, которым не платили по часам. Сотни сообщений отправлялись повторно, снова и снова, из-за ошибок в протоколе. Все зашифрованы и прочесть их невозможно, но Ольга и так знала, что там: заявки на оформление судов, заказы бронирования. Декларации о доставке, подробно описывавшие чьи-то бесчисленные грузы, выделялись из общей массы, как самые аккуратные. Даже контрольные суммы были старательно дополнены. Но даже их кому-то приходилось отправлять несколько раз, прежде чем порт соглашался их принять.
|
||||||
|
Терминал не показывал ничего, что не было бы новым ложным срабатыванием.
|
||||||
|
Она уставилась на экран.
|
||||||
|
Декларации действительно выглядели очень аккуратно, как будто, для разнообразия, те, кто их отправляли, знали протокол, и следовали правилам – строго. Очень строго.
|
||||||
|
Слишком строго.
|
||||||
|
Ольга сощурилась.
|
||||||
|
Контрольные суммы было не обязательно дополнять до предельной длины, протоколы этого не требовали. Да и все остальные этим не утруждались.
|
||||||
|
Она перепроверила.
|
||||||
|
Дополнения контрольных сумм делали их достаточно длинными, чтобы в них поместились настоящие данные. Если бы кто-нибудь хотел передать сообщение так, чтобы его было нельзя не только прочитать, но даже обнаружить, то в эти тщательно сделанные дополнения как раз можно поместить небольшую шифрограмму.
|
||||||
|
Она стукнула пальцем по наушнику:
|
||||||
|
— Я знаю, как они это делают. Они прячут…
|
||||||
|
— Два “Спринтера” въехали на твою улицу, по одному с каждой стороны. — перебил ее Реджи. Его голос звучал встревоженно. Ольга не любила, когда он тревожился.
|
||||||
|
— Кто? — быстро спросила она.
|
||||||
|
— Не знаю, но вооружены.
|
||||||
|
— _Сегуридад_?
|
||||||
|
— Они предупредили бы, но ничего не говорили. Слушай, эти ребята двигаются к твоей позиции.
|
||||||
|
Последовала очень короткая пауза, потом Реджи сказал:
|
||||||
|
— _Сегуридад_ не знают, что происходит. Надо уходить.
|
||||||
|
— Можешь не уговаривать.
|
||||||
|
Ольга захлопнула терминал, запихнула в сумку вместе с антеннами, спрыгнула вниз через люк и побежала к выходу.
|
||||||
|
Снаружи кулаки в армированных перчатках забарабанили по металлической двери. Кто-то закричал “Полиция! Открывай! _Полисия эспесьяль_!”
|
||||||
|
Она выругалась шипящим шепотом, бросилась назад. Забралась на стол, подпрыгнула, схватилась за края люка, подтянулась и снова вылезла на крышу. Под ногами заскрипел асбест и редкий гравий. Она побежала к отвесному склону холма, поднимавшемуся почти из стены здания, и вскарабкалась наверх.
|
||||||
|
Внизу с лязгающим грохотом упала железная дверь, которую сбили с петель, но Ольга уже бежала вверх по пустой извивающейся улочке. За ее спиной раздались выкрики.
|
||||||
|
— Поверни налево. — сказал голос Реджи.
|
||||||
|
Она повернула.
|
||||||
|
Возможно, это действительно была полиция, но возможно, нет. Плохо и то, и другое. Кто бы это ни был, они преследуют ее. Это еще хуже.
|
||||||
|
Сумка с оборудованием била по спине.
|
||||||
|
— Снова налево. — сказал Реджи. — К площади.
|
||||||
|
Площадь была заполнена людьми. Многие танцевали, стучали барабаны, развевались флаги. Прошла фигура на высоких ходулях, а за ней жонглеры. Какой-то местный праздник или карнавал.
|
||||||
|
Ольга смешалась с толпой – очередная туристка, одетая не по погоде, вся в поту. Танцующие женщины в ярких костюмах и плюмажах из высоких перьев окружили ее, и через минуту исчезли. Совсем рядом кто-то захохотал, почти ей в лицо. Это был счастливый, заразительный смех. Музыка становилась громче. Ольга двигалась в людском потоке несколько кварталов, затем выскользнула в узенький переулок.
|
||||||
|
— Окей. — сказал Реджи. — Пока чисто.
|
||||||
|
— Разве это не здорово. — ответила она.
|
||||||
|
— Давай в сейфхаус. Я пока выясню, что смогу.
|
||||||
|
— Нет. — сказала Ольга. — Выясни больше.
|
||||||
|
Никто не пытался ее остановить. Никто даже не шел следом. Вскоре ее уже уносил поезд метро.
|
||||||
|
Вагон был почти пустой. Ольга разглядывала наклейку на стене – флаг, герб и девиз “Разумом или силой” – и вспоминала, сколько камер наблюдения ее видели и записали.
|
||||||
|
Вошли четверо копов в форме. Она глянула на них из-под капюшона, не поворачивая головы. Копы прошли мимо и сели через ряд, продолжая разговор. Судя по обрывкам, которые Ольга могла разобрать, речь шла о перелетных птицах.
|
||||||
|
Она вышла на ближайшей станции.
|
||||||
|
Солнце было еще высоко, но тени уже делались длиннее и воздух колебался волнами вечернего жара. Ольга осторожно обошла квартал по кругу, потом еще раз, и только после этого подошла к двери. Наушник щелкнул.
|
||||||
|
— Это действительно была полиция. — сказал Реджи. — И они все еще тебя ищут. Не лично тебя, конечно. Просто того, кто был там в это время. Примерно твоего роста, примерно в твоей одежде.
|
||||||
|
— Сразу стало гораздо спокойнее. — ответила она.
|
||||||
|
Она наконец сняла сумку с терминалом, положила ее на стол и осмотрелась. Стол, стул, кровать – сейфхаус был не особенно богат мебелью. Она и не собиралась долго там оставаться.
|
||||||
|
За окном взвыла сирена полицейской машины, пронеслась рядом и затихла вдали.
|
||||||
|
— Давай рассуждать. — сказал Реджи. — _Сегуридад_ обещают, что сейчас сделают несколько звонков и буря уляжется, но если честно, я так не думаю. Те ребята приехали, потому что знали, что ты там. Знали, где искать.
|
||||||
|
— Да, меня тоже это удивило. — сказала Ольга.
|
||||||
|
— Как будто внезапно появился новый игрок. Или кто-то мутит воду, чтобы проверить, как мы себя поведем. Даже не знаю, что мне нравится меньше.
|
||||||
|
— А есть разница?
|
||||||
|
— Слушай. — сказал Реджи. — Слушай, в этот раз была вооруженная полиция. В следующий раз будет кто-то ещё хуже. Кажется, на тебя открыли охоту.
|
||||||
|
— Съездишь в Чили, будет как отпуск, даже не работа. — пробормотала Ольга.
|
||||||
|
— Вот именно. — согласился Реджи. — Будь осторожной, ладно? Окей, давай теперь хорошие новости. Ты поняла их способ коммуникации. Как это устроено?
|
||||||
|
Ольга не ответила. У нее возникло чувство, что на нее смотрят. Она резко обернулась.
|
||||||
|
Наклонив голову на бок, ее пристально разглядывала молодая женщина, одетая в черную форму без опознавательных знаков. Очень загорелая, коротко стриженная, под левым глазом короткие глубокие шрамы, как после установки тактического импланта, ТНИ-2 или что-нибудь поновее.
|
||||||
|
Но ее лицо Ольга узнала сразу же.
|
||||||
|
— Марта? — хотела спросить она. — Что ты здесь делаешь?
|
||||||
|
Хотя все было понятно и так, и стало окончательно ясно, когда в руке Марты появился нож и она метнулась вперед.
|
||||||
|
Ольга увернулась, прыгнула в сторону, потом в другую, свалила Марту с ног и покатилась с ней по полу, стараясь держать опасную руку подальше. Она оказалась сверху, придавила руку с ножом, ударила Марту в лицо и била до тех пор, пока та не перестала шевелиться. Потом заметила, что вокруг все в крови, и что эта кровь течет из ее бока. Она зажала бок, но кровь шла еще откуда-то. Она слезла с неподвижной Марты, отползла в сторону и прислонилась к стене.
|
||||||
|
— Ольга, что происходит? Ольга! — голос Реджи доносился как будто откуда-то издалека.
|
||||||
|
— Подожди. — беззвучно сказала она.
|
||||||
|
Остановить кровь не получилось. Комната наклонилась вбок и вверх, в глазах стало темнеть, и она отстраненно подумала, что сейчас, наверно, перед ней пронесется вся жизнь, но жизнь не пронеслась, и вообще ничего не случилось. Она только вспомнила, что однажды уже хотела по-настоящему убить Марту – очень давно, в другом месте, и, считай, в другой жизни. Причина ссоры забылась, но в тот день, когда они впервые увидели друг друга, Ольга ударила ее. Никто не мог точно сказать, что именно Марта перед этим сделала, хотя все соглашались: это был какой-то плохой поступок, и ударить ее санками по голове было вполне естественно. Произошел скандал, сопровождавшийся громким плачем, несколько слов прозвучали на повышенных тонах, но всему этому недоставало решительности, и кто-то даже сказал "А так ей и надо". Спустя пару дней они снова встретились на детской площадке и после нескольких минут взаимного недоверия выяснили, что обе интересуются динозаврами, и обе согласны, что велоцираптор самый лучший. Вскоре было объявлено, что они пойдут в одну школу и будут учиться в одном классе.
|
||||||
|
Школа оказалась самым интересным на свете – но только на первое время. Через три или четыре года новизна ушла, и непонятным образом получилось, что это место скучное и не очень-то приятное. Ольга поняла, что предпочитает лето, главным образом потому что не надо сидеть на уроках, и не надо ходить потом в спортивную секцию. Конечно, Марте доставалось даже больше, у нее в придачу ко всему по субботам были еще и занятия танцами. Танцы Марта презирала и, как сама говорила, лучше бы пошла в тир. Она даже сделала рогатку и постоянно носила ее с собой. В общем, и та, и другая считали дни до конца учебного года.
|
||||||
|
Кроме того, летом ездили в деревню, и, в отличие от города, жизнь там была очень насыщенная.
|
||||||
|
Там имелся кинотеатр. Обычно зрительный зал был закрыт, зато у входа стояли игровые автоматы. Их никогда не включали, но кнопки нажимались и рули крутились, так что если применить воображение, весь процесс ничем не отличался от настоящей игры.
|
||||||
|
Рядом был маленький парк аттракционов. Можно было кататься на карликовом колесе обозрения, только кому-то приходилось забраться на коробку с мотором и крутить ремень передач.
|
||||||
|
Также имелся песчаный карьер, и еще огромная ржавая труба лежала прямо посреди луга. Насчет происхождения и назначения трубы строились самые невероятные догадки и гипотезы. Наиболее научная утверждала, что это часть космической ракеты, упавшая с орбиты. Идея принадлежала Марте, и косвенные подтвержения этой версии приходилось признать. Впрочем, собственное предположение Ольги – труба должна стать частью длинного подземного тоннеля под рекой – было не хуже.
|
||||||
|
В любом случае, труба была хорошей полезной вещью. Можно было ходить по ней и в ней, кидать камни или кричать внутрь, чтобы получалось эхо. Если же каких-нибудь двух пешеходов, гулявших по лугу, заставал дождь на пути домой, в трубе им находилось укрытие.
|
||||||
|
Других труба тоже привлекала. Это была компания соседских, по большей части знакомых, которых регулярно встречали по дороге на реку или в так называемый центр. С ними не дружили, но и не враждовали, обе стороны признавали взаимное существование, и оставляли друг друга в покое.
|
||||||
|
По большей части.
|
||||||
|
Однажды, когда уже надо было уходить из парка с аттракционами и идти обедать, эта компания появилась, как бы принимая пост на микроскопическом колесе обозрения. Ольга молча глянула на них, как делала всегда, и ей ответили тем же, а когда расстояние достаточно увеличилось, было выкрикнуто какое-то слово и все засмеялись. Это слово Ольге было неизвестно, но звучало оно не очень, а потом его повторили еще раз и все снова засмеялись. Ольга повернулась к Марте:
|
||||||
|
— А что это значит?
|
||||||
|
Та была бледная от злости.
|
||||||
|
В руках у неё появилась рогатка, в воздухе свистнуло. Кто-то коротко вскрикнул.
|
||||||
|
Марта стала белая как бумага. Быстрыми шагами она подошла к компании. Те попятились. Один из них, самого низкого роста, зажимал лицо ладонью. Она силой убрала его руку. Глаз был цел, под ним набухал маленький багровый след в форме подковы.
|
||||||
|
— Не ной, все нормально. — бросила она, и повернулась к Ольге. — Пошли. Пошли, все нормально.
|
||||||
|
Но все было далеко не нормально. Очень скоро на них уже громко кричали. Упоминалась милиция. Пострадавшего звали Марк, и его лишь чудом не покалечили. Марту посадили под домашний арест. Ей запретили выходить из дома, и даже из комнаты. Рогатку отобрали и уничтожили.
|
||||||
|
— Я все равно новую собиралась сделать. — пробурчала она.
|
||||||
|
Ольга тоже провела некоторое время в комнате, но ей сказали, что она-то ни в чем не виновата и может ходить куда угодно. Вечером ей даже сказали:
|
||||||
|
— “Вспомнить всё” идет. Будешь смотреть? Вы хотели.
|
||||||
|
— Мы потом посмотрим. — вежливо ответила она.
|
||||||
|
Утром она подкараулила Марка, когда тот был один, и молча разбила ему нос. На следующий день она сделала то же самое, а на третий Марта остановила ее в дверях.
|
||||||
|
— Не бей его больше. — сказала она.
|
||||||
|
— Почему это? — спросила Ольга. — Пусть родителям не стучит.
|
||||||
|
— Потому это. — сказала Марта. — Во-первых, он не стучал. Он сказал им, что разбирал велосипед, и что-то отлетело.
|
||||||
|
— И ты поверила?
|
||||||
|
— Да. — сказала Марта. — А во-вторых, он сказал, что тоже тебе даст. Что тебе мало не покажется.
|
||||||
|
Ольга фыркнула.
|
||||||
|
— Пусть попробует.
|
||||||
|
— А в третьих, мы помирились.
|
||||||
|
Это было отвратительное, низкое предательство, но Ольга только равнодушно сказала:
|
||||||
|
— Все понятно.
|
||||||
|
— Что тебе понятно? — спросила Марта.
|
||||||
|
— Все мне понятно.
|
||||||
|
Через несколько дней они пошли в старый карьер, забрались на самую высокую вершину, и ногами сталкивали вниз куски слежавшегося песка, чтобы получались лавины. Лавины сметали все на пути, падали на воображаемые города и деревни внизу и оставляли только руины.
|
||||||
|
— Не придет. — сказала Ольга. — Пошли лучше в парк.
|
||||||
|
— Придет. — ответила Марта.
|
||||||
|
— Орел – придет, решка – не придет. — Ольга бросила монетку и посмотрела, что выпало.
|
||||||
|
— Решка. — она продемонстрировала результат, показывая, что не жульничает. — Не придет.
|
||||||
|
— Придет. — ответила Марта.
|
||||||
|
Внизу появилась маленькая фигурка.
|
||||||
|
— Фома неверующая. — сказала Марта, и поехала по склону.
|
||||||
|
Ольга немного посидела на вершине в одиночестве, потом встала на ноги и пошла вниз.
|
||||||
|
Маленькая фигурка ждала и не двигалась.
|
||||||
|
Ольга остановилась в паре шагов. Они посмотрели друг на друга.
|
||||||
|
— Можешь мне тоже врезать, если хочешь. — сказала Ольга.
|
||||||
|
— Я человек интеллектуальной культуры. — ответил Марк.
|
||||||
|
— А вообще умеешь? — спросила Ольга.
|
||||||
|
Тот пожал плечами.
|
||||||
|
— Пойдете на свалку? — предложил он.
|
||||||
|
— А что там? — Марта сняла с ноги кроссовок и вытряхнула песок.
|
||||||
|
— Разное. Дохлые коровы. И бывают пузыри из газа. Их можно поджигать – если есть чем. Только это за шоссе, вон туда. — Марк показал вдаль рукой.
|
||||||
|
Ольга переглянулась с Мартой.
|
||||||
|
Поджечь газ не удалось, и дохлых коров не видели. Они обнаружили лужу какой-то омерзительной жижи, которая тоже не загорелась. Еще им попались две курицы с отрезанными головами.
|
||||||
|
— Вымерли и лежат в грунте, как динозавры. — сказал Марк.
|
||||||
|
— А они и есть динозавры. — возразила Марта.
|
||||||
|
Конечно, все это не годилось для школьного сочинения. Там полагалось писать о восходах и закатах, и о прогулках в лесу. Раньше Ольга пыталась спорить с этими правилами, но со временем перестала. Так что особенно написать было нечего.
|
||||||
|
Рядом Марта выводила строчку за строчкой каллиграфическим почерком и уже начала третью страницу. Ольга грызла ручку, глядела в окно, через мутное стекло: падающий лист, черно-белая ветка голого дерева, толстая пчела в тяжелом гулком полете.
|
||||||
|
Другие занятия были такими же безынтересными, за исключением, пожалуй, алгебры, наименее унылой из всех.
|
||||||
|
Большие надежды возлагались однажды на информатику, и вначале чертить логическую схему было интересно, но затем оказалось, что двадцать человек сидят около выключенных компьютеров и разглядывают использованную старую перфокарту, каждое отверстие которой объясняется в величайшей подробности. После такого даже спортивная секция казалась глотком свежего воздуха.
|
||||||
|
Наконец Марта смогла дать положению дел точную оценку.
|
||||||
|
— Слава богу. — сказала она. — Слава богу, можно до сентября теперь об этом дебилизме не думать.
|
||||||
|
— Ага. — сказала Ольга.
|
||||||
|
Она бросила взгляд в сторону двухэтажного домика в конце улицы.
|
||||||
|
— Можешь не смотреть, еще не приехали. — сказала Марта.
|
||||||
|
— Приедут.
|
||||||
|
— Приедут. — согласилась Марта.
|
||||||
|
— А мы, значит, в этот раз первые.
|
||||||
|
— Похоже на то.
|
||||||
|
Тишину этих вечеров нарушали только цикады, которых все, даже – удивительно – Марта, называли сверчками.
|
||||||
|
Постепенно шумов делалось больше, в домах зажигались окна, и по ночам застучала музыка.
|
||||||
|
|
||||||
|
— Добрый день, девушки. — сказал Марк.
|
||||||
|
— Наконец-то! — воскликнула Марта — Вот эта заждалась уже. Хочет дать тебе в рожу, видимо.
|
||||||
|
— Да? — спросили Марк. — А ты?
|
||||||
|
— Я бы _никогда_!
|
||||||
|
— Привет. — сказала Ольга. Она заулыбалась и с этим ничего нельзя было поделать.
|
||||||
|
— Вы как, уже придумали что-нибудь на сегодня? — спросил Марк. — А то есть лодка.
|
||||||
|
— Мы за. — сказала Ольга.
|
||||||
|
Марта уточнила:
|
||||||
|
— Моторная? Что за лодка?
|
||||||
|
— Нет, Марта, не моторная. Это лодка. В которой надо грести веслами.
|
||||||
|
— Мы за. — сказала Ольга.
|
||||||
|
Марк посмотрел на неё сверху вниз и подмигнул.
|
||||||
|
— Настоящий друг. — сказал он.
|
||||||
|
Марта наклонила голову набок.
|
||||||
|
— А ты когда это так загорел? — спросила она. — Вы же только приехали.
|
||||||
|
— В Египте. — ответил Марк.
|
||||||
|
— В Египте? — переспросила Ольга.
|
||||||
|
— Ездили в марте на неделю.
|
||||||
|
— Ничего себе. — сказала Марта.
|
||||||
|
|
||||||
|
|
||||||
|
Она снова отметила про себя, что шрам в конце концов получился очень красивый: в меру широкий, в удачном месте, и похожий на белую змейку.
|
||||||
|
– Давай. – согласился Марк.
|
||||||
|
– Не говори так девушкам. – сказала Марта. – Нам еще можно, мы еще поймем. А другим не говори.
|
||||||
|
|
||||||
|
Ольга повернулась в сторону берега, сложила ладони рупором и заорала:
|
||||||
|
– Эй, городские! А ну пошли нахер с моего пляжа!
|
||||||
|
С берега замахали и закричали что-то в ответ.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
— Думаете, трактор когда-нибудь переедет поле? — спросил Марк.
|
||||||
|
— Какой трактор? — сказала Марта.
|
||||||
|
Марк кивнул на открытое окно.
|
||||||
|
|
||||||
|
— Я не виновата, что у нас окна выходят в эту сторону. — возмутилась Марта.
|
||||||
|
— А я ничего не говорю. — сказала Ольга.
|
||||||
|
Марта посмотрела на Марка:
|
||||||
|
— Хотя можем к тебе пойти.
|
||||||
|
|
||||||
|
Ольга наклонила голову и высунула кончик языка. От него отделилась ниточка слюны и попала внутрь приставки. Ольга размазала ее пальцем.
|
||||||
|
— Ты плюешь туда, что ли? — Марта была так поражена, что даже отставила в сторону лак для ногтей.
|
||||||
|
— Чтобы контакты очистить. Так делают. — ответила Ольга.
|
||||||
|
— Так делают дебилы. — сказала Марта. — Кстати, знаешь, почему дебилы всегда спрашивают “почему”?
|
||||||
|
— Почему? — спросила Ольга, не отрываясь от приставки.
|
||||||
|
Марта наклонила голову на бок и посмотрела на нее пристальным изучающим взглядом. Потом снова стала красить ногти.
|
||||||
|
— Ее уже не починить. Она же сгорела в прошлом году, даже дымилась, помните? — сказал Марк. — Это бессмысленно.
|
||||||
|
— Как и вся жизнь. — сказала Ольга.
|
||||||
|
— Как и вся ты. — сказала Марта. — Чего ты вообще к ней прицепилась? Она _старая_. И вся _грязная_.
|
||||||
|
Ольга пожала плечами.
|
||||||
|
— Вдруг захотела снова поиграть. Хорошо было бы снова поиграть, правда?
|
||||||
|
— Ну, наверно. — сказала Марта.
|
||||||
|
Она поставила лак на стол и быстро потрясла руками в воздухе, чтобы ногти скорее подсохли.
|
||||||
|
— Так, _я_ готова.
|
||||||
|
|
||||||
|
|
||||||
|
— Мне уже восемнадцать. — сказала Марта. — Я уже здесь самая взрослая.
|
||||||
|
|
||||||
|
Внизу громко спорили. Ольга подошла к лестнице и прислушалась. Большую часть криков издавала Марта. Ольга спустилась по лестнице.
|
||||||
|
— Мне уже шестнадцать! — крикнула Марта. Ей ответили, что просто быть шестнадцатилетней недостаточно.
|
||||||
|
— Я не хочу ехать! Это тупо!
|
||||||
|
Она как будто пыталась сдвинуть каменную стену, и та не подавалась ни на миллиметр. Ольга подошла и встала рядом.
|
||||||
|
— Это дебилизм! — снова крикнула Марта.
|
||||||
|
Ольга быстро собралась с мыслями и подняла руку:
|
||||||
|
— Если можно, я бы объяснила…
|
||||||
|
Ей было позволено объяснить.
|
||||||
|
Это не дало результата, на который Ольга рассчитывала. Она подумала еще секунду, и сказала:
|
||||||
|
— В этом мире девушки все равно что гусеницы, потом они станут бабочками или мотыльками, но сперва надо превратиться в куколку или там в личинку, у всех по-разному, но судьба общая, так пишет мсье Бюффон в той большой книге, что стоит на полке в соседней зале.
|
||||||
|
Она обвела взглядом слушателей.
|
||||||
|
— Мы ответственные гусеницы и намерены стать хорошими бабочками. На два дня вы можете со спокойной душой оставить нас здесь.
|
||||||
|
Им был предъявлен список условий и требований – быть дома не позже десяти часов, не шляться ночью, категорически никакого алкоголя.
|
||||||
|
Обе с энтузиазмом закивали.
|
||||||
|
|
||||||
|
— Я тут сидеть не буду. — заявила Марта и показала на белоснежные брюки. Марк демонстративно вздохнул – как сделал бы человек, бесконечно уставший от капризов.
|
||||||
|
— Пойдём в лодку. — сказал он. — В лодке будешь сидеть?
|
||||||
|
— Конечно. — сказала Марта. — Лодка же _чистая_.
|
||||||
|
|
||||||
|
У костра сидели несколько фигур. Играла музыка, к удивлению, не так уж громко.
|
||||||
|
— Показываем документы! — сказала она из темноты.
|
||||||
|
Произошла короткая суета. Музыка была выключена. Что-то было быстро спрятано. Горящая сигарета была выброшена.
|
||||||
|
— Чего ты так пугаешь! — сказал кто-то, когда волнение уляглось.
|
||||||
|
— Двигайтесь. — сказала Ольга и села на бревно рядом с кем-то, каким-то юношей, или, может, девушкой. — Что у вас там? Можете не прятать. Бренди?
|
||||||
|
— Не бренди, конечно. — сказал кто-то.
|
||||||
|
— Ну, угощайте, что ли.
|
||||||
|
Ей передали бутылку. Она глотнула прямо из горлышка. Этикетка выглядела дорогой, но водка была на вкус как отчаяние.
|
||||||
|
— Только у нас закуски нет. — сказал кто-то.
|
||||||
|
— Я не закусываю никогда. — ответила Ольга.
|
||||||
|
Прозвучали одобрительные голоса.
|
||||||
|
Вкус ржавчины и дешевого лака для ногтей остался во рту даже наутро, когда она обнаружила себя лежащей на траве около дома. Солнце едва поднялось. Все было мокрое от росы и холодное. Каждая клетка ее тела болела.
|
||||||
|
Потребовалось усилие, чтобы отпереть дверь. В голове кружились какие-то неразборчивые образы: она держит руки на чьих-то плечах и двигается в ритме, напоминающем медленный танец, она пьет из бутылки, кто-то еще пьет из бутылки и нюхает чьи-то волосы. Она остановилась перед зеркалом. На ней все еще была вчерашняя одежда - клетчатая рубашка, уже с пятнами грязи, и драные джинсы. Она смутно вспомнила, как просовывает палец в дырку над коленом и говорит “Рваная, но чистая” и ее вырвало прямо на пол. Она даже не могла вспомнить, кому говорила это.
|
||||||
|
В конце концов она собрала достаточно сил, чтобы умыться, а потом вышла из дома.
|
||||||
|
Марту она нашла на речке. Та издалека, из воды замахала ей руками и крикнула что-то. Ольга медленно спустилась на так называемый пляж. Марта подплыла ближе, наполовину вылезла из воды, сделалавшись похожей на горбоносую мускулистую русалку.
|
||||||
|
– Спорим? – закричала она. – Спорим, я первая доплыву до острова? Давай! Я уже один раз сплавала!
|
||||||
|
Ольга села на песок и сказала:
|
||||||
|
– Неохота. Мне не в чем.
|
||||||
|
– Ну и что! – крикнула Марта. – Смотри! Нету же никого!
|
||||||
|
– А там? – спросила Ольга.
|
||||||
|
– Там вообще никого! Давай, погнали!
|
||||||
|
– Одежду украдут. – сказала Ольга.
|
||||||
|
– Ты что! – ответила Марта, и быстро объяснила, что будет с тем, кто украдет одежду.
|
||||||
|
– Да? – сказала Ольга. – Ну ладно.
|
||||||
|
Доплыли абсолютно одновременно, но Марта закричала:
|
||||||
|
– Я первая!
|
||||||
|
– Все понятно. – ответила Ольга.
|
||||||
|
– Что тебе понятно?
|
||||||
|
– Все мне понятно.
|
||||||
|
Марта сразу, шумно дыша, бросилась на землю. Ольга из осторожности и из любопытства обошла весь островок, потом еще раз. Раньше она не была здесь, и за все время, проведенное на реке, видела его только издалека. Потом улеглась в стороне от Марты и стала греться на солнце.
|
||||||
|
– Есть хочется. – сказала Марта. – Сейчас обратно поплывем.
|
||||||
|
Ольга перевернулась на спину и попробовала смотреть на небо, но было слишком ярко, и приходилось жмуриться. Она снова легла на живот. Голова болела уже гораздо меньше, тошнота почти прошла, и ей даже стало казаться, что впереди еще возможно какое-то будущее, какая-то жизнь. Надо только как-то пережить этот день, но в конце концов люди и не такое переживают. И вот именно в этот момент Марта мечтательно сказала:
|
||||||
|
– Прикинь. Только июнь. Еще целое лето.
|
||||||
13
functions.py
@@ -1,7 +1,7 @@
|
|||||||
import os, subprocess
|
import os, subprocess
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from classes import DefaultFrontmatterHeader
|
from classes import *
|
||||||
from config import Config
|
from config import Config
|
||||||
from logger import logger
|
from logger import logger
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ def edit_content(filename):
|
|||||||
filename = Path(f"{Config.CONTENT_DIR}/{filename}.md")
|
filename = Path(f"{Config.CONTENT_DIR}/{filename}.md")
|
||||||
subprocess.call([editor, filename])
|
subprocess.call([editor, filename])
|
||||||
|
|
||||||
def init_site():
|
def init_site_old():
|
||||||
logger.debug(f"Initializing new site")
|
logger.debug(f"Initializing new site")
|
||||||
|
|
||||||
# Recreate output directory if necessary
|
# Recreate output directory if necessary
|
||||||
@@ -39,7 +39,10 @@ def init_site():
|
|||||||
# Create "About.md" content item
|
# Create "About.md" content item
|
||||||
create_content("about")
|
create_content("about")
|
||||||
|
|
||||||
|
def init_site():
|
||||||
|
logger.debug(f"Initializing new site")
|
||||||
|
site = SitePrototype
|
||||||
|
site.init_site()
|
||||||
|
|
||||||
def build_site():
|
def build_site():
|
||||||
logger.debug(f"Starting build")
|
logger.debug("Building site")
|
||||||
|
|
||||||
|
|
||||||
21
hydrogen.py
@@ -1,30 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import shutil
|
|
||||||
from config import Config
|
|
||||||
from logger import logger
|
|
||||||
from argparser import argparser
|
from argparser import argparser
|
||||||
from classes import *
|
from classes import *
|
||||||
from functions import *
|
from functions import *
|
||||||
|
|
||||||
content_dir = Path('content')
|
|
||||||
template_dir = Path('templates')
|
|
||||||
output_dir = Path(Config.OUTPUT_DIR)
|
|
||||||
img_dir = Path('public/images')
|
|
||||||
assets_css_dir = Path(output_dir / 'static/css')
|
|
||||||
assets_js_dir = Path(output_dir / 'static/js')
|
|
||||||
static_dir = Path('static')
|
|
||||||
header_image = Config.HEADER_IMAGE or '/static/header.jpg'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def build_site_old():
|
def build_site_old():
|
||||||
logger.info(f"Building the '{Config.MAIN_PAGE_TITLE}' site.")
|
logger.info(f"Building the '{Config.MAIN_PAGE_TITLE}' site.")
|
||||||
|
|
||||||
|
|||||||
8
jinja_env.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from config import Config
|
||||||
|
|
||||||
|
# Prepare template rendering engine
|
||||||
|
env = Environment(loader=FileSystemLoader(Config.TEMPLATES_DIR))
|
||||||
|
env.globals['header_image'] = Config.HEADER_IMAGE
|
||||||
|
index_template = env.get_template("index.html")
|
||||||
|
content_item_template = env.get_template("content_item.html")
|
||||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
24
templates.bak/content_item.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{% extends "default.html" %}
|
||||||
|
|
||||||
|
{% block head_includes %}
|
||||||
|
{% if content_item.custom_css %}
|
||||||
|
<link href="{{ content_item.custom_css }}">
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mt-4 mb-5">
|
||||||
|
{% if content_item.image %}<img src="{{ content_item.image }}" class="img-fluid mb-5" style="border-radius: 5px;">{% endif %}
|
||||||
|
{% if not content_item.omit_second_title %}
|
||||||
|
<h1>{{ content_item.title }}</h1>
|
||||||
|
{% endif %}
|
||||||
|
<article>{{ content_item.html | safe }}</article>
|
||||||
|
<a href="index.html" class="btn btn-secondary mt-4">← Back</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer_includes %}
|
||||||
|
{% if content_item.custom_js %}
|
||||||
|
<script src="{{ content_item.custom_js }}"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
90
templates.bak/default.html
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
{% set base = "" %}
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
|
||||||
|
<title>{{ page_title }}</title>
|
||||||
|
<style>
|
||||||
|
.top_header {
|
||||||
|
position: relative;
|
||||||
|
height: 250px;
|
||||||
|
background: rgb(70, 70, 124);
|
||||||
|
background-image: url("{{ header_image }}");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_header_text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.top_menu a {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
padding: 6px 12px;
|
||||||
|
margin-left: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_menu a:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.top_header_text {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
{% block head_includes %}
|
||||||
|
{% endblock %}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="top_header">
|
||||||
|
<div >
|
||||||
|
<h1 class="top_header_text"><a style="border-radius: 5px; padding: 10px; opacity: 0.7; background: black">{{ page_title }}</a></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="top_menu d-flex justify-content-end" style="margin-bottom: 20px;">
|
||||||
|
|
||||||
|
<a href="index.html">Home</a>
|
||||||
|
<a href="about.html">About</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">{% block content %}{% endblock %}</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
|
||||||
|
{% block footer_includes %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
19
templates.bak/index.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "default.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="row row-cols-1 row-cols-md-3 g-4 px-1 py-2">
|
||||||
|
{% for content_item in content_items %}
|
||||||
|
<div class="col mb-3">
|
||||||
|
<div class="card h-100" style="border-radius: 5px;">
|
||||||
|
<img src="{{ content_item.image }}" alt="{{ content_item.title }}" class="card-img-top img-fluid" style="background: rgb(77, 77, 77); object-fit: cover; height: 200px; border-radius: 5px;">
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<h5 class="card-title"><a class="text-decoration-none text-body" href="{{ content_item.slug}}.html ">{{ content_item.title }}</a></h5>
|
||||||
|
<p class="card-text mb-3">{{ content_item.preview | safe}}</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
140
templates.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
default = """
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
{% set base = "" %}
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
|
||||||
|
<title>{{ page_title }}</title>
|
||||||
|
<style>
|
||||||
|
.top_header {
|
||||||
|
position: relative;
|
||||||
|
height: 250px;
|
||||||
|
background: rgb(70, 70, 124);
|
||||||
|
background-image: url("{{ header_image }}");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_header_text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.top_menu a {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
padding: 6px 12px;
|
||||||
|
margin-left: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_menu a:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.top_header_text {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
{% block head_includes %}
|
||||||
|
{% endblock %}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="top_header">
|
||||||
|
<div >
|
||||||
|
<h1 class="top_header_text"><a style="border-radius: 5px; padding: 10px; opacity: 0.7; background: black">{{ page_title }}</a></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="top_menu d-flex justify-content-end" style="margin-bottom: 20px;">
|
||||||
|
|
||||||
|
<a href="index.html">Home</a>
|
||||||
|
<a href="about.html">About</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">{% block content %}{% endblock %}</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
|
||||||
|
{% block footer_includes %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
"""
|
||||||
|
|
||||||
|
index = """
|
||||||
|
{% extends "default.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="row row-cols-1 row-cols-md-3 g-4 px-1 py-2">
|
||||||
|
{% for content_item in content_items %}
|
||||||
|
<div class="col mb-3">
|
||||||
|
<div class="card h-100" style="border-radius: 5px;">
|
||||||
|
<img src="{{ content_item.image }}" alt="{{ content_item.title }}" class="card-img-top img-fluid" style="background: rgb(77, 77, 77); object-fit: cover; height: 200px; border-radius: 5px;">
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<h5 class="card-title"><a class="text-decoration-none text-body" href="{{ content_item.slug}}.html ">{{ content_item.title }}</a></h5>
|
||||||
|
<p class="card-text mb-3">{{ content_item.preview | safe}}</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
content_item="""
|
||||||
|
{% extends "default.html" %}
|
||||||
|
|
||||||
|
{% block head_includes %}
|
||||||
|
{% if content_item.custom_css %}
|
||||||
|
<link href="{{ content_item.custom_css }}">
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mt-4 mb-5">
|
||||||
|
{% if content_item.image %}<img src="{{ content_item.image }}" class="img-fluid mb-5" style="border-radius: 5px;">{% endif %}
|
||||||
|
{% if not content_item.omit_second_title %}
|
||||||
|
<h1>{{ content_item.title }}</h1>
|
||||||
|
{% endif %}
|
||||||
|
<article>{{ content_item.html | safe }}</article>
|
||||||
|
<a href="index.html" class="btn btn-secondary mt-4">← Back</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer_includes %}
|
||||||
|
{% if content_item.custom_js %}
|
||||||
|
<script src="{{ content_item.custom_js }}"></script>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
"""
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
{% extends "default.html" %}
|
{% extends "default.html" %}
|
||||||
|
|
||||||
{% block head_includes %}
|
{% block head_includes %}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
{% extends "default.html" %}
|
{% extends "default.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|||||||