This commit is contained in:
muwoo 2021-06-04 17:28:48 +08:00
parent ce490acb6a
commit d9c0f5ed7b
16 changed files with 430 additions and 14525 deletions

14453
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@
}, },
"mac": { "mac": {
"icon": "build/icons/icon.icns", "icon": "build/icons/icon.icns",
"target":"pkg" "target": "pkg"
}, },
"win": { "win": {
"icon": "build/icons/icon.ico" "icon": "build/icons/icon.ico"
@ -58,9 +58,10 @@
"dependencies": { "dependencies": {
"ant-design-vue": "^1.7.5", "ant-design-vue": "^1.7.5",
"anyproxy": "^4.1.3", "anyproxy": "^4.1.3",
"axios": "^0.18.0", "axios": "^0.18.1",
"download-git-repo": "^3.0.2", "download-git-repo": "^3.0.2",
"electron-store": "^8.0.0", "electron-store": "^8.0.0",
"uuid": "^8.3.2",
"vue": "^2.5.16", "vue": "^2.5.16",
"vue-electron": "^1.0.6", "vue-electron": "^1.0.6",
"vue-router": "^3.0.1", "vue-router": "^3.0.1",
@ -84,7 +85,7 @@
"del": "^3.0.0", "del": "^3.0.0",
"devtron": "^1.4.0", "devtron": "^1.4.0",
"electron": "^11.0.2", "electron": "^11.0.2",
"electron-builder": "^20.19.2", "electron-builder": "22.10.5",
"electron-debug": "^1.5.0", "electron-debug": "^1.5.0",
"electron-devtools-installer": "^2.2.4", "electron-devtools-installer": "^2.2.4",
"file-loader": "^1.1.11", "file-loader": "^1.1.11",

18
src/main/api.js Normal file
View File

@ -0,0 +1,18 @@
import {app} from 'electron';
export default {
getPath(arg) {
return app.getPath(arg.name);
},
hideMainWindow(arg, mainWindow) {
mainWindow.hide();
},
showMainWindow(arg, mainWindow) {
mainWindow.show();
},
onPluginEnter(arg) {
return arg
},
setExpendHeight({height}, mainWindow) {
mainWindow.setSize(788, height);
}
}

View File

@ -1,4 +1,5 @@
import {BrowserWindow, globalShortcut, ipcMain} from 'electron'; import {globalShortcut, ipcMain} from 'electron';
import Api from './api';
export default function init(mainWindow) { export default function init(mainWindow) {
ipcMain.on('changeWindowSize', (event, arg) => { ipcMain.on('changeWindowSize', (event, arg) => {
@ -12,6 +13,11 @@ export default function init(mainWindow) {
globalShortcut.register('Alt+R', () => { globalShortcut.register('Alt+R', () => {
mainWindow.show(); mainWindow.show();
}); });
ipcMain.on('msg-trigger', async (event, arg) => {
const data = Api[arg.type](arg, mainWindow);
event.sender.send(`msg-back-${arg.type}`, data);
});
} }

View File

@ -13,10 +13,12 @@
</a-tag> </a-tag>
</div> </div>
<a-input <a-input
id="search"
placeholder="Hi, Rubick" placeholder="Hi, Rubick"
class="main-input" class="main-input"
@change="onSearch" @change="onSearch"
:value="searchValue" :value="searchValue"
:maxLength="selected ? 0 : 1000"
> >
<a-icon class="icon-tool" type="tool" slot="suffix" @click="() => { <a-icon class="icon-tool" type="tool" slot="suffix" @click="() => {
showMainUI(); showMainUI();
@ -24,7 +26,7 @@
}"/> }"/>
} }
</a-input> </a-input>
<div class="options" v-show="options.length && !showMain"> <div class="options" v-show="showOptions">
<a-list item-layout="horizontal" :data-source="options"> <a-list item-layout="horizontal" :data-source="options">
<a-list-item @click="() => item.click($router)" class="op-item" slot="renderItem" slot-scope="item, index"> <a-list-item @click="() => item.click($router)" class="op-item" slot="renderItem" slot-scope="item, index">
<a-list-item-meta <a-list-item-meta
@ -49,9 +51,19 @@ import {ipcRenderer} from "electron";
import {getWindowHeight} from "./assets/common/utils"; import {getWindowHeight} from "./assets/common/utils";
export default { export default {
mounted() {
document.getElementById('search').addEventListener('keydown', this.checkNeedInit)
},
beforeDestroy() {
},
methods: { methods: {
...mapActions('main', ['onSearch', 'showMainUI']), ...mapActions('main', ['onSearch', 'showMainUI']),
...mapMutations('main', ['commonUpdate']), ...mapMutations('main', ['commonUpdate']),
checkNeedInit(e) {
if (this.searchValue === '' && e.keyCode === 8) {
this.closeTag();
}
},
changePath({key}) { changePath({key}) {
this.$router.push({path: `/home/${key}`}); this.$router.push({path: `/home/${key}`});
this.commonUpdate({ this.commonUpdate({
@ -72,7 +84,13 @@ export default {
} }
}, },
computed: { computed: {
...mapState('main', ['showMain', 'current', 'options', 'selected', 'searchValue']) ...mapState('main', ['showMain', 'current', 'options', 'selected', 'searchValue']),
showOptions() {
//
if (this.options.length && !this.showMain) {
return true;
}
}
} }
}; };
</script> </script>

View File

@ -1,4 +1,4 @@
export default { export default {
development: 'http://localhost:7001', development: 'http://localhost:7001',
// development: 'http://kaer-server.qa.91jkys.com', production: 'http://localhost:7001',
}; };

View File

@ -1,4 +1,4 @@
const WINDOW_MAX_HEIGHT = 766; const WINDOW_MAX_HEIGHT = 600;
const WINDOW_MIN_HEIGHT = 60; const WINDOW_MIN_HEIGHT = 60;
const PRE_ITEM_HEIGHT = 60; const PRE_ITEM_HEIGHT = 60;

View File

@ -14,6 +14,7 @@ function getWindowHeight(searchList) {
} }
function searchKeyValues(lists, value){ function searchKeyValues(lists, value){
console.log(lists);
return lists.filter(item => item.indexOf(value) >= 0) return lists.filter(item => item.indexOf(value) >= 0)
} }
@ -83,6 +84,9 @@ const sysFile = {
} catch (e) { } catch (e) {
return [] return []
} }
},
removeAllPlugins() {
store.delete('user-plugins');
} }
} }

View File

@ -1,33 +1,34 @@
<template> <template>
<div> <div>
<webview style="width: 100%;height: 100vh" id="webview" :src="path"></webview> <webview style="width: 100%;height: 100vh" id="webview" :src="path" :preload="preload"></webview>
</div> </div>
</template> </template>
<script> <script>
import path from 'path'; import path from 'path';
import fs from 'fs'; import {ipcRenderer} from 'electron';
import {getlocalDataFile} from '../../../common/utils';
import { remote } from 'electron';
// import process from 'child_process';
export default { export default {
name: "index.vue", name: "index.vue",
data() { data() {
return { return {
path: `File://${this.$route.query.sourceFile}`, path: `File://${this.$route.query.sourceFile}`,
preload: path.join(process.cwd(), './api/index.js') preload: `File://${path.join(__static, './preload.js')}`
} }
}, },
mounted() { mounted() {
console.log(1111, this.$route.query) const webview = document.querySelector('webview');
// const webview = document.querySelector('webview') webview.addEventListener('dom-ready', () => {
// webview.addEventListener('dom-ready', () => { webview.openDevTools();
// webview.openDevTools() webview.send('onPluginReady', this.$route.query);
// }) webview.send('onPluginEnter', this.$route.query)
});
}, },
beforeRouteUpdate() { beforeRouteUpdate() {
this.path = `File://${this.$route.query.sourceFile}` this.path = `File://${this.$route.query.sourceFile}`
},
beforeDestroy() {
const webview = document.querySelector('webview');
webview.send('onPluginOut', this.$route.query)
} }
} }
</script> </script>

View File

@ -6,7 +6,7 @@
<a-icon type="appstore"/> <a-icon type="appstore"/>
插件中心 插件中心
</a-menu-item> </a-menu-item>
<a-menu-item key="favo"> <a-menu-item key="plugin">
<a-icon type="heart"/> <a-icon type="heart"/>
已安装 已安装
</a-menu-item> </a-menu-item>

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="dev-container"> <div class="dev-container">
<div class="dev-detail" v-if="devPlugins.length"> <div class="dev-detail" v-if="devPlugin.length">
<a-menu v-model="currentSelect" style="width: 256px; height: 100%" mode="vertical"> <a-menu v-model="currentSelect" style="width: 256px; height: 100%" mode="vertical">
<a-menu-item @click="currentSelect = [index]" v-for="(plugin, index) in devPlugins" :key="index"> <a-menu-item @click="currentSelect = [index]" v-for="(plugin, index) in devPlugin" :key="index">
<div>{{ plugin.pluginName }}</div> <div>{{ plugin.pluginName }}</div>
<div>{{ plugin.description }}</div> <div>{{ plugin.description }}</div>
</a-menu-item> </a-menu-item>
@ -22,7 +22,7 @@
</div> </div>
</div> </div>
<div class="right"> <div class="right">
<a-switch /> <a-switch :checked="pluginDetail.status" @change="devPluginStatusChange(pluginDetail)" />
</div> </div>
</div> </div>
<a-tabs default-active-key="1"> <a-tabs default-active-key="1">
@ -30,7 +30,7 @@
<div class="desc-item"> <div class="desc-item">
<div class="desc-title"> <div class="desc-title">
<p>重新加载</p> <p>重新加载</p>
<a-button type="link">重载</a-button> <a-button type="link" @click="reloadDevPlugin(pluginDetail)">重载</a-button>
</div> </div>
<div class="desc-info"> <div class="desc-info">
如果你修改了plugin.json文件需要重新加载以应用最近版本 如果你修改了plugin.json文件需要重新加载以应用最近版本
@ -39,7 +39,7 @@
<div class="desc-item"> <div class="desc-item">
<div class="desc-title"> <div class="desc-title">
<p>发布</p> <p>发布</p>
<a-button type="link">发布</a-button> <a-button @click="release(pluginDetail)" type="link">发布</a-button>
</div> </div>
<div class="desc-info"> <div class="desc-info">
发布后用户可以通过插件中心下载且享受最新的更新 发布后用户可以通过插件中心下载且享受最新的更新
@ -48,7 +48,7 @@
<div class="desc-item"> <div class="desc-item">
<div class="desc-title"> <div class="desc-title">
<p>删除</p> <p>删除</p>
<a-button type="link">删除</a-button> <a-button type="link" @click="deleteDevPlugin(pluginDetail)">删除</a-button>
</div> </div>
<div class="desc-info"> <div class="desc-info">
删除这个插件不可以恢复 删除这个插件不可以恢复
@ -61,22 +61,50 @@
</a-tabs> </a-tabs>
</div> </div>
</div> </div>
<div class="empty" v-else>
<a-empty description="暂无开发中的插件" />
</div>
</div> </div>
</template> </template>
<script> <script>
import {mapState} from 'vuex'; import {mapState, mapMutations, mapActions} from 'vuex';
import api from "../../../assets/api";
export default { export default {
data() { data() {
return { return {
currentSelect: [0] currentSelect: [0]
} }
}, },
methods: {
...mapMutations('main', ['deleteDevPlugin', 'devPluginStatusChange']),
...mapActions('main', ['releasePlugin', 'reloadDevPlugin']),
release(plugin) {
if (!plugin.author) return this.$message.error('请填写作者!');
if (!plugin.status) return this.$message.error('请开启插件!');
api.plugin.add({
pluginName: plugin.pluginName,
author: plugin.author,
logo: plugin.logo,
gitUrl: plugin.gitUrl,
title: plugin.title,
description: plugin.description,
version: plugin.version,
name: plugin.name
}).then(res => {
this.$message.success('发布成功!')
}).catch(e => {
this.$message.error(e);
});
}
},
computed: { computed: {
...mapState('main', ['devPlugins']), ...mapState('main', ['devPlugins']),
pluginDetail() { pluginDetail() {
console.log(this.$store) return this.devPlugin[this.currentSelect]
return this.devPlugins[this.currentSelect] },
devPlugin() {
return this.devPlugins.filter(plugin => plugin.type === 'dev')
} }
} }
} }
@ -121,5 +149,8 @@ export default {
} }
} }
} }
.empty {
padding-top: 20px;
}
} }
</style> </style>

