chuijs 4.0.1 → 4.0.3

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 (222) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +43 -632
  3. package/framework/appLayout/default/chui_app_layout.js +314 -273
  4. package/framework/appLayout/default/global_style.css +93 -53
  5. package/framework/components/chui_accordion/accordion.js +29 -26
  6. package/framework/components/chui_accordion/styles.css +6 -5
  7. package/framework/components/chui_action_sheet/action_sheet.js +59 -33
  8. package/framework/components/chui_action_sheet/styles.css +2 -1
  9. package/framework/components/chui_affix/affix.js +11 -6
  10. package/framework/components/chui_alert/alert.js +23 -14
  11. package/framework/components/chui_avatar/avatar.js +23 -22
  12. package/framework/components/chui_avatar_group/avatar_group.js +21 -19
  13. package/framework/components/chui_backtop/backtop.js +35 -19
  14. package/framework/components/chui_badge/badge.js +15 -13
  15. package/framework/components/chui_breadcrumb/breadcrumb.js +15 -13
  16. package/framework/components/chui_button/button.js +42 -22
  17. package/framework/components/chui_button/styles.css +2 -2
  18. package/framework/components/chui_calendar/calendar.js +12 -10
  19. package/framework/components/chui_card/card.js +43 -23
  20. package/framework/components/chui_chat/chat.js +72 -33
  21. package/framework/components/chui_clipboard_history/clipboard_history.js +30 -21
  22. package/framework/components/chui_clipboard_history/styles.css +4 -2
  23. package/framework/components/chui_code/code.js +2 -2
  24. package/framework/components/chui_code/styles.css +2 -2
  25. package/framework/components/chui_code_editor/code_editor.js +47 -31
  26. package/framework/components/chui_code_editor/styles.css +3 -2
  27. package/framework/components/chui_command_palette/command_palette.js +60 -37
  28. package/framework/components/chui_console/console.js +5 -5
  29. package/framework/components/chui_console/styles.css +1 -1
  30. package/framework/components/chui_content_block/content_block.js +33 -19
  31. package/framework/components/chui_content_block/styles.css +2 -2
  32. package/framework/components/chui_context_menu/context_menu.js +9 -4
  33. package/framework/components/chui_custom_element/custom_element.js +22 -15
  34. package/framework/components/chui_data_grid/data_grid.js +396 -234
  35. package/framework/components/chui_data_grid/styles.css +10 -8
  36. package/framework/components/chui_descriptions/descriptions.js +34 -17
  37. package/framework/components/chui_details/details.js +32 -26
  38. package/framework/components/chui_details/styles.css +5 -4
  39. package/framework/components/chui_diff_viewer/diff_viewer.js +91 -66
  40. package/framework/components/chui_diff_viewer/styles.css +1 -1
  41. package/framework/components/chui_divider/divider.js +13 -11
  42. package/framework/components/chui_drawer/drawer.js +27 -25
  43. package/framework/components/chui_drop_zone/drop_zone.js +31 -21
  44. package/framework/components/chui_empty_state/empty_state.js +20 -15
  45. package/framework/components/chui_fieldset/fieldset.js +37 -20
  46. package/framework/components/chui_fieldset/styles.css +1 -1
  47. package/framework/components/chui_file_explorer/file_explorer.js +157 -86
  48. package/framework/components/chui_file_explorer/styles.css +2 -1
  49. package/framework/components/chui_form/form.js +31 -19
  50. package/framework/components/chui_form/styles.css +1 -1
  51. package/framework/components/chui_gantt/gantt.js +64 -40
  52. package/framework/components/chui_graphs/graphs.js +301 -200
  53. package/framework/components/chui_graphs/styles.css +1 -1
  54. package/framework/components/chui_hero/hero.js +40 -22
  55. package/framework/components/chui_html_block/html_block.js +6 -6
  56. package/framework/components/chui_html_block/styles.css +2 -2
  57. package/framework/components/chui_hx/hx.js +3 -3
  58. package/framework/components/chui_hx/styles.css +11 -4
  59. package/framework/components/chui_icons/icons.js +2109 -2109
  60. package/framework/components/chui_icons/styles.css +2 -2
  61. package/framework/components/chui_inputs/chui_check_box/check_box.js +31 -19
  62. package/framework/components/chui_inputs/chui_check_box/styles.css +18 -18
  63. package/framework/components/chui_inputs/chui_chips/chips.js +50 -40
  64. package/framework/components/chui_inputs/chui_chips/styles.css +4 -2
  65. package/framework/components/chui_inputs/chui_color_picker/color_picker.js +36 -24
  66. package/framework/components/chui_inputs/chui_combo_box/combo_box.js +100 -82
  67. package/framework/components/chui_inputs/chui_combo_box/styles.css +14 -12
  68. package/framework/components/chui_inputs/chui_date/date.js +102 -66
  69. package/framework/components/chui_inputs/chui_date/styles.css +12 -11
  70. package/framework/components/chui_inputs/chui_date_range/date_range.js +40 -29
  71. package/framework/components/chui_inputs/chui_datetime/datetime.js +35 -27
  72. package/framework/components/chui_inputs/chui_email/email.js +43 -31
  73. package/framework/components/chui_inputs/chui_email/styles.css +5 -5
  74. package/framework/components/chui_inputs/chui_file/file.js +68 -41
  75. package/framework/components/chui_inputs/chui_file/styles.css +13 -12
  76. package/framework/components/chui_inputs/chui_hotkey/hotkey.js +61 -36
  77. package/framework/components/chui_inputs/chui_masked/masked.js +39 -28
  78. package/framework/components/chui_inputs/chui_mention/mention.js +77 -30
  79. package/framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box.js +173 -153
  80. package/framework/components/chui_inputs/chui_multi_combo_box/styles.css +9 -7
  81. package/framework/components/chui_inputs/chui_number/number.js +58 -41
  82. package/framework/components/chui_inputs/chui_number/styles.css +7 -7
  83. package/framework/components/chui_inputs/chui_otp/otp.js +38 -29
  84. package/framework/components/chui_inputs/chui_password/password.js +58 -36
  85. package/framework/components/chui_inputs/chui_password/styles.css +8 -8
  86. package/framework/components/chui_inputs/chui_radio_button/radio_button.js +37 -25
  87. package/framework/components/chui_inputs/chui_radio_button/styles.css +20 -20
  88. package/framework/components/chui_inputs/chui_rating/rating.js +26 -22
  89. package/framework/components/chui_inputs/chui_select_box/select_box.js +102 -78
  90. package/framework/components/chui_inputs/chui_select_box/styles.css +11 -11
  91. package/framework/components/chui_inputs/chui_slider/slider.js +29 -26
  92. package/framework/components/chui_inputs/chui_slider/styles.css +2 -1
  93. package/framework/components/chui_inputs/chui_text/styles.css +8 -8
  94. package/framework/components/chui_inputs/chui_text/text.js +65 -44
  95. package/framework/components/chui_inputs/chui_text_area/styles.css +6 -6
  96. package/framework/components/chui_inputs/chui_text_area/text_area.js +51 -28
  97. package/framework/components/chui_inputs/chui_time/styles.css +5 -3
  98. package/framework/components/chui_inputs/chui_time/time.js +38 -27
  99. package/framework/components/chui_inputs/chui_toggle/styles.css +3 -3
  100. package/framework/components/chui_inputs/chui_toggle/toggle.js +16 -16
  101. package/framework/components/chui_json_editor/json_editor.js +53 -43
  102. package/framework/components/chui_json_viewer/json_viewer.js +52 -33
  103. package/framework/components/chui_json_viewer/styles.css +3 -2
  104. package/framework/components/chui_kanban/kanban.js +52 -34
  105. package/framework/components/chui_kbd/kbd.js +12 -10
  106. package/framework/components/chui_label/label.js +14 -12
  107. package/framework/components/chui_label/styles.css +2 -2
  108. package/framework/components/chui_link/link.js +27 -20
  109. package/framework/components/chui_list/list.js +31 -27
  110. package/framework/components/chui_list/styles.css +2 -1
  111. package/framework/components/chui_markdown_editor/markdown_editor.js +60 -44
  112. package/framework/components/chui_media/audio.js +326 -227
  113. package/framework/components/chui_media/audio_styles.css +40 -21
  114. package/framework/components/chui_media/image.js +70 -38
  115. package/framework/components/chui_media/image_compare.js +52 -21
  116. package/framework/components/chui_media/image_styles.css +3 -3
  117. package/framework/components/chui_media/video.js +155 -62
  118. package/framework/components/chui_media/video_styles.css +33 -16
  119. package/framework/components/chui_menu_bar/menu_bar.js +26 -24
  120. package/framework/components/chui_menu_bar/styles.css +4 -2
  121. package/framework/components/chui_modal/modal.js +44 -43
  122. package/framework/components/chui_modal/styles.css +3 -2
  123. package/framework/components/chui_notification/notification.js +50 -34
  124. package/framework/components/chui_notification/notification_download.js +31 -25
  125. package/framework/components/chui_notification/notification_download_progress.css +3 -2
  126. package/framework/components/chui_notification/notification_download_progress.js +47 -41
  127. package/framework/components/chui_notification/notification_update.js +23 -16
  128. package/framework/components/chui_notification/status_plate.js +2 -2
  129. package/framework/components/chui_notification/styles_download.css +1 -1
  130. package/framework/components/chui_notification/styles_update.css +1 -1
  131. package/framework/components/chui_org_chart/org_chart.js +74 -35
  132. package/framework/components/chui_pagination/pagination.js +62 -27
  133. package/framework/components/chui_paragraph/paragraph.js +2 -2
  134. package/framework/components/chui_paragraph/styles.css +1 -1
  135. package/framework/components/chui_popover/popover.js +49 -28
  136. package/framework/components/chui_popups/popups.js +137 -108
  137. package/framework/components/chui_popups/styles.css +2 -2
  138. package/framework/components/chui_print_button/print_button.js +33 -23
  139. package/framework/components/chui_progress_bar/progress_bar.js +23 -15
  140. package/framework/components/chui_progress_bar/styles.css +2 -2
  141. package/framework/components/chui_progress_ring/progress_ring.js +17 -15
  142. package/framework/components/chui_qrcode/qrcode.js +75 -31
  143. package/framework/components/chui_query_builder/query_builder.js +216 -123
  144. package/framework/components/chui_radio_group/radio_group.js +36 -26
  145. package/framework/components/chui_radio_group/styles.css +1 -1
  146. package/framework/components/chui_result/result.js +26 -16
  147. package/framework/components/chui_scheduler/scheduler.js +57 -35
  148. package/framework/components/chui_scheduler/styles.css +6 -2
  149. package/framework/components/chui_segmented_control/segmented_control.js +27 -21
  150. package/framework/components/chui_settings/settings.js +122 -48
  151. package/framework/components/chui_shortcut_help/shortcut_help.js +55 -32
  152. package/framework/components/chui_sidebar_nav/sidebar_nav.js +33 -23
  153. package/framework/components/chui_skeleton/skeleton.js +29 -24
  154. package/framework/components/chui_slideshow/slideshow.js +60 -41
  155. package/framework/components/chui_slideshow/styles.css +18 -12
  156. package/framework/components/chui_spinner/spinner.js +8 -8
  157. package/framework/components/chui_spinner/styles.css +0 -1
  158. package/framework/components/chui_split_button/split_button.js +23 -18
  159. package/framework/components/chui_split_button/styles.css +1 -1
  160. package/framework/components/chui_splitter/splitter.js +35 -28
  161. package/framework/components/chui_statistic/statistic.js +65 -38
  162. package/framework/components/chui_status_bar/status_bar.js +11 -9
  163. package/framework/components/chui_stepper/stepper.js +27 -22
  164. package/framework/components/chui_sticky_bar/sticky_bar.js +9 -4
  165. package/framework/components/chui_sticky_bar/styles.css +3 -1
  166. package/framework/components/chui_tabs/chui_tabs.js +28 -22
  167. package/framework/components/chui_tabs/styles.css +3 -3
  168. package/framework/components/chui_text_editor/styles.css +20 -19
  169. package/framework/components/chui_text_editor/text_editor.js +85 -61
  170. package/framework/components/chui_text_editor/text_editor_panel.js +346 -284
  171. package/framework/components/chui_theme_toggle/theme_toggle.js +27 -22
  172. package/framework/components/chui_timeline/timeline.js +14 -12
  173. package/framework/components/chui_toast_queue/toast_queue.js +52 -46
  174. package/framework/components/chui_toolbar/toolbar.js +20 -13
  175. package/framework/components/chui_tooltip/tooltip.js +37 -24
  176. package/framework/components/chui_tour/styles.css +8 -3
  177. package/framework/components/chui_tour/tour.js +61 -27
  178. package/framework/components/chui_transfer/styles.css +2 -1
  179. package/framework/components/chui_transfer/transfer.js +75 -42
  180. package/framework/components/chui_tree_table/tree_table.js +43 -29
  181. package/framework/components/chui_tree_view/styles.css +4 -2
  182. package/framework/components/chui_tree_view/tree_view.js +142 -99
  183. package/framework/components/chui_watermark/watermark.js +24 -16
  184. package/framework/components/chui_webview/styles.css +2 -2
  185. package/framework/components/chui_webview/webview.js +62 -50
  186. package/framework/components/telegram_bot/chui_telegram_bot.js +71 -64
  187. package/framework/modules/chui_a11y/chui_a11y.js +77 -46
  188. package/framework/modules/chui_animations/animations.js +42 -42
  189. package/framework/modules/chui_animations/styles.css +1 -1
  190. package/framework/modules/chui_bridge/preload.js +56 -26
  191. package/framework/modules/chui_functions.js +52 -50
  192. package/framework/modules/chui_highlight/chui_highlight.js +25 -22
  193. package/framework/modules/chui_i18n/chui_i18n.js +9 -7
  194. package/framework/modules/chui_i18n/locales/a11y.js +5 -5
  195. package/framework/modules/chui_i18n/locales/app_layout.js +5 -5
  196. package/framework/modules/chui_i18n/locales/calendar.js +3 -3
  197. package/framework/modules/chui_i18n/locales/common.js +3 -3
  198. package/framework/modules/chui_i18n/locales/data.js +36 -26
  199. package/framework/modules/chui_i18n/locales/editors.js +40 -13
  200. package/framework/modules/chui_i18n/locales/feedback.js +3 -3
  201. package/framework/modules/chui_i18n/locales/forms.js +8 -8
  202. package/framework/modules/chui_i18n/locales/graphics.js +8 -8
  203. package/framework/modules/chui_i18n/locales/media.js +4 -4
  204. package/framework/modules/chui_i18n/locales/navigation.js +8 -8
  205. package/framework/modules/chui_i18n/locales/validate.js +3 -3
  206. package/framework/modules/chui_json/chui_json.js +9 -9
  207. package/framework/modules/chui_layout/chui_layout.js +12 -10
  208. package/framework/modules/chui_logger/chui_logger.js +29 -25
  209. package/framework/modules/chui_page/page.js +33 -27
  210. package/framework/modules/chui_page/styles.css +3 -26
  211. package/framework/modules/chui_qr/chui_qr.js +158 -67
  212. package/framework/modules/chui_scrollbar/chui_scrollbar.js +35 -24
  213. package/framework/modules/chui_scrollbar/styles.css +3 -1
  214. package/framework/modules/chui_services/chui_channels.js +76 -11
  215. package/framework/modules/chui_services/chui_services.js +39 -31
  216. package/framework/modules/chui_services/desktop.js +149 -30
  217. package/framework/modules/chui_sortable/chui_sortable.js +17 -12
  218. package/framework/modules/chui_validate/chui_validate.js +7 -4
  219. package/framework/tray_bar/chui_menu_items.js +136 -46
  220. package/index.d.ts +4814 -0
  221. package/index.js +449 -334
  222. package/package.json +79 -68
