chuijs 4.0.3 → 4.0.4

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.
Files changed (258) hide show
  1. package/CHANGELOG.md +536 -0
  2. package/framework/appLayout/default/chui_app_layout.js +361 -303
  3. package/framework/appLayout/default/global_style.css +243 -89
  4. package/framework/appLayout/default/main_theme_style.css +188 -101
  5. package/framework/components/chui_accordion/accordion.js +17 -17
  6. package/framework/components/chui_accordion/styles.css +7 -7
  7. package/framework/components/chui_action_sheet/action_sheet.js +35 -32
  8. package/framework/components/chui_action_sheet/styles.css +10 -4
  9. package/framework/components/chui_actions_bar/actions_bar.js +338 -0
  10. package/framework/components/chui_actions_bar/styles.css +139 -0
  11. package/framework/components/chui_affix/affix.js +4 -4
  12. package/framework/components/chui_affix/styles.css +5 -0
  13. package/framework/components/chui_alert/alert.js +11 -5
  14. package/framework/components/chui_alert/styles.css +7 -0
  15. package/framework/components/chui_avatar/avatar.js +13 -13
  16. package/framework/components/chui_avatar/styles.css +1 -1
  17. package/framework/components/chui_avatar_group/avatar_group.js +4 -4
  18. package/framework/components/chui_avatar_group/styles.css +1 -1
  19. package/framework/components/chui_backtop/backtop.js +25 -2
  20. package/framework/components/chui_backtop/styles.css +21 -0
  21. package/framework/components/chui_badge/badge.js +15 -15
  22. package/framework/components/chui_badge/styles.css +6 -6
  23. package/framework/components/chui_banner/banner.js +222 -0
  24. package/framework/components/chui_banner/styles.css +170 -0
  25. package/framework/components/chui_breadcrumb/styles.css +1 -1
  26. package/framework/components/chui_button/button.js +24 -28
  27. package/framework/components/chui_button/styles.css +5 -5
  28. package/framework/components/chui_calendar/calendar.js +6 -6
  29. package/framework/components/chui_card/card.js +14 -14
  30. package/framework/components/chui_card_scroll/card_scroll.js +169 -0
  31. package/framework/components/chui_card_scroll/styles.css +86 -0
  32. package/framework/components/chui_chat/chat.js +73 -51
  33. package/framework/components/chui_chat/styles.css +18 -7
  34. package/framework/components/chui_clipboard_history/clipboard_history.js +25 -20
  35. package/framework/components/chui_clipboard_history/styles.css +10 -8
  36. package/framework/components/chui_code/code.js +8 -8
  37. package/framework/components/chui_code/styles.css +2 -2
  38. package/framework/components/chui_code_editor/code_editor.js +26 -26
  39. package/framework/components/chui_code_editor/styles.css +3 -3
  40. package/framework/components/chui_command_palette/command_palette.js +29 -29
  41. package/framework/components/chui_command_palette/styles.css +5 -5
  42. package/framework/components/chui_console/console.js +9 -9
  43. package/framework/components/chui_console/styles.css +5 -2
  44. package/framework/components/chui_content_block/content_block.js +2 -2
  45. package/framework/components/chui_context_menu/context_menu.js +162 -54
  46. package/framework/components/chui_context_menu/styles.css +82 -8
  47. package/framework/components/chui_custom_element/custom_element.js +11 -11
  48. package/framework/components/chui_data_grid/data_grid.js +268 -268
  49. package/framework/components/chui_data_grid/styles.css +14 -14
  50. package/framework/components/chui_descriptions/styles.css +1 -1
  51. package/framework/components/chui_details/details.js +12 -12
  52. package/framework/components/chui_details/styles.css +7 -7
  53. package/framework/components/chui_diff_viewer/diff_viewer.js +88 -88
  54. package/framework/components/chui_diff_viewer/styles.css +9 -9
  55. package/framework/components/chui_divider/divider.js +6 -6
  56. package/framework/components/chui_divider/styles.css +2 -2
  57. package/framework/components/chui_drawer/drawer.js +37 -14
  58. package/framework/components/chui_drawer/styles.css +18 -4
  59. package/framework/components/chui_drop_zone/drop_zone.js +6 -6
  60. package/framework/components/chui_drop_zone/styles.css +10 -6
  61. package/framework/components/chui_fieldset/fieldset.js +6 -6
  62. package/framework/components/chui_fieldset/styles.css +5 -5
  63. package/framework/components/chui_file_explorer/file_explorer.js +115 -115
  64. package/framework/components/chui_file_explorer/styles.css +6 -6
  65. package/framework/components/chui_form/form.js +11 -11
  66. package/framework/components/chui_gantt/gantt.js +41 -41
  67. package/framework/components/chui_gantt/styles.css +5 -5
  68. package/framework/components/chui_graphs/graphs.js +82 -46
  69. package/framework/components/chui_graphs/styles.css +26 -5
  70. package/framework/components/chui_hero/hero.js +63 -20
  71. package/framework/components/chui_hero/styles.css +176 -7
  72. package/framework/components/chui_html_block/html_block.js +5 -5
  73. package/framework/components/chui_html_block/styles.css +1 -1
  74. package/framework/components/chui_hx/styles.css +2 -2
  75. package/framework/components/chui_icons/icons.js +7 -7
  76. package/framework/components/chui_icons/styles.css +2 -1
  77. package/framework/components/chui_inputs/chui_check_box/styles.css +12 -3
  78. package/framework/components/chui_inputs/chui_chips/chips.js +92 -31
  79. package/framework/components/chui_inputs/chui_chips/styles.css +28 -5
  80. package/framework/components/chui_inputs/chui_color_picker/color_picker.js +20 -15
  81. package/framework/components/chui_inputs/chui_color_picker/styles.css +7 -7
  82. package/framework/components/chui_inputs/chui_combo_box/combo_box.js +58 -58
  83. package/framework/components/chui_inputs/chui_combo_box/styles.css +9 -9
  84. package/framework/components/chui_inputs/chui_date/date.js +93 -95
  85. package/framework/components/chui_inputs/chui_date/styles.css +8 -8
  86. package/framework/components/chui_inputs/chui_date_range/date_range.js +22 -22
  87. package/framework/components/chui_inputs/chui_date_range/styles.css +3 -3
  88. package/framework/components/chui_inputs/chui_datetime/datetime.js +12 -12
  89. package/framework/components/chui_inputs/chui_datetime/styles.css +3 -3
  90. package/framework/components/chui_inputs/chui_email/email.js +10 -10
  91. package/framework/components/chui_inputs/chui_email/styles.css +6 -6
  92. package/framework/components/chui_inputs/chui_file/file.js +8 -8
  93. package/framework/components/chui_inputs/chui_file/styles.css +12 -9
  94. package/framework/components/chui_inputs/chui_hotkey/hotkey.js +39 -39
  95. package/framework/components/chui_inputs/chui_hotkey/styles.css +6 -6
  96. package/framework/components/chui_inputs/chui_masked/masked.js +44 -44
  97. package/framework/components/chui_inputs/chui_masked/styles.css +5 -5
  98. package/framework/components/chui_inputs/chui_mention/mention.js +13 -13
  99. package/framework/components/chui_inputs/chui_mention/styles.css +14 -8
  100. package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +147 -137
  101. package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +14 -8
  102. package/framework/components/chui_inputs/chui_number/number.js +42 -45
  103. package/framework/components/chui_inputs/chui_number/styles.css +6 -6
  104. package/framework/components/chui_inputs/chui_otp/otp.js +32 -32
  105. package/framework/components/chui_inputs/chui_otp/styles.css +5 -5
  106. package/framework/components/chui_inputs/chui_password/password.js +30 -36
  107. package/framework/components/chui_inputs/chui_password/styles.css +7 -7
  108. package/framework/components/chui_inputs/chui_radio_button/styles.css +2 -2
  109. package/framework/components/chui_inputs/chui_rating/rating.js +28 -28
  110. package/framework/components/chui_inputs/chui_rating/styles.css +3 -3
  111. package/framework/components/chui_inputs/chui_select_box/select_box.js +54 -54
  112. package/framework/components/chui_inputs/chui_select_box/styles.css +10 -10
  113. package/framework/components/chui_inputs/chui_slider/slider.js +14 -14
  114. package/framework/components/chui_inputs/chui_slider/styles.css +5 -4
  115. package/framework/components/chui_inputs/chui_text/styles.css +7 -7
  116. package/framework/components/chui_inputs/chui_text/text.js +13 -17
  117. package/framework/components/chui_inputs/chui_text_area/styles.css +8 -8
  118. package/framework/components/chui_inputs/chui_text_area/text_area.js +23 -23
  119. package/framework/components/chui_inputs/chui_time/styles.css +13 -17
  120. package/framework/components/chui_inputs/chui_time/time.js +35 -24
  121. package/framework/components/chui_inputs/chui_toggle/styles.css +10 -3
  122. package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
  123. package/framework/components/chui_json_editor/json_editor.js +87 -87
  124. package/framework/components/chui_json_editor/styles.css +5 -5
  125. package/framework/components/chui_json_viewer/json_viewer.js +80 -80
  126. package/framework/components/chui_json_viewer/styles.css +6 -6
  127. package/framework/components/chui_kanban/kanban.js +27 -27
  128. package/framework/components/chui_kanban/styles.css +8 -4
  129. package/framework/components/chui_kbd/styles.css +1 -1
  130. package/framework/components/chui_label/label.js +17 -17
  131. package/framework/components/chui_label/styles.css +1 -1
  132. package/framework/components/chui_link/link.js +9 -9
  133. package/framework/components/chui_link/styles.css +11 -4
  134. package/framework/components/chui_list/list.js +11 -11
  135. package/framework/components/chui_list/styles.css +2 -2
  136. package/framework/components/chui_markdown_editor/markdown_editor.js +66 -66
  137. package/framework/components/chui_markdown_editor/styles.css +5 -5
  138. package/framework/components/chui_media/audio.js +240 -246
  139. package/framework/components/chui_media/audio_styles.css +41 -23
  140. package/framework/components/chui_media/image.js +49 -46
  141. package/framework/components/chui_media/image_compare.js +35 -35
  142. package/framework/components/chui_media/image_compare_styles.css +5 -5
  143. package/framework/components/chui_media/image_styles.css +1 -1
  144. package/framework/components/chui_media/video.js +148 -148
  145. package/framework/components/chui_media/video_styles.css +27 -11
  146. package/framework/components/chui_menu_bar/menu_bar.js +54 -54
  147. package/framework/components/chui_menu_bar/styles.css +10 -10
  148. package/framework/components/chui_message_bar/message_bar.js +551 -0
  149. package/framework/components/chui_message_bar/styles.css +248 -0
  150. package/framework/components/chui_modal/modal.js +37 -13
  151. package/framework/components/chui_notification/notification.js +76 -54
  152. package/framework/components/chui_notification/notification_download.js +26 -81
  153. package/framework/components/chui_notification/notification_download_card.js +115 -0
  154. package/framework/components/chui_notification/notification_download_progress.css +2 -2
  155. package/framework/components/chui_notification/notification_download_progress.js +70 -108
  156. package/framework/components/chui_notification/notification_update.js +31 -31
  157. package/framework/components/chui_notification/status_plate.css +3 -3
  158. package/framework/components/chui_notification/styles.css +6 -1
  159. package/framework/components/chui_notification/styles_download.css +1 -1
  160. package/framework/components/chui_notification/styles_update.css +2 -2
  161. package/framework/components/chui_org_chart/org_chart.js +29 -29
  162. package/framework/components/chui_org_chart/styles.css +2 -2
  163. package/framework/components/chui_pagination/pagination.js +3 -3
  164. package/framework/components/chui_pagination/styles.css +1 -1
  165. package/framework/components/chui_paragraph/styles.css +2 -2
  166. package/framework/components/chui_popover/popover.js +29 -23
  167. package/framework/components/chui_popover/styles.css +1 -1
  168. package/framework/components/chui_popups/popups.js +124 -124
  169. package/framework/components/chui_popups/styles.css +5 -5
  170. package/framework/components/chui_print_button/print_button.js +15 -10
  171. package/framework/components/chui_progress_bar/progress_bar.js +34 -12
  172. package/framework/components/chui_progress_bar/styles.css +4 -4
  173. package/framework/components/chui_progress_ring/progress_ring.js +15 -15
  174. package/framework/components/chui_progress_ring/styles.css +1 -1
  175. package/framework/components/chui_qrcode/qrcode.js +12 -12
  176. package/framework/components/chui_query_builder/query_builder.js +46 -46
  177. package/framework/components/chui_query_builder/styles.css +4 -4
  178. package/framework/components/chui_radio_group/styles.css +1 -1
  179. package/framework/components/chui_result/result.js +15 -15
  180. package/framework/components/chui_result/styles.css +3 -3
  181. package/framework/components/chui_scheduler/scheduler.js +74 -74
  182. package/framework/components/chui_scheduler/styles.css +5 -5
  183. package/framework/components/chui_screen_spinner/screen_spinner.js +167 -0
  184. package/framework/components/chui_screen_spinner/styles.css +49 -0
  185. package/framework/components/chui_segmented_control/segmented_control.js +63 -6
  186. package/framework/components/chui_segmented_control/styles.css +32 -6
  187. package/framework/components/chui_settings/settings.js +45 -46
  188. package/framework/components/chui_settings/styles.css +4 -4
  189. package/framework/components/chui_shortcut_help/shortcut_help.js +32 -34
  190. package/framework/components/chui_sidebar_nav/sidebar_nav.js +98 -29
  191. package/framework/components/chui_sidebar_nav/styles.css +9 -3
  192. package/framework/components/chui_slideshow/slideshow.js +85 -85
  193. package/framework/components/chui_slideshow/styles.css +6 -6
  194. package/framework/components/chui_spinner/spinner.js +10 -10
  195. package/framework/components/chui_split_button/split_button.js +30 -12
  196. package/framework/components/chui_split_button/styles.css +21 -5
  197. package/framework/components/chui_splitter/splitter.js +11 -11
  198. package/framework/components/chui_splitter/styles.css +2 -2
  199. package/framework/components/chui_statistic/statistic.js +47 -47
  200. package/framework/components/chui_status_bar/styles.css +2 -2
  201. package/framework/components/chui_stepper/stepper.js +202 -31
  202. package/framework/components/chui_stepper/styles.css +79 -14
  203. package/framework/components/chui_sticky_bar/sticky_bar.js +44 -13
  204. package/framework/components/chui_sticky_bar/styles.css +10 -8
  205. package/framework/components/chui_tabs/chui_tabs.js +74 -26
  206. package/framework/components/chui_tabs/styles.css +40 -12
  207. package/framework/components/chui_text_editor/styles.css +7 -7
  208. package/framework/components/chui_text_editor/text_editor.js +56 -56
  209. package/framework/components/chui_text_editor/text_editor_panel.js +173 -174
  210. package/framework/components/chui_theme_toggle/theme_toggle.js +21 -5
  211. package/framework/components/chui_timeline/styles.css +3 -1
  212. package/framework/components/chui_toast_queue/styles.css +11 -11
  213. package/framework/components/chui_toast_queue/toast_queue.js +30 -27
  214. package/framework/components/chui_toolbar/styles.css +2 -2
  215. package/framework/components/chui_tooltip/styles.css +2 -2
  216. package/framework/components/chui_tooltip/tooltip.js +3 -3
  217. package/framework/components/chui_tour/styles.css +8 -8
  218. package/framework/components/chui_tour/tour.js +72 -72
  219. package/framework/components/chui_transfer/styles.css +6 -6
  220. package/framework/components/chui_transfer/transfer.js +93 -106
  221. package/framework/components/chui_tree_table/styles.css +4 -4
  222. package/framework/components/chui_tree_table/tree_table.js +45 -45
  223. package/framework/components/chui_tree_view/styles.css +10 -8
  224. package/framework/components/chui_tree_view/tree_view.js +185 -183
  225. package/framework/components/chui_user_bar/styles.css +145 -0
  226. package/framework/components/chui_user_bar/user_bar.js +402 -0
  227. package/framework/components/chui_user_list_bar/styles.css +173 -0
  228. package/framework/components/chui_user_list_bar/user_list_bar.js +485 -0
  229. package/framework/components/chui_watermark/watermark.js +17 -17
  230. package/framework/components/chui_webview/webview.js +9 -9
  231. package/framework/components/chui_write_bar/styles.css +172 -0
  232. package/framework/components/chui_write_bar/write_bar.js +320 -0
  233. package/framework/main_window.js +1234 -0
  234. package/framework/modules/chui_a11y/chui_a11y.js +78 -79
  235. package/framework/modules/chui_animations/animations.js +77 -15
  236. package/framework/modules/chui_animations/motion.js +120 -0
  237. package/framework/modules/chui_animations/styles.css +36 -3
  238. package/framework/modules/chui_functions.js +31 -27
  239. package/framework/modules/chui_highlight/styles.css +13 -10
  240. package/framework/modules/chui_i18n/chui_i18n.js +3 -3
  241. package/framework/modules/chui_i18n/locales/feedback.js +62 -0
  242. package/framework/modules/chui_i18n/locales/navigation.js +4 -0
  243. package/framework/modules/chui_json/chui_json.js +2 -2
  244. package/framework/modules/chui_logger/chui_logger.js +23 -8
  245. package/framework/modules/chui_page/page.js +10 -10
  246. package/framework/modules/chui_palette/palette.js +61 -0
  247. package/framework/modules/chui_qr/chui_qr.js +23 -23
  248. package/framework/modules/chui_scrollbar/chui_scrollbar.js +45 -45
  249. package/framework/modules/chui_scrollbar/styles.css +3 -3
  250. package/framework/modules/chui_services/chui_services.js +8 -8
  251. package/framework/modules/chui_services/desktop.js +77 -71
  252. package/framework/modules/chui_sortable/chui_sortable.js +17 -17
  253. package/framework/modules/chui_validate/chui_validate.js +3 -3
  254. package/framework/tray_bar/chui_menu_items.js +2 -2
  255. package/index.d.ts +266 -34
  256. package/index.js +20 -1168
  257. package/package.json +6 -4
  258. package/framework/components/telegram_bot/chui_telegram_bot.js +0 -191
