poe-svelte-ui-lib 1.40.0 → 1.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,13 +15,19 @@ npm install poe-svelte-ui-lib
15
15
 
16
16
  ### Подключение стилей и темы
17
17
 
18
- Пакет не поставляет готовый CSS-файл — цветовые токены (`--back-color`, `--accent-color`, `--red-color` и т.д.) и типографика ожидаются от проекта-потребителя. Возьмите `src/app.css` этого репозитория как стартовый шаблон и скопируйте в свой проект (палитру можно переопределить своими значениями — имена переменных и структура классов `bg-*`/`border-*` должны остаться прежними, чтобы компоненты стилизовались корректно). В `app.css` потребителя также нужно указать Tailwind, откуда сканировать классы из собранного пакета:
18
+ Тема поставляется пакетом — `poe-svelte-ui-lib/theme.css`: цветовые токены светлой и тёмной темы (`--back-color`, `--accent-color`, `--red-color`…), типографика тегов в `@layer base`, компактный Markdown (`.markdown-content` — обёртка для `{@html}` с результатом `RenderMarkdown`) и роли заливки `bg-*`/`border-*`. Эталон темы — DevCloud; витрина подключает тот же файл, поэтому компоненты в ней выглядят ровно как в облаке. В `app.css` потребителя:
19
19
 
20
20
  ```css
21
21
  @import "tailwindcss";
22
+ @import "poe-svelte-ui-lib/theme.css";
22
23
  @source "../node_modules/poe-svelte-ui-lib/dist";
24
+ @custom-variant dark (&:where(.dark, .dark *));
25
+
26
+ /* дальше — только собственные отличия приложения, например :root { min-width: 640px; } */
23
27
  ```
24
28
 
29
+ Текст в Markdown, таблицы и код переносятся по ширине контейнера — горизонтальной прокрутки быть не должно, только вертикальная. Markdown рендерится с `breaks: true` (как в облаке), поэтому абзац и пункт списка пишутся одной строкой — ручной перенос внутри абзаца превращается в разрыв строки.
30
+
25
31
  **Глобальные стили тегов — только в `@layer base`.** Компоненты задают размеры текста и отступы Tailwind-утилитами (например `text-[12px]` у подписи `<h5>`). Правило вне слоя (`h5 { font-size: 1rem }`, `label { … }`, `p { … }`) сильнее любой утилиты, и в приложении подписи станут крупнее задуманного и перестанут помещаться. Поэтому типографику тегов оборачивайте:
26
32
 
27
33
  ```css
@@ -85,15 +91,11 @@ npm run Formatting # prettier --write .
85
91
 
86
92
  ### Публикация в npm
87
93
 
88
- Коммит в `main` с `[publish]` в сообщении (`"X.Y.Z [deploy] [publish]"`, версия поднята в `package.json`) запускает
89
- `.github/workflows/npm_publish.yaml`. Публикация идёт через **trusted publisher** npm (OIDC): workflow получает
90
- одноразовый токен от GitHub (`permissions: id-token: write`), постоянный `NPM_TOKEN` не нужен, provenance добавляется
91
- автоматически. Требования npm: npm CLI 11.5.1+ (workflow обновляет его сам), Node 22.14+, `repository.url` в
92
- `package.json` дословно совпадает с репозиторием.
94
+ Коммит в `main` с `[publish]` в сообщении (`"X.Y.Z [deploy] [publish]"`, версия поднята в `package.json`) запускает `.github/workflows/npm_publish.yaml`. Публикация идёт через **trusted publisher** npm (OIDC): workflow получает одноразовый токен от GitHub (`permissions: id-token: write`), постоянный `NPM_TOKEN` не нужен, provenance добавляется автоматически. Требования npm: npm CLI 11.5.1+ (workflow обновляет его сам), Node 22.14+, `repository.url` в `package.json` дословно совпадает с репозиторием.
95
+
96
+ Связь настраивается на npmjs.com → пакет → Settings → Trusted Publisher: GitHub Actions, `Oleg-Perevyshin/ElementsUI`, workflow `npm_publish.yaml`, разрешение «npm publish». Новая связь подтверждается первой успешной публикацией и должна пройти её в течение 2 дней, иначе истекает. Существующую связь изменить нельзя — только удалить и создать заново.
93
97
 
94
- Связь настраивается на npmjs.com → пакет → Settings → Trusted Publisher: GitHub Actions, `Oleg-Perevyshin/ElementsUI`,
95
- workflow `npm_publish.yaml`, разрешение «npm publish». Новая связь подтверждается первой успешной публикацией и
96
- должна пройти её в течение 2 дней, иначе истекает. Существующую связь изменить нельзя — только удалить и создать заново.
98
+ **Обновление DevCloud — автоматически.** После публикации job `update-devcloud` того же workflow ждёт новую версию в npm, клонирует `xPOE-Studio/DevCloud` (Forgejo) по секрету `FORGEJO_ACCESS_TOKEN`, ставит `poe-svelte-ui-lib@<версия>`, прогоняет `prisma generate` и `svelte-check` и, если проверка прошла, пушит в `main` коммит `poe-svelte-ui-lib <версия> [deploy]` — облако разворачивается само. Вручную версию в облаке не поднимаем; изменения кода облака, которым нужна новая версия библиотеки, коммитятся без `[deploy]` до выпуска — их развернёт этот коммит. Если `svelte-check` упал, коммита нет, облако остаётся на прежней версии (смотрите лог job в GitHub Actions).
97
99
 
98
100
  ---
99
101
 
@@ -21,30 +21,21 @@
21
21
 
22
22
  ### Адаптивность
23
23
 
24
- - Столбики растягиваются на всю ширину контейнера (`flex-1` на каждый), высота — в процентах от
25
- верха шкалы в зоне фиксированной высоты (`h-40`); ненулевое значение всегда видно минимум на 2 %
26
- - Шкала Y слева: подписи делений + горизонтальная сетка за столбиками (пунктир, базовая линия 0 —
27
- сплошная). Шкала пересчитывается автоматически при смене уровня или данных: `0…100`,
28
- `0…0.08` с шагом `0.02`, `0…3M`. Число знаков после запятой — по величине шага, для больших
29
- значений — суффиксы `k` (верх ≥ 10 000) и `M` (≥ 1 000 000)
30
- - Точное значение — в подсказке при наведении на столбик: 4 значащие цифры + `unit`
31
- (`910: 0.0734 мкВт/см²`), малые величины не теряются
32
- - Подписи по X (`labels[i]`/индекс+1) — отдельной строкой под столбиками, при большом числе
33
- столбиков прореживаются (не больше ~12 подписей)
34
- - Полностью на CSS-переменных темы (`--back-color`, `--accent-color`, `--hairline-color`,
35
- `--faint-color`) — тёмная/светлая тема не требует отдельной настройки
24
+ - Столбики растягиваются на всю ширину контейнера (`flex-1` на каждый), высота — в процентах от верха шкалы в зоне фиксированной высоты (`h-40`); ненулевое значение всегда видно минимум на 2 %
25
+ - Шкала Y слева: подписи делений + горизонтальная сетка за столбиками (пунктир, базовая линия 0 — сплошная). Шкала пересчитывается автоматически при смене уровня или данных: `0…100`, `0…0.08` с шагом `0.02`, `0…3M`. Число знаков после запятой — по величине шага, для больших значений — суффиксы `k` (верх ≥ 10 000) и `M` (≥ 1 000 000)
26
+ - Точное значение — в подсказке при наведении на столбик: 4 значащие цифры + `unit` (`910: 0.0734 мкВт/см²`), малые величины не теряются
27
+ - Подписи по X (`labels[i]`/индекс+1) — отдельной строкой под столбиками, при большом числе столбиков прореживаются (не больше ~12 подписей)
28
+ - Полностью на CSS-переменных темы (`--back-color`, `--accent-color`, `--hairline-color`, `--faint-color`) — тёмная/светлая тема не требует отдельной настройки
36
29
 
37
30
  ### Ограничения
38
31
 
39
- - Компонент презентационный — не хранит историю, не опрашивает устройство сам; при смене
40
- `levels` извне (например после ответа на `onLevelChange`) график перерисовывается автоматически
32
+ - Компонент презентационный — не хранит историю, не опрашивает устройство сам; при смене `levels` извне (например после ответа на `onLevelChange`) график перерисовывается автоматически
41
33
  - Отрицательные значения не отображаются — шкала всегда начинается с 0
42
34
  - Округление только для отображения — исходные данные в `levels[].data` не изменяются
43
35
 
44
36
  ### Производительность
45
37
 
46
- - Никакого `<canvas>` — чистый HTML/CSS, подходит для умеренного числа столбиков (проверено на
47
- вплоть до 31); для сотен/тысяч точек стоит использовать `Graph`
38
+ - Никакого `<canvas>` — чистый HTML/CSS, подходит для умеренного числа столбиков (проверено на вплоть до 31); для сотен/тысяч точек стоит использовать `Graph`
48
39
 
49
40
  ## Пропсы
50
41
 
@@ -63,8 +54,7 @@
63
54
 
64
55
  ### Колбэки
65
56
 
66
- Компонент не генерирует DOM-события — используйте колбэк `onLevelChange` для реакции на смену
67
- уровня (например чтобы догрузить свежие данные для только что выбранного уровня).
57
+ Компонент не генерирует DOM-события — используйте колбэк `onLevelChange` для реакции на смену уровня (например чтобы догрузить свежие данные для только что выбранного уровня).
68
58
 
69
59
  ## Примеры
70
60
 
@@ -80,9 +70,7 @@
80
70
 
81
71
  ### Реальные данные с устройства (например BL0910: PWR.EYear/EMonth/EDay/EHour)
82
72
 
83
- `variable` в каждом уровне — это имя Cfg-ключа устройства (`PWR.EHour` и т.п.), по которому
84
- конструктор облака подписывается через `eventHandler.Variables` и подставляет актуальный `data`;
85
- сам компонент значение `variable` не читает, только рисует уже готовый `data`.
73
+ `variable` в каждом уровне — это имя Cfg-ключа устройства (`PWR.EHour` и т.п.), по которому конструктор облака подписывается через `eventHandler.Variables` и подставляет актуальный `data`; сам компонент значение `variable` не читает, только рисует уже готовый `data`.
86
74
 
87
75
  ```svelte