package/index.js CHANGED
@@ -1,153 +1,194 @@
1
1
  // === ИНСТРУМЕНТЫ ===
2
- const {app, BrowserWindow, Menu, Tray, ipcMain, ipcRenderer, shell, nativeTheme, session, webContents, systemPreferences, dialog} = require('electron');
2
+ const {
3
+ app,
4
+ BrowserWindow,
5
+ Menu,
6
+ Tray,
7
+ ipcMain,
8
+ ipcRenderer,
9
+ shell,
10
+ nativeTheme,
11
+ session,
12
+ webContents,
13
+ systemPreferences,
14
+ dialog,
15
+ } = require("electron");
3
16
  // Одно хранилище настроек на модуль: его получает приложение через `require('chuijs').store`,
4
17
  // из него читают тему, язык и история буфера, а `Settings` берёт его для своих значений по умолчанию
5
- const {store} = require("./framework/modules/chui_store/chui_store");
18
+ const { store } = require("./framework/modules/chui_store/chui_store");
6
19
  const path = require("path");
7
20
  const fs = require("fs");
8
21
  const os = require("os");
9
22
 
10
- const request = require('@cypress/request');
11
- const {transliterate} = require("transliteration");
23
+ const request = require("@cypress/request");
24
+ const { transliterate } = require("transliteration");
12
25
  let downloadSession = undefined;
13
26
  // === === ===
14
27
  // process.env
15
- process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = "true";
28
+ process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
16
29
  // === === ===
17
30
 
18
31
  //ПОЛЯ ВВОДА
19
- const {TextInput} = require('./framework/components/chui_inputs/chui_text/text');
20
- const {DateInput} = require('./framework/components/chui_inputs/chui_date/date');
21
- const {NumberInput} = require('./framework/components/chui_inputs/chui_number/number');
22
- const {EmailInput} = require('./framework/components/chui_inputs/chui_email/email');
23
- const {PasswordInput} = require('./framework/components/chui_inputs/chui_password/password');
24
- const {Select} = require('./framework/components/chui_inputs/chui_select_box/select_box');
25
- const {ComboBox} = require('./framework/components/chui_inputs/chui_combo_box/combo_box');
26
- const {TextArea} = require('./framework/components/chui_inputs/chui_text_area/text_area');
27
- const {RadioButton} = require('./framework/components/chui_inputs/chui_radio_button/radio_button');
28
- const {CheckBox} = require('./framework/components/chui_inputs/chui_check_box/check_box');
29
- const {RadioGroup} = require('./framework/components/chui_radio_group/radio_group');
30
- const {Slider} = require('./framework/components/chui_inputs/chui_slider/slider');
31
- const {Chips} = require('./framework/components/chui_inputs/chui_chips/chips');
32
+ const { TextInput } = require("./framework/components/chui_inputs/chui_text/text");
33
+ const { DateInput } = require("./framework/components/chui_inputs/chui_date/date");
34
+ const { NumberInput } = require("./framework/components/chui_inputs/chui_number/number");
35
+ const { EmailInput } = require("./framework/components/chui_inputs/chui_email/email");
36
+ const { PasswordInput } = require("./framework/components/chui_inputs/chui_password/password");
37
+ const { Select } = require("./framework/components/chui_inputs/chui_select_box/select_box");
38
+ const { ComboBox } = require("./framework/components/chui_inputs/chui_combo_box/combo_box");
39
+ const { TextArea } = require("./framework/components/chui_inputs/chui_text_area/text_area");
40
+ const { RadioButton } = require("./framework/components/chui_inputs/chui_radio_button/radio_button");
41
+ const { CheckBox } = require("./framework/components/chui_inputs/chui_check_box/check_box");
42
+ const { RadioGroup } = require("./framework/components/chui_radio_group/radio_group");
43
+ const { Slider } = require("./framework/components/chui_inputs/chui_slider/slider");
44
+ const { Chips } = require("./framework/components/chui_inputs/chui_chips/chips");
32
45
  //==========
