Compare commits

...

14 Commits

Author SHA1 Message Date
muwoo
a535c5d89b feat:增加request功能 2021-09-17 16:23:51 +08:00
muwoo
1eeb0fad9a bugfix: #45, #40 2021-09-16 20:27:55 +08:00
muwoo
511b357e28 ref: 优化window读取系统应用 2021-09-13 17:25:45 +08:00
muwoo
87e7501c0a ref: bugfix windows rm 命令无法执行 2021-09-13 17:24:51 +08:00
muwoo
9242f17cde ref: 优化window读取系统应用 2021-09-09 11:44:17 +08:00
muwoo
d047119076 ref: 优化window读取系统应用icon展示 2021-09-09 11:21:19 +08:00
muwoo
5118fa6ca4 ref: 优化window读取系统应用,增加自动更新提示 2021-09-08 20:46:38 +08:00
muwoo
424c40a99c bugfix: #44,支持windows应用图标展示 2021-09-07 16:58:04 +08:00
muwoo
651e202ab1 bugfix: windows 搜索图标有问题 2021-09-06 20:37:38 +08:00
muwoo
614d5ae369 bugfix: #42; ref: 支持windows拼音搜索系统应用 2021-09-06 14:58:18 +08:00
muwoo
f287d10ca9 bugfix: 修复 windows 搜索系统应用报错问题 2021-09-06 11:32:02 +08:00
muwoo
a04efd0d4f feat: 支持 windows 搜索系统应用:#39 2021-09-03 18:09:55 +08:00
muwoo
153d9dc5d7 feat: 支持 windows 搜索系统应用:#39 2021-09-03 18:09:08 +08:00
muwoo
de138955b5 feat: 支持win系统软件搜索 2021-09-03 18:07:42 +08:00
10 changed files with 272 additions and 137 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "rubick2",
"version": "0.0.3-beta.9",
"version": "0.0.7-beta.1",
"author": "muwoo <2424880409@qq.com>",
"description": "An electron-vue project",
"license": null,
@@ -11,7 +11,7 @@
"dev": "node .electron-vue/dev-runner.js",
"rebuild": " ./node_modules/.bin/electron-rebuild",
"rebuild_win": "npm rebuild --runtime=electron --target=11.4.10 --disturl=https://atom.io/download/atom-shell --abi=85",
"rebuild_linux": "npm rebuild --runtime=electron --target=12.0.15 --disturl=https://atom.io/download/atom-shell --abi=87"
"rebuild_linux": "npm rebuild --runtime=electron --target=11.4.10 --disturl=https://atom.io/download/atom-shell --abi=85"
},
"build": {
"asar": true,
@@ -56,7 +56,9 @@
},
"linux": {
"icon": "build/icons/",
"publish": ["github"]
"publish": [
"github"
]
}
},
"dependencies": {
@@ -67,6 +69,7 @@
"download-git-repo": "^3.0.2",
"electron-is-dev": "^2.0.0",
"electron-store": "^8.0.0",
"extract-file-icon": "^0.3.2",
"iohook": "^0.9.3",
"is-chinese": "^1.4.2",
"jian-pinyin": "^0.2.3",
@@ -82,6 +85,7 @@
"request-promise": "^4.2.6",
"robotjs": "git+https://github.com/Toinane/robotjs.git",
"semver": "^7.3.5",
"shelljs": "^0.8.4",
"sudo-prompt": "^9.2.1",
"systeminformation": "^5.8.0",
"unzip": "^0.1.11",
@@ -109,7 +113,7 @@
"css-loader": "^0.28.11",
"del": "^3.0.0",
"devtron": "^1.4.0",
"electron": "^12.0.15",
"electron": "^11.4.10",
"electron-builder": "22.10.5",
"electron-debug": "^1.5.0",
"electron-devtools-installer": "^2.2.4",
@@ -140,7 +144,7 @@
"iohook": {
"targets": [
"node-83",
"electron-87"
"electron-85"
],
"platforms": [
"darwin",

View File

@@ -14,7 +14,7 @@ module.exports = () => {
win = new BrowserWindow({
height: 60,
useContentSize: true,
resizable: false,
resizable: true,
width: 800,
frame: false,
title: '拉比克',

View File

@@ -5,7 +5,7 @@ import pkg from '../../../package.json';
const os = require('os');
const version = pkg.version;
const releaseUrl = 'http://rubick-server.qa.91jkys.com/release/query';
const releaseUrl = 'http://118.195.176.247:8080/release/query';
export async function autoUpdate() {
let res;

View File

@@ -7,7 +7,7 @@ const configPath = path.join(getlocalDataFile(), './rubick-config.json')
let defaultConfig = {
Darwin: {
version: 2,
version: 3,
perf: {
shortCut: {
showAndHidden: 'Option+R',
@@ -32,7 +32,7 @@ let defaultConfig = {
global: []
},
Windows_NT: {
version: 2,
version: 3,
perf: {
shortCut: {
showAndHidden: 'Option+R',
@@ -57,7 +57,7 @@ let defaultConfig = {
global: []
},
Linux: {
version: 1,
version: 3,
perf: {
shortCut: {
showAndHidden: 'Option+R',
@@ -85,13 +85,13 @@ let defaultConfig = {
global.opConfig = {
config: null,
get() {
const platform = os.type()
const platform = os.type();
try {
if (!opConfig.config) {
opConfig.config = JSON.parse(fs.readFileSync(configPath) || JSON.stringify(defaultConfig[platform]))
}
// 重置
if (!opConfig.version || opConfig.version < defaultConfig[platform].version) {
if (!opConfig.config.version || opConfig.config.version < defaultConfig[platform].version) {
opConfig.config = defaultConfig[platform]
fs.writeFileSync(configPath, JSON.stringify(opConfig.config))
}
@@ -102,7 +102,7 @@ global.opConfig = {
}
},
set(key, value) {
opConfig.config[key] = value
opConfig.config[key] = value;
fs.writeFileSync(configPath, JSON.stringify(opConfig.config))
}
}

View File

@@ -1,4 +1,14 @@
import {app, nativeImage, BrowserWindow, clipboard, globalShortcut, ipcMain, Notification, screen, TouchBar} from "electron";
import {
app,
nativeImage,
BrowserWindow,
clipboard,
globalShortcut,
ipcMain,
Notification,
screen,
TouchBar
} from 'electron';
import {exec, spawn} from "child_process";
import robot from "robotjs";
import Api from "./api";
@@ -54,7 +64,7 @@ class Listener {
// 注册偏好快捷键
globalShortcut.register(config.perf.shortCut.showAndHidden, () => {
const {x, y} = screen.getCursorScreenPoint();
const currentDisplay = screen.getDisplayNearestPoint({ x, y });
const currentDisplay = screen.getDisplayNearestPoint({x, y});
const wx = parseInt(currentDisplay.workArea.x + currentDisplay.workArea.width / 2 - 400);
const wy = parseInt(currentDisplay.workArea.y + currentDisplay.workArea.height / 2 - 200);
@@ -86,7 +96,7 @@ class Listener {
init(mainWindow) {
this.fn = throttle(({x, y}, picker) => {
const { scaleFactor } = screen.getDisplayNearestPoint({x, y});
const {scaleFactor} = screen.getDisplayNearestPoint({x, y});
const img = robot.screen.capture(
x - parseInt(5 / scaleFactor),
y - parseInt(5 / scaleFactor),
@@ -96,7 +106,7 @@ class Listener {
const colors = {}
for(let i = 0; i< 9; i++) {
for (let i = 0; i < 9; i++) {
colors[i] = {};
for (let j = 0; j < 9; j++) {
colors[i][j] = img.colorAt(j, i);
@@ -142,7 +152,7 @@ class Listener {
const colors = {}
for(let i = 0; i< 9; i++) {
for (let i = 0; i < 9; i++) {
colors[i] = {};
for (let j = 0; j < 9; j++) {
colors[i][j] = img.colorAt(j, i);
@@ -173,7 +183,7 @@ class Listener {
let y = e.y
const color = "#" + robot.getPixelColor(parseInt(x), parseInt(y));
clipboard.writeText("#" + robot.getPixelColor(parseInt(x), parseInt(y)));
new Notification({ title: 'Rubick 通知', body: `${color} 已保存到剪切板` }).show();
new Notification({title: 'Rubick 通知', body: `${color} 已保存到剪切板`}).show();
this.closePicker();
}
});
@@ -195,7 +205,7 @@ class Listener {
initTouchBar(mainWindow) {
if (!commonConst.macOS()) return;
const { TouchBarButton, TouchBarGroup, TouchBarPopover } = TouchBar;
const {TouchBarButton, TouchBarGroup, TouchBarPopover} = TouchBar;
let items = [];
let system = [];
ipcMain.on('pluginInit', (e, args) => {
@@ -218,7 +228,7 @@ class Listener {
}).filter(Boolean);
system = args.plugins.map((item) => {
if(item.type === 'system') {
if (item.type === 'system') {
return new TouchBarButton({
icon: nativeImage.createFromDataURL(item.logo).resize({width: 20, height: 20}),
click() {
@@ -364,6 +374,7 @@ class Listener {
windowMoveInit(win) {
let hasInit = false;
ipcMain.on('window-move', () => {
let bounds = win.getBounds();
if (!hasInit) {
hasInit = true;
ioHook.start(false);
@@ -377,8 +388,9 @@ class Listener {
const cursorPosition = screen.getCursorScreenPoint();
const dx = winStartPosition.x + cursorPosition.x - mouseStartPosition.x;
const dy = winStartPosition.y + cursorPosition.y - mouseStartPosition.y;
let {x, y} = {x: dx, y: dy};
win.setPosition(parseInt(x), parseInt(y));
bounds.x = parseInt(dx);
bounds.y = parseInt(dy);
win.setBounds(bounds);
});
ioHook.on('mouseup', e => {

View File

@@ -1,10 +1,12 @@
import { app, globalShortcut } from 'electron'
import '../renderer/store'
import init from './common/common'
import { autoUpdate } from './common/autoUpdate'
import createTray from './tray'
import { commonConst } from './common/utils'
import pkg from '../../package.json'
import Store from 'electron-store';
Store.initRenderer()
const { main } = require("./browsers")()
@@ -106,4 +108,4 @@ class initApp {
}
}
(new initApp()).launchApp()
(new initApp()).launchApp()

View File

@@ -0,0 +1,120 @@
import fs from "fs";
import path from "path";
import {nativeImage} from "electron";
import translate from "./translate";
import {APP_FINDER_PATH} from "./constans";
import iconvLite from "iconv-lite";
import bpList from "bplist-parser";
const fileLists = [];
const isZhRegex = /[\u4e00-\u9fa5]/;
const getDisplayNameRegex = /\"(?:CFBundleDisplayName)\"\s\=\s\"(.*)\"/;
async function getAppZhName(rootPath, appName) {
try {
const ERROR_RESULT = '';
const systemPath = path.join(rootPath, `${appName}/Contents/Resources/zh_CN.lproj/InfoPlist.strings`);
const customizePath = path.join(rootPath, `${appName}/Contents/Resources/zh-Hans.lproj/InfoPlist.strings`);
let appInfoPath = '';
if (fs.existsSync(systemPath)) {
appInfoPath = systemPath;
} else if (fs.existsSync(customizePath)) {
appInfoPath = customizePath;
} else {
return ERROR_RESULT;
}
let appZhName = '';
if (rootPath == '/Applications') {
const container = iconvLite.decode(fs.readFileSync(appInfoPath), 'utf-16');
if (container) {
const res = container.match(getDisplayNameRegex);
appZhName = res && res[1];
} else {
return ERROR_RESULT;
}
} else {
const [{ CFBundleDisplayName = '', CFBundleName = '' }] = await bpList.parseFile(appInfoPath);
appZhName = CFBundleDisplayName || CFBundleName;
}
return appZhName;
} catch (error) {
return ERROR_RESULT;
}
}
function getDarwinAppList () {
APP_FINDER_PATH.forEach((searchPath, index) => {
fs.readdir(searchPath, async (err, files) => {
try {
for (let i = 0; i < files.length; i++) {
const appName = files[i];
const extname = path.extname(appName);
const appSubStr = appName.split(extname)[0];
if ((extname === '.app' || extname === '.prefPane') >= 0) {
try {
const path1 = path.join(searchPath, `${appName}/Contents/Resources/App.icns`);
const path2 = path.join(searchPath, `${appName}/Contents/Resources/AppIcon.icns`);
const path3 = path.join(searchPath, `${appName}/Contents/Resources/${appSubStr}.icns`);
const path4 = path.join(searchPath, `${appName}/Contents/Resources/${appSubStr.replace(' ', '')}.icns`);
let iconPath = path1;
if (fs.existsSync(path1)) {
iconPath = path1;
} else if (fs.existsSync(path2)) {
iconPath = path2;
} else if (fs.existsSync(path3)) {
iconPath = path3;
} else if (fs.existsSync(path4)) {
iconPath = path4;
} else {
// 性能最低的方式
const resourceList = fs.readdirSync(path.join(searchPath, `${appName}/Contents/Resources`));
const iconName = resourceList.filter((file) => path.extname(file) === '.icns')[0];
iconPath = path.join(searchPath, `${appName}/Contents/Resources/${iconName}`);
}
const img = await nativeImage.createThumbnailFromPath(iconPath, { width: 64, height: 64 });
const appZhName = await getAppZhName(searchPath, appName);
const fileOptions = {
value: 'plugin',
icon: img.toDataURL(),
desc: path.join(searchPath, appName),
type: 'app',
action: `open ${path.join(searchPath, appName).replace(' ', '\\ ')}`,
keyWords: [appSubStr]
};
if (appZhName && isZhRegex.test(appZhName)) {
const py = translate(appZhName);
const pinyinArr = py.split(',');
const firstLatter = pinyinArr.map(py => py[0]);
// 拼音
fileOptions.keyWords.push(pinyinArr.join(''));
// 缩写
fileOptions.keyWords.push(firstLatter.join(''));
// 中文
fileOptions.keyWords.push(appZhName);
}
fileLists.push({
...fileOptions,
name: appSubStr,
names: JSON.parse(JSON.stringify(fileOptions.keyWords)),
});
} catch (e) {}
}
}
} catch (e) {
console.log(e);
}
});
});
}
export const getApp = {
init: getDarwinAppList,
fileLists,
}

View File

@@ -1,19 +1,19 @@
import { WINDOW_MAX_HEIGHT, WINDOW_MIN_HEIGHT, PRE_ITEM_HEIGHT, SYSTEM_PLUGINS } from './constans';
import path from 'path';
import fs from 'fs';
import process from 'child_process';
import Store from 'electron-store';
import downloadFile from 'download';
import { nativeImage, ipcRenderer } from 'electron';
import { APP_FINDER_PATH } from './constans';
import { ipcRenderer } from 'electron';
import { getlocalDataFile } from '../../../main/common/utils';
import iconvLite from 'iconv-lite';
import bpList from 'bplist-parser';
import shell from 'shelljs';
import translate from './translate'
const getApp = process.platform === 'win32' ? require('./win-app').getApp : require('./darwin-app').getApp;
const store = new Store();
getApp.init();
const fileLists = getApp.fileLists;
function getWindowHeight(searchList) {
if (!searchList) return WINDOW_MAX_HEIGHT;
if (!searchList.length) return WINDOW_MIN_HEIGHT;
@@ -50,7 +50,7 @@ async function downloadZip(downloadRepoUrl, name) {
const temp_dest = `${plugin_path}/${name}`;
// 下载模板
if (await existOrNot(temp_dest)) {
await process.execSync(`rm -rf ${temp_dest}`);
shell.rm('-rf', temp_dest);
}
await downloadFile(downloadRepoUrl, plugin_path, { extract: true });
@@ -158,110 +158,6 @@ function find(p, target = 'plugin.json') {
console.log(e);
}
}
const fileLists = [];
// 默认搜索目录
const isZhRegex = /[\u4e00-\u9fa5]/;
const getDisplayNameRegex = /\"(?:CFBundleDisplayName)\"\s\=\s\"(.*)\"/;
async function getAppZhName(rootPath, appName) {
try {
const ERROR_RESULT = '';
const systemPath = path.join(rootPath, `${appName}/Contents/Resources/zh_CN.lproj/InfoPlist.strings`);
const customizePath = path.join(rootPath, `${appName}/Contents/Resources/zh-Hans.lproj/InfoPlist.strings`);
let appInfoPath = '';
if (fs.existsSync(systemPath)) {
appInfoPath = systemPath;
} else if (fs.existsSync(customizePath)) {
appInfoPath = customizePath;
} else {
return ERROR_RESULT;
}
let appZhName = '';
if (rootPath == '/Applications') {
const container = iconvLite.decode(fs.readFileSync(appInfoPath), 'utf-16');
if (container) {
const res = container.match(getDisplayNameRegex);
appZhName = res && res[1];
} else {
return ERROR_RESULT;
}
} else {
const [{ CFBundleDisplayName = '', CFBundleName = '' }] = await bpList.parseFile(appInfoPath);
appZhName = CFBundleDisplayName || CFBundleName;
}
return appZhName;
} catch (error) {
return ERROR_RESULT;
}
}
APP_FINDER_PATH.forEach((searchPath, index) => {
fs.readdir(searchPath, async (err, files) => {
try {
for (let i = 0; i < files.length; i++) {
const appName = files[i];
const extname = path.extname(appName);
const appSubStr = appName.split(extname)[0];
if ((extname === '.app' || extname === '.prefPane') >= 0) {
try {
const path1 = path.join(searchPath, `${appName}/Contents/Resources/App.icns`);
const path2 = path.join(searchPath, `${appName}/Contents/Resources/AppIcon.icns`);
const path3 = path.join(searchPath, `${appName}/Contents/Resources/${appSubStr}.icns`);
const path4 = path.join(searchPath, `${appName}/Contents/Resources/${appSubStr.replace(' ', '')}.icns`);
let iconPath = path1;
if (fs.existsSync(path1)) {
iconPath = path1;
} else if (fs.existsSync(path2)) {
iconPath = path2;
} else if (fs.existsSync(path3)) {
iconPath = path3;
} else if (fs.existsSync(path4)) {
iconPath = path4;
} else {
// 性能最低的方式
const resourceList = fs.readdirSync(path.join(searchPath, `${appName}/Contents/Resources`));
const iconName = resourceList.filter((file) => path.extname(file) === '.icns')[0];
iconPath = path.join(searchPath, `${appName}/Contents/Resources/${iconName}`);
}
const img = await nativeImage.createThumbnailFromPath(iconPath, { width: 64, height: 64 });
const appZhName = await getAppZhName(searchPath, appName);
const fileOptions = {
value: 'plugin',
icon: img.toDataURL(),
desc: path.join(searchPath, appName),
type: 'app',
action: `open ${path.join(searchPath, appName).replace(' ', '\\ ')}`,
keyWords: [appSubStr]
};
if (appZhName && isZhRegex.test(appZhName)) {
const py = translate(appZhName);
const pinyinArr = py.split(',');
const firstLatter = pinyinArr.map(py => py[0]);
// 拼音
fileOptions.keyWords.push(pinyinArr.join(''));
// 缩写
fileOptions.keyWords.push(firstLatter.join(''));
// 中文
fileOptions.keyWords.push(appZhName);
}
fileLists.push({
...fileOptions,
name: appSubStr,
names: JSON.parse(JSON.stringify(fileOptions.keyWords)),
});
} catch (e) {}
}
}
} catch (e) {
console.log(e);
}
});
});
function debounce(fn, delay) {
let timer;

View File

@@ -0,0 +1,93 @@
import fs from 'fs';
import path from 'path';
import os from 'os';
import translate from './translate';
import {shell} from 'electron';
const fileIcon = require('extract-file-icon');
const filePath = path.resolve('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs');
const fileLists = [];
const isZhRegex = /[\u4e00-\u9fa5]/;
const icondir = path.join(os.tmpdir(), 'ProcessIcon');
const exists = fs.existsSync(icondir);
if (!exists) { fs.mkdirSync(icondir) }
const getico = app =>{
try {
const buffer = fileIcon(app.desc, 32);
const iconpath = path.join(icondir, `${app.name}.png`);
fs.exists(iconpath, exists => {
if (!exists) {
fs.writeFile(iconpath, buffer, 'base64', () => {});
}
});
} catch(e) {
console.log(e, app.desc);
}
}
function fileDisplay(filePath){
//根据文件路径读取文件,返回文件列表
fs.readdir(filePath,function(err,files){
if(err){
console.warn(err)
}else{
files.forEach(function(filename){
const filedir = path.join(filePath, filename);
fs.stat(filedir,function(eror,stats){
if(eror){
console.warn('获取文件stats失败');
} else {
const isFile = stats.isFile(); // 是文件
const isDir = stats.isDirectory(); // 是文件夹
if(isFile){
const appName = filename.split('.')[0];
const keyWords = [appName];
const appDetail = shell.readShortcutLink(filedir);
if (!appDetail.target) return;
if (isZhRegex.test(appName)) {
const py = translate(appName);
const pinyinArr = py.split(',');
const zh_firstLatter = pinyinArr.map(py => py[0]);
// 拼音
keyWords.push(pinyinArr.join(''));
// 缩写
keyWords.push(zh_firstLatter.join(''));
} else {
const firstLatter = appName.split(' ').map(name => name[0]).join('');
keyWords.push(firstLatter);
}
const icon = path.join(os.tmpdir(), 'ProcessIcon', `${encodeURIComponent(appName)}.png`);
const appInfo = {
value: 'plugin',
desc: appDetail.target,
type: 'app',
icon,
action: `start "dummyclient" "${appDetail.target}"`,
keyWords: keyWords,
name: appName,
names: JSON.parse(JSON.stringify(keyWords)),
}
fileLists.push(appInfo);
getico(appInfo);
}
if(isDir) {
fileDisplay(filedir); // 递归,如果是文件夹,就继续遍历该文件夹下面的文件
}
}
})
});
}
});
}
export const getApp = {
init: () => fileDisplay(filePath),
fileLists
}

View File

@@ -1,5 +1,5 @@
const {getData, getlocalDataFile, saveData} = require("./utils");
const axios = require('axios');
const marked = require("marked");
const rendererMD = new marked.Renderer();
const path = require('path');
@@ -255,6 +255,14 @@ window.rubick = {
shellOpenPath(path) {
shell.openPath(path)
},
request(config = {}) {
return axios.create({
timeout: 10000,
withCredentials: true,
...config,
});
}
}
const preloadPath = getQueryVariable('preloadPath') || './preload.js';