Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Модель компонентов Light

Этот документ фиксирует архитектурную модель компонентов Light: что считается компонентом, какие бывают типы, как они описываются, распространяются и собираются в приложение.

Документ — «конституция» для всех дальнейших работ: новые элементы, подсистемы и инструменты обязаны соответствовать изложенным здесь правилам. Изменения в этом документе требуют обоснования и записи в CHANGELOG.md.

Связанные документы:

  • docs/DESIGN_PRINCIPLES.md — общие принципы (в частности, «только используемый код попадает в бинарник»).
  • docs/guide.md — руководство пользователя.
  • docs/COMPONENT_AUTHORING.md — как писать собственные компоненты (появится после реализации CLI).

1. Что такое компонент

Компонент — это автономная единица функциональности Light, которая:

  1. Имеет манифест с именем, версией, типом и совместимостью с ядром.
  2. Распространяется через стандартный реестр (crates.io или приватный mirror) либо как asset pack (архив данных).
  3. Может быть включён или выключен в конкретном приложении — код выключенного компонента не попадает в бинарник.
  4. Объявляет, что предоставляет (provides): элементы, атрибуты, директивы, API-функции.
  5. Объявляет, что требует (requires): другие компоненты и совместимую версию ядра.
  6. Следует правилам из DESIGN_PRINCIPLES.md.

Компонент — это не «плагин во время выполнения». Это compile-time единица: решение о включении принимается один раз, при сборке.

Компонентом может быть как отдельный Rust-крейт, так и пакет данных. Разница — в способе распространения и наличии кода (см. §5).


2. Принципы

  1. Ядро минимально. Сам фреймворк — это core (vdom, reactive, layout, paint, окно). Всё остальное — компоненты.
  2. Один компонент — одна ответственность. Не бывает «мега-компонента» с checkbox, dropdown и графиком внутри. Если функциональность распадается на независимые части — это разные компоненты.
  3. Зависимости явные. Компонент не может «неявно» требовать другой компонент. Все требования — в манифесте.
  4. Совместимость — по semver. Без своих версий и «совместимо со всем».
  5. Собирается только используемое. См. DESIGN_PRINCIPLES.md.
  6. Cargo остаётся движком. Компоненты-крейты собираются Cargo. CLI Light — надстройка, не замена.
  7. Данные отдельно от кода. Темы, иконки, локали — asset packs, не крейты. Они не содержат нативного кода и не требуют сборки.

3. Таксономия компонентов

Компоненты делятся на восемь типов. Тип определяет роль, способ распространения и правила авторинга.

3.1. Core (ядро)

Не компонент, а сам фреймворк. light-fw-core, light-fw-macros. Минимальный набор: виртуальный DOM, реактивность, базовая раскладка, окно и paint, публичный API.

Ядро не может быть выключено — без него нет приложения. Версия ядра — это версия, с которой сверяются все компоненты.

Бесплатно (без отдельного компонента) в ядре живут базовые элементы: <div>, <span>, <h1>–<h5>, <titlebar>, <window>, <button>, а также текстовые поля <text>, <password> и <textarea>. Способы отрисовки window/a11y и элементы elements-basic/ elements-input включаются Cargo-фичами light-fw-core, а не отдельными крейтами: для них фич достаточно.

Крейт light — тонкая мета-обёртка: он реэкспортирует light-fw-core, макросы и подключённые компоненты, а его фичи транслируются в фичи light-fw-core (см. docs/guide.md).

3.2. Element (элемент)

Примитивные сущности разметки: теги, доступные в HTML-шаблонах. Один крейт = одна логическая группа элементов.

Примеры:

  • light-element-basic — <div>, <span>, <h1>–<h5>, <titlebar>, <window> (в текущей реализации — фича light-fw-core).
  • light-fw-element-form — <checkbox>, <radio>, <select>, <option>.
  • light-element-media — <img>, <video>, <canvas>.
  • light-element-list — <list>, <virtual-list>, <table>.

Правила:

  • Каждый элемент из группы регистрируется в макросе #[template].
  • Использование элемента без его компонента — ошибка компиляции с подсказкой, какой компонент включить.
  • Element-компонент может зависеть от других element-компонентов (например, light-fw-element-form может требовать light-element-basic).

3.3. Behavior (поведение)

Не создают новых элементов. Добавляют функциональность существующим: обработку событий, свойства, модификацию раскладки.

Примеры:

  • light-behavior-drag — перетаскивание элементов.
  • light-behavior-hotkeys — глобальные горячие клавиши.
  • light-behavior-animation — анимации переходов между состояниями.
  • light-fw-behavior-validation — валидация форм (:validator, @validate, required).
  • light-behavior-dnd-files — drop файлов из ОС.
  • light-behavior-focus-trap — focus-trap для модальных окон.

Правила:

  • Behavior-компонент объявляет, какие атрибуты или директивы он предоставляет (provides.attributes или provides.directives).
  • Не может вводить новые теги.
  • Может расширять NodeKind (если требует новых полей), но не создавать новые варианты.

3.4. Widget (виджет)