88
76
  <script>
@@ -108,9 +96,7 @@
108
96
 
109
97
  ### Описание
110
98
 
111
- Компонент `PeriodChartProps.svelte` предоставляет визуальный интерфейс для редактирования свойств
112
- `PeriodChart`. Поддерживает два режима отображения: для конструктора и для редактирования —
113
- идентично остальным компонентам библиотеки (`ProgressBarProps`, `TabsProps`).
99
+ Компонент `PeriodChartProps.svelte` предоставляет визуальный интерфейс для редактирования свойств `PeriodChart`. Поддерживает два режима отображения: для конструктора и для редактирования — идентично остальным компонентам библиотеки (`ProgressBarProps`, `TabsProps`).
114
100
 
115
101
  ### Пропсы конструктора
116
102
 
@@ -122,20 +108,9 @@
122
108
 
123
109
  ### Особенности конструктора
124
110
 
125
- - Общие поля через `CommonSnippets`: `Access` (без `viewOnly` — компонент чисто отображающий, как
126
- `Graph`/`Tabs`/`ProgressBar`), `Colors` (пишет в `wrapperClass`, тот же цвет уходит на
127
- переключатель и столбики), `Label`; в режиме редактирования дополнительно `Identificator`,
128
- `WrapperClass`
111
+ - Общие поля через `CommonSnippets`: `Access` (без `viewOnly` — компонент чисто отображающий, как `Graph`/`Tabs`/`ProgressBar`), `Colors` (пишет в `wrapperClass`, тот же цвет уходит на переключатель и столбики), `Label`; в режиме редактирования дополнительно `Identificator`, `WrapperClass`
129
112
  - `unit` — отдельное текстовое поле ввода
130
- - Редактор уровней (`levels[]`) — тот же паттерн повторяющегося списка, что у `TabsSettings`/
131
- `ProgressBarOptions`: перетаскивание (`UI.Dragging`) для порядка вкладок, добавление/удаление
132
- через `ButtonAdd`/`ButtonDelete`, на каждый уровень — поля `name`, `variable`, `data` и
133
- `labels`. `variable` — тот же `Select` по списку переменных устройства (`DeviceVariables` из
134
- контекста), что и у `Graph`/`CommonSnippets.Variable`, только по одному на каждый уровень; при
135
- любом изменении списка уровней редактор пересобирает `eventHandler.Variables` как объединение
136
- всех непустых `variable` со всех уровней — так рантайм подписывается сразу на все нужные ключи.
137
- `data`/`labels` остаются полями для литеральных демо-данных — массивы чисел/строк вводятся одной
138
- строкой через запятую (`"10, 20, 30"`) и парсятся на лету
113
+ - Редактор уровней (`levels[]`) — тот же паттерн повторяющегося списка, что у `TabsSettings`/ `ProgressBarOptions`: перетаскивание (`UI.Dragging`) для порядка вкладок, добавление/удаление через `ButtonAdd`/`ButtonDelete`, на каждый уровень — поля `name`, `variable`, `data` и `labels`. `variable` — тот же `Select` по списку переменных устройства (`DeviceVariables` из контекста), что и у `Graph`/`CommonSnippets.Variable`, только по одному на каждый уровень; при любом изменении списка уровней редактор пересобирает `eventHandler.Variables` как объединение всех непустых `variable` со всех уровней — так рантайм подписывается сразу на все нужные ключи. `data`/`labels` остаются полями для литеральных демо-данных — массивы чисел/строк вводятся одной строкой через запятую (`"10, 20, 30"`) и парсятся на лету
139
114
 
140
115
  ### Использование
141
116
 
@@ -167,9 +142,7 @@
167
142
 
168
143
  - `currentLevelIndex` (`$state`) — индекс активной вкладки, всегда инициализируется `0` (первый уровень)
169
144
  - `currentLevel`/`values` (`$derived`) — текущий уровень и его массив значений
170
- - `axis` (`$derived`) — шкала Y по максимуму текущего массива: шаг из ряда 1 / 2 / 2.5 / 5 × 10ⁿ
171
- (~4 деления), верх шкалы = максимум, округлённый вверх до шага, низ всегда 0; высота столбиков
172
- считается от верха шкалы
145
+ - `axis` (`$derived`) — шкала Y по максимуму текущего массива: шаг из ряда 1 / 2 / 2.5 / 5 × 10ⁿ (~4 деления), верх шкалы = максимум, округлённый вверх до шага, низ всегда 0; высота столбиков считается от верха шкалы
173
146
 
174
147
  ### Сторы и зависимости
175
148
 
@@ -27,21 +27,10 @@
27
27
 
28
28
  ## Особенности
29
29
 
