chuijs 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +29 -6
- package/framework/appLayout/default/chui_app_layout.js +66 -46
- package/framework/appLayout/default/global_style.css +27 -29
- package/framework/components/chui_data_grid/data_grid.js +7 -3
- package/framework/components/chui_notification/notification_update.js +3 -0
- package/framework/components/chui_notification/status_plate.css +7 -0
- package/framework/components/chui_spinner/spinner.js +38 -21
- package/framework/components/chui_spinner/styles.css +34 -28
- package/framework/components/chui_sticky_bar/sticky_bar.js +230 -0
- package/framework/components/chui_sticky_bar/styles.css +37 -0
- package/framework/modules/chui_bridge/preload.js +14 -0
- package/framework/modules/chui_functions.js +22 -6
- package/framework/modules/chui_services/chui_channels.js +33 -2
- package/framework/modules/chui_services/desktop.js +45 -0
- package/index.js +67 -28
- package/package.json +24 -6
package/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// === ИНСТРУМЕНТЫ ===
|
|
2
|
-
const {app, BrowserWindow, Menu, Tray, ipcMain, ipcRenderer, shell, nativeTheme, session, webContents, systemPreferences} = require('electron');
|
|
3
|
-
const { dialog } = require('@electron/remote/main');
|
|
2
|
+
const {app, BrowserWindow, Menu, Tray, ipcMain, ipcRenderer, shell, nativeTheme, session, webContents, systemPreferences, dialog} = require('electron');
|
|
4
3
|
// Одно хранилище настроек на модуль: его получает приложение через `require('chuijs').store`,
|
|
5
4
|
// из него читают тему, язык и история буфера, а `Settings` берёт его для своих значений по умолчанию
|
|
6
5
|
const {store} = require("./framework/modules/chui_store/chui_store");
|
|
@@ -50,6 +49,7 @@ const {Descriptions} = require('./framework/components/chui_descriptions/descrip
|
|
|
50
49
|
const {Statistic} = require('./framework/components/chui_statistic/statistic');
|
|
51
50
|
const {Backtop} = require('./framework/components/chui_backtop/backtop');
|
|
52
51
|
const {Affix} = require('./framework/components/chui_affix/affix');
|
|
52
|
+
const {StickyBar} = require('./framework/components/chui_sticky_bar/sticky_bar');
|
|
53
53
|
const {Watermark} = require('./framework/components/chui_watermark/watermark');
|
|
54
54
|
const {ShortcutHelp} = require('./framework/components/chui_shortcut_help/shortcut_help');
|
|
55
55
|
const {Tooltip} = require('./framework/components/chui_tooltip/tooltip');
|
|
@@ -78,7 +78,7 @@ const {CodeBlock} = require('./framework/components/chui_code/code')
|
|
|
78
78
|
const {HtmlBlock} = require('./framework/components/chui_html_block/html_block')
|
|
79
79
|
const {sleep, render, getDefaultIcon, formatBytes} = require('./framework/modules/chui_functions');
|
|
80
80
|
const {Shortcuts, Clipboard, RecentDocuments, TaskbarProgress} = require('./framework/modules/chui_services/chui_services');
|
|
81
|
-
const {SERVICES_CHANNELS, BRIDGE_CHANNELS, BRIDGE_ARGS} = require('./framework/modules/chui_services/chui_channels');
|
|
81
|
+
const {SERVICES_CHANNELS, BRIDGE_CHANNELS, BRIDGE_ARGS, APP_PATH_NAMES} = require('./framework/modules/chui_services/chui_channels');
|
|
82
82
|
const {Desktop} = require('./framework/modules/chui_services/desktop');
|
|
83
83
|
const {I18n} = require('./framework/modules/chui_i18n/chui_i18n');
|
|
84
84
|
const {Shell, Theme, Locale, ClipboardHistory: ClipboardHistoryService, Print} = require('./framework/modules/chui_services/chui_services');
|
|
@@ -150,6 +150,11 @@ const {QRCode} = require("./framework/components/chui_qrcode/qrcode");
|
|
|
150
150
|
const {Log} = require("./framework/modules/chui_logger/chui_logger");
|
|
151
151
|
const url = require("node:url");
|
|
152
152
|
|
|
153
|
+
/** Код исполняется на странице (renderer), а не в main: от этого зависит доступ к модулям Electron */
|
|
154
|
+
function inRenderer() {
|
|
155
|
+
return process !== undefined && process.type === "renderer";
|
|
156
|
+
}
|
|
157
|
+
|
|
153
158
|
//VARS
|
|
154
159
|
let isQuiting = false;
|
|
155
160
|
|
|
@@ -236,6 +241,7 @@ class Main {
|
|
|
236
241
|
#security = {contextIsolation: false, csp: false, channels: []};
|
|
237
242
|
#secure_contents = new Set();
|
|
238
243
|
#bridge_enabled = false;
|
|
244
|
+
#window_actions_bound = false;
|
|
239
245
|
#theme_bound = false;
|
|
240
246
|
#page_message_handlers = new Map();
|
|
241
247
|
#onUncaughtException = (error) => {
|
|
@@ -431,7 +437,6 @@ class Main {
|
|
|
431
437
|
plugins: false,
|
|
432
438
|
nodeIntegration: false,
|
|
433
439
|
contextIsolation: true,
|
|
434
|
-
enableRemoteModule: false,
|
|
435
440
|
sandbox: false,
|
|
436
441
|
preload: Main.SECURE_PRELOAD,
|
|
437
442
|
additionalArguments: Main.#bridgeArguments(options.render, security),
|
|
@@ -448,7 +453,6 @@ class Main {
|
|
|
448
453
|
preload: options.render === undefined ? this.#renderer : options.render,
|
|
449
454
|
spellcheck: false,
|
|
450
455
|
webviewTag: true,
|
|
451
|
-
enableRemoteModule: true,
|
|
452
456
|
webSecurity: this.#webSecurity,
|
|
453
457
|
nodeIntegrationInSubFrames: true,
|
|
454
458
|
},
|
|
@@ -627,6 +631,9 @@ class Main {
|
|
|
627
631
|
enableServices() {
|
|
628
632
|
if (this.#services_enabled) return this;
|
|
629
633
|
this.#services_enabled = true;
|
|
634
|
+
// Кнопки окна: каналы нужны и обычным окнам — в них `AppLayout` зовёт их по IPC,
|
|
635
|
+
// потому что `@electron/remote` в рендерере больше нет
|
|
636
|
+
this.#enableWindowActions();
|
|
630
637
|
this.services = {
|
|
631
638
|
shortcuts: new Shortcuts(),
|
|
632
639
|
shell: new Shell(),
|
|
@@ -686,6 +693,25 @@ class Main {
|
|
|
686
693
|
this.services.clipboard.clear();
|
|
687
694
|
return true;
|
|
688
695
|
});
|
|
696
|
+
// Приложение и системные диалоги для страницы: Electron советует IPC вместо `remote`.
|
|
697
|
+
// Путь — только из белого списка, диалог и сессия — у окна-отправителя.
|
|
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
|
+
});
|
|
702
|
+
this.#handle(SERVICES_CHANNELS.APP_INFO, () => ({
|
|
703
|
+
name: app.getName(),
|
|
704
|
+
version: app.getVersion(),
|
|
705
|
+
locale: app.getLocale(),
|
|
706
|
+
systemLocale: app.getSystemLocale(),
|
|
707
|
+
isPackaged: app.isPackaged
|
|
708
|
+
}));
|
|
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));
|
|
712
|
+
this.#handle(SERVICES_CHANNELS.SESSION_CACHE_SIZE, (event) => event.sender.session.getCacheSize());
|
|
713
|
+
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));
|
|
689
715
|
this.#handle(SERVICES_CHANNELS.RECENT_ADD, (event, path, name) => {
|
|
690
716
|
this.services.recent.add(path, name);
|
|
691
717
|
return true;
|
|
@@ -745,9 +771,19 @@ class Main {
|
|
|
745
771
|
* Включает мост безопасных окон (`contextBridge`): управление окном, тема и прикладные
|
|
746
772
|
* сообщения по белому списку каналов. Вызывается сам при создании безопасного окна.
|
|
747
773
|
*/
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
774
|
+
/** Диалог в окне отправителя: без окна (уже закрыто) Electron зовётся без родителя */
|
|
775
|
+
static #showDialog(event, method = String(), options = Object()) {
|
|
776
|
+
const window = BrowserWindow.fromWebContents(event.sender);
|
|
777
|
+
return window === null ? dialog[method](options) : dialog[method](window, options);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Каналы кнопок окна. Ими пользуются оба режима: безопасное окно — через мост
|
|
782
|
+
* `window.chui.window`, обычное — прямым `ipcRenderer.invoke`. Регистрируются один раз на приложение.
|
|
783
|
+
*/
|
|
784
|
+
#enableWindowActions() {
|
|
785
|
+
if (this.#window_actions_bound) return this;
|
|
786
|
+
this.#window_actions_bound = true;
|
|
751
787
|
this.#handle(BRIDGE_CHANNELS.WINDOW_ACTION, (event, action) => Main.#applyWindowAction(event.sender, action));
|
|
752
788
|
this.#handle(BRIDGE_CHANNELS.WINDOW_STATE, (event) => {
|
|
753
789
|
const window = BrowserWindow.fromWebContents(event.sender);
|
|
@@ -759,6 +795,13 @@ class Main {
|
|
|
759
795
|
isFocused: window.isFocused()
|
|
760
796
|
};
|
|
761
797
|
});
|
|
798
|
+
return this;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
enableBridge() {
|
|
802
|
+
if (this.#bridge_enabled) return this;
|
|
803
|
+
this.#bridge_enabled = true;
|
|
804
|
+
this.#enableWindowActions();
|
|
762
805
|
this.#handle(BRIDGE_CHANNELS.THEME_GET, () => (nativeTheme.shouldUseDarkColors ? "dark" : "light"));
|
|
763
806
|
this.#handle(BRIDGE_CHANNELS.LOCALE_GET, () => this.services.locale.get());
|
|
764
807
|
this.#handle(BRIDGE_CHANNELS.MESSAGE, (event, channel, ...args) => {
|
|
@@ -790,7 +833,7 @@ class Main {
|
|
|
790
833
|
return this.#send(BRIDGE_CHANNELS.MESSAGE, [channel, payload], windowName);
|
|
791
834
|
}
|
|
792
835
|
|
|
793
|
-
/** Действия кнопок
|
|
836
|
+
/** Действия кнопок окна: и обычные окна, и безопасные зовут их по IPC */
|
|
794
837
|
static #applyWindowAction(sender, action = String()) {
|
|
795
838
|
const window = BrowserWindow.fromWebContents(sender);
|
|
796
839
|
if (window === null || window.isDestroyed()) return false;
|
|
@@ -1021,6 +1064,7 @@ class Main {
|
|
|
1021
1064
|
this.#on_theme_updated = undefined;
|
|
1022
1065
|
this.#page_message_handlers.clear();
|
|
1023
1066
|
this.#bridge_enabled = false;
|
|
1067
|
+
this.#window_actions_bound = false;
|
|
1024
1068
|
if (this.#updaterListening) {
|
|
1025
1069
|
this.#updaterListening = false;
|
|
1026
1070
|
require("electron-updater").autoUpdater.removeListener('update-downloaded', this.#onUpdateDownloaded);
|
|
@@ -1074,9 +1118,7 @@ class Main {
|
|
|
1074
1118
|
Menu.setApplicationMenu(null)
|
|
1075
1119
|
}
|
|
1076
1120
|
this.enableServices();
|
|
1077
|
-
|
|
1078
|
-
require("@electron/remote/main").initialize();
|
|
1079
|
-
require("@electron/remote/main").enable(window.webContents);
|
|
1121
|
+
this.#buildWindow({name: Main.MAIN_WINDOW, hideOnClose: options.hideOnClose});
|
|
1080
1122
|
})
|
|
1081
1123
|
}
|
|
1082
1124
|
|
|
@@ -1170,25 +1212,16 @@ class Styles {
|
|
|
1170
1212
|
|
|
1171
1213
|
class Application {
|
|
1172
1214
|
getApp() {
|
|
1173
|
-
if (
|
|
1174
|
-
|
|
1175
|
-
} else {
|
|
1176
|
-
return app;
|
|
1177
|
-
}
|
|
1215
|
+
if (inRenderer()) throw new Error("App.get(): странице не отдают модули main — возьмите Desktop.app.path()/info() или свой канал из security.channels");
|
|
1216
|
+
return app;
|
|
1178
1217
|
}
|
|
1179
1218
|
getSession() {
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
} else {
|
|
1183
|
-
return session;
|
|
1184
|
-
}
|
|
1219
|
+
if (inRenderer()) throw new Error("App.getSession(): странице не отдают сессию main — возьмите Desktop.session.*");
|
|
1220
|
+
return session;
|
|
1185
1221
|
}
|
|
1186
1222
|
getWebContents() {
|
|
1187
|
-
if (
|
|
1188
|
-
|
|
1189
|
-
} else {
|
|
1190
|
-
return webContents;
|
|
1191
|
-
}
|
|
1223
|
+
if (inRenderer()) throw new Error("App.getWebContents(): странице не отдают webContents main — действия идут через Desktop или свой канал из security.channels");
|
|
1224
|
+
return webContents;
|
|
1192
1225
|
}
|
|
1193
1226
|
}
|
|
1194
1227
|
|
|
@@ -1246,6 +1279,7 @@ module.exports = {
|
|
|
1246
1279
|
Statistic: Statistic,
|
|
1247
1280
|
Backtop: Backtop,
|
|
1248
1281
|
Affix: Affix,
|
|
1282
|
+
StickyBar: StickyBar,
|
|
1249
1283
|
Watermark: Watermark,
|
|
1250
1284
|
ShortcutHelp: ShortcutHelp,
|
|
1251
1285
|
Alert: Alert,
|
|
@@ -1388,7 +1422,12 @@ module.exports = {
|
|
|
1388
1422
|
//
|
|
1389
1423
|
downloadSession: downloadSession,
|
|
1390
1424
|
Log: Log,
|
|
1391
|
-
chooseFile:
|
|
1425
|
+
chooseFile: {
|
|
1426
|
+
// Диалоги: странице — по 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))
|
|
1430
|
+
},
|
|
1392
1431
|
//
|
|
1393
1432
|
systemPreferences: systemPreferences
|
|
1394
1433
|
}
|
package/package.json
CHANGED
|
@@ -2,20 +2,35 @@
|
|
|
2
2
|
"name": "chuijs",
|
|
3
3
|
"productName": "chuijs",
|
|
4
4
|
"description": "component framework chUiJS",
|
|
5
|
-
"version": "4.0.
|
|
5
|
+
"version": "4.0.1",
|
|
6
6
|
"private": false,
|
|
7
7
|
"main": "index.js",
|
|
8
|
+
"type": "commonjs",
|
|
8
9
|
"author": {
|
|
9
10
|
"name": "saintdizen",
|
|
10
11
|
"email": "saintdizen@gmail.com"
|
|
11
12
|
},
|
|
12
13
|
"homepage": "https://dethmond.gitverse.site/chuijs",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://gitverse.ru/DethMond/chuijs.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://gitverse.ru/DethMond/chuijs/issues"
|
|
20
|
+
},
|
|
13
21
|
"files": [
|
|
14
22
|
"framework/**/*",
|
|
15
23
|
"index.js",
|
|
16
24
|
"package.json",
|
|
17
|
-
"README.md"
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md"
|
|
18
27
|
],
|
|
28
|
+
"exports": {
|
|
29
|
+
".": "./index.js",
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
"./framework/*": "./framework/*.js",
|
|
32
|
+
"./framework/*.js": "./framework/*.js"
|
|
33
|
+
},
|
|
19
34
|
"keywords": [
|
|
20
35
|
"Electron",
|
|
21
36
|
"Framework",
|
|
@@ -30,18 +45,21 @@
|
|
|
30
45
|
"format": "prettier --write .",
|
|
31
46
|
"format:check": "prettier --check ."
|
|
32
47
|
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
33
51
|
"license": "MIT",
|
|
34
52
|
"dependencies": {
|
|
35
53
|
"@cypress/request": "~4.0.1",
|
|
36
|
-
"@electron/remote": "~2.1.3",
|
|
37
|
-
"dataurl": "~0.1.0",
|
|
38
54
|
"electron": "~44.4.5",
|
|
39
55
|
"electron-store": "~8.2.0",
|
|
40
56
|
"electron-updater": "~6.8.9",
|
|
41
57
|
"image-size": "~2.0.4",
|
|
58
|
+
"markdown-it": "^15.0.2",
|
|
59
|
+
"markdown-it-task-lists": "^2.1.1",
|
|
42
60
|
"randomstring": "~1.3.0",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
61
|
+
"transliteration": "~2.6.1",
|
|
62
|
+
"turndown": "^7.2.4"
|
|
45
63
|
},
|
|
46
64
|
"devDependencies": {
|
|
47
65
|
"@eslint/js": "^10.0.1",
|