feat: 支持快捷键设置;支持超级面板

This commit is contained in:
muwoo 2021-07-02 17:12:38 +08:00
parent 4bac5ac8a2
commit 7d55ef06a6
18 changed files with 586 additions and 172 deletions

View File

@ -62,13 +62,17 @@
"download": "^8.0.0", "download": "^8.0.0",
"download-git-repo": "^3.0.2", "download-git-repo": "^3.0.2",
"electron-store": "^8.0.0", "electron-store": "^8.0.0",
"is-chinese": "^1.4.2",
"keycode": "^2.2.0", "keycode": "^2.2.0",
"marked": "^2.0.7", "marked": "^2.0.7",
"md5": "^2.3.0",
"mime-types": "^2.1.31",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"osx-mouse": "git+https://github.com/Toinane/osx-mouse.git", "osx-mouse": "git+https://github.com/Toinane/osx-mouse.git",
"puppeteer-core": "^10.0.0", "puppeteer-core": "^10.0.0",
"puppeteer-in-electron": "^3.0.3", "puppeteer-in-electron": "^3.0.3",
"query-string": "^7.0.0", "query-string": "^7.0.0",
"request-promise": "^4.2.6",
"robotjs": "git+ssh://git@github.com/Toinane/robotjs.git", "robotjs": "git+ssh://git@github.com/Toinane/robotjs.git",
"semver": "^7.3.5", "semver": "^7.3.5",
"sudo-prompt": "^9.2.1", "sudo-prompt": "^9.2.1",

View File

@ -2,4 +2,5 @@ module.exports = () => ({
picker: require("./picker")(), picker: require("./picker")(),
separator: require("./separate")(), separator: require("./separate")(),
capture: require("./capture")(), capture: require("./capture")(),
superPanel: require("./superPanel")(),
}); });

View File

@ -0,0 +1,54 @@
const { BrowserWindow, ipcMain, app } = require("electron");
module.exports = () => {
let win;
let init = (mainWindow) => {
if (win === null || win === undefined) {
createWindow();
ipcMain.on('superPanel-hidden', () => {
win.hide();
});
ipcMain.on('superPanel-setSize', (e, height) => {
win.setSize(250, height);
});
ipcMain.on('superPanel-openPlugin', (e, args) => {
mainWindow.webContents.send('superPanel-openPlugin', args);
});
}
};
let createWindow = () => {
win = new BrowserWindow({
frame: false,
autoHideMenuBar: true,
width: 250,
height: 50,
show: false,
alwaysOnTop: true,
webPreferences: {
webSecurity: false,
enableRemoteModule: true,
backgroundThrottling: false,
nodeIntegration: true,
devTools: false,
},
});
win.loadURL(`file://${__static}/plugins/superPanel/index.html`);
win.once('ready-to-show', () => win.show());
win.on("closed", () => {
win = undefined;
});
// 打包后,失焦隐藏
win.on('blur', () => {
win.hide();
});
};
let getWindow = () => win;
return {
init: init,
getWindow: getWindow,
};
};

View File