33
- const {MenuItem} = require('./framework/tray_bar/chui_menu_items');
34
- const {AppLayout, Route} = require('./framework/appLayout/default/chui_app_layout');
35
- const {Page} = require('./framework/modules/chui_page/page');
36
- const {WebView} = require('./framework/components/chui_webview/webview');
37
- const {Button} = require('./framework/components/chui_button/button');
38
- const {Spinner} = require('./framework/components/chui_spinner/spinner');
39
- const {Icon, Icons} = require('./framework/components/chui_icons/icons');
40
- const {TextEditor} = require('./framework/components/chui_text_editor/text_editor');
41
- const {Badge} = require('./framework/components/chui_badge/badge');
42
- const {Divider} = require('./framework/components/chui_divider/divider');
43
- const {Link} = require('./framework/components/chui_link/link');
44
- const {Avatar} = require('./framework/components/chui_avatar/avatar');
45
- const {AvatarGroup} = require('./framework/components/chui_avatar_group/avatar_group');
46
- const {EmptyState} = require('./framework/components/chui_empty_state/empty_state');
47
- const {Result} = require('./framework/components/chui_result/result');
48
- const {Descriptions} = require('./framework/components/chui_descriptions/descriptions');
49
- const {Statistic} = require('./framework/components/chui_statistic/statistic');
50
- const {Backtop} = require('./framework/components/chui_backtop/backtop');
51
- const {Affix} = require('./framework/components/chui_affix/affix');
52
- const {StickyBar} = require('./framework/components/chui_sticky_bar/sticky_bar');
53
- const {Watermark} = require('./framework/components/chui_watermark/watermark');
54
- const {ShortcutHelp} = require('./framework/components/chui_shortcut_help/shortcut_help');
55
- const {Tooltip} = require('./framework/components/chui_tooltip/tooltip');
56
- const {Calendar} = require('./framework/components/chui_calendar/calendar');
57
- const {Notification} = require('./framework/components/chui_notification/notification');
58
- const {UpdateNotification} = require("./framework/components/chui_notification/notification_update");
59
- const {DownloadNotification} = require("./framework/components/chui_notification/notification_download");
60
- const {Tabs, Tab} = require('./framework/components/chui_tabs/chui_tabs');
61
- const {BarGraph, PieGraph, LineGraph, AreaGraph, ScatterGraph, Gauge, Sparkline, RadarGraph, HeatmapGraph} = require('./framework/components/chui_graphs/graphs');
62
- const {Toggle} = require('./framework/components/chui_inputs/chui_toggle/toggle');
63
- const {ContentBlock} = require('./framework/components/chui_content_block/content_block');
64
- const {Card} = require('./framework/components/chui_card/card');
65
- const {Hero} = require('./framework/components/chui_hero/hero');
66
- const {Skeleton} = require('./framework/components/chui_skeleton/skeleton');
67
- const {Drawer} = require('./framework/components/chui_drawer/drawer');
68
- const {ToastQueue, Toast} = require('./framework/components/chui_toast_queue/toast_queue');
69
- const {ProgressBar} = require('./framework/components/chui_progress_bar/progress_bar');
70
- const {Label} = require('./framework/components/chui_label/label');
71
- const {Dialog} = require('./framework/components/chui_modal/modal');
72
- const {Paragraph} = require('./framework/components/chui_paragraph/paragraph');
73
- const {H} = require('./framework/components/chui_hx/hx');
74
- const {DataGrid} = require('./framework/components/chui_data_grid/data_grid');
75
- const {Details} = require('./framework/components/chui_details/details');
76
- const {Accordion} = require('./framework/components/chui_accordion/accordion')
77
- const {CodeBlock} = require('./framework/components/chui_code/code')
78
- const {HtmlBlock} = require('./framework/components/chui_html_block/html_block')
79
- const {sleep, render, getDefaultIcon, formatBytes} = require('./framework/modules/chui_functions');
80
- const {Shortcuts, Clipboard, RecentDocuments, TaskbarProgress} = require('./framework/modules/chui_services/chui_services');
81
- const {SERVICES_CHANNELS, BRIDGE_CHANNELS, BRIDGE_ARGS, APP_PATH_NAMES} = require('./framework/modules/chui_services/chui_channels');
82
- const {Desktop} = require('./framework/modules/chui_services/desktop');
83
- const {I18n} = require('./framework/modules/chui_i18n/chui_i18n');
84
- const {Shell, Theme, Locale, ClipboardHistory: ClipboardHistoryService, Print} = require('./framework/modules/chui_services/chui_services');
85
- const {FileInput, AcceptTypes} = require("./framework/components/chui_inputs/chui_file/file");
86
- const {TreeView} = require("./framework/components/chui_tree_view/tree_view");
87
- const {ThemeToggle} = require("./framework/components/chui_theme_toggle/theme_toggle");
88
- const {SegmentedControl} = require("./framework/components/chui_segmented_control/segmented_control");
89
- const {Alert} = require("./framework/components/chui_alert/alert");
90
- const {Kbd} = require("./framework/components/chui_kbd/kbd");
91
- const {List, ListItem} = require("./framework/components/chui_list/list");
92
- const {Popover} = require("./framework/components/chui_popover/popover");
93
- const {Tour} = require("./framework/components/chui_tour/tour");
94
- const {SplitButton} = require("./framework/components/chui_split_button/split_button");
95
- const {Pagination} = require("./framework/components/chui_pagination/pagination");
96
- const {Breadcrumb} = require("./framework/components/chui_breadcrumb/breadcrumb");
97
- const {Stepper} = require("./framework/components/chui_stepper/stepper");
98
- const {Timeline} = require("./framework/components/chui_timeline/timeline");
99
- const {Rating} = require("./framework/components/chui_inputs/chui_rating/rating");
100
- const {ColorPicker} = require("./framework/components/chui_inputs/chui_color_picker/color_picker");
101
- const {TimeInput} = require("./framework/components/chui_inputs/chui_time/time");
102
- const {DateTimeInput} = require("./framework/components/chui_inputs/chui_datetime/datetime");
103
- const {DateRangeInput} = require("./framework/components/chui_inputs/chui_date_range/date_range");
104
- const {HotkeyInput} = require("./framework/components/chui_inputs/chui_hotkey/hotkey");
105
- const {MaskedInput} = require("./framework/components/chui_inputs/chui_masked/masked");
106
- const {OtpInput} = require("./framework/components/chui_inputs/chui_otp/otp");
107
- const {Transfer} = require("./framework/components/chui_transfer/transfer");
108
- const {TreeTable} = require("./framework/components/chui_tree_table/tree_table");
109
- const {SidebarNav} = require("./framework/components/chui_sidebar_nav/sidebar_nav");
110
- const {MarkdownEditor} = require("./framework/components/chui_markdown_editor/markdown_editor");
111
- const {DiffViewer} = require("./framework/components/chui_diff_viewer/diff_viewer");
112
- const {JsonViewer} = require("./framework/components/chui_json_viewer/json_viewer");
113
- const {JsonEditor} = require("./framework/components/chui_json_editor/json_editor");
114
- const {QueryBuilder} = require("./framework/components/chui_query_builder/query_builder");
115
- const {FileExplorer} = require("./framework/components/chui_file_explorer/file_explorer");
116
- const {Settings} = require("./framework/components/chui_settings/settings");
117
- const {DropZone} = require("./framework/components/chui_drop_zone/drop_zone");
118
- const {ClipboardHistory} = require("./framework/components/chui_clipboard_history/clipboard_history");
119
- const {PrintButton} = require("./framework/components/chui_print_button/print_button");
120
- const {Splitter} = require("./framework/components/chui_splitter/splitter");
121
- const {Scheduler} = require("./framework/components/chui_scheduler/scheduler");
122
- const {Gantt} = require("./framework/components/chui_gantt/gantt");
123
- const {Kanban} = require("./framework/components/chui_kanban/kanban");
124
- const {CodeEditor} = require("./framework/components/chui_code_editor/code_editor");
125
- const {ProgressRing} = require("./framework/components/chui_progress_ring/progress_ring");
126
- const {Toolbar} = require("./framework/components/chui_toolbar/toolbar");
127
- const {StatusBar} = require("./framework/components/chui_status_bar/status_bar");
128
- const {Sortable} = require("./framework/modules/chui_sortable/chui_sortable");
129
- const {Form} = require("./framework/components/chui_form/form");
130
- const {SlideShow} = require("./framework/components/chui_slideshow/slideshow");
131
- const {FieldSet} = require("./framework/components/chui_fieldset/fieldset");
132
- const {Popup} = require("./framework/components/chui_popups/popups");
133
- const {TelegramBot} = require("./framework/components/telegram_bot/chui_telegram_bot");
134
- const {MenuBar} = require("./framework/components/chui_menu_bar/menu_bar");
135
- const {CommandPalette} = require("./framework/components/chui_command_palette/command_palette");
46
+ const { MenuItem } = require("./framework/tray_bar/chui_menu_items");
47
+ const { AppLayout, Route } = require("./framework/appLayout/default/chui_app_layout");
48
+ const { Page } = require("./framework/modules/chui_page/page");
49
+ const { WebView } = require("./framework/components/chui_webview/webview");
50
+ const { Button } = require("./framework/components/chui_button/button");
51
+ const { Spinner } = require("./framework/components/chui_spinner/spinner");
52
+ const { Icon, Icons } = require("./framework/components/chui_icons/icons");
53
+ const { TextEditor } = require("./framework/components/chui_text_editor/text_editor");
54
+ const { Badge } = require("./framework/components/chui_badge/badge");
55
+ const { Divider } = require("./framework/components/chui_divider/divider");
56
+ const { Link } = require("./framework/components/chui_link/link");
57
+ const { Avatar } = require("./framework/components/chui_avatar/avatar");
58
+ const { AvatarGroup } = require("./framework/components/chui_avatar_group/avatar_group");
59
+ const { EmptyState } = require("./framework/components/chui_empty_state/empty_state");
60
+ const { Result } = require("./framework/components/chui_result/result");
61
+ const { Descriptions } = require("./framework/components/chui_descriptions/descriptions");
62
+ const { Statistic } = require("./framework/components/chui_statistic/statistic");
63
+ const { Backtop } = require("./framework/components/chui_backtop/backtop");
64
+ const { Affix } = require("./framework/components/chui_affix/affix");
65
+ const { StickyBar } = require("./framework/components/chui_sticky_bar/sticky_bar");
66
+ const { Watermark } = require("./framework/components/chui_watermark/watermark");
67
+ const { ShortcutHelp } = require("./framework/components/chui_shortcut_help/shortcut_help");
68
+ const { Tooltip } = require("./framework/components/chui_tooltip/tooltip");
69
+ const { Calendar } = require("./framework/components/chui_calendar/calendar");
70
+ const { Notification } = require("./framework/components/chui_notification/notification");
71
+ const { UpdateNotification } = require("./framework/components/chui_notification/notification_update");
72
+ const { DownloadNotification } = require("./framework/components/chui_notification/notification_download");
73
+ const { Tabs, Tab } = require("./framework/components/chui_tabs/chui_tabs");
74
+ const {
75
+ BarGraph,
76
+ PieGraph,
77
+ LineGraph,
78
+ AreaGraph,
79
+ ScatterGraph,
80
+ Gauge,
81
+ Sparkline,
82
+ RadarGraph,
83
+ HeatmapGraph,
84
+ } = require("./framework/components/chui_graphs/graphs");
85
+ const { Toggle } = require("./framework/components/chui_inputs/chui_toggle/toggle");
86
+ const { ContentBlock } = require("./framework/components/chui_content_block/content_block");
87
+ const { Card } = require("./framework/components/chui_card/card");
88
+ const { Hero } = require("./framework/components/chui_hero/hero");
89
+ const { Skeleton } = require("./framework/components/chui_skeleton/skeleton");
90
+ const { Drawer } = require("./framework/components/chui_drawer/drawer");
91
+ const { ToastQueue, Toast } = require("./framework/components/chui_toast_queue/toast_queue");
92
+ const { ProgressBar } = require("./framework/components/chui_progress_bar/progress_bar");
93
+ const { Label } = require("./framework/components/chui_label/label");
94
+ const { Dialog } = require("./framework/components/chui_modal/modal");
95
+ const { Paragraph } = require("./framework/components/chui_paragraph/paragraph");
96
+ const { H } = require("./framework/components/chui_hx/hx");
97
+ const { DataGrid } = require("./framework/components/chui_data_grid/data_grid");
98
+ const { Details } = require("./framework/components/chui_details/details");
99
+ const { Accordion } = require("./framework/components/chui_accordion/accordion");
100
+ const { CodeBlock } = require("./framework/components/chui_code/code");
101
+ const { HtmlBlock } = require("./framework/components/chui_html_block/html_block");
102
+ const { sleep, render, getDefaultIcon, formatBytes } = require("./framework/modules/chui_functions");
103
+ const {
104
+ Shortcuts,
105
+ Clipboard,
106
+ RecentDocuments,
107
+ TaskbarProgress,
108
+ } = require("./framework/modules/chui_services/chui_services");
109
+ const {
110
+ SERVICES_CHANNELS,
111
+ BRIDGE_CHANNELS,
112
+ BRIDGE_ARGS,
113
+ APP_PATH_NAMES,
114
+ } = require("./framework/modules/chui_services/chui_channels");
115
+ const { Desktop } = require("./framework/modules/chui_services/desktop");
116
+ const { I18n } = require("./framework/modules/chui_i18n/chui_i18n");
117
+ const {
118
+ Shell,
119
+ Theme,
120
+ Locale,
121
+ ClipboardHistory: ClipboardHistoryService,
122
+ Print,
123
+ } = require("./framework/modules/chui_services/chui_services");
124
+ const { FileInput, AcceptTypes } = require("./framework/components/chui_inputs/chui_file/file");
125
+ const { TreeView } = require("./framework/components/chui_tree_view/tree_view");
126
+ const { ThemeToggle } = require("./framework/components/chui_theme_toggle/theme_toggle");
127
+ const { SegmentedControl } = require("./framework/components/chui_segmented_control/segmented_control");
128
+ const { Alert } = require("./framework/components/chui_alert/alert");
129
+ const { Kbd } = require("./framework/components/chui_kbd/kbd");
130
+ const { List, ListItem } = require("./framework/components/chui_list/list");
131
+ const { Popover } = require("./framework/components/chui_popover/popover");
132
+ const { Tour } = require("./framework/components/chui_tour/tour");
133
+ const { SplitButton } = require("./framework/components/chui_split_button/split_button");
134
+ const { Pagination } = require("./framework/components/chui_pagination/pagination");
135
+ const { Breadcrumb } = require("./framework/components/chui_breadcrumb/breadcrumb");
136
+ const { Stepper } = require("./framework/components/chui_stepper/stepper");
137
+ const { Timeline } = require("./framework/components/chui_timeline/timeline");
138
+ const { Rating } = require("./framework/components/chui_inputs/chui_rating/rating");
139
+ const { ColorPicker } = require("./framework/components/chui_inputs/chui_color_picker/color_picker");
140
+ const { TimeInput } = require("./framework/components/chui_inputs/chui_time/time");
141
+ const { DateTimeInput } = require("./framework/components/chui_inputs/chui_datetime/datetime");
142
+ const { DateRangeInput } = require("./framework/components/chui_inputs/chui_date_range/date_range");
143
+ const { HotkeyInput } = require("./framework/components/chui_inputs/chui_hotkey/hotkey");
144
+ const { MaskedInput } = require("./framework/components/chui_inputs/chui_masked/masked");
145
+ const { OtpInput } = require("./framework/components/chui_inputs/chui_otp/otp");
146
+ const { Transfer } = require("./framework/components/chui_transfer/transfer");
147
+ const { TreeTable } = require("./framework/components/chui_tree_table/tree_table");
148
+ const { SidebarNav } = require("./framework/components/chui_sidebar_nav/sidebar_nav");
149
+ const { MarkdownEditor } = require("./framework/components/chui_markdown_editor/markdown_editor");
150
+ const { DiffViewer } = require("./framework/components/chui_diff_viewer/diff_viewer");
151
+ const { JsonViewer } = require("./framework/components/chui_json_viewer/json_viewer");
152
+ const { JsonEditor } = require("./framework/components/chui_json_editor/json_editor");
153
+ const { QueryBuilder } = require("./framework/components/chui_query_builder/query_builder");
154
+ const { FileExplorer } = require("./framework/components/chui_file_explorer/file_explorer");
155
+ const { Settings } = require("./framework/components/chui_settings/settings");
156
+ const { DropZone } = require("./framework/components/chui_drop_zone/drop_zone");
157
+ const { ClipboardHistory } = require("./framework/components/chui_clipboard_history/clipboard_history");
158
+ const { PrintButton } = require("./framework/components/chui_print_button/print_button");
159
+ const { Splitter } = require("./framework/components/chui_splitter/splitter");
160
+ const { Scheduler } = require("./framework/components/chui_scheduler/scheduler");
161
+ const { Gantt } = require("./framework/components/chui_gantt/gantt");
162
+ const { Kanban } = require("./framework/components/chui_kanban/kanban");
163
+ const { CodeEditor } = require("./framework/components/chui_code_editor/code_editor");
164
+ const { ProgressRing } = require("./framework/components/chui_progress_ring/progress_ring");
165
+ const { Toolbar } = require("./framework/components/chui_toolbar/toolbar");
166
+ const { StatusBar } = require("./framework/components/chui_status_bar/status_bar");
167
+ const { Sortable } = require("./framework/modules/chui_sortable/chui_sortable");
168
+ const { Form } = require("./framework/components/chui_form/form");
169
+ const { SlideShow } = require("./framework/components/chui_slideshow/slideshow");
170
+ const { FieldSet } = require("./framework/components/chui_fieldset/fieldset");
171
+ const { Popup } = require("./framework/components/chui_popups/popups");
172
+ const { TelegramBot } = require("./framework/components/telegram_bot/chui_telegram_bot");
173
+ const { MenuBar } = require("./framework/components/chui_menu_bar/menu_bar");
174
+ const { CommandPalette } = require("./framework/components/chui_command_palette/command_palette");
136
175
  const { ContextMenu } = require("./framework/components/chui_context_menu/context_menu");
