[add] first
This commit is contained in:
commit
3e45d31437
60
README.md
Normal file
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Cocos Creater OutputComponent
|
||||
|
||||
## 插件 安裝方式
|
||||
|
||||
1. 安裝在 **項目** 底下
|
||||
> 把插件資料夾放到項目的packages(跟assets同層)
|
||||
|
||||
2. 安裝在 **全局** 底下
|
||||
> 把插件資料夾放到C:\Users\%USERNAME%\.CocosCreator\packages
|
||||
|
||||
## 插件稍微說明(都是搬過來的資料 XD)
|
||||
|
||||
剩下沒寫的可以到參考資料裡面看看😀
|
||||
|
||||
1. 定義你的包描述文件:**package.json**
|
||||
> **name** String - 定義了包的名字,包的名字是全局唯一的,他關係到你今後在官網服務器上登錄時的名字。
|
||||
>
|
||||
> **version** String - 版本號,我們推薦使用semver格式管理你的包版本。
|
||||
>
|
||||
> **description** String(可选) - 一句話描述你的包是做什麼的。
|
||||
>
|
||||
> **author** String(可选) - 擴展包的作者
|
||||
>
|
||||
> **main** String (可选) - 入口程序
|
||||
>
|
||||
> **scene-script** String (可选) - 調用引擎API 和項目腳本
|
||||
>
|
||||
> **main-menu** Object (可选) - 主菜單定義
|
||||
>
|
||||
> **有要使用介面的話:**
|
||||
>
|
||||
> **panel** Object (可选) - 定義的面板在package裡的描述
|
||||
>
|
||||
> **注意panel的type有兩種:**
|
||||
>
|
||||
> dockable:可停靠面板,打開該面板後,可以通過拖拽面板標籤到編輯器裡,實現擴展面板嵌入到編輯器中。下面我們介紹的面板入口程序都是按照可停靠面板的要求聲明的。
|
||||
>
|
||||
> simple:簡單Web面板,不可停靠到編輯器主窗口,相當於一份通用的HTML前端頁面。詳細情況請見定義簡單面板。
|
||||
>
|
||||
> 在simple-package文件夾下面創建一個panel文件夾,然後在panel文件夾下創建一個index.js或者一個html文件都可以
|
||||
|
||||
2. 入口程序:**main.js**
|
||||
|
||||
3. 定義介面以及按鈕綁定的方法,和主進程的通信:**index.js**
|
||||
|
||||
3. 可以使用包括全部引擎API 和用戶組件腳本里聲明的方法和屬性:**scene-obtain..js**
|
||||
> 可以在擴展包中獲取到場景裡的Canvas根節點有多少子節點,當然還可以用來對場景節點進行更多的查詢和操作。
|
||||
|
||||
## 參考資料
|
||||
|
||||
* 你的第一個擴展包
|
||||
> https://docs.cocos.com/creator/manual/zh/extension/your-first-extension.html
|
||||
|
||||
* CocosCreator拓展编辑器
|
||||
> https://blog.csdn.net/qq_34772097/category_9577457.html
|
||||
|
||||
* Cocos Creator Editor 編輯器擴展API記錄
|
||||
> https://blog.csdn.net/kingbook928/article/details/108659319
|
||||
> https://blog.csdn.net/qq_17209641/article/details/106822296
|
||||
> https://forum.cocos.org/t/creator-api/92605
|
136
core/FileUtil.js
Normal file
136
core/FileUtil.js
Normal file
@ -0,0 +1,136 @@
|
||||
let fs = require("fire-fs");
|
||||
let path = require("fire-path");
|
||||
|
||||
let self = module.exports = {
|
||||
getDirAllFiles(dirPath, result) {
|
||||
let files = fs.readdirSync(dirPath);
|
||||
files.forEach((val, index) => {
|
||||
let fPath = path.join(dirPath, val);
|
||||
let stats = fs.statSync(fPath);
|
||||
if (stats.isDirectory()) {
|
||||
this.getDirAllFiles(fPath, result);
|
||||
} else if (stats.isFile()) {
|
||||
result.push(fPath);
|
||||
}
|
||||
});
|
||||
},
|
||||
getFileString(fileList, options) {
|
||||
let curIndex = 0;
|
||||
let totalIndex = fileList.length;
|
||||
let str = {};
|
||||
for (let key in fileList) {
|
||||
let filePath = fileList[key];
|
||||
let b = this._isFileExit(filePath);
|
||||
if (b) {
|
||||
fs.readFile(filePath, 'utf-8', function (err, data) {
|
||||
if (!err) {
|
||||
self._collectString(data, str);
|
||||
} else {
|
||||
console.log("error: " + filePath);
|
||||
}
|
||||
self._onCollectStep(filePath, ++curIndex, totalIndex, str, options);
|
||||
})
|
||||
} else {
|
||||
self._onCollectStep(filePath, ++curIndex, totalIndex, str, options);
|
||||
}
|
||||
}
|
||||
},
|
||||
_onCollectStep(filePath, cur, total, str, data) {
|
||||
if (data && data.stepCb) {
|
||||
data.stepCb(filePath, cur, total);
|
||||
}
|
||||
if (cur >= total) {
|
||||
self._onCollectOver(str, data);
|
||||
}
|
||||
},
|
||||
_onCollectOver(collectObjArr, data) {
|
||||
let strArr = [];
|
||||
let str = "";
|
||||
for (let k in collectObjArr) {
|
||||
str += k;
|
||||
strArr.push(k);
|
||||
}
|
||||
// console.log("一共有" + strArr.length + "个字符, " + strArr);
|
||||
console.log("一共有" + strArr.length + "个字符");
|
||||
if (data && data.compCb) {
|
||||
data.compCb(str);
|
||||
}
|
||||
},
|
||||
mkDir(path) {
|
||||
try {
|
||||
fs.mkdirSync(path);
|
||||
} catch (e) {
|
||||
if (e.code !== 'EEXIST') throw e;
|
||||
}
|
||||
},
|
||||
isFileExit(file) {
|
||||
try {
|
||||
fs.accessSync(file, fs.F_OK);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
_collectString(data, collectObject) {
|
||||
for (let i in data) {
|
||||
let char = data.charAt(i);
|
||||
if (collectObject[char]) {
|
||||
collectObject[char]++;
|
||||
} else {
|
||||
collectObject[char] = 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
emptyDir(rootFile) {
|
||||
//删除所有的文件(将所有文件夹置空)
|
||||
let emptyDir = function (fileUrl) {
|
||||
let files = fs.readdirSync(fileUrl);//读取该文件夹
|
||||
for (let k in files) {
|
||||
let filePath = path.join(fileUrl, files[k]);
|
||||
let stats = fs.statSync(filePath);
|
||||
if (stats.isDirectory()) {
|
||||
emptyDir(filePath);
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
console.log("删除文件:" + filePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
//删除所有的空文件夹
|
||||
let rmEmptyDir = function (fileUrl) {
|
||||
let files = fs.readdirSync(fileUrl);
|
||||
if (files.length > 0) {
|
||||
for (let k in files) {
|
||||
let rmDir = path.join(fileUrl, files[k]);
|
||||
rmEmptyDir(rmDir);
|
||||
}
|
||||
if (fileUrl !== rootFile) {// 不删除根目录
|
||||
fs.rmdirSync(fileUrl);
|
||||
console.log('删除空文件夹' + fileUrl);
|
||||
}
|
||||
} else {
|
||||
if (fileUrl !== rootFile) {// 不删除根目录
|
||||
fs.rmdirSync(fileUrl);
|
||||
console.log('删除空文件夹' + fileUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
emptyDir(rootFile);
|
||||
rmEmptyDir(rootFile);
|
||||
},
|
||||
/*
|
||||
is_fileType($('#uploadfile').val(), 'doc,pdf,txt,wps,odf,md,png,gif,jpg')
|
||||
* */
|
||||
is_fileType(filename, types) {
|
||||
types = types.split(',');
|
||||
let pattern = '\.(';
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
if (0 !== i) {
|
||||
pattern += '|';
|
||||
}
|
||||
pattern += types[i].trim();
|
||||
}
|
||||
pattern += ')$';
|
||||
return new RegExp(pattern, 'i').test(filename);
|
||||
}
|
||||
}
|
55
i18n/en.js
Normal file
55
i18n/en.js
Normal file
@ -0,0 +1,55 @@
|
||||
module.exports = {
|
||||
'name': 'References Finder',
|
||||
'find': 'Find Current Selected',
|
||||
'find-panel': 'Find Panel',
|
||||
'settings': 'Settings',
|
||||
'check-update': 'Check Update',
|
||||
// update
|
||||
'current-latest': 'Currently the latest version!',
|
||||
'has-new-version': 'New version found!',
|
||||
'local-version': 'Local version: ',
|
||||
'latest-version': 'Latest version: ',
|
||||
'git-releases': 'Releases: https://gitee.com/ifaswind/ccc-references-finder/releases',
|
||||
'cocos-store': 'Cocos Store: https://store.cocos.com/app/detail/2531',
|
||||
// main
|
||||
'please-select-assets': 'Please select asset(s) in Asset Panel first',
|
||||
'invalid-uuid': 'Invalid uuid',
|
||||
'not-support-folder': 'Does not support folder',
|
||||
'find-asset-refs': 'Find references',
|
||||
'no-refs': 'No references found',
|
||||
'scene': 'Scene',
|
||||
'prefab': 'Prefab',
|
||||
'animation': 'Animation',
|
||||
'material': 'Material',
|
||||
'font': 'Font',
|
||||
'node': 'Node',
|
||||
'component': 'Component',
|
||||
'property': 'Property',
|
||||
'result': 'Reference result',
|
||||
'node-refs': 'Node References',
|
||||
'asset-refs': 'Asset References',
|
||||
'asset-info': 'Asset Info',
|
||||
'asset-type': 'Type: ',
|
||||
'asset-uuid': 'Uuid: ',
|
||||
'asset-url': 'Url: ',
|
||||
'asset-path': 'Path: ',
|
||||
// settings
|
||||
'none': 'None',
|
||||
'select-key': 'Hotkey',
|
||||
'select-key-tooltip': 'Chose a hotkey to open the search bar quickly',
|
||||
'custom-key': 'Custom',
|
||||
'custom-key-placeholder': 'Choose a hotkey above or customize one by yourself',
|
||||
'custom-key-tooltip': 'You can also customize your own hotkey',
|
||||
'auto-check-update': 'Auto Check Update',
|
||||
'auto-check-update-tooltip': 'Check if there is a new version when the extension is loaded',
|
||||
'reference': '· Hotkey customization reference: ',
|
||||
'accelerator': 'Keyboard Shortcuts',
|
||||
'repository': '· Git repository of this extension: ',
|
||||
'apply': 'Apply',
|
||||
'quote-error': 'Do not use double quotes!',
|
||||
'custom-key-error': 'Please specify a hotkey!',
|
||||
'print-details': 'Show Details',
|
||||
'print-details-tooltip': 'Show more details(node, component, property)',
|
||||
'print-folding': 'Fold Result',
|
||||
'print-folding-tooltip': 'Fold result in one log',
|
||||
};
|
55
i18n/zh.js
Normal file
55
i18n/zh.js
Normal file
@ -0,0 +1,55 @@
|
||||
module.exports = {
|
||||
'name': '引用查找器',
|
||||
'find': '查找当前选中资源',
|
||||
'find-panel': '查找面板',
|
||||
'settings': '设置',
|
||||
'check-update': '检查更新',
|
||||
// update
|
||||
'current-latest': '当前已是最新版本!',
|
||||
'has-new-version': '发现新版本!',
|
||||
'local-version': '本地版本:',
|
||||
'latest-version': '最新版本:',
|
||||
'git-releases': '发行版:https://gitee.com/ifaswind/ccc-references-finder/releases',
|
||||
'cocos-store': 'Cocos 商店:https://store.cocos.com/app/detail/2531',
|
||||
// main
|
||||
'please-select-assets': '请先在资源管理器中选择需要查找引用的资源',
|
||||
'invalid-uuid': '无效的 uuid',
|
||||
'not-support-folder': '暂不支持查找文件夹',
|
||||
'find-asset-refs': '查找资源引用',
|
||||
'no-refs': '没有找到该资源的引用',
|
||||
'scene': '场景',
|
||||
'prefab': '预制体',
|
||||
'animation': '动画',
|
||||
'material': '材质',
|
||||
'font': '字体',
|
||||
'node': '节点',
|
||||
'component': '组件',
|
||||
'property': '属性',
|
||||
'result': '引用查找结果',
|
||||
'node-refs': '节点引用',
|
||||
'asset-refs': '资源引用',
|
||||
'asset-info': '资源信息',
|
||||
'asset-type': 'Type:',
|
||||
'asset-uuid': 'Uuid:',
|
||||
'asset-url': 'Url:',
|
||||
'asset-path': 'Path:',
|
||||
// settings
|
||||
'none': '无',
|
||||
'select-key': '快捷键',
|
||||
'select-key-tooltip': '选择一个快速打开搜索栏的快捷键',
|
||||
'custom-key': '自定义',
|
||||
'custom-key-placeholder': '在上方选择一个快捷键或自定义一个快捷键',
|
||||
'custom-key-tooltip': '自定义快速打开搜索栏的快捷键',
|
||||
'auto-check-update': '自动检查更新',
|
||||
'auto-check-update-tooltip': '扩展启动时自动检查是否有新版本',
|
||||
'reference': '· 快捷键自定义请参考:',
|
||||
'accelerator': '键盘快捷键',
|
||||
'repository': '· 本扩展的 Git 仓库:',
|
||||
'apply': '应用',
|
||||
'quote-error': '请勿使用双引号!',
|
||||
'custom-key-error': '请指定一个快捷键!',
|
||||
'print-details': '展示详情',
|
||||
'print-details-tooltip': '引用查找结果精确到节点、组件和属性',
|
||||
'print-folding': '折叠结果',
|
||||
'print-folding-tooltip': '引用查找结果将需要手动展开,拯救你的控制台',
|
||||
};
|
1649
lib/node-fetch.js
Normal file
1649
lib/node-fetch.js
Normal file
File diff suppressed because it is too large
Load Diff
7
lib/vue.global.prod.js
Normal file
7
lib/vue.global.prod.js
Normal file
File diff suppressed because one or more lines are too long
174
main.js
Normal file
174
main.js
Normal file
@ -0,0 +1,174 @@
|
||||
"use strict";
|
||||
let fs = require("fs");
|
||||
let path = require("path");
|
||||
const FileUtil = require('./core/FileUtil');
|
||||
const { print, translate } = require('./src/eazax/editor-main-util');
|
||||
const EditorAPI = require('./src/main/editor-api');
|
||||
const Parser = require('./src/main/parser');
|
||||
const Finder = require('./src/main/finder');
|
||||
const Printer = require('./src/main//printer');
|
||||
|
||||
module.exports = {
|
||||
load() {
|
||||
this.init();
|
||||
},
|
||||
unload() {
|
||||
// Editor.log("卸載執行");
|
||||
},
|
||||
/** 初始化 */
|
||||
init() {
|
||||
// this.createDirectory();
|
||||
},
|
||||
/** 創建Spine節點 */
|
||||
sourcecheck(...args) {
|
||||
let self = this;
|
||||
try {
|
||||
// Editor.log("文件1: " + JSON.stringify(args[0]._value));
|
||||
Editor.Scene.callSceneScript("sourcecheck", "getFsPath", { args: args }, async function (err, response) {
|
||||
// Editor.log("getFsPath response: " + response);
|
||||
let res = JSON.parse(response);
|
||||
if (!FileUtil.isFileExit(res.fsPath)) {
|
||||
Editor.error(`路徑錯誤: ${res.fsPath}`);
|
||||
Editor.log(`檢查停止`);
|
||||
return;
|
||||
}
|
||||
Editor.log(`檢查路徑: ${res.url}`);
|
||||
let files = [];
|
||||
files = self.fileSearch(res.fsPath, res.url, files);
|
||||
// Editor.log(`總共有${files.length}個物件`);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
// for (let i = 0; i < 1; i++) {
|
||||
await self.checkreq(files[i][0], files[i][1]);
|
||||
}
|
||||
Editor.log(`檢查完畢`);
|
||||
});
|
||||
} catch (error) {
|
||||
Editor.log(`sourcecheck error: ${error}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fsPath fsPath
|
||||
*/
|
||||
fileSearch(fsPath, url, files) {
|
||||
try {
|
||||
// Editor.log(`fsPath: ${fsPath}`);
|
||||
if (FileUtil.isFileExit(fsPath)) {
|
||||
fs.readdirSync(fsPath).forEach(file => {
|
||||
// Editor.log(`file: ${file}`);
|
||||
let path = `${fsPath}\\${file}`
|
||||
if (this.checkfile(path)) {
|
||||
files = this.fileSearch(path, url, files);
|
||||
} else if (this.checkusefile(path)) {
|
||||
// this.checkreq(path, url);
|
||||
files.push([path, url])
|
||||
}
|
||||
});
|
||||
return files;
|
||||
} else {
|
||||
// Editor.error(`fsPath error: ${fsPath}`);
|
||||
return files;
|
||||
}
|
||||
} catch (error) {
|
||||
Editor.error(`fileSearch error: ${error}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 檢查引用
|
||||
* @param fsPath fsPath
|
||||
*/
|
||||
async checkreq(fsPath, myurl) {
|
||||
// Editor.log(`file: ${fsPath}`);
|
||||
try {
|
||||
let uuid = EditorAPI.fspathToUuid(fsPath);
|
||||
// Editor.log(`uuid: ${uuid}, file: ${fsPath}`);
|
||||
|
||||
const assetInfo = EditorAPI.assetInfoByUuid(uuid),
|
||||
shortUrl = assetInfo.url.replace('db://', '');
|
||||
// 查找引用
|
||||
// print('log', '🔍', `${translate('find-asset-refs')} ${shortUrl}`);
|
||||
let refs = null;
|
||||
try {
|
||||
refs = await Finder.findByUuid(uuid);
|
||||
} catch (error) {
|
||||
Editor.error(`findByUuid error: ${error}, fsPath: ${fsPath}`);
|
||||
return;
|
||||
}
|
||||
// Editor.log(`refs: ${JSON.stringify(refs)}`);
|
||||
|
||||
let newrefs = [];
|
||||
for (let i = 0; i < refs.length; i++) {
|
||||
let ref = refs[i];
|
||||
let url = ref.url;
|
||||
if (url.indexOf(myurl) === -1) {
|
||||
// Editor.log(`url: ${url}, myurl: ${myurl}, fsPath: ${EditorAPI.fspathToUrl(fsPath)}`);
|
||||
newrefs.push(ref);
|
||||
}
|
||||
}
|
||||
if (newrefs.length > 0) {
|
||||
// 打印结果
|
||||
Printer.printResult({
|
||||
type: assetInfo.type,
|
||||
uuid: uuid,
|
||||
url: assetInfo.url,
|
||||
path: assetInfo.path,
|
||||
refs: newrefs,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
Editor.error(`checkreq error: ${error}, fsPath: ${fsPath}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 檢查副檔名
|
||||
* @param fsPath fsPath
|
||||
*/
|
||||
checkusefile(fsPath) {
|
||||
try {
|
||||
if ((fsPath.indexOf(".PNG") !== -1 || fsPath.indexOf(".png") !== -1 || fsPath.indexOf(".jpg") !== -1 || fsPath.indexOf(".anim") !== -1 || fsPath.indexOf(".fnt") !== -1) && fsPath.indexOf(".meta") === -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
Editor.error(`checkfile error: ${error}`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 檢查副檔名
|
||||
* @param fsPath fsPath
|
||||
*/
|
||||
checkfile(fsPath) {
|
||||
try {
|
||||
if (fsPath.indexOf(".fire") === -1 && fsPath.indexOf(".fnt") === -1 && fsPath.indexOf(".anim") === -1 && fsPath.indexOf(".prefab") === -1 && fsPath.indexOf(".meta") === -1 && fsPath.indexOf(".pac") === -1 && fsPath.indexOf(".PNG") === -1 && fsPath.indexOf(".png") === -1 && fsPath.indexOf(".jpg") === -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
Editor.error(`checkfile error: ${error}`);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
/** 打開面板 */
|
||||
"open-panel"() {
|
||||
Editor.Panel.open("sourcecheck");
|
||||
},
|
||||
/** 保存按鈕點擊 */
|
||||
"run-click"(event, ...args) {
|
||||
this.sourcecheck(...args);
|
||||
},
|
||||
// /** 面板加載完成 */
|
||||
// "panel-load-finish"(evnet, ...args) {
|
||||
// Editor.Scene.callSceneScript("sourcecheck", "get-default-info", { args: args }, function (err, response) {
|
||||
// // Editor.log("callSceneScript: " + response);
|
||||
// Editor.Ipc.sendToPanel("sourcecheck", "setDefault", response);
|
||||
// });
|
||||
// },
|
||||
},
|
||||
|
||||
}
|
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "sourcecheck",
|
||||
"version": "0.0.1",
|
||||
"description": "Sample-Package",
|
||||
"author": "Sample",
|
||||
"main": "main.js",
|
||||
"scene-script": "scene-obtain.js",
|
||||
"engineSupport": true,
|
||||
"main-menu": {
|
||||
"扩展/Source Check": {
|
||||
"message": "sourcecheck:open-panel"
|
||||
}
|
||||
},
|
||||
"panel": {
|
||||
"main": "panel/index.js",
|
||||
"type": "dockable",
|
||||
"title": "Source Check",
|
||||
"width": 400,
|
||||
"height": 350
|
||||
}
|
||||
}
|
62
panel/index.js
Normal file
62
panel/index.js
Normal file
@ -0,0 +1,62 @@
|
||||
Editor.Panel.extend({
|
||||
style: `
|
||||
:host { margin: 5px; }
|
||||
h2 { color: #f90; }
|
||||
.bottom {
|
||||
height: 30px;
|
||||
}
|
||||
`,
|
||||
|
||||
template: `
|
||||
<h2 style="text-align:center">Source Check</h2>
|
||||
<hr />
|
||||
<div>
|
||||
<!-- 1. 把要檢查的資料夾拉進來<br> -->
|
||||
1. 填入要檢查的資料夾(從assets開始算)<br>
|
||||
2. 執行<br>
|
||||
PS. 目前只有檢查png、jpg、anim、fnt
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
要檢查的資料夾 db://asstes/
|
||||
<ui-input placeholder="UIScenes/Backpack" id="filepath"></ui-input>
|
||||
<!-- <ui-asset class="flex-1" type="native-asset" droppable="asset" id="node"></ui-asset> -->
|
||||
</div>
|
||||
<hr />
|
||||
<hr />
|
||||
<div style="text-align:right">
|
||||
<ui-button id="run" class="blue">執行</ui-button>
|
||||
</div>
|
||||
`,
|
||||
|
||||
$: {
|
||||
/** filepath */
|
||||
filepath: "#filepath",
|
||||
/** 生成按鈕 */
|
||||
run: "#run",
|
||||
},
|
||||
|
||||
ready() {
|
||||
// Editor.Ipc.sendToMain("sourcecheck:panel-load-finish");
|
||||
this.onClickRun();
|
||||
|
||||
},
|
||||
/** 保存按鈕點擊事件 */
|
||||
onClickRun() {
|
||||
this.$run.addEventListener("confirm", () => {
|
||||
if (!this.$filepath._value) {
|
||||
Editor.error("請輸入要檢查的資料夾");
|
||||
return;
|
||||
}
|
||||
Editor.Ipc.sendToMain("sourcecheck:run-click", this.$filepath);
|
||||
});
|
||||
},
|
||||
messages: {
|
||||
// "setDefault": function (event, ...agrs) {
|
||||
// if (event.reply) {
|
||||
// //if no error, the first argument should be null
|
||||
// event.reply(null, "Fine, thank you!");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
});
|
37
scene-obtain.js
Normal file
37
scene-obtain.js
Normal file
@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
let fs = require("fs");
|
||||
let path = require("path");
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* 獲取場景節點下的配置信息
|
||||
* @param event event
|
||||
* @param data 這邊把panel的Node帶進來
|
||||
*/
|
||||
"getFsPath": function (event, request) {
|
||||
let self = this;
|
||||
var response = null;
|
||||
var args = request.args;
|
||||
try {
|
||||
let url = `db://assets/${args[0]._value}`;
|
||||
url = url.replace(/\\/g, "/");
|
||||
// let url = `db://assets/=SceneLobby`;
|
||||
// Editor.log(`url: ${url}`);
|
||||
let fsPath = Editor.remote.assetdb.urlToFspath(url);
|
||||
// Editor.log(`fsPath: ${fsPath}`);
|
||||
|
||||
// this.fileSearch(fsPath);
|
||||
|
||||
let res = {
|
||||
url: url,
|
||||
fsPath, fsPath
|
||||
}
|
||||
response = JSON.stringify(res);
|
||||
} catch (error) {
|
||||
Editor.log(`getFsPath error: ${error}`);
|
||||
}
|
||||
if (event.reply) {
|
||||
event.reply(null, response);
|
||||
}
|
||||
},
|
||||
};
|
104
src/common/config-manager.js
Normal file
104
src/common/config-manager.js
Normal file
@ -0,0 +1,104 @@
|
||||
const Path = require('path');
|
||||
const Fs = require('fs');
|
||||
const PackageUtil = require('../eazax/package-util');
|
||||
|
||||
/** 配置文件路径 */
|
||||
const CONFIG_PATH = Path.join(__dirname, '../../config.json');
|
||||
|
||||
/** package.json 的路径 */
|
||||
const PACKAGE_PATH = Path.join(__dirname, '../../package.json');
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
|
||||
/** 快捷键行为 */
|
||||
const ACTION_NAME = 'find';
|
||||
|
||||
/** package.json 中的菜单项 key */
|
||||
const MENU_ITEM_KEY = `i18n:MAIN_MENU.package.title/i18n:${PACKAGE_NAME}.name/i18n:${PACKAGE_NAME}.${ACTION_NAME}`;
|
||||
|
||||
/**
|
||||
* 配置管理器
|
||||
*/
|
||||
const ConfigManager = {
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
get defaultConfig() {
|
||||
return {
|
||||
version: '1.1',
|
||||
printDetails: true,
|
||||
printFolding: true,
|
||||
autoCheckUpdate: true,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 读取配置
|
||||
*/
|
||||
get() {
|
||||
// 配置
|
||||
const config = ConfigManager.defaultConfig;
|
||||
if (Fs.existsSync(CONFIG_PATH)) {
|
||||
const localConfig = JSON.parse(Fs.readFileSync(CONFIG_PATH));
|
||||
for (const key in config) {
|
||||
if (localConfig[key] !== undefined) {
|
||||
config[key] = localConfig[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // 快捷键
|
||||
// config.hotkey = ConfigManager.getAccelerator();
|
||||
|
||||
// Done
|
||||
return config;
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
* @param {*} value 配置
|
||||
*/
|
||||
set(value) {
|
||||
// 配置
|
||||
const config = ConfigManager.defaultConfig;
|
||||
for (const key in config) {
|
||||
if (value[key] !== undefined) {
|
||||
config[key] = value[key];
|
||||
}
|
||||
}
|
||||
Fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
||||
|
||||
// 快捷键
|
||||
ConfigManager.setAccelerator(value.hotkey);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取快捷键
|
||||
* @returns {string}
|
||||
*/
|
||||
getAccelerator() {
|
||||
const package = JSON.parse(Fs.readFileSync(PACKAGE_PATH)),
|
||||
item = package['main-menu'][MENU_ITEM_KEY];
|
||||
return item['accelerator'] || '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置快捷键
|
||||
* @param {string} value
|
||||
*/
|
||||
setAccelerator(value) {
|
||||
const package = JSON.parse(Fs.readFileSync(PACKAGE_PATH)),
|
||||
item = package['main-menu'][MENU_ITEM_KEY];
|
||||
if (value != undefined && value !== '') {
|
||||
item['accelerator'] = value;
|
||||
} else {
|
||||
delete item['accelerator'];
|
||||
}
|
||||
Fs.writeFileSync(PACKAGE_PATH, JSON.stringify(package, null, 2));
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ConfigManager;
|
61
src/eazax/browser-util.js
Normal file
61
src/eazax/browser-util.js
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 浏览器工具
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210729
|
||||
*/
|
||||
const BrowserUtil = {
|
||||
|
||||
/**
|
||||
* 获取当前 Url 中的参数
|
||||
* @param {string} key 键
|
||||
* @returns {string}
|
||||
*/
|
||||
getUrlParam(key) {
|
||||
if (!window || !window.location) {
|
||||
return null;
|
||||
}
|
||||
const query = window.location.search.replace('?', '');
|
||||
if (query === '') {
|
||||
return null;
|
||||
}
|
||||
const substrings = query.split('&');
|
||||
for (let i = 0; i < substrings.length; i++) {
|
||||
const keyValue = substrings[i].split('=');
|
||||
if (decodeURIComponent(keyValue[0]) === key) {
|
||||
return decodeURIComponent(keyValue[1]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取 Cookie 值
|
||||
* @param {string} key 键
|
||||
* @returns {string}
|
||||
*/
|
||||
getCookie(key) {
|
||||
const regExp = new RegExp(`(^| )${key}=([^;]*)(;|$)`),
|
||||
values = document.cookie.match(regExp);
|
||||
if (values !== null) {
|
||||
return values[2];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置 Cookie
|
||||
* @param {string} key 键
|
||||
* @param {string | number | boolean} value 值
|
||||
* @param {string} expires 过期时间(GMT)
|
||||
*/
|
||||
setCookie(key, value, expires) {
|
||||
let keyValues = `${key}=${encodeURIComponent(value)};`;
|
||||
if (expires) {
|
||||
keyValues += `expires=${expires};`;
|
||||
}
|
||||
document.cookie = keyValues;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = BrowserUtil;
|
37
src/eazax/color-util.js
Normal file
37
src/eazax/color-util.js
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 颜色工具
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210725
|
||||
*/
|
||||
const ColorUtil = {
|
||||
|
||||
/**
|
||||
* 将十六进制颜色值转为 RGB 格式
|
||||
* @param {string} hex
|
||||
* @returns {{ r: number, g: number, b: number }}
|
||||
*/
|
||||
hexToRGB(hex) {
|
||||
// 是否为 HEX 格式
|
||||
const regExp = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
|
||||
if (!regExp.test(hex)) {
|
||||
return null;
|
||||
}
|
||||
// 四位
|
||||
if (hex.length === 4) {
|
||||
const r = hex.slice(1, 2),
|
||||
g = hex.slice(2, 3),
|
||||
b = hex.slice(3, 4);
|
||||
hex = `#${r}${r}${g}${g}${b}${b}`;
|
||||
}
|
||||
// 转换进制
|
||||
const rgb = {
|
||||
r: parseInt(`0x${hex.slice(1, 3)}`),
|
||||
g: parseInt(`0x${hex.slice(3, 5)}`),
|
||||
b: parseInt(`0x${hex.slice(5, 7)}`),
|
||||
}
|
||||
return rgb;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ColorUtil;
|
201
src/eazax/css/cocos-class.css
Normal file
201
src/eazax/css/cocos-class.css
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
Cocos Creator 风格样式
|
||||
版本: 20210911
|
||||
作者: 陈皮皮 (ifaswind)
|
||||
主页: https://gitee.com/ifaswind
|
||||
公众号: 菜鸟小栈
|
||||
*/
|
||||
|
||||
/* 属性容器 */
|
||||
.properties {
|
||||
width: 100%;
|
||||
border: 1px solid #666;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.properties > * {
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.properties:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.properties:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 属性 */
|
||||
.property {
|
||||
width: 100%;
|
||||
min-height: 23px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* 属性标签 */
|
||||
.property > .label {
|
||||
width: 38%;
|
||||
min-width: 70px;
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
line-height: 23px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
flex-shrink: 0;
|
||||
align-items: baseline;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* 属性标签文本:虚指 */
|
||||
.property:hover > .label > .text {
|
||||
color: #09f;
|
||||
}
|
||||
|
||||
/* 属性标签文本:聚焦内部 */
|
||||
.property:focus-within > .label > .text {
|
||||
color: #fd942b;
|
||||
}
|
||||
|
||||
/* 属性标签内容:聚焦内部 */
|
||||
.property:focus-within > .content > * {
|
||||
border-color: #fd942b;
|
||||
}
|
||||
|
||||
/* tooltip */
|
||||
.tooltip {
|
||||
background-color: #333333;
|
||||
padding: 5px 8px;
|
||||
border: 1px solid #646464;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
top: -38px;
|
||||
left: -5px;
|
||||
visibility: hidden;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* tooltip 三角形 */
|
||||
.tooltip::before,
|
||||
.tooltip::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 6px solid transparent;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/* tooltip 三角形 */
|
||||
.tooltip::before {
|
||||
border-right: 6px solid #333333;
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
/* tooltip 三角形边框 */
|
||||
.tooltip::after {
|
||||
border-right: 6px solid #646464;
|
||||
top: calc(100% + 1px);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* 前一个元素虚指时的 tooltip */
|
||||
*:hover + .tooltip {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* 属性内容 */
|
||||
.property > .content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.property > .content > * {
|
||||
width: auto;
|
||||
min-width: 20px;
|
||||
height: 21px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.property > .content > *:focus {
|
||||
border-color: #fd942b;
|
||||
}
|
||||
|
||||
/* 提示 */
|
||||
.tip {
|
||||
width: 100%;
|
||||
min-height: 45px;
|
||||
background: #333;
|
||||
border: 1px solid #666;
|
||||
border-radius: 3px;
|
||||
padding: 12px 8px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
color: #bdbdbd;
|
||||
line-height: 17px;
|
||||
font-size: 13px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.tip > * {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* *::滚动条 */
|
||||
*::-webkit-scrollbar {
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
/* *::滚动条-按钮 */
|
||||
*::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* *::滚动条-横竖交汇处 */
|
||||
*::-webkit-scrollbar-corner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* *::滚动条-轨道 */
|
||||
*::-webkit-scrollbar-track {
|
||||
/* background: rgba(0, 0, 0, 0.5); */
|
||||
background: none !important;
|
||||
background-clip: content-box;
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
|
||||
/* *::滚动条-滑块 */
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: #7d7d7d;
|
||||
background-clip: content-box;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* *::滚动条-滑块:虚指 */
|
||||
*::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #fd942b;
|
||||
border: 3px solid transparent;
|
||||
}
|
197
src/eazax/css/cocos-tag.css
Normal file
197
src/eazax/css/cocos-tag.css
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
Cocos Creator 风格标签 (橙黑)
|
||||
版本: 20210725
|
||||
作者: 陈皮皮 (ifaswind)
|
||||
主页: https://gitee.com/ifaswind
|
||||
公众号: 菜鸟小栈
|
||||
*/
|
||||
|
||||
/* 下拉选择器 */
|
||||
select {
|
||||
background-color: #262626;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #171717;
|
||||
border-radius: 100px;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
color: #bdbdbd;
|
||||
cursor: pointer;
|
||||
/* 替换默认的箭头 */
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAA+ElEQVRYR+2VXw6CMAzGV3YUjSZ6CsI4l3gutnALH/xzEB8spokSQphbiwkv5RHafb9+awuYlR9YWd8ogDqgDqgDPx3w3h8A4FlV1UOysLz3O2stlmV5j+VHAbqu2yLijRIR8VzXdcOBaNu2KYriRDl93x+dc5e5/ChACGFjjBnIORBj8Q/A3jl3ZQFQcAiBKhgqz4GYiqdyklPAgeCKU5FJgFwnJOLZACkIqTgLgILnhOj9t9slE5N1BePunUKMv6Uajj0Fsbmfg5CIs68g5oRUfBEAJdOqBYCXdFUvBuCsZva/4B+H55zBnoKcQzkxCqAOqAOrO/AGwnWWIa30xvoAAAAASUVORK5CYII=);
|
||||
background-size: 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 3px center;
|
||||
}
|
||||
|
||||
/* 下拉选择器:虚指 */
|
||||
select:hover {
|
||||
border-color: #888888;
|
||||
}
|
||||
|
||||
/* 输入框,文本区域 */
|
||||
input,
|
||||
textarea {
|
||||
background-color: #262626;
|
||||
box-sizing: border-box;
|
||||
padding: 0 5px;
|
||||
border: 1px solid #171717;
|
||||
border-radius: 3px;
|
||||
color: #fd942b;
|
||||
font-size: 12px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 文本区域 */
|
||||
textarea {
|
||||
min-height: 40px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* 输入框,文本区域::占位符 */
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* 数字输入框 */
|
||||
input[type='number'] {
|
||||
width: 50px !important;
|
||||
}
|
||||
|
||||
/* 数字输入框::增减按钮 */
|
||||
input[type='number']::-webkit-outer-spin-button,
|
||||
input[type='number']::-webkit-inner-spin-button {
|
||||
/* appearance: none; */
|
||||
/* -webkit-appearance: none; */
|
||||
/* margin: 0; */
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
/* 复选框 */
|
||||
input[type='checkbox'] {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
min-width: 16px !important;
|
||||
background-image: none;
|
||||
background-color: #262626;
|
||||
border: 1px solid #171717;
|
||||
border-radius: 3px;
|
||||
padding-left: 0;
|
||||
position: relative !important;
|
||||
flex: 0 !important;
|
||||
margin: 0;
|
||||
color: #fd942b;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 复选框:勾选 */
|
||||
input[type='checkbox']:checked::after {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
display: inline-block;
|
||||
content: '';
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABKUlEQVRYR+2W2w2DMAxF41ZdBnWzqjMQZkBdjWUqkiqUgCHOO4Ef+EECwTlO7AvATj7gZD67BA5bgW/fcLXdj/cwnfVxiMD4aVomYQILKTssUV0Aw3XVWKKqgFr2G0BLTZqWqCbggq8NIHkVgSD43A/FBag9J8MOJL+/hq6oQGjlbIYrMUNAVSAEg/28+iI7BW4IuObVJZAK3wj45tUmkANfBFyNs08uLBIKd70DgroWNY0WKAGfVmBUHwlLWuFqcRWl4OsWREioh2zxuukTYtWoPlrGcOyfnIEkc9s3gsb9QLg5hiUkIuB0EOVIRMJJAXUxaTsS4FaBaIlEuFMgWCID7hXwSmTCgwSsEgXgwQJ/CZSYheBRAlpCEP/20UGFHij6R5Qicgn8AIQ23JRIyuB1AAAAAElFTkSuQmCC);
|
||||
background-size: 12px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* 输入框:虚指 */
|
||||
input:hover {
|
||||
border-color: #888888;
|
||||
}
|
||||
|
||||
/* 滑动条 */
|
||||
input[type='range'] {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
height: 4px !important;
|
||||
background-color: #262626;
|
||||
border: 1px solid #171717;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
/* 滑动条:虚指|聚焦 */
|
||||
input[type='range']:hover,
|
||||
input[type='range']:focus,
|
||||
*:focus-within input[type='range'] {
|
||||
border-color: #171717 !important;
|
||||
}
|
||||
|
||||
/* 滑动条::把手 */
|
||||
input[type='range']::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 2px;
|
||||
background: #333;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #949494;
|
||||
box-shadow: 0 1px 3px 1px #000 inset, 0 1px 1px 0 rgba(0, 0, 0, 0.9);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
/* 滑动条::把手:虚指 */
|
||||
input[type='range']::-webkit-slider-thumb:hover {
|
||||
border-color: #bcbcbc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 滑动条::把手:激活 */
|
||||
input[type='range']::-webkit-slider-thumb:active,
|
||||
*:focus-within > input[type='range']::-webkit-slider-thumb {
|
||||
color: #bdbdbd;
|
||||
border-color: #fd942b !important;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
/* 取色器 */
|
||||
input[type='color'] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 1px;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 取色器::色板容器 */
|
||||
input[type='color']::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 取色器::色板 */
|
||||
input[type='color']::-webkit-color-swatch {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 超链接 */
|
||||
a {
|
||||
color: #fd942b;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 超链接:虚指 */
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #666;
|
||||
border: none;
|
||||
margin: 10px 0 !important;
|
||||
}
|
18
src/eazax/css/eazax-colors.css
Normal file
18
src/eazax/css/eazax-colors.css
Normal file
@ -0,0 +1,18 @@
|
||||
:root {
|
||||
/* 背景颜色 */
|
||||
--eazax-bg-color: #454545;
|
||||
/* 主颜色 */
|
||||
--eazax-main-color: #262626;
|
||||
/* 强调色 */
|
||||
--eazax-accent-color: #2e88fb;
|
||||
/* 聚焦色 */
|
||||
--eazax-focus-color: #fd942b;
|
||||
/* 边框调色 */
|
||||
--eazax-border-color: #171717;
|
||||
/* 边框虚指调色 */
|
||||
--eazax-border-hover-color: #888888;
|
||||
/* 文本颜色 */
|
||||
--eazax-font-color: #bdbdbd;
|
||||
/* 内容颜色 */
|
||||
--eazax-content-color: #fd942b;
|
||||
}
|
53
src/eazax/editor-main-kit.js
Normal file
53
src/eazax/editor-main-kit.js
Normal file
@ -0,0 +1,53 @@
|
||||
const MainEvent = require('./main-event');
|
||||
const { print, checkUpdate } = require('./editor-main-util');
|
||||
|
||||
/**
|
||||
* (渲染进程)检查更新回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
* @param {boolean} logWhatever 无论有无更新都打印提示
|
||||
*/
|
||||
function onCheckUpdateEvent(event, logWhatever) {
|
||||
checkUpdate(logWhatever);
|
||||
}
|
||||
|
||||
/**
|
||||
* (渲染进程)打印事件回调
|
||||
* @param {Electron.IpcMainEvent} event
|
||||
* @param {'log' | 'info' | 'warn' | 'error' | any} type
|
||||
* @param {any[]?} args
|
||||
*/
|
||||
function onPrintEvent(event, type) {
|
||||
// print(type, ...args);
|
||||
const args = [type];
|
||||
for (let i = 2, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
print.apply(null, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑器主进程套件 (依赖 Cocos Creator 编辑器)
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210818
|
||||
*/
|
||||
const EditorMainKit = {
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
register() {
|
||||
MainEvent.on('check-update', onCheckUpdateEvent);
|
||||
MainEvent.on('print', onPrintEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消注册
|
||||
*/
|
||||
unregister() {
|
||||
MainEvent.removeListener('check-update', onCheckUpdateEvent);
|
||||
MainEvent.removeListener('print', onPrintEvent);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = EditorMainKit;
|
147
src/eazax/editor-main-util.js
Normal file
147
src/eazax/editor-main-util.js
Normal file
@ -0,0 +1,147 @@
|
||||
const I18n = require('./i18n');
|
||||
const PackageUtil = require('./package-util');
|
||||
const Updater = require('./updater');
|
||||
|
||||
/** 编辑器语言 */
|
||||
const LANG = Editor.lang || Editor.I18n.getLanguage();
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
|
||||
/** 扩展名称 */
|
||||
const EXTENSION_NAME = I18n.get(LANG, 'name');
|
||||
|
||||
/**
|
||||
* 编辑器主进程工具 (依赖 Cocos Creator 编辑器)
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210929
|
||||
*/
|
||||
const EditorMainUtil = {
|
||||
|
||||
/**
|
||||
* 语言
|
||||
*/
|
||||
get language() {
|
||||
return LANG;
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n
|
||||
* @param {string} key 关键词
|
||||
* @returns {string}
|
||||
*/
|
||||
translate(key) {
|
||||
return I18n.get(LANG, key);
|
||||
},
|
||||
|
||||
/**
|
||||
* 打印信息到控制台(带标题)
|
||||
* @param {'log' | 'info' | 'warn' | 'error' | any} type
|
||||
* @param {any[]?} args
|
||||
*/
|
||||
print(type) {
|
||||
const args = [`[${EXTENSION_NAME}]`];
|
||||
for (let i = 1, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
const object = Editor.log ? Editor : console;
|
||||
switch (type) {
|
||||
case 'log': {
|
||||
object.log.apply(object, args);
|
||||
break;
|
||||
}
|
||||
case 'info': {
|
||||
object.info.apply(object, args);
|
||||
break;
|
||||
}
|
||||
case 'warn': {
|
||||
object.warn.apply(object, args);
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
object.error.apply(object, args);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
args.splice(1, 0, type);
|
||||
object.log.apply(object, args);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 打印信息到控制台(不带标题)
|
||||
* @param {'log' | 'info' | 'warn' | 'error' | any} type
|
||||
* @param {any[]?} args
|
||||
*/
|
||||
pureWithoutTitle(type) {
|
||||
const args = [];
|
||||
for (let i = 1, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
const object = Editor.log ? Editor : console;
|
||||
switch (type) {
|
||||
case 'log': {
|
||||
object.log.apply(object, args);
|
||||
break;
|
||||
}
|
||||
case 'info': {
|
||||
object.info.apply(object, args);
|
||||
break;
|
||||
}
|
||||
case 'warn': {
|
||||
object.warn.apply(object, args);
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
object.error.apply(object, args);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
args.splice(1, 0, type);
|
||||
object.log.apply(object, args);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
* @param {boolean} logWhatever 无论有无更新都打印提示
|
||||
*/
|
||||
async checkUpdate(logWhatever) {
|
||||
// 编辑器本次启动是否已经检查过了
|
||||
if (!logWhatever && (Editor[PACKAGE_NAME] && Editor[PACKAGE_NAME].hasCheckUpdate)) {
|
||||
return;
|
||||
}
|
||||
Editor[PACKAGE_NAME] = { hasCheckUpdate: true };
|
||||
// 是否有新版本
|
||||
const hasNewVersion = await Updater.check();
|
||||
// 打印到控制台
|
||||
const { print, translate } = EditorMainUtil;
|
||||
const localVersion = Updater.getLocalVersion();
|
||||
if (hasNewVersion) {
|
||||
const remoteVersion = await Updater.getRemoteVersion();
|
||||
print('info', translate('has-new-version'));
|
||||
print('info', `${translate('local-version')}${localVersion}`);
|
||||
print('info', `${translate('latest-version')}${remoteVersion}`);
|
||||
print('info', translate('git-releases'));
|
||||
print('info', translate('cocos-store'));
|
||||
} else if (logWhatever) {
|
||||
print('info', translate('current-latest'));
|
||||
print('info', `${translate('local-version')}${localVersion}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* (3.x)重新加载扩展
|
||||
*/
|
||||
async reload() {
|
||||
const path = await Editor.Package.getPath(PACKAGE_NAME);
|
||||
await Editor.Package.unregister(path);
|
||||
await Editor.Package.register(path);
|
||||
await Editor.Package.enable(path);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = EditorMainUtil;
|
26
src/eazax/editor-renderer-kit.js
Normal file
26
src/eazax/editor-renderer-kit.js
Normal file
@ -0,0 +1,26 @@
|
||||
const RendererEvent = require("./renderer-event");
|
||||
|
||||
/**
|
||||
* 编辑器渲染进程套件 (依赖 Cocos Creator 编辑器)
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210818
|
||||
*/
|
||||
const EditorRendererKit = {
|
||||
|
||||
/**
|
||||
* 打印信息到 Creator 编辑器控制台
|
||||
* @param {'log' | 'info' | 'warn' | 'error' | any} type
|
||||
* @param {any[]?} args
|
||||
*/
|
||||
print(type) {
|
||||
// return RendererEvent.send('print', type, ...args);
|
||||
const args = ['print', type];
|
||||
for (let i = 1, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
return RendererEvent.send.apply(RendererEvent, args);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = EditorRendererKit;
|
158
src/eazax/file-util.js
Normal file
158
src/eazax/file-util.js
Normal file
@ -0,0 +1,158 @@
|
||||
const Fs = require('fs');
|
||||
const Path = require('path');
|
||||
const { promisify } = require('util');
|
||||
|
||||
/**
|
||||
* 文件工具 (Promise 化)
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210818
|
||||
*/
|
||||
const FileUtil = {
|
||||
|
||||
/**
|
||||
* 获取文件状态
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @returns {Promise<Fs.stats>}
|
||||
*/
|
||||
stat: promisify(Fs.stat),
|
||||
|
||||
/**
|
||||
* 创建文件夹
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {Fs.MakeDirectoryOptions?} options 选项
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
mkdir: promisify(Fs.mkdir),
|
||||
|
||||
/**
|
||||
* 读取文件夹
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {any} options 选项
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
readdir: promisify(Fs.readdir),
|
||||
|
||||
/**
|
||||
* 移除文件夹
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {Fs.RmDirOptions?} options 选项
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
rmdir: promisify(Fs.rmdir),
|
||||
|
||||
/**
|
||||
* 读取文件
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {any} options 选项
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
readFile: promisify(Fs.readFile),
|
||||
|
||||
/**
|
||||
* 创建文件
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {string | NodeJS.ArrayBufferView} data 数据
|
||||
* @param {Fs.WriteFileOptions?} options 选项
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
writeFile: promisify(Fs.writeFile),
|
||||
|
||||
/**
|
||||
* 移除文件
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
unlink: promisify(Fs.unlink),
|
||||
|
||||
/**
|
||||
* 测试路径是否存在 (同步)
|
||||
* @param {Fs.PathLike} path 路径
|
||||
*/
|
||||
existsSync: Fs.existsSync,
|
||||
|
||||
/**
|
||||
* 复制文件/文件夹
|
||||
* @param {Fs.PathLike} srcPath 源路径
|
||||
* @param {Fs.PathLike} destPath 目标路径
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async copy(srcPath, destPath) {
|
||||
if (!FileUtil.existsSync(srcPath)) {
|
||||
return false;
|
||||
}
|
||||
const stats = await FileUtil.stat(srcPath);
|
||||
if (stats.isDirectory()) {
|
||||
if (!FileUtil.existsSync(destPath)) {
|
||||
await FileUtil.createDir(destPath);
|
||||
}
|
||||
const names = await FileUtil.readdir(srcPath);
|
||||
for (const name of names) {
|
||||
await FileUtil.copy(Path.join(srcPath, name), Path.join(destPath, name));
|
||||
}
|
||||
} else {
|
||||
await FileUtil.writeFile(destPath, await FileUtil.readFile(srcPath));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建文件夹 (递归)
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async createDir(path) {
|
||||
if (FileUtil.existsSync(path)) {
|
||||
return true;
|
||||
} else {
|
||||
const dir = Path.dirname(path);
|
||||
if (await FileUtil.createDir(dir)) {
|
||||
await FileUtil.mkdir(path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除文件/文件夹 (递归)
|
||||
* @param {Fs.PathLike} path 路径
|
||||
*/
|
||||
async remove(path) {
|
||||
if (!FileUtil.existsSync(path)) {
|
||||
return;
|
||||
}
|
||||
const stats = await FileUtil.stat(path);
|
||||
if (stats.isDirectory()) {
|
||||
const names = await FileUtil.readdir(path);
|
||||
for (const name of names) {
|
||||
await FileUtil.remove(Path.join(path, name));
|
||||
}
|
||||
await FileUtil.rmdir(path);
|
||||
} else {
|
||||
await FileUtil.unlink(path);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 遍历文件/文件夹并执行函数
|
||||
* @param {Fs.PathLike} path 路径
|
||||
* @param {(filePath: Fs.PathLike, stat: Fs.Stats) => void | Promise<void>} handler 处理函数
|
||||
*/
|
||||
async map(path, handler) {
|
||||
if (!FileUtil.existsSync(path)) {
|
||||
return;
|
||||
}
|
||||
const stats = await FileUtil.stat(path);
|
||||
if (stats.isDirectory()) {
|
||||
const names = await FileUtil.readdir(path);
|
||||
for (const name of names) {
|
||||
await FileUtil.map(Path.join(path, name), handler);
|
||||
}
|
||||
} else {
|
||||
await handler(path, stats);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = FileUtil;
|
36
src/eazax/i18n.js
Normal file
36
src/eazax/i18n.js
Normal file
@ -0,0 +1,36 @@
|
||||
const zh = require('../../i18n/zh');
|
||||
const en = require('../../i18n/en');
|
||||
|
||||
/**
|
||||
* 多语言
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210929
|
||||
*/
|
||||
const I18n = {
|
||||
|
||||
/**
|
||||
* 中文
|
||||
*/
|
||||
zh,
|
||||
|
||||
/**
|
||||
* 英文
|
||||
*/
|
||||
en,
|
||||
|
||||
/**
|
||||
* 获取多语言文本
|
||||
* @param {string} lang 语言
|
||||
* @param {string} key 关键字
|
||||
* @returns {string}
|
||||
*/
|
||||
get(lang, key) {
|
||||
if (I18n[lang] && I18n[lang][key]) {
|
||||
return I18n[lang][key];
|
||||
}
|
||||
return key;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = I18n;
|
81
src/eazax/main-event.js
Normal file
81
src/eazax/main-event.js
Normal file
@ -0,0 +1,81 @@
|
||||
const { ipcMain } = require('electron');
|
||||
const PackageUtil = require('./package-util');
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
|
||||
/**
|
||||
* 主进程 IPC 事件
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210818
|
||||
*/
|
||||
const MainEvent = {
|
||||
|
||||
/**
|
||||
* 监听事件(一次性)
|
||||
* @param {string} channel 频道
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
once(channel, callback) {
|
||||
return ipcMain.once(`${PACKAGE_NAME}:${channel}`, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param {string} channel 频道
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
on(channel, callback) {
|
||||
return ipcMain.on(`${PACKAGE_NAME}:${channel}`, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消事件监听
|
||||
* @param {string} channel 频道
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
removeListener(channel, callback) {
|
||||
return ipcMain.removeListener(`${PACKAGE_NAME}:${channel}`, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消事件的所有监听
|
||||
* @param {string} channel 频道
|
||||
*/
|
||||
removeAllListeners(channel) {
|
||||
return ipcMain.removeAllListeners(`${PACKAGE_NAME}:${channel}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 发送事件到指定渲染进程
|
||||
* @param {Electron.WebContents} webContents 渲染进程事件对象
|
||||
* @param {string} channel 频道
|
||||
* @param {any[]?} args 参数
|
||||
*/
|
||||
send(webContents, channel) {
|
||||
// return webContents.send(`${PACKAGE_NAME}:${channel}`, ...args);
|
||||
const args = [`${PACKAGE_NAME}:${channel}`];
|
||||
for (let i = 2, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
return webContents.send.apply(webContents, args);
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复事件给渲染进程
|
||||
* @param {Electron.IpcMainEvent} ipcMainEvent 事件对象
|
||||
* @param {string} channel 频道
|
||||
* @param {any[]?} args 参数
|
||||
*/
|
||||
reply(ipcMainEvent, channel) {
|
||||
// return ipcMainEvent.reply(`${PACKAGE_NAME}:${channel}`, ...args);
|
||||
const args = [`${PACKAGE_NAME}:${channel}`];
|
||||
for (let i = 2, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
return ipcMainEvent.reply.apply(ipcMainEvent, args);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = MainEvent;
|
47
src/eazax/package-util.js
Normal file
47
src/eazax/package-util.js
Normal file
@ -0,0 +1,47 @@
|
||||
const { shell } = require('electron');
|
||||
|
||||
/** 包信息 */
|
||||
const PACKAGE_JSON = require('../../package.json');
|
||||
|
||||
/**
|
||||
* 包工具
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210908
|
||||
*/
|
||||
const PackageUtil = {
|
||||
|
||||
/**
|
||||
* 包名
|
||||
* @type {string}
|
||||
*/
|
||||
get name() {
|
||||
return PACKAGE_JSON.name;
|
||||
},
|
||||
|
||||
/**
|
||||
* 版本
|
||||
* @type {string}
|
||||
*/
|
||||
get version() {
|
||||
return PACKAGE_JSON.version;
|
||||
},
|
||||
|
||||
/**
|
||||
* 仓库地址
|
||||
* @type {string}
|
||||
*/
|
||||
get repository() {
|
||||
return PACKAGE_JSON.repository;
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开仓库页面
|
||||
*/
|
||||
openRepository() {
|
||||
const url = PackageUtil.repository;
|
||||
shell.openExternal(url);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = PackageUtil;
|
80
src/eazax/renderer-event.js
Normal file
80
src/eazax/renderer-event.js
Normal file
@ -0,0 +1,80 @@
|
||||
const { ipcRenderer } = require('electron');
|
||||
const PackageUtil = require('./package-util');
|
||||
|
||||
/** 包名 */
|
||||
const PACKAGE_NAME = PackageUtil.name;
|
||||
|
||||
/**
|
||||
* 渲染进程 IPC 事件
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210818
|
||||
*/
|
||||
const RendererEvent = {
|
||||
|
||||
/**
|
||||
* 监听事件(一次性)
|
||||
* @param {string} channel 频道
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
once(channel, callback) {
|
||||
return ipcRenderer.once(`${PACKAGE_NAME}:${channel}`, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param {string} channel 频道
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
on(channel, callback) {
|
||||
return ipcRenderer.on(`${PACKAGE_NAME}:${channel}`, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消事件监听
|
||||
* @param {string} channel 频道
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
removeListener(channel, callback) {
|
||||
return ipcRenderer.removeListener(`${PACKAGE_NAME}:${channel}`, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消事件的所有监听
|
||||
* @param {string} channel 频道
|
||||
*/
|
||||
removeAllListeners(channel) {
|
||||
return ipcRenderer.removeAllListeners(`${PACKAGE_NAME}:${channel}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 发送事件到主进程
|
||||
* @param {string} channel 频道
|
||||
* @param {...any} args 参数
|
||||
*/
|
||||
send(channel) {
|
||||
// return ipcRenderer.send(`${PACKAGE_NAME}:${channel}`, ...args);
|
||||
const args = [`${PACKAGE_NAME}:${channel}`];
|
||||
for (let i = 1, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
return ipcRenderer.send.apply(ipcRenderer, args);
|
||||
},
|
||||
|
||||
/**
|
||||
* 发送事件到主进程(同步)
|
||||
* @param {string} channel 频道
|
||||
* @param {...any} args 参数
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
sendSync(channel) {
|
||||
// return ipcRenderer.sendSync(`${PACKAGE_NAME}:${channel}`, ...args);
|
||||
const args = [`${PACKAGE_NAME}:${channel}`];
|
||||
for (let i = 1, l = arguments.length; i < l; i++) {
|
||||
args.push(arguments[i]);
|
||||
}
|
||||
return ipcRenderer.sendSync.apply(ipcRenderer, args);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = RendererEvent;
|
92
src/eazax/updater.js
Normal file
92
src/eazax/updater.js
Normal file
@ -0,0 +1,92 @@
|
||||
const fetch = require('../../lib/node-fetch');
|
||||
const PackageUtil = require('./package-util');
|
||||
const { compareVersion } = require('./version-util');
|
||||
|
||||
/** 本地版本 */
|
||||
const LOCAL_VERSION = PackageUtil.version;
|
||||
|
||||
/** 远程仓库地址 */
|
||||
const REMOTE_URL = PackageUtil.repository;
|
||||
|
||||
/**
|
||||
* 更新器
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210804
|
||||
*/
|
||||
const Updater = {
|
||||
|
||||
/**
|
||||
* 远程仓库地址
|
||||
* @type {string}
|
||||
*/
|
||||
get remote() {
|
||||
return REMOTE_URL;
|
||||
},
|
||||
|
||||
/**
|
||||
* 分支
|
||||
* @type {string}
|
||||
*/
|
||||
branch: 'master',
|
||||
|
||||
/**
|
||||
* 获取远端的 package.json
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async getRemotePackageJson() {
|
||||
const packageJsonUrl = `${Updater.remote}/raw/${Updater.branch}/package.json`;
|
||||
// 发起网络请求
|
||||
const response = await fetch(packageJsonUrl, {
|
||||
method: 'GET',
|
||||
cache: 'no-cache',
|
||||
mode: 'no-cors',
|
||||
});
|
||||
// 请求结果
|
||||
if (response.status !== 200) {
|
||||
return null;
|
||||
}
|
||||
// 读取 json
|
||||
const json = response.json();
|
||||
return json;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取远端版本号
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async getRemoteVersion() {
|
||||
const package = await Updater.getRemotePackageJson();
|
||||
if (package && package.version) {
|
||||
return package.version;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取本地版本号
|
||||
* @returns {string}
|
||||
*/
|
||||
getLocalVersion() {
|
||||
return LOCAL_VERSION;
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查远端是否有新版本
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async check() {
|
||||
// 远端版本号
|
||||
const remoteVersion = await Updater.getRemoteVersion();
|
||||
if (!remoteVersion) {
|
||||
return false;
|
||||
}
|
||||
// 本地版本号
|
||||
const localVersion = Updater.getLocalVersion();
|
||||
// 对比版本号
|
||||
const result = compareVersion(localVersion, remoteVersion);
|
||||
return (result < 0);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = Updater;
|
61
src/eazax/version-util.js
Normal file
61
src/eazax/version-util.js
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 版本工具
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210814
|
||||
*/
|
||||
const VersionUtil = {
|
||||
|
||||
/**
|
||||
* 拆分版本号
|
||||
* @param {string | number} version 版本号文本
|
||||
* @returns {number[]}
|
||||
* @example
|
||||
* splitVersionString('1.2.0'); // [1, 2, 0]
|
||||
*/
|
||||
splitVersionString(version) {
|
||||
if (typeof version === 'number') {
|
||||
return [version];
|
||||
}
|
||||
if (typeof version === 'string') {
|
||||
return (
|
||||
version.replace(/-/g, '.')
|
||||
.split('.')
|
||||
.map(v => (parseInt(v) || 0))
|
||||
);
|
||||
}
|
||||
return [0];
|
||||
},
|
||||
|
||||
/**
|
||||
* 对比版本号
|
||||
* @param {string | number} a 版本 a
|
||||
* @param {string | number} b 版本 b
|
||||
* @returns {-1 | 0 | 1}
|
||||
* @example
|
||||
* compareVersion('1.0.0', '1.0.1'); // -1
|
||||
* compareVersion('1.1.0', '1.1.0'); // 0
|
||||
* compareVersion('1.2.1', '1.2.0'); // 1
|
||||
* compareVersion('1.2.0.1', '1.2.0'); // 1
|
||||
*/
|
||||
compareVersion(a, b) {
|
||||
const acs = VersionUtil.splitVersionString(a),
|
||||
bcs = VersionUtil.splitVersionString(b);
|
||||
const count = Math.max(acs.length, bcs.length);
|
||||
for (let i = 0; i < count; i++) {
|
||||
const ac = acs[i],
|
||||
bc = bcs[i];
|
||||
// 前者缺少分量或前者小于后者
|
||||
if (ac == undefined || ac < bc) {
|
||||
return -1;
|
||||
}
|
||||
// 后者缺少分量或前者大于后者
|
||||
if (bc == undefined || ac > bc) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = VersionUtil;
|
80
src/eazax/window-util.js
Normal file
80
src/eazax/window-util.js
Normal file
@ -0,0 +1,80 @@
|
||||
const { BrowserWindow } = require('electron');
|
||||
|
||||
/**
|
||||
* 窗口工具(主进程)
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210825
|
||||
*/
|
||||
const WindowUtil = {
|
||||
|
||||
/**
|
||||
* 最先打开的窗口
|
||||
* @returns {BrowserWindow}
|
||||
*/
|
||||
getFirstWindow() {
|
||||
const wins = BrowserWindow.getAllWindows();
|
||||
return wins[wins.length - 1];
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前聚焦的窗口
|
||||
* @returns {BrowserWindow}
|
||||
*/
|
||||
getFocusedWindow() {
|
||||
return BrowserWindow.getFocusedWindow();
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算窗口位置(相对于最先打开的窗口)
|
||||
* @param {[number, number]} size 窗口尺寸
|
||||
* @param {'top' | 'center'} anchor 锚点
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
calcWindowPosition(size, anchor) {
|
||||
const win = WindowUtil.getFirstWindow();
|
||||
return WindowUtil.calcWindowPositionByTarget(size, anchor, win);
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算窗口位置(相对于当前聚焦的窗口)
|
||||
* @param {[number, number]} size 窗口尺寸
|
||||
* @param {'top' | 'center'} anchor 锚点
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
calcWindowPositionByFocused(size, anchor) {
|
||||
const win = WindowUtil.getFocusedWindow();
|
||||
return WindowUtil.calcWindowPositionByTarget(size, anchor, win);
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算窗口位置(相对于当前聚焦的窗口)
|
||||
* @param {[number, number]} size 窗口尺寸
|
||||
* @param {'top' | 'center'} anchor 锚点
|
||||
* @param {BrowserWindow} win 目标窗口
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
calcWindowPositionByTarget(size, anchor, win) {
|
||||
// 根据目标窗口的位置和尺寸来计算
|
||||
const winSize = win.getSize(),
|
||||
winPos = win.getPosition();
|
||||
// 注意:原点 (0, 0) 在屏幕左上角
|
||||
// 另外,窗口的位置值必须是整数,否则修改无效(像素的最小粒度为 1)
|
||||
const x = Math.floor(winPos[0] + (winSize[0] / 2) - (size[0] / 2));
|
||||
let y;
|
||||
switch (anchor) {
|
||||
case 'top': {
|
||||
y = Math.floor(winPos[1]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case 'center': {
|
||||
y = Math.floor(winPos[1] + (winSize[1] / 2) - (size[1] / 2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [x, y];
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = WindowUtil;
|
98
src/main/editor-api.js
Normal file
98
src/main/editor-api.js
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 编辑器 API(用于抹平不同版本编辑器之间的差异)
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210830
|
||||
*/
|
||||
const EditorAPI = {
|
||||
|
||||
/**
|
||||
* 当前语言
|
||||
* @returns {string}
|
||||
*/
|
||||
getLanguage() {
|
||||
return Editor.lang || Editor.I18n.getLanguage();
|
||||
},
|
||||
|
||||
/**
|
||||
* 绝对路径转为UUID
|
||||
* @param {string} fspath
|
||||
*/
|
||||
fspathToUuid(fspath) {
|
||||
return Editor.assetdb.fspathToUuid(fspath);
|
||||
},
|
||||
|
||||
/**
|
||||
* 绝对路径转为编辑器资源路径
|
||||
* @param {string} fspath
|
||||
*/
|
||||
fspathToUrl(fspath) {
|
||||
return Editor.assetdb.fspathToUrl(fspath);
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑器资源路径转为绝对路径
|
||||
* @param {string} url
|
||||
*/
|
||||
urlToFspath(url) {
|
||||
return Editor.assetdb.urlToFspath(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* 通过 uuid 获取资源信息
|
||||
* @param {string} uuid
|
||||
*/
|
||||
assetInfoByUuid(uuid) {
|
||||
return Editor.assetdb.assetInfoByUuid(uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 通过 uuid 获取子资源信息
|
||||
* @param {string} uuid
|
||||
*/
|
||||
subAssetInfosByUuid(uuid) {
|
||||
return Editor.assetdb.subAssetInfosByUuid(uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前选中的资源 uuid
|
||||
* @returns {string[]}
|
||||
*/
|
||||
getCurrentSelectedAssets() {
|
||||
return Editor.Selection.curSelection('asset');
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前选中的节点 uuid
|
||||
* @returns {string[]}
|
||||
*/
|
||||
getCurrentSelectedNodes() {
|
||||
return Editor.Selection.curSelection('node');
|
||||
},
|
||||
|
||||
/**
|
||||
* 是否为 uuid
|
||||
* @param {string} uuid
|
||||
*/
|
||||
isUuid(uuid) {
|
||||
return Editor.Utils.UuidUtils.isUuid(uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 压缩 uuid
|
||||
* @param {string} uuid
|
||||
*/
|
||||
compressUuid(uuid) {
|
||||
return Editor.Utils.UuidUtils.compressUuid(uuid);
|
||||
},
|
||||
|
||||
/**
|
||||
* 反压缩 uuid
|
||||
* @param {string} uuid
|
||||
*/
|
||||
decompressUuid(uuid) {
|
||||
return Editor.Utils.UuidUtils.decompressUuid(uuid);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = EditorAPI;
|
248
src/main/finder.js
Normal file
248
src/main/finder.js
Normal file
@ -0,0 +1,248 @@
|
||||
const { extname, basename } = require("path");
|
||||
const EditorAPI = require("./editor-api");
|
||||
const { print, translate } = require("../eazax/editor-main-util");
|
||||
const FileUtil = require("../eazax/file-util");
|
||||
const { containsValue } = require("./object-util");
|
||||
const Parser = require("./parser");
|
||||
|
||||
/** 扩展名对应文件类型 */
|
||||
const ASSET_TYPE_MAP = {
|
||||
// 场景
|
||||
'.fire': 'scene',
|
||||
'.scene': 'scene',
|
||||
// 预制体
|
||||
'.prefab': 'prefab',
|
||||
// 动画
|
||||
'.anim': 'animation',
|
||||
// 材质
|
||||
'.mtl': 'material',
|
||||
// 字体
|
||||
'.fnt.meta': 'font',
|
||||
};
|
||||
|
||||
/**
|
||||
* 查找器
|
||||
*/
|
||||
const Finder = {
|
||||
|
||||
/**
|
||||
* 使用 uuid 进行查找
|
||||
* @param {string} uuid
|
||||
*/
|
||||
async findByUuid(uuid) {
|
||||
// 是否为有效 uuid
|
||||
if (!EditorAPI.isUuid(uuid)) {
|
||||
print('log', translate('invalid-uuid'), uuid);
|
||||
return [];
|
||||
}
|
||||
// 获取资源信息
|
||||
const assetInfo = EditorAPI.assetInfoByUuid(uuid);
|
||||
if (assetInfo) {
|
||||
// 记录子资源 uuid
|
||||
const subAssetUuids = [];
|
||||
// 资源类型检查
|
||||
if (assetInfo.type === 'texture') {
|
||||
// 纹理子资源
|
||||
const subAssetInfos = EditorAPI.subAssetInfosByUuid(uuid);
|
||||
if (subAssetInfos && subAssetInfos.length > 0) {
|
||||
for (let i = 0; i < subAssetInfos.length; i++) {
|
||||
subAssetUuids.push(subAssetInfos[i].uuid);
|
||||
}
|
||||
}
|
||||
} else if (assetInfo.type === 'typescript' || assetInfo.type === 'javascript') {
|
||||
// 脚本资源
|
||||
uuid = EditorAPI.compressUuid(uuid);
|
||||
}
|
||||
// 查找资源引用
|
||||
const results = [],
|
||||
selfResults = await Finder.findRefs(uuid);
|
||||
for (let i = 0, l = selfResults.length; i < l; i++) {
|
||||
results.push(selfResults[i]);
|
||||
}
|
||||
// 查找子资源的引用
|
||||
if (subAssetUuids.length > 0) {
|
||||
for (let i = 0, l = subAssetUuids.length; i < l; i++) {
|
||||
const subResults = await Finder.findRefs(subAssetUuids[i]);
|
||||
for (let j = 0, l = subResults.length; j < l; j++) {
|
||||
results.push(subResults[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
} else {
|
||||
// 不存在的资源,直接查找 uuid
|
||||
print('log', translate('find-asset-refs'), uuid);
|
||||
return (await Finder.findRefs(uuid));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 查找引用
|
||||
* @param {string} uuid
|
||||
* @returns {Promise<{ type: string, url: string, refs?: object[]}[]>}
|
||||
*/
|
||||
async findRefs(uuid) {
|
||||
const result = [];
|
||||
// 文件处理函数
|
||||
const handler = async (path, stats) => {
|
||||
const ext = extname(path);
|
||||
if (ext === '.fire' || ext === '.scene' || ext === '.prefab') {
|
||||
// 场景和预制体资源(转为节点树)
|
||||
const tree = await Parser.getNodeTree(path);
|
||||
if (!tree) {
|
||||
return;
|
||||
}
|
||||
// 遍历第一层节点查找引用
|
||||
const refs = [];
|
||||
for (let children = tree.children, i = 0, l = children.length; i < l; i++) {
|
||||
Finder.findRefsInNode(tree, children[i], uuid, refs);
|
||||
}
|
||||
// 保存当前文件引用结果
|
||||
if (refs.length > 0) {
|
||||
result.push({
|
||||
type: ASSET_TYPE_MAP[ext],
|
||||
url: EditorAPI.fspathToUrl(path),
|
||||
refs: refs,
|
||||
});
|
||||
}
|
||||
} else if (ext === '.anim') {
|
||||
// 动画资源
|
||||
const data = JSON.parse(await FileUtil.readFile(path)),
|
||||
curveData = data['curveData'],
|
||||
contains = containsValue(curveData, uuid);
|
||||
if (contains) {
|
||||
result.push({
|
||||
type: ASSET_TYPE_MAP[ext],
|
||||
url: EditorAPI.fspathToUrl(path),
|
||||
});
|
||||
}
|
||||
} else if (ext === '.mtl' || path.endsWith('.fnt.meta')) {
|
||||
// 材质和字体资源
|
||||
const data = JSON.parse(await FileUtil.readFile(path));
|
||||
// 需排除自己
|
||||
if ((data['uuid'] === uuid)) {
|
||||
return;
|
||||
}
|
||||
// 是否引用
|
||||
const contains = containsValue(data, uuid);
|
||||
if (contains) {
|
||||
const _ext = (ext === '.mtl') ? '.mtl' : '.fnt.meta';
|
||||
result.push({
|
||||
type: ASSET_TYPE_MAP[_ext],
|
||||
url: EditorAPI.fspathToUrl(path),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
// 遍历资源目录下的文件
|
||||
const assetsPath = EditorAPI.urlToFspath('db://assets');
|
||||
await FileUtil.map(assetsPath, handler);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 查找节点中的引用
|
||||
* @param {object} tree 节点树
|
||||
* @param {object} node 目标节点
|
||||
* @param {string} uuid 查找的 uuid
|
||||
* @param {object[]} result 结果
|
||||
*/
|
||||
findRefsInNode(tree, node, uuid, result) {
|
||||
// 检查节点上的组件是否有引用
|
||||
const components = node.components;
|
||||
if (components && components.length > 0) {
|
||||
for (let i = 0, l = components.length; i < l; i++) {
|
||||
const properties = Finder.getContainsUuidProperties(components[i], uuid);
|
||||
if (properties.length === 0) {
|
||||
continue;
|
||||
}
|
||||
// 资源类型
|
||||
let type = components[i]['__type__'];
|
||||
// 是否为脚本资源
|
||||
if (EditorAPI.isUuid(type)) {
|
||||
const scriptUuid = EditorAPI.decompressUuid(type),
|
||||
assetInfo = EditorAPI.assetInfoByUuid(scriptUuid);
|
||||
type = basename(assetInfo.url);
|
||||
}
|
||||
// 遍历相关属性名
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
let property = properties[i];
|
||||
if (property === '__type__') {
|
||||
property = null;
|
||||
} else {
|
||||
// 处理属性名称(Label 组件需要特殊处理)
|
||||
if (type === 'cc.Label' && property === '_N$file') {
|
||||
property = 'font';
|
||||
}
|
||||
// 去除属性名的前缀
|
||||
if (property.startsWith('_N$')) {
|
||||
property = property.replace('_N$', '');
|
||||
} else if (property[0] === '_') {
|
||||
property = property.substring(1);
|
||||
}
|
||||
}
|
||||
// 保存结果
|
||||
result.push({
|
||||
node: node.path,
|
||||
component: type,
|
||||
property: property,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 检查预制体是否有引用
|
||||
const prefab = node.prefab;
|
||||
if (prefab) {
|
||||
// 排除预制体自己
|
||||
if (uuid !== tree.uuid) {
|
||||
const contains = containsValue(prefab, uuid);
|
||||
if (contains) {
|
||||
result.push({
|
||||
node: node.path,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 遍历子节点
|
||||
const children = node.children;
|
||||
if (children && children.length > 0) {
|
||||
for (let i = 0, l = children.length; i < l; i++) {
|
||||
Finder.findRefsInNode(tree, children[i], uuid, result);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取对象包含指定 uuid 的属性
|
||||
* @param {object} object 对象
|
||||
* @param {string} uuid 值
|
||||
* @returns {string[]}
|
||||
*/
|
||||
getContainsUuidProperties(object, uuid) {
|
||||
const properties = [];
|
||||
const search = (target, path) => {
|
||||
if (Object.prototype.toString.call(target) === '[object Object]') {
|
||||
for (const key in target) {
|
||||
const curPath = (path != null) ? `${path}.${key}` : key;
|
||||
if (target[key] === uuid) {
|
||||
properties.push(path || key);
|
||||
}
|
||||
search(target[key], curPath);
|
||||
}
|
||||
} else if (Array.isArray(target)) {
|
||||
for (let i = 0, l = target.length; i < l; i++) {
|
||||
const curPath = (path != null) ? `${path}[${i}]` : `[${i}]`;
|
||||
if (target[i] === uuid) {
|
||||
properties.push(path || `[${i}]`);
|
||||
}
|
||||
search(target[i], curPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
search(object, null);
|
||||
return properties;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = Finder;
|
155
src/main/index.js
Normal file
155
src/main/index.js
Normal file
@ -0,0 +1,155 @@
|
||||
const PanelManager = require('./panel-manager');
|
||||
const ConfigManager = require('../common/config-manager');
|
||||
const EditorMainKit = require('../eazax/editor-main-kit');
|
||||
const { checkUpdate, print, translate } = require('../eazax/editor-main-util');
|
||||
const { openRepository } = require('../eazax/package-util');
|
||||
const EditorAPI = require('./editor-api');
|
||||
const Parser = require('./parser');
|
||||
const Finder = require('./finder');
|
||||
const Printer = require('./printer');
|
||||
|
||||
/**
|
||||
* 生命周期:加载
|
||||
*/
|
||||
function load() {
|
||||
// 监听事件
|
||||
EditorMainKit.register();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生命周期:卸载
|
||||
*/
|
||||
function unload() {
|
||||
// 取消事件监听
|
||||
EditorMainKit.unregister();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找当前选中资源
|
||||
*/
|
||||
async function findCurrentSelection() {
|
||||
// 过滤选中的资源 uuid
|
||||
const uuids = EditorAPI.getCurrentSelectedAssets();
|
||||
for (let i = 0; i < uuids.length; i++) {
|
||||
const assetInfo = EditorAPI.assetInfoByUuid(uuids[i]);
|
||||
if (assetInfo.type === 'folder') {
|
||||
uuids.splice(i--);
|
||||
}
|
||||
}
|
||||
// 未选择资源
|
||||
if (uuids.length === 0) {
|
||||
print('log', translate('please-select-assets'));
|
||||
return;
|
||||
}
|
||||
// 遍历查找
|
||||
for (let i = 0; i < uuids.length; i++) {
|
||||
const uuid = uuids[i],
|
||||
assetInfo = EditorAPI.assetInfoByUuid(uuid),
|
||||
shortUrl = assetInfo.url.replace('db://', '');
|
||||
// 查找引用
|
||||
print('log', '🔍', `${translate('find-asset-refs')} ${shortUrl}`);
|
||||
const refs = await Finder.findByUuid(uuid);
|
||||
if (refs.length === 0) {
|
||||
print('log', '📂', `${translate('no-refs')} ${shortUrl}`);
|
||||
continue;
|
||||
}
|
||||
// 打印结果
|
||||
Printer.printResult({
|
||||
type: assetInfo.type,
|
||||
uuid: uuid,
|
||||
url: assetInfo.url,
|
||||
path: assetInfo.path,
|
||||
refs: refs,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getSelection() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源变化回调
|
||||
* @param {{ type: string, uuid: string }} info
|
||||
*/
|
||||
function onAssetChanged(info) {
|
||||
const { type, uuid } = info;
|
||||
// 场景和预制体
|
||||
if (type === 'scene' || type === 'prefab') {
|
||||
const { url, path } = EditorAPI.assetInfoByUuid(uuid);
|
||||
// 排除内置资源
|
||||
if (url.indexOf('db://internal') !== -1) {
|
||||
return;
|
||||
}
|
||||
// 更新节点树
|
||||
Parser.updateCache(path);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* 扩展消息
|
||||
*/
|
||||
messages: {
|
||||
|
||||
/**
|
||||
* 查找当前选中资源
|
||||
* @param {*} event
|
||||
*/
|
||||
'find-current-selection'(event) {
|
||||
findCurrentSelection();
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开设置面板
|
||||
* @param {*} event
|
||||
*/
|
||||
'open-settings-panel'(event) {
|
||||
PanelManager.openSettingsPanel();
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
* @param {*} event
|
||||
*/
|
||||
'menu-check-update'(event) {
|
||||
checkUpdate(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 版本
|
||||
* @param {*} event
|
||||
*/
|
||||
'menu-version'(event) {
|
||||
openRepository();
|
||||
},
|
||||
|
||||
/**
|
||||
* 场景面板加载完成后
|
||||
* @param {*} event
|
||||
*/
|
||||
'scene:ready'(event) {
|
||||
// 自动检查更新
|
||||
const config = ConfigManager.get();
|
||||
if (config.autoCheckUpdate) {
|
||||
checkUpdate(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 资源变化
|
||||
* @param {*} event
|
||||
* @param {{ type: string, uuid: string }} info
|
||||
*/
|
||||
'asset-db:asset-changed'(event, info) {
|
||||
onAssetChanged(info);
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
load,
|
||||
|
||||
unload,
|
||||
|
||||
};
|
70
src/main/object-util.js
Normal file
70
src/main/object-util.js
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 对象工具
|
||||
* @author 陈皮皮 (ifaswind)
|
||||
* @version 20210929
|
||||
*/
|
||||
const ObjectUtil = {
|
||||
|
||||
/**
|
||||
* 判断指定值是否是一个对象
|
||||
* @param {any} arg 参数
|
||||
*/
|
||||
isObject(arg) {
|
||||
return Object.prototype.toString.call(arg) === '[object Object]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 对象中是否包含指定的属性
|
||||
* @param {object} object 对象
|
||||
* @param {string} name 属性名
|
||||
*/
|
||||
containsProperty(object, name) {
|
||||
let result = false;
|
||||
const search = (_object) => {
|
||||
if (ObjectUtil.isObject(_object)) {
|
||||
for (const key in _object) {
|
||||
if (key == name) {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
search(_object[key]);
|
||||
}
|
||||
} else if (Array.isArray(_object)) {
|
||||
for (let i = 0, l = _object.length; i < l; i++) {
|
||||
search(_object[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
search(object);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 对象中是否包含指定的值
|
||||
* @param {object} object 对象
|
||||
* @param {any} value 值
|
||||
*/
|
||||
containsValue(object, value) {
|
||||
let result = false;
|
||||
const search = (_object) => {
|
||||
if (ObjectUtil.isObject(_object)) {
|
||||
for (const key in _object) {
|
||||
if (_object[key] === value) {
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
search(_object[key]);
|
||||
}
|
||||
} else if (Array.isArray(_object)) {
|
||||
for (let i = 0, l = _object.length; i < l; i++) {
|
||||
search(_object[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
search(object);
|
||||
return result;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ObjectUtil;
|
86
src/main/panel-manager.js
Normal file
86
src/main/panel-manager.js
Normal file
@ -0,0 +1,86 @@
|
||||
const { BrowserWindow } = require('electron');
|
||||
const { join } = require('path');
|
||||
const { language, translate } = require('../eazax/editor-main-util');
|
||||
const { calcWindowPosition } = require('../eazax/window-util');
|
||||
|
||||
/** 扩展名称 */
|
||||
const EXTENSION_NAME = translate('name');
|
||||
|
||||
/**
|
||||
* 面板管理器 (主进程)
|
||||
*/
|
||||
const PanelManager = {
|
||||
|
||||
/**
|
||||
* 面板实例
|
||||
* @type {BrowserWindow}
|
||||
*/
|
||||
settings: null,
|
||||
|
||||
/**
|
||||
* 打开设置面板
|
||||
*/
|
||||
openSettingsPanel() {
|
||||
// 已打开则直接展示
|
||||
if (PanelManager.settings) {
|
||||
PanelManager.settings.show();
|
||||
return;
|
||||
}
|
||||
// 窗口尺寸和位置
|
||||
const winSize = [500, 346],
|
||||
winPos = calcWindowPosition(winSize, 'center');
|
||||
// 创建窗口
|
||||
const win = PanelManager.settings = new BrowserWindow({
|
||||
width: winSize[0],
|
||||
height: winSize[1],
|
||||
minWidth: winSize[0],
|
||||
minHeight: winSize[1],
|
||||
x: winPos[0],
|
||||
y: winPos[1] - 100,
|
||||
useContentSize: true,
|
||||
frame: true,
|
||||
title: `${EXTENSION_NAME} | Cocos Creator`,
|
||||
autoHideMenuBar: true,
|
||||
resizable: true,
|
||||
minimizable: false,
|
||||
maximizable: false,
|
||||
fullscreenable: false,
|
||||
skipTaskbar: false,
|
||||
alwaysOnTop: true,
|
||||
hasShadow: true,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
});
|
||||
// 就绪后(展示,避免闪烁)
|
||||
win.on('ready-to-show', () => win.show());
|
||||
// 关闭后
|
||||
win.on('closed', () => (PanelManager.settings = null));
|
||||
// 监听按键
|
||||
win.webContents.on('before-input-event', (event, input) => {
|
||||
if (input.key === 'Escape') PanelManager.closeSettingsPanel();
|
||||
});
|
||||
// 调试用的 devtools
|
||||
// win.webContents.openDevTools({ mode: 'detach' });
|
||||
// 加载页面
|
||||
const path = join(__dirname, '../renderer/settings/index.html');
|
||||
win.loadURL(`file://${path}?lang=${language}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭面板
|
||||
*/
|
||||
closeSettingsPanel() {
|
||||
if (!PanelManager.settings) {
|
||||
return;
|
||||
}
|
||||
PanelManager.settings.hide();
|
||||
PanelManager.settings.close();
|
||||
PanelManager.settings = null;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = PanelManager;
|
161
src/main/parser.js
Normal file
161
src/main/parser.js
Normal file
@ -0,0 +1,161 @@
|
||||
const { print } = require("../eazax/editor-main-util");
|
||||
const FileUtil = require("../eazax/file-util");
|
||||
const { containsProperty } = require("./object-util");
|
||||
|
||||
/**
|
||||
* 解析器
|
||||
*/
|
||||
const Parser = {
|
||||
|
||||
/**
|
||||
* 节点树缓存
|
||||
* @type {{ [key: string]: object }}
|
||||
*/
|
||||
caches: Object.create(null),
|
||||
|
||||
/**
|
||||
* 获取节点树
|
||||
* @param {string} path 路径
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async getNodeTree(path) {
|
||||
if (!Parser.caches[path]) {
|
||||
const file = await FileUtil.readFile(path);
|
||||
let data = null;
|
||||
try {
|
||||
data = JSON.parse(file);
|
||||
} catch (error) {
|
||||
print('warn', '文件解析失败', path);
|
||||
print('warn', error);
|
||||
}
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
Parser.caches[path] = Parser.convert(data);
|
||||
}
|
||||
return Parser.caches[path];
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新缓存
|
||||
* @param {string} path 路径
|
||||
*/
|
||||
async updateCache(path) {
|
||||
Parser.caches[path] = null;
|
||||
await Parser.getNodeTree(path);
|
||||
},
|
||||
|
||||
/**
|
||||
* 将资源解析为节点树
|
||||
* @param {object} source 源数据
|
||||
* @returns {object}
|
||||
*/
|
||||
convert(source) {
|
||||
const tree = Object.create(null),
|
||||
type = source[0]['__type__'];
|
||||
if (type === 'cc.SceneAsset') {
|
||||
// 场景资源
|
||||
const sceneId = source[0]['scene']['__id__'],
|
||||
children = source[sceneId]['_children'];
|
||||
tree.type = 'cc.Scene'; // 类型
|
||||
tree.id = sceneId; // ID
|
||||
// 场景下可以有多个一级节点
|
||||
tree.children = [];
|
||||
for (let i = 0, l = children.length; i < l; i++) {
|
||||
const nodeId = children[i]['__id__'];
|
||||
Parser.convertNode(source, nodeId, tree);
|
||||
}
|
||||
} else if (type === 'cc.Prefab') {
|
||||
// 预制体资源
|
||||
const uuid = source[source.length - 1]['asset']['__uuid__'];
|
||||
tree.type = 'cc.Prefab'; // 类型
|
||||
tree.uuid = uuid; // uuid
|
||||
// 预制体本身就是一个节点
|
||||
tree.children = [];
|
||||
const nodeId = source[0]['data']['__id__'];
|
||||
Parser.convertNode(source, nodeId, tree);
|
||||
}
|
||||
return tree;
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析节点
|
||||
* @param {object} source 源数据
|
||||
* @param {number} nodeId 节点 ID
|
||||
* @param {object} parent 父节点
|
||||
*/
|
||||
convertNode(source, nodeId, parent) {
|
||||
const srcNode = source[nodeId],
|
||||
node = Object.create(null);
|
||||
// 基本信息
|
||||
node.name = srcNode['_name'];
|
||||
node.id = nodeId;
|
||||
node.type = srcNode['__type__'];
|
||||
|
||||
// 路径
|
||||
const parentPath = parent.path || null;
|
||||
node.path = parentPath ? `${parentPath}/${node.name}` : node.name;
|
||||
|
||||
// 预制体引用
|
||||
const srcPrefab = srcNode['_prefab'];
|
||||
if (srcPrefab) {
|
||||
const id = srcPrefab['__id__'];
|
||||
node.prefab = Parser.extractValidInfo(source[id]);
|
||||
}
|
||||
|
||||
// 组件
|
||||
node.components = [];
|
||||
const srcComponents = srcNode['_components'];
|
||||
if (srcComponents && srcComponents.length > 0) {
|
||||
for (let i = 0, l = srcComponents.length; i < l; i++) {
|
||||
const compId = srcComponents[i]['__id__'],
|
||||
component = Parser.extractValidInfo(source[compId]);
|
||||
node.components.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
// 子节点
|
||||
node.children = [];
|
||||
const srcChildren = srcNode['_children'];
|
||||
if (srcChildren && srcChildren.length > 0) {
|
||||
for (let i = 0, l = srcChildren.length; i < l; i++) {
|
||||
const nodeId = srcChildren[i]['__id__'];
|
||||
Parser.convertNode(source, nodeId, node);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到父节点
|
||||
parent.children.push(node);
|
||||
},
|
||||
|
||||
/**
|
||||
* 提取有效信息(含有 uuid)
|
||||
* @param {object} source 源数据
|
||||
* @returns {{ __type__: string, _name: string, fileId?: string }}
|
||||
*/
|
||||
extractValidInfo(source) {
|
||||
const result = Object.create(null);
|
||||
|
||||
// 记录有用的属性
|
||||
const keys = ['__type__', '_name', 'fileId'];
|
||||
for (let i = 0, l = keys.length; i < l; i++) {
|
||||
const key = keys[i];
|
||||
if (source[key] !== undefined) {
|
||||
result[key] = source[key];
|
||||
}
|
||||
}
|
||||
|
||||
// 记录包含 uuid 的属性
|
||||
for (const key in source) {
|
||||
const contains = containsProperty(source[key], '__uuid__');
|
||||
if (contains) {
|
||||
result[key] = source[key];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = Parser;
|
108
src/main/printer.js
Normal file
108
src/main/printer.js
Normal file
@ -0,0 +1,108 @@
|
||||
const { translate, print, pureWithoutTitle } = require('../eazax/editor-main-util');
|
||||
const ConfigManager = require('../common/config-manager');
|
||||
|
||||
/** 图标表 */
|
||||
const ICON_MAP = {
|
||||
'scene': '🔥',
|
||||
'prefab': '💠',
|
||||
'node': '🎲',
|
||||
'component': '🧩',
|
||||
'property': '📄',
|
||||
'asset': '📦',
|
||||
'asset-info': '📋',
|
||||
'node-refs': '📙',
|
||||
'asset-refs': '📗',
|
||||
};
|
||||
|
||||
/**
|
||||
* 打印机
|
||||
*/
|
||||
const Printer = {
|
||||
|
||||
/**
|
||||
* 打印结果至控制台
|
||||
* @param {object} result
|
||||
*/
|
||||
printResult(result) {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
const { printDetails, printFolding } = ConfigManager.get();
|
||||
// 标志位
|
||||
const nodeRefs = [], assetRefs = [];
|
||||
let nodeRefsCount = 0, assetRefsCount = 0;
|
||||
// 遍历引用信息
|
||||
for (let refs = result.refs, i = 0, l = refs.length; i < l; i++) {
|
||||
const ref = refs[i],
|
||||
type = ref.type,
|
||||
url = ref.url.replace('db://', '').replace('.meta', '');
|
||||
if (type === 'scene' || type === 'prefab') {
|
||||
// 场景或预制体
|
||||
nodeRefs.push(` ${ICON_MAP[type]} [${translate(type)}] ${url}`);
|
||||
// 节点引用
|
||||
for (let details = ref.refs, j = 0, l = details.length; j < l; j++) {
|
||||
nodeRefsCount++;
|
||||
// 详情
|
||||
if (printDetails) {
|
||||
const detail = details[j];
|
||||
let item = ` ${ICON_MAP['node']} [${translate('node')}] ${detail.node}`;
|
||||
if (detail.component) {
|
||||
item += ` → ${ICON_MAP['component']} [${translate('component')}] ${detail.component}`;
|
||||
}
|
||||
if (detail.property) {
|
||||
item += ` → ${ICON_MAP['property']} [${translate('property')}] ${detail.property}`;
|
||||
}
|
||||
nodeRefs.push(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 资源引用
|
||||
assetRefsCount++;
|
||||
assetRefs.push(` ${ICON_MAP['asset']} [${translate(type)}] ${url}`);
|
||||
}
|
||||
}
|
||||
// 组装文本
|
||||
const texts = [];
|
||||
// 分割线
|
||||
texts.push(`${'- - '.repeat(36)}`);
|
||||
// 基础信息
|
||||
texts.push(`${ICON_MAP['asset-info']} ${translate('asset-info')}`);
|
||||
texts.push(` - ${translate('asset-type')}${result.type}`);
|
||||
texts.push(` - ${translate('asset-uuid')}${result.uuid}`);
|
||||
texts.push(` - ${translate('asset-url')}${result.url}`);
|
||||
texts.push(` - ${translate('asset-path')}${result.path}`);
|
||||
// 分割线
|
||||
texts.push(`${'- - '.repeat(36)}`);
|
||||
// 节点引用
|
||||
if (nodeRefs.length > 0) {
|
||||
texts.push(`${ICON_MAP['node-refs']} ${translate('node-refs')} x ${nodeRefsCount}`);
|
||||
for (let i = 0, l = nodeRefs.length; i < l; i++) {
|
||||
texts.push(nodeRefs[i]);
|
||||
}
|
||||
}
|
||||
// 资源引用
|
||||
if (assetRefs.length > 0) {
|
||||
texts.push(`${ICON_MAP['asset-refs']} ${translate('asset-refs')} x ${assetRefsCount}`);
|
||||
for (let i = 0, l = assetRefs.length; i < l; i++) {
|
||||
texts.push(assetRefs[i]);
|
||||
}
|
||||
}
|
||||
// 结尾分割线
|
||||
texts.push(`${'- - '.repeat(36)}`);
|
||||
// 打印到控制台
|
||||
if (printFolding) {
|
||||
// 单行打印
|
||||
texts.unshift(`🗂 ${translate('result')} >>>`);
|
||||
print('log', texts.join('\n'));
|
||||
} else {
|
||||
// 逐行打印
|
||||
print('log', translate('result'));
|
||||
for (let i = 0, l = texts.length; i < l; i++) {
|
||||
pureWithoutTitle(` ${texts[i]}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = Printer;
|
61
src/renderer/settings/index.css
Normal file
61
src/renderer/settings/index.css
Normal file
@ -0,0 +1,61 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
background-color: #454545;
|
||||
color: #bdbdbd;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* 属性容器 */
|
||||
.properties {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* 应用按钮 */
|
||||
.apply-btn {
|
||||
min-width: 20px;
|
||||
height: 33px;
|
||||
background-image: linear-gradient(#4281b6, #4281b6);
|
||||
border: 1px solid #171717;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.apply-btn:hover {
|
||||
background-image: none;
|
||||
background-color: #4c87b6;
|
||||
}
|
||||
|
||||
.apply-btn:active {
|
||||
background-image: none;
|
||||
background-color: #2e6da2;
|
||||
border-color: #fd942b;
|
||||
color: #cdcdcd;
|
||||
box-shadow: 1px 1px 10px #262626 inset;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
90
src/renderer/settings/index.html
Normal file
90
src/renderer/settings/index.html
Normal file
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- 样式 -->
|
||||
<link rel="stylesheet" type="text/css" href="../../eazax/css/cocos-tag.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../eazax/css/cocos-class.css">
|
||||
<link rel="stylesheet" type="text/css" href="index.css">
|
||||
<!-- 脚本 -->
|
||||
<script type="text/javascript" src="../../../lib/vue.global.prod.js" defer></script>
|
||||
<script type="text/javascript" src="index.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" v-cloak>
|
||||
<!-- 标题 -->
|
||||
<div class="title">{{ t('settings') }}</div>
|
||||
<!-- 配置 -->
|
||||
<div class="properties">
|
||||
<!-- 选择快捷键 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('select-key') }}</span>
|
||||
<span class="tooltip">{{ t('select-key-tooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<select v-model="selectKey">
|
||||
<option v-for="item in presets" :key="item.key" :value="item.key">{{ item.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 自定义快捷键 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('custom-key') }}</span>
|
||||
<span class="tooltip">{{ t('custom-key-tooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input v-model="customKey" :placeholder="t('custom-key-placeholder')" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 展示详情 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('print-details') }}</span>
|
||||
<span class="tooltip">{{ t('print-details-tooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="printDetails" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 折叠结果 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('print-folding') }}</span>
|
||||
<span class="tooltip">{{ t('print-folding-tooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="printFolding" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 自动检查更新 -->
|
||||
<div class="property">
|
||||
<div class="label">
|
||||
<span class="text">{{ t('auto-check-update') }}</span>
|
||||
<span class="tooltip">{{ t('auto-check-update-tooltip') }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<input type="checkbox" v-model="autoCheckUpdate" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 快捷键参考 -->
|
||||
<div class="tip">
|
||||
<span>{{ t('reference') }}</span>
|
||||
<a href="https://www.electronjs.org/docs/api/accelerator" target="_blank">{{ t('accelerator') }}</a>
|
||||
</div>
|
||||
<!-- Git 仓库 -->
|
||||
<div class="tip">
|
||||
<span>{{ t('repository') }}</span>
|
||||
<a :href="repositoryUrl" target="_blank">{{ packageName }}</a>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<!-- 应用按钮 -->
|
||||
<button class="apply-btn" @click="onApplyBtnClick">{{ t('apply') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
191
src/renderer/settings/index.js
Normal file
191
src/renderer/settings/index.js
Normal file
@ -0,0 +1,191 @@
|
||||
const { shell } = require('electron');
|
||||
const { getUrlParam } = require('../../eazax/browser-util');
|
||||
const I18n = require('../../eazax/i18n');
|
||||
const RendererEvent = require('../../eazax/renderer-event');
|
||||
const PackageUtil = require('../../eazax/package-util');
|
||||
const EditorRendererKit = require('../../eazax/editor-renderer-kit');
|
||||
const ConfigManager = require('../../common/config-manager');
|
||||
|
||||
// 导入 Vue 工具函数
|
||||
const { ref, watch, onMounted, onBeforeUnmount, createApp } = Vue;
|
||||
|
||||
/** 当前语言 */
|
||||
const LANG = getUrlParam('lang');
|
||||
|
||||
// 构建 Vue 应用
|
||||
const App = {
|
||||
|
||||
/**
|
||||
* 设置
|
||||
* @param {*} props
|
||||
* @param {*} context
|
||||
*/
|
||||
setup(props, context) {
|
||||
|
||||
// 预设快捷键
|
||||
const presets = ref([
|
||||
{ key: '', name: t('none') },
|
||||
{ key: 'custom', name: t('custom-key') },
|
||||
{ key: 'F1', name: 'F1' },
|
||||
{ key: 'F3', name: 'F3' },
|
||||
{ key: 'F4', name: 'F4' },
|
||||
{ key: 'F5', name: 'F5' },
|
||||
{ key: 'F6', name: 'F6' },
|
||||
{ key: 'CmdOrCtrl+F', name: 'Cmd/Ctrl + F' },
|
||||
{ key: 'CmdOrCtrl+B', name: 'Cmd/Ctrl + B' },
|
||||
{ key: 'CmdOrCtrl+Shift+F', name: 'Cmd/Ctrl + Shift + F' },
|
||||
]);
|
||||
// 选择
|
||||
const selectKey = ref('');
|
||||
// 自定义
|
||||
const customKey = ref('');
|
||||
// 打印详情
|
||||
const printDetails = ref(true);
|
||||
// 单行打印
|
||||
const printFolding = ref(true);
|
||||
// 自动检查更新
|
||||
const autoCheckUpdate = ref(false);
|
||||
|
||||
// 仓库地址
|
||||
const repositoryUrl = PackageUtil.repository;
|
||||
// 包名
|
||||
const packageName = PackageUtil.name;
|
||||
|
||||
// 监听选择快捷键
|
||||
watch(selectKey, (value) => {
|
||||
if (value !== 'custom') {
|
||||
customKey.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// 监听自定义
|
||||
watch(customKey, (value) => {
|
||||
if (value !== '' && selectKey.value !== 'custom') {
|
||||
selectKey.value = 'custom';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*/
|
||||
function getConfig() {
|
||||
const config = ConfigManager.get();
|
||||
if (!config) return;
|
||||
// 配置
|
||||
printDetails.value = config.printDetails;
|
||||
printFolding.value = config.printFolding;
|
||||
autoCheckUpdate.value = config.autoCheckUpdate;
|
||||
// 快捷键
|
||||
const hotkey = config.hotkey;
|
||||
if (!hotkey || hotkey === '') {
|
||||
selectKey.value = '';
|
||||
customKey.value = '';
|
||||
return;
|
||||
}
|
||||
// 预设快捷键
|
||||
for (let i = 0, l = presets.value.length; i < l; i++) {
|
||||
if (presets.value[i].key === hotkey) {
|
||||
selectKey.value = hotkey;
|
||||
customKey.value = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 自定义快捷键
|
||||
selectKey.value = 'custom';
|
||||
customKey.value = hotkey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存配置
|
||||
*/
|
||||
function setConfig() {
|
||||
const config = {
|
||||
hotkey: null,
|
||||
printDetails: printDetails.value,
|
||||
printFolding: printFolding.value,
|
||||
autoCheckUpdate: autoCheckUpdate.value,
|
||||
};
|
||||
if (selectKey.value === 'custom') {
|
||||
// 自定义输入是否有效
|
||||
if (customKey.value === '') {
|
||||
EditorRendererKit.print('warn', t('custom-key-error'));
|
||||
return;
|
||||
}
|
||||
// 不可以使用双引号(避免 json 值中出现双引号而解析错误,导致插件加载失败)
|
||||
if (customKey.value.includes('"')) {
|
||||
customKey.value = customKey.value.replace(/\"/g, '');
|
||||
EditorRendererKit.print('warn', t('quote-error'));
|
||||
return;
|
||||
}
|
||||
config.hotkey = customKey.value;
|
||||
} else {
|
||||
config.hotkey = selectKey.value;
|
||||
}
|
||||
// 保存到本地
|
||||
ConfigManager.set(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用按钮点击回调
|
||||
* @param {*} event
|
||||
*/
|
||||
function onApplyBtnClick(event) {
|
||||
// 保存配置
|
||||
setConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译
|
||||
* @param {string} key
|
||||
*/
|
||||
function t(key) {
|
||||
return I18n.get(LANG, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生命周期:挂载后
|
||||
*/
|
||||
onMounted(() => {
|
||||
// 获取配置
|
||||
getConfig();
|
||||
// 覆盖 a 标签点击回调(使用默认浏览器打开网页)
|
||||
const links = document.querySelectorAll('a[href]');
|
||||
links.forEach((link) => {
|
||||
link.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const url = link.getAttribute('href');
|
||||
shell.openExternal(url);
|
||||
});
|
||||
});
|
||||
// (主进程)检查更新
|
||||
RendererEvent.send('check-update', false);
|
||||
});
|
||||
|
||||
/**
|
||||
* 生命周期:卸载前
|
||||
*/
|
||||
onBeforeUnmount(() => {
|
||||
|
||||
});
|
||||
|
||||
return {
|
||||
presets,
|
||||
selectKey,
|
||||
customKey,
|
||||
printDetails,
|
||||
printFolding,
|
||||
autoCheckUpdate,
|
||||
repositoryUrl,
|
||||
packageName,
|
||||
onApplyBtnClick,
|
||||
t,
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
// 创建实例
|
||||
const app = createApp(App);
|
||||
// 挂载
|
||||
app.mount('#app');
|
Loading…
Reference in New Issue
Block a user