@ -4,6 +4,7 @@ import {
BrowserWindow, BrowserWindow,
clipboard, clipboard,
Notification, Notification,
app,
} from 'electron'; } from 'electron';
import Api from './api'; import Api from './api';
import robot from 'robotjs'; import robot from 'robotjs';
@ -11,7 +12,9 @@ import './config';
const browsers = require("../browsers")(); const browsers = require("../browsers")();
const mouseEvents = require("osx-mouse"); const mouseEvents = require("osx-mouse");
const {picker, separator} = browsers; const {picker, separator, superPanel} = browsers;
// 需要在超级面板展示的插件
let optionPlugin = [];
let closePicker = (newColor) => { let closePicker = (newColor) => {
if (picker.getWindow()) { if (picker.getWindow()) {
@ -34,18 +37,88 @@ function registerShortCut(mainWindow) {
}); });
} }
const getSelectedText = () => {
return new Promise((resolve) => {
const lastText = clipboard.readText('clipboard');
const platform = process.platform;
if (platform === 'darwin') {
robot.keyTap('c', 'command');
} else {
robot.keyTap('c', 'control');
}
setTimeout(() => {
const text = clipboard.readText('clipboard') || ''
const fileUrl = clipboard.read('public.file-url');
clipboard.writeText(lastText);
resolve({
text,
fileUrl
})
}, 100);
})
}
export default function init(mainWindow) { export default function init(mainWindow) {
ipcMain.on('optionPlugin', (e, args) => {
optionPlugin = args;
});
const mouseTrack = mouseEvents(); const mouseTrack = mouseEvents();
let down_time = 0; let down_time = 0;
let isPress = false;
mouseTrack.on('right-down', () => { mouseTrack.on('right-down', () => {
isPress = true;
down_time = Date.now(); down_time = Date.now();
const config = global.opConfig.get();
setTimeout(async () => {
if (isPress) {
const copyResult = await getSelectedText();
let win = superPanel.getWindow();
if (win) {
win.webContents.send('trigger-super-panel', {
...copyResult,
optionPlugin: optionPlugin.plugins,
});
} else {
superPanel.init(mainWindow);
win = superPanel.getWindow();
win.once('ready-to-show', () => {
win.webContents.send('trigger-super-panel', {
...copyResult,
optionPlugin: optionPlugin.plugins,
});
});
}
const pos = robot.getMousePos();
win.setPosition(parseInt(pos.x), parseInt(pos.y));
win.show();
}
}, config.superPanel.mouseDownTime);
}) })
mouseTrack.on('right-up', () => { mouseTrack.on('right-up', () => {
if ((Date.now() - down_time) > 1000) { isPress = false;
new Notification({ title: 'Rubick 通知', body: '长按了' }).show(); });
// 注册快捷键
registerShortCut(mainWindow);
// 设置开机启动
const config = global.opConfig.get();
app.setLoginItemSettings({
openAtLogin: config.perf.common.start,
openAsHidden: true,
});
mainWindow.once("ready-to-show", () => {
// 非隐藏式启动需要显示主窗口
if (!app.getLoginItemSettings().wasOpenedAsHidden) {
mainWindow.show();
} }
}); });
registerShortCut(mainWindow);
ipcMain.on('re-register', (event, arg) => { ipcMain.on('re-register', (event, arg) => {
registerShortCut(mainWindow); registerShortCut(mainWindow);
@ -55,10 +128,12 @@ export default function init(mainWindow) {
mainWindow.setSize(arg.width || 800, arg.height); mainWindow.setSize(arg.width || 800, arg.height);
}); });
// 打包后,失焦隐藏
mainWindow.on('blur', () => { mainWindow.on('blur', () => {
mainWindow.hide(); app.isPackaged && mainWindow.hide();
}); });
// 响应 preload.js 事件
ipcMain.on('msg-trigger', async (event, arg) => { ipcMain.on('msg-trigger', async (event, arg) => {
const window = arg.winId ? BrowserWindow.fromId(arg.winId) : mainWindow const window = arg.winId ? BrowserWindow.fromId(arg.winId) : mainWindow
const operators = arg.type.split('.'); const operators = arg.type.split('.');
@ -70,6 +145,7 @@ export default function init(mainWindow) {
event.sender.send(`msg-back-${arg.type}`, data); event.sender.send(`msg-back-${arg.type}`, data);
}); });
// 窗口分离
ipcMain.on('new-window', (event, arg) => { ipcMain.on('new-window', (event, arg) => {
const opts = { const opts = {
...arg, ...arg,
@ -78,6 +154,7 @@ export default function init(mainWindow) {
separator.init(opts); separator.init(opts);
}); });
// 拾色器
ipcMain.on('start-picker', () => { ipcMain.on('start-picker', () => {
const mouseTrack = mouseEvents(); const mouseTrack = mouseEvents();
picker.init(); picker.init();

View File

@ -20,9 +20,15 @@ let defaultConfig = {
search: true, search: true,
} }
}, },
superPanel: {
baiduAPI: {
key: '',
appid: '',
},
mouseDownTime: 500
}
} }
} }
global.opConfig = { global.opConfig = {
config: null, config: null,
get() { get() {
@ -38,7 +44,6 @@ global.opConfig = {
} }
}, },
set(key, value) { set(key, value) {
console.log(opConfig.config);
opConfig.config[key] = value; opConfig.config[key] = value;
fs.writeFileSync(configPath, JSON.stringify(opConfig.config)); fs.writeFileSync(configPath, JSON.stringify(opConfig.config));
} }

View File

@ -80,15 +80,23 @@ export default {
} }
}, },
mounted() { mounted() {
//
ipcRenderer.send('init-shortcut');
ipcRenderer.on('init-rubick', this.closeTag); ipcRenderer.on('init-rubick', this.closeTag);
ipcRenderer.on('new-window', this.newWindow); ipcRenderer.on('new-window', this.newWindow);
//
ipcRenderer.on('superPanel-openPlugin', (e, args) => {
this.openPlugin({
cmd: args.cmd,
plugin: args.plugin,
feature: args.feature,
router: this.$router,
payload: args.data,
})
});
const searchNd = document.getElementById('search'); const searchNd = document.getElementById('search');
searchNd && searchNd.addEventListener('keydown', this.checkNeedInit) searchNd && searchNd.addEventListener('keydown', this.checkNeedInit)
}, },
methods: { methods: {
...mapActions('main', ['onSearch', 'showMainUI']), ...mapActions('main', ['onSearch', 'showMainUI', 'openPlugin']),
...mapMutations('main', ['commonUpdate']), ...mapMutations('main', ['commonUpdate']),
search(v) { search(v) {
if (!this.searchFn) { if (!this.searchFn) {

View File

@ -4,7 +4,7 @@ import fs from 'fs';
import process from 'child_process'; import process from 'child_process';
import Store from 'electron-store'; import Store from 'electron-store';
import downloadFile from 'download'; import downloadFile from 'download';
import {nativeImage} from 'electron'; import {nativeImage, ipcRenderer} from 'electron';
import {APP_FINDER_PATH} from './constans'; import {APP_FINDER_PATH} from './constans';
import {getlocalDataFile} from "../../../main/common/utils"; import {getlocalDataFile} from "../../../main/common/utils";
@ -57,6 +57,19 @@ async function downloadZip(downloadRepoUrl, name) {
const sysFile = { const sysFile = {
savePlugins(plugins) { savePlugins(plugins) {
ipcRenderer.send('optionPlugin', {
plugins: plugins.filter((plugin) => {
let hasOption = false;
plugin.features.forEach(fe => {
fe.cmds.forEach(cmd => {
if (cmd.type) {
hasOption = true;
}
})
});
return hasOption;
})
});
store.set('user-plugins', plugins); store.set('user-plugins', plugins);
}, },
getUserPlugins() { getUserPlugins() {
@ -72,7 +85,7 @@ const sysFile = {
} }
function mergePlugins(plugins) { function mergePlugins(plugins) {
return [ const result = [
...plugins, ...plugins,
...SYSTEM_PLUGINS.map(plugin => { ...SYSTEM_PLUGINS.map(plugin => {
return { return {
@ -81,8 +94,18 @@ function mergePlugins(plugins) {
sourceFile: '', sourceFile: '',
type: 'system', type: 'system',
} }
}), })
] ]
return result.filter((item, i) => {
let targetIndex;
result.forEach((tg, j) => {
if (tg.tag === item.tag && tg.type === 'system') {
targetIndex = j
}
});
return i === targetIndex;
});
} }
function find(p, target = 'plugin.json') { function find(p, target = 'plugin.json') {

View File

@ -18,19 +18,14 @@ export default {
32: 'space', 32: 'space',
33: 'page up', 33: 'page up',
34: 'page down', 34: 'page down',
35: 'end', 35: 'End',
36: 'home', 36: 'Home',
37: 'left arrow', 37: 'Left',
38: 'up arrow', 38: 'Up',
39: 'right arrow', 39: 'Right',
40: 'down arrow', 40: 'Down',
41: 'select', 45: 'Insert',
42: 'print', 46: 'Delete',
43: 'execute',
44: 'Print Screen',
45: 'insert',
46: 'delete',
47: 'help',
48: '0', 48: '0',
49: '1', 49: '1',
50: '2', 50: '2',
@ -41,130 +36,55 @@ export default {
55: '7', 55: '7',
56: '8', 56: '8',
57: '9', 57: '9',
58: ':', 65: 'A',
59: 'semicolon (firefox), equals', 66: 'B',
60: '<', 67: 'C',
61: 'equals (firefox)', 68: 'D',
63: 'ß', 69: 'E',
64: '@ (firefox)', 70: 'F',
65: 'a', 71: 'G',
66: 'b', 72: 'H',
67: 'c', 73: 'I',
68: 'd', 74: 'J',
69: 'e', 75: 'K',
70: 'f', 76: 'L',
71: 'g', 77: 'M',
72: 'h', 78: 'N',
73: 'i', 79: 'O',
74: 'j', 80: 'P',
75: 'k', 81: 'Q',
76: 'l', 82: 'R',
77: 'm', 83: 'S',
78: 'n', 84: 'T',
79: 'o', 85: 'U',
80: 'p', 86: 'V',
81: 'q', 87: 'W',
82: 'r', 88: 'X',
83: 's', 89: 'Y',
84: 't', 90: 'Z',
85: 'u', 112: 'F1',
86: 'v', 113: 'F2',
87: 'w', 114: 'F3',
88: 'x', 115: 'F4',
89: 'y', 116: 'F5',
90: 'z', 117: 'F6',
91: 'Windows Key / Left ⌘ / Chromebook Search key', 118: 'F7',
92: 'right window key', 119: 'F8',
93: 'Windows Menu / Right ⌘', 120: 'F9',
95: 'sleep', 121: 'F10',
96: 'numpad 0', 122: 'F11',
97: 'numpad 1', 123: 'F12',
98: 'numpad 2', 186: ';',
99: 'numpad 3', 187: '=',
100: 'numpad 4', 188: ',',
101: 'numpad 5', 189: '-',
102: 'numpad 6', 190: '.',
103: 'numpad 7', 191: '/',
104: 'numpad 8', 192: '`',
105: 'numpad 9', 219: '[',
106: 'multiply', 220: '\\',
107: 'add', 221: ']',
108: 'numpad period (firefox)', 222: "'",
109: 'subtract',
110: 'decimal point',
111: 'divide',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5',
117: 'f6',
118: 'f7',
119: 'f8',
120: 'f9',
121: 'f10',
122: 'f11',
123: 'f12',
124: 'f13',
125: 'f14',
126: 'f15',
127: 'f16',
128: 'f17',
129: 'f18',
130: 'f19',
131: 'f20',
132: 'f21',
133: 'f22',
134: 'f23',
135: 'f24',
136: 'f25',
137: 'f26',
138: 'f27',
139: 'f28',
140: 'f29',
141: 'f30',
142: 'f31',
143: 'f32',
144: 'num lock',
145: 'scroll lock',
151: 'airplane mode',
160: '^',
161: '!',
162: '؛ (arabic semicolon)',
163: '#',
164: '$',
165: 'ù',
166: 'page backward',
167: 'page forward',
168: 'refresh',
169: 'closing paren (AZERTY)',
170: '*',
171: '~ + * key',
172: 'home key',
173: 'minus (firefox), mute/unmute',
174: 'decrease volume level',
175: 'increase volume level',
176: 'next',
177: 'previous',
178: 'stop',
179: 'play/pause',
180: 'e-mail',
181: 'mute/unmute (firefox)',
182: 'decrease volume level (firefox)',
183: 'increase volume level (firefox)',
186: 'semi-colon / ñ',
187: 'equal sign',
188: 'comma',
189: 'dash',
190: 'period',
191: 'forward slash / ç',
192: 'grave accent / ñ / æ / ö',
193: '?, / or °',
194: 'numpad period (chrome)',
219: 'open bracket',
220: 'back slash',
221: 'close bracket / å',
222: 'single quote / ø / ä',
223: '`', 223: '`',
224: 'left or right ⌘ key (firefox)', 224: 'left or right ⌘ key (firefox)',
225: 'altgr', 225: 'altgr',

View File

@ -30,8 +30,8 @@ export default {
mounted() { mounted() {
this.webview = document.querySelector('webview'); this.webview = document.querySelector('webview');
this.webview.addEventListener('dom-ready', () => { this.webview.addEventListener('dom-ready', () => {
this.webview.send('onPluginReady', this.$route.query); this.webview.send('onPluginReady', this.pluginInfo);
this.webview.send('onPluginEnter', this.$route.query); this.webview.send('onPluginEnter', this.pluginInfo);
}); });
this.setSubPlaceHolder('Hi, Rubick'); this.setSubPlaceHolder('Hi, Rubick');
this.webview.addEventListener('ipc-message', (event) => { this.webview.addEventListener('ipc-message', (event) => {
@ -74,14 +74,16 @@ export default {
...mapMutations('main', ['setSubPlaceHolder', 'commonUpdate']), ...mapMutations('main', ['setSubPlaceHolder', 'commonUpdate']),
}, },
beforeRouteUpdate() { beforeRouteUpdate() {
this.path = `File://${this.$route.query.sourceFile}` this.path = `File://${this.$route.query.sourceFile}`;
console.log(this.pluginInfo)
this.webview.send('onPluginEnter', this.pluginInfo);
}, },
beforeDestroy() { beforeDestroy() {
const webview = document.querySelector('webview'); const webview = document.querySelector('webview');
webview && webview.send('onPluginOut', this.$route.query) webview && webview.send('onPluginOut', this.$route.query)
}, },
computed: { computed: {
...mapState('main', ['searchValue', 'devPlugins']), ...mapState('main', ['searchValue', 'devPlugins', 'pluginInfo']),
pluginDetail() { pluginDetail() {
return (this.devPlugins.filter(plugin => plugin.name === this.query.name)[0] || {}).features return (this.devPlugins.filter(plugin => plugin.name === this.query.name)[0] || {}).features
}, },

View File

@ -6,13 +6,7 @@
偏好设置 偏好设置
</a-menu-item> </a-menu-item>
<a-menu-item :key="1"> <a-menu-item :key="1">
本地启动文件 超级面板
</a-menu-item>
<a-menu-item :key="2">
全局快捷键
</a-menu-item>
<a-menu-item :key="3">
所有关键字
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
<div class="settings-detail"> <div class="settings-detail">
@ -47,6 +41,30 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="currentSelect[0] === 1">
<div class="setting-item">
<div class="title">弹出面板</div>
<a-select value="mouseRight" style="width: 200px" disabled>
<a-select-option value="mouseRight">长按鼠标右键</a-select-option>
</a-select>
</div>
<div class="setting-item">
<div class="title">长按以下设置的毫秒响应</div>
<a-slider :step="100" v-model:value="config.superPanel.mouseDownTime" :min="200" :max="1000" />
</div>
<div class="setting-item">
<div class="title">百度搜索配置</div>
<a-form :label-col="{ span: 3 }" :wrapper-col="{ span: 14 }">
<a-form-item label="appid">
<a-input v-model:value="config.superPanel.baiduAPI.appid" />
</a-form-item>
<a-form-item label="key">
<a-input v-model:value="config.superPanel.baiduAPI.key" />
</a-form-item>
</a-form>
</div>
<img width="100%" src="https://static.91jkys.com/upload/202107/02/fa4a5c614234409fb32ddda70cb900aa.jpg" />
</div>
</div> </div>
</div> </div>
</div> </div>
@ -63,7 +81,7 @@ export default {
data() { data() {
return { return {
currentSelect: [0], currentSelect: [0],
config: JSON.parse(JSON.stringify(opConfig.get())) config: JSON.parse(JSON.stringify(opConfig.get())),
} }
}, },
methods: { methods: {
@ -96,6 +114,7 @@ export default {
deep: true, deep: true,
handler() { handler() {
opConfig.set('perf', this.config.perf); opConfig.set('perf', this.config.perf);
opConfig.set('superPanel', this.config.superPanel);
ipcRenderer.send('re-register'); ipcRenderer.send('re-register');
} }
} }
@ -111,14 +130,19 @@ export default {
height: 100%; height: 100%;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
background: #F8FAFC; background: #fff;
} }
.settings-detail { .settings-detail {
padding: 20px; padding: 20px;
box-sizing: border-box; box-sizing: border-box;
flex: 1; flex: 1;
overflow: auto;
height: 100%;
.setting-item { .setting-item {
margin-bottom: 20px; margin-bottom: 20px;
.ant-form-item {
margin-bottom: 0;
}
.title { .title {
color: #6C9FE2; color: #6C9FE2;
font-size: 15px; font-size: 15px;

View File

@ -22,6 +22,7 @@ const state = {
searchValue: '', searchValue: '',
devPlugins: mergePlugins(sysFile.getUserPlugins() || []), devPlugins: mergePlugins(sysFile.getUserPlugins() || []),
subPlaceHolder: '', subPlaceHolder: '',
pluginInfo: {},
} }
const mutations = { const mutations = {
@ -29,7 +30,7 @@ const mutations = {
Object.keys(payload).forEach((key) => { Object.keys(payload).forEach((key) => {
state[key] = payload[key]; state[key] = payload[key];
if (key === 'devPlugins') { if (key === 'devPlugins') {
sysFile.savePlugins(payload) sysFile.savePlugins(payload[key])
} }
}); });
}, },
@ -236,7 +237,7 @@ const actions = {
devPlugins: [pluginConfig, ...state.devPlugins], devPlugins: [pluginConfig, ...state.devPlugins],
}); });
}, },
openPlugin({commit}, {cmd, plugin, feature, router}) { openPlugin({commit}, {cmd, plugin, feature, router, payload}) {
if (plugin.type === 'app') { if (plugin.type === 'app') {
execSync(plugin.action); execSync(plugin.action);
commit('commonUpdate', { commit('commonUpdate', {
@ -253,7 +254,7 @@ const actions = {
commit('commonUpdate', { commit('commonUpdate', {
selected: { selected: {
key: 'plugin-container', key: 'plugin-container',
name: cmd, name: cmd.label ? cmd.label : cmd,
icon: 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`), icon: 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`),
}, },
searchValue: '', searchValue: '',
@ -277,10 +278,20 @@ const actions = {
}); });
return; return;
} }
commit('commonUpdate', {
pluginInfo: {
cmd,
...plugin,
detail: feature,
payload,
}
});
router.push({ router.push({
path: '/plugin', path: '/plugin',
query: { query: {
...plugin, ...plugin,
_modify: Date.now(),
detail: JSON.stringify(feature) detail: JSON.stringify(feature)
}, },
}) })

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

View File

@ -0,0 +1,101 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<script crossorigin="anonymous" src="https://lib.baomitu.com/vue/2.6.12/vue.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
#app {
-webkit-app-region: drag;
width: 100%;
box-sizing: border-box;
background: #fff;
overflow: hidden;
}
.top {
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px #ddd dashed;
padding: 0 10px;
box-sizing: border-box;
}
.top .img {
width: 32px;
height: 32px;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #314659;
}
.top .img img {
width: 22px;
height: 22px;
}
.top .text {
color: #999;
font-size: 14px;
}
.translate {
width: 100%;
background: #F7F7F9;
font-size: 12px;
color: #555;
box-sizing: border-box;
padding: 5px;
}
.trans-item {
line-height: 20px;
}
.options-item {
border-bottom: 1px #ddd dashed;
height: 35px;
line-height: 35px;
padding: 0 10px;
color: #333;
font-size: 14px;
cursor: pointer;
}
.options-item:last-child {
border-bottom: none !important;
}
.icon {
width: 20px;
height: 20px;
margin-right: 5px;
}
.select-item {
display: flex;
align-items: center;
}
</style>
<script src="./index.js" type="module"></script>
<body>
<div id="app">
<div class="top" @click="openMainWindow">
<span class="img"><img src="./assets/logo.png" /></span>
<span class="text" v-if="selectData.text && selectData.text.length">选择的文本 {{selectData.text.length}} 个</span>
</div>
<div class="translate" v-if="selectData.translate">
<div class="trans-item" v-for="trans in selectData.translate">
<div>{{trans.src}}</div>
<div>n. {{trans.dst}}</div>
</div>
</div>
<div @click="() => commonClick(op, selectData.fileUrl)" class="options-item" v-for="op in targetOptions">
<div class="select-item">
<img class="icon" :src="op.icon" />
{{op.name}}
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,185 @@
const {ipcRenderer, nativeImage, remote, clipboard} = require('electron')
const md5 = require("md5");
const rp = require("request-promise");
const isChinese = require('is-chinese');
const path = require('path');
const fs = require('fs');
const { spawn } = require ('child_process');
const mineType = require("mime-types");
const opConfig = remote.getGlobal('opConfig');
new Vue({
el: '#app',
data: {
code: '',
current: {},
selectData: {
translate: {},
optionPlugin: [],
},
options: {
translate: [],
common: [
{
type: 'default',
name: '终端中打开',
icon: './assets/terminal.png',
click: (fileUrl) => {
spawn('open', [ '-a', 'Terminal', fileUrl ]);
}
},
{
type: 'default',
name: '新建文件',
icon: './assets/new.png',
click: (fileUrl) => {
remote.dialog.showSaveDialog({
title: "请选择要保存的文件名",
buttonLabel: "保存",
defaultPath: fileUrl.replace('file://', ''),
showsTagField: false,
nameFieldLabel: '',
}).then(result => {
fs.writeFileSync(result.filePath, '');
});
}
},
{
type: 'default',
name: '复制当前路径',
icon: './assets/link.png',
click: (fileUrl) => {
clipboard.writeText(fileUrl.replace('file://', ''))
}
}
],
selected: [
{
type: 'default',
name: '复制当前路径',
icon: './assets/link.png',
click: (fileUrl) => {
clipboard.writeText(fileUrl.replace('file://', ''))
}
}
]
},
targetOptions: [],
},
created() {
// 简单唤起超级面板
ipcRenderer.on('trigger-super-panel', (e, args) => {
this.selectData = args;
const ext = path.extname(this.selectData.fileUrl);
// 剪切板只有文本时,显示翻译
if (!this.selectData.fileUrl) {
const word = this.selectData.text;
const isCh = isChinese(word);
this.translate(word, isCh ? 'en' : 'zh');
this.targetOptions = this.options.translate;
} else if (!ext || path.parse(this.selectData.fileUrl).base === 'Desktop') {
// 如果在桌面上或者没有选择任何文件,则展示通用选项
this.targetOptions = this.options.common;
} else {
// 有文件选择
this.targetOptions = JSON.parse(JSON.stringify(this.options.selected));
// 检测上传
(this.selectData.optionPlugin || []).forEach(plugin => {
plugin.features.forEach(fe => {
fe.cmds.forEach(cmd => {
// 如果是图片,则唤起图片选项
const regImg = /\.(png|jpg|gif|jpeg|webp)$/;
if (cmd.type === 'img' && regImg.test(ext)) {
console.log(plugin);
this.targetOptions.push({
type: 'ext',
name: cmd.label,
icon: plugin.icon,
click: (fileUrl) => {
const base64 = this.fileToBase64(fileUrl);
ipcRenderer.send('superPanel-openPlugin', {
cmd: cmd,
plugin: plugin,
feature: fe,
data: base64,
});
}
})
}
// 如果是文件,且符合文件正则类型
if (cmd.type === 'file' && new RegExp(cmd.match).test(ext)) {
this.targetOptions.push({
type: 'ext',
name: cmd.label,
icon: '',
click: () => {
ipcRenderer.send('superPanel-openPlugin', {
cmd: cmd,
plugin: plugin,
feature: fe,
data: {
isFile: true,
isDirectory: false,
name: path.basename(this.selectData.fileUrl),
path: this.selectData.fileUrl
}
})
}
})
}
})
});
});
}
});
},
methods: {
translate(msg, to) {
const {appid, key} = opConfig.get().superPanel.baiduAPI;
if (!appid || !key) return;
const q = msg;
const salt = parseInt(Math.random() * 1000000000); //加盐
const sign = md5(appid + q + salt + key); //生成签名
const params = encodeURI(
`q=${q}&from=auto&to=${to}&appid=${appid}&salt=${salt}&sign=${sign}`
);
const options = {
uri: `https://fanyi-api.baidu.com/api/trans/vip/translate?${params}`,
};
return rp(options).then((res) => {
this.$set(this.selectData, 'translate', JSON.parse(res).trans_result)
})
},
commonClick(item, fileUrl) {
ipcRenderer.send('superPanel-hidden')
item.click(fileUrl);
},
fileToBase64 (filePath) {
let data = fs.readFileSync(filePath.replace('file://', ''));
data = new Buffer(data).toString("base64");
let base64 = "data:" + mineType.lookup(filePath) + ";base64," + data;
return base64;
},
openMainWindow() {
ipcRenderer.send('msg-trigger', {
type: 'showMainWindow',
});
}
},
watch: {
selectData: {
deep: true,
handler() {
this.$nextTick(() => {
ipcRenderer.send('superPanel-setSize', parseInt(getComputedStyle(document.getElementById('app')).height))
})
}
}
}
})

View File

@ -43,16 +43,15 @@ function convertImgToBase64(url, callback, outputFormat){
window.utools = window.rubick = { window.utools = window.rubick = {
// 事件 // 事件
onPluginEnter(cb) { onPluginEnter(cb) {
ipcRenderer.once('onPluginEnter', (e, message) => { ipcRenderer.on('onPluginEnter', (e, message) => {
const feature = JSON.parse(message.detail) const feature = message.detail;
console.log(feature) cb({...feature, type: message.cmd.type ? message.cmd.type : 'text', payload: message.payload})
cb({...feature, type: 'text'})
}) })
}, },
onPluginReady(cb) { onPluginReady(cb) {
ipcRenderer.once('onPluginReady', (e, message) => { ipcRenderer.once('onPluginReady', (e, message) => {
const feature = JSON.parse(message.detail) const feature = message.detail
cb({...feature, type: 'text'}) cb({...feature, type: message.cmd.type ? message.cmd.type : 'text', payload: message.payload})
}) })
}, },
onPluginOut(cb) { onPluginOut(cb) {