137
- const {Image} = require('./framework/components/chui_media/image');
138
- const {Audio} = require("./framework/components/chui_media/audio");
139
- const {Video} = require("./framework/components/chui_media/video");
140
- const {CustomElement} = require("./framework/components/chui_custom_element/custom_element");
141
- const {Console} = require("./framework/components/chui_console/console");
142
- const {DownloadProgressNotification} = require("./framework/components/chui_notification/notification_download_progress");
143
- const {MultiComboBox} = require("./framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box");
144
- const {Mention} = require("./framework/components/chui_inputs/chui_mention/mention");
145
- const {Chat} = require("./framework/components/chui_chat/chat");
146
- const {ActionSheet} = require("./framework/components/chui_action_sheet/action_sheet");
147
- const {ImageCompare} = require("./framework/components/chui_media/image_compare");
148
- const {OrgChart} = require("./framework/components/chui_org_chart/org_chart");
149
- const {QRCode} = require("./framework/components/chui_qrcode/qrcode");
150
- const {Log} = require("./framework/modules/chui_logger/chui_logger");
176
+ const { Image } = require("./framework/components/chui_media/image");
177
+ const { Audio } = require("./framework/components/chui_media/audio");
178
+ const { Video } = require("./framework/components/chui_media/video");
179
+ const { CustomElement } = require("./framework/components/chui_custom_element/custom_element");
180
+ const { Console } = require("./framework/components/chui_console/console");
181
+ const {
182
+ DownloadProgressNotification,
183
+ } = require("./framework/components/chui_notification/notification_download_progress");
184
+ const { MultiComboBox } = require("./framework/components/chui_inputs/chui_multi_combo_box/multi_combo_box");
185
+ const { Mention } = require("./framework/components/chui_inputs/chui_mention/mention");
186
+ const { Chat } = require("./framework/components/chui_chat/chat");
187
+ const { ActionSheet } = require("./framework/components/chui_action_sheet/action_sheet");
188
+ const { ImageCompare } = require("./framework/components/chui_media/image_compare");
189
+ const { OrgChart } = require("./framework/components/chui_org_chart/org_chart");
190
+ const { QRCode } = require("./framework/components/chui_qrcode/qrcode");
191
+ const { Log } = require("./framework/modules/chui_logger/chui_logger");
151
192
  const url = require("node:url");
152
193
 
153
194
  /** Код исполняется на странице (renderer), а не в main: от этого зависит доступ к модулям Electron */
@@ -155,6 +196,18 @@ function inRenderer() {
155
196
  return process !== undefined && process.type === "renderer";
156
197
  }
157
198
 
199
+ /**
200
+ * Путь приложения по имени из белого списка.
201
+ *
202
+ * Одно правило на два канала (`APP_PATH` и `APP_PATH_SYNC`): странице не должен быть доступен
203
+ * произвольный каталог системы, поэтому всё, что вне `APP_PATH_NAMES`, main отклоняет. Сам
204
+ * `app.getPath` синхронный и без диска — именно поэтому путь можно отдавать значением.
205
+ */
206
+ function appPathByName(name = String()) {
207
+ if (APP_PATH_NAMES.includes(name) === false) throw new Error(`Desktop.app.path: имя «${name}» вне белого списка`);
208
+ return app.getPath(name);
209
+ }
210
+
158
211
  //VARS
159
212
  let isQuiting = false;
160
213
 