30
- Смарт-виджет информации об устройстве. Как и `WidgetWiFi`, не привязывается к одной произвольной
31
- переменной устройства — работает с фиксированным набором полей, соответствующих реальной
32
- прошивочной конвенции (`main_config.c`, группа `CFG`, ProdFactory-ESP): `DevSN`, `DevID`, `DevFW`,
33
- `RunCnt`, `StaIP`, `ApIP` — только чтение; `DevName`, `HostName`, `WebUser`, `WebPsw` —
34
- редактируются локально и сохраняются одним пакетом по кнопке "Сохранить". Отдельная кнопка
35
- "Перезагрузить" рядом с заголовком вызывает `onRestart` немедленно (не входит в пакет сохранения
36
- полей). Реальное имя поля устройства для каждого ключа можно переопределить через `keys` (по
37
- умолчанию совпадает с именем ключа). Компонент ничего не знает про DeviceStore/WebSocket —
38
- только `value`/`onSave`/`onRestart`.
39
-
40
- - Серийный номер устройства обычно длинный (например
41
- `00.00.000-00_0000AC670000B22B00007428:DBC5`) — в разметке занимает отдельную строку, чтобы не
42
- обрезаться в узкой сетке.
43
- - `WebPsw` в реальном устройстве исторически передаётся обычным текстом; в виджете сделан
44
- `type="password"` для консистентности с остальным обликом конструктора.
30
+ Смарт-виджет информации об устройстве. Как и `WidgetWiFi`, не привязывается к одной произвольной переменной устройства — работает с фиксированным набором полей, соответствующих реальной прошивочной конвенции (`main_config.c`, группа `CFG`, ProdFactory-ESP): `DevSN`, `DevID`, `DevFW`, `RunCnt`, `StaIP`, `ApIP` — только чтение; `DevName`, `HostName`, `WebUser`, `WebPsw` — редактируются локально и сохраняются одним пакетом по кнопке "Сохранить". Отдельная кнопка "Перезагрузить" рядом с заголовком вызывает `onRestart` немедленно (не входит в пакет сохранения полей). Реальное имя поля устройства для каждого ключа можно переопределить через `keys` (по умолчанию совпадает с именем ключа). Компонент ничего не знает про DeviceStore/WebSocket — только `value`/`onSave`/`onRestart`.
31
+
32
+ - Серийный номер устройства обычно длинный (например `00.00.000-00_0000AC670000B22B00007428:DBC5`) — в разметке занимает отдельную строку, чтобы не обрезаться в узкой сетке.
33
+ - `WebPsw` в реальном устройстве исторически передаётся обычным текстом; в виджете сделан `type="password"` для консистентности с остальным обликом конструктора.
45
34
 
46
35
  ## Пропсы
47
36
 
@@ -109,10 +98,7 @@
109
98
  | `component` | `UIComponent & { properties: Partial<IWidgetDeviceInfoProps> }` | Объект компонента с его свойствами |
110
99
  | `onPropertyChange` | `(updates: Partial<{ properties?: string \| object; name?: string; access?: string }>) => void` | Коллбэк для обновления свойств компонента |
111
100
 
112
- Панель содержит заголовок, мастер-цвет (через общий `WidgetAccentPicker.svelte`), префикс
113
- прошивочной группы (ID), переопределение имени поля устройства для каждого ключа
114
- `IWidgetDeviceInfoConfig` (по умолчанию совпадает с именем ключа) и команду перезагрузки
115
- (Header/Argument, по умолчанию `SET`/`Restart`).
101
+ Панель содержит заголовок, мастер-цвет (через общий `WidgetAccentPicker.svelte`), префикс прошивочной группы (ID), переопределение имени поля устройства для каждого ключа `IWidgetDeviceInfoConfig` (по умолчанию совпадает с именем ключа) и команду перезагрузки (Header/Argument, по умолчанию `SET`/`Restart`).
116
102
 
117
103
  ### Что настраивается
118
104
 
@@ -25,10 +25,7 @@
25
25
  }
26
26
  ```
27
27
 
28
- `Upload` есть, только пока идёт закачка. Снимок со списком `List` приходит на `GET ModCfg`,
29
- `GET FS`, после удаления, после завершения или ошибки закачки и в рассылке `OK! Update`; ход
30
- закачки — только `{"Upload": {...}}` (без списка, раз в 5%). Объект со списком заменяет состояние
31
- целиком, без списка — дописывается поверх (это делает вызывающий код).
28
+ `Upload` есть, только пока идёт закачка. Снимок со списком `List` приходит на `GET ModCfg`, `GET FS`, после удаления, после завершения или ошибки закачки и в рассылке `OK! Update`; ход закачки — только `{"Upload": {...}}` (без списка, раз в 5%). Объект со списком заменяет состояние целиком, без списка — дописывается поверх (это делает вызывающий код).
32
29
 
33
30
  Команды — один аргумент `FS`, операция в поле `Op`; ответ всегда `FS` с тем же `Op`:
34
31
 
@@ -52,21 +49,11 @@
52
49
 
53
50
  ## Особенности
54
51
 
55
- Смарт-виджет файловой системы устройства: занятость памяти, список файлов с удалением и закачка
56
- нового файла с прогрессом. Работает с одним объектом `CFG.FS` ровно в том виде, в каком его отдаёт
57
- прошивка (`FS_WriteState`, ProdFactory-ESP), и с одной командой `FS`. Компонент ничего не знает про
58
- DeviceStore/WebSocket — только `value` и колбэки `onRefresh`/`onDelete`/`onUpload`, вся отправка
59
- (в том числе протокол закачки) на стороне вызывающего кода.
60
-
61
- - **Занятость** — «занято X из Y», процент и полоса мастер-цвета; процент оранжевый от 75%,
62
- красный от 90%. Размеры в Б/КБ/МБ.
63
- - **Файлы** — таблица «Путь / Размер / Удалить»; длинный путь обрезается «…», удаление
64
- подтверждается внутри карточки. Пустая ФС — «Файлов нет». Колонка удаления есть, только если
65
- передан `onDelete`.
66
- - **Закачка** — блок есть, только если передан `onUpload`. Имя файла проверяется до отправки
67
- (`maxNameLength`, по умолчанию 31 — ограничение прошивки). Прогресс берётся из `value.Upload`
68
- и виден, пока закачку ведёт этот клиент или пока устройство сообщает `Upload` (закачка с
69
- другого клиента). Ошибка из `onUpload` выводится под полем.
52
+ Смарт-виджет файловой системы устройства: занятость памяти, список файлов с удалением и закачка нового файла с прогрессом. Работает с одним объектом `CFG.FS` ровно в том виде, в каком его отдаёт прошивка (`FS_WriteState`, ProdFactory-ESP), и с одной командой `FS`. Компонент ничего не знает про DeviceStore/WebSocket — только `value` и колбэки `onRefresh`/`onDelete`/`onUpload`, вся отправка (в том числе протокол закачки) на стороне вызывающего кода.
53
+
54
+ - **Занятость** — «занято X из Y», процент и полоса мастер-цвета; процент оранжевый от 75%, красный от 90%. Размеры в Б/КБ/МБ.
55
+ - **Файлы** — таблица «Путь / Размер / Удалить»; длинный путь обрезается «…», удаление подтверждается внутри карточки. Пустая ФС — «Файлов нет». Колонка удаления есть, только если передан `onDelete`.
56
+ - **Закачка** — блок есть, только если передан `onUpload`. Имя файла проверяется до отправки (`maxNameLength`, по умолчанию 31 — ограничение прошивки). Прогресс берётся из `value.Upload` и виден, пока закачку ведёт этот клиент или пока устройство сообщает `Upload` (закачка с другого клиента). Ошибка из `onUpload` выводится под полем.
70
57
 
71
58
  ## Пропсы
72
59
 
@@ -122,8 +109,7 @@ DeviceStore/WebSocket — только `value` и колбэки `onRefresh`/`on
122
109
  | `component` | `UIComponent & { properties: Partial<IWidgetFileSystemProps> }` | Объект компонента с его свойствами |
123
110
  | `onPropertyChange` | `(updates: Partial<{ properties?: string \| object; name?: string; access?: string }>) => void` | Коллбэк для обновления свойств компонента |
124
111
 
125
- Группы панели: общие (заголовок, префикс группы, мастер-цвет), закачка (`accept`,
126
- `maxNameLength`), устройство (поле `FS`, аргумент команд).
112
+ Группы панели: общие (заголовок, префикс группы, мастер-цвет), закачка (`accept`, `maxNameLength`), устройство (поле `FS`, аргумент команд).
127
113
 
128
114
  ### Что настраивается
129
115
 
@@ -14,16 +14,10 @@
14
14
 
15
15
  ### Как работать с картой
16
16
 
17
- 1. **Устройство** шлёт поля группы в поток телеметрии (`Telemetry_SendObject`, ключ `Data`) — облако и WebApp
18
- раскладывают их в переменные `<ModSN>|<группа>.<ключ>`. Координаты — раз в 0.5–1 с, имя и серийный номер —
19
- реже (раз в несколько секунд) отдельным кадром: буфер `Telemetry_SendObject` — 512 байт, строки его быстро съедают.
20
- 2. **В конструкторе** добавьте «Карту» из смарт-виджетов и перечислите группы через пробел (`MAV` или
21
- `MAV GNSS`). Переменные привязывать не нужно — виджет сам берёт ключи из таблицы ниже.
22
- 3. **Оператор** поверх карты включает траекторию, выбирает число её точек и таймаут маркеров. Кнопка
23
- **«Все борта»** в шапке виджета вписывает все видимые борта в экран (один борт — приближение к нему).
24
- Сама карта вид не меняет — бортов может быть много.
25
- 4. **Клик по маркеру** открывает карточку борта: имя, группа, координаты, высота, курс, спутники, серийный
26
- номер; кнопки «Копировать» (всё в буфер обмена) и «Скрыть» (до следующего обновления борта).
17
+ 1. **Устройство** шлёт поля группы в поток телеметрии (`Telemetry_SendObject`, ключ `Data`) — облако и WebApp раскладывают их в переменные `<ModSN>|<группа>.<ключ>`. Координаты — раз в 0.5–1 с, имя и серийный номер — реже (раз в несколько секунд) отдельным кадром: буфер `Telemetry_SendObject` — 512 байт, строки его быстро съедают.
18
+ 2. **В конструкторе** добавьте «Карту» из смарт-виджетов и перечислите группы через пробел (`MAV` или `MAV GNSS`). Переменные привязывать не нужно — виджет сам берёт ключи из таблицы ниже.
19
+ 3. **Оператор** поверх карты включает траекторию, выбирает число её точек и таймаут маркеров. Кнопка **«Все борта»** в шапке виджета вписывает все видимые борта в экран (один борт — приближение к нему). Сама карта вид не меняет — бортов может быть много.
20
+ 4. **Клик по маркеру** открывает карточку борта: имя, группа, координаты, высота, курс, спутники, серийный номер; кнопки «Копировать» (всё в буфер обмена) и «Скрыть» (до следующего обновления борта).
27
21
 
28
22
  ```c
29
23
  /* Прошивка: группа MAV — те же ключи читает и WidgetPFD, одна отправка кормит оба виджета */
@@ -52,15 +46,11 @@ Telemetry_SendObject(g_telemetry_stream, g_mcfg.DevSN, "Data", dev, sizeof(dev)
52
46
 
53
47
  ## Особенности
54
48
 
55
- Смарт-виджет «Карта» — положение бортов на карте MapLibre с траекториями. Каждый борт — одна **группа полей**
56
- устройства (префикс, например `MAV`); виджет показывает сразу несколько групп, например `MAV GNSS` — автопилот
57
- и собственный GNSS-приёмник модуля. Только отображение: ничего не отправляет и не знает про DeviceStore/WebSocket.
58
- Заменяет удалённый компонент `Map`.
49
+ Смарт-виджет «Карта» — положение бортов на карте MapLibre с траекториями. Каждый борт — одна **группа полей** устройства (префикс, например `MAV`); виджет показывает сразу несколько групп, например `MAV GNSS` — автопилот и собственный GNSS-приёмник модуля. Только отображение: ничего не отправляет и не знает про DeviceStore/WebSocket. Заменяет удалённый компонент `Map`.
59
50
 
60
51
  ### Поведение
61
52
 
62
- - **Свежесть.** Борт зелёный, пока его значения менялись за таймаут; без изменений — красный; ещё через таймаут
63
- скрывается до новых данных.
53
+ - **Свежесть.** Борт зелёный, пока его значения менялись за таймаут; без изменений — красный; ещё через таймаут скрывается до новых данных.
64
54
  - **Траектория** — точка добавляется только при смене координат (курс и высота могут обновляться чаще позиции).
65
55
  - **Только 2D** — наклон (3D) запрещён; правой кнопкой мыши карта только поворачивается, кнопка-компас под зумом возвращает её на север.
66
56
  - **Шапка** — сколько бортов на связи из заданных: зелёный — все, серый — часть, красный — ни одного.
@@ -94,8 +84,7 @@ Telemetry_SendObject(g_telemetry_stream, g_mcfg.DevSN, "Data", dev, sizeof(dev)
94
84
 
95
85
  ## Конструктор свойств (WidgetMapProps.svelte)
96
86
 
97
- Группы панели: общие (заголовок, группы полей через пробел, мастер-цвет), оформление (высота карты S/M/L/XL — 250/400/600/800 px, иконка
98
- маркера), по умолчанию (траектория и её длина — оператор меняет их на карте).
87
+ Группы панели: общие (заголовок, группы полей через пробел, мастер-цвет), оформление (высота карты S/M/L/XL — 250/400/600/800 px, иконка маркера), по умолчанию (траектория и её длина — оператор меняет их на карте).
99
88
 
100
89
  ### Что настраивается
101
90
 
@@ -105,6 +94,4 @@ Telemetry_SendObject(g_telemetry_stream, g_mcfg.DevSN, "Data", dev, sizeof(dev)
105
94
 
106
95
  ## Внутреннее устройство
107
96
 
108
- `mapWrapper/` — тонкие Svelte-обёртки над MapLibre GL (`MapLibre`, `Marker`, `Popup`, `Line`, контролы,
109
- `CustomControl` для своих элементов поверх карты). MapLibre подгружается при монтировании; без интернета
110
- показывается заглушка «Карта не загружена».
97
+ `mapWrapper/` — тонкие Svelte-обёртки над MapLibre GL (`MapLibre`, `Marker`, `Popup`, `Line`, контролы, `CustomControl` для своих элементов поверх карты). MapLibre подгружается при монтировании; без интернета показывается заглушка «Карта не загружена».
@@ -49,20 +49,13 @@
49
49
 
50
50
  ## Особенности
51
51
 
52
- Смарт-виджет «Пилотажный дисплей» (Primary Flight Display) — главный прибор контроля полёта, как в
53
- стеклянной кабине самолёта или наземной станции управления. Работает с полями группы (по умолчанию
54
- `MAV`) ровно в том виде, в каком их отдаёт телеметрия автопилота MAVLink устройства 20.00.000-01.
55
- Только отображение: ничего не отправляет и не знает про DeviceStore/WebSocket.
56
-
57
- - **Строка состояния** — ARMED/DISARMED, режим полёта (название по типу аппарата: самолёт, коптер,
58
- наземный), GPS (тип фиксации и спутники), батарея (В, А, %), расстояние и пеленг на точку взлёта.
59
- - **Авиагоризонт** — небо и земля по крену и тангажу, лестница тангажа через 5°, дуга крена
60
- (10/20/30/45/60°), символ самолёта. Ориентация сглаживается между пакетами (Tween 120 мс).
61
- - **Лента скорости** (слева, путевая) с воздушной числом, **лента высоты** (справа, над точкой взлёта)
62
- с вариометром, **лента курса** (снизу) с меткой трека (розовая) и направления на дом (зелёная).
52
+ Смарт-виджет «Пилотажный дисплей» (Primary Flight Display) — главный прибор контроля полёта, как в стеклянной кабине самолёта или наземной станции управления. Работает с полями группы (по умолчанию `MAV`) ровно в том виде, в каком их отдаёт телеметрия автопилота MAVLink устройства 20.00.000-01. Только отображение: ничего не отправляет и не знает про DeviceStore/WebSocket.
53
+
54
+ - **Строка состояния** — ARMED/DISARMED, режим полёта (название по типу аппарата: самолёт, коптер, наземный), GPS (тип фиксации и спутники), батарея (В, А, %), расстояние и пеленг на точку взлёта.
55
+ - **Авиагоризонт** — небо и земля по крену и тангажу, лестница тангажа через 5°, дуга крена (10/20/30/45/60°), символ самолёта. Ориентация сглаживается между пакетами (Tween 120 мс).
56
+ - **Лента скорости** (слева, путевая) с воздушной числом, **лента высоты** (справа, над точкой взлёта) с вариометром, **лента курса** (снизу) с меткой трека (розовая) и направления на дом (зелёная).
63
57
  - **Под прибором** — координаты, высота над уровнем моря, давление, температура.
64
- - **Нет данных** — если пакеты не приходили дольше `limits.staleMs` (по умолчанию 2 с), прибор
65
- перечёркивается и помечается «НЕТ ДАННЫХ»; в шапке всегда виден возраст данных.
58
+ - **Нет данных** — если пакеты не приходили дольше `limits.staleMs` (по умолчанию 2 с), прибор перечёркивается и помечается «НЕТ ДАННЫХ»; в шапке всегда виден возраст данных.
66
59
  - **Узкая ячейка** (уже 440 px) — ленты скорости и высоты заменяются числами поверх горизонта.
67
60
 
68
61
  ## Пропсы
@@ -24,8 +24,7 @@
24
24
  "LST.MaxCount": 64 // ёмкость из прошивки, только чтение
25
25
  ```
