Commit e1efc4fe authored by liujiangnan's avatar liujiangnan

feat: 添加组件

parent a3f5ba9f
No preview for this file type
var fs = require('fs-extra');
var file = require('./util/file');
module.exports = {
load() {
},
unload() {
},
replaceDirUuid: function (path, dbpath) {
Editor.log('开始检查:' + path);
file.findDirUuid(path);
Editor.log('资源检查完成');
},
messages: {
'checkFileName'() {
var uuids = Editor.Selection.curSelection('asset');
uuids.forEach((uuid) => {
var dir_path = Editor.assetdb._uuid2path[uuid];
if (fs.existsSync(dir_path)) {
this.replaceDirUuid(dir_path, Editor.assetdb.uuidToUrl(uuid));
}
});
},
},
}
\ No newline at end of file
{
"name": "check-file-name",
"version": "0.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "0.0.1",
"dependencies": {
"node-uuid": "1.4.8"
}
},
"node_modules/node-uuid": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
"deprecated": "Use uuid module instead",
"bin": {
"uuid": "bin/uuid"
}
}
},
"dependencies": {
"node-uuid": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
}
}
}
{
"name": "check-file-name",
"version": "0.0.1",
"description": "check-file-name",
"author": "Cocos Creator",
"main": "main.js",
"main-menu": {
"i18n:MAIN_MENU.package.title/check-file-name": {
"message": "check-file-name:checkFileName"
}
},
"dependencies": {
"node-uuid": "1.4.8"
}
}
var fs = require("fs-extra");
var path = require("path");
var AppName = ""
module.exports = {
/**
* 递归目录 检查文件名
* 参考 https://docs.cocos.com/creator/manual/zh/advanced-topics/meta.html
*/
findDirUuid: function (dir) {
if(AppName == '') {
AppName = this.getRootDirName(dir);
if(AppName != "") {
Editor.log("AppName: " + AppName);
}
}
var stat = fs.statSync(dir);
if (!stat.isDirectory()) {
return;
}
var subpaths = fs.readdirSync(dir),
subpath;
for (var i = 0; i < subpaths.length; ++i) {
if (subpaths[i][0] === ".") {
continue;
}
subpath = path.join(dir, subpaths[i]);
stat = fs.statSync(subpath);
if (stat.isDirectory()) {
this.findDirUuid(subpath);
} else if (stat.isFile()) {
var metastr = subpath.substr(subpath.length - 5, 5);
if (metastr != ".meta") {
this.check(AppName, subpaths[i]);
}
}
}
},
getRootDirName: function (path) {
let pArr = path.split("/");
let assteIndex = -1;
pArr.find((item, index) => {
if(item == 'assets') {
assteIndex = index;
return true
} else {
return false
}
})
if(assteIndex > 0 && assteIndex<(pArr.length-1)) {
return pArr[assteIndex + 1];
} else {
return ""
}
},
check: (appName, filePath) => {
if(escape(filePath).indexOf("%u")>=0) {
Editor.log(`检测到[中文或中文符号]命名的文件: ${filePath}`);
}
if (!/^\S*$/.test(filePath)) {
Editor.log(`检测到[包含空格]命名的文件: ${filePath}`);
}
if (/[-]/.test(filePath)) {
Editor.log(`检测到存在[包含减号(-)]命名的文件: ${filePath}`);
}
if(filePath.indexOf(appName) == -1 && (filePath.endsWith(".js") || filePath.endsWith(".ts"))) {
Editor.log(`检测到[不包含包名(${appName})]的文件: ${filePath}`);
}
}
};
# 配置文件
config.json
\ No newline at end of file
### 1.3.0.20210929
1. 代码重构
2. 引入 eazax 框架
3. 查找性能大幅提升
4. 文件解析优化,增强稳定性
5. 去除「通过 uuid 查找」面板
6. 加入检查更新机制
### 1.2.0.20210405
1. 修复“双开编辑器卡死”的问题
2. 代码优化,提高查找速度
3. 增加多语言(英语)支持
4. 支持自定义快捷键(设置面板)
### 1.1.0-20201112
1. 增加“通过 uuid 查找”功能(新增面板)
2. 优化代码
### 1.0.0-20201103
1. 首次发布
MIT License
Copyright (c) 2021 陈皮皮
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# References Finder
## Introduction
[Cocos Creator Editor Extension]
**Find asset references in seconds by one press.**
## Open Source
This extension is an open source project, here is the git repository: [https://gitee.com/ifaswind/ccc-references-finder](https://gitee.com/ifaswind/ccc-references-finder)
If you like this project, don't forget to star [![star](https://gitee.com/ifaswind/ccc-references-finder/badge/star.svg?theme=dark)](https://gitee.com/ifaswind/ccc-references-finder/stargazers)!
*If you have any usage problems, just create an issue on Gitee or add my WeChat `im_chenpipi` and leave a message.*
## Screenshots
![screenshot-1](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/screenshot-1.png)
![screenshot-2](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/screenshot-2.png)
![settings-panel](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/settings-panel.png)
## Environment
Platform: Windows、macOS
Engine: Cocos Creator 2.x
## Download & Installation
### Install from Cocos Store
You can find this extension in Cocos Store now, click on *Extension -> Cocos Store* option to open the Cocos Store.
Enter "**References Finder**" in the search bar, find it and then install it.
![cocos-store](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/cocos-store.png)
*References Finder: [https://store.cocos.com/app/detail/2531](https://store.cocos.com/app/detail/2531)*
### Download from git repository
Click [here](https://gitee.com/ifaswind/ccc-references-finder/releases) or go to the release panel, download the latest version package of this extension.
And then unzip the package:
- Windows: Unzip to `C:\Users\${your username}\.CocosCreator\packages\`
- macOS: Unzip to `~/.CocosCreator/packages/`
For example, on my Windows computer, the full path of `package.json` file should be `C:\Users\Shaun\.CocosCreator\packages\ccc-references-finder\package.json`.
## Usage
### Find Asset References
1. Select any asset(s) in Asset Panel.
2. Press the hotkey (The default is `F6`) or click on *Extension -> References Finder -> Find Selected* option to find references.
3. Then all references of asset would be printed on Console Panel.
### Settings
Click on *Extension -> References Finder -> Setting* option to open the setting panel.
- **Show Details**: Show more details(node, component, property)
- **Fold Result**: Fold result in one log
In the **Hotkey** option, you can choose a hotkey(shortcut, for finding references of current selected asset) in preset list, or you can customize one in **Custom** option by yourself.
One thing you should know, not every keys/keys-combinations can be used, because some keys/keys-combinations have been used by the system or Cocos Creator.
*Accelerator reference: [https://www.electronjs.org/docs/api/accelerator](https://www.electronjs.org/docs/api/accelerator)*
🥳 Enjoy!
---
# 公众号
## 菜鸟小栈
😺 我是陈皮皮,一个还在不断学习的游戏开发者,一个热爱分享的 Cocos Star Writer。
🎨 这是我的个人公众号,专注但不仅限于游戏开发和前端技术分享。
💖 每一篇原创都非常用心,你的关注就是我原创的动力!
> Input and output.
![](https://gitee.com/ifaswind/image-storage/raw/master/weixin/official-account.png)
\ No newline at end of file
# 引用查找器
## 介绍
[Cocos Creator 编辑器扩展]
**一键查找资源的所有引用,可精确到预制体或场景中的节点、组件和属性(不包括代码中的动态引用)。**
## 开源
本扩展项目完全开源,仓库地址:[https://gitee.com/ifaswind/ccc-references-finder](https://gitee.com/ifaswind/ccc-references-finder)
如果你觉得这个项目还不错,请不要忘记点 [![star](https://gitee.com/ifaswind/ccc-references-finder/badge/star.svg?theme=dark)](https://gitee.com/ifaswind/ccc-references-finder/stargazers)!
*如有使用上的问题,可以在 Gitee 仓库中提 Issue 或者添加我的微信 `im_chenpipi` 并留言。*
## 截图
![screenshot-1](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/screenshot-1.png)
![screenshot-2](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/screenshot-2.png)
![settings-panel](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/settings-panel.png)
## 运行环境
平台:Windows、macOS
引擎:Cocos Creator 2.x
## 下载 & 安装
### 扩展商店安装
本扩展已上架 Cocos 商店,点击 Cocos Creator 编辑器顶部菜单栏中的 *扩展 -> 扩展商店* 即可打开扩展商店。
在页面上方的搜索栏中搜索“**引用查找器**”就可以找到本插件,进入详情页即可直接安装(建议安装到全局)。
![cocos-store](https://gitee.com/ifaswind/image-storage/raw/master/repositories/ccc-references-finder/cocos-store.png)
*引用查找器:[https://store.cocos.com/app/detail/2531](https://store.cocos.com/app/detail/2531)*
### 自行下载安装
[此处](https://gitee.com/ifaswind/ccc-references-finder/releases)或仓库发行版处下载最新的扩展压缩包。
下载完成后将压缩包解压:
- Windows:解压到 `C:\Users\${你的用户名}\.CocosCreator\packages\` 目录下
- macOS:解压到 `~/.CocosCreator/packages/` 目录下
以 Windows 为例,扩展的 `package.json` 文件在我的电脑上的完整目录为 `C:\Users\Shaun\.CocosCreator\packages\ccc-references-finder\package.json`。
## 使用说明
### 一键查找资源引用
安装本扩展后,在资源管理器中选中任意资源,按下快捷键(默认为 `F6`)即可获取该资源的所有引用(不包括代码中的动态引用),结果将在控制台中以文本的方式打印出来。
> 查找快捷键可进入扩展的设置面板进行修改
### 设置
点击编辑器顶部菜单栏中的 *扩展 -> 引用查找器 -> 设置* 即可打开扩展的设置面板。
- **自动展开结果**:切换不同的结果展示方式(自动展开或手动展开)
- **精确到组件属性**:引用信息精确到预制体或场景中的节点上的组件和属性(有的话)
在设置面板中你可以更换快速查找引用的快捷键,也可以自定义一个自己喜欢的快捷键。
不过需要注意的是,并非所有的按键都可以使用,因为有些快捷键已被系统或 Cocos Creator 占用。
*键盘快捷键参考:[https://www.electronjs.org/docs/api/accelerator](https://www.electronjs.org/docs/api/accelerator)*
---
# 公众号
## 菜鸟小栈
😺 我是陈皮皮,一个还在不断学习的游戏开发者,一个热爱分享的 Cocos Star Writer。
🎨 这是我的个人公众号,专注但不仅限于游戏开发和前端技术分享。
💖 每一篇原创都非常用心,你的关注就是我原创的动力!
> Input and output.
![](https://gitee.com/ifaswind/image-storage/raw/master/weixin/official-account.png)
## 游戏开发交流群
皮皮创建了一个**游戏开发交流群**,供小伙伴们交流开发经验、问题求助和摸鱼(划掉)。
感兴趣的小伙伴可以添加我微信 `im_chenpipi` 并留言 `加群`
\ No newline at end of file
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',
};
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': '引用查找结果将需要手动展开,拯救你的控制台',
};
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"checkJs": false
},
"exclude": [
"node_modules"
],
"include": [
"lib/**/*",
"src/**/*",
"typings/**/*"
]
}
\ No newline at end of file
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "ccc-references-finder",
"version": "1.3.0.20210929",
"description": "一键查找资源的所有引用,可精确到预制体或场景中的节点、组件和属性(不包括代码中的动态引用)。",
"author": {
"name": "陈皮皮 (ifaswind)",
"email": "952157129@qq.com",
"url": "https://chenpipi.cn",
"wechat": "im_chenpipi",
"git-home": "https://gitee.com/ifaswind",
"official-account": "公众号「菜鸟小栈」"
},
"repository": "https://gitee.com/ifaswind/ccc-references-finder",
"license": "MIT",
"main": "src/main/index.js",
"main-menu": {
"i18n:MAIN_MENU.package.title/i18n:ccc-references-finder.name/i18n:ccc-references-finder.find": {
"message": "ccc-references-finder:find-current-selection",
"icon": "/images/search.png",
"accelerator": "F6"
},
"i18n:MAIN_MENU.package.title/i18n:ccc-references-finder.name/i18n:ccc-references-finder.settings": {
"message": "ccc-references-finder:open-settings-panel",
"icon": "/images/settings.png"
},
"i18n:MAIN_MENU.package.title/i18n:ccc-references-finder.name/i18n:ccc-references-finder.check-update": {
"message": "ccc-references-finder:menu-check-update",
"icon": "/images/update.png"
},
"i18n:MAIN_MENU.package.title/i18n:ccc-references-finder.name/v1.3.0.20210929": {
"message": "ccc-references-finder:menu-version",
"icon": "/images/version.png"
}
},
"reload": {
"renderer": [],
"ignore": [
"config.json",
"CHANGELOG.md",
"README.md",
"README.en.md"
]
}
}
\ No newline at end of file
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;
/**
* 浏览器工具
* @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;
/**
* 颜色工具
* @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;
/*
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;
}
/*
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;
}
: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;
}
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;
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;
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;
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;
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;
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;
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;
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;
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;
/**
* 版本工具
* @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;
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;
/**
* 编辑器 API(用于抹平不同版本编辑器之间的差异)
* @author 陈皮皮 (ifaswind)
* @version 20210830
*/
const EditorAPI = {
/**
* 当前语言
* @returns {string}
*/
getLanguage() {
return Editor.lang || Editor.I18n.getLanguage();
},
/**
* 绝对路径转为编辑器资源路径
* @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;
This diff is collapsed.
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,
};
/**
* 对象工具
* @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;
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;
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;
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;
* {
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;
}
<!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>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment