提交 f5f6d766 编写于 作者: aaronchen2k2k's avatar aaronchen2k2k

import monaco editor and electron

上级 33194837
import {app, BrowserWindow} from 'electron';
import Lang from './utils/lang';
import {IS_MAC_OSX, setEntryPath} from './utils/env';
import {logInfo} from './utils/log';
import {getUIServerUrl, killZtfServer} from "./service";
export default class ZtfApp {
constructor(entryPath) {
setEntryPath(entryPath);
this.lang = Lang;
this.bindElectronEvents();
logInfo(`>> ZtfApp: created, entry path is "${entryPath}".`);
}
ready() {
logInfo('>> ZtfApp: ready.');
this.openOrCreateWindow();
// this.buildAppMenu();
}
openOrCreateWindow() {
const {lastFocusedAppWin} = this;
if (lastFocusedAppWin) {
lastFocusedAppWin.showAndFocus();
} else {
this.createWindow();
}
}
showAndFocus() {
logInfo(`>> AppWindow[${this.name}]: show and focus`);
const {browserWindow} = this;
if (browserWindow.isMinimized()) {
browserWindow.restore();
} else {
browserWindow.setOpacity(1);
browserWindow.show();
}
browserWindow.focus();
}
createWindow() {
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
require('@electron/remote/main').initialize()
const mainWindow = new BrowserWindow({
show: false,
webPreferences: {nodeIntegration: true, contextIsolation: false}
})
require("@electron/remote/main").enable(mainWindow.webContents)
mainWindow.maximize()
mainWindow.show()
getUIServerUrl().then((url) => {
mainWindow.loadURL(url);
mainWindow.webContents.openDevTools({mode: 'bottom'});
})
};
bindElectronEvents() {
app.on('window-all-closed', () => {
logInfo(`>> Event window-all-closed`)
app.quit();
});
app.on('quit', () => {
logInfo(`>> Event quit`)
killZtfServer();
});
app.on('activate', () => {
logInfo('>> ElectronApp: activate');
// 在 OS X 系统上,可能存在所有应用窗口关闭了,但是程序还没关闭,此时如果收到激活应用请求需要
// 重新打开应用窗口并创建应用菜单
this.createWindow();
// this.buildAppMenu();
});
}
// buildAppMenu() {
// if (!IS_MAC_OSX) {
// return;
// }
//
// const template = [{
// label: Lang.string('app.title', Config.pkg.displayName),
// submenu: [{
// label: Lang.string('menu.about'),
// selector: 'orderFrontStandardAboutPanel:'
// }, {
// type: 'separator'
// }, {
// label: 'Services',
// submenu: []
// }, {
// type: 'separator'
// }, {
// label: Lang.string('menu.hideCurrentWindow'),
// accelerator: 'Command+H',
// selector: 'hide:'
// }, {
// label: Lang.string('menu.hideOtherWindows'),
// accelerator: 'Command+Shift+H',
// selector: 'hideOtherApplications:'
// }, {
// label: Lang.string('menu.showAllWindows'),
// selector: 'unhideAllApplications:'
// }, {
// type: 'separator'
// }, {
// label: Lang.string('menu.quit'),
// accelerator: 'Command+Q',
// click: () => {
// this.quit();
// }
// }]
// },
// {
// label: Lang.string('menu.edit'),
// submenu: [{
// label: Lang.string('menu.undo'),
// accelerator: 'Command+Z',
// selector: 'undo:'
// }, {
// label: Lang.string('menu.redo'),
// accelerator: 'Shift+Command+Z',
// selector: 'redo:'
// }, {
// type: 'separator'
// }, {
// label: Lang.string('menu.cut'),
// accelerator: 'Command+X',
// selector: 'cut:'
// }, {
// label: Lang.string('menu.copy'),
// accelerator: 'Command+C',
// selector: 'copy:'
// }, {
// label: Lang.string('menu.paste'),
// accelerator: 'Command+V',
// selector: 'paste:'
// }, {
// label: Lang.string('menu.selectAll'),
// accelerator: 'Command+A',
// selector: 'selectAll:'
// }]
// },
// {
// label: Lang.string('menu.view'),
// submenu: (DEBUG) ? [{
// label: Lang.string('menu.reload'),
// accelerator: 'Command+R',
// click: () => {
// this.getLastFocusedWindow({includeChildWindow: true}).browserWindow.webContents.reload();
// }
// }, {
// label: Lang.string('menu.toggleFullscreen'),
// accelerator: 'Ctrl+Command+F',
// click: () => {
// const lastWindow = this.getLastFocusedWindow();
// lastWindow.browserWindow.setFullScreen(!lastWindow.browserWindow.isFullScreen());
// }
// }, {
// label: Lang.string('menu.toggleDeveloperTool'),
// accelerator: 'Alt+Command+I',
// click: () => {
// this.lastFocusedAppWin.browserWindow.toggleDevTools();
// }
// }] : [{
// label: Lang.string('menu.toggleFullscreen'),
// accelerator: 'Ctrl+Command+F',
// click: () => {
// const lastWindow = this.getLastFocusedWindow();
// lastWindow.browserWindow.setFullScreen(!lastWindow.browserWindow.isFullScreen());
// }
// }]
// },
// {
// label: Lang.string('menu.window'),
// submenu: [{
// label: Lang.string('menu.createNewWindow'),
// accelerator: 'Command+N',
// click: () => {
// this.createMainWindow();
// }
// }, {
// label: Lang.string('menu.minimize'),
// accelerator: 'Command+M',
// selector: 'performMiniaturize:'
// }, {
// label: Lang.string('menu.close'),
// accelerator: 'Command+W',
// selector: 'performClose:'
// }, {
// type: 'separator'
// }, {
// label: Lang.string('menu.bringAllToFront'),
// selector: 'arrangeInFront:'
// }]
// },
// {
// label: Lang.string('menu.help'),
// submenu: [{
// label: Lang.string('menu.website'),
// click: () => {
// shell.openExternal(Lang.string('app.homepage', Config.pkg.homepage));
// }
// }, {
// label: Lang.string('menu.community'),
// click() {
// shell.openExternal('https://www.xuanim.com/forum/');
// }
// }]
// }];
//
// const menu = Menu.buildFromTemplate(template);
// Menu.setApplicationMenu(menu);
//
// if (DEBUG) {
// console.log('>> XuanxuanApp: build application menu.');
// }
// }
}
\ No newline at end of file
......@@ -6,8 +6,8 @@ import express from 'express';
const psTree = require('ps-tree');
const { killPortProcess } = require('kill-port-process');
import {portClient, portServer, uuid} from './consts';
import {logInfo, logErr} from './log';
import {portClient, portServer, uuid} from './utils/consts';
import {logInfo, logErr} from './utils/log';
const DEBUG = process.env.NODE_ENV === 'development';
const isWin = /^win/.test(process.platform);
......
/**
* 是否是 Mac OS 系统
* @type {boolean}
* @private
*/
export const IS_MAC_OSX = process.platform === 'darwin';
/**
* 是否是 Windows 系统
* @type {boolean}
* @private
*/
export const IS_WINDOWS_OS = process.platform === 'win32';
/**
* 是否是 Linux 系统
* @type {boolean}
* @private
*/
export const IS_LINUX = process.platform === 'linux';
/**
* 获取Electron 应用入口文件所在目录
* @returns {string} 目录路径
*/
export function getEntryPath() {
return global.entryPath;
}
/**
* 获取Electron 应用入口文件所在目录
* @param {string} entryPath 目录路径
* @return {void}
*/
export function setEntryPath(entryPath) {
global.entryPath = entryPath;
}
import TextMap from './text-map';
import {formatString} from './string';
/**
* 语言访问辅助类
*/
export default class LangHelper extends TextMap {
/**
* 创建一个语言访问辅助类对象
* @param {?String} name 语言名称
* @param {?Map<String, String>} langData 语言文本表对象
* @memberof LangHelper
*/
constructor(name, langData) {
super(langData);
this._name = name;
}
/**
* 变更语言名称和语言数据
* @param {String} name 语言名称
* @param {Map<String, String>} langData 语言文本表对象
* @return {void}
* @memberof LangHelper
*/
change(name, langData) {
this._data = langData;
this._name = name;
}
/**
* 获取语言名称
*
* @readonly
* @memberof LangHelper
* @type {String}
*/
get name() {
return this._name;
}
/**
* 获取错误信息对应的语言文本
*
* @param {string|Error} err 错误信息或错误对象本身
* @return {string} 语言文本
*/
error(err) {
if (!err) {
if (DEBUG) {
console.collapse('LANG.error', 'redBg', '<Unknown Error>', 'redPale');
console.error(err);
console.groupEnd();
}
return '<Unknown Error>';
}
if (typeof err === 'string') {
return this.string(err.startsWith('error.') ? err : `error.${err}`, err);
}
if (Array.isArray(err)) {
return err.map(this.error).join(';');
}
let message = '';
if (err.code) {
message += this.string(`error.${err.code}`, `${err.message || ''}[${err.code}]`);
} else if (err.message) {
message = this.string(`error.${err.message}`, err.message);
}
if (message) {
let formatParams = err.formats || err.extras;
if (formatParams) {
if (typeof formatParams === 'object' && !Array.isArray(formatParams)) {
message = formatString(message, formatParams);
} else {
if (!Array.isArray(formatParams)) {
formatParams = [formatParams];
}
message = formatString(message, ...formatParams);
}
}
}
return message;
}
}
import LangHelper from './lang-helper';
/**
* 语言访问辅助对象
* @type {LangHelper}
*/
const langHelper = new LangHelper();
export default langHelper;
/**
* 格式化字符串
* @param {string} str 要格式化的字符串
* @param {...any} args 格式化参数
* @return {string} 格式化后的字符串
* @example <caption>通过参数序号格式化</caption>
* var hello = $.format('{0} {1}!', 'Hello', 'world');
* // hello 值为 'Hello world!'
* @example <caption>通过对象名称格式化</caption>
* var say = $.format('Say {what} to {who}', {what: 'hello', who: 'you'});
* // say 值为 'Say hello to you'
*/
export const formatString = (str, ...args) => {
let result = str;
if (args.length > 0) {
let reg;
if (args.length === 1 && (typeof args[0] === 'object')) {
args = args[0];
Object.keys(args).forEach(key => {
if (args[key] !== undefined) {
reg = new RegExp(`({${key}})`, 'g');
result = result.replace(reg, args[key]);
}
});
} else {
for (let i = 0; i < args.length; i++) {
if (args[i] !== undefined) {
reg = new RegExp(`({[${i}]})`, 'g');
result = result.replace(reg, args[i]);
}
}
}
}
return result;
};
/**
* 字节单位表
* @type {Object}
*/
export const BYTE_UNITS = {
B: 1,
KB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024,
TB: 1024 * 1024 * 1024 * 1024,
};
/**
* 格式化字节值为包含单位的字符串
* @param {number} size 字节大小
* @param {number} [fixed=2] 保留的小数点尾数
* @param {string} [unit=''] 单位,如果留空,则自动使用最合适的单位
* @return {string} 格式化后的字符串
*/
export const formatBytes = (size, fixed = 2, unit = '') => {
if (typeof size !== 'number') {
size = Number.parseInt(size, 10);
}
if (Number.isNaN(size)) {
return '?KB';
}
if (!unit) {
if (size < BYTE_UNITS.KB) {
unit = 'B';
} else if (size < BYTE_UNITS.MB) {
unit = 'KB';
} else if (size < BYTE_UNITS.GB) {
unit = 'MB';
} else if (size < BYTE_UNITS.TB) {
unit = 'GB';
} else {
unit = 'TB';
}
}
return (size / BYTE_UNITS[unit]).toFixed(fixed) + unit;
};
/**
* 检查字符串是否为未定义(`null` 或者 `undefined`)或者为空字符串
* @param {string} s 要检查的字符串
* @return {boolean} 如果未定义或为空字符串则返回 `true`,否则返回 `false`
*/
export const isEmptyString = s => (s === undefined || s === null || s === '');
/**
* 检查字符串是否不是空字符串
* @param {string} s 要检查的字符串
* @return {boolean} 如果为非空字符串则返回 `true`,否则返回 `false`
*/
export const isNotEmptyString = s => (s !== undefined && s !== null && s !== '');
/**
* 检查字符串是否不是空字符串,如果为空则返回第二个参数给定的字符串,否则返回字符串自身
* @param {string} str 要检查的字符串
* @param {string} thenStr 如果为空字符串时要返回的字符串
* @return {boolean} 如果未定义或为空字符串则返回 [thenStr],否则返回 [str]
*/
export const ifEmptyStringThen = (str, thenStr) => (isEmptyString(str) ? thenStr : str);
/**
* 确保字符串长度不超过指定值,如果超出则去掉截取的部分
* @param {string} str 要操作的字符串
* @param {number} length 要限制的最大长度
* @param {string} suffix 如果超出显示要添加的后缀
* @returns {string} 返回新的字符串
*/
export const limitStringLength = (str, length, suffix) => {
if (str.length > length) {
str = str.substr(0, length);
if (suffix) {
str = `${str}${suffix}`;
}
}
return str;
};
/**
* 用于匹配 @ 用户的正则表达式
* @type {string}
*/
export const REGEXP_AT_USER = '@(#?[_.\\w\\d\\u4e00-\\u9fa5]{1,20})';
/**
* 还原包含 @ 成员的文本消息
* @param {string} message @ 成员消息
* @returns {string} 原是文本
*/
export const restoreMessageContainAt = (message) => {
if (typeof message !== 'string' || !message.replace) {
return message;
}
return message.replace(new RegExp(`\\[(?<atuser>${REGEXP_AT_USER})\\]\\(\\@\\#\\d+\\)`, 'g'), '$<atuser>');
};
export default {
format: formatString,
isEmpty: isEmptyString,
isNotEmpty: isNotEmptyString,
formatBytes,
ifEmptyThen: ifEmptyStringThen,
limitLength: limitStringLength
};
import {formatString} from './string';
/**
* 文本表类
*/
export default class TextMap {
/**
* 创建一个文本表类实例
* @param {Map<String, String>} data 数据类型
* @memberof TextMap
*/
constructor(data) {
this._data = {...data};
}
/**
* 获取数据对象
* @memberof TextMap
* @type {Map<String, String>}
* @readonly
*/
get data() {
return {...this._data};
}
/**
* 获取使用参数格式化的文本
*
* @param {string} name 配置名称
* @param {...any} args 格式化参数
* @return {string} 文本
*/
format(name, ...args) {
const str = this.string(name);
if (str === undefined || !str.length) {
return '';
}
if (args && args.length) {
try {
return formatString(str, ...args);
} catch (e) {
throw new Error(`Cannot format lang string with key '${name}', the lang string is '${str}'.`);
}
}
return str;
}
/**
* 根据配置名称获取文本
* @param {string} name 配置名称
* @param {string} defaultValue 默认文本,如果没有在找到文本则返回此值
* @return {string} 文本
*/
string(name, defaultValue) {
const value = this._data[name];
return value === undefined ? defaultValue : value;
}
}
import {app, BrowserWindow} from 'electron';
import {getUIServerUrl, startZtfServer, killZtfServer} from './service';
import {logInfo, logErr} from './log';
import {app} from 'electron';
import ZtfApp from "./app/app";
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
const createWindow = (url) => {
// Create the browser window.
// const mainWindow = new BrowserWindow({
// width: 1200,
// height: 800,
// });
require('@electron/remote/main').initialize()
const mainWindow = new BrowserWindow({show: false, webPreferences: {nodeIntegration: true, contextIsolation: false}})
require("@electron/remote/main").enable(mainWindow.webContents)
mainWindow.maximize()
mainWindow.show()
// and load the index.html of the app.
mainWindow.loadURL(url);
// Open the DevTools.
mainWindow.webContents.openDevTools({mode: 'bottom'});
};
let _starting = false;
async function startApp() {
if (_starting) {
return;
}
_starting = true;
// try {
// const ztfServerUrl = await startZtfServer();
// logInfo(`>> ZTF Server started successfully: ${ztfServerUrl}`);
// } catch (error) {
// logErr('>> Start ztf server failed: ' + error);
// process.exit(1);
// return;
// }
const url = await getUIServerUrl();
logInfo(`>> UI server url is ${url}`);
createWindow(url);
_starting = false;
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', startApp);
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
logInfo(`>> Event window-all-closed`)
// if (process.platform !== 'darwin') {
app.quit();
// }
});
app.on('before-quit',function(){
logInfo(`>> Event before-quit`)
})
app.on('will-quit',function(){
logInfo(`>> Event will-quit`)
})
app.on('quit',function(){
logInfo(`>> Event quit`)
killZtfServer();
})
app.on('activate', () => {
logInfo(`>> Event activate`)
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
startApp();
}
});
const ztfApp = new ZtfApp(__dirname);
app.on('ready', ztfApp.ready);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册