Модель компонентов Light
Этот документ фиксирует архитектурную модель компонентов Light: что считается компонентом, какие бывают типы, как они описываются, распространяются и собираются в приложение.
Документ — «конституция» для всех дальнейших работ: новые элементы,
подсистемы и инструменты обязаны соответствовать изложенным здесь
правилам. Изменения в этом документе требуют обоснования и записи
в CHANGELOG.md.
Связанные документы:
docs/DESIGN_PRINCIPLES.md— общие принципы (в частности, «только используемый код попадает в бинарник»).docs/guide.md— руководство пользователя.docs/COMPONENT_AUTHORING.md— как писать собственные компоненты (появится после реализации CLI).
1. Что такое компонент
Компонент — это автономная единица функциональности Light, которая:
- Имеет манифест с именем, версией, типом и совместимостью с ядром.
- Распространяется через стандартный реестр (crates.io или приватный mirror) либо как asset pack (архив данных).
- Может быть включён или выключен в конкретном приложении — код выключенного компонента не попадает в бинарник.
- Объявляет, что предоставляет (
provides): элементы, атрибуты, директивы, API-функции. - Объявляет, что требует (
requires): другие компоненты и совместимую версию ядра. - Следует правилам из
DESIGN_PRINCIPLES.md.
Компонент — это не «плагин во время выполнения». Это compile-time единица: решение о включении принимается один раз, при сборке.
Компонентом может быть как отдельный Rust-крейт, так и пакет данных. Разница — в способе распространения и наличии кода (см. §5).
2. Принципы
- Ядро минимально. Сам фреймворк — это core (vdom, reactive, layout, paint, окно). Всё остальное — компоненты.
- Один компонент — одна ответственность. Не бывает «мега-компонента» с checkbox, dropdown и графиком внутри. Если функциональность распадается на независимые части — это разные компоненты.
- Зависимости явные. Компонент не может «неявно» требовать другой компонент. Все требования — в манифесте.
- Совместимость — по semver. Без своих версий и «совместимо со всем».
- Собирается только используемое. См.
DESIGN_PRINCIPLES.md. - Cargo остаётся движком. Компоненты-крейты собираются Cargo. CLI Light — надстройка, не замена.
- Данные отдельно от кода. Темы, иконки, локали — 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-datepickerlight-widget-autocompletelight-widget-code-editorlight-widget-markdown-viewerlight-widget-chartlight-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"]