Сложные составные компоненты, собранные из elements + behaviors. Предоставляют готовый к использованию UI-элемент.

Примеры:

  • light-widget-datepicker
  • light-widget-autocomplete
  • light-widget-code-editor
  • light-widget-markdown-viewer
  • light-widget-chart
  • light-widget-treeview

Правила:

  • Виджет может предоставлять новый тег (например, <datepicker>) или активироваться через атрибут существующего элемента.
  • Виджет не расширяет ядро напрямую. Всё, что он делает — через публичный API элементов и behaviors.
  • Виджет может требовать другие виджеты (композиция).

3.5. Integration (интеграция)

Мост с внешним миром: ОС, устройства, сторонние сервисы.

Примеры:

  • light-integration-tray — системный трей.
  • light-integration-notifications — системные уведомления.
  • light-integration-clipboard — работа с буфером обмена.
  • light-integration-filedialog — диалоги выбора файла.
  • light-integration-audio.
  • light-integration-midi.
  • light-integration-http — асинхронные HTTP-запросы.

Правила:

  • Integration не создаёт элементов. Даёт Rust-API для компонента.
  • Может требовать специфические системные зависимости (windows-sys, objc2, gtk, …).
  • Обязан документировать платформенную поддержку.

3.6. Subsystem (подсистема)

Крупные блоки, меняющие поведение всего фреймворка или большого его слоя.

Примеры:

  • light-subsystem-a11y — интеграция с AccessKit.
  • light-subsystem-i18n — локализация (Fluent).
  • light-subsystem-theme — темы и токены дизайна.
  • light-subsystem-undo — глобальный стек undo/redo.
  • light-subsystem-persistence — сохранение состояния окон.
  • light-subsystem-scripting — встраивание Rhai/Lua.
  • light-subsystem-devtools — инспектор, профайлер.

Правила:

  • Subsystem включается одним Cargo-флагом.
  • Не имеет графических элементов «из коробки».
  • Обязан иметь выключенное состояние: без него фреймворк полноценно работает (просто без функции).
  • Любые hooks, которые он встраивает в ядро, должны быть гейтнуты #[cfg(feature = "...")] и не мешать другим подсистемам.

3.7. Data (данные)

Не код. Пакет файлов: темы, иконки, локали, шрифты, звуки, сниппеты.

Примеры:

  • Theme — .light-theme.zip: цвета, метрики, шрифты.
  • Icons — .light-icons.zip: набор SVG с именами.
  • Locale — .light-locale.zip: Fluent-файлы (.ftl).
  • Template — .light-template.zip: HTML-сниппеты для <include>.

Правила:

  • Asset pack не содержит нативного кода.
  • Формат — стандартный zip (или tar.zst) с manifest.toml внутри.
  • Не требует сборки — читается в рантайме.
  • Может зависеть от версии ядра (проверяется при загрузке).

3.8. Dev-only (для разработки)

Не попадает в релизную сборку. Используется во время разработки.

Примеры:

  • light-devtools — интерактивный инспектор UI и состояния.
  • light-hot-reload — горячая перезагрузка шаблонов.
  • light-mock — мок-объекты для тестов.
  • light-bench — бенчмарки.

Правила:

  • Всегда под фичей, никогда не в default.
  • Может зависеть от unstable-API ядра.
  • Документируется отдельно — не для конечного пользователя.

4. Манифест компонента (крейт)

Компоненты-крейты используют стандартный Cargo.toml плюс секцию [package.metadata.light].

Пример полного манифеста:

[package]
name = "light-widget-datepicker"
version = "0.3.1"
edition = "2021"
rust-version = "1.75"
license = "MIT OR Apache-2.0"
description = "Календарь для выбора даты"
authors = ["Иван Иванов <ivan@example.com>"]
repository = "https://github.com/gifoff/light-widget-datepicker"
homepage = "https://light.rs/components/datepicker"
keywords = ["light", "widget", "date", "input"]
categories = ["gui"]

[package.metadata.light]
# Тип компонента: element | behavior | widget | integration | subsystem | dev
kind = "widget"

# Человекочитаемое имя (в UI, документации, интерактивных подсказках)
display-name = "Date Picker"

# Совместимость с ядром — semver-range
core = "^0.6"

# Что компонент предоставляет
[package.metadata.light.provides]
elements = ["datepicker"]              # новые теги
attributes = []                        # новые атрибуты
directives = []                        # новые директивы (v-*)
# traits = ["DatePickerApi"]           # публичные трейты (для integrations/subsystems)

# Что компонент требует
[package.metadata.light.requires]
# Компоненты, которые обязательны
components = [
    "light-fw-element-form ^0.1",
    "light-fw-behavior-validation ^0.1",
]
# Rust-зависимости, помимо Cargo-секции [dependencies]
# (обычно не нужно — используется [dependencies])

# Конфликты: нельзя включать одновременно
conflicts = ["light-widget-old-datepicker"]

# Метаданные для CLI и реестра
[package.metadata.light.registry]
official = false                       # официальный ли компонент
tags = ["form", "date", "input", "calendar"]