@@ -167,7 +220,8 @@ class Main {
167
220
  * Строгая политика для безопасных окон: скрипты и стили только свои (и файлы приложения),
168
221
  * никаких внешних источников, `connect-src 'none'` — окну не нужна сеть.
169
222
  */
170
- static SECURE_CSP = "default-src 'none'; script-src 'self' file:; style-src 'self' file: 'unsafe-inline'; img-src 'self' file: data: blob:; font-src 'self' file: data:; media-src 'self' file: data: blob:; connect-src 'none'";
223
+ static SECURE_CSP =
224
+ "default-src 'none'; script-src 'self' file:; style-src 'self' file: 'unsafe-inline'; img-src 'self' file: data: blob:; font-src 'self' file: data:; media-src 'self' file: data: blob:; connect-src 'none'";
171
225
  /**
172
226
  * Разметка страницы окна: пустая оболочка без собственного кода — всё приходит из
173
227
  * preload'а приложения (обычный режим) или из моста (`security: {contextIsolation: true}`,
@@ -203,42 +257,46 @@ class Main {
203
257
  #updaterListening = false;
204
258
  #lastUpdateInfo = undefined;
205
259
  #onShowSystemNotification = (e, title, body) => {
206
- let {Notification} = require('electron');
207
- new Notification({title, body}).show();
208
- }
260
+ let { Notification } = require("electron");
261
+ new Notification({ title, body }).show();
262
+ };
209
263
  #onSendLogText = (e, text, type) => {
210
264
  if (type === "info") {
211
- console.info(text)
265
+ console.info(text);
212
266
  } else if (type === "error") {
213
- console.error(text)
267
+ console.error(text);
214
268
  }
215
- this.#writeLog(text)
216
- }
269
+ this.#writeLog(text);
270
+ };
217
271
  #onUpdateInstallConfirm = (e, check) => {
218
272
  if (check) {
219
273
  Log.info("Установка обновления...");
220
274
  require("electron-updater").autoUpdater.quitAndInstall();
221
275
  }
222
- }
276
+ };
223
277
  #onUpdateDownloaded = async () => {
224
278
  await this.#sendNotificationUpdateLoadClose();
225
279
  await this.#sendNotificationUpdate(this.#appName, I18n.t("appLayout.update.downloaded"));
226
280
  setTimeout(async () => await this.#sendNotificationUpdateClose(), 3000);
227
281
  Log.info("Обновление скачано!");
228
- const version = this.#lastUpdateInfo === undefined ? app.getVersion() : this.#lastUpdateInfo.versionInfo.version;
282
+ const version =
283
+ this.#lastUpdateInfo === undefined ? app.getVersion() : this.#lastUpdateInfo.versionInfo.version;
229
284
  this.#window.webContents.send("checkUpdatesTrue", true, version);
230
- }
285
+ };
231
286
  // Глобальные обработчики ошибок процесса: регистрируются один раз, снимаются в stop()/restart()
232
287
  #processHandlersRegistered = false;
233
288
  // Десктоп-сервисы (буфер, недавние, прогресс, глобальные клавиши) и их подписки по окнам
234
289
  #services_enabled = false;
235
290
  #service_handlers = [];
291
+ // Синхронные каналы снимаются не `removeHandler`, а `removeListener`: они зарегистрированы
292
+ // через `ipcMain.on` — см. `#handleSync`
293
+ #service_sync_handlers = [];
236
294
  #shortcut_subscribers = new Map();
237
295
  #shortcut_watchers = new Map();
238
296
  #progress_windows = new Map();
239
297
  #on_theme_updated = undefined;
240
298
  // Безопасный режим: preload-мост, contextIsolation и CSP вместо Node в рендерере
241
- #security = {contextIsolation: false, csp: false, channels: []};
299
+ #security = { contextIsolation: false, csp: false, channels: [] };
242
300
  #secure_contents = new Set();
243
301
  #bridge_enabled = false;
244
302
  #window_actions_bound = false;
@@ -246,17 +304,17 @@ class Main {
246
304
  #page_message_handlers = new Map();
247
305
  #onUncaughtException = (error) => {
248
306
  Main.#logProcessError("Необработанное исключение", error);
249
- }
307
+ };
250
308
  #onUnhandledRejection = (reason) => {
251
309
  Main.#logProcessError("Необработанный reject промиса", reason);
252
- }
310
+ };
253
311
  static #logProcessError(prefix, value) {
254
312
  const text = `${prefix}: ${Main.#formatError(value)}`;
255
313
  try {
256
- Log.error(text)
314
+ Log.error(text);
257
315
  } catch (e) {
258
316
  // Сам обработчик ошибок падать не должен (например, недоступен userData для лога)
259
- console.error(text, e)
317
+ console.error(text, e);
260
318
  }
261
319
  }
262
320
  static #formatError(value) {
@@ -274,51 +332,53 @@ class Main {
274
332
  const target = path.join(this.#downloadPath, item.getFilename());
275
333
  if (fs.existsSync(target)) return;
276
334
  try {
277
- if (!fs.existsSync(this.#downloadPath)) fs.mkdirSync(this.#downloadPath, {recursive: true});
335
+ if (!fs.existsSync(this.#downloadPath)) fs.mkdirSync(this.#downloadPath, { recursive: true });
278
336
  item.setSavePath(target);
279
337
  } catch (error) {
280
338
  Log.error(`Не удалось сохранить загрузку в ${this.#downloadPath}: ${Main.#formatError(error)}`);
281
339
  }
282
- }
340
+ };
283
341
 
284
- constructor(options = {
285
- name: String(),
286
- sizes: { height: Number(), width: Number(), minHeight: Number(), minWidth: Number() },
287
- render: String(),
288
- devTools: Boolean(),
289
- webSecurity: Boolean(),
290
- resizable: Boolean(),
291
- security: { contextIsolation: Boolean(), csp: Boolean(), channels: [] },
292
- paths: {
293
- downloadPath: String(),
342
+ constructor(
343
+ options = {
344
+ name: String(),
345
+ sizes: { height: Number(), width: Number(), minHeight: Number(), minWidth: Number() },
346
+ render: String(),
347
+ devTools: Boolean(),
348
+ webSecurity: Boolean(),
349
+ resizable: Boolean(),
350
+ security: { contextIsolation: Boolean(), csp: Boolean(), channels: [] },
351
+ paths: {
352
+ downloadPath: String(),
353
+ },
294
354
  }
295
- }) {
355
+ ) {
296
356
  this.#app_icon = options.icon;
297
357
  if (this.#app_icon === undefined) {
298
358
  if (process.platform === "darwin") {
299
- this.#app_icon = Main.#resizeIconTray(getDefaultIcon())
359
+ this.#app_icon = Main.#resizeIconTray(getDefaultIcon());
300
360
  } else {
301
361
  this.#app_icon = getDefaultIcon();
302
362
  }
303
363
  } else {
304
364
  if (process.platform === "darwin") {
305
- this.#app_icon = Main.#resizeIconTray(options.icon)
365
+ this.#app_icon = Main.#resizeIconTray(options.icon);
306
366
  } else {
307
367
  this.#app_icon = options.icon;
308
368
  }
309
369
  }
310
370
  // app.commandLine.appendSwitch('--enable-features', 'OverlayScrollbar')
311
- app.commandLine.appendSwitch('disable-hid-blocklist')
312
- app.commandLine.appendSwitch('enable-features=OverlayScrollbar')
371
+ app.commandLine.appendSwitch("disable-hid-blocklist");
372
+ app.commandLine.appendSwitch("enable-features=OverlayScrollbar");
313
373
  // app.commandLine.appendSwitch("disable-http-cache");
314
- app.commandLine.appendSwitch('enable-transparent-visuals');
315
- app.commandLine.appendSwitch('enable-gpu-rasterization', "true");
316
- app.commandLine.appendSwitch('enable-native-gpu-memory-buffers', "true");
317
- app.commandLine.appendSwitch('high-dpi-support', "true");
318
- app.commandLine.appendSwitch('device-scale-factor', "true");
319
- app.commandLine.appendSwitch('disable-touch-adjustment', "true");
320
- app.commandLine.appendSwitch('main-frame-resizes-are-orientation-changes', "true");
321
- app.commandLine.appendSwitch('disable-pinch', "true");
374
+ app.commandLine.appendSwitch("enable-transparent-visuals");
375
+ app.commandLine.appendSwitch("enable-gpu-rasterization", "true");
376
+ app.commandLine.appendSwitch("enable-native-gpu-memory-buffers", "true");
377
+ app.commandLine.appendSwitch("high-dpi-support", "true");
378
+ app.commandLine.appendSwitch("device-scale-factor", "true");
379
+ app.commandLine.appendSwitch("disable-touch-adjustment", "true");
380
+ app.commandLine.appendSwitch("main-frame-resizes-are-orientation-changes", "true");
381
+ app.commandLine.appendSwitch("disable-pinch", "true");
322
382
 
323
383
  // Без опции загрузки идут обычным путём Electron: раньше значение вычислялось и не использовалось
324
384
  this.#downloadPath = options.paths === undefined ? undefined : options.paths.downloadPath;
@@ -339,7 +399,7 @@ class Main {
339
399
  }
340
400
 
341
401
  static #resizeIconTray(image) {
342
- return require('electron').nativeImage.createFromPath(image).resize({width: 16, height: 16});
402
+ return require("electron").nativeImage.createFromPath(image).resize({ width: 16, height: 16 });
343
403
  }
344
404
 
345
405
  /** Опции безопасности окна: по умолчанию включён привычный режим (Node в рендерере) */
@@ -347,7 +407,7 @@ class Main {
347
407
  return {
348
408
  contextIsolation: security.contextIsolation === true,
349
409
  csp: security.csp === undefined ? true : security.csp,
350
- channels: security.channels === undefined ? [] : security.channels
410
+ channels: security.channels === undefined ? [] : security.channels,
351
411
  };
352
412
  }
353
413
 
@@ -409,10 +469,19 @@ class Main {
409
469
  * (`createWindow()`) собираются одним кодом: у всех окон одинаковые webPreferences,
410
470
  * иконка, поведение закрытия и подписка на `chui_resizable_false`.
411
471
  */
412
- #buildWindow(options = {
413
- name: String(), title: String(), sizes: Object(), render: String(), parent: undefined,
414
- security: Object(), hideOnClose: Boolean(), resizable: Boolean(), devTools: Boolean()
415
- }) {
472
+ #buildWindow(
473
+ options = {
474
+ name: String(),
475
+ title: String(),
476
+ sizes: Object(),
477
+ render: String(),
478
+ parent: undefined,
479
+ security: Object(),
480
+ hideOnClose: Boolean(),
481
+ resizable: Boolean(),
482
+ devTools: Boolean(),
483
+ }
484
+ ) {
416
485
  const sizes = options.sizes === undefined ? {} : options.sizes;
417
486
  const name = options.name === undefined ? Main.MAIN_WINDOW : options.name;
418
487
  const resizable = options.resizable === undefined ? this.#resizable : options.resizable;
@@ -434,35 +503,35 @@ class Main {
434
503
  // выполняется как обычный <script> документа (см. chui_bridge/preload.js)
435
504
  webPreferences: secure
436
505
  ? {
437
- plugins: false,
438
- nodeIntegration: false,
439
- contextIsolation: true,
440
- sandbox: false,
441
- preload: Main.SECURE_PRELOAD,
442
- additionalArguments: Main.#bridgeArguments(options.render, security),
443
- spellcheck: false,
444
- webviewTag: false,
445
- webSecurity: this.#webSecurity,
446
- nodeIntegrationInSubFrames: false,
447
- }
506
+ plugins: false,
507
+ nodeIntegration: false,
508
+ contextIsolation: true,
509
+ sandbox: false,
510
+ preload: Main.SECURE_PRELOAD,
511
+ additionalArguments: Main.#bridgeArguments(options.render, security),
512
+ spellcheck: false,
513
+ webviewTag: false,
514
+ webSecurity: this.#webSecurity,
515
+ nodeIntegrationInSubFrames: false,
516
+ }
448
517
  : {
449
- plugins: false,
450
- nodeIntegration: true,
451
- contextIsolation: false,
452
- // Рендерер окна: у дополнительных окон может быть свой скрипт
453
- preload: options.render === undefined ? this.#renderer : options.render,
454
- spellcheck: false,
455
- webviewTag: true,
456
- webSecurity: this.#webSecurity,
457
- nodeIntegrationInSubFrames: true,
458
- },
518
+ plugins: false,
519
+ nodeIntegration: true,
520
+ contextIsolation: false,
521
+ // Рендерер окна: у дополнительных окон может быть свой скрипт
522
+ preload: options.render === undefined ? this.#renderer : options.render,
523
+ spellcheck: false,
524
+ webviewTag: true,
525
+ webSecurity: this.#webSecurity,
526
+ nodeIntegrationInSubFrames: true,
527
+ },
459
528
  frame: false,
