# SharedDocsLib SharedDocsLib — небольшой Python-фреймворк для документации. Страницы и HTML-компоненты описываются функциями с декораторами, а библиотека собирает Obsidian-подобный автономный клиент и отдаёт его через FastAPI. ## Возможности - страницы с номерами `1`, `1.1`, `1.1.2` и автоматическим деревом; - собственные `@Component` и набор безопасных базовых компонентов; - поиск по всему контенту, светлая/тёмная тема, breadcrumbs, Previous/Next; - изображения из `assets_dir`, единожды закодированные в base64 внутри manifest; - полностью автономный `index.html`: начальный manifest вместе с CSS и JavaScript встроен; - хранение manifest в IndexedDB и лёгкая проверка обновления через файл `version`; - `index.zip`, содержащий только `index.html`; - live-preview с перекомпиляцией изменившегося Python-модуля. UI не содержит языко-зависимых служебных подписей. Базовый шрифт — `system-ui 16px`, а служебные UTF-8-пиктограммы используют Arial. Навигационные стрелки отрисовываются CSS-границами без текстовых символов. AMOLED-тема включена по умолчанию и сохраняется локально после переключения. Accessibility-кнопка создаётся системным JavaScript, поэтому появляется и в ранее скомпилированных HTML после обновления manifest. На десктопе она закреплена в левом нижнем углу ниже loading-overlay, на мобильном располагается рядом с переключателем темы внутри меню. Псевдостраница настроек позволяет менять размер текста от 12 до 28 px, сбрасывать параметры и выбирать только локальные браузерные семейства `system-ui`, `sans-serif`, `serif` и `monospace`; сетевые шрифты не используются. Размер применяется только к sidebar, документации и кнопкам Previous/Next, а семейство — ко всему интерфейсу, кроме UTF-8 glyph-кнопок. Параметры сохраняются в `localStorage`. На десктопе компактный header содержит toggle боковой панели, полное название документации, поиск и переключатель темы. Sidebar по умолчанию открыт, его состояние не сохраняется между загрузками, а при сворачивании внутренняя раскладка остаётся неподвижной и обрезается границей панели без переноса текста. Поисковый placeholder медленно печатает первые слова случайного названия, добавляет троеточие только в конце и сохраняет готовый текст 5 секунд; при вводе поле раскрывает ранжированные результаты вниз. Проверка manifest выполняется автоматически в фоне без отдельной кнопки обновления. Обновлённый runtime сохраняет совместимость со старыми скомпилированными HTML-оболочками: отсутствие desktop-toggle, мобильного переключателя темы или общего контейнера page-transition не останавливает запуск клиента. На мобильных устройствах используется viewport `100dvh` и нижний dock. Кнопка `☰` заменяет содержимое страницы копией desktop-sidebar, которая выезжает с левой границы экрана; между header-кнопками и деревом доступен круглый переключатель темы. Выбор страницы в открытом мобильном меню закрывает его и показывает новый контент моментально, без page-transition. Ввод в нижний поиск заменяет содержимое результатами, расположенными снизу вверх по релевантности. При остальных переходах контент вместе с кнопками предыдущей и следующей страницы плавно смещается на 30 px по направлению навигации и меняет прозрачность. ## Установка из Git ```bash uv venv source .venv/bin/activate uv pip install "git+https://git.snw.su/snw/SharedDocsLib.git@main" ``` Для разработки и запуска примера из клонированного репозитория: ```bash uv sync --extra dev uv run python examples/basic.py ``` После запуска доступны: - `GET http://127.0.0.1:1234/manifest.json` - `GET http://127.0.0.1:1234/version` - `GET http://127.0.0.1:1234/index.html` - `GET http://127.0.0.1:1234/index.zip` - `GET http://127.0.0.1:1234/live` (только при `live_preview=True`) ## Минимальный пример ```python from docslib import HeaderButton, run from docslib.hooks import Component, Page from docslib.components import H1, Image, LocalLink, P @Component def Lead(text): return f'
{text}
' @Page("1.", "Page title") def page1(): return [ H1("Hello"), P("Documentation text"), Image("photo.png", "Description"), LocalLink("Open nested page", subpage1), ] @Page("1.1", "Sub-page") def subpage1(): return [H1("Nested page")] if __name__ == "__main__": run( port=1234, host="0.0.0.0", title="My docs", assets_dir="./assets", custom_css="./assets/inject.css", custom_js="./assets/inject.js", manifest_path="http://127.0.0.1:1234/", live_preview=True, header_buttons=[ HeaderButton("Знакомство", page1), HeaderButton("API reference", subpage1), HeaderButton.external("Project", "https://example.com") ], ) ``` `manifest_path` принимает полный путь к JSON либо базовый URL, к которому будет добавлен `manifest.json`. URL файла `version` автоматически вычисляется рядом с manifest. Для совместимости также принят вариант `manifest_parh` из первоначального API, но в новом коде лучше использовать правильное имя. ## Статическая сборка `build` принимает параметры компиляции из `run`, не связанные с сервером, и создаёт готовый статический комплект. По умолчанию используется каталог `./build`; отсутствующие родительские каталоги создаются автоматически. ```python from docslib import HeaderButton, build build( build_directory="./dist/docs", title="My docs", assets_dir="./assets", custom_css="./assets/inject.css", custom_js="./assets/inject.js", manifest_path="./manifest.json", header_buttons=[ HeaderButton("Знакомство", page1), HeaderButton("API reference", subpage1), ], ) ``` В `build_directory` записываются ровно четыре файла: - `index.html` — полностью автономный клиент; - `manifest.json` — manifest для публикации отдельным файлом; - `version` — короткий текстовый файл, содержащий `revision` manifest; - `index.zip` — архив, содержащий только `index.html`. Функция возвращает `BuildResult` с теми же данными в памяти. Серверные параметры `host`, `port`, `live_preview`, `cors_origins` и `log_level` ей не требуются. ## Компоненты Доступны `H1`, `H2`, `H3`, `P`, `MarkdownText`, `Image`, `Link`, `LocalLink` (`LocLink`), `Code`, `InlineCode`, `Quote`, `Callout`, `List`, `Table`, `Divider`, `Badge`, `Details`, `Json` и `RawHTML`. Обычные компоненты экранируют пользовательский текст. `RawHTML` и HTML из собственных `@Component` считаются доверенными и вставляются как есть. Функция страницы может вернуть строку, список/генератор строк, вложенные списки или `None`. ### Ссылки между страницами `LocalLink` создаёт обычную браузерную ссылку с hash-route. Поэтому ссылку можно копировать, открывать в новой вкладке и активировать с клавиатуры. Клиент перехватывает только обычный клик и показывает нужную страницу без перезагрузки. ```python # По объекту функции страницы — предпочтительный вариант: LocalLink("Установка", install_page) # По точному номеру: LocalLink("Установка", "1.2") # По уникальному названию: LocalLink("Установка", "Install") # По названию внутри раздела, если названия повторяются: LocalLink("Установка для сервера", "Install", section="2") ``` Цель по функции разрешается во время компиляции, когда все `@Page` уже зарегистрированы, поэтому можно ссылаться и на функцию, объявленную ниже по файлу. Если название неоднозначно или страница отсутствует, компиляция завершится понятной ошибкой. `LocLink` является коротким псевдонимом `LocalLink`. ### Ключевые кнопки в header Для навигации верхнего уровня используйте `HeaderButton`. Локальная цель поддерживает те же варианты, что и `LocalLink`: объект функции страницы, номер, уникальный заголовок или заголовок внутри указанного раздела. ```python run( header_buttons=[ # Короткая форма по функции страницы: HeaderButton("Знакомство", introduction_page), # Явная форма по номеру: HeaderButton.local("API reference", "3"), # По названию в определённом разделе: HeaderButton.local("Установка", "Install", section="2"), # Внешняя ссылка: HeaderButton.external("GitHub", "https://github.com/example/project"), ] ) ``` Локальная header-кнопка компилируется в обычный `href="#/page/…"`, открывает страницу без перезагрузки и подсвечивается на всех вложенных страницах своего раздела. Старые словари остаются совместимыми: ```python header_buttons=[ {"label": "Знакомство", "page": introduction_page}, {"label": "API", "page": "API reference", "section": "3"}, {"label": "GitHub", "href": "https://github.com/", "target": "_blank"}, ] ``` ## Manifest Manifest имеет версию схемы, заголовок, `revision`, `updated_at`, страницы, настройки UI, словарь ресурсов и полный клиентский runtime. Системные файлы шаблона и пользовательские дополнения хранятся раздельно, а при загрузке объединяются в указанном порядке: ```json { "schema_version": 2, "title": "My docs", "revision": "…", "updated_at": "…", "pages": [{"number": "1", "title": "Home", "content": "