View File

@ -21,7 +21,7 @@
<h2>插件</h2> <h2>插件</h2>
<a-list item-layout="horizontal" style="width: 100%" :grid="{ gutter: 16, column: 2 }" :data-source="pluginList"> <a-list item-layout="horizontal" style="width: 100%" :grid="{ gutter: 16, column: 2 }" :data-source="pluginList">
<a-list-item slot="renderItem" slot-scope="item, index"> <a-list-item slot="renderItem" slot-scope="item, index">
<a-button :loading="loading[index]" type="link" slot="actions" @click="download(index, item)"> <a-button v-if="showButton(item)" :loading="loading[index]" type="link" slot="actions" @click="download(index, item)">
<a-icon v-show="!loading[index]" style="font-size: 20px;" type="cloud-download" /> <a-icon v-show="!loading[index]" style="font-size: 20px;" type="cloud-download" />
</a-button> </a-button>
@ -42,7 +42,7 @@
<script> <script>
import api from '../../../assets/api'; import api from '../../../assets/api';
import {mapActions} from 'vuex'; import {mapActions, mapState} from 'vuex';
export default { export default {
data() { data() {
@ -60,13 +60,16 @@ export default {
async download(index, item) { async download(index, item) {
if (this.loading[index]) return; if (this.loading[index]) return;
this.$set(this.loading, index, true); this.$set(this.loading, index, true);
console.log(this.loading);
await this.downloadPlugin(item); await this.downloadPlugin(item);
this.$set(this.loading, index, false); this.$set(this.loading, index, false);
console.log(this.loading); },
showButton(item) {
return !this.devPlugins.filter(plugin => (plugin.name === item.name && plugin.type === 'prod')).length;
}, },
...mapActions('main', ['downloadPlugin']) ...mapActions('main', ['downloadPlugin'])
},
computed: {
...mapState('main', ['devPlugins'])
} }
} }
</script> </script>

View File

@ -0,0 +1,113 @@
<template>
<div class="dev-container">
<div class="dev-detail" v-if="prodPlugin.length">
<a-menu v-model="currentSelect" style="width: 256px; height: 100%" mode="vertical">
<a-menu-item @click="currentSelect = [index]" v-for="(plugin, index) in devPlugins" :key="index">
<div>{{ plugin.pluginName }}</div>
<div>{{ plugin.description }}</div>
</a-menu-item>
</a-menu>
<div class="plugin-detail">
<div class="plugin-top">
<div class="left">
<div class="title">
{{pluginDetail.pluginName}}
<a-tag>{{pluginDetail.version}}</a-tag>
</div>
<div class="desc">
开发者{{`${pluginDetail.author || '未知'}`}}
</div>
<div class="desc">
{{pluginDetail.description}}
</div>
</div>
<div class="right">
<a-button type="danger" size="small" shape="round" @click="deleteProdPlugin(pluginDetail)">移除</a-button>
</div>
</div>
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="功能关键字">
<div class="desc-item" v-for="item in pluginDetail.features">
<div>{{item.explain}}</div>
<a-tag v-for="cmd in item.cmds">{{cmd}}</a-tag>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="详情介绍">
Content of Tab Pane 2
</a-tab-pane>
</a-tabs>
</div>
</div>
<div class="empty" v-else>
<a-empty description="暂无已安装插件" />
</div>
</div>
</template>
<script>
import {mapState, mapMutations} from 'vuex';
export default {
data() {
return {
currentSelect: [0]
}
},
methods: {
...mapMutations('main', ['deleteProdPlugin'])
},
computed: {
...mapState('main', ['devPlugins']),
pluginDetail() {
return this.devPlugins[this.currentSelect]
},
prodPlugin() {
return this.devPlugins.filter(plugin => plugin.type === 'prod')
}
}
}
</script>
<style lang="scss">
.dev-container {
height: calc(100vh - 110px);
.dev-detail {
display: flex;
align-items: flex-start;
height: 100%;
}
.plugin-detail {
padding: 20px;
box-sizing: border-box;
flex: 1;
.plugin-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
.title {
font-size: 20px;
display: flex;
align-items: center;
}
.desc {
font-size: 13px;
color: #999;
}
}
.desc-item {
border-bottom: 1px solid #ddd;
padding: 10px 0;
.desc-title {
display: flex;
align-items: center;
justify-content: space-between;
}
.desc-info {
color: #999;
}
}
}
.empty {
padding-top: 20px;
}
}
</style>