460
529
  resizable: resizable,
461
530
  minimizable: true,
462
531
  maximizable: true,
463
532
  center: true,
464
533
  hasShadow: true,
465
- parent: options.parent
534
+ parent: options.parent,
466
535
  });
467
536
  const window = this.#window;
468
537
  const webContentsId = window.webContents.id;
@@ -497,7 +566,7 @@ class Main {
497
566
  setTimeout(() => {
498
567
  if (!window.isDestroyed()) window.show();
499
568
  }, 650)
500
- )
569
+ );
501
570
  // Подписки глобальных клавиш привязаны к отправителю: окно закрылось — снимаем их
502
571
  window.on("closed", () => {
503
572
  if (this.#windows.get(name) === window) this.#windows.delete(name);
@@ -507,7 +576,7 @@ class Main {
507
576
  });
508
577
 
509
578
  if (options.hideOnClose === true) {
510
- window.on('close', (evt) => {
579
+ window.on("close", (evt) => {
511
580
  if (!isQuiting) evt.preventDefault();
512
581
  window.hide();
513
582
  });
@@ -522,7 +591,7 @@ class Main {
522
591
  #bindBeforeQuit() {
523
592
  if (this.#before_quit_bound) return;
524
593
  this.#before_quit_bound = true;
525
- app.on('before-quit', () => {
594
+ app.on("before-quit", () => {
526
595
  isQuiting = true;
527
596
  });
528
597
  }
@@ -532,18 +601,19 @@ class Main {
532
601
  if (this.#webSecurity || this.#web_request_bound) return;
533
602
  this.#web_request_bound = true;
534
603
  window.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => {
535
- const {requestHeaders} = details;
536
- Main.#keyToChangeLower(requestHeaders, 'Access-Control-Allow-Origin', ['*']);
537
- Main.#keyToChangeLower(requestHeaders, 'X-Frame-Options', ['*']);
538
- callback({requestHeaders});
539
- },);
604
+ const { requestHeaders } = details;
605
+ Main.#keyToChangeLower(requestHeaders, "Access-Control-Allow-Origin", ["*"]);
606
+ Main.#keyToChangeLower(requestHeaders, "X-Frame-Options", ["*"]);
607
+ callback({ requestHeaders });
608
+ });
540
609
  window.webContents.session.webRequest.onHeadersReceived((details, callback) => {
541
- const {responseHeaders} = details;
542
- Main.#keyToChangeLower(responseHeaders, 'Access-Control-Allow-Origin', ['*']);
543
- Main.#keyToChangeLower(responseHeaders, 'Access-Control-Allow-Headers', ['*']);
544
- Main.#keyToChangeLower(responseHeaders, 'X-Frame-Options', ['*']);
610
+ const { responseHeaders } = details;
611
+ Main.#keyToChangeLower(responseHeaders, "Access-Control-Allow-Origin", ["*"]);
612
+ Main.#keyToChangeLower(responseHeaders, "Access-Control-Allow-Headers", ["*"]);
613
+ Main.#keyToChangeLower(responseHeaders, "X-Frame-Options", ["*"]);
545
614
  callback({
546
- responseHeaders, cancel: false
615
+ responseHeaders,
616
+ cancel: false,
547
617
  });
548
618
  });
549
619
  }
@@ -560,10 +630,19 @@ class Main {
560
630
  * main.sendTo('preview', 'preview_update', payload);
561
631
  * ```
562
632
  */
563
- async createWindow(options = {
564
- name: String(), title: String(), sizes: Object(), render: String(), parent: undefined,
565
- security: Object(), hideOnClose: Boolean(), resizable: Boolean(), devTools: Boolean()
566
- }) {
633
+ async createWindow(
634
+ options = {
635
+ name: String(),
636
+ title: String(),
637
+ sizes: Object(),
638
+ render: String(),
639
+ parent: undefined,
640
+ security: Object(),
641
+ hideOnClose: Boolean(),
642
+ resizable: Boolean(),
643
+ devTools: Boolean(),
644
+ }
645
+ ) {
567
646
  await app.whenReady();
568
647
  this.enableServices();
569
648
  return this.#buildWindow(options);
@@ -584,7 +663,7 @@ class Main {
584
663
  return [...this.#windows].map(([name, window]) => ({
585
664
  name: name,
586
665
  window: window,
587
- secure: this.#secure_contents.has(window.webContents.id)
666
+ secure: this.#secure_contents.has(window.webContents.id),
588
667
  }));
589
668
  }
590
669
 
@@ -642,8 +721,8 @@ class Main {
642
721
  progress: new TaskbarProgress(),
643
722
  theme: new Theme(store),
644
723
  locale: new Locale(store),
645
- clipboard_history: new ClipboardHistoryService({store: store}),
646
- print: new Print()
724
+ clipboard_history: new ClipboardHistoryService({ store: store }),
725
+ print: new Print(),
647
726
  };
648
727
  // Сохранённая тема применяется до создания окна: иначе при запуске была бы видна
649
728
  // вспышка системной палитры
@@ -695,23 +774,57 @@ class Main {
695
774
  });
696
775
  // Приложение и системные диалоги для страницы: Electron советует IPC вместо `remote`.
697
776
  // Путь — только из белого списка, диалог и сессия — у окна-отправителя.
698
- this.#handle(SERVICES_CHANNELS.APP_PATH, (event, name) => {
699
- if (APP_PATH_NAMES.includes(name) === false) throw new Error(`App.path: имя «${name}» вне белого списка`);
700
- return app.getPath(name);
701
- });
777
+ this.#handle(SERVICES_CHANNELS.APP_PATH, (event, name) => appPathByName(name));
778
+ // `Desktop.app.path` отвечает значением, а не промисом, поэтому у пути есть второй,
779
+ // синхронный канал: `sendSync` ждёт `event.returnValue`, а не ответ `handle`.
780
+ // Асинхронный канал выше оставлен — его ключ есть в карте `window.chui.services`
781
+ this.#handleSync(SERVICES_CHANNELS.APP_PATH_SYNC, (event, name) => appPathByName(name));
702
782
  this.#handle(SERVICES_CHANNELS.APP_INFO, () => ({
703
783
  name: app.getName(),
704
784
  version: app.getVersion(),
705
785
  locale: app.getLocale(),
706
786
  systemLocale: app.getSystemLocale(),
707
- isPackaged: app.isPackaged
787
+ isPackaged: app.isPackaged,
708
788
  }));
709
- this.#handle(SERVICES_CHANNELS.DIALOG_OPEN, (event, options) => Main.#showDialog(event, "showOpenDialog", options));
710
- this.#handle(SERVICES_CHANNELS.DIALOG_SAVE, (event, options) => Main.#showDialog(event, "showSaveDialog", options));
711
- this.#handle(SERVICES_CHANNELS.DIALOG_MESSAGE, (event, options) => Main.#showDialog(event, "showMessageBox", options));
789
+ // Закрытие и перезапуск со страницы: страница — код самого приложения, поэтому кнопке
790
+ // «Выход» не нужен свой канал в каждом проекте. Идут через `Main.stop()`/`restart()` —
791
+ // те же шаги, что у пункта меню: снимаются обработчики сервисов, глобальные клавиши и
792
+ // подписки, а не просто закрывается окно. Ответ уходит странице до выхода
793
+ // (`setImmediate`): иначе промис отваливался бы с «Render frame was disposed», и
794
+ // `await Desktop.app.quit()` со страницы выглядел бы ошибкой
795
+ this.#handle(SERVICES_CHANNELS.APP_QUIT, () => {
796
+ setImmediate(() => this.stop());
797
+ return true;
798
+ });
799
+ this.#handle(SERVICES_CHANNELS.APP_RESTART, () => {
800
+ setImmediate(() => this.restart());
801
+ return true;
802
+ });
803
+ // У закрытия и перезапуска есть и синхронные каналы: `Desktop.app.quit()`/`restart()`
804
+ // отвечают значением, а не промисом — как `Desktop.app.path`. Выход по-прежнему
805
+ // откладывается `setImmediate`, чтобы `event.returnValue` успел уйти странице.
806
+ this.#handleSync(SERVICES_CHANNELS.APP_QUIT_SYNC, () => {
807
+ setImmediate(() => this.stop());
808
+ return true;
809
+ });
810
+ this.#handleSync(SERVICES_CHANNELS.APP_RESTART_SYNC, () => {
811
+ setImmediate(() => this.restart());
812
+ return true;
813
+ });
814
+ this.#handle(SERVICES_CHANNELS.DIALOG_OPEN, (event, options) =>
815
+ Main.#showDialog(event, "showOpenDialog", options)
816
+ );
817
+ this.#handle(SERVICES_CHANNELS.DIALOG_SAVE, (event, options) =>
818
+ Main.#showDialog(event, "showSaveDialog", options)
819
+ );
820
+ this.#handle(SERVICES_CHANNELS.DIALOG_MESSAGE, (event, options) =>
821
+ Main.#showDialog(event, "showMessageBox", options)
822
+ );
712
823
  this.#handle(SERVICES_CHANNELS.SESSION_CACHE_SIZE, (event) => event.sender.session.getCacheSize());
713
824
  this.#handle(SERVICES_CHANNELS.SESSION_CLEAR_CACHE, (event) => event.sender.session.clearCache());
714
- this.#handle(SERVICES_CHANNELS.SESSION_CLEAR_STORAGE, (event, options) => event.sender.session.clearStorageData(options));
825
+ this.#handle(SERVICES_CHANNELS.SESSION_CLEAR_STORAGE, (event, options) =>
826
+ event.sender.session.clearStorageData(options)
827
+ );
715
828
  this.#handle(SERVICES_CHANNELS.RECENT_ADD, (event, path, name) => {
716
829
  this.services.recent.add(path, name);
717
830
  return true;
@@ -730,17 +843,26 @@ class Main {
730
843
  return true;
731
844
  });
732
845
  this.#handle(SERVICES_CHANNELS.SHELL_OPEN_EXTERNAL, (event, url) => this.services.shell.openExternal(url));
733
- this.#handle(SERVICES_CHANNELS.PRINT_PAGE, (event, options) => this.services.print.page(BrowserWindow.fromWebContents(event.sender), this.#printOptions(event, options)));
734
- this.#handle(SERVICES_CHANNELS.PRINT_HTML, (event, html, options) => this.services.print.html(html, this.#printOptions(event, options)));
735
- this.#handle(SERVICES_CHANNELS.PRINT_PDF, (event, html, options) => this.services.print.pdf(html, this.#printOptions(event, options)));
736
- this.#handle(SERVICES_CHANNELS.THEME_GET, () => ({theme: this.services.theme.get(), source: this.services.theme.getSource()}));
846
+ this.#handle(SERVICES_CHANNELS.PRINT_PAGE, (event, options) =>
847
+ this.services.print.page(BrowserWindow.fromWebContents(event.sender), this.#printOptions(event, options))
848
+ );
849
+ this.#handle(SERVICES_CHANNELS.PRINT_HTML, (event, html, options) =>
850
+ this.services.print.html(html, this.#printOptions(event, options))
851
+ );
852
+ this.#handle(SERVICES_CHANNELS.PRINT_PDF, (event, html, options) =>
853
+ this.services.print.pdf(html, this.#printOptions(event, options))
854
+ );
855
+ this.#handle(SERVICES_CHANNELS.THEME_GET, () => ({
856
+ theme: this.services.theme.get(),
857
+ source: this.services.theme.getSource(),
858
+ }));
737
859
  this.#handle(SERVICES_CHANNELS.THEME_SET, (event, source) => {
738
860
  this.services.theme.set(source);
739
- return {theme: this.services.theme.get(), source: this.services.theme.getSource()};
861
+ return { theme: this.services.theme.get(), source: this.services.theme.getSource() };
740
862
  });
741
863
  this.#handle(SERVICES_CHANNELS.THEME_TOGGLE, () => {
742
864
  this.services.theme.toggle();
743
- return {theme: this.services.theme.get(), source: this.services.theme.getSource()};
865
+ return { theme: this.services.theme.get(), source: this.services.theme.getSource() };
744
866
  });
745
867
  this.#handle(SERVICES_CHANNELS.LOCALE_GET, () => this.services.locale.get());
746
868
  this.#handle(SERVICES_CHANNELS.LOCALE_SET, (event, locale) => {
@@ -752,7 +874,9 @@ class Main {
752
874
  this.#emitLocale(applied);
753
875
  return applied;
754
876
  });
755
- this.#handle(SERVICES_CHANNELS.SHORTCUT_SUBSCRIBE, (event, accelerator) => this.#subscribeShortcut(event, accelerator));
877
+ this.#handle(SERVICES_CHANNELS.SHORTCUT_SUBSCRIBE, (event, accelerator) =>
878
+ this.#subscribeShortcut(event, accelerator)
879
+ );
756
880
  this.#handle(SERVICES_CHANNELS.SHORTCUT_UNSUBSCRIBE, (event, accelerator) => {
757
881
  this.#unsubscribeShortcut(event, accelerator);
758
882
  return true;
@@ -767,6 +891,25 @@ class Main {
767
891
  return this;
768
892
  }
769
893
 
894
+ /**
895
+ * Синхронный канал: `ipcRenderer.sendSync` в странице ждёт `event.returnValue`, а не ответ
896
+ * `handle`, поэтому здесь `ipcMain.on`. Ответ — конверт `{value}`/`{error}`: у синхронного
897
+ * канала нет способа передать исключение, и без конверта страница получила бы `undefined`
898
+ * вместо причины отказа (см. `SERVICES_SYNC_CALLS` в chui_channels.js).
899
+ */
900
+ #handleSync(channel = String(), handler = () => {}) {
901
+ const listener = (event, ...args) => {
902
+ try {
903
+ event.returnValue = { value: handler(event, ...args) };
904
+ } catch (error) {
905
+ event.returnValue = { error: error instanceof Error ? error.message : String(error) };
906
+ }
907
+ };
908
+ ipcMain.on(channel, listener);
909
+ this.#service_sync_handlers.push([channel, listener]);
910
+ return this;
911
+ }
912
+
770
913
  /**
771
914
  * Включает мост безопасных окон (`contextBridge`): управление окном, тема и прикладные
772
915
  * сообщения по белому списку каналов. Вызывается сам при создании безопасного окна.
@@ -792,7 +935,7 @@ class Main {
792
935
  isMaximized: window.isMaximized(),
793
936
  isMinimized: window.isMinimized(),
794
937
  isFullScreen: window.isFullScreen(),
795
- isFocused: window.isFocused()
938
+ isFocused: window.isFocused(),
796
939
  };
797
940
  });
798
941
  return this;
@@ -806,7 +949,7 @@ class Main {
806
949
  this.#handle(BRIDGE_CHANNELS.LOCALE_GET, () => this.services.locale.get());
807
950
  this.#handle(BRIDGE_CHANNELS.MESSAGE, (event, channel, ...args) => {
808
951
  const handler = this.#page_message_handlers.get(channel);
809
- if (handler === undefined) return {handled: false};
952
+ if (handler === undefined) return { handled: false };
810
953
  return handler(...args, event);
811
954
  });
812
955
  this.#bindThemeEvents();
@@ -956,7 +1099,7 @@ class Main {
956
1099
  * поэтому `parent` берём из события, а не из запроса страницы.
957
1100
  */
958
1101
  #printOptions(event, options = Object()) {
959
- return {...options, parent: BrowserWindow.fromWebContents(event.sender)};
1102
+ return { ...options, parent: BrowserWindow.fromWebContents(event.sender) };
960
1103
  }
961
1104
 
962
1105
  #emitShortcut(accelerator = String()) {
@@ -1000,15 +1143,15 @@ class Main {
1000
1143
  #hideAndShow() {
1001
1144
  const window = this.#targetWindow();
1002
1145
  if (window === undefined) return;
1003
- let visible = window.isVisible()
1004
- let focus = window.isFocused()
1146
+ let visible = window.isVisible();
1147
+ let focus = window.isFocused();
1005
1148
 
1006
1149
  if (visible && focus) {
1007
- window.hide()
1150
+ window.hide();
1008
1151
  } else if (!visible && !focus) {
1009
- window.show()
1152
+ window.show();
1010
1153
  } else {
1011
- window.focus()
1154
+ window.focus();
1012
1155
  }
1013
1156
  }
1014
1157
 
@@ -1026,17 +1169,17 @@ class Main {
1026
1169
 
1027
1170
  stop() {
1028
1171
  this.#removeGlobalHandlers();
1029
- if (process.platform !== 'darwin') {
1172
+ if (process.platform !== "darwin") {
1030
1173
  app.quit();
1031
1174
  } else {
1032
- app.exit(0)
1175
+ app.exit(0);
1033
1176
  }
1034
1177
  }
1035
1178
 
1036
1179
  restart() {
1037
1180
  this.#removeGlobalHandlers();
1038
1181
  app.relaunch();
1039
- app.exit(0)
1182
+ app.exit(0);
1040
1183
  }
1041
1184
 
1042
1185
  #removeGlobalHandlers() {
@@ -1051,6 +1194,8 @@ class Main {
1051
1194
  // Десктоп-сервисы: снимаем обработчики IPC, подписки клавиш и сами клавиши
1052
1195
  for (const [channel] of this.#service_handlers) ipcMain.removeHandler(channel);
1053
1196
  this.#service_handlers = [];
1197
+ for (const [channel, listener] of this.#service_sync_handlers) ipcMain.removeListener(channel, listener);
1198
+ this.#service_sync_handlers = [];
1054
1199
  this.#shortcut_subscribers.clear();
1055
1200
  this.#shortcut_watchers.clear();
1056
1201
  this.#progress_windows.clear();
@@ -1067,7 +1212,7 @@ class Main {
1067
1212
  this.#window_actions_bound = false;
1068
1213
  if (this.#updaterListening) {
1069
1214
  this.#updaterListening = false;
1070
- require("electron-updater").autoUpdater.removeListener('update-downloaded', this.#onUpdateDownloaded);
1215
+ require("electron-updater").autoUpdater.removeListener("update-downloaded", this.#onUpdateDownloaded);
1071
1216
  }
1072
1217
  if (this.#processHandlersRegistered) {
1073
1218
  this.#processHandlersRegistered = false;
@@ -1077,7 +1222,7 @@ class Main {
1077
1222
  this.#ipcRegistered = false;
1078
1223
  }
1079
1224
 
1080
- start(options = {hideOnClose: Boolean(), globalMenu: [], tray: [], extensions: []}) {
1225
+ start(options = { hideOnClose: Boolean(), globalMenu: [], tray: [], extensions: [] }) {
1081
1226
  nativeTheme.themeSource = "system";
1082
1227
  // Обработчики процесса — один раз на экземпляр Main (иначе копились при повторном start())
1083
1228
  if (this.#ipcRegistered === false) {
@@ -1094,7 +1239,7 @@ class Main {
1094
1239
 
1095
1240
  app.whenReady().then(() => {
1096
1241
  session.defaultSession.setPermissionRequestHandler((webContents, permission, callback) => {
1097
- if (permission === 'media') {
1242
+ if (permission === "media") {
1098
1243
  callback(true); // Approve microphone/camera access
1099
1244
  } else {
1100
1245
  callback(false);
@@ -1102,49 +1247,53 @@ class Main {
1102
1247
  });
1103
1248
 
1104
1249
  if (options.extensions) {
1105
- for (let extension of options.extensions) session.defaultSession.loadExtension(extension).then(r => Log.info(r)).catch(e => Log.error(e))
1250
+ for (let extension of options.extensions)
1251
+ session.defaultSession
1252
+ .loadExtension(extension)
1253
+ .then((r) => Log.info(r))
1254
+ .catch((e) => Log.error(e));
1106
1255
  }
1107
1256
  if (options.tray) {
1108
1257
  this.#tray = new Tray(this.#app_icon);
1109
1258
  const context = Menu.buildFromTemplate(options.tray);
1110
1259
  this.#tray.setContextMenu(context);
1111
1260
 
1112
- this.#tray.on("click", () => this.#hideAndShow())
1261
+ this.#tray.on("click", () => this.#hideAndShow());
1113
1262
  }
1114
1263
  if (options.globalMenu) {
1115
- const menu = Menu.buildFromTemplate(options.globalMenu)
1116
- Menu.setApplicationMenu(menu)
1264
+ const menu = Menu.buildFromTemplate(options.globalMenu);
1265
+ Menu.setApplicationMenu(menu);
1117
1266
  } else {
1118
- Menu.setApplicationMenu(null)
1267
+ Menu.setApplicationMenu(null);
1119
1268
  }
1120
1269
  this.enableServices();
1121
- this.#buildWindow({name: Main.MAIN_WINDOW, hideOnClose: options.hideOnClose});
1122
- })
1270
+ this.#buildWindow({ name: Main.MAIN_WINDOW, hideOnClose: options.hideOnClose });
1271
+ });
1123
1272
  }
1124
1273
 
1125
1274
  #writeLog(text) {
1126
- let today = new Date().toLocaleDateString().replace("/", ".")
1127
- let log_dir_path = path.join(app.getPath("userData"), "logs")
1128
- let log_file_path = path.join(log_dir_path, `app_${today}.log`)
1275
+ let today = new Date().toLocaleDateString().replace("/", ".");
1276
+ let log_dir_path = path.join(app.getPath("userData"), "logs");
1277
+ let log_file_path = path.join(log_dir_path, `app_${today}.log`);
1129
1278
 
1130
1279
  if (!fs.existsSync(log_dir_path)) {
1131
1280
  fs.mkdirSync(log_dir_path, { recursive: true });
1132
1281
  fs.writeFileSync(log_file_path, "", "utf-8");
1133
1282
  }
1134
1283
 
1135
- fs.writeFile(log_file_path, `${text}\n`, { flag: 'a+' }, err => {
1284
+ fs.writeFile(log_file_path, `${text}\n`, { flag: "a+" }, (err) => {
1136
1285
  if (err) console.error(err);
1137
1286
  });
1138
1287
  }
1139
1288
 
1140
1289
  enableAutoUpdateApp(start = Number()) {
1141
- const {autoUpdater} = require("electron-updater");
1290
+ const { autoUpdater } = require("electron-updater");
1142
1291
  // Подписки на апдейтер и подтверждение установки — один раз на экземпляр Main:
1143
1292
  // раньше ipcMain.on("updateInstallConfirm") и autoUpdater.on добавлялись
1144
1293
  // внутри update-downloaded, то есть на каждое событие.
1145
1294
  if (this.#updaterListening === false) {
1146
1295
  this.#updaterListening = true;
1147
- autoUpdater.on('update-downloaded', this.#onUpdateDownloaded);
1296
+ autoUpdater.on("update-downloaded", this.#onUpdateDownloaded);
1148
1297
  ipcMain.on("updateInstallConfirm", this.#onUpdateInstallConfirm);
1149
1298
  }
1150
1299
  setTimeout(async () => {
@@ -1153,7 +1302,10 @@ class Main {
1153
1302
  if (updates !== null) {
1154
1303
  this.#lastUpdateInfo = updates;
1155
1304
  if (updates.versionInfo.version > app.getVersion()) {
1156
- await this.#sendNotificationUpdateLoad(this.#appName, I18n.t("appLayout.update.downloading", {version: updates.versionInfo.version}));
1305
+ await this.#sendNotificationUpdateLoad(
1306
+ this.#appName,
1307
+ I18n.t("appLayout.update.downloading", { version: updates.versionInfo.version })
1308
+ );
1157
1309
  }
1158
1310
  }
1159
1311
  }, start);
@@ -1173,81 +1325,43 @@ class Main {
1173
1325
 
1174
1326
  /** Уведомление о загрузке; последним аргументом можно указать окно реестра */
1175
1327
  sendDownload(text, body, windowName = undefined) {
1176
- setTimeout(() => this.#send("sendNotificationDownload", [text, body], windowName), 1)
1328
+ setTimeout(() => this.#send("sendNotificationDownload", [text, body], windowName), 1);
1177
1329
  }
1178
1330
  sendDownloadUpdate(text, body, windowName = undefined) {
1179
- setTimeout(() => this.#send("sendNotificationDownloadUpdate", [text, body], windowName), 1)
1331
+ setTimeout(() => this.#send("sendNotificationDownloadUpdate", [text, body], windowName), 1);
1180
1332
  }
1181
1333
  sendDownloadComplete(text, body, windowName = undefined) {
1182
- setTimeout(() => this.#send("sendNotificationDownloadComplete", [text, body], windowName), 250)
1334
+ setTimeout(() => this.#send("sendNotificationDownloadComplete", [text, body], windowName), 250);
1183
1335
  }
1184
1336
  sendDownloadError(text, body, windowName = undefined) {
1185
- setTimeout(() => this.#send("sendNotificationDownloadError", [text, body], windowName), 250)
1337
+ setTimeout(() => this.#send("sendNotificationDownloadError", [text, body], windowName), 250);
1186
1338
  }
1187
1339
  }
1188
1340
 
1189
1341
  class Styles {
1190
- static WORD_BREAK = {NORMAL: "normal", BREAK_ALL: "break-all", KEEP_ALL: "keep-all", BREAK_WORD: "break-word"}
1191
- static TEXT_ALIGN = {CENTER: 'center', END: 'end', START: 'start'};
1192
- static ALIGN = {CENTER: 'center', END: 'flex-end', START: 'flex-start', BASELINE: 'baseline', STRETCH: 'stretch'};
1342
+ static WORD_BREAK = { NORMAL: "normal", BREAK_ALL: "break-all", KEEP_ALL: "keep-all", BREAK_WORD: "break-word" };
1343
+ static TEXT_ALIGN = { CENTER: "center", END: "end", START: "start" };
1344
+ static ALIGN = { CENTER: "center", END: "flex-end", START: "flex-start", BASELINE: "baseline", STRETCH: "stretch" };
1193
1345
  static JUSTIFY = {
1194
- CENTER: 'center',
1195
- END: 'flex-end',
1196
- START: 'flex-start',
1197
- SPACE_AROUND: 'space-around',
1198
- SPACE_BEETWEEN: 'space-between',
1199
- SPACE_EVENLY: 'space-evenly'
1346
+ CENTER: "center",
1347
+ END: "flex-end",
1348
+ START: "flex-start",
1349
+ SPACE_AROUND: "space-around",
1350
+ SPACE_BEETWEEN: "space-between",
1351
+ SPACE_EVENLY: "space-evenly",
1200
1352
  };
1201
- static DIRECTION = {ROW: 'row', COLUMN: 'column'};
1202
- static WRAP = {NOWRAP: 'nowrap', WRAP: 'wrap'};
1353
+ static DIRECTION = { ROW: "row", COLUMN: "column" };
1354
+ static WRAP = { NOWRAP: "nowrap", WRAP: "wrap" };
1203
1355
  static SIZE = {
1204
- AUTO: 'auto',
1205
- FIT_CONTENT: 'fit-content',
1206
- MAX_CONTENT: 'max-content',
1207
- MIN_CONTENT: 'min-content',
1208
- WEBKIT_FILL: '-webkit-fill-available',
1209
- INHERIT: 'inherit'
1356
+ AUTO: "auto",
1357
+ FIT_CONTENT: "fit-content",
1358
+ MAX_CONTENT: "max-content",
1359
+ MIN_CONTENT: "min-content",
1360
+ WEBKIT_FILL: "-webkit-fill-available",
1361
+ INHERIT: "inherit",
1210
1362
  };
1211
1363
  }
1212
1364
 
1213
- class Application {
1214
- getApp() {
1215
- if (inRenderer()) throw new Error("App.get(): странице не отдают модули main — возьмите Desktop.app.path()/info() или свой канал из security.channels");
1216
- return app;
1217
- }
1218
- getSession() {
1219
- if (inRenderer()) throw new Error("App.getSession(): странице не отдают сессию main — возьмите Desktop.session.*");
1220
- return session;
1221
- }
1222
- getWebContents() {
1223
- if (inRenderer()) throw new Error("App.getWebContents(): странице не отдают webContents main — действия идут через Desktop или свой канал из security.channels");
1224
- return webContents;
1225
- }
1226
- }
1227
-
1228
- class App {
1229
- static get() { return new Application().getApp() }
1230
- static getSession() { return new Application().getSession() }
1231
- static getWebContents() { return new Application().getWebContents() }
1232
- // ПУТИ
1233
- static homePath() { return new Application().getApp().getPath("home") }
1234
- static appDataPath() { return new Application().getApp().getPath("appData") }
1235
- static userDataPath() { return new Application().getApp().getPath("userData") }
1236
- static sessionDataPath() { return new Application().getApp().getPath("sessionData") }
1237
- static logsPath() { return new Application().getApp().getPath("logs") }
1238
- static tempPath() { return new Application().getApp().getPath("temp") }
1239
- static exePath() { return new Application().getApp().getPath("exe") }
1240
- static modulePath() { return new Application().getApp().getPath("module") }
1241
- static desktopPath() { return new Application().getApp().getPath("desktop") }
1242
- static documentsPath() { return new Application().getApp().getPath("documents") }
1243
- static downloadsPath() { return new Application().getApp().getPath("downloads") }
1244
- static musicPath() { return new Application().getApp().getPath("music") }
1245
- static picturesPath() { return new Application().getApp().getPath("pictures") }
1246
- static videosPath() { return new Application().getApp().getPath("videos") }
1247
- static recentPath() { return new Application().getApp().getPath("recent") }
1248
- static crashDumpsPath() { return new Application().getApp().getPath("crashDumps") }
1249
- }
1250
-
1251
1365
  module.exports = {
1252
1366
  Main: Main,
1253
1367
  sleep: sleep,
@@ -1405,8 +1519,6 @@ module.exports = {
1405
1519
  transliterate: transliterate,
1406
1520
  store: store,
1407
1521
  //
1408
- App: App,
1409
- //
1410
1522
  // Десктоп-сервисы: классы main-процесса и доступ из страницы
1411
1523
  Shortcuts: Shortcuts,
1412
1524
  Shell: Shell,
@@ -1424,10 +1536,13 @@ module.exports = {
1424
1536
  Log: Log,
1425
1537
  chooseFile: {
1426
1538
  // Диалоги: странице — по IPC (окно берётся у отправителя), в main — обычный dialog Electron
1427
- showOpenDialog: (options = {}) => (inRenderer() ? Desktop.dialog.open(options) : dialog.showOpenDialog(options)),
1428
- showSaveDialog: (options = {}) => (inRenderer() ? Desktop.dialog.save(options) : dialog.showSaveDialog(options)),
1429
- showMessageBox: (options = {}) => (inRenderer() ? Desktop.dialog.message(options) : dialog.showMessageBox(options))
1539
+ showOpenDialog: (options = {}) =>
1540
+ inRenderer() ? Desktop.dialog.open(options) : dialog.showOpenDialog(options),
1541
+ showSaveDialog: (options = {}) =>
1542
+ inRenderer() ? Desktop.dialog.save(options) : dialog.showSaveDialog(options),
1543
+ showMessageBox: (options = {}) =>
1544
+ inRenderer() ? Desktop.dialog.message(options) : dialog.showMessageBox(options),
1430
1545
  },
1431
1546
  //
1432
- systemPreferences: systemPreferences
1433
- }
1547
+ systemPreferences: systemPreferences,
1548
+ };