26
26
 
27
- Изменения — `SET ModCfg` с нужными ключами группы. Пин данных и способ передачи (RMT/SPI) задаются
28
- в прошивке (`RGB_Strip_Init`) и через виджет не меняются.
27
+ Изменения — `SET ModCfg` с нужными ключами группы. Пин данных и способ передачи (RMT/SPI) задаются в прошивке (`RGB_Strip_Init`) и через виджет не меняются.
29
28
 
30
29
  ## Ключи
31
30
 
@@ -41,15 +40,10 @@
41
40
 
42
41
  ## Особенности
43
42
 
44
- Смарт-виджет адресной светодиодной ленты. Работает с полями группы `LST` ровно в том виде, в каком их
45
- отдаёт драйвер `RGB_Strip` (ProdFactory-ESP). Компонент ничего не знает про DeviceStore/WebSocket —
46
- только `value` и колбэки `onChange`/`onSave`.
43
+ Смарт-виджет адресной светодиодной ленты. Работает с полями группы `LST` ровно в том виде, в каком их отдаёт драйвер `RGB_Strip` (ProdFactory-ESP). Компонент ничего не знает про DeviceStore/WebSocket — только `value` и колбэки `onChange`/`onSave`.
47
44
 
48
- - **Свечение** — палитра цвета, белый канал (только для модели SK6812), эффект и скорость. Каждое
49
- изменение сразу уходит в `onChange`.
50
- - **Оборудование** — количество светодиодов (1…`MaxCount`), модель чипа и порядок цветов.
51
- Редактируются локально и отправляются одним пакетом по «Сохранить»: прошивка пересоздаёт драйвер
52
- ленты. Пока правок нет, поля показывают свежие значения с устройства.
45
+ - **Свечение** — палитра цвета, белый канал (только для модели SK6812), эффект и скорость. Каждое изменение сразу уходит в `onChange`.
46
+ - **Оборудование** — количество светодиодов (1…`MaxCount`), модель чипа и порядок цветов. Редактируются локально и отправляются одним пакетом по «Сохранить»: прошивка пересоздаёт драйвер ленты. Пока правок нет, поля показывают свежие значения с устройства.
53
47
  - В шапке — образец текущего цвета.
54
48
 
55
49
  ## Пропсы
@@ -23,10 +23,7 @@
23
23
  }
24
24
  ```
25
25
 
26
- Все размеры в байтах. ESP отдаёт две кучи (`Internal` — внутренний DRAM, `Default` — всё, что
27
- отдаёт `malloc`, включая PSRAM) и `Core` (-1 — без привязки к ядру); STM — одну кучу `Heap`
28
- (heap_4, `configTOTAL_HEAP_SIZE`) и без `Core`. `State` — `eTaskState`: 0 работает, 1 готова,
29
- 2 ожидает, 3 приостановлена, 4 удалена.
26
+ Все размеры в байтах. ESP отдаёт две кучи (`Internal` — внутренний DRAM, `Default` — всё, что отдаёт `malloc`, включая PSRAM) и `Core` (-1 — без привязки к ядру); STM — одну кучу `Heap` (heap_4, `configTOTAL_HEAP_SIZE`) и без `Core`. `State` — `eTaskState`: 0 работает, 1 готова, 2 ожидает, 3 приостановлена, 4 удалена.
30
27
 
31
28
  ## Ключи
32
29
 
@@ -37,19 +34,11 @@
37
34
 
38
35
  ## Особенности
39
36
 
40
- Отладочный смарт-виджет памяти и задач FreeRTOS: занятость каждой кучи и минимальный запас стека
41
- каждой задачи. Работает с одним объектом `StackInfo` ровно в том виде, в каком его отдаёт
42
- `API_GetStackInfo` (ProdFactory-ESP и ProdFactory-STM — контракт общий), и с одним запросом
43
- `GET StackInfo`. Компонент ничего не знает про DeviceStore/WebSocket — только `value` и `onRefresh`.
37
+ Отладочный смарт-виджет памяти и задач FreeRTOS: занятость каждой кучи и минимальный запас стека каждой задачи. Работает с одним объектом `StackInfo` ровно в том виде, в каком его отдаёт `API_GetStackInfo` (ProdFactory-ESP и ProdFactory-STM — контракт общий), и с одним запросом `GET StackInfo`. Компонент ничего не знает про DeviceStore/WebSocket — только `value` и `onRefresh`.
44
38
 
45
- - **Кучи** — карточка на каждую: «занято X из Y», процент (оранжевый от 75%, красный от 90%) и
46
- полоса мастер-цвета; светлая подложка полосы — пик занятости с момента старта (`Total − MinFree`).
47
- - **Задачи** — таблица «Задача / Состояние / Приоритет / Ядро / Запас». Порядок всегда по
48
- возрастанию запаса стека: самые рискованные сверху (сортировка кликом при автоопросе сбрасывалась
49
- бы на каждом ответе). Запас меньше `warnBytes` — красный, меньше удвоенного — оранжевый, над
50
- таблицей — число задач ниже порога. Колонка «Ядро» есть, только если устройство передаёт `Core`.
51
- - **Обновление** — кнопка «Обновить» и автоопрос «Выкл / 1 с / 5 с / 10 с» (`period`); при
52
- включении опрос сразу шлёт запрос, свёрнутый виджет не опрашивает.
39
+ - **Кучи** — карточка на каждую: «занято X из Y», процент (оранжевый от 75%, красный от 90%) и полоса мастер-цвета; светлая подложка полосы — пик занятости с момента старта (`Total − MinFree`).
40
+ - **Задачи** — таблица «Задача / Состояние / Приоритет / Ядро / Запас». Порядок всегда по возрастанию запаса стека: самые рискованные сверху (сортировка кликом при автоопросе сбрасывалась бы на каждом ответе). Запас меньше `warnBytes` — красный, меньше удвоенного — оранжевый, над таблицей — число задач ниже порога. Колонка «Ядро» есть, только если устройство передаёт `Core`.
41
+ - **Обновление** — кнопка «Обновить» и автоопрос «Выкл / 1 с / 5 с / 10 с» (`period`); при включении опрос сразу шлёт запрос, свёрнутый виджет не опрашивает.
53
42
 
54
43
  ## Пропсы
55
44
 
@@ -86,8 +75,7 @@
86
75
  | `component` | `UIComponent & { properties: Partial<IWidgetStackInfoProps> }` | Объект компонента с его свойствами |
87
76
  | `onPropertyChange` | `(updates: Partial<{ properties?: string \| object; name?: string; access?: string }>) => void` | Коллбэк для обновления свойств компонента |
88
77
 
89
- Группы панели: общие (заголовок, мастер-цвет), отображение (`warnBytes`, `period`), устройство
90
- (объект ответа, аргумент запроса).
78
+ Группы панели: общие (заголовок, мастер-цвет), отображение (`warnBytes`, `period`), устройство (объект ответа, аргумент запроса).
91
79
 
92
80
  ### Что настраивается
93
81
 
@@ -25,22 +25,12 @@
25
25
 
26
26
  ## Особенности
27
27
 
28
- Смарт-виджет настройки WiFi (режимы STA / AP / STA+AP). В отличие от примитивов конструктора,
29
- не привязывается к одной произвольной переменной устройства через Select — работает с
30
- фиксированным набором полей, соответствующих реальной прошивочной конвенции (`main_config.c`,
31
- группа `CFG`, ProdFactory-ESP): `WiFiMode`, `StaSSID`, `StaPSK`, `StaticIP`, `StaIP`, `StaMS`,
32
- `StaGW`, `ApSSID`, `ApPSK`, `ApIP`, `ApMS`, `ApGW`. Компонент ничего не знает про
33
- DeviceStore/WebSocket — только `value`/`onSave`, вся отправка на стороне вызывающего кода.
34
-
35
- Поля редактируются локально и отправляются одним пакетом по кнопке "Сохранить". Переход в
36
- режим AP (отключает STA) подтверждается отдельно — предупреждение появляется в момент нажатия
37
- "Сохранить", а не при простом переключении сегмент-контрола, чтобы можно было спокойно открыть
38
- секцию AP и заполнить SSID/пароль до реальной отправки.
39
-
40
- - Первый по-настоящему составной (multi-field) тип компонента конструктора — примитивы всегда
41
- привязаны к одной переменной устройства через `properties.id`.
42
- - Чтение/батч-запись нескольких ключей `DeviceStore` реализуется на стороне приложения
43
- (`GUIPreview.svelte`), а не внутри библиотеки.
28
+ Смарт-виджет настройки WiFi (режимы STA / AP / STA+AP). В отличие от примитивов конструктора, не привязывается к одной произвольной переменной устройства через Select — работает с фиксированным набором полей, соответствующих реальной прошивочной конвенции (`main_config.c`, группа `CFG`, ProdFactory-ESP): `WiFiMode`, `StaSSID`, `StaPSK`, `StaticIP`, `StaIP`, `StaMS`, `StaGW`, `ApSSID`, `ApPSK`, `ApIP`, `ApMS`, `ApGW`. Компонент ничего не знает про DeviceStore/WebSocket — только `value`/`onSave`, вся отправка на стороне вызывающего кода.
29
+
30
+ Поля редактируются локально и отправляются одним пакетом по кнопке "Сохранить". Переход в режим AP (отключает STA) подтверждается отдельно — предупреждение появляется в момент нажатия "Сохранить", а не при простом переключении сегмент-контрола, чтобы можно было спокойно открыть секцию AP и заполнить SSID/пароль до реальной отправки.
31
+
32
+ - Первый по-настоящему составной (multi-field) тип компонента конструктора — примитивы всегда привязаны к одной переменной устройства через `properties.id`.
33
+ - Чтение/батч-запись нескольких ключей `DeviceStore` реализуется на стороне приложения (`GUIPreview.svelte`), а не внутри библиотеки.
44
34
 
45
35
  ## Пропсы
46
36
 
@@ -102,9 +92,7 @@ DeviceStore/WebSocket — только `value`/`onSave`, вся отправка
102
92
  | `component` | `UIComponent & { properties: Partial<IWidgetWiFiProps> }` | Объект компонента с его свойствами |
103
93
  | `onPropertyChange` | `(updates: Partial<{ properties?: string \| object; name?: string; access?: string }>) => void` | Коллбэк для обновления свойств компонента |
104
94
 
105
- Панель проще, чем у примитивов: не "к какой переменной привязать" (полей несколько, и они
106
- фиксированы прошивочной конвенцией), а "как виджет себя ведёт" — заголовок, разрешённые режимы,
107
- подтверждение при AP, мастер-цвет (через общий `WidgetAccentPicker.svelte`).
95
+ Панель проще, чем у примитивов: не "к какой переменной привязать" (полей несколько, и они фиксированы прошивочной конвенцией), а "как виджет себя ведёт" — заголовок, разрешённые режимы, подтверждение при AP, мастер-цвет (через общий `WidgetAccentPicker.svelte`).
108
96
 
109
97
  ### Что настраивается
110
98
 
package/dist/theme.css ADDED
@@ -0,0 +1,545 @@
1
+ /* poe-svelte-ui-lib/theme.css — единая тема DevCloud, витрины ElementsUI и приложений на библиотеке:
2
+ токены палитры (.light/.dark), типографика тегов в @layer base, компактный Markdown (.markdown-content),
3
+ роли заливки bg-* и border-*. Эталон — облако; подключать после Tailwind:
4
+ @import "tailwindcss";
5
+ @import "poe-svelte-ui-lib/theme.css";
6
+ @source "../node_modules/poe-svelte-ui-lib/dist";
7
+ @custom-variant dark (&:where(.dark, .dark *));
8
+ В app.css приложения — только его собственные отличия. */
9
+
10
+ :root {
11
+ /* Интерфейс — системный шрифт: спроектирован для плотных панелей и мелких кеглей.
12
+ Montserrat остаётся в логотипе и крупных заголовках витрин (класс .font-display). */
13
+ font-family:
14
+ -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
15
+ "Apple Color Emoji", "Segoe UI Emoji";
16
+ font-optical-sizing: auto;
17
+ font-style: normal;
18
+ box-sizing: border-box;
19
+ margin: 0;
20
+ padding: 0;
21
+
22
+ /* Было: font-size 13px, font-weight 450, text-align center.
23
+ 13px остаётся доступен классом .text-dense для таблиц и сервисных панелей. */
24
+ font-size: 15px;
25
+ font-weight: 400;
26
+ line-height: 1.5;
27
+ -webkit-font-smoothing: antialiased;
28
+ -moz-osx-font-smoothing: grayscale;
29
+ }
30
+
31
+ .font-display {
32
+ font-family:
33
+ "Montserrat",
34
+ -apple-system,
35
+ BlinkMacSystemFont,
36
+ "Segoe UI",
37
+ sans-serif;
38
+ }
39
+
40
+ /* Плотный режим для таблиц, сервисного раздела и отладки */
41
+ .text-dense {
42
+ font-size: 13px;
43
+ line-height: 1.45;
44
+ }
45
+
46
+ /* Глобальные стили тегов — в слое base: утилиты Tailwind (классы компонентов poe-svelte-ui-lib) всегда сильнее */
47
+ @layer base {
48
+ /* ─── Типографическая шкала ─────────────────────────────────────────── */
49
+ h1,
50
+ h2,
51
+ h3,
52
+ h4,
53
+ h5,
54
+ h6 {
55
+ margin: 0;
56
+ text-wrap: pretty;
57
+ }
58
+
59
+ h1 {
60
+ font-size: 2.2rem; /* 33px */
61
+ font-weight: 700;
62
+ line-height: 1.12;
63
+ letter-spacing: -0.022em;
64
+ }
65
+
66
+ h2 {
67
+ font-size: 1.75rem; /* 26px */
68
+ font-weight: 650;
69
+ line-height: 1.2;
70
+ letter-spacing: -0.016em;
71
+ }
72
+
73
+ h3 {
74
+ font-size: 1.35rem; /* 20px */
75
+ font-weight: 620;
76
+ line-height: 1.3;
77
+ letter-spacing: -0.01em;
78
+ }
79
+
80
+ h4 {
81
+ font-size: 1.13rem; /* 17px */
82
+ font-weight: 600;
83
+ line-height: 1.4;
84
+ }
85
+
86
+ h5 {
87
+ font-size: 1rem; /* 15px */
88
+ font-weight: 600;
89
+ }
90
+
91
+ h6 {
92
+ font-size: 0.8rem; /* 12px */
93
+ font-weight: 700;
94
+ letter-spacing: 0.08em;
95
+ text-transform: uppercase;
96
+ color: var(--muted-color);
97
+ }
98
+
99
+ p {
100
+ margin: 0;
101
+ }
102
+
103
+ /* Длинные слова (имена файлов, типы) переносятся — без горизонтальной прокрутки */
104
+ h1,
105
+ h2,
106
+ h3,
107
+ h4,
108
+ h5,
109
+ h6,
110
+ p,
111
+ li {
112
+ overflow-wrap: break-word;
113
+ }
114
+
115
+ label {
116
+ margin: 0;
117
+ font-size: 0.8rem;
118
+ font-weight: 600;
119
+ color: var(--muted-color);
120
+ }
121
+
122
+ ul,
123
+ ol {
124
+ padding-left: 1.25rem;
125
+ margin: 0.25rem 0;
126
+ }
127
+
128
+ ul {
129
+ list-style: disc;
130
+ }
131
+
132
+ ol {
133
+ list-style: decimal;
134
+ }
135
+
136
+ li {
137
+ margin: 0.25rem 0;
138
+ }
139
+
140
+ blockquote {
141
+ margin: 1rem 0;
142
+ padding-left: 1rem;
143
+ border-left: 3px solid var(--border-color);
144
+ color: var(--muted-color);
145
+ font-style: normal;
146
+ }
147
+
148
+ pre {
149
+ padding: 0.5rem 0.75rem;
150
+ text-align: left;
151
+ background-color: var(--container-color);
152
+ border: 1px solid var(--border-color);
153
+ border-radius: 10px;
154
+ /* Без горизонтальной прокрутки: длинные строки кода переносятся */
155
+ white-space: pre-wrap;
156
+ overflow-wrap: anywhere;
157
+ }
158
+
159
+ pre code {
160
+ padding: 0;
161
+ background-color: transparent;
162
+ white-space: inherit;
163
+ }
164
+
165
+ code {
166
+ padding: 0.1rem 0.35rem;
167
+ text-align: left;
168
+ background-color: var(--container-color);
169
+ border-radius: 6px;
170
+ white-space: pre-wrap;
171
+ overflow-wrap: anywhere;
172
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
173
+ font-size: 0.9em;
174
+ }
175
+
176
+ /* ─── Таблицы (Markdown + плотный режим) ─────────────────────────────── */
177
+ table {
178
+ border-collapse: collapse;
179
+ width: 100%;
180
+ margin: 0.5rem 0;
181
+ text-align: left;
182
+ font-size: 13px;
183
+ font-variant-numeric: tabular-nums;
184
+ }
185
+
186
+ th,
187
+ td {
188
+ padding: 0.4rem 0.6rem;
189
+ border: 0;
190
+ border-top: 1px solid var(--hairline-color);
191
+ /* Длинные типы и значения переносятся внутри ячейки — таблица не шире контейнера */
192
+ overflow-wrap: anywhere;
193
+ vertical-align: top;
194
+ }
195
+
196
+ /* Ключи и имена в первой колонке не рвутся посреди слова — переносятся только по пробелам */
197
+ td:first-child code {
198
+ overflow-wrap: break-word;
199
+ }
200
+
201
+ th {
202
+ background-color: var(--container-color);
203
+ border-top: 0;
204
+ font-size: 11px;
205
+ font-weight: 700;
206
+ letter-spacing: 0.06em;
207
+ text-transform: uppercase;
208
+ color: var(--muted-color);
209
+ }
210
+
211
+ /* Зебра убрана: строки разделяет волосяная линия, выделение — тинт акцента */
212
+ tr:nth-child(even) td {
213
+ background-color: transparent;
214
+ }
215
+
216
+ /* ─── Контент из Markdown (marked → {@html}) ──────────────────────────
217
+ Глобальная шкала h1..h6 выше (33/26/20px…) рассчитана на витринные
218
+ заголовки страниц, а не на текст внутри карточек/чата/модалок/каталога —
219
+ там с ней markdown выглядел непропорционально крупным. Здесь — свой
220
+ компактный масштаб, подключается классом .markdown-content на обёртке
221
+ каждого {@html}-блока с результатом RenderMarkdown(). */
222
+ .markdown-content {
223
+ font-size: 14px;
224
+ line-height: 1.6;
225
+ }
226
+
227
+ .markdown-content > :first-child {
228
+ margin-top: 0;
229
+ }
230
+
231
+ .markdown-content > :last-child {
232
+ margin-bottom: 0;
233
+ }
234
+
235
+ .markdown-content h1 {
236
+ margin: 0.7em 0 0.35em;
237
+ font-size: 1.3rem; /* 18px */
238
+ font-weight: 650;
239
+ line-height: 1.3;
240
+ letter-spacing: -0.012em;
241
+ }
242
+
243
+ .markdown-content h2 {
244
+ margin: 0.7em 0 0.35em;
245
+ font-size: 1.15rem; /* 16px */
246
+ font-weight: 650;
247
+ line-height: 1.35;
248
+ }
249
+
250
+ .markdown-content h3 {
251
+ margin: 0.6em 0 0.3em;
252
+ font-size: 1.05rem; /* 15px */
253
+ font-weight: 600;
254
+ }
255
+
256
+ .markdown-content h4,
257
+ .markdown-content h5,
258
+ .markdown-content h6 {
259
+ margin: 0.5em 0 0.25em;
260
+ font-size: 1rem; /* 14px */
261
+ font-weight: 600;
262
+ letter-spacing: normal;
263
+ text-transform: none;
264
+ color: inherit;
265
+ }
266
+
267
+ .markdown-content p {
268
+ margin: 0 0 0.6em;
269
+ }
270
+
271
+ .markdown-content ul,
272
+ .markdown-content ol {
273
+ margin: 0.4em 0;
274
+ }
275
+
276
+ .markdown-content li {
277
+ margin: 0.15em 0;
278
+ }
279
+
280
+ .markdown-content a {
281
+ color: var(--accent-color);
282
+ text-decoration: underline;
283
+ text-underline-offset: 2px;
284
+ }
285
+
286
+ .markdown-content pre,
287
+ .markdown-content code {
288
+ font-size: 0.85em;
289
+ }
290
+
291
+ tbody tr:hover td {
292
+ background-color: var(--accent-soft);
293
+ }
294
+
295
+ body {
296
+ background: var(--bg-body);
297
+ color: var(--font-color);
298
+ }
299
+
300
+ a {
301
+ color: var(--accent-color);
302
+ text-decoration: none;
303
+ }
304
+
305
+ a:hover {
306
+ color: var(--accent-hover);
307
+ text-decoration: underline;
308
+ }
309
+
310
+ /* Единое кольцо фокуса вместо размытых теней у каждого компонента */
311
+ :where(button, a, input, select, textarea, [tabindex]):focus-visible {
312
+ outline: 2px solid var(--accent-color);
313
+ outline-offset: 2px;
314
+ border-radius: 10px;
315
+ }
316
+ }
317
+
318
+ /* ─── Полоса прокрутки: нейтральная, проявляется при наведении ───────── */
319
+ ::-webkit-scrollbar {
320
+ width: 10px;
321
+ height: 10px;
322
+ }
323
+
324
+ ::-webkit-scrollbar-track {
325
+ background: transparent;
326
+ }
327
+
328
+ ::-webkit-scrollbar-thumb {
329
+ background-color: var(--scrollbar-color);
330
+ border: 3px solid transparent;
331
+ background-clip: content-box;
332
+ border-radius: 999px;
333
+ }
334
+
335
+ ::-webkit-scrollbar-thumb:hover {
336
+ background-color: var(--muted-color);
337
+ }
338
+
339
+ /* ════════════════════════════════════════════════════════════════════
340
+ Цветовая гамма — «Графит и сапфир»
341
+ Акцент выведен из градиента логотипа (#86b6ff → #274c92).
342
+ Градиент на body убран: фон плоский, глубину даёт волосяная линия.
343
+ ════════════════════════════════════════════════════════════════════ */
344
+ .light {
345
+ --bg-body: #f5f6f8;
346
+ --back-color: #ffffff;
347
+ --container-color: #f0f2f5;
348
+ --field-color: #ffffff;
349
+ --border-color: #d5d9e0;
350
+ --hairline-color: rgb(17 24 39 / 0.09);
351
+
352
+ --font-color: #10141a;
353
+ --muted-color: #545c6b;
354
+ --faint-color: #8b93a1;
355
+
356
+ --accent-color: #4175c8;
357
+ --accent-hover: #33599e;
358
+ --accent-soft: rgb(65 117 200 / 0.1);
359
+ --on-accent: #ffffff;
360
+
361
+ --shadow-color: #34343a;
362
+ --border-shadow-color: 0 1px 2px rgb(16 24 40 / 0.05);
363
+ --focus-shadow-color: 0 0 0 3px rgb(65 117 200 / 0.18);
364
+ --elevation-1: 0 1px 2px rgb(16 24 40 / 0.05);
365
+ --elevation-2: 0 4px 12px rgb(16 24 40 / 0.08);
366
+ --elevation-3: 0 12px 32px rgb(16 24 40 / 0.12);
367
+ --scrollbar-color: #c4c9d2;
368
+
369
+ /* Семантические заливки: светлая тема — светлые, но не кислотные тона (S=55%, L — самая
370
+ светлая точка, где контраст с белым текстом ещё ≥ 4.5:1). Тёмная тема ниже — свои, тёмные. */
371
+ --gray-color: #e4e7ec;
372
+ --red-color: #c94e45;
373
+ --orange-color: #9e6a2e;
374
+ --yellow-color: #8e7329;
375
+ --green-color: #278660;
376
+ --cyan-color: #2a7d92;
377
+ --blue-color: #4175c8;
378
+ --purple-color: #855dd0;
379
+ --pink-color: #c84182;
380
+
381
+ /* Мягкие тинты для фонов блоков и подсветки строк */
382
+ --gray-soft: #f0f2f5;
383
+ --red-soft: #fbeae8;
384
+ --orange-soft: #fcefe1;
385
+ --yellow-soft: #faf3dc;
386
+ --green-soft: #e4f4ec;
387
+ --cyan-soft: #e2f2f6;
388
+ --blue-soft: #e8f0fc;
389
+ --purple-soft: #efe9fb;
390
+ --pink-soft: #fbe7f0;
391
+ }
392
+
393
+ .dark {
394
+ --bg-body: #0a0c10;
395
+ --back-color: #13161d;
396
+ --container-color: #1a1e27;
397
+ --field-color: #1a1e27;
398
+ --border-color: #333a47;
399
+ --hairline-color: rgb(255 255 255 / 0.1);
400
+
401
+ --font-color: #e9ebf0;
402
+ --muted-color: #9ba3b2;
403
+ --faint-color: #6d7686;
404
+
405
+ --accent-color: #3863a8;
406
+ --accent-hover: #4a78bf;
407
+ --accent-soft: rgb(56 99 168 / 0.18);
408
+ --on-accent: #ffffff;
409
+
410
+ --shadow-color: #000000;
411
+ --border-shadow-color: 0 1px 2px rgb(0 0 0 / 0.4);
412
+ --focus-shadow-color: 0 0 0 3px rgb(56 99 168 / 0.32);
413
+ --elevation-1: 0 1px 2px rgb(0 0 0 / 0.4);
414
+ --elevation-2: 0 4px 12px rgb(0 0 0 / 0.45);
415
+ --elevation-3: 0 12px 32px rgb(0 0 0 / 0.5);
416
+ --scrollbar-color: #333a47;
417
+
418
+ /* Тёмная тема — тёмные, приглушённые тона (S=50%, L — самая тёмная точка, где контраст
419
+ с белым текстом ещё ≥ 4.5:1 и с --bg-body ещё ≥ 3.2:1), а не светлее светлой темы */
420
+ --gray-color: #242a35;
421
+ --red-color: #a84038;
422
+ --orange-color: #825a2b;
423
+ --yellow-color: #776128;
424
+ --green-color: #256f51;
425
+ --cyan-color: #296a7a;
426
+ --blue-color: #3863a8;
427
+ --purple-color: #754bc3;
428
+ --pink-color: #a8386e;
429
+
430
+ --gray-soft: rgb(255 255 255 / 0.06);
431
+ --red-soft: rgb(168 64 56 / 0.18);
432
+ --orange-soft: rgb(130 90 43 / 0.18);
433
+ --yellow-soft: rgb(119 97 40 / 0.18);
434
+ --green-soft: rgb(37 111 81 / 0.18);
435
+ --cyan-soft: rgb(41 106 122 / 0.18);
436
+ --blue-soft: rgb(56 99 168 / 0.18);
437
+ --purple-soft: rgb(117 75 195 / 0.18);
438
+ --pink-soft: rgb(168 56 110 / 0.18);
439
+ }
440
+
441
+ /* ─── Утилиты заливки (без изменений в именах) ───────────────────────── */
442
+ .bg-max {
443
+ --bg-color: var(--back-color);
444
+ }
445
+ .bg-gray {
446
+ --bg-color: var(--gray-color);
447
+ }
448
+ .bg-red {
449
+ --bg-color: var(--red-color);
450
+ }
451
+ .bg-orange {
452
+ --bg-color: var(--orange-color);
453
+ }
454
+ .bg-yellow {
455
+ --bg-color: var(--yellow-color);
456
+ }
457
+ .bg-green {
458
+ --bg-color: var(--green-color);
459
+ }
460
+ .bg-cyan {
461
+ --bg-color: var(--cyan-color);
462
+ }
463
+ .bg-blue {
464
+ --bg-color: var(--blue-color);
465
+ }
466
+ .bg-purple {
467
+ --bg-color: var(--purple-color);
468
+ }
469
+ .bg-pink {
470
+ --bg-color: var(--pink-color);
471
+ }
472
+
473
+ /* Мягкие варианты — для карточек, плашек и подсветки, где заливка не должна кричать */
474
+ .bg-red-soft {
475
+ --bg-color: var(--red-soft);
476
+ }
477
+ .bg-orange-soft {
478
+ --bg-color: var(--orange-soft);
479
+ }
480
+ .bg-yellow-soft {
481
+ --bg-color: var(--yellow-soft);
482
+ }
483
+ .bg-green-soft {
484
+ --bg-color: var(--green-soft);
485
+ }
486
+ .bg-cyan-soft {
487
+ --bg-color: var(--cyan-soft);
488
+ }
489
+ .bg-blue-soft {
490
+ --bg-color: var(--blue-soft);
491
+ }
492
+ .bg-purple-soft {
493
+ --bg-color: var(--purple-soft);
494
+ }
495
+ .bg-pink-soft {
496
+ --bg-color: var(--pink-soft);
497
+ }
498
+
499
+ .border-max {
500
+ --border-color: var(--back-color);
501
+ }
502
+ .border-gray {
503
+ --border-color: var(--gray-color);
504
+ }
505
+ .border-red {
506
+ --border-color: var(--red-color);
507
+ }
508
+ .border-orange {
509
+ --border-color: var(--orange-color);
510
+ }
511
+ .border-yellow {
512
+ --border-color: var(--yellow-color);
513
+ }
514
+ .border-green {
515
+ --border-color: var(--green-color);
516
+ }
517
+ .border-cyan {
518
+ --border-color: var(--cyan-color);
519
+ }
520
+ .border-blue {
521
+ --border-color: var(--blue-color);
522
+ }
523
+ .border-purple {
524
+ --border-color: var(--purple-color);
525
+ }
526
+ .border-pink {
527
+ --border-color: var(--pink-color);
528
+ }
529
+
530
+ /* Подсказка placeholder */
531
+ @layer base {
532
+ input::placeholder,
533
+ textarea::placeholder {
534
+ color: var(--faint-color);
535
+ opacity: 1;
536
+ }
537
+ }
538
+
539
+ @layer base {
540
+ hr {
541
+ margin: 0.5rem 0;
542
+ border: 0;
543
+ border-top: 1px solid var(--hairline-color);
544
+ }
545
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.40.0",
3
+ "version": "1.40.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Oleg-Perevyshin/ElementsUI.git"
@@ -23,7 +23,8 @@
23
23
  ".": {
24
24
  "types": "./dist/index.d.ts",
25
25
  "svelte": "./dist/index.js"
26
- }
26
+ },
27
+ "./theme.css": "./dist/theme.css"
27
28
  },
28
29
  "files": [
29
30
  "dist"