diff --git a/docs/normalization/README.md b/docs/normalization/README.md new file mode 100644 index 00000000..a371f1d8 --- /dev/null +++ b/docs/normalization/README.md @@ -0,0 +1,54 @@ +# Документация: UI Normalization + +Spec-driven описание **config-driven** форм (редактор **normalization-tool**, просмотр **transpile-viewer**, общий DSL). + +--- + +## Итоговый вид документации (три слоя) + +```mermaid +flowchart TB + subgraph layer1 [1. Обзор решения] + T[tooling.md] + end + subgraph layer2 [2. Фичи] + F[features/README.md] + F1[feat-*.md] + end + subgraph layer3 [3. Требования по эпикам] + P[plans/plan-*.md] + end + subgraph meta [Мета] + R[roadmap.md] + end + R --> T + R --> F + R --> P + T --> F + F --> F1 + P --> F1 +``` + +| Слой | Документ | Назначение | +|------|----------|------------| +| **1** | **[tooling.md](./tooling.md)** | Краткое **описание решения** (что строим, зачем, как устроено в целом) и **таблица ссылок** на фичи. В конце файла — **прежние ссылки на разделы** (`#…`) для совместимости. | +| **2** | **[features/README.md](./features/README.md)** | **Дерево вложенных фич** и ссылки на файлы **`feat-*.md`**: контракты, ограничения, связь с кодом. | +| **3** | **[plans/](./plans/)** | **Требования** закрытых и текущих эпиков: таблица *`id` · фича · описание*; трассировка к роадмапу и к файлам из **`features/`**. | +| **Мета** | **[roadmap.md](./roadmap.md)** | Этапы **п.1–9**, статусы, указатели на слои 1–3. | + +**Точка входа для чтения:** [tooling.md](./tooling.md) → при необходимости углубления — конкретный **`features/feat-…`** → для приёмки эпика — **`plans/plan-…`**. + +**П.9 роадмапа** закрепляет поддержание этой структуры при эволюции кода. + +--- + +## Быстрый указатель + +| Тема | Куда | +|------|------| +| Два приложения, паритет, границы продукта | [feat-product-and-parity.md](./features/feat-product-and-parity.md) | +| `FormData`, дерево контролов | [feat-dsl-root-model.md](./features/feat-dsl-root-model.md) | +| `configHook` | [feat-config-hook.md](./features/feat-config-hook.md) | +| Sucrase, чанки, `loadFormDslBrowserRuntime` | [feat-lazy-dsl-runtime.md](./features/feat-lazy-dsl-runtime.md) | +| FSA, IndexedDB, `?form=` | [feat-workspace-browser.md](./features/feat-workspace-browser.md) | +| Button / Text / Grid / Table / DSL п.2 | [plans/](./plans/) | diff --git a/docs/normalization/features/README.md b/docs/normalization/features/README.md new file mode 100644 index 00000000..edd9ffda --- /dev/null +++ b/docs/normalization/features/README.md @@ -0,0 +1,48 @@ +# Каталог фич: UI Normalization + +Иерархия **возможностей** (feature tree) туллинга. Каждая фича — отдельный файл: контракт, ограничения, связь с кодом. Сводный обзор решения и навигация — **[tooling.md](../tooling.md)**. + +--- + +## Дерево (вложенность) + +``` +Решение: config-driven формы + Hexa UI +│ +├── Продукт и поставка +│ └── [Продукт, паритет, два приложения](./feat-product-and-parity.md) +│ +├── DSL и исполнение +│ ├── [Корневая модель FormData](./feat-dsl-root-model.md) +│ ├── [Принцип «одна настройка — один способ»](./feat-dsl-one-setting.md) +│ ├── [Контракт configHook](./feat-config-hook.md) +│ ├── [Ленивый рантайм: parseFormTs, Sucrase, чанки](./feat-lazy-dsl-runtime.md) +│ └── [Типизация автором и импорты потребителя](./feat-typing-and-imports.md) +│ +├── Рабочее пространство (браузер) +│ └── [FSA, IndexedDB, URL form, сценарии, среда](./feat-workspace-browser.md) +│ +├── Целевое состояние (роадмап) +│ ├── [Архитектурные решения и направления](./feat-architecture-directions.md) +│ ├── [Превью в редакторе — единый рендер-движок](./feat-editor-preview-target.md) +│ └── [Один configHook на форму](./feat-form-config-hook-unified.md) +│ +└── Репозиторий + └── [Пакеты и shared-модули](./feat-repo-layout.md) +``` + +--- + +## Связь с другими документами + +| Документ | Роль | +|----------|------| +| [tooling.md](../tooling.md) | Краткое **описание решения** + таблица ссылок на фичи | +| [roadmap.md](../roadmap.md) | Этапы п.1–9, статусы | +| [plans/](../plans/) | **Требования** по эпикам (фича → описание), трассировка к роадмапу | + +--- + +## Именование фич в требованиях (plans) + +В планах используются стабильные идентификаторы вида **`domain.short-name`** (например **`config-hook.button`**) для ссылок из обсуждений и PR. diff --git a/docs/normalization/features/feat-architecture-directions.md b/docs/normalization/features/feat-architecture-directions.md new file mode 100644 index 00000000..95a3dc53 --- /dev/null +++ b/docs/normalization/features/feat-architecture-directions.md @@ -0,0 +1,29 @@ +# Фича: архитектурные решения и долгосрочные направления + +**Родитель:** целевое состояние (роадмап п.5–8 и часть п.7). + +--- + +## Описание + +Зафиксированные и целевые решения, которые задают рамку эволюции кода и продуктов, без пошаговой инструкции внедрения. + +--- + +## Требования / направления + +- **`arch.dsl.shared-module`:** один общий модуль DSL в дереве репозитория; **отдельный publishable npm-пакет** под DSL **не вводится**; **двух расходящихся копий** полного DSL **нет**. +- **`arch.forms.ts-only`:** артефакты форм в каталоге — **только `.ts`**. +- **`arch.code.genericity`:** контролы обрабатываются общими механизмами; частное — в дескрипторах и типах, не копипаста веток в рендерерах. +- **`arch.products.storybook`:** tool — целевой сценарий аддона **Storybook**; viewer — общий движок рендера для Storybook и прод. +- **`arch.shell.hexa`:** оболочки инструментов на **Hexa UI**; целевое **dogfooding** — UI инструментов из DSL + общего рендерера (оговорки для холста/DnD — при проектировании). +- **`arch.rename`:** целевые имена пакетов **`kant-editor`** / **`kant-renderer`**. +- **`arch.compat.json-ui-builder`:** обратная совместимость **`form-dsl`** и артефактов с **JSON UI Builder** (или явный мост версий). +- **`arch.viewer.adapters`:** viewer на адаптерах и **Auto UI** без монолитного маппинга всех типов; **Auto UI** — миграция на **React** end-to-end. + +--- + +## Ссылки + +- [roadmap.md](../roadmap.md) п.5–8, п.7 +- Детализация превью в редакторе: [feat-editor-preview-target.md](./feat-editor-preview-target.md) diff --git a/docs/normalization/features/feat-config-hook.md b/docs/normalization/features/feat-config-hook.md new file mode 100644 index 00000000..b4a739cd --- /dev/null +++ b/docs/normalization/features/feat-config-hook.md @@ -0,0 +1,41 @@ +# Фича: контракт configHook + +**Родитель:** DSL и исполнение. Зависит от [feat-dsl-one-setting](./feat-dsl-one-setting.md). Загрузка модулей — [feat-lazy-dsl-runtime](./feat-lazy-dsl-runtime.md). + +--- + +## Описание + +В динамическом режиме пропсы экземпляра компонента ДС задаёт логика **`configHook`**. Для **нормализованной** формы модуль **один на всю форму** — см. [feat-form-config-hook-unified](./feat-form-config-hook-unified.md): default export возвращает объект **`control.id` → хук контрола**. Колбэки (`onClick`, `onChange`, …) объявляются **внутри** объекта пропсов, который возвращает функция для конкретного контрола. + +--- + +## Требования + +- **`config-hook.api.input`:** единственный аргумент функции для контрола — **`FormSlice`** (`state` + `config.elements`); тип контрола задаётся телом этой функции в форменном модуле (по ключу **`control.id`** в реестре). +- **`config-hook.api.output`:** полный объект пропсов экземпляра компонента ДС **или `null`**; **`null`** — не монтировать компонент; отдельных полей `visible` в DSL для этого нет. +- **`config-hook.react`:** модуль исполняется как React-хук; пересчёт через React и стейт, без декларативного списка зависимостей в DSL. +- **`config-hook.context.layers`:** в колбэках автор разводит контекст **формы/инструмента** и контекст **Hexa** (события, пропсы экземпляра). +- **`config-hook.file.ts-only`:** путь в DSL и в пикере — только **`.ts`**; перед исполнением в браузере — транспиляция (см. ленивый рантайм). + +### Примеры возврата (Hexa UI) + +- Кнопка: **`ButtonProps | null`** +- Текст: **`TextProps | null`** +- Сетка / таблица: см. планы [plan-grid](../plans/plan-grid.md), [plan-table](../plans/plan-table.md) + +### Артефакты формы + +| Артефакт | Расширение | Загрузка | +|----------|------------|----------| +| **`configHook`** | **`.ts`** | [feat-lazy-dsl-runtime](./feat-lazy-dsl-runtime.md) → транспиляция → blob → `import()` | +| В **`.ts`** формы | — | путь к хуку сериализуется как **`() => import('./…')`** | + +Пути **`handlers`** на форме/контроле в UI могут указывать **`.js`** или **`.ts`**; для новых артефактов приоритет **TS** ([tooling — обзор](../tooling.md)). + +--- + +## Ссылки + +- Пример стиля: `packages/kaspersky-ui-normalization-tool/dsl/demo/demo.config-hook.ts` +- Код: `loadConfigHookDefaultExport` в tool и viewer diff --git a/docs/normalization/features/feat-dsl-one-setting.md b/docs/normalization/features/feat-dsl-one-setting.md new file mode 100644 index 00000000..1cdeb149 --- /dev/null +++ b/docs/normalization/features/feat-dsl-one-setting.md @@ -0,0 +1,23 @@ +# Фича: одна настройка — один способ + +**Родитель:** DSL и исполнение. + +--- + +## Описание + +У каждого **пропа** экземпляра компонента ДС в форме ровно **один** источник значения: либо статическое поле в DSL (инспектор), либо динамика целиком через **`configHook`**. Смешивание для одного пропа запрещено. + +--- + +## Требования + +- **`dsl.setting.single-source`:** параметр либо **статический** (хранится в DSL и задаётся в панели свойств), либо **динамический** (только из модуля **`configHook`**; отдельного поля DSL для этого пропа нет). +- **`dsl.setting.no-merge`:** нет объединения статики и динамики и нет «динамика перекрывает статику» для одного и того же пропа. + +--- + +## Ссылки + +- Контракт динамики: [feat-config-hook.md](./feat-config-hook.md) +- Один модуль на форму: [feat-form-config-hook-unified.md](./feat-form-config-hook-unified.md) diff --git a/docs/normalization/features/feat-dsl-root-model.md b/docs/normalization/features/feat-dsl-root-model.md new file mode 100644 index 00000000..3d27d586 --- /dev/null +++ b/docs/normalization/features/feat-dsl-root-model.md @@ -0,0 +1,26 @@ +# Фича: корневая модель DSL (FormData) + +**Родитель:** DSL и исполнение. + +--- + +## Описание + +Форма в рантайме — объект **`FormData`**: идентификаторы, опционально схема полей и обработчики уровня формы, дерево контролов. Источник в репозитории — **`shared/types/form.ts`** и нормализация в **`shared/normalization-form-dsl/form-dsl-core.ts`**. + +--- + +## Требования + +- **`dsl.root.fields`:** корень содержит **`name`**, **`id`**. +- **`dsl.root.schema`:** опционально — описание полей данных формы. +- **`dsl.root.handlers`:** опционально — события уровня формы → пути к файлам-скриптам. +- **`dsl.root.elements`:** дерево контролов: семантический **`type`**, **`id`**, привязки к данным, валидация, условия видимости/доступности, обработчики на контроле. +- **`dsl.mapping.semantics-to-hexa`:** маппинг «семантика контрола → компонент ДС» живёт в **реестрах / рендерерах** приложений, не как отдельный дублирующий метатип в DSL. + +--- + +## Ссылки + +- Типы: `shared/types/form.ts` +- Логика контролов: `shared/normalization-form-dsl/form-dsl-core.ts`, entry `form-dsl.ts` diff --git a/docs/normalization/features/feat-editor-preview-target.md b/docs/normalization/features/feat-editor-preview-target.md new file mode 100644 index 00000000..439ebde2 --- /dev/null +++ b/docs/normalization/features/feat-editor-preview-target.md @@ -0,0 +1,60 @@ +# Фича: превью в редакторе — единый рендер-движок + +**Родитель:** решение config-driven. Роадмап **п.3.1** ✅ ([якорь](../roadmap.md#normalization-roadmap-p31)); структура Grid/Table — **п.3.2** ✅ ([якорь](../roadmap.md#normalization-roadmap-p32)); WYSIWYG-canvas — **п.3.3** ✅ ([якорь](../roadmap.md#normalization-roadmap-p33)); один `configHook` на форму — **п.3.4.1** ✅ ([якорь](../roadmap.md#normalization-roadmap-p341), [план](../plans/plan-form-config-hook-341.md), [фича](./feat-form-config-hook-unified.md)). + +--- + +## Описание + +Режим визуализации в **normalization-tool** использует **тот же** `FormRenderer`, что и **transpile-viewer**. Два режима tool: **editor** (холст, DnD, свойства) и **viewer** (превью через `FormRenderer`). Визуализация в превью идентична внешнему потребителю — один рендер-движок, один контур модулей. + +--- + +## Реализация + +| Артефакт | Назначение | +|----------|-----------| +| `vite.config.ts` → `@viewer` | Алиас на `transpile-viewer/src`; `resolve.dedupe` для react/hexa — один экземпляр зависимостей | +| `src/viewer-form-renderer.d.ts` | Ambient-модуль `@viewer/components/FormRenderer` — типы для `tsc` без компиляции исходника viewer | +| `src/components/ToolbarStaticPreview.tsx` | Шим: реэкспорт `ToolbarStaticPreview` из дескриптора тулбара для импорта `@/components/ToolbarStaticPreview` из `FormRenderer` | +| `App.tsx` → `` | В `previewMode` — `FormRenderer` с `elements`, `formDirectoryHandle`, `formKey` | + +Все `@/` внутри `FormRenderer` при сборке tool резолвятся в `tool/src/` — утилиты (`loadConfigHookModule`, `tableControlHexa`, `defaultGridHexaProps`), типы (`form-dsl`), компоненты. Viewer-исходник **не** входит в `tsconfig` tool (нет дублирования типов React/Hexa). + +--- + +## Требования + +| ID | Описание | Статус | +|----|----------|--------| +| `editor.preview.embed-viewer` | В превью — `FormRenderer` из viewer, без дублирования кода | ✅ | +| `editor.preview.same-import-graph` | Один контур `@/`-импортов, те же `configHook` и утилиты | ✅ | +| `editor.preview.ts-isolation` | Ambient `.d.ts` вместо `tsconfig`-включения viewer | ✅ | +| `editor.preview.toolbar-shim` | Шим `ToolbarStaticPreview` для `FormRenderer` | ✅ | +| `editor.preview.vite-dedupe` | `resolve.dedupe` react/hexa — единый экземпляр | ✅ | +| `editor.preview.scenario-hook` | Опциональный модуль моков/патча `state` для превью (черновик) | ⏳ | +| `editor.preview.grid-hook-cols` | `configHook` может вернуть `cols` для Grid; renderer — pad/truncate children | ✅ (п.3.2) | +| `editor.preview.table-hook-dims` | `configHook` может вернуть `dslCols`/`dslRows` для Table; rebuild matrix | ✅ (п.3.2) | +| `editor.preview.table-toolbar` | `toolbar` из хука — нативный Hexa; при отсутствии — статический превью DSL | ✅ (п.3.2) | +| `editor.wysiwyg.data-control-id` | Каждый контрол в `FormRenderer` обёрнут в `div[data-control-id]` — DOM-якорь для overlay | ✅ (п.3.3) | +| `editor.wysiwyg.overlay-selection` | `WysiwygCanvas`: transparent overlay, `elementFromPoint` → click-to-select, highlight с `ResizeObserver` | ✅ (п.3.3) | +| `editor.wysiwyg.props-panel` | Панель свойств видна в WYSIWYG-режиме; редактирование свойств через панель, undo/redo | ✅ (п.3.3) | +| `editor.wysiwyg.dnd` | DnD в WYSIWYG-режиме: шестерёнка = drag source, drop zones overlay, root reorder/insert, palette drop, grid/table cell move/insert | ✅ (п.3.3 it.2) | +| `editor.wysiwyg.dnd-utils` | `src/utils/dnd.ts`: shared DATA_ID_KEY/DATA_TYPE_KEY, getDropTypeAndOptions, tree mutation helpers; FormCanvas переходит на импорты из utils | ✅ (п.3.3 it.2) | +| `editor.wysiwyg.palette-both-modes` | Палитра компонентов видна в обоих режимах (Editor и WYSIWYG) | ✅ (п.3.3 it.2) | + +--- + +## Решённые вопросы (п.3.2 ✅) + +- ~~Синхронизация **`rows`/`cols`** у Grid/Table~~ → хук возвращает нужные размерности, renderer делает pad/truncate. +- ~~Единый контракт **тулбара**~~ → `toolbar` из хука заменяет DSL-превью; `rowSelection` — пропс Hexa Table, проходит через хук. +- **`editor.preview.scenario-hook`** — остаётся открытым, не привязан к п.3.2. + +--- + +## Ссылки + +- [plan-editor-preview.md](../plans/plan-editor-preview.md) +- [plan-form-config-hook-341.md](../plans/plan-form-config-hook-341.md) (п.3.4.1) +- [roadmap §3.1](../roadmap.md#normalization-roadmap-p31), [§3.2](../roadmap.md#normalization-roadmap-p32), [§3.3](../roadmap.md#normalization-roadmap-p33), [§3.4.1](../roadmap.md#normalization-roadmap-p341), [§3.4](../roadmap.md#normalization-roadmap-p34) diff --git a/docs/normalization/features/feat-form-config-hook-unified.md b/docs/normalization/features/feat-form-config-hook-unified.md new file mode 100644 index 00000000..22865805 --- /dev/null +++ b/docs/normalization/features/feat-form-config-hook-unified.md @@ -0,0 +1,58 @@ +# Фича: один `configHook` на форму (форменный модуль) + +**Статус:** ✅ п.3.4.1 реализован (роадмап [§3.4.1](../roadmap.md#normalization-roadmap-p341)). + +**Родитель:** DSL и исполнение, превью. Зависит от [feat-config-hook](./feat-config-hook.md), [feat-lazy-dsl-runtime](./feat-lazy-dsl-runtime.md), [feat-dsl-root-model](./feat-dsl-root-model.md). План: [plan-form-config-hook-341](../plans/plan-form-config-hook-341.md). + +--- + +## Цель + +Все динамические пропсы контролов формы задаются **одним** TS-модулем рядом с формой. Ключ диспатча — **`control.id`**. **Обратной совместимости** с моделью «`configHook` на каждом контроле» **нет**: поле на элементах дерева **не поддерживается** и **не сериализуется**. + +--- + +## Имя файла и привязка к форме + +- Каталог формы: **`dsl/{FormData.id}/`** (например `dsl/demo/`). +- Описание: **`{FormData.id}.schema.ts`**, хук: **`{FormData.id}.config-hook.ts`** (рядом в том же каталоге). +- В DSL хранится путь/ленивый импорт **только на уровне формы** (`FormData.configHook` — относительно каталога схемы, обычно `./id.config-hook.ts`). + +--- + +## Контракт модуля + +- **Default export:** функция **`(): Record`**. +- **`ConfigHookFn`:** та же сигнатура, что у контракта [feat-config-hook](./feat-config-hook.md): один аргумент **`FormSlice`**, результат — полный объект пропсов экземпляра компонента ДС **или `null`** (не монтировать). +- **`ControlId`:** строковый идентификатор контрола в дереве формы (**`control.id`**), совпадающий с ключами возвращаемого объекта. + +При отсутствии ключа **`control.id`** в объекте, возвращённом фабрикой, **отдельный per-control модуль не загружается** — используются только **статические** пропсы из DSL для данного узла (и прочая логика типа без изменений). + +### Сетка (`type: 'grid'`) — полишинг контракта + +- В **schema / DSL** у узла сетки хранятся только общие поля контрола и линейный массив **`children`** (ячейки по порядку, строка за строкой). Полей **`rows`** и **`cols`** в модели **нет** и в JSON/экспорт они **не попадают**; настраивать размерность сетки через инспектор **normalization-tool** нельзя. +- **Геометрия и пропсы Hexa ``** (включая обязательное число столбцов **`cols`**) задаются **только** функцией реестра для **`grid.id`** в форменном **configHook**: результат — **`Partial`** с **`cols` ≥ 1** (иначе рендерер и холст редактора показывают сообщение об ошибке контракта). Длина/укладка **`children`** подстраивается в рантайме (pad/truncate) по правилам, совпадающим с **`FormRenderer`** и холстом. +- Отдельного пункта в роадмапе под это изменение нет: это уточнение контракта и выравнивание редактора с рендерером. + +--- + +## Рантайм + +- Модуль загружается **один раз** на экземпляр формы в **`FormRenderer`** (кэш на время жизни формы; общий контур с [feat-lazy-dsl-runtime](./feat-lazy-dsl-runtime.md)). +- Реестр: результат вызова default export (фабрики). Вызов хука для контрола — **`registry[id](slice)`** при наличии `registry[id]`. + +--- + +## Редактор и демо + +- В **normalization-tool** путь к хуку задаётся как **единственный** артефакт динамической конфигурации для формы (без per-control полей в UI модели). +- **Демо:** каталог **`dsl/demo/`** — **`demo.schema.ts`** и **`demo.config-hook.ts`**. +- Файл **дефолтов настроек** при создании хука и прочие пункты §3.4 (URL, WYSIWYG-спека отдельно, dev) — **не входят** в этот этап; **текущие дефолты в коде на этом этапе не меняются**. + +--- + +## Ссылки + +- [plan-form-config-hook-341](../plans/plan-form-config-hook-341.md) +- [feat-config-hook](./feat-config-hook.md) +- [roadmap §3.4](../roadmap.md#normalization-roadmap-p34) diff --git a/docs/normalization/features/feat-lazy-dsl-runtime.md b/docs/normalization/features/feat-lazy-dsl-runtime.md new file mode 100644 index 00000000..73e534d1 --- /dev/null +++ b/docs/normalization/features/feat-lazy-dsl-runtime.md @@ -0,0 +1,32 @@ +# Фича: ленивый рантайм DSL (parseFormTs, Sucrase, чанки) + +**Родитель:** DSL и исполнение. Роадмап **п.2.2** ✅. + +--- + +## Описание + +Тяжёлый путь **Sucrase** + парсинг **`.ts`** форм + сериализация **TS/JSON** + транспиляция **`configHook`** выносится в **отдельный async-чанк**, подгружаемый через **`loadFormDslBrowserRuntime()`**, чтобы основной бандл tool/viewer не включал Sucrase до первого запроса. + +--- + +## Требования + +- **`runtime.lazy.entry`:** **`loadFormDslBrowserRuntime()`** (`shared/normalization-form-dsl/load-form-dsl-runtime.ts`) кэширует один **`import('./form-dsl-browser-runtime')`**. +- **`runtime.chunk.exports`:** чанк предоставляет **`parseFormTs`**, **`formToTs`**, **`formToJsonString`**, **`transpileConfigHookSource`**, **`isConfigHookPathTs`**. +- **`runtime.parse.split`:** **`parseFormTs`** в **`parse-form-ts.ts`**; **`form-dsl-core.ts`** не импортирует Sucrase. +- **`runtime.serialize.dynamic-imports`:** **`formToTs`** записывает **`configHook`** и **`handlers`** как **`() => import('./относительный-путь')`** для ленивой подгрузки при исполнении модуля формы из blob. +- **`runtime.call-sites`:** tool — **`useFormFile`**, **`CodeExportDialog`**, **`loadConfigHookModule`**; viewer — **`useFormLoader`**, **`loadConfigHookModule`**. +- **`runtime.public-api`:** типы и операции дерева без парсинга — из **`@normalization/form-dsl`**; **`parseFormTs`** для скриптов/тестов — прямой импорт **`parse-form-ts.ts`**. + +--- + +## Тесты + +- `form-dsl-browser-runtime.test.ts`, `transpile-config-hook-module.test.ts`, `form-dsl.test.ts` (Vitest, пакет normalization-tool). + +--- + +## Ссылки + +- [plan-dsl-typing.md](../plans/plan-dsl-typing.md) (требования эпика п.2) diff --git a/docs/normalization/features/feat-product-and-parity.md b/docs/normalization/features/feat-product-and-parity.md new file mode 100644 index 00000000..2a5399eb --- /dev/null +++ b/docs/normalization/features/feat-product-and-parity.md @@ -0,0 +1,36 @@ +# Фича: продукт, границы и паритет приложений + +**Родитель:** решение config-driven (см. [tooling.md](../tooling.md)). + +--- + +## Описание + +Зафиксированы **границы продукта** (откуда берутся контролы в редакторе) и правило **паритета** двух приложений монорепозитория. + +--- + +## Требования + +- **`product.control-sources`:** в редакторе допустимы только **(1)** компоненты **`@kaspersky/hexa-ui`** и **(2)** кастомные компоненты, подключаемые отдельно; третьих классов источников нет. +- **`product.parity`:** возможность из роадмапа считается закрытой только при согласованном DSL, семантике и поведении в **normalization-tool** и **transpile-viewer** без отставания одного из пакетов. + +--- + +## Два приложения + +| | **Normalization Tool** | **Transpile Viewer** | +|---|------------------------|----------------------| +| **Пакет** | `packages/kaspersky-ui-normalization-tool` | `packages/kaspersky-ui-normalization-transpile-viewer` | +| **Роль** | Сборка и правка формы | Только просмотр | +| **Редактирование** | Холст, DnD, свойства, undo/redo | Нет | +| **Файлы форм** | **`.ts`** (File System Access API) | То же | +| **Рендер** | Холст (editor) + **`FormRenderer`** (превью, [п.3.1](../roadmap.md#normalization-roadmap-p31)) | **`FormRenderer`** | +| **Тесты** | Vitest, Playwright e2e | По необходимости | + +--- + +## Ссылки + +- [roadmap → границы](../roadmap.md) (цель раздела) +- Код: границы продукта соблюдаются реестром контролов и политикой палитры в tool. diff --git a/docs/normalization/features/feat-repo-layout.md b/docs/normalization/features/feat-repo-layout.md new file mode 100644 index 00000000..6e349ec1 --- /dev/null +++ b/docs/normalization/features/feat-repo-layout.md @@ -0,0 +1,47 @@ +# Фича: структура кода в репозитории + +**Родитель:** репозиторий. + +--- + +## Описание + +Расположение документации, пакетов приложений и **shared** DSL в монорепозитории. + +--- + +## Дерево (ориентир) + +``` +docs/normalization/ + README.md + roadmap.md + tooling.md + features/ # каталог фич + plans/ # требования по эпикам +packages/kaspersky-ui-normalization-tool/ + src/utils/directoryHandleStorage.ts + src/utils/formUrlSync.ts + src/types/form-dsl.ts + src/controls/ + src/components/ +packages/kaspersky-ui-normalization-transpile-viewer/ + src/utils/directoryHandleStorage.ts + src/utils/formUrlSync.ts + src/types/form-dsl.ts + src/components/FormRenderer.tsx +shared/types/form.ts +shared/normalization-form-dsl/ + form-dsl.ts + form-dsl-core.ts + parse-form-ts.ts + load-form-dsl-runtime.ts + form-dsl-browser-runtime.ts + transpile-config-hook-module.ts +``` + +--- + +## Ссылки + +- Пакеты: `README.md` / `AGENTS.md` внутри каждого пакета diff --git a/docs/normalization/features/feat-typing-and-imports.md b/docs/normalization/features/feat-typing-and-imports.md new file mode 100644 index 00000000..dee34802 --- /dev/null +++ b/docs/normalization/features/feat-typing-and-imports.md @@ -0,0 +1,25 @@ +# Фича: типизация DSL и импорты потребителя + +**Родитель:** DSL и исполнение. Связана с [feat-lazy-dsl-runtime](./feat-lazy-dsl-runtime.md). + +--- + +## Описание + +Автор формы опирается на **`FormData`** из общего модуля; потребитель **viewer** как библиотеки подключает конфиги и модули через **динамический `import()`** / чанки, а не единый статический бандл всего каталога форм. + +--- + +## Требования + +- **`typing.author.form`:** модуль формы — **`export default`** с опорой на **`FormData`** (`satisfies`, аннотация, `.d.ts`, codegen — на выбор). +- **`typing.chain`:** целевая проверяемость цепочки **`FormSlice`** → контрол → пропсы Hexa в **`configHook`**. +- **`typing.state.flex`:** **`FormSlice.state`** — гибкий контракт (ориентир **`Record`**), без обязательной жёсткой связки со **`schema`** на текущем этапе ([feat-architecture-directions](./feat-architecture-directions.md)). +- **`typing.consumer.lazy`:** в проде — динамическая подгрузка конфигов форм и хуков (чанки), не монолитный бандл каталога. + +--- + +## Ссылки + +- [roadmap п.2](../roadmap.md) +- [tooling.md — обзор](../tooling.md) diff --git a/docs/normalization/features/feat-workspace-browser.md b/docs/normalization/features/feat-workspace-browser.md new file mode 100644 index 00000000..640a10a9 --- /dev/null +++ b/docs/normalization/features/feat-workspace-browser.md @@ -0,0 +1,33 @@ +# Фича: рабочее пространство и браузер + +**Родитель:** решение (операционный контур). + +--- + +## Описание + +Оба приложения работают с **каталогом на диске** через **File System Access API**, запоминают выбор в **IndexedDB**, синхронизируют открытую форму с **URL**. + +--- + +## Требования + +- **`workspace.browser`:** **Chrome** или **Edge** (desktop) — выбор каталога и запись файлов. +- **`workspace.node`:** **Node.js 18+** для установки и скриптов в пакетах. +- **`workspace.indexeddb`:** база **`kaspersky-ui-normalization-fsa`**, ключи **`normalization-tool`** и **`transpile-viewer`**; при следующем визите — **`queryPermission` / `requestPermission`** (`read`); при **`granted`** диалог каталога не показывается. +- **`workspace.url.form`:** query **`form`** — относительный путь к **`.ts`** формы; обновление через **`history.replaceState`**; после перезагрузки форма открывается, если каталог восстановлен. +- **`workspace.e2e`:** при **`window.__E2E_DEMO__`** у tool отключены персистентность каталога и синхронизация **`form`** в URL. + +--- + +## Пользовательские сценарии + +1. **Разработка:** Tool → каталог → **`.ts`** → холст → сохранение; превью и экспорт по необходимости. +2. **Просмотр:** Viewer → тот же каталог → файл в дереве → проверка рендера. +3. **Передача артефактов:** каталог с **`.ts`** и **`handlers/`**; те же требования к браузеру. + +--- + +## Ссылки + +- Код: `directoryHandleStorage.ts`, `formUrlSync.ts` в обоих пакетах diff --git a/docs/normalization/plans/plan-button.md b/docs/normalization/plans/plan-button.md new file mode 100644 index 00000000..e2f4e124 --- /dev/null +++ b/docs/normalization/plans/plan-button.md @@ -0,0 +1,23 @@ +# Button: требования (роадмап п.1) + +**Статус:** ✅ выполнено +**Связанные фичи:** [feat-config-hook](../features/feat-config-hook.md), [feat-dsl-one-setting](../features/feat-dsl-one-setting.md), [feat-typing-and-imports](../features/feat-typing-and-imports.md) + +--- + +## Требования + +| ID | Фича | Описание | +|----|------|----------| +| `dsl.button.surface` | Модель **ButtonControl** в DSL | В DSL только **`id`** и опционально **`configHook`** (путь **`.ts`**). Статических полей пропсов Hexa (`text`, `mode`, `onClick`, …) нет. | +| `config-hook.button.contract` | Возврат хука | Модуль **`configHook`** вызывается с **`FormSlice`**; возвращает **`ButtonProps \| null`** (`@kaspersky/hexa-ui`). **`null`** — не монтировать ` + ))} + + +
+ Размер + +