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
@@ -0,0 +1,248 @@
1
+ /* ── MessageBar — лента сообщений ───────────────────────────── */
2
+ chui_message_bar {
3
+ display: flex;
4
+ flex-direction: column;
5
+ box-sizing: border-box;
6
+ width: -webkit-fill-available;
7
+ height: max-content;
8
+ margin: var(--block_margin);
9
+ overflow: hidden;
10
+ border-radius: var(--radius_sm);
11
+ background: var(--element_background);
12
+ border: 1px solid var(--border_color);
13
+ font-size: var(--font_default_size);
14
+ color: var(--text_color);
15
+ }
16
+
17
+ /* Лента и кнопка «Новые сообщения» живут в одной коробке: кнопка лежит поверх нижнего края,
18
+ а не под лентой, — поэтому она и попадает в поле зрения, и не отнимает высоту у сообщений */
19
+ message_bar_view {
20
+ position: relative;
21
+ display: flex;
22
+ flex-direction: column;
23
+ height: var(--message_bar_height, 320px);
24
+ min-height: 0;
25
+ }
26
+
27
+ /* Лента прокручивается сама: страница под ней не растёт. Сообщения лежат отдельным слоем —
28
+ за его размером следит `ResizeObserver`, чтобы лента осталась у нижнего края, когда стили
29
+ доехали, окно изменило ширину или догрузилась картинка */
30
+ message_bar_feed {
31
+ display: flex;
32
+ flex-direction: column;
33
+ flex: 1 1 auto;
34
+ min-height: 0;
35
+ box-sizing: border-box;
36
+ padding: var(--control_padding);
37
+ overflow-y: auto;
38
+ overflow-x: hidden;
39
+ background: var(--element_background_2);
40
+ /* Кольцо фокуса рисует общее правило темы, а смещает его внутрь общий список
41
+ обрезанных коробок (`message_bar_feed` рядом с `chat_log` в global_style.css):
42
+ лента лежит в коробке с `overflow: hidden`, внешний контур срезался бы */
43
+ }
44
+
45
+ message_bar_content {
46
+ display: flex;
47
+ flex-direction: column;
48
+ flex: 1 1 auto;
49
+ gap: 6px;
50
+ min-height: 0;
51
+ }
52
+
53
+ message_bar_empty {
54
+ margin: auto;
55
+ padding: var(--control_padding);
56
+ color: var(--text_color_disabled);
57
+ font-size: var(--font_labels_size);
58
+ text-align: center;
59
+ }
60
+
61
+ message_bar_empty[hidden] {
62
+ display: none;
63
+ }
64
+
65
+ /* Кнопка «Новые сообщения»/«К низу»: показ и уборка идут переходом, поэтому кнопка
66
+ не мигает и в скрытом состоянии не перехватывает щелчки (видимость снимается целиком) */
67
+ .message_bar_jump {
68
+ position: absolute;
69
+ inset-block-end: var(--control_padding);
70
+ inset-inline-start: 50%;
71
+ display: flex;
72
+ flex-direction: row;
73
+ align-items: center;
74
+ gap: 4px;
75
+ box-sizing: border-box;
76
+ padding: 4px var(--control_padding);
77
+ border: none;
78
+ border-radius: var(--radius_pill);
79
+ background: var(--blue_prime_background);
80
+ color: var(--text_color_hover);
81
+ font-family: inherit;
82
+ font-size: var(--font_labels_size);
83
+ font-weight: 500;
84
+ cursor: pointer;
85
+ box-shadow: var(--box_shadow_main);
86
+ visibility: hidden;
87
+ opacity: 0;
88
+ transform: translateX(-50%) translateY(4px);
89
+ transition:
90
+ opacity var(--motion_fast) var(--motion_ease),
91
+ transform var(--motion_fast) var(--motion_ease),
92
+ visibility var(--motion_fast);
93
+ }
94
+
95
+ .message_bar_jump[data-visible="true"] {
96
+ visibility: visible;
97
+ opacity: 1;
98
+ transform: translateX(-50%);
99
+ }
100
+
101
+ .message_bar_jump:hover {
102
+ background: var(--blue_prime_background_tint_strong);
103
+ }
104
+
105
+ .message_bar_jump_icon {
106
+ display: flex;
107
+ align-items: center;
108
+ }
109
+
110
+ .message_bar_jump chui_icon {
111
+ color: inherit;
112
+ }
113
+
114
+ /* Разделитель дня: подпись-пилюля по центру ленты */
115
+ message_bar_day {
116
+ display: flex;
117
+ justify-content: center;
118
+ padding: 2px 0;
119
+ }
120
+
121
+ message_bar_day_label {
122
+ padding: 2px var(--control_padding);
123
+ border-radius: var(--radius_sm);
124
+ background: var(--element_background);
125
+ border: 1px solid var(--border_color);
126
+ color: var(--text_color_disabled);
127
+ font-size: var(--font_labels_size);
128
+ }
129
+
130
+ /* Группа сообщений одного автора одного дня: аватар и стопка пузырей */
131
+ message_bar_group {
132
+ display: flex;
133
+ flex-direction: row;
134
+ align-items: flex-end;
135
+ gap: 8px;
136
+ }
137
+
138
+ message_bar_group[data-own="true"] {
139
+ flex-direction: row-reverse;
140
+ }
141
+
142
+ message_bar_bubbles {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: 4px;
146
+ max-width: 78%;
147
+ min-width: 0;
148
+ }
149
+
150
+ message_bar_bubble {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: 2px;
154
+ padding: 8px 10px;
155
+ border-radius: var(--radius_sm);
156
+ background: var(--element_background);
157
+ border: 1px solid var(--border_color);
158
+ color: var(--text_color);
159
+ /* Новое сообщение встаёт на место: кадр общий — см. `chui_appear_up` в global_style.css */
160
+ animation: chui_appear_up var(--motion_base) var(--motion_ease);
161
+ }
162
+
163
+ /* Хвостик стопки: у последнего пузыря угол со стороны автора скруглён меньше — по нему
164
+ видно, где стопка кончается */
165
+ message_bar_bubble[data-last="true"][data-own="false"] {
166
+ border-end-start-radius: calc(var(--radius_sm) / 2);
167
+ }
168
+
169
+ message_bar_bubble[data-last="true"][data-own="true"] {
170
+ border-end-end-radius: calc(var(--radius_sm) / 2);
171
+ }
172
+
173
+ /* Свои сообщения — акцентом и справа */
174
+ message_bar_bubble[data-own="true"] {
175
+ background: var(--blue_prime_background);
176
+ border-color: var(--blue_prime_background);
177
+ color: var(--text_color_hover);
178
+ }
179
+
180
+ message_bar_author {
181
+ display: block;
182
+ font-size: var(--font_labels_size);
183
+ font-weight: 600;
184
+ color: var(--blue_prime_background);
185
+ }
186
+
187
+ message_bar_text {
188
+ display: block;
189
+ white-space: pre-wrap;
190
+ word-break: break-word;
191
+ line-height: 1.35;
192
+ }
193
+
194
+ message_bar_attachment {
195
+ display: block;
196
+ margin-block-start: 2px;
197
+ font-size: var(--font_labels_size);
198
+ text-decoration: underline;
199
+ }
200
+
201
+ message_bar_attachment a {
202
+ color: inherit;
203
+ }
204
+
205
+ message_bar_meta {
206
+ display: flex;
207
+ flex-direction: row;
208
+ align-items: center;
209
+ gap: 4px;
210
+ align-self: flex-end;
211
+ color: var(--text_color_disabled);
212
+ font-size: var(--font_labels_size);
213
+ }
214
+
215
+ message_bar_bubble[data-own="true"] message_bar_meta {
216
+ color: var(--text_color_hover_disabled);
217
+ }
218
+
219
+ message_bar_status {
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ }
224
+
225
+ message_bar_status chui_icon {
226
+ color: inherit;
227
+ font-size: var(--font_labels_size);
228
+ }
229
+
230
+ message_bar_status[data-status="failed"] {
231
+ color: var(--red_prime_background);
232
+ }
233
+
234
+ /* Строка «печатает…»: на контейнерной ступени под лентой, как у чата */
235
+ message_bar_typing {
236
+ padding: var(--control_padding) var(--block_padding);
237
+ color: var(--text_color_disabled);
238
+ font-size: var(--font_labels_size);
239
+ animation: chui_appear_up var(--motion_base) var(--motion_ease);
240
+ }
241
+
242
+ .message_bar_typing_leaving {
243
+ animation: chui_leave_up var(--motion_fast) var(--motion_ease) forwards;
244
+ }
245
+
246
+ message_bar_typing[hidden] {
247
+ display: none;
248
+ }
@@ -14,7 +14,9 @@ class Dialog {
14
14
  #footer = document.createElement("dialog_footer");
15
15
  //
16
16
  #a11y = undefined;
17
- #outside_click_bound = false;
17
+ #outsideClickBound = false;
18
+ // Место, откуда слой открыли: туда он вернётся после ухода
19
+ #origin = undefined;
18
20
  isOpen = false;
19
21
 
20
22
  constructor(
@@ -44,26 +46,43 @@ class Dialog {
44
46
 
45
47
  if (options.closeOutSideClick) {
46
48
  this.#dialog.addEventListener("animationend", (evt) => {
47
- if (evt.animationName === "fade-in" && !this.#outside_click_bound) {
48
- this.#outside_click_bound = true;
49
+ if (evt.animationName === "fade-in" && !this.#outsideClickBound) {
50
+ this.#outsideClickBound = true;
49
51
  window.addEventListener("click", this.#closeOutsideEvent);
50
52
  }
51
53
  });
52
54
  }
55
+ // Слой возвращается на место после ухода и снимает занавес из документа
56
+ this.#dialog.addEventListener("animationend", this.#returnHome);
53
57
  //ADDS
54
58
  this.#dialog.appendChild(this.#body);
55
59
  }
56
60
 
57
61
  #closeOutsideEvent = (event) => {
58
62
  if (!this.#dialog.contains(event.target)) {
59
- if (this.#outside_click_bound) {
60
- this.#outside_click_bound = false;
63
+ if (this.#outsideClickBound) {
64
+ this.#outsideClickBound = false;
61
65
  window.removeEventListener("click", this.#closeOutsideEvent);
62
66
  }
63
67
  this.close();
64
68
  }
65
69
  };
66
70
 
71
+ /**
72
+ * Уход доигран: диалог возвращается туда, откуда открывался, а занавес убирается из
73
+ * документа — закрытому слою в body делать нечего (карточку, из которой его открыли,
74
+ * могли уже снести), при следующем открытии занавес встанет заново.
75
+ * Слушатель висит постоянно: вход (`fade-in`) и чужие кадры внутри диалога пропускаем.
76
+ */
77
+ #returnHome = (event) => {
78
+ if (event.target !== this.#dialog || event.animationName !== "fade-out" || this.isOpen) return;
79
+ this.#scrim.remove();
80
+ const origin = this.#origin;
81
+ if (origin === undefined || origin === null || origin === document.body) return;
82
+ if (origin.isConnected === false) return;
83
+ origin.appendChild(this.#dialog);
84
+ };
85
+
67
86
  #resizeEvent = () => {
68
87
  if (!document.contains(this.#dialog)) return;
69
88
  this.#setCenter(this.#dialog, window);
@@ -71,28 +90,28 @@ class Dialog {
71
90
 
72
91
  addToHeader(...components) {
73
92
  this.#dialog.insertBefore(this.#header, this.#body);
74
- for (let component of components) this.#header.appendChild(component.set());
93
+ for (const component of components) this.#header.appendChild(component.set());
75
94
  }
76
95
 
77
96
  addToBody(...components) {
78
- for (let component of components) this.#body.appendChild(component.set());
97
+ for (const component of components) this.#body.appendChild(component.set());
79
98
  }
80
99
 
81
100
  addToFooter(...components) {
82
101
  this.#dialog.appendChild(this.#footer);
83
- for (let component of components) this.#footer.appendChild(component.set());
102
+ for (const component of components) this.#footer.appendChild(component.set());
84
103
  }
85
104
 
86
105
  removeFromHeader(...components) {
87
- for (let component of components) new Animation(component.set()).fadeOutAndRemove();
106
+ for (const component of components) new Animation(component.set()).fadeOutAndRemove();
88
107
  }
89
108
 
90
109
  removeFromBody(...components) {
91
- for (let component of components) new Animation(component.set()).fadeOutAndRemove();
110
+ for (const component of components) new Animation(component.set()).fadeOutAndRemove();
92
111
  }
93
112
 
94
113
  removeFromFooter(...components) {
95
- for (let component of components) new Animation(component.set()).fadeOutAndRemove();
114
+ for (const component of components) new Animation(component.set()).fadeOutAndRemove();
96
115
  }
97
116
 
98
117
  open() {
@@ -108,6 +127,11 @@ class Dialog {
108
127
  // }
109
128
 
110
129
  window.addEventListener("resize", this.#resizeEvent);
130
+ // Слой встаёт в body — рядом с занавесом и шапкой окна: у обоих `backdrop-filter` и
131
+ // `z-index`, а внутри карточки Chromium собирает такой слой отдельной поверхностью и
132
+ // рисует её под слоями из body — занавес закрывал диалог целиком
133
+ this.#origin = this.#dialog.parentElement;
134
+ if (this.#dialog.parentElement !== document.body) document.body.appendChild(this.#dialog);
111
135
  // Занавес нужен только открытому слою
112
136
  if (document.body.contains(this.#scrim) === false) document.body.appendChild(this.#scrim);
113
137
  new Animation(this.#dialog).fadeIn();
@@ -132,8 +156,8 @@ class Dialog {
132
156
  }
133
157
 
134
158
  close() {
135
- if (this.#outside_click_bound) {
136
- this.#outside_click_bound = false;
159
+ if (this.#outsideClickBound) {
160
+ this.#outsideClickBound = false;
137
161
  window.removeEventListener("click", this.#closeOutsideEvent);
138
162
  }
139
163
  window.removeEventListener("resize", this.#resizeEvent);
@@ -33,14 +33,14 @@ class Notification {
33
33
  #id = require("randomstring").generate();
34
34
  #time = 5000;
35
35
  #notification = document.createElement(`notification`);
36
- #notification_icon = document.createElement("notification_icon");
37
- #notification_content = document.createElement("notification_content");
38
- #notification_header = document.createElement("notification_header");
39
- #notification_title = document.createElement("notification_title");
40
- #notification_date = document.createElement("notification_date");
41
- #notification_text = document.createElement("notification_text");
42
- #notification_progress = document.createElement("notification_progress");
43
- #notification_close = document.createElement("button");
36
+ #notificationIcon = document.createElement("notification_icon");
37
+ #notificationContent = document.createElement("notification_content");
38
+ #notificationHeader = document.createElement("notification_header");
39
+ #notificationTitle = document.createElement("notification_title");
40
+ #notificationDate = document.createElement("notification_date");
41
+ #notificationText = document.createElement("notification_text");
42
+ #notificationProgress = document.createElement("notification_progress");
43
+ #notificationClose = document.createElement("button");
44
44
  // Отсчёт автоскрытия: таймер, дедлайн и остаток — чтобы пауза не теряла время
45
45
  #timer = undefined;
46
46
  #deadline = 0;
@@ -60,29 +60,29 @@ class Notification {
60
60
  setStyles(__dirname + "/styles.css", "chUiJS_Notification");
61
61
  // Время показа — только настоящее число: заготовка `Number()` из описания опций
62
62
  // раньше доезжала как значение и карточка без опций исчезала мгновенно
63
- const show_time = Number(options.showTime);
64
- if (Number.isFinite(show_time)) this.#time = show_time;
63
+ const showTime = Number(options.showTime);
64
+ if (Number.isFinite(showTime)) this.#time = showTime;
65
65
  this.#notification.id = this.#id;
66
66
  // Пустая заготовка опции равносильна отсутствующей: иначе обе опции читались как заданные
67
67
  const title = optionOr(options.title, undefined);
68
- const markdown_title = optionOr(options.markdownTitle, undefined);
68
+ const markdownTitle = optionOr(options.markdownTitle, undefined);
69
69
  const text = optionOr(options.text, undefined);
70
- const markdown_text = optionOr(options.markdownText, undefined);
70
+ const markdownText = optionOr(options.markdownText, undefined);
71
71
  // Стили заголовка уведомления
72
- if (title !== undefined && markdown_title !== undefined) {
72
+ if (title !== undefined && markdownTitle !== undefined) {
73
73
  throw new Error("Должна быть установлена одна опция title или markdownTitle");
74
74
  } else {
75
- if (title !== undefined) this.#notification_title.innerText = title;
76
- if (markdown_title !== undefined) this.#notification_title.innerHTML = markdownToHtml(markdown_title);
75
+ if (title !== undefined) this.#notificationTitle.innerText = title;
76
+ if (markdownTitle !== undefined) this.#notificationTitle.innerHTML = markdownToHtml(markdownTitle);
77
77
  }
78
78
  // Стили текста уведомления
79
- if (text !== undefined && markdown_text !== undefined) {
79
+ if (text !== undefined && markdownText !== undefined) {
80
80
  throw new Error("Должна быть установлена одна опция text или markdownText");
81
81
  } else {
82
- if (text !== undefined) this.#notification_text.innerText = text;
83
- if (markdown_text !== undefined) this.#notification_text.innerHTML = markdownToHtml(markdown_text);
82
+ if (text !== undefined) this.#notificationText.innerText = text;
83
+ if (markdownText !== undefined) this.#notificationText.innerHTML = markdownToHtml(markdownText);
84
84
  }
85
- this.#notification_date.innerHTML = getTime();
85
+ this.#notificationDate.innerHTML = getTime();
86
86
  // Ключ дня для группировки истории в ящике: подпись группы («Сегодня», «Вчера»,
87
87
  // дата) считает ящик, карточке достаточно самого ключа. Записи приходят
88
88
  // непрочитанными — ящик снимает отметку, когда его открыли.
@@ -96,32 +96,32 @@ class Notification {
96
96
  this.#notification.setAttribute("role", style === Notification.STYLE.ERROR ? "alert" : "status");
97
97
  // Иконка состояния — украшение: стиль читается и по тексту уведомления.
98
98
  // Цвет и глиф плашки выбирает общий помощник семейства — по ключу стиля
99
- this.#notification_icon.setAttribute("aria-hidden", "true");
99
+ this.#notificationIcon.setAttribute("aria-hidden", "true");
100
100
  setStatusPlate(
101
- this.#notification_icon,
101
+ this.#notificationIcon,
102
102
  typeof style === "string" ? style.replace("notification_", "") : undefined
103
103
  );
104
104
  // Закрытие — настоящая кнопка: доступна мышью, с клавиатуры и скринридеру
105
- this.#notification_close.className = "notification_close";
106
- this.#notification_close.type = "button";
107
- this.#notification_close.setAttribute("aria-label", I18n.t("a11y.closeNotification"));
108
- this.#notification_close.setAttribute("title", I18n.t("common.close"));
109
- this.#notification_close.innerHTML = new Icon(Icons.NAVIGATION.CLOSE, "16px").getHTML();
105
+ this.#notificationClose.className = "notification_close";
106
+ this.#notificationClose.type = "button";
107
+ this.#notificationClose.setAttribute("aria-label", I18n.t("a11y.closeNotification"));
108
+ this.#notificationClose.setAttribute("title", I18n.t("common.close"));
109
+ this.#notificationClose.innerHTML = new Icon(Icons.NAVIGATION.CLOSE, "16px").getHTML();
110
110
  //
111
- this.#notification_header.appendChild(this.#notification_title);
112
- this.#notification_header.appendChild(this.#notification_date);
111
+ this.#notificationHeader.appendChild(this.#notificationTitle);
112
+ this.#notificationHeader.appendChild(this.#notificationDate);
113
113
  //
114
- this.#notification_content.appendChild(this.#notification_header);
115
- this.#notification_content.appendChild(this.#notification_text);
116
- this.#notification.appendChild(this.#notification_icon);
117
- this.#notification.appendChild(this.#notification_content);
118
- this.#notification.appendChild(this.#notification_close);
114
+ this.#notificationContent.appendChild(this.#notificationHeader);
115
+ this.#notificationContent.appendChild(this.#notificationText);
116
+ this.#notification.appendChild(this.#notificationIcon);
117
+ this.#notification.appendChild(this.#notificationContent);
118
+ this.#notification.appendChild(this.#notificationClose);
119
119
  // Полоса отсчёта нужна только тем уведомлениям, которые закрываются сами
120
120
  if (this.#time > 0) {
121
- this.#notification_progress.style.animationDuration = `${this.#time}ms`;
121
+ this.#notificationProgress.style.animationDuration = `${this.#time}ms`;
122
122
  // Отсчёт стартует вместе с таймером, а не с появлением узла в DOM
123
- this.#notification_progress.style.animationPlayState = "paused";
124
- this.#notification.appendChild(this.#notification_progress);
123
+ this.#notificationProgress.style.animationPlayState = "paused";
124
+ this.#notification.appendChild(this.#notificationProgress);
125
125
  }
126
126
  // Курсор или фокус внутри карточки останавливают отсчёт
127
127
  this.#notification.addEventListener("mouseenter", () => this.pause());
@@ -147,8 +147,8 @@ class Notification {
147
147
  if (showOnSystem)
148
148
  require("electron").ipcRenderer.send(
149
149
  "show_system_notification",
150
- this.#notification_title.innerText,
151
- this.#notification_text.innerText
150
+ this.#notificationTitle.innerText,
151
+ this.#notificationText.innerText
152
152
  );
153
153
  return this;
154
154
  }
@@ -160,7 +160,7 @@ class Notification {
160
160
  clearTimeout(this.#timer);
161
161
  this.#timer = undefined;
162
162
  this.#remaining = Math.max(0, this.#deadline - Date.now());
163
- this.#notification_progress.style.animationPlayState = "paused";
163
+ this.#notificationProgress.style.animationPlayState = "paused";
164
164
  return this;
165
165
  }
166
166
 
@@ -182,7 +182,7 @@ class Notification {
182
182
  this.#dismissed = true;
183
183
  this.#clearTimer();
184
184
  // Полоса отсчёта больше не нужна: её animationend мог бы прилететь во время ухода
185
- this.#notification_progress.remove();
185
+ this.#notificationProgress.remove();
186
186
  this.#removeNotification(this.#notification);
187
187
  return this;
188
188
  }
@@ -192,7 +192,7 @@ class Notification {
192
192
  this.#remaining = duration;
193
193
  this.#deadline = Date.now() + duration;
194
194
  this.#timer = setTimeout(() => this.dismiss(), duration);
195
- this.#notification_progress.style.animationPlayState = "running";
195
+ this.#notificationProgress.style.animationPlayState = "running";
196
196
  return this;
197
197
  }
198
198
 
@@ -204,29 +204,51 @@ class Notification {
204
204
 
205
205
  #removeNotification(notification) {
206
206
  // Анимация ухода требует уже заданного animationName — у невидимого уведомления его нет
207
- if (notification.style.animationName === "") notification.style.animationName = "slide-right-in";
207
+ if (notification.style.animationName === "") {
208
+ notification.style.animationName = "slide-right-in";
209
+ notification.dataset.motion = "enter";
210
+ }
208
211
  new Animation(notification).slideRightOutAndRemove();
209
- notification.addEventListener("animationend", (event) => {
212
+ // Въезд в ящик — событие одноразовое: сняв себя после своего кадра, обработчик больше
213
+ // не трогает карточку. Без снятия его ловил любой конец кадра — включая кадр уборки:
214
+ // запись переселялась в начало списка и кадр начинался заново
215
+ const land = (event) => {
210
216
  if (event.target !== notification) return;
211
- // Точечный сброс следов анимации: `removeAttribute("style")` снёс бы и чужие
212
- // инлайновые значения (в карточке их несколько — отсчёт, отклик на наведение)
217
+ notification.removeEventListener("animationend", land);
218
+ // Следы кадра ухода снимаем точечно: `removeAttribute("style")` снёс бы и чужие
219
+ // инлайновые значения (в карточке их несколько — отсчёт, отклик на наведение).
220
+ // Вид ухода держит конечное состояние (`fill-mode: both`), а инлайновое имя кадра
221
+ // перебило бы любой следующий: без этого въезд записи не проигрался бы
213
222
  notification.style.removeProperty("animation-name");
223
+ notification.removeAttribute("data-motion");
214
224
  notification.style.removeProperty("transition");
215
- notification.style.display = "flex";
216
- notification.style.width = "-webkit-fill-available";
217
- notification.style.opacity = "0";
218
- notification.style.transform = "translateX(100%)";
219
225
  // В ящике карточка — запись истории: полоса отсчёта и закрытие там лишние
220
226
  notification.classList.add("notification_in_box");
221
227
  const box = requireLayoutNode(LAYOUT_ROLE.NOTIFICATION_BOX_MAIN, notification);
222
228
  // Новые записи идут сверху: история читается от свежего к старому, и группы дней
223
229
  // (их расставляет NotificationBox) ложатся в том же порядке — «Сегодня» первой.
224
230
  box.prepend(notification);
225
- setTimeout(() => {
226
- notification.style.opacity = "1";
227
- notification.style.transform = "translateX(0)";
228
- }, 500);
229
- });
231
+ /*
232
+ * Запись встаёт на место тем же кадром, каким пришёл тост: она только что уехала вправо,
233
+ * поэтому и возвращается оттуда. Прежде карточка ставилась инлайновыми `opacity`
234
+ * и `transform` и ждала 500 мс — и слайд не проигрывался вовсе: вид `exit` держит
235
+ * `transform` вне переходов (`[data-motion]`), поэтому запись просто вспыхивала
236
+ * на месте. Кадр ведёт драйвер: время и кривую берёт вид `enter` из токенов темы.
237
+ */
238
+ new Animation(notification).slideRightIn();
239
+ // Кадр въезда доигран — запись возвращается к стилям: инлайнового имени кадра
240
+ // и вида у неё не остаётся, иначе они спорили бы со следующим кадром (уборкой)
241
+ notification.addEventListener(
242
+ "animationend",
243
+ (event) => {
244
+ if (event.target !== notification) return;
245
+ notification.style.removeProperty("animation-name");
246
+ notification.removeAttribute("data-motion");
247
+ },
248
+ { once: true }
249
+ );
250
+ };
251
+ notification.addEventListener("animationend", land);
230
252
  }
231
253
 
232
254
  static STYLE = { ERROR: "notification_error", SUCCESS: "notification_success", WARNING: "notification_warning" };