View File

@ -2,6 +2,7 @@ import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
import Market from '../pages/search/subpages/market'; import Market from '../pages/search/subpages/market';
import Dev from '../pages/search/subpages/dev'; import Dev from '../pages/search/subpages/dev';
import Installed from '../pages/search/subpages/plugin';
Vue.use(Router) Vue.use(Router)
@ -20,6 +21,10 @@ export default new Router({
path: 'dev', path: 'dev',
component: Dev component: Dev
}, },
{
path: 'plugin',
component: Installed
},
] ]
}, },
{ {

View File

@ -1,6 +1,6 @@
import {clipboard, ipcRenderer} from "electron"; import {clipboard, ipcRenderer} from "electron";
import { v4 as uuidv4 } from 'uuid';
import {getWindowHeight, searchKeyValues, downloadFunc, sysFile} from '../../assets/common/utils'; import {getWindowHeight, searchKeyValues, downloadFunc, sysFile} from '../../assets/common/utils';
import fs from "fs"; import fs from "fs";
import path from 'path'; import path from 'path';
@ -22,6 +22,24 @@ const mutations = {
} }
}); });
}, },
deleteDevPlugin(state, payload) {
state.devPlugins = state.devPlugins.filter(plugin => plugin.name !== payload.name);
sysFile.savePlugins(state.devPlugins);
},
deleteProdPlugin(state, payload) {
state.devPlugins = state.devPlugins.filter(plugin => plugin.name !== payload.name);
sysFile.savePlugins(state.devPlugins);
// todo 删除 static 目录下的对应插件
},
devPluginStatusChange(state, payload) {
state.devPlugins.forEach(plugin => {
if (plugin.name === payload.name) {
plugin.status = !plugin.status;
}
});
state.devPlugins = [...state.devPlugins];
sysFile.savePlugins(state.devPlugins);
}
} }
const actions = { const actions = {
@ -37,7 +55,30 @@ const actions = {
height: getWindowHeight(), height: getWindowHeight(),
}); });
}, },
reloadDevPlugin({ commit }, payload) {
const config = JSON.parse(fs.readFileSync(path.join(payload.sourceFile, '../plugin.json'), 'utf-8'));
const pluginConfig = {
...config,
sourceFile: path.join(payload.sourceFile, `../${config.main}`),
};
const devPlugins = [...state.devPlugins];
commit('commonUpdate', {
devPlugins: devPlugins.map(plugin => {
if (plugin.name === payload.name) {
return {
...plugin,
...pluginConfig,
}
}
return plugin;
})
})
},
onSearch ({ commit }, paylpad) { onSearch ({ commit }, paylpad) {
if (state.selected) {
commit('commonUpdate', {searchValue: ''});
return;
}
const value = paylpad.target.value; const value = paylpad.target.value;
const fileUrl = clipboard.read('public.file-url').replace('file://', ''); const fileUrl = clipboard.read('public.file-url').replace('file://', '');
commit('commonUpdate', {searchValue: value}) commit('commonUpdate', {searchValue: value})
@ -46,8 +87,10 @@ const actions = {
const config = JSON.parse(fs.readFileSync(fileUrl, 'utf-8')); const config = JSON.parse(fs.readFileSync(fileUrl, 'utf-8'));
const pluginConfig = { const pluginConfig = {
...JSON.parse(fs.readFileSync(fileUrl, 'utf-8')), ...config,
sourceFile: path.join(fileUrl, `../${config.main}`) sourceFile: path.join(fileUrl, `../${config.main}`),
name: uuidv4(),
type: 'dev'
}; };
commit('commonUpdate', { commit('commonUpdate', {
selected: { selected: {
@ -98,39 +141,46 @@ const actions = {
let options = []; let options = [];
// check 是否是插件 // check 是否是插件
state.devPlugins.forEach((plugin) => { if (value) {
const feature = plugin.features; state.devPlugins.forEach((plugin) => {
feature.forEach(fe => { // dev 插件未开启
const cmds = searchKeyValues(fe.cmds, value); if (plugin.type === 'dev' && !plugin.status) return;
const feature = plugin.features;
options = [ feature.forEach(fe => {
...options, const cmds = searchKeyValues(fe.cmds, value);
...cmds.map((cmd) => ({ console.log(plugin);
name: cmd, options = [
value: 'plugin', ...options,
icon: 'plus-circle', ...cmds.map((cmd) => ({
desc: fe.explain, name: cmd,
click: (router) => { value: 'plugin',
commit('commonUpdate', { icon: 'plus-circle',
selected: { desc: fe.explain,
key: cmd, click: (router) => {
name: cmd commit('commonUpdate', {
}, selected: {
searchValue: '', key: cmd,
showMain: true, name: cmd
}); },
ipcRenderer.send('changeWindowSize', { searchValue: '',
height: getWindowHeight(), showMain: true,
}); });
router.push({ ipcRenderer.send('changeWindowSize', {
path: '/plugin', height: getWindowHeight(),
query: plugin, });
}) router.push({
} path: '/plugin',
})) query: {
] ...plugin,
}) detail: JSON.stringify(fe)
}); },
})
}
}))
]
})
});
}
commit('commonUpdate', { commit('commonUpdate', {
options options
@ -146,7 +196,8 @@ const actions = {
const config = JSON.parse(fs.readFileSync(`${fileUrl}/plugin.json`, 'utf-8')); const config = JSON.parse(fs.readFileSync(`${fileUrl}/plugin.json`, 'utf-8'));
const pluginConfig = { const pluginConfig = {
...config, ...config,
sourceFile: `${fileUrl}/${config.main}` sourceFile: `${fileUrl}/${config.main}`,
type: 'prod'
}; };
commit('commonUpdate', { commit('commonUpdate', {
devPlugins: [pluginConfig, ...state.devPlugins], devPlugins: [pluginConfig, ...state.devPlugins],

107
static/preload.js Normal file
View File

@ -0,0 +1,107 @@
const path = require('path');
const filePath = location.href.replace('file://', '');
const {ipcRenderer, nativeImage, clipboard} = require('electron');
const Store = require('electron-store');
const store = new Store();
function convertImgToBase64(url, callback, outputFormat){
var canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function(){
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img,0,0);
var dataURL = canvas.toDataURL(outputFormat || 'image/png');
callback.call(this, dataURL);
canvas = null;
};
img.src = url;
}
window.utools = window.rubick = {
// 事件
onPluginEnter(cb) {
ipcRenderer.once('onPluginEnter', (e, message) => {
const feature = JSON.parse(message.detail)
cb(feature)
})
},
onPluginReady(cb) {
ipcRenderer.once('onPluginReady', (e, message) => {
const feature = JSON.parse(message.detail)
cb(feature)
})
},
onPluginOut(cb) {
ipcRenderer.once('onPluginOut', (e, message) => {
const feature = JSON.parse(message.detail)
cb(feature)
})
},
// 窗口交互
hideMainWindow() {
ipcRenderer.send('msg-trigger', {
type: 'hideMainWindow',
});
},
showMainWindow() {
ipcRenderer.send('msg-trigger', {
type: 'showMainWindow',
});
},
setExpendHeight(height) {
ipcRenderer.send('msg-trigger', {
type: 'setExpendHeight',
height,
});
},
setSubInput(onChange, placeHolder, isFocus) {
},
getPath(name) {
ipcRenderer.send('msg-trigger', {
type: 'getPath',
name,
});
return new Promise((resolve, reject) => {
ipcRenderer.once(`msg-back-getPath`, (e, result) => {
result ? resolve(result) : reject();
});
})
},
showNotification(body, clickFeatureCode) {
const myNotification = new Notification('Rubick 通知', {
body
});
return myNotification;
// todo 实现 clickFeatureCode
},
copyImage(img) {
convertImgToBase64(img,function(base64Image) {
const image = nativeImage.createFromDataURL(base64Image)
clipboard.writeImage(image)
})
},
copyText(text) {
clipboard.writeText(text);
},
db: {
put(key, value) {
console.log(key, value);
store.set(key, value)
},
get(key) {
store.get(key);
},
remove() {}
},
isDarkColors() {
return false;
},
}
require(path.join(filePath, '../preload.js'));