package/CHANGELOG.md CHANGED
@@ -3,6 +3,542 @@
3
3
  Формат — [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
4
4
  версии — [Semantic Versioning](https://semver.org/lang/ru/).
5
5
 
6
+ ## [4.0.4] - 2026-10-01
7
+
8
+ ### Добавлено
9
+
10
+ - **`ScreenSpinner` — экран занят.** Слой поверх всего окна с индикатором загрузки: пока он
11
+ открыт, до содержимого под ним не добраться — мышь и клавиатура приходятся на слой, фон
12
+ становится инертным, а фокус удерживается внутри. Слой встаёт в `body` (в карточке
13
+ с `backdrop-filter` Chromium собирает такой узел отдельной поверхностью и рисует её под
14
+ слоями из `body`), идёт выше модальных слоёв и объявляет себя `dialog` с именем загрузки,
15
+ внутри — `progressbar`. `delay` гасит мигание на коротких операциях, `hide()` до появления
16
+ слоя просто отменяет показ; на экране всегда один такой слой — новый убирает предыдущий.
17
+ - **`CardScroll` — горизонтальная лента карточек.** Лента идёт вбок: вертикальное колесо
18
+ прокручивает её вдоль, карточка прилипает к началу (`snap: false` отключает), у краёв
19
+ появляются стрелки — только там, где за краем есть карточки. Клавиатура: стрелки влево-вправо
20
+ ведут по карточке с зазором, Home и End — к началу и концу; список объявлен
21
+ `role="list"` с подсчётом карточек, поэтому скринридер называет их число. Когда лента
22
+ упирается, колесо отдаётся странице — иначе список «съел» бы прокрутку.
23
+ - **`Banner` — карточка-объявление.** Кромка со значком или фотографией, заголовок
24
+ с подзаголовком, действия и закрытие: размер `s` — строка, `m` — карточка с картинкой
25
+ во всю ширину, вид `tint` — значок в цветной плашке, `image` — фотография. Подпись не лежит
26
+ поверх снимка (она рядом), поэтому читается при любом кадре; закрытие идёт общим кадром ухода
27
+ с одноразовым слушателем и сроком из токена. `onClick` делает карточку кнопкой — роль, обход
28
+ клавиатурой и активация Enter/Space, а имя баннера даёт заголовок.
29
+ - **`WriteBar` — панель ввода сообщения.** Слева вложения и эмодзи, по центру поле текста,
30
+ справа отправка: поле растёт под содержимое до `maxHeight`, дальше текст уходит в прокрутку
31
+ поля, и панель места не отнимает. Enter отправляет, Shift+Enter переносит строку (во время
32
+ набора иероглифов Enter не наш), `setEnterSends(false)` возвращает обычное поле. Кнопки панели
33
+ собираются фабрикой `WriteBar.Icon(...)` — со значком, подписью для скринридера и счётчиком;
34
+ отправка гаснет на пустом поле. Прилипание к низу окна панель не делает сама: для этого есть
35
+ `StickyBar`, а `shadow` поднимает её над содержимым тенью.
36
+ - **`MessageBar` — лента сообщений в пару к панели ввода.** Группирует пузыри по автору и дню,
37
+ свои ставит справа акцентом, чужим даёт аватар и подпись автора, между днями рисует дату.
38
+ Лента следит за нижним краем: если человек читает историю выше, новое сообщение не сдёргивает
39
+ прокрутку, а над нижним краем появляется кнопка «Новые сообщения» со счётчиком; та же кнопка
40
+ зовёт вниз без новых сообщений. `linkWriteBar(bar)` связывает ленту с панелью одним вызовом —
41
+ отправленное из панели встаёт в ленту со состоянием `sending`, дальше приложение ставит
42
+ `sent`/`read` через `updateMessage()`, а правка состояния не трогает прокрутку.
43
+ - **`UserBar` — полоса пользователя, шапка разговора.** Аватар, имя, состояние точкой и подписью,
44
+ кнопка возврата и действия со значками и счётчиком. Состояние показывается дважды — точкой
45
+ на аватаре и подписью («в сети», «отошёл», «занят», «не в сети»), причём своя подпись
46
+ (`setSubtitle('Иван печатает…')`) состояние не теряет, а `setSubtitle()` без аргумента
47
+ возвращает подпись состояния. С `onClick` полоса становится кнопкой (роль, обход клавиатурой,
48
+ Enter/Space, имя «открыть профиль: …»), но щелчок по кнопкам возврата и действий профиль
49
+ не открывает. Вместе с `MessageBar` и `WriteBar` собирает экран переписки.
50
+ - **`UserListBar` — список разговоров, левая колонка мессенджера.** Строка показывает человека
51
+ с точкой состояния, последнюю строку разговора, время и число непрочитанных. Время принимает
52
+ и дату, и готовую строку: дата становится «14:20» за сегодня, «Вчера» — за вчера, короткой
53
+ датой — дальше. `updateItem()` правит строку на месте, не пересобирая список: последняя
54
+ строка в разговоре меняется постоянно, и пересборка сдвигала бы прокрутку. `search: true`
55
+ ставит поле поиска, которое фильтрует по имени и последней строке; пустой список говорит
56
+ об этом, а пустой от фильтра — «Ничего не найдено». Список — `role="listbox"` с одной
57
+ остановкой Tab: стрелки водят по строкам, Home/End — края, выбор по клику и Enter/Space.
58
+ Меню строки (`menu`) открывается правым щелчком, клавишей «Меню» и Shift+F10 и строится
59
+ по строке: функция `(item) => действия` даёт подписи и доступность по её состоянию,
60
+ а `onClick(action, item)` знает и действие, и строку; для строки без действий меню
61
+ не открывается вовсе.
62
+ - **`ContextMenu`: `setItems()` и опасный пункт.** Пункты можно заменить (`setItems`) — так меню
63
+ строят по тому, на чём его открыли; у пункта появился `danger` — подпись и значок красного
64
+ цвета (цвет один на всю строку: прежде красная подпись шла с обычным значком), у слоя —
65
+ роль `menu`, у пунктов — `menuitem` и `aria-haspopup` у подменю: документация обещала это
66
+ раньше, чем код. Имя меню для скринридера даёт `setLabel(text)` — им пользуется список
67
+ разговоров: без имени меню читалось бы просто группой пунктов.
68
+ - **`ActionsBar` — панель действий.** Подпись и ряд подписанных кнопок со значками: «Закрепить»,
69
+ «Без звука», «Найти в переписке», «Очистить историю». Переключатели (`toggle`) держат
70
+ состояние, объявляют его через `aria-pressed` и видны подложкой; щелчок и правка из кода
71
+ сообщают в `onChange` одинаково, а у обычного действия состояния нет вовсе — подсветка
72
+ без программного состояния читалась бы как выбранное действие. `danger` красит подпись
73
+ акцентом ошибки, `setBadge` показывает число на плашке и добавляет его в имя кнопки.
74
+ Внутри живёт `Toolbar`: стрелки ходят по действиям, Home/End — края, Tab проходит
75
+ по кнопкам, кнопки переносятся по строкам. Ориентация `VERTICAL` ставит действия столбцом,
76
+ как пункты меню, — в таком виде панель кладут в меню за кнопкой «⋯» полосы пользователя
77
+ (`UserBar({ menu: [...] })`), и отдельная полоса в шапке не нужна: переключатели в меню
78
+ остаются открытыми, а выбор обычного действия его закрывает.
79
+
80
+ - **`Stepper`: состояния шагов и события.** Шаг может нести своё состояние (`status` у шага или
81
+ `setStatus(index, status)`): готово — зелёные подсветка и галочка, ошибка — красные подсветка
82
+ и крест, ожидание выполнения — спиннер в маркере. Состояние по месту (пройден, текущий,
83
+ предстоящий) остаётся `getStatus()`/`getStatuses()`; `undefined` возвращает шаг к нему.
84
+ Своё состояние сильнее состояния по месту — шаг с ошибкой не теряет её, когда человек ушёл
85
+ дальше. О смене состояния сообщают `addStatusListener()` и опция `onStatus`: сам `setStatus`
86
+ на шаг не переходит, а по событию приложение возвращает человека на шаг с ошибкой
87
+ (`steps.setActive(index)`). Слова состояний взяты из словарей (ru/en), а объявляет о перемене
88
+ скрытый `role="status"` словами имени маркера.
89
+ - **Токены движения** — `--motion_fast` (0.15s), `--motion_base` (0.25s), `--motion_slow` (0.4s),
90
+ кривые `--motion_ease` и `--motion_spring` в `global_style.css`. Прежний `--transition_time`
91
+ остаётся псевдонимом `--motion_fast`, поэтому свои стили, написанные раньше, не ломаются:
92
+ один жест в разных местах теперь идёт одним временем и одной кривой.
93
+ - **Виды движения в `Animation`** — `data-motion="enter" | "exit" | "pop"` в
94
+ `chui_animations/styles.css`. Имя кадра по-прежнему даёт JS, а длительность и кривую —
95
+ вид: вход базовый, выход короче (уход не задерживает следующий экран), отклик мелких
96
+ слоёв с лёгким перелётом.
97
+ - **Сторожа системы движения** — `test/chui_motion.test.js`: компонент не задаёт время
98
+ перехода своим числом, у каждого вида есть правило, компонент, называющий кадр вручную,
99
+ называет и вид, инлайновый переход не возвращается.
100
+
101
+ - **Появление и исчезновение у слоёв.** Добавлен общий кадр ухода `chui_leave_up`
102
+ (пара к `chui_appear_up`), а узел убирается по концу кадра — поэтому ничего не пропадает
103
+ рывком: закрываемое оповещение `Alert`, подсказка графиков, меню `SplitButton`,
104
+ выпадающие у `Time` и `Chips`, строка «печатает…» в `Chat`, закрываемая вкладка `Tabs`
105
+ и убираемая метка. Открытие за время ухода отменяет уборку. Раньше все они прятались
106
+ мгновенно (`display: none`, `hidden`, `remove()`).
107
+ - **Построение диаграмм.** `BarGraph` — столбцы растут от оси, `PieGraph` — секторы
108
+ прочерчиваются по кругу (подписи встают, когда круг замкнулся), `LineGraph` и `AreaGraph` —
109
+ линия идёт слева направо, `ScatterGraph` — точки растут из нуля, `Gauge` — дуга идёт от
110
+ начала шкалы к значению. Прежде полотно появлялось готовым.
111
+ - **Догоняющее значение `ProgressBar`.** Заливка догоняет число за время токена
112
+ `--motion_base`: ширину заливки у нативного `progress` задаёт браузер, переходом её
113
+ не задать, поэтому целевое значение догоняют кадры.
114
+ - **Подложка выбора у `Tabs`** — как у `SegmentedControl`: одна деталь переезжает между
115
+ вкладками, а не вспыхивает на новой.
116
+ - **Общий движок кадров** — `framework/modules/chui_animations/motion.js`: `animate(step)`
117
+ считает шаг 0…1 за время токена с торможением к покою и умеет останавливать предыдущее
118
+ движение; при уменьшенном движении шаг выполняется сразу.
119
+ - **Смена темы идёт движением — кроссфейдом.** `crossFade(work, {waitFor})` в том же модуле
120
+ просит браузер снять страницу до и после работы и перевести один кадр в другой, а `Desktop.theme.set()`
121
+ и `toggle()` идут через него: прежде тема переключалась кадром — смена `prefers-color-scheme`
122
+ приходит от браузера, и CSS-переход на неё не запускается (проверено замером: пиксель подложки
123
+ менялся скачком, хотя класс с временем перехода на корне уже стоял). Время и кривую берут
124
+ токены движения (`::view-transition-*` в `global_style.css`), при уменьшенном движении
125
+ кроссфейд не запускается вовсе. Сторож — `test/chui_motion.test.js`: обработчик перехода
126
+ не ждёт кадров (на время перехода браузер останавливает отрисовку, и `requestAnimationFrame`
127
+ не приходит — переход зависал до таймаута) и обязательно дожидается смены схемы, иначе снимок
128
+ «после» берётся до перекраски, и кроссфейд идёт из старой схемы в старую.
129
+
130
+ ### Изменено
131
+
132
+ - **Палитра пересобрана по эталонным таблицам цвета.** Основа — две таблицы, светлая и тёмная
133
+ колонки: акценты (синий `#007aff`/`#0a84ff`, красный `#ff3b30`/`#ff453a`, оранжевый
134
+ `#ff9500`/`#ff9f0a` и остальные десять) и серая шкала для фонов и нейтралей (деления 1…6).
135
+ Поверхности разложены по шкале: подложка — деление 6 (`#f2f2f7`/`#1c1c1e`), карточка в тёмной
136
+ схеме — деление 5 (`#2c2c2e`, ровно шаг шкалы), в светлой — белая (в шкале белого нет);
137
+ волосяная линия — деление 4 (в тёмной схеме это ровно то, что даёт эталонный разделитель),
138
+ под курсором — деление 2 в светлой и деление 3 в тёмной. Все двенадцать цветов таблицы
139
+ акцентов выставлены токенами (`--orange_prime_background` … `--gray_prime_background`), бейдж
140
+ предупреждения больше не коричневый и не жёлтый, а оранжевый акцент — как и положено
141
+ предупреждению. Сверка `npm run palette:check` читает значения прямо из файла темы, сравнивает
142
+ с эталоном, печатает, где занято каждое деление шкалы, и считает контраст по WCAG; список
143
+ решённого иначе (тёмная подложка не чёрная, волосяная линия тише эталонного разделителя, текст
144
+ ссылки — затемнённый акцентный синий) живёт там же с причиной у каждого пункта. Запасной цвет
145
+ графиков `#1676f3` заменён на акцентный синий.
146
+
147
+ - **Уборка истории уведомлений — каскадом, а не одним щелчком.** Записи ящика уезжают
148
+ по очереди: номер в очереди задаёт переменную `--notification_clear_index`, а шаг считает CSS
149
+ долей токена `--motion_fast`, поэтому список читается как рассыпающийся. Подписи дней гаснут
150
+ вместе с записями, а подсказка «Уведомлений пока нет» приходит общим кадром появления
151
+ `chui_appear_up` — раньше записи исчезали мгновенно и рывком, а подсказка вспыхивала.
152
+ Срок каскада считается из тех же токенов движения, а не из своего числа (в коде жило
153
+ `setTimeout(…, 300)`, которое надо было держать в согласии со стилями): конец уборки
154
+ не зависит от того, проиграл ли браузер кадр, — у скрытой записи `animationend` не приходит
155
+ вовсе. Пока каскад идёт, кнопка «Очистить» недоступна; при уменьшенном движении история
156
+ очищается сразу.
157
+
158
+ - **Запись встаёт в ящик кадром прихода, а не вспышкой на месте.** Тост, уезжая вправо,
159
+ возвращается в историю оттуда же — тем же кадром `slide-right-in`, каким он пришёл в панель.
160
+ Прежде карточка ставилась инлайновыми `opacity`/`transform` и ждала 500 мс, а вид `exit`
161
+ держит `transform` вне переходов: слайд не проигрывался вовсе, и запись просто появлялась.
162
+ Вид записи истории (`notification_in_box`) теперь сам показывает карточку — инлайновый
163
+ `display` из JS ушёл. Въезд стал одноразовым событием: раньше его обработчик ловил любой конец
164
+ кадра, в том числе кадр уборки, и переселял запись в начало списка, начиная её кадр заново.
165
+
166
+ - **Сайт документации — в тех же цветах.** Палитра сайта (`docs/assets/style.css`) выровнена
167
+ по токенам фреймворка: подложка — деление 6 серой шкалы, карточки — белые в светлой схеме
168
+ и деление 5 в тёмной, волосяная линия — деление 4, ссылка — затемнённый акцентный синий
169
+ (осветлённый в тёмной схеме). Совпадение значений проверяет `npm run palette:check`:
170
+ раздел «сайт документации» сверяет токены `--p_*` с палитрой фреймворка.
171
+ - **Подсветка примеров кода на сайте — тоже из палитры приложения.** Шесть токенов
172
+ подсветки (`--p_tok_*`) были цветами чужой таблицы редактора: синие ключевые слова,
173
+ кирпичные строки, зелёные числа. Теперь каждый — акцент палитры, подмешанный к чёрному
174
+ (светлая схема) или к белому (тёмная): ключевые слова — фиолетовый, строки — зелёный,
175
+ числа — оранжевый, вызовы — индиго, типы — бирюзовый, комментарии — серый. Доля
176
+ подмешивания выбрана под норму 4.5 (сам акцент на заливке кода даёт для текста от 1.3
177
+ до 3.6) — вышедший контраст печатает `npm run palette:check` вместе с таблицей долей.
178
+ Служебные цвета страницы тоже приведены к палитре: жёлтая отметка поиска — жёлтый акцент
179
+ (0.4 / 0.32), занавес открытого меню на узком экране — `modal_scrim`, а в печати, где
180
+ схема одна, стоят плоские значения светлой схемы. Сторож сверяет и это: цвета вне блока
181
+ токенов допускаются только из палитры (сейчас это четыре значения для печати), а мёртвые
182
+ `--p_warn` и `--p_ok` убраны.
183
+ - **Из описаний убраны имена чужой таблицы.** Палитра и её сверка говорят о «таблицах
184
+ акцентов и серой шкалы» своими словами: значения не изменились, а имена, по которым они
185
+ раньше сверялись, ушли из комментариев, отчёта и документации.
186
+
187
+ - **Открытие выпадающих панелей — движением, а не вспышкой.** Выпадающий список, меню и поповер
188
+ открываются общим кадром `chui_panel_in` (`global_style.css`) с видом `pop`: панель приходит
189
+ сверху вниз и чуть вырастает, кривая с лёгким перелётом. Прежде они брали `fade-in` — панель
190
+ не двигалась вовсе, и открытие читалось рывком. Кадр объявляет только начальное состояние,
191
+ поэтому после него на панели не остаётся вычисленного `transform`. На кадр перешли Select,
192
+ ComboBox, MultiComboBox, DateInput, палитра команд, выпадашка полосы меню, выпадашка редактора
193
+ кода, а панели, которые показывает класс или снятие `hidden` (Chips, Mention, TimeInput,
194
+ SplitButton, эквалайзер Audio), берут тот же кадр из стилей. Свой кадр `time_dropdown_in`
195
+ удалён как дубль. Панели без движения вовсе: Popover (был `display = "none"`/`flex` рывком —
196
+ теперь кадр при открытии и уход по концу кадра), меню SplitButton (кадр был только у ухода),
197
+ подменю контекстного меню и список подсказок Mention. У Popover и SplitButton уход тоже
198
+ ведёт кадр. У `Animation` появился метод `panelIn()`.
199
+ - **SidebarNav: раскрытие пункта стало движением.** Панель переключается на месте, а не пересборкой
200
+ списка (новые узлы не помнят прошлого состояния — поэтому и содержимое, и стрелка появлялись
201
+ готовыми), высоту ведёт `max-height` переходом по токену движения, `hidden` возвращается по концу
202
+ перехода, при уменьшенном движении состояние применяется сразу. Новая метка Chips встаёт в строку
203
+ кадром появления, а не возникает рывком.
204
+
205
+ - **Каждому контролу — своё движение, а не мгновенная смена состояния.** Ручка `Toggle` едет
206
+ пружиной; галка `CheckBox` прорастает из нуля; бегунки `Audio`/`Video` растут под курсором
207
+ плавно; у `SegmentedControl` выбранный вариант несёт подвижная подложка (её место и ширину
208
+ компонент считает по вёрстке и пересчитывает при изменении размера — растянутый контрол и
209
+ поворот окна её не сбивают); `DropZone` приподнимается при приёме файла; `Kanban` приподнимает
210
+ и наклоняет взятую карточку.
211
+ - **Свой ритм у появления и ухода.** Кнопка `Backtop` поднимается и гаснет (появление —
212
+ `--motion_base`, уход — `--motion_fast`), а `hidden` включается по концу ухода: в закреплённом
213
+ положении он держит место в раскладке панели.
214
+ - **Переходы `Stepper` идут движением, а не вспышкой.** Состояние применяется на месте,
215
+ без пересборки списка: узлы те же, поэтому смена цвета маркера и заливки линии читаются
216
+ переходом по токену движения, знак состояния приходит кадром, а фокус и прокрутка остаются
217
+ на месте (пересборка теряла то и другое и читалась вспышкой). Знак пересобирается только
218
+ тогда, когда он и впрямь сменился, — иначе кадр перезапускался бы сразу у всех маркеров.
219
+ Полулиния заполняется от маркера-предшественника и растёт по `scaleX` (в вертикальном
220
+ варианте — по `scaleY` сверху вниз), а заливка пройденного пути стала зелёной: за готовность
221
+ отвечает `--green_prime_background`, синий акцент остался за текущим шагом. Свой `icon` шага
222
+ показывается у текущего и предстоящего шага; у состояний знак свой — галочка, крест, спиннер.
223
+ Выключенный шаг помечен `aria-disabled="true"`.
224
+ - **Ключи цвета собраны в одном месте — в теме.** Прежде цвет жил не только в палитре:
225
+ подсветка кода в модуле была набрана цветами одной тёмной колонки (в светлой схеме примеры
226
+ читались тускло), ручка плеера стояла значением в общих стилях, а тени контролов, край
227
+ закреплённой колонки, ореол ручки сравнения картинок и затемняющая накладка `Gantt` были
228
+ своими числами в компонентах. Теперь всё это — ключи палитры: `--box_shadow_control` (бегунок
229
+ и головка переключателя), `--box_shadow_edge`, `--box_shadow_halo`, `--overlay_dim`,
230
+ `--player_*` и восемь `--code_token_*` (акценты, приглушённые к заливке кода до нормы 4.5 —
231
+ доли и вышедший контраст печатает `npm run palette:check`). Светлые тени и ручка плеера
232
+ переехали из `global_style.css` в тему: цветовых значений вне `main_theme_style.css`
233
+ в дереве больше нет.
234
+
235
+ - **Имена ключей приведены к правилам.** `--text_color_hover_disable` → `--text_color_hover_disabled`,
236
+ `--blue_prime_background_trans` → `--blue_prime_background_tint_strong` (имя говорило
237
+ «прозрачный» вместо «плотнее слабой тонировки»), восемь плашек бейджей `--badge_*_back` →
238
+ `--badge_*_background`, группа плеера `--ap_*` → `--player_*` (`--ap_slider_buf_background` →
239
+ `--player_slider_buffer_background`), мёртвый `--ap_main_background_2` убран. Цвет в JS тоже
240
+ собран в одно место: модуль `chui_palette` (`paletteColor`, `paletteScheme`, `paletteNames`) —
241
+ им теперь пользуются графики (оси, сетка, запасные цвета, пороги `Gauge`) и набор
242
+ `ColorPicker.PALETTE`; цвета графиков стали следовать схеме окна.
243
+
244
+ - **Сторож ключей и найденные им поломки.** `test/chui_tokens.test.js` требует: цветовые
245
+ литералы — только в теме, схемы объявляют один набор ключей, имена по правилам, таблица
246
+ палитры для JS совпадает с темой, и каждая ссылка `var(--имя)` разрешается. Последняя
247
+ проверка сразу нашла пять битых ссылок: код в `Badge` и `MarkdownEditor` брал необъявленный
248
+ `--code_font` (моношрифт не применялся — теперь общий ключ `--font_mono`), лента `CardScroll`
249
+ — необъявленный `--box_shadow_sm` (тень карточки не рисовалась — теперь `--box_shadow_main`),
250
+ а ручка и кнопки сравнения картинок — необъявленный `--background_color` (подложки не было;
251
+ теперь `--element_background_2`).
252
+
253
+ - **Имена всех переменных — по одним правилам, а не только цветовых.** Дефис внутри имени
254
+ убран: `--fx-before-width`, `--seek-before-width`, `--volume-before-width` →
255
+ `--fx_before_width`, `--seek_before_width`, `--volume_before_width` (дефис остаётся только
256
+ после `--`, как разделитель префикса). Сокращения заменены словами: `--tabs_x`/`--tabs_w` →
257
+ `--tabs_offset`/`--tabs_width`, `--segmented_x`/`--segmented_w` →
258
+ `--segmented_offset`/`--segmented_width`, а на сайте документации `--p_bg`, `--p_code_bg` и
259
+ шесть `--p_tok_*` → `--p_background`, `--p_code_background`, `--p_token_*`. Уточнены имена
260
+ без своего компонента: `--progress_height` → `--progress_bar_height`, `--sticky_offset` →
261
+ `--sticky_bar_offset`. Прежние публичные имена остались рабочими, но значение держит старый
262
+ ключ, а новый читает его: `--margin` → `--block_margin`, `--main_padding` → `--block_padding`,
263
+ `--main_padding_sm` → `--block_padding_sm`, `--border_radius` → `--radius_sm`,
264
+ `--test_padding` → `--control_padding`, `--transition_time` → `--motion_fast`,
265
+ `--ease_out` → `--motion_ease`. Правила теперь проверяются для каждого ключа дерева:
266
+ строчные буквы и подчёркивание, слово целиком вместо сокращения, никаких имён-заглушек,
267
+ и каждый объявленный ключ кем-то читается — акценты палитры читает код через `paletteColor`,
268
+ прежние имена перечислены исключениями с причиной. Витрина и сайт входят в ту же проверку:
269
+ ключ, который читает только их вёрстка, обязан быть объявленным.
270
+
271
+ - **Имена в JS — `camelCase`, а `const` выбирается по факту.** `snake_case` остался только
272
+ у ключей CSS и у данных чужого API: переменные, параметры и функции приведены к `camelCase` —
273
+ 226 имён в 271 месте. Из них не только camelCase, но и слово вместо сокращения и заглушки:
274
+ `main_test` → `sectionNode` (и `treeNode` в дереве), `checkbox_input` → `checkboxInput`,
275
+ `filter_test` → `preset`, `test_main`/`test_body`/`test_remove` →
276
+ `optionChip`/`optionChipBody`/`optionChipRemove`, `p_slider`/`p_val` →
277
+ `preampSlider`/`preampValue`, `ec_length` → `errorCorrectionLength`, `table_cols` →
278
+ `tableColumns`, `sum_slides` → `slideTotal`, `id_name` → `identifier`, `pos_bool` →
279
+ `isEnabled`, `stringz` → `styleGroups`, а `widow_size` → `windowSize` (опечатка),
280
+ `задержка_ms` → `delayMs`. Переименование сделано по смыслу, а не по тексту: имена классов
281
+ вида `checkbox_input` в строках разметки не тронуты — сверка литералов до и после правки
282
+ совпала. `let` остался только там, где значение переприсваивают, — 612 объявлений стали
283
+ `const`; `var` убран из `docs/assets/wiki.js` (91 объявление), где он остался от старого
284
+ стиля. Заодно `ActionSheet.show` собран без «пустого» `let sheet`: имя объявлено `const`
285
+ там, где слой создаётся (ответ зовут уже после сборки), а `image.js` — без `let imageSet`
286
+ под одну сборку. Публичное имя `chui_functions.style_parse` осталось ключом — его читает
287
+ `chui_fonts.js` и чужой код, — а сама функция называется `parseStyle`.
288
+ - **Правила имён и объявлений стережёт линтер.** В `eslint.config.js` добавлены `camelcase`,
289
+ `prefer-const` и `no-var`: правило входит в `npm run lint`, то есть в `npm run check`
290
+ и в CI, поэтому прежние семьи имён больше не вернутся. «Стиль кода» в `CONTRIBUTING.md`
291
+ называет те же три правила и порядок для публичных имён.
292
+ - **Приватные имена классов — тоже `camelCase`.** Поля и методы вида `#label_text` стали
293
+ `#labelText` — 4433 идентификатора в 118 файлах (551 имя). Правку делал разбор деревом
294
+ TypeScript, а не поиск по тексту: строки и комментарии не тронуты, сверка литералов до и после
295
+ совпала. Константы (`#DEFAULT_MAX`, `#MAX_COLUMNS`) оставлены как были — правило `camelCase`
296
+ их не касается. Восемь имён, рядом с которыми уже жило такое же имя в camelCase (поле-опция
297
+ и метод), названы развёрнуто: `#on_item_click` → `#onItemClickCallback`, `#value_text` →
298
+ `#valueLabel`, `#window_page` → `#windowPagePath`. Правило стережёт линтер: `camelcase`
299
+ приватные имена не смотрит (`properties: "never"`), поэтому в `eslint.config.js` добавлен
300
+ селектор `PrivateIdentifier[name=/_[a-z]/]`, а подчёркивание осталось только у констант.
301
+ - **`Main` переехал из точки сборки в свой модуль.** Класс окна вынесен в
302
+ `framework/main_window.js`: `index.js` перестал быть самым большим файлом репозитория
303
+ (1606 → 400 строк), а публичный доступ не изменился — `require('chuijs').Main` тот же.
304
+ Страница `Styles` заодно перестала показывать методы окна: они попадали в её «Состав в коде»
305
+ только потому, что оба класса лежали в одном файле.
306
+ - **Ссылки окно открывает в браузере, а не в себе.** `window.open`, `target="_blank"` (ссылки
307
+ `Link`) и переход на другой адрес уходят в системный браузер — наружу только `http(s)`.
308
+ Раньше `window.open` создавал новое окно Electron с собственными `webPreferences`, а переход
309
+ уносил окно без адресной строки на чужой сайт без пути назад.
310
+ - **Проверки стали строже, а сторожа — точнее.** `npm run lint` идёт с `--max-warnings=0`,
311
+ добавлено правило `eqeqeq` (и два сравнения `!= null` исправлены), компонентам запрещён
312
+ `console`; у покрытия появился порог (`--check-coverage`), который не даст ему тихо просесть;
313
+ `actions/cache@v3` заменён на `@v4`. Тестов стало 329 (было 284): подсветка кода, DOM-часть
314
+ `chui_functions`, раскрывающиеся блоки (`Accordion`/`Details`/`FieldSet`) и ограничение
315
+ навигации окна.
316
+ - **Скан «текст против кода» в доках перестал считать ложные упоминания.** Сверка методов
317
+ принимала за заявку упоминание в отрицании («`destroy()` не нужен») и имена платформы
318
+ (`String()`, `getBoundingClientRect()`): таких «упомянут, но не найден» было 110, стало 70.
319
+ - **CSS подключается через `textContent`.** `setStyles` и `parseStyle` писали тело `<style>`
320
+ через `innerHTML` и `innerText`: первое разбирает CSS как разметку, второе в jsdom не работает
321
+ вовсе. Теперь в `<style>` кладётся текст — как и положено этому элементу.
322
+
323
+ - **Документация — про имена целиком, а не только про цвета.** На странице «Темы» появился
324
+ раздел «Имена переменных — одни правила для всех ключей»: подчёркивание внутри имени, слово
325
+ целиком вместо сокращения, имена без заглушек, каждый объявленный ключ читается; там же —
326
+ таблица прежних публичных имён и объяснение, почему старый ключ держит значение, а новый его
327
+ читает. «Константы» уточняют шкалу плеера (глиф панели задаётся ключом самому значку, а не
328
+ наследует размер кнопки), перечисляют пары имён и называют убранные мёртвые ключи. «Плееры»
329
+ и «Дерево» описаны по коду: глиф берёт размер из `--player_icon_size`, а высота строки в
330
+ переменном режиме идёт одним ключом `--tree_view_row_height` и на распорки, и на вёрстку.
331
+ В «Планах» обновлены числа (284 теста) и список сторожей — сторожа ключей теперь называют
332
+ и правила имён.
333
+
334
+ - **Строки списков встают на место.** Журнал `Console`, сообщения `Chat`, элементы
335
+ `ClipboardHistory` и события `Timeline` берут общий кадр `chui_appear_up` из
336
+ `global_style.css` — одно движение на все списки, которые наполняются по одной. Графики на
337
+ канвасе появляются тем же кадром: полотно встаёт на место, а не вспыхивает (рост столбцов и
338
+ прорисовку линии задаёт само рисование — это следующий шаг для графиков).
339
+ - **Токен `--motion_none`** (`0s`) — переключатель без движения: им `visibility` возвращается
340
+ только по концу ухода, поэтому узел не мигает и не исчезает посреди анимации.
341
+ - **Переходы компонентов переведены на токены.** Свои числа и `--transition_time` убраны из
342
+ `accordion`, `details`, `icons`, `inputs/file`, `inputs/slider`, `inputs/time`, `sidebar_nav`,
343
+ `tree_table`, `tree_view`, `media/audio`, `media/video`, `progress_ring`, `scrollbar`,
344
+ `slideshow`, `sticky_bar`, `tour`, `tooltip`. Кривая везде одна (`--motion_ease`), а время
345
+ выбрано по роли: состояния — `--motion_fast`, появление и перемещение — `--motion_base`,
346
+ крупные поверхности — `--motion_slow`. Круговые индикаторы (`spinner`, `skeleton`,
347
+ `progress_ring`) оставлены со своим временем: это фон ожидания, а не жест.
348
+ - **Яркость в общем правиле.** `filter` добавлен в `* { transition }`: подсветка события в
349
+ `Gantt` и `Scheduler` (единственные места с `filter`) больше не переключается рывком.
350
+ - **Крупные слои называют своё время сами.** Панель `Drawer` выезжает временем
351
+ `--motion_slow`, а уезжает `--motion_fast`; у `ActionSheet` и `ContextMenu` уход короткий.
352
+ Прежде у этих слоёв уход шёл временем появления, а у меню — ещё и кривой браузера.
353
+ - **Ссылка в тексте** подчёркивается плавно: `text-decoration-color` вместо мгновенного
354
+ появления подчёркивания. Закреплённый `Affix` меняет радиус и тень одним переходом.
355
+ - **Вычислено вместо инлайна.** У слоя движения переход на `transform` выключен правилом
356
+ `[data-motion] { transition-property: … }`, а не инлайновым `style.transition`.
357
+
358
+ - **CI гоняет те же проверки, что и локально.** В матрице Node 18/20/22 проверки идут одним шагом
359
+ `npm run check:code` (линтер, форматирование, типы, сверка палитры и тесты) вместо перечисления
360
+ шагов в workflow — теперь новая проверка попадает в CI сама. Прежде `npm run palette:check`
361
+ в CI не запускался вовсе, а тесты в матрице не гонялись: их считала только задача покрытия.
362
+ `npm run check` — это `check:code` плюс `docs:check`.
363
+ - Документация: в `CONTRIBUTING.md` добавлены команды `palette:check` и `check:code`; на странице
364
+ «Планы» обновлены цифры — 227 тестов и покрытие `framework/**` около 26% вместо прежних 6%,
365
+ и перечислены сторожа общих правил (движение, «тихие» отказы, модальный слой, карточки
366
+ уведомлений).
367
+
368
+ ### Исправлено
369
+
370
+ - **Размытие подложки у слоёв с движением.** Кадр появления оставлял на слое вычисленный
371
+ `transform` — вид движения держит конечное состояние (`animation-fill-mode: both`). Элемент
372
+ с `transform` становится собственным «backdrop root», и размытие подложки перестаёт видеть
373
+ содержимое под слоем: контекстное меню, выпадающие списки, поповер, панель меню-бара и тосты
374
+ шли прозрачной подложкой без блюра (замер: у меню контекста картинка не менялась при
375
+ отключении `backdrop-filter`). Теперь драйвер снимает вид по концу кадра входа (`#entranceEnd`),
376
+ а имя кадра остаётся — по нему видно, что слой открыт. Контекстному меню этого мало: снятие
377
+ вида помогало лишь после кадра — и всё время открытия меню (0.25 с) шло без блюра, а размытие
378
+ появлялось рывком уже на открытом меню. Поэтому у меню пункты переехали в отдельный слой
379
+ (`context_menu_panel`, его отдаёт `getPanel()`), кадр с прозрачностью и усадкой играет он,
380
+ а поверхность с блюром стоит недвижимо и растворяется своим кадром по псевдоэлементу —
381
+ замер: на 30-й мс открытия и поверхность, и пункты на 0.53, `transform: none` у слоя,
382
+ `backdrop-filter: saturate(1.8) blur(20px)`. Уход играет та же пара кадров.
383
+
384
+ - **Рамка меню на уходе становилась меньше пунктов.** Поверхность меню подрастала по `inset`
385
+ (8px → 0), и на уходе, пока пункты ещё не погасли, рамка с тенью сжималась внутрь — подпись
386
+ вылезала за кромку: замер на 60-й мс ухода — поверхность на 7px меньше пунктов при их
387
+ прозрачности 0.06. Теперь кадр поверхности идёт только по прозрачности и ничего не двигает:
388
+ рамка всё время охватывает пункты, а растворение поверхности и пунктов идёт одним временем
389
+ (на 25-й мс ухода оба на 0.47, меню убирается по концу кадра). Прозрачность на самой
390
+ поверхности блюру не мешает — проверено снимком при 0.55.
391
+
392
+ - **Прилипающая полоса теряла фон.** Полоса отдавала контейнером прокрутки документ вместо своей
393
+ области прокрутки, если узел попадал в документ позже первого кадра (страница витрины собирает
394
+ навигацию после сборки содержимого): порог закрепления вставал на нуле, состояние `data-stuck`
395
+ оставалось `false`, и материал не рисовался — полоса шла прозрачной поверх содержимого. Теперь
396
+ подключение ждёт появления узла в документе (наблюдателем, а не опросом по кадрам), а не одного
397
+ кадра. Навигация по разделам в витрине получила зазор до шапки (`offset: 8`).
398
+
399
+ - **Список `Select` мигал при открытии.** Поле открывают и `focus`, и `click` одного действия —
400
+ показ приходил в драйвер дважды, а `panelIn()` перед кадром снимает имя кадра, поэтому второй
401
+ вызов начинал кадр заново (панель успевала показаться и тут же прыгала к началу). Теперь
402
+ повторный показ уже открытой панели кадр не запускает, а после настоящего закрытия кадр
403
+ играет снова с нуля. Правило в драйвере, поэтому оно закрывает и остальные поля с тем же
404
+ действием (ComboBox, MultiComboBox, DateInput).
405
+
406
+ - **Заголовок ящика и весь диалог пропадали.** Модальный слой при открытии не покидал карточку
407
+ приложения, а занавес и шапка окна живут в `body`. Chromium собирает слой с `backdrop-filter`
408
+ и `z-index` отдельной поверхностью и рисует её под слоями из `body`: у `Drawer` шапка срезала
409
+ верх панели вместе с заголовком и кнопкой закрытия, а `Dialog` целиком уходил под занавес и
410
+ читался одним размытым пятном. Теперь оба слоя при открытии встают в `body` — как уже делают
411
+ `ActionSheet`, `ContextMenu`, `MenuBar` и попапы, — а после ухода возвращаются туда, откуда
412
+ их открыли, и занавес диалога уходит из документа (`test/chui_overlay_host.test.js`).
413
+ - **Инлайновый `transition: transform .0s` глушил у слоя все переходы** — он подменял список
414
+ свойств целиком, поэтому слой после первого показа терял плавность цвета, тени и прозрачности.
415
+ - **У `ContextMenu` не было ни своего времени, ни кривой приложения**: меню шло временем общего
416
+ правила и кривой браузера (`ease`) — своим ритмом из общего ряда.
417
+ - **Слои, называвшие кадр вручную** (`Drawer`, `ActionSheet`, `ContextMenu`, страховочные имена
418
+ у `Notification` и `ToastQueue`), берут время и кривую по виду движения, а не по общему правилу.
419
+
420
+ - **Цвета в приложении на сеансе Wayland совпадают с токенами темы.** Причина найдена замером
421
+ пикселей на экране, а не чтением `getComputedStyle`: Chromium поднимал окно через протокол
422
+ цветоуправления Wayland (`wp_color_manager_v1`) и применял к тёмным тонам чужую кривую —
423
+ вместо `#1c1c1e` из `main_theme_style.css` на экране было `#222223` (сдвиг +6), тогда как
424
+ чёрный и белый оставались на месте. `Main` при старте отключает этот протокол в Chromium
425
+ (`WaylandWpColorManagerV1` добавляется в `--disable-features`), нативный Wayland при этом
426
+ сохраняется: сдвиг +6 → 0 на всех проверенных токенах, и теперь приложение показывает цвет
427
+ так же, как терминал. Значение флага, переданное клиентом, не затирается — своё дописывается
428
+ к нему. Рядом остаётся страховка `--force-color-profile=srgb` — но уже как отдельная мера: она
429
+ держит цвета темы в sRGB на широкоохватных экранах и сдвиг на Wayland не лечила (замер это
430
+ показал), так что причиной правки не является.
431
+ Про цветовое пространство и про то, почему пипетка по полупрозрачным материалам даёт смесь,
432
+ написано на странице «Themes».
433
+
434
+ - **Меню `SplitButton` больше не залипает открытым.** Класс показа задан селектором с элементом
435
+ (`chui_split_menu.split_button_menu_open`), а класс ухода — коротким (`.split_button_menu_leaving`):
436
+ кадр ухода проигрывал по специфичности, `animationend` не приходил, класс показа оставался на узле,
437
+ и меню не закрывалось ни повторным нажатием каретки, ни кликом снаружи (состояние при этом уже
438
+ считалось закрытым). Правило ухода теперь называет оба класса, поэтому кадр играет и класс
439
+ снимается. Сторож — `test/chui_motion.test.js`: правило ухода не должно быть слабее по
440
+ специфичности правила показа того же узла, иначе узел остаётся открытым.
441
+
442
+ - **Вложенный контейнер больше не сливается с карточкой.** Поверхностей в теме три: подложка,
443
+ карточка и нейтральная заливка. Контейнер носил карточную — и карточка тоже, поэтому внутри
444
+ карточки шаг светлоты был нулевым и блоки разделяла только граница: в витрине так выглядели
445
+ `SidebarNav`, `List`, `Toolbar`, `CodeEditor`, `TreeView`, `Gantt`, `Scheduler`, `Statistic`,
446
+ `Alert`, `DropZone`, `Splitter`, `ShortcutHelp`, `ClipboardHistory`, `JsonViewer`,
447
+ `StatusBar`, `Code` и разделы настроек. Теперь вложенный контейнер берёт нейтральную заливку
448
+ (правило в `global_style.css`), и шаг виден: замер пикселя даёт 8.3 в тёмной схеме и 5.5
449
+ в светлой — не меньше, чем между подложкой и карточкой (7.7 и 4.4). Своя заливка у контейнера
450
+ остаётся: на подложке он по-прежнему читается как карточка, опускает его только вложенность.
451
+ Числа печатает `npm run palette:check` новым разделом, он же проверяет, что список контейнеров
452
+ в правиле не разошёлся с компонентами.
453
+
454
+ - **Обводка в тёмной схеме видна так же, как в светлой.** Волосяная линия была делением 4
455
+ (`#3a3a3c`): на подложке это давало шаг светлоты 14, а на карточке — всего 6.4 против 16
456
+ в светлой схеме, поэтому в тёмной теме рамки читались «не очень». А нужна рамка на карточке
457
+ чаще, чем на подложке: ею разделены блоки внутри карточки. Линия переехала на деление 3
458
+ (`#48484a`, шаг 12.6), под курсором — деление 2 (`#636366`), то же в палитре сайта документации.
459
+ Числа печатает `npm run palette:check`, он же следит, чтобы шаг не стал меньше 10.
460
+
461
+ - **Hero — акцентная шапка страницы.** Подложка теперь несёт свечение акцентов по углам
462
+ и акцентную линию сверху, знак залит градиентом двух акцентов вместо плоского синего,
463
+ а плитки статистики берут акценты по кругу (синий, сиреневый, зелёный, оранжевый) —
464
+ `addStat()` ставит акцент сам. Цвет идёт заливкой, рамкой и полосой, а число и подпись
465
+ читаются цветом темы: акцентный текст на тинте дал бы контраст ниже 3.
466
+
467
+ - **Появление Hero — по шагам, числа догоняют значения.** Движение шапки было одним кадром:
468
+ сейчас это хореография из одиночных кадров на токенах темы — линия сверху прочерчивается
469
+ слева направо, знак встаёт с перелётом и выпускает кольцо-отклик, заголовок → описание →
470
+ плитки приходят по очереди (у каждой плитки своя ступенька, и её полоса растёт сверху вниз),
471
+ а замыкает блик по подложке. Сами числа в плитках догоняют значения шагом общего движка
472
+ движения (`animate`), поэтому время — из темы, а при уменьшенном движении значение встаёт
473
+ сразу; значение, которое не разбирается как число, остаётся текстом. Непрерывных кадров нет:
474
+ шапка не индикатор ожидания — по кругу идут только индикаторы.
475
+
476
+ - **Материалы стали прозрачнее: 0.6 при блюре `saturate(180%) blur(20px)`.** Шапка, панели,
477
+ меню, тосты и модалки брали заливку при прозрачности 0.72–0.9, теперь у всех 0.6
478
+ (`--header_background`, `--header_background_scrolled`, `--modal_background`); фильтр у них
479
+ и был тем же — `--global_blur` со значением `saturate(180%) blur(20px)`. Сквозь материал
480
+ видно содержимое: это и есть смысл материала, а не плоской заливки. У шапки при прокрутке
481
+ плотная ступень убрана — состояние показывает тень, а не заливка.
482
+
483
+ - **Полосы оболочки носят цвет подложки.** У шапки, меню маршрутов и ящика уведомлений
484
+ (и у подписи дня в ящике) заливка была общей с меню и поповерами — `--header_background`,
485
+ то есть цвет карточки; в тёмной схеме полоса выходила светлее подложки и читалась отдельной
486
+ плашкой. Появился токен `--bar_background` — цвет подложки при прозрачности 0.6, и полосы
487
+ берут его: в тёмной схеме это `#1c1c1e`, в светлой — `#f2f2f7` (там же был и материал меню,
488
+ поэтому светлая схема визуально не изменилась). Всплывающие меню, попапы, дропдауны и тосты
489
+ остались на `--header_background`: над подложкой они должны читаться отдельной поверхностью.
490
+ Блюр у полос тот же — `--global_blur`.
491
+
492
+ - **Подпись дня в ящике уведомлений стала полностью прозрачной и встала по центру.** «Сегодня»,
493
+ «Вчера» и дата раньше несли заливку полосы оболочки — на тёмной схеме читались тёмной полосой
494
+ поверх карточек. Теперь у подписи нет ни заливки, ни размытия: она только называет день,
495
+ а карточки при прокрутке проходят прямо под ней.
496
+
497
+ - **Закреплённая полоса прилипания тоже носит цвет подложки.** `StickyBar` в состоянии
498
+ `data-stuck="true"` брал `--header_background` (цвет карточки) и выходил светлой плашкой
499
+ поверх подложки — теперь берёт `--bar_background`, как шапка, меню маршрутов и ящик
500
+ уведомлений: полоса читается продолжением страницы, а отделяют её волосяной край и тень.
501
+ Блюр остался общий — `--global_blur`.
502
+
503
+ - **Состояния чипов в полосе прилипания — как у кнопок.** Чипы витрины остались своими узлами
504
+ (капсула и подпись 12px — как было), но наведение и нажатие взяты из правил `.chui_button`:
505
+ наведение — нейтральная ступень `--element_background_hover` и рамка `--border_color_hover`,
506
+ нажатие — акцентная заливка, акцентная рамка и лёгкая усадка `scale(0.97)`, как у кнопки
507
+ компонента. Прежде чип заливался акцентом уже на наведении и нажатие этим стирал.
508
+ - **`MultiComboBox`: строка списка занимает всю ширину слоя.** Секция держит
509
+ `align-items: flex-start` (иначе подпись группы растягивалась и наследовала центрирование),
510
+ а от него строка сжималась до содержимого: подсветка под курсором и курсор клавиатуры шли
511
+ прямоугольником вокруг флажка вместо строки (замер: 53 px против 288 px у списка). Отмеченная
512
+ строка при этом по-прежнему ничего не подсвечивает — выбор видно чекбоксом; заголовки секций
513
+ не изменились.
514
+ - **`WriteBar`: поле панели в одну линию ровно высотой с кнопки.** Поле было коробкой
515
+ с отступами по восьми пикселям от края, а кнопки — кругами контрольной высоты, и при
516
+ выравнивании по низу верх поля выходил выше кнопок: строка панели читалась кривой. Теперь
517
+ поле — такая же коробка, как у остальных полей фреймворка (заливка контрола, волосяная
518
+ линия, радиус), а отступы и строка подобраны под контрольную высоту, поэтому значок слева,
519
+ поле и отправка стоят в одну линию; поле по-прежнему растёт под текст до `maxHeight`.
520
+ Заодно у поля снято кольцо фокуса — в панели ввода каретка и так показывает, где набирают,
521
+ а контур вокруг строки рябил на каждом щелчке; активное поле выдаёт кромка акцентом.
522
+ - **Мёртвые ключи убраны.** `--button_background_hover` был объявлен в обеих схемах и не
523
+ читался нигде: наведение кнопки красит общий `--element_background_hover`, а старый ключ
524
+ остался от прежней семьи `button_*`. `--player_row_height` был описан в документации, но к
525
+ нему нет разметки: списка дорожек в плеере нет вовсе. Оба ключа и строка документации
526
+ удалены.
527
+ - **Глиф плеера берёт размер ключом.** Размер значка панели был вписан числом в код — `18px`
528
+ в пяти местах `audio.js` и `video.js`, — а документация обещала ключ `--player_icon_size`.
529
+ Теперь его читают стили плееров; общий ключ оказался нужен потому, что у значка есть своё
530
+ правило кегля (`chui_icon` носит размер шапки), и без своего правила глиф съезжал на 13pt:
531
+ замер на витрине — 17.33 px вместо 18 px, после правки снова 18 px.
532
+ - **Высота строки дерева — одно число на вёрстку и на распорки.** Переменный режим писал
533
+ `--tree_view_row_height` из замера строки, но в стилях её никто не читал: `tree_view_button`
534
+ держала `min-height: 32px` своим числом, и замер влиял только на арифметику распорок.
535
+ Теперь строка читает ключ (`min-height: var(--tree_view_row_height, 32px)`), а 32 px остаются
536
+ значением до замера — вёрстка и окно переменного режима считают по одной высоте.
537
+
538
+ ### Пакет
539
+
540
+ - Версия 4.0.4.
541
+
6
542
  ## [4.0.3] - 2026-09-30
7
543
 
8
544
  ### Добавлено