Compare commits

..

149 Commits

Author SHA1 Message Date
muwoo
357846d2e6 👷 构建问题 2022-01-13 20:38:59 +08:00
muwoo
3b3ddf224c 👷 构建问题 2022-01-13 20:32:55 +08:00
muwoo
4439d0548f 👷 构建问题 2022-01-13 20:29:17 +08:00
muwoo
21163b2277 👷 构建问题 2022-01-13 20:13:50 +08:00
muwoo
480aaf2970 👷 构建问题 2022-01-13 20:12:27 +08:00
muwoo
06596d87ae 👷 构建问题 2022-01-13 19:57:17 +08:00
muwoo
1008e86fbb 👷 构建问题 2022-01-13 19:17:08 +08:00
muwoo
62ec316337 :ci: 构建问题 2022-01-13 18:59:57 +08:00
木偶
735a450260 Merge pull request #81 from rubickCenter/feat-dev
LGTM
2022-01-13 18:41:08 +08:00
muwoo
417ab071df 🐛 #80 修复 win depd bug 2022-01-13 18:40:04 +08:00
muwoo
1e73ab5ee6 🐛 #80 修复 win depd bug 2022-01-13 18:38:44 +08:00
muwoo
e5ff219685 ref: 修复win下多文件复制 2022-01-13 13:48:20 +08:00
muwoo
2beac06e7c ref: 修复win下多文件复制 2022-01-13 13:47:55 +08:00
muwoo
6b96df3da5 🐛 修复复制文件bug 2022-01-13 12:04:53 +08:00
muwoo
8521262344 Merge remote-tracking branch 'origin/master' 2022-01-11 19:39:12 +08:00
muwoo
4cf00f9270 支持 removePlugin API 2022-01-11 19:38:52 +08:00
muwoo
bdae8c280b ref: 支持win搜索快捷启动 2022-01-11 12:18:08 +08:00
muwoo
04e674d1cd 支持内网部署配置能力 2022-01-11 10:17:41 +08:00
muwoo
371565744e 支持内网部署配置能力 2022-01-10 19:13:26 +08:00
muwoo
58aabb9f1e 增加首屏打开速度 2022-01-05 19:41:14 +08:00
muwoo
19cd77b26c 支持插件分离,增加开发者工具功能 2022-01-04 14:03:25 +08:00
muwoo
c69be6c24f 增加 blur API 2021-12-29 20:15:11 +08:00
muwoo
8ca01d900d 📝 rubick 开发者文档更新 2021-12-29 17:07:12 +08:00
muwoo
1be13e5aa1 📝 更新打赏能力 2021-12-29 14:26:06 +08:00
muwoo
56faae0e35 🐛 #77, 修复偏好设置不生效问题 2021-12-28 14:20:18 +08:00
muwoo
f94c52f490 支持 ctrl/command + v 图片匹配 2021-12-28 14:18:34 +08:00
muwoo
a8eeac5f8f 支持 ctrl/command + v 图片匹配 2021-12-27 14:49:01 +08:00
木偶
91ce71f139 Merge pull request #76 from rubickCenter/pre-release
 支持开发者刷新插件
2021-12-27 11:55:28 +08:00
muwoo
ab8b616f6a 支持开发者刷新插件 2021-12-27 11:52:54 +08:00
muwoo
905838235c 支持 Escape 回到主界面 2021-12-24 13:50:42 +08:00
木偶
7bebcf4d54 Merge pull request #75 from flightless-bird/mybranch
🐛  修复启动app时转义不全问题
2021-12-24 11:32:25 +08:00
Flightless bird
01df2041a6 修复启动app时转义不全问题 2021-12-23 15:28:10 +08:00
muwoo
ee4c8031c4 🎉 rubick 2 正式投入开发 2021-12-23 12:01:06 +08:00
muwoo
cf19617714 Merge remote-tracking branch 'origin/pre-release' 2021-12-23 11:24:27 +08:00
muwoo
cfa4f80d63 feat: merge rubick2 2021-12-23 11:24:24 +08:00
muwoo
e89cfd049a 📄 增加 MIT 协议 2021-12-23 11:07:31 +08:00
muwoo
1644f5606e 📝 文档修改 2021-12-23 11:01:30 +08:00
muwoo
86a5a4bb23 📝 文档修改 2021-12-23 10:45:29 +08:00
muwoo
1cac93f1d2 📝 更新文档 2021-12-23 10:15:35 +08:00
muwoo
024aace801 📝 更新文档 2021-12-22 18:11:59 +08:00
muwoo
acddbb724f 🐛 更新 2021-12-22 16:52:13 +08:00
muwoo
dc656d3bca 🐛 修复插件市场搜索空白问题 2021-12-22 14:58:42 +08:00
muwoo
5cfaa70c67 feat: suport windows search app 2021-12-22 11:58:46 +08:00
muwoo
2cd6bab58c bugfix: 修复windows preload 加载路径错误 2021-12-21 18:38:19 +08:00
muwoo
581599614b windows ci build 2021-12-21 18:06:27 +08:00
muwoo
3ba4be2e4a feat: souport windows 2021-12-21 18:03:58 +08:00
木偶
5101491cc1 Update README.md 2021-12-20 20:53:22 +08:00
木偶
7c692e28be Update README.md 2021-12-20 20:53:04 +08:00
木偶
571ff12730 Update README.md 2021-12-20 20:45:18 +08:00
muwoo
ff118dfe2d 支持文件检索呼起插件 2021-12-20 18:33:00 +08:00
muwoo
ced8aa846b 增加 dbStorage API 2021-12-18 16:17:49 +08:00
muwoo
7cb78e00a8 支持 template 模板 list 模式 2021-12-17 18:14:56 +08:00
muwoo
8250dfdb59 支持 template 模板 list 模式 2021-12-17 18:07:18 +08:00
muwoo
ef33ff0a3d 支持 template 模板 list 模式 2021-12-17 17:59:07 +08:00
muwoo
8bcc5d409c 支持 template 模板 list 模式 2021-12-17 17:47:51 +08:00
muwoo
afca4f2b5a 🙈 不忽略public目录 2021-12-15 14:53:17 +08:00
muwoo
c855fb470e 支持插件详情展示 2021-12-15 13:36:00 +08:00
muwoo
898e78080c 支持插件详情展示 2021-12-15 13:35:16 +08:00
muwoo
2c6d0c4c15 🚧 ci 2021-12-15 13:34:18 +08:00
muwoo
777cf015ef 💚 修复ci build 2021-12-15 12:01:47 +08:00
muwoo
3a05e13948 👷 支持github workflow 2021-12-15 11:58:11 +08:00
muwoo
7fc60e2ca8 🐛 构建bugfix 2021-12-13 20:17:51 +08:00
muwoo
a9538f5d86 🐛 修复构建bug 2021-12-13 18:00:51 +08:00
muwoo
6f9830a181 支持插件详情展示 2021-12-10 17:48:08 +08:00
muwoo
6ee0b2a795 支持系统菜单功能 2021-12-10 16:47:33 +08:00
Starry North
6310b5a1bd Update README.md 2021-12-09 22:11:36 +08:00
Starry North
9f3397f369 fix: 二维码图片 2021-12-09 22:10:33 +08:00
muwoo
c9d6b04a7d 📝 更新readme 2021-12-09 21:00:36 +08:00
muwoo
1ee379b288 适配超级面板 2021-12-09 20:36:33 +08:00
muwoo
b3a00c88ad 支持插件开发者模式 2021-12-09 11:46:06 +08:00
muwoo
fc7e3e91bd 支持偏好设置 2021-12-08 21:28:16 +08:00
muwoo
64f2eba2fa 支持系统插件 2021-12-08 20:01:25 +08:00
muwoo
8a35e60e48 ♻️ search 框输入交互优化 2021-12-08 15:51:54 +08:00
muwoo
951f21f5fa API 部分迁移完成 2021-12-08 13:45:40 +08:00
muwoo
a9827c6db1 支持 setSubInput API 2021-12-07 11:19:08 +08:00
muwoo
1353c440aa ♻️ 代码重构 2021-12-06 18:23:34 +08:00
muwoo
cd41f0561c 修改插件runner为browserview 2021-12-03 17:54:58 +08:00
muwoo
0132a11d7e 支持ui插件下载&运行 2021-12-02 17:55:45 +08:00
muwoo
c2f43bea39 主界面开发&插件运行容器开发&菜单开发 2021-11-30 15:44:56 +08:00
muwoo
766ba46dcc 主界面开发&插件运行容器开发&菜单开发 2021-11-26 17:22:45 +08:00
木偶
8fb01cd158 Merge pull request #71 from woojufon/patch-5
自定义 CommandOrControl+W,关闭窗口会导致错误
2021-11-22 15:09:01 +08:00
woojufon
4a2b2ab82d 自定义 CommandOrControl+W,关闭窗口会导致错误
自定义 CommandOrControl+W,关闭窗口会导致错误
2021-11-22 15:04:22 +08:00
muwoo
072d57f068 🎉 初始化项目 electron13 + vue3 + typescript 2021-11-19 15:57:25 +08:00
muwoo
a7cbc2c890 🎉 初始化项目 electron13 + vue3 + typescript 2021-11-19 13:54:34 +08:00
muwoo
b3ad8343ca init 2021-11-19 10:36:38 +08:00
Starry North
b198288c7f 更新二维码 2021-10-22 13:26:56 +08:00
木偶
2315e095cc Merge pull request #66 from tcsnzh/dev-tcsnzh
修改钉住样式。
2021-10-11 09:43:13 +08:00
木偶
0fda62e82c Merge pull request #65 from mahaoming/master
部分app搜索不到
2021-10-11 09:39:12 +08:00
tcsnzh
b95c8fad6f Merge branch 'master' of github.com:clouDr-f2e/rubick into dev-tcsnzh 2021-10-10 15:06:59 +08:00
tcsnzh
c0adb7a03d 修改'钉住'样式从图钉按钮改为在插件菜单中选项. 2021-10-10 15:06:27 +08:00
sovlookup
81cc106452 doc: 修复文档截图问题 2021-10-09 17:17:18 +08:00
sovlookup
566927ab7b doc: 修复文档URL 2021-10-09 16:43:50 +08:00
马豪铭
4309a86eff ref: 中文转拼音优化 2021-10-08 15:44:51 +08:00
马豪铭
28f8c47752 fix: 部分app的中文名获取错误 2021-10-08 15:43:18 +08:00
木偶
74e4956050 Merge pull request #62 from tcsnzh/dev-tcsnzh
失焦隐藏or不隐藏小按钮 + 优化代码
2021-10-08 10:03:51 +08:00
tcsnzh
ba45c627ab 失焦隐藏or不隐藏小按钮现在不会在搜索框出现,且切换到搜索框时一定会隐藏 2021-10-04 20:30:49 +08:00
tcsnzh
747c5512a7 增加失焦隐藏or不隐藏的小按钮 2021-10-04 19:14:16 +08:00
tcsnzh
60b1138dd8 修改部分不规范的命名,优化部分可简化的代码 2021-10-04 16:46:18 +08:00
tcsnzh
960b16bb4c 修改一个单词+一个等号 2021-10-02 11:32:08 +08:00
tcsnzh
ef6ac06c40 Merge branch 'master' of github.com:clouDr-f2e/rubick into dev-tcsnzh 2021-10-02 11:27:23 +08:00
muwoo
80476aadf7 changelog 2021-09-28 11:31:44 +08:00
muwoo
c3fdbbc3a8 chore(release): 0.0.12 2021-09-28 11:28:49 +08:00
muwoo
bcd89ec3c3 Merge remote-tracking branch 'origin/rubick-2.0' 2021-09-28 11:27:20 +08:00
muwoo
50452bc36b bugfix: [48]; win应用搜索出现卸载应用问题。
ref: 优化rubick removeFeature API
2021-09-28 11:25:51 +08:00
muwoo
79b60b89a5 ref: 支持新api 2021-09-27 19:07:49 +08:00
muwoo
e4aafb8e60 Merge branch 'master' of github.com:clouDr-f2e/rubick 2021-09-27 11:48:20 +08:00
muwoo
7064f504de Merge branch 'rubick-2.0' 2021-09-27 11:47:29 +08:00
muwoo
96f2f62787 chore(release): 0.0.11 2021-09-27 11:47:10 +08:00
muwoo
26bd47cce7 bugfix: [#52]windows alt + space 热键冲突问题 2021-09-27 11:47:04 +08:00
木偶
daa422b8e7 Merge pull request #50 from SOVLOOKUP/master
增加微信公众号二维码
2021-09-26 10:54:33 +08:00
sovlookup
243c954ab6 add wechat qrcode 2021-09-26 10:33:17 +08:00
sovlookup
6e02163752 add wechat qrcode 2021-09-26 10:25:40 +08:00
tcsnzh
f9559ac1b0 Merge remote-tracking branch 'main/master' into dev-tcsnzh
合并主分支.
2021-09-24 11:21:33 +08:00
tcsnzh
fb6006d96b 格式化main.js 2021-09-24 11:19:29 +08:00
muwoo
c89ecb9c66 docs: 增加 changelog 功能 2021-09-23 17:50:02 +08:00
muwoo
0a1ba91792 docs: 增加 changelog 功能 2021-09-23 17:48:23 +08:00
muwoo
858bec38b2 chore(release): 0.0.10 2021-09-23 17:47:24 +08:00
muwoo
3bd9271403 docs: 增加 changelog 功能 2021-09-23 17:47:03 +08:00
muwoo
69626ed69e feat: 增加 changelog 功能 2021-09-23 17:35:22 +08:00
muwoo
a16b198798 chore(release): 0.0.9 2021-09-23 17:03:03 +08:00
muwoo
988acd7638 docs: 文档修改 2021-09-23 17:02:18 +08:00
muwoo
6a9d3517c4 chore(release): 0.0.8 2021-09-23 17:01:18 +08:00
muwoo
96f9d41460 0.0.7 2021-09-23 16:55:38 +08:00
muwoo
ace106a231 docs: update README.md 2021-09-22 18:08:15 +08:00
muwoo
6864836399 docs: update README.md 2021-09-22 18:04:55 +08:00
muwoo
7a1777e083 ref: 优化window读取系统应用 2021-09-22 17:39:25 +08:00
muwoo
fa30074019 ref: 优化window读取系统应用 2021-09-22 17:36:59 +08:00
muwoo
5f4e77f88e docs: update docs 2021-09-22 17:35:30 +08:00
muwoo
2df00360e4 docs: 更新英文文档 2021-09-22 17:30:36 +08:00
muwoo
1c893542b9 bugfix: 修复windows下读取插件preload.js位置问题;
ref:搜索插件不区分大消息
2021-09-22 15:38:52 +08:00
木偶
e9fa7b2276 Merge pull request #47 from bijiwiki-community/dev-tcsnzh
使搜索框在存在输入内容时mousedown不会造成拖动
2021-09-21 11:05:11 +08:00
tcsnzh
db12d66ff7 适配Linux Gnome的复制plugin.json文件的功能。修复粘贴plugin.json受定时器影响不跳出按钮、搜索栏为空时退格路由报错两个Bug 2021-09-20 23:37:13 +08:00
tcsnzh
9d3e7fc4a5 使搜索框在存在输入内容时mousedown不会造成拖动 2021-09-20 18:56:16 +08:00
muwoo
be21302738 docs: update readme 2021-09-17 17:21:35 +08:00
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
muwoo
8d340cb76c bugfix:#40 2021-09-03 14:22:41 +08:00
251 changed files with 57924 additions and 18525 deletions

View File

@@ -1,30 +0,0 @@
{
"comments": false,
"env": {
"main": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
]
},
"renderer": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
},
"web": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}

3
.browserslistrc Normal file
View File

@@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

View File

@@ -1,132 +0,0 @@
'use strict'
process.env.NODE_ENV = 'production'
const { say } = require('cfonts')
const chalk = require('chalk')
const del = require('del')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Multispinner = require('multispinner')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
const webConfig = require('./webpack.web.config')
const doneLog = chalk.bgGreen.white(' DONE ') + ' '
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
const isCI = process.env.CI || false
if (process.env.BUILD_TARGET === 'clean') clean()
else if (process.env.BUILD_TARGET === 'web') web()
else build()
function clean () {
del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
console.log(`\n${doneLog}\n`)
process.exit()
}
function build () {
greeting()
del.sync(['dist/electron/*', '!.gitkeep'])
const tasks = ['main', 'renderer']
const m = new Multispinner(tasks, {
preText: 'building',
postText: 'process'
})
let results = ''
m.on('success', () => {
process.stdout.write('\x1B[2J\x1B[0f')
console.log(`\n\n${results}`)
console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
process.exit()
})
pack(mainConfig).then(result => {
results += result + '\n\n'
m.success('main')
}).catch(err => {
m.error('main')
console.log(`\n ${errorLog}failed to build main process`)
console.error(`\n${err}\n`)
process.exit(1)
})
pack(rendererConfig).then(result => {
results += result + '\n\n'
m.success('renderer')
}).catch(err => {
m.error('renderer')
console.log(`\n ${errorLog}failed to build renderer process`)
console.error(`\n${err}\n`)
process.exit(1)
})
}
function pack (config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (err, stats) => {
if (err) reject(err.stack || err)
else if (stats.hasErrors()) {
let err = ''
stats.toString({
chunks: false,
colors: true
})
.split(/\r?\n/)
.forEach(line => {
err += ` ${line}\n`
})
reject(err)
} else {
resolve(stats.toString({
chunks: false,
colors: true
}))
}
})
})
}
function web () {
del.sync(['dist/web/*', '!.gitkeep'])
webConfig.mode = 'production'
webpack(webConfig, (err, stats) => {
if (err || stats.hasErrors()) console.log(err)
console.log(stats.toString({
chunks: false,
colors: true
}))
process.exit()
})
}
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 85) text = 'lets-build'
else if (cols > 60) text = 'lets-|build'
else text = false
if (text && !isCI) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n lets-build'))
console.log()
}

View File

@@ -1,40 +0,0 @@
const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(event => {
/**
* Reload browser when HTMLWebpackPlugin emits a new index.html
*
* Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
* https://github.com/SimulatedGREG/electron-vue/issues/437
* https://github.com/jantimon/html-webpack-plugin/issues/680
*/
// if (event.action === 'reload') {
// window.location.reload()
// }
/**
* Notify `mainWindow` when `main` process is compiling,
* giving notice for an expected reload of the `electron` process
*/
if (event.action === 'compiling') {
document.body.innerHTML += `
<style>
#dev-client {
background: #4fc08d;
border-radius: 4px;
bottom: 20px;
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
color: #fff;
font-family: 'Source Sans Pro', sans-serif;
left: 20px;
padding: 8px 12px;
position: absolute;
}
</style>
<div id="dev-client">
Compiling Main Process...
</div>
`
}
})

View File

@@ -1,191 +0,0 @@
'use strict'
const chalk = require('chalk')
const electron = require('electron')
const path = require('path')
const { say } = require('cfonts')
const { spawn } = require('child_process')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const webpackHotMiddleware = require('webpack-hot-middleware')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
let electronProcess = null
let manualRestart = false
let hotMiddleware
function logStats (proc, data) {
let log = ''
log += chalk.yellow.bold(`${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`)
log += '\n\n'
if (typeof data === 'object') {
data.toString({
colors: true,
chunks: false
}).split(/\r?\n/).forEach(line => {
log += ' ' + line + '\n'
})
} else {
log += ` ${data}\n`
}
log += '\n' + chalk.yellow.bold(`${new Array(28 + 1).join('-')}`) + '\n'
console.log(log)
}
function startRenderer () {
return new Promise((resolve, reject) => {
rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
rendererConfig.mode = 'development'
const compiler = webpack(rendererConfig)
hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500
})
compiler.hooks.compilation.tap('compilation', compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
compiler.hooks.done.tap('done', stats => {
logStats('Renderer', stats)
})
const server = new WebpackDevServer(
compiler,
{
contentBase: path.join(__dirname, '../'),
quiet: true,
hot: true,
before (app, ctx) {
// app.use(hotMiddleware)
ctx.middleware.waitUntilValid(() => {
resolve()
})
}
}
)
server.listen(9080)
})
}
function startMain () {
return new Promise((resolve, reject) => {
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
mainConfig.mode = 'development'
const compiler = webpack(mainConfig)
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
logStats('Main', chalk.white.bold('compiling...'))
hotMiddleware.publish({ action: 'compiling' })
done()
})
compiler.watch({}, (err, stats) => {
if (err) {
console.log(err)
return
}
logStats('Main', stats)
if (electronProcess && electronProcess.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 5000)
}
resolve()
})
})
}
function startElectron () {
var args = [
'--inspect=5858',
path.join(__dirname, '../dist/electron/main.js')
]
// detect yarn or npm and process commandline args accordingly
if (process.env.npm_execpath.endsWith('yarn.js')) {
args = args.concat(process.argv.slice(3))
} else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
args = args.concat(process.argv.slice(2))
}
electronProcess = spawn(electron, args)
electronProcess.stdout.on('data', data => {
electronLog(data, 'blue')
})
electronProcess.stderr.on('data', data => {
electronLog(data, 'red')
})
electronProcess.on('close', () => {
if (!manualRestart) process.exit()
})
}
function electronLog (data, color) {
let log = ''
data = data.toString().split(/\r?\n/)
data.forEach(line => {
log += ` ${line}\n`
})
if (/[0-9A-z]+/.test(log)) {
console.log(
chalk[color].bold('┏ Electron -------------------') +
'\n\n' +
log +
chalk[color].bold('┗ ----------------------------') +
'\n'
)
}
}
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 104) text = 'electron-vue'
else if (cols > 76) text = 'electron-|vue'
else text = false
if (text) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n electron-vue'))
console.log(chalk.blue(' getting ready...') + '\n')
}
function init () {
greeting()
Promise.all([startRenderer(), startMain()])
.then(() => {
startElectron()
})
.catch(err => {
console.error(err)
})
}
init()

View File

@@ -1,82 +0,0 @@
'use strict'
process.env.BABEL_ENV = 'main'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const MinifyPlugin = require("babel-minify-webpack-plugin")
let mainConfig = {
entry: {
main: path.join(__dirname, '../src/main/index.js')
},
externals: [
...Object.keys(dependencies || {})
],
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
},
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
],
resolve: {
extensions: ['.js', '.json', '.node']
},
target: 'electron-main'
}
/**
* Adjust mainConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
mainConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust mainConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
mainConfig.plugins.push(
new MinifyPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
})
)
}
module.exports = mainConfig

View File

@@ -1,191 +0,0 @@
'use strict'
process.env.BABEL_ENV = 'renderer'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const MinifyPlugin = require("babel-minify-webpack-plugin")
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
/**
* List of node_modules to include in webpack bundle
*
* Required for specific packages like Vue UI libraries
* that provide pure *.vue files that need compiling
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
*/
let whiteListedModules = ['vue']
let rendererConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
renderer: path.join(__dirname, '../src/renderer/main.js')
},
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
],
module: {
rules: [
{
test: /\.scss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.sass$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax']
},
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: process.env.NODE_ENV === 'production',
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader',
less: 'vue-style-loader!css-loader!less-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]'
}
}
}
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: 'styles.css'}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options,
},
process,
};
},
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: process.env.NODE_ENV !== 'production'
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json', '.css', '.node']
},
target: 'electron-renderer'
}
/**
* Adjust rendererConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
rendererConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust rendererConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
new MinifyPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/electron/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = rendererConfig

View File

@@ -1,152 +0,0 @@
'use strict'
process.env.BABEL_ENV = 'web'
const path = require('path')
const webpack = require('webpack')
const MinifyPlugin = require("babel-minify-webpack-plugin")
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
let webConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
web: path.join(__dirname, '../src/renderer/main.js')
},
module: {
rules: [
{
test: /\.scss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.sass$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax']
},
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
include: [ path.resolve(__dirname, '../src/renderer') ],
exclude: /node_modules/
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: true,
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader',
less: 'vue-style-loader!css-loader!less-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name].[ext]'
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
}
]
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: 'styles.css'}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options,
},
process,
};
},
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: false
}),
new webpack.DefinePlugin({
'process.env.IS_WEB': 'true'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
path: path.join(__dirname, '../dist/web')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json', '.css']
},
target: 'web'
}
/**
* Adjust webConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
webConfig.devtool = ''
webConfig.plugins.push(
new MinifyPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = webConfig

20
.eslintrc.js Normal file
View File

@@ -0,0 +1,20 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint",
],
parserOptions: {
ecmaVersion: 2020,
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
},
};

View File

@@ -23,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-2019]
os: [macos-11, windows-2022]
# create steps
steps:
@@ -43,26 +43,10 @@ jobs:
sudo apt-get install libxtst-dev libpng++-dev
sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils
# step3: yarn
- name: macos Yarn install
if: ${{runner.os == 'macOS'}}
- name: Yarn install
run: |
yarn
yarn global add xvfb-maybe
npm run rebuild
- name: windows Yarn install
if: ${{runner.os == 'Windows'}}
run: |
yarn
yarn global add xvfb-maybe
npm run rebuild_win
- name: linux Yarn install
if: ${{runner.os == 'Linux'}}
run: |
yarn
yarn global add xvfb-maybe
npm run rebuild_linux
- name: Build & release app
run: |

34
.gitignore vendored
View File

@@ -1,12 +1,26 @@
.DS_Store
dist/electron/*
dist/web/*
build/*
!build/icons/
node_modules/
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep
node_modules
/build
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
dist/
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
#Electron-builder output
/dist_electron

View File

@@ -1,5 +0,0 @@
{
"0 debug pnpm:scope": {
"selected": 1
}
}

View File

@@ -1,40 +0,0 @@
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
osx_image: xcode8.3
sudo: required
dist: trusty
language: c
matrix:
include:
- os: osx
# - os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
apt:
packages:
- libgnome-keyring-dev
- icnsutils
before_install:
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
"$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
| tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
install:
- nvm install 8.9
- curl -o- -L https://yarnpkg.com/install.sh | bash
- source ~/.bashrc
- npm install -g xvfb-maybe
- yarn
- cd docs
- yarn
script:
- npm run release
- sh deploy.sh
branches:
only:
- master

123
README.md
View File

@@ -1,3 +1,6 @@
English | [简体中文](./README.zh-CN.md)
<div align= "center">
<img align="center" width=200 src="https://user-images.githubusercontent.com/21073039/128333805-73e086f0-5523-46a3-a096-cba80b904c46.png" />
</div>
@@ -5,93 +8,87 @@
<div align= "center">
<h1>Rubick</h1>
<img alt="release" src="https://img.shields.io/github/downloads/clouDr-f2e/rubick/total" />
<a href="https://github.com/clouDr-f2e/rubick/releases">
<img alt="release" src="https://img.shields.io/github/package-json/v/clouDr-f2e/rubick" />
</a>
<a href="https://github.com/clouDr-f2e/rubick/actions">
<img alt=building src=https://img.shields.io/github/workflow/status/clouDr-f2e/rubick/Build>
</a>
<a href="https://github.com/clouDr-f2e/rubick/blob/master/LICENSE">
<img alt="npm" src="https://img.shields.io/github/license/clouDr-f2e/rubick" />
</a>
<img alt="star" src="https://img.shields.io/github/stars/clouDr-f2e/rubick?style=social"></a>
<img alt="release" src="https://img.shields.io/github/downloads/rubickCenter/rubick/total" />
<a href="https://github.com/rubickCenter/rubick/releases">
<img alt="release" src="https://img.shields.io/github/package-json/v/rubickCenter/rubick" />
</a>
<a href="https://github.com/rubickCenter/rubick/actions">
<img alt=building src=https://img.shields.io/github/workflow/status/rubickCenter/rubick/Build>
</a>
<a href="https://github.com/rubickCenter/rubick/blob/master/LICENSE">
<img alt="npm" src="https://img.shields.io/github/license/rubickCenter/rubick" />
</a>
<a href="https://github.com/rubickCenter/rubick/stargazers">
<img alt="star" src="https://img.shields.io/github/stars/rubickCenter/rubick?style=social">
</a>
<a href="https://gitee.com/monkeyWang/rubick">
<img alt="码云" src="https://img.shields.io/badge/Gitee--yellow.svg?style=social&logo=data:image/svg+xml;base64,PHN2ZyB0PSIxNTc0ODM3MTM4ODM3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE3NzAiICAgICB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPiAgICA8cGF0aCBkPSJNODkxIDQyOC44SDQ2NS44Yy0yMC40IDAtMzcgMTYuNS0zNyAzN3Y5Mi40YzAgMjAuNCAxNi41IDM3IDM3IDM3aDI1OC45YzIwLjQgMCAzNyAxNi42IDM3IDM3djE4LjRjMCA2MS4zLTQ5LjcgMTEwLjktMTEwLjkgMTEwLjlIMjk5LjRjLTIwLjQgMC0zNy0xNi42LTM3LTM3VjM3My4yYzAtNjEuMyA0OS43LTExMC45IDExMC45LTExMC45aDUxNy42YzIwLjQgMCAzNy0xNi41IDM3LTM3bDAuMS05Mi4zYzAtMjAuNC0xNi41LTM3LTM3LTM3SDM3My4zQzIyMC4yIDk2IDk2IDIyMC4yIDk2IDM3My4zVjg5MWMwIDIwLjQgMTYuNiAzNyAzNyAzN2g1NDUuNEM4MTYuMiA5MjggOTI4IDgxNi4zIDkyOCA2NzguNFY0NjUuOGMwLTIwLjQtMTYuNi0zNy0zNy0zN3oiICAgICAgICAgIGZpbGw9IiNkODFlMDYiIHAtaWQ9IjE3NzEiPjwvcGF0aD48L3N2Zz4="/>
</a>
</div>
Based on electron open source toolbox, free integration of rich plug-ins, to create the ultimate desktop efficiency tool。Rubick is one of the heroes of Dota The core skill is the ability to use other heroes as plug-insFinished the walk 。Very consistent with the design concept of this toolSo named Rubick。
基于 electron 的开源工具箱自由集成丰富插件打造极致的桌面端效能工具。Rubick(拉比克) 是 dota 里面的英雄之一,其核心技能是插件化使用其他英雄的技能,用完即走。非常符合本工具的设计理念,所以取名 Rubick。
## Installation package
* [Rubick Mac OS](https://github.com/rubickCenter/rubick/releases)
* [Rubick Windows](https://github.com/rubickCenter/rubick/releases)
## 安装包
* [Rubick Mac OS](https://github.com/clouDr-f2e/rubick/releases)
* [Rubick Windows](https://github.com/clouDr-f2e/rubick/releases)
* [Rubick Linux](https://github.com/clouDr-f2e/rubick/releases)
## 支持能力
- [x] 支持远程下载安装插件,支持插件开发者模式
- [x] 支持插件分离
- [x] 支持系统命令取色、截屏、帮助
- [x] 支持超级面板,长按右击呼出
- [x] 支持全局快捷键设置
- [x] 支持搜索本地已安装 app 或 偏好设置
- [x] macos touchBar 唤起插件
- [x] 支持 Windows(目前是 mac 的 alpha 版本,功能尚不全,正在迁移中)
- [x] 支持 Linux
## Feature list
- [x] Plug-in management based on npm package mode, installing plugins is as easy as installing npm packages.
- [x] Support system plug-ins, as long as rubick is running, plug-ins can be used at any time.
- [x] Support global shortcut key settings
- [x] Support clipboard file search
- [x] Support searching for locally installed apps or preferences
- [x] Support MacOS
- [x] Support Windows
## Docs
![example.gif](https://user-images.githubusercontent.com/21073039/128359309-2377d3cf-7b70-4e8f-9973-ae8f337a8006.gif)
## 使用文档
[Rubick Docs](https://cloudr-f2e.github.io/rubick/)
[Rubick Docs](https://rubickCenter.github.io/rubick/)
## 使用问题
1. 依赖于 `robotjs` dev 环境运行请在 `install` 后执行 `npm run rebuild`
2. windows 版本目前有了一个最基础的可用版,代码在 [feat-win](https://github.com/clouDr-f2e/rubick/tree/feat-win) 分支。完整版正在开发中,敬请期待
3. linux 开发分支 cross-platform 的 iohook 开发模式下可能需要手动下载并放入node_modules, 将在未来修复
## Core function
### 1. Search system application
Support pinyin and abbreviations to search system applications
## 目前支持能力
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba363e8f60f540e6a5c365c4317c4413~tplv-k3u1fbpfcp-watermark.image)
### touchBar 唤起插件
macOS 支持 touchbar 快速唤起插件
### 2. UI plug-in installation
Click the `rubick` icon on the right side of the search box to enter the plug-in market, select the desired plug-in, and click the download button to download. After the download is complete, you can find the installed plug-in under the Installed tab
![2oyn8-wu97m (3)](https://user-images.githubusercontent.com/21073039/129894362-1dbb8436-921c-4138-be9c-072dc2e62549.gif)
After the installation is complete, enter the plug-in call up command to use the corresponding plug-in
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7ae45c7ede1f4e3bb7d35ae845e60b64~tplv-k3u1fbpfcp-watermark.image)
### 超级面板
长按鼠标右键,即可呼起超级面板,可以根据当前鼠标选择内容,匹配对应插件能力。比如当前选择图片后长按右击,则会呼起上传图床插件:
### 3. System plug-in installation
The system plug-in installation method is the same as that of the UI category. In the plug-in market, select the `system category` and find the system plug-in that suits you to install it.
```
After the system plug-in is installed successfully, rubick needs to be restarted to take effect
```
![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1706cc730f1f46078cb700a445211317~tplv-k3u1fbpfcp-watermark.image)
### 4. The input box focus automatically matches the plug-in according to the clipboard content
Search for `Preferences` in `rubick`, and then turn on the `Auto Paste` function to match the clipboard content and automatically match the plug-in for use.
### 模板
模板即是一个内置 UI 样式的功能插件。
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01ef50fbfa064ba9a88bebe1531eacd4~tplv-k3u1fbpfcp-watermark.image)
<img src=https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b113ad547974699b9c73c28bc09b9b1~tplv-k3u1fbpfcp-watermark.image width=500 />
### More features
If you need more features, please come here to give us suggestions[issues](https://github.com/rubickCenter/rubick/issues) 。
We will add valuable ideas to the later development. At the same time, welcome to join and build together。
### 系统命令
#### 取色
基于 `robot.js` 以及 `iohook` 实现。未使用 C++ 扩展。
## Sponsor
开源不容易,如果该项目对你有用的话,可以打赏我们喝杯 coffee ☕️.
![image](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3036ae85bf3549fc8bbbe2926ecbad55~tplv-k3u1fbpfcp-watermark.image)
<img width=200 src=https://pic1.zhimg.com/80/v2-688385687a37e962fe32daf136139feb_720w.png />
<img width=200 src=https://pica.zhimg.com/80/v2-1ba296fd2cece45ee1094ee7c259035c_720w.png />
#### 截屏
## 贡献
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. <a href="https://github.com/rubickCenter/rubick/graphs/contributors"><img src="https://opencollective.com/rubick/contributors.svg?width=890&button=false" /></a>
<img src=https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/18023dab52e1420c9e87362cefddb2a1~tplv-k3u1fbpfcp-watermark.image width=500 />
#### 全局快捷键
<img src=https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/62cc424eacac4c9eb178f0e055e87d9a~tplv-k3u1fbpfcp-watermark.image width=500 />
### 最后
## 反馈
对本项目有兴趣或者想要交流学习的同学可以扫码加下面的微信,备注 rubick帮助我们更好的成长
![image](https://user-images.githubusercontent.com/21073039/127327603-9796f246-ee4b-4950-a69d-ce3205ec9569.png)
## License
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/clouDr-f2e/rubick/blob/master/LICENSE) file for details.
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/rubickCenter/rubick/blob/master/LICENSE) file for details.

96
README.zh-CN.md Normal file
View File

@@ -0,0 +1,96 @@
[English](./README.md) | 简体中文
<div align= "center">
<img align="center" width=200 src="https://user-images.githubusercontent.com/21073039/128333805-73e086f0-5523-46a3-a096-cba80b904c46.png" />
</div>
<div align= "center">
<h1>Rubick</h1>
<img alt="release" src="https://img.shields.io/github/downloads/rubickCenter/rubick/total" />
<a href="https://github.com/rubickCenter/rubick/releases">
<img alt="release" src="https://img.shields.io/github/package-json/v/rubickCenter/rubick" />
</a>
<a href="https://github.com/rubickCenter/rubick/actions">
<img alt=building src=https://img.shields.io/github/workflow/status/rubickCenter/rubick/Build>
</a>
<a href="https://github.com/rubickCenter/rubick/blob/master/LICENSE">
<img alt="npm" src="https://img.shields.io/github/license/rubickCenter/rubick" />
</a>
<a href="https://github.com/rubickCenter/rubick/stargazers">
<img alt="star" src="https://img.shields.io/github/stars/rubickCenter/rubick?style=social">
</a>
<a href="https://gitee.com/monkeyWang/rubick">
<img alt="码云" src="https://img.shields.io/badge/Gitee--yellow.svg?style=social&logo=data:image/svg+xml;base64,PHN2ZyB0PSIxNTc0ODM3MTM4ODM3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE3NzAiICAgICB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPiAgICA8cGF0aCBkPSJNODkxIDQyOC44SDQ2NS44Yy0yMC40IDAtMzcgMTYuNS0zNyAzN3Y5Mi40YzAgMjAuNCAxNi41IDM3IDM3IDM3aDI1OC45YzIwLjQgMCAzNyAxNi42IDM3IDM3djE4LjRjMCA2MS4zLTQ5LjcgMTEwLjktMTEwLjkgMTEwLjlIMjk5LjRjLTIwLjQgMC0zNy0xNi42LTM3LTM3VjM3My4yYzAtNjEuMyA0OS43LTExMC45IDExMC45LTExMC45aDUxNy42YzIwLjQgMCAzNy0xNi41IDM3LTM3bDAuMS05Mi4zYzAtMjAuNC0xNi41LTM3LTM3LTM3SDM3My4zQzIyMC4yIDk2IDk2IDIyMC4yIDk2IDM3My4zVjg5MWMwIDIwLjQgMTYuNiAzNyAzNyAzN2g1NDUuNEM4MTYuMiA5MjggOTI4IDgxNi4zIDkyOCA2NzguNFY0NjUuOGMwLTIwLjQtMTYuNi0zNy0zNy0zN3oiICAgICAgICAgIGZpbGw9IiNkODFlMDYiIHAtaWQ9IjE3NzEiPjwvcGF0aD48L3N2Zz4="/>
</a>
</div>
基于 electron 的开源工具箱自由集成丰富插件打造极致的桌面端效能工具。Rubick(拉比克) 是 dota 里面的英雄之一,其核心技能是插件化使用其他英雄的技能,用完即走。非常符合本工具的设计理念,所以取名 Rubick。
## 安装包
* [Rubick Mac OS](https://github.com/rubickCenter/rubick/releases)
* [Rubick Windows](https://github.com/rubickCenter/rubick/releases)
## 支持能力
- [x] 基于 npm 包模式的插件管理,安装插件和安装 npm 包一样简单
- [x] 支持系统插件,只要在 rubick 运行时,插件可以随时使用
- [x] 支持全局快捷键设置
- [x] 支持剪贴板文件搜索
- [x] 支持搜索本地已安装 app 或 偏好设置
- [x] 支持 MacOS
- [x] 支持 Windows
## 使用文档
[Rubick Docs](https://rubickCenter.github.io/rubick/)
## 目前支持能力
### 1. 搜索系统应用
支持拼音和缩写来搜索系统安装应用:
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba363e8f60f540e6a5c365c4317c4413~tplv-k3u1fbpfcp-watermark.image)
### 2. UI类插件安装
点击搜索框右侧 `rubick` 图标,进入插件市场,选择所需插件,点击下载按钮即可下载,下载完成后在已安装 tab 下可以找到安装插件。
安装完成后,输入插件呼起命令即可使用对应插件:
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7ae45c7ede1f4e3bb7d35ae845e60b64~tplv-k3u1fbpfcp-watermark.image)
### 3. 系统类插件安装
系统插件安装方式和UI类一样在插件市场选择`系统分类`,寻找适合自己的系统插件安装即可。
```
系统插件安装成功后,需要重启 rubick 才能生效
```
### 4. 输入框聚焦自动根据剪切板内容匹配插件
`rubick` 内搜索`偏好设置`,然后开启`自动粘贴` 功能,即可匹配剪切板内容自动匹配适合插件进行使用。
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01ef50fbfa064ba9a88bebe1531eacd4~tplv-k3u1fbpfcp-watermark.image)
### 更多功能
如果您还需要更多功能,欢迎来这里给我们提建议:[issues](https://github.com/rubickCenter/rubick/issues) 。
有价值的想法我们会加入到后期的开发当中。同时也欢迎一起加入共建。
## 赞助
开源不容易,如果该项目对你有用的话,可以打赏我们喝杯 coffee ☕️.
<img width=200 src=https://pic1.zhimg.com/80/v2-688385687a37e962fe32daf136139feb_720w.png />
<img width=200 src=https://pica.zhimg.com/80/v2-1ba296fd2cece45ee1094ee7c259035c_720w.png />
## 贡献
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. <a href="https://github.com/rubickCenter/rubick/graphs/contributors"><img src="https://opencollective.com/rubick/contributors.svg?width=890&button=false" /></a>
## 反馈
对本项目有兴趣或者想要交流学习的同学可以扫码加下面的微信,备注 rubick帮助我们更好的成长
![image](https://user-images.githubusercontent.com/21073039/127327603-9796f246-ee4b-4950-a69d-ce3205ec9569.png)
## License
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/rubickCenter/rubick/blob/master/LICENSE) file for details.

View File

@@ -1,29 +0,0 @@
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- yarn
- node --version
build_script:
- yarn build
test: off

9
babel.config.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [
[
"import",
{ libraryName: "ant-design-vue", libraryDirectory: "es", style: "css" },
], // `style: true` 会加载 less 文件
],
};

View File

@@ -19,6 +19,6 @@ git commit -m 'deploy'
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git master
# 如果发布到 https://<USERNAME>.github.io/<REPO>
git push -f git@github.com:clouDr-f2e/rubick.git master:gh-pages
git push -f git@github.com:rubickCenter/rubick.git master:gh-pages
cd -

23
detach/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
detach/README.md Normal file
View File

@@ -0,0 +1,24 @@
# detach
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

3
detach/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
};

13240
detach/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

57
detach/package.json Normal file
View File

@@ -0,0 +1,57 @@
{
"name": "detach",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"lodash.throttle": "^4.1.1",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.0.0",
"prettier": "^2.2.1",
"typescript": "~4.1.5"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
detach/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

17
detach/public/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

150
detach/src/App.vue Normal file
View File

@@ -0,0 +1,150 @@
<template>
<div :class="[platform, 'detach']">
<div class="info">
<img :src="plugInfo.logo" />
<input
autofocus
@input="changeValue"
v-if="showInput"
:value="plugInfo.subInput?.value"
:placeholder="plugInfo.subInput?.placeholder"
/>
<span v-else>{{ plugInfo.pluginName }}</span>
</div>
<div class="handle">
<div class="devtool" @click="openDevTool" title="开发者工具"></div>
</div>
</div>
</template>
<script setup>
import throttle from "lodash.throttle";
import { ref } from "vue";
const { ipcRenderer } = window.require("electron");
const platform = ref(window.process.platform);
const plugInfo = ref({});
const showInput = ref(false);
window.initDetach = (pluginInfo) => {
plugInfo.value = pluginInfo;
showInput.value =
pluginInfo.subInput &&
(!!pluginInfo.subInput.value || !!pluginInfo.subInput.placeholder);
console.log(showInput.value);
};
const changeValue = throttle((e) => {
ipcRenderer.send("msg-trigger", {
type: "detachInputChange",
data: {
text: e.target.value,
},
});
}, 500);
const openDevTool = () => {
ipcRenderer.send("msg-trigger", { type: "openPluginDevTools" });
};
Object.assign(window, {
setSubInputValue: ({ value }) => {
plugInfo.value.subInput.value = value;
},
setSubInput: (placeholder) => {
plugInfo.value.subInput.placeholder = placeholder;
},
removeSubInput: () => {
plugInfo.value.subInput = null;
},
});
</script>
<style>
html, body {
margin: 0;
padding: 0;
font-family: system-ui, "PingFang SC", "Helvetica Neue", "Microsoft Yahei", sans-serif;
user-select: none;
overflow: hidden;
}
.detach {
width: 100%;
height: 56px;
display: flex;
align-items: center;
background: #eee;
}
.detach {
flex: 1;
display: flex;
align-items: center;
font-size: 18px;
padding-left: 10px;
font-weight: 500;
box-sizing: border-box;
justify-content: space-between;
}
.detach.darwin {
padding-left: 80px;
-webkit-app-region: drag;
}
.detach.win32 {
-webkit-app-region: drag;
}
.detach img {
width: 36px;
height: 36px;
margin-right: 10px;
}
.detach input {
background-color: #FFFFFF;
color: #333333;
width: 360px;
height: 36px;
line-height: 36px;
border-radius: 4px;
font-size: 14px;
border: none;
padding: 0 10px;
outline: none;
-webkit-app-region: no-drag;
}
.detach input::-webkit-input-placeholder {
color: #aaa;
user-select: none;
}
.detach .info {
display: flex;
align-items: center;
}
.handle {
display: flex;
-webkit-app-region: no-drag;
}
.handle > div {
width: 36px;
height: 36px;
border-radius: 18px;
cursor: pointer;
margin-right: 6px;
}
.handle > div:hover {
background-color: #dee2e6;
}
.detach .devtool {
background: center / 18px no-repeat url("./assets/devtool.svg");
}
</style>

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1576121932768" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2610" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M344.792 518.575L303.4 477.184a26.947 26.947 0 0 1 38.13-38.13l60.174 60.173a26.947 26.947 0 0 1 0.27 37.834L114.392 833.16a26.947 26.947 0 0 0 0.27 37.834l68.984 68.958a26.947 26.947 0 0 0 38.077 0l291.301-291.3a26.947 26.947 0 0 1 38.104 0l146.324 146.323a26.947 26.947 0 1 1-38.104 38.13L532.076 705.833 259.853 978.055a80.842 80.842 0 0 1-114.337 0L76.53 909.096a80.842 80.842 0 0 1-0.809-113.475l269.043-277.046z m473.546 155.54a26.947 26.947 0 1 1-38.104 38.104L597.288 529.273a26.947 26.947 0 0 1 0-38.103l148.13-148.103a26.947 26.947 0 0 1 15.36-7.653l88.603-12.18 89.627-170.927-56.697-60.39-167.37 97.254-16.546 85.53a26.947 26.947 0 0 1-7.384 13.96l-148.13 148.102a26.947 26.947 0 0 1-38.103 0l-77.474-77.474a26.947 26.947 0 1 1 38.104-38.103l58.422 58.422 123.23-123.23 17.273-89.466a26.947 26.947 0 0 1 12.935-18.19l196.5-114.175a26.947 26.947 0 0 1 33.173 4.85l84.48 90.004a26.947 26.947 0 0 1 4.203 30.963l-104.96 200.165a26.947 26.947 0 0 1-20.21 14.201l-93.346 12.854-122.637 122.637 163.867 163.894z" p-id="2611" fill="#888888"></path><path d="M610.816 784.573a26.947 26.947 0 0 1 38.104-38.104l52.089 52.09a26.947 26.947 0 0 1-38.104 38.103l-52.089-52.09zM368.371 543.42a26.947 26.947 0 1 1 37.995-38.185L705.671 803.22a26.947 26.947 0 0 1 7.814 21.45 111.373 111.373 0 0 0 31.475 87.471 107.79 107.79 0 1 0 68.662-183.727c-2.129 0.135-3.934 0.081-5.578-0.054a26.947 26.947 0 0 1-19.537-7.868L485.24 417.954a26.947 26.947 0 1 1 38.05-38.158l295.181 294.481A161.684 161.684 0 1 1 706.83 950.272a165.16 165.16 0 0 1-47.642-117.275L368.37 543.421z" p-id="2612" fill="#888888"></path><path d="M783.076 874.036a53.895 53.895 0 1 0 76.22-76.219 53.895 53.895 0 1 0-76.22 76.219zM421.807 588.989a26.947 26.947 0 0 1 38.104 38.13L221.723 865.28a26.947 26.947 0 1 1-38.104-38.104L421.807 588.99z m81.597-229.808a26.947 26.947 0 1 1-38.104 38.104l-37.996-37.996a26.947 26.947 0 0 1-5.847-29.345c0.808-1.914 1.05-2.426 3.368-7.06l0.189-0.432c0.754-1.509 1.24-2.506 1.159-2.263a188.632 188.632 0 0 0-43.601-198.818 187.877 187.877 0 0 0-129.698-55.215 189.736 189.736 0 0 0-73.135 13.15l-2.506 0.97-1.752 0.728a26.947 26.947 0 0 1-21.073-49.61c1.887-0.809 1.887-0.809 3.423-1.402l2.102-0.808a242.068 242.068 0 0 1 93.992-16.896 241.772 241.772 0 0 1 166.723 70.98 242.526 242.526 0 0 1 57.722 250.88l25.007 25.033zM25.869 160.013a26.947 26.947 0 0 1 49.61 21.02 187.284 187.284 0 0 0-14.74 65.374 188.039 188.039 0 0 0 55.054 141.743 188.632 188.632 0 0 0 44.463 33.037 26.947 26.947 0 1 1-25.411 47.536 242.526 242.526 0 0 1-57.129-42.47A241.907 241.907 0 0 1 6.9 244.035a243.443 243.443 0 0 1 18.97-84.022z m224.337 337.274a26.947 26.947 0 0 1-0.215-53.895 189.17 189.17 0 0 0 61.79-10.644c4.366-1.51 7.168-2.21 10.94-1.563a26.947 26.947 0 0 1 18.81 7.895l33.145 33.146a26.947 26.947 0 0 1-38.103 38.13l-21.99-22.016a243.308 243.308 0 0 1-64.377 8.947z" p-id="2613" fill="#888888"></path><path d="M148.48 77.824a26.947 26.947 0 1 1 38.104-38.104l161.792 161.82a26.947 26.947 0 0 1 7.087 25.6l-22.986 91.35a26.947 26.947 0 0 1-19.564 19.565L221.56 361.04a26.947 26.947 0 0 1-25.6-7.06L30.343 188.362a26.947 26.947 0 1 1 38.13-38.103L223.26 305.044l60.901-15.306 15.306-60.9L148.48 77.823z" p-id="2614" fill="#888888"></path></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

4
detach/src/main.ts Normal file
View File

@@ -0,0 +1,4 @@
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");

6
detach/src/shims-vue.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

39
detach/tsconfig.json Normal file
View File

@@ -0,0 +1,39 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

13
detach/vue.config.js Normal file
View File

@@ -0,0 +1,13 @@
const path = require("path");
module.exports = {
css: { // 配置css模块
loaderOptions: { // 向预处理器 Loader 传递配置选项
less: { // 配置less其他样式解析用法一致
javascriptEnabled: true, // 设置为true
},
},
},
outputDir: path.join(__dirname, "../public/detach"),
publicPath: process.env.NODE_ENV === "production" ? "" : "/",
};

View File

@@ -21,29 +21,20 @@ module.exports = {
sidebarDepth: 1, // 可选的, 默认值是 1
},
{
title: '开发',
title: '插件开发',
path: '/dev/',
},
{
title: '常见问题',
path: '/qs/',
title: 'API',
path: '/api/',
},
{
title: 'TODO: 原理解析',
children: [
{
title: '插件化实现原理',
path: '/blogs/plugin/',
},
{
title: '超级面板实现原理',
path: '/blogs/superPannel/',
},
]
},
title: '贡献 rubick',
path: '/run/',
}
],
// 假定是 GitHub. 同时也可以是一个完整的 GitLab URL
repo: 'https://github.com/clouDr-f2e/rubick',
repo: 'https://github.com/rubickCenter/rubick',
// 自定义仓库链接文字。默认从 `themeConfig.repo` 中自动推断为
// "GitHub"/"GitLab"/"Bitbucket" 其中之一,或是 "Source"。
repoLabel: 'Github',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 749 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -6,11 +6,11 @@ tagline: 基于 Electron 开源的插件化工具箱
actionText: 快速上手 →
actionLink: /guide/
features:
- title: 开箱即用
details: 支持快速检索系统应用
- title: 自由定制
details: 支持自定义插件开发
- title: 海量示例
details: 新版文档 code Demo & playground 一应俱全
- title: 极其轻量
details: rubick 仅仅包含插件运行所需要的API本身不内置任何插件可以看成是 electron 的二次封装框架。
- title: 更强大的插件能力
details: 不仅仅支持ui插件需要搜索呼起使用还支持系统插件不需要搜索只要rubick在运行系统插件即可用
- title: 更便捷的插件管理
details: rubick 插件全部托管在 npm 仓库rubick 插件的安装、使用、删除就是 npm 包的安装、使用、删除
footer: MIT Licensed | Copyright (c) 2021 muwoo
---

261
docs/docs/api/README.md Normal file
View File

@@ -0,0 +1,261 @@
## 事件
### onPluginReady(callback)、onPluginEnter(callback)
* `callback` Function
`callback` 内会返回一个 `object` 对象,来描述进入当前插件的环境信息:
* `code` String
> plugin.json 配置的 feature.code
* `type` String
> plugin.json 配置的 feature.cmd.type可以为 "text"、"img"、 "files"、 "regex"、 "over"、"window"
* payload String | Object | Array
> feature.cmd.type 对应匹配的数据
当插件装载成功rubick 将会主动调用这个方法, 所有的 `api` 都应该在 `onPluginReady` 之后进行调用。
#### 示例
```js
rubcik.onPluginReady(({ code, type, payload }) => {
console.log('插件装配完成,已准备好')
})
/*
type 为 "files" 时, payload 值示例
[
{
"isFile": true,
"isDirectory": false,
"name": "demo.js",
"path": "C:\\demo.js"
}
]
type 为 "img" 时, payload 值示例
data:image/png;base64,...
type 为 "text"、"regex"、 "over" 时, payload 值为进入插件时的主输入框文本
*/
```
### onPluginOut(callback)
* `callback` Function
每当插件从前台进入到后台时rubick 将会主动调用这个方法。
## 窗口交互
### hideMainWindow()
隐藏主窗口
### showMainWindow()
显示主窗口
### setExpendHeight(height)
执行该方法将会修改插件窗口的高度。
* `height` Integer
* 返回 `Boolean`
#### 示例
```js
rubick.setExpendHeight(100)
```
### setSubInput(onChange, placeholder)
设置插件输入框监听,当进入插件后,用户搜索会触发`onChange` 函数
* `onChange` Function
* `Object`
* `text` String
> 子输入框文本修改时触发
* `placeholder` String (可选)
> 子输入框占位符
* `isFocus` Boolean (可选)
> 子输入框是否获得焦点,默认 true
`返回 Boolean`
#### 示例
```js
rubick.setSubInput(({ text }) => {
console.log(text)
}, '搜索')
```
### setSubInputValue(value)
直接对子输入框的值进行设置。
* `value` String
* `返回` Boolean
#### 示例
```js
rubick.setSubInputValue('rubick')
```
## 系统
### showNotification(body)
显示系统通知
* `body` String
```js
rubick.showNotification('Hi, rubick')
```
### shellOpenPath(fullPath)
打开给定路径的文件
* `fullPath` String
```js
rubick.shellOpenPath('/path/file')
```
### shellOpenExternal(url)
浏览器打开URL
* `url` String
```js
rubick.shellOpenExternal('https://www.baidu.com')
```
### getPath(name)
electron 内置 getPath 能力,详见 [electron API](https://www.electronjs.org/docs/latest/api/app#appgetpathname)
```js
console.log(rubick.getPath('cache'));
```
## 本地数据库
`rubick db` 是基于开源的 [pouchdb](https://github.com/pouchdb/pouchdb) 封装的
### rubick.db.put(doc)
* `doc` Object
* `返回` Object
#### 示例
```js
// 创建请求
rubick.db.put({
_id: "demo",
data: "demo"
})
// 返回 {id: "demo", ok: true, rev: "1-05c9b92e6f24287dc1f4ec79d9a34fa8"}
// 更新请求
rubick.db.put({
_id: "demo",
data: "demo",
_rev: "1-05c9b92e6f24287dc1f4ec79d9a34fa8"
})
```
_id 代表这个文档在数据库中唯一值,如果值不存在,则会创建一个新的文档,如果值已经存在,则会进行更新。你可能已经注意到,返回对象中包含一个 rev
属性,这是代表此文档的版本,每次对文档进行更新时,都要带上最新的版本号,否则更新将失败,版本化的意义在于解决同步时数据冲突。
另外需要注意,每次更新时都要传入完整的文档数据,无法对单个字段进行更新。
### rubick.db.get(id)
执行该方法将会根据文档 ID 获取数据
* `id` String
* `返回` Object
```js
rubick.db.get("demo")
// 返回 {_id: "demo", _rev: "3-9836c5c68af5aef618e17d615882942a", data: "demo"}
```
### rubick.db.remove(doc)
* `doc` String | Object
* `返回` Object 执行该方法将会删除数据库文档,可以传入文档对象或文档 id 进行操作。
```js
rubick.db.remove("demo")
// 返回 {id: "demo", ok: true, rev: "2-effe5dbc23dffc180d8411b23f3108fb"}
```
### rubick.db.bulkDocs(docs)
* `docs` Array
* `返回` Array 执行该方法将会批量更新数据库文档,传入需要更改的文档对象合并成数组进行批量更新。
```js
rubick.db.bulkDocs([{
_id: "demo1",
data: "demo",
_rev: "1-c8817a74e292eda4cba1a45924853af6"
}, {
_id: "demo2",
data: "demo",
_rev: "1-f0399b42cc6123a9cc8503632ba7b3a7"
}])
/* 返回
[{
id: "demo1", ok: true, rev: "2-7857b2801bc0303d2cc0bb82e8afd796"
}, {
id: "demo2", ok: true, rev: "2-7857b2801bc0303d2cc0bb82e8afd796"
}]
*/
```
### rubick.db.allDocs(key)
* `key` String | Array
* `返回` Array 执行该方法将会获取所有数据库文档,如果传入字符串,则会返回以字符串开头的文档,也可以传入指定 ID 的数组,不传入则为获取所有文档。
```js
// 获取所有文档
rubick.db.allDocs()
// 传入字符串则返回id以 demo 开头的文档
rubick.db.allDocs("demo")
/* 返回
[{
_id: "demo/123", _rev: "2-7857b2801bc0303d2cc0bb82e8afd796", data: "demo"
}, {
_id: "demo/124", _rev: "1-f0399b42cc6123a9cc8503632ba7b3a7", data: "demo"
}, {
_id: "demo/125", _rev: "1-f0399b42cc6123a9cc8503632ba7b3a7", data: "demo"
}]
*/
// 根据id数组请求
rubick.db.allDocs([
"demo1",
"demo2"
])
/* 返回
[{
_id: "demo1", _rev: "2-7857b2801bc0303d2cc0bb82e8afd796", data: "demo"
}, {
_id: "demo2", _rev: "1-f0399b42cc6123a9cc8503632ba7b3a7", data: "demo"
}]
*/
```

View File

@@ -1,167 +0,0 @@
## 插件化原理
浏览器是打开不同网页进行浏览就是一个天然的插件,我们在做 `hybird` 混合开发的时候App 内的 H5 页面也是可以类比成一个个
插件。微信小程序在微信环境内运行也可以看做一个插件。他们都有一个共性:**在宿主环境内运行插件页面,需要使用宿主能力时
调用宿主提供的API来完成自身能力的增强**。
所以 electron 也可以看做一个移动端 APP我们通过 `webview` 来加载 `H5` 页面,`H5` 页面调用 `electron App` 内置 API
完成功能增强。所以这就是我们核心的原理思想。
## electron webview 方式
### 1. electron 中使用 webview
```html
<webview src="https://xxx.xx.com/index.html" preload="preload.js" />
```
### 2. 实现 `bridge`
```js
// preload.js
window.rubickBridge = {
sayHello() {
console.log('hello world')
}
}
```
### 3. 插件借助 `bridge` 调用 `electron` 的能力
```html
<html>
<body>
<div>这是一个插件<div>
</body>
<script>
window.rubickBridge.sayHello()
</script>
</html>
```
### 4. 通信
因为 `proload.js``electron``renderer` 进程的,所以如果需要使用部分 `main` 进程的能力,则需要使用通信机制:
```js
// main process
ipcMain.on('msg-trigger', async (event, arg) => {
const window = arg.winId ? BrowserWindow.fromId(arg.winId) : mainWindow
const operators = arg.type.split('.');
let fn = Api;
operators.forEach((op) => {
fn = fn[op];
});
const data = await fn(arg, window);
event.sender.send(`msg-back-${arg.type}`, data);
});
// renderer process
ipcRenderer.send('msg-trigger', {
type: 'getPath',
name,
});
ipcRenderer.on(`msg-back-getPath`, (e, result) => {
console.log(result)
});
```
## 插件加载原理
### rubick 使用插件
首先我们需要实现一个插件,必须要有个 `plugin.json`,这玩意就是用来告诉 `rubick` 插件的信息。
```json
{
"pluginName": "helloWorld",
"description": "我的第一个uTools插件",
"main": "index.html",
"version": "0.0.1",
"logo": "logo.png",
"features": [
{
"code": "hello",
"explain": "hello world",
"cmds":["hello", "你好"]
}
]
}
```
接下来是将写好的插件用 `rubick` 跑起来,复制 `plugin.json``rubick` 搜索框即可,所以当 `rubick` 检测到输入框内执行
`ctrl/command + c` 时,读取剪切板内容,如果剪切板复制的是文件类型的 `plugin.json`,那么就将构造插件的 `pluginConfig` 配置文件,用于后续搜索
时使用。
```js
// 监听 input change
// 读取剪切板内容
const fileUrl = clipboard.read('public.file-url').replace('file://', '');
// 复制文件
if (fileUrl && value === 'plugin.json') {
// 读取 plugin.json 配置
const config = JSON.parse(fs.readFileSync(fileUrl, 'utf-8'));
const pluginConfig = {
...config,
// index.html 文件位置用于webview加载
sourceFile: path.join(fileUrl, `../${config.main || 'index.html'}`),
id: uuidv4(),
type: 'dev',
icon: 'image://' + path.join(fileUrl, `../${config.logo}`),
subType: (() => {
if (config.main) {
return ''
}
return 'template';
})()
};
}
```
实现效果如下:
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b40162dd4c774a3ca6db2aa63c3606eb~tplv-k3u1fbpfcp-watermark.image)
### rubick 内搜索插件原理
接下来就是进行命令搜索插件:
![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/236e9308fa324a3bac266ff7332cd1ab~tplv-k3u1fbpfcp-watermark.image)
实现这个功能其实也就是对之前存储的`pluginConfig`的里面的 `features` 进行遍历,找到相应的 `cmd` 后进行下拉框展示即可。
```js
state.devPlugins.forEach((plugin) => {
// dev 插件未开启
if (plugin.type === 'dev' && !plugin.status) return;
const feature = plugin.features;
feature.forEach((fe) => {
// fe.cmds: 所有插件的命令; value: 当前输入框内搜索的名称
const cmds = searchKeyValues(fe.cmds, value);
options = [
...options,
...cmds.map((cmd) => ({
name: cmd,
value: 'plugin',
icon: plugin.sourceFile ? 'image://' + path.join(plugin.sourceFile, `../${plugin.logo}`) : plugin.logo,
desc: fe.explain,
type: plugin.type,
click: (router) => {
// 跳转到指定插件页面
actions.openPlugin({ commit }, { cmd, plugin, feature: fe, router });
}
}))
];
});
});
```
当点击 input 内插件时,需要跳转到插件 `webview` 加载页面:
```js
// actions.openPlugin
router.push({
path: '/plugin',
query: {
...plugin,
_modify: Date.now(),
detail: JSON.stringify(feature)
}
});
```
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/37cf1909b1374606bdd1fbae657433c7~tplv-k3u1fbpfcp-watermark.image)
本页写的插件demo已上传 [github](https://github.com/clouDr-f2e/rubick-plugin-demo)

View File

@@ -1,175 +0,0 @@
## 前言
超级面板用于增强用户右击能力,实现快速呼起插件的能力,本次实现方式是通过 `robotjs` 以及 `iohook` 一起来完成
### 功能截图:
#### 文件夹下长按右建
<img src=https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9b3d6ea7a25b4b908acc471471628979~tplv-k3u1fbpfcp-watermark.image width=300 />
#### 选择文件后长按右键
<img src=https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8e220166cb464328b432f42d14ba9ab2~tplv-k3u1fbpfcp-watermark.image width=300 />
#### 选择文字后长按右键
<img src=https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f9f909d08a8441e8a506831796908ef9~tplv-k3u1fbpfcp-watermark.image width=300 />
## 实现原理
### 获取选中文案
要实现改功能核心是要读取当前用户选中的文案或者文件,根据当前选择内容进行不同功能展示。但是核心有一个问题是如何来实现获取当前选中的内容。这个问题思考了很久很久,要想获取选中的文案,感觉唯一的办法是使用 `ctrl + c` 或者 `command + c` 来先复制到剪切板,再通过 `electron clipboard` 来获取当前剪切板内容。但是 `utools` 可不是通过先复制再长按这样的操作来实现的,而是直接选中文本或者文件长按后呼起超级面板。**所以一定要在右击长按前获取到当前选中的内容。**
如果要这么干,可能真的无解了,之前就因为这么想,才被无解了。正确的思路应该是先长按再获取选中的内容。别看只是掉了个个,但实现确实天壤之别:
1. 先获取选中内容:这就要求我们必须监听原生系统选中事件,但是 `electron` 并没有提供能力,我们也无法监听系统选择事件。
2. 先右击,后获取内容,这样的好处在于先右击可以通过监听鼠标右击事件,相比选择事件更加容易。
所以思路就有了,先监听长按右击事件:
```js
// macos
const mouseEvents = require("osx-mouse");
const mouseTrack = mouseEvents();
// 按下去的 time
let down_time = 0;
// 是否弹起
let isPress = false;
// 监听右击
mouseTrack.on('right-down', () => {
isPress = true;
down_time = Date.now();
// 长按 500ms 后触发
setTimeout(async () => {
if (isPress) {
// 获取选中内容
const copyResult = await getSelectedText();
}, 500);
})
mouseTrack.on('right-up', () => {
isPress = false;
});
```
接下来一步就是要去实现获取选中内容,要获取选中内容有个比较骚的操作,就是:
1. 通过 `clipboard` 先获取当前剪切板内容,并存下 A
2. 通过 `robot.js` 来调用系统 `command + c` 或者 `ctrl + c`
3. 再通过 `clipboard` 先获取当前剪切板内容,并存下 B
4. 再将 A 写到剪切板中,返回 B
先存剪切板内容的目的在于我们是偷偷帮用户执行了复制动作,当读取完用户选择内容后,需要回复用户之前的剪切板内容。接下来看一下简单的实现:
```js
const getSelected = () => {
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
})
}, 300);
})
}
```
### 通知超级面板窗口当前选中内容
当获取到了选中内容后,接下来就是需要创建超级面板的 `BrowserWindow`:
```js
const { BrowserWindow, ipcMain, app } = require("electron");
module.exports = () => {
let win;
let init = (mainWindow) => {
if (win === null || win === undefined) {
createWindow();
}
};
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;
});
};
let getWindow = () => win;
return {
init: init,
getWindow: getWindow,
};
};
```
然后再通知 `superPanel` 进行内容展示:
```js
win.webContents.send('trigger-super-panel', {
...copyResult,
optionPlugin: optionPlugin.plugins,
});
```
### 超级面板点击操作
接下来要实现超级面板点击操作,这块也是比较简单的了,直接上代码好了:
#### 1. 打开 Terminal
```js
const { spawn } = require ('child_process');
spawn('open', [ '-a', 'Terminal', fileUrl ]);
```
#### 2. 新建文件
```js
remote.dialog.showSaveDialog({
title: "请选择要保存的文件名",
buttonLabel: "保存",
defaultPath: fileUrl.replace('file://', ''),
showsTagField: false,
nameFieldLabel: '',
}).then(result => {
fs.writeFileSync(result.filePath, '');
});
```
#### 3. 复制路径
```js
clipboard.writeText(fileUrl.replace('file://', ''))
```

View File

@@ -1,46 +1,61 @@
## 开发一个最基础的插件
## 插件说明
`rubick` 插件分为 `UI插件``系统插件` 2类。下面分别介绍这 2 类插件的区别和作用。
1. UI 插件 <Badge type="tip" text="最常用" />:都会有 UI 界面,用于和用户交互,且需要通过关键词搜索选择进行使用,比如 `斗图` 插件,有界面展示,且需要再搜索框内搜索关键词选择后进行呼起才能使用。
2. 系统插件:可能不会有 UI 界面,在 `rubick` 启动的时候,会注册执行系统插件。比如`超级面板` 插件,安装完成后,即可在 `rubick` 运行时随时使用,不需要任何关键词和匹配。
## 开发 UI 插件
一个最基础插件的目录是这样的:
```
rubick-plugin-demo
|-- index.html
|-- logo.png
|-- plugin.json
|-- package.json
|-- preload.js
```
## 文件说明
### plugin.json
### 文件说明
#### package.json
用于指定插件最基础的配置,一个最基础的配置信息如下:
```json
{
"pluginName": "测试插件",
"name": "rubick-ui-plugin-demo",
"pluginName": "插件demo",
"description": "rubick ui 插件demo",
"author": "muwoo",
"description": "我的第一个 rubick 插件",
"main": "index.html",
"version": "0.0.2",
"logo": "logo.png",
"name": "rubick-plugin-demo",
"logo": "https://www.img/demo.png",
"version": "0.0.1",
"preload":"preload.js",
"homePage": "https://gitee.com/rubick-center/rubick-ui-plugin-demo/raw/master/README.md",
"pluginType": "ui",
"features": [
{
"code": "hello",
"explain": "这是一个测试插件",
"cmds":["hello", "你好"]
"code": "index",
"explain": "测试插件",
"cmds":[
"demo",
"测试"
]
}
],
"preload": "preload.js"
]
}
```
核心字段说明:
* name 插件仓库名称需要保持和git仓库同名不要随意变更
* pluginName 插件显示名称,用于展示给使用者
* description 插件描述,描述这个插件的作用
* main 入口文件,一般为 `index.html`
* version 插件的版本
* features 插件核心功能列表
* features.code 插件某个功能的识别码,可用于区分不同的功能
* features.cmds 输入框内搜索该 cmd 进入插件
* name 插件 `npm` 包名称,<Badge type="tip" text="必填" />
* pluginName 插件显示名称,用于展示给使用者 <Badge type="tip" text="必填" />
* description 插件描述,描述这个插件的作用 <Badge type="tip" text="必填" />
* author 插件作者
* main 入口文件,一般为 `index.html`
* logo 尺寸建议 200 * 200, 插件的 logo, 需要是 http/https 在线地址不支持本地logo <Badge type="tip" text="必填" />
* version 插件的版本 <Badge type="tip" text="必填" />
* preload 预加载脚本
* homePage: 插件 readme raw 地址
* pluginType: 插件类型枚举ui, system. 当前选 ui <Badge type="tip" text="必填" />
* features 插件核心功能列表 <Badge type="tip" text="必填" />
* features.code 插件某个功能的识别码,可用于区分不同的功能 <Badge type="tip" text="必填" />
* features.explain 插件某个功能的解释 <Badge type="tip" text="必填" />
* features.cmds 输入框内搜索该 cmd 进入插件 <Badge type="tip" text="必填" />
### index.html
#### index.html
插件的入口文件,用于展示插件的样式,一个最基础的 `html` 结构可以是这样:
```html
<!DOCTYPE html>
@@ -57,25 +72,105 @@ rubick-plugin-demo
</html>
```
### preload.js
#### preload.js
细心的同学可能已经注意到上面的 `index.html` 使用了一个全局函数 `showNotification` 那么这个函数是在哪里定义的呢?
答案就是在 `preload.js` 里面。我们知道 `electron` 是可以再渲染进程中执行 `node.js` 的,所以 `preload.js` 是既可以
执行 `node.js` 以及执行 `Rubick` 提供的系统命令的位置:
答案就是在 `preload.js` 里面。`preload.js` 可以为页面提供全局函数
```js
window.showNotification = function () {
rubick.showNotification('HI, rubick')
}
```
rubick 更多支持 API 能力参考:[rubick 全局API](https://github.com/clouDr-f2e/rubick/blob/master/static/preload.js#L49)
rubick 更多支持 API 能力参考:[rubick 全局API](https://github.com/rubickCenter/rubick/blob/master/static/preload.js#L49)
### logo.png
当前插件的logo图标建议是 200 x 200 方形图标
### 测试写好的插件
由于 `rubick` 插件是基于 `npm` 的管理方式,所以开发者调试插件,也是基于 `npm` 的软连接的方式进行调试。
首先需要再插件 `package.json` 目录下执行:
```shell
$ npm link
```
然后将插件通过插件市场的 `开发者` 菜单进行安装,填写插件的 `name` 即可,如果插件需要调试,可以通过右上角 ... 来打开开发者工具进行调试,页面变更直接刷新即可:
## 测试插件
复制 `plugin.json` 文件,在 `rubick` 主窗口执行 `ctrl/command + v` 即可唤起安装插件的功能,选择`新建rubick插件`,进入插件主界面,
开启插件后,在插件主窗口即可通过命令打开插件:
![](https://pica.zhimg.com/80/v2-d7d6d5cba1151527aeff8e2c9b8cefb4_720w.gif)
![](/rubick/images/5.gif)
本小节所有代码:[rubcik-plugin-demo](https://github.com/clouDr-f2e/rubick-plugin-demo)
本小节所有代码:[rubick-ui-plugin-demo](https://gitee.com/rubick-center/rubick-ui-plugin-demo)
## 开发系统插件
一个最基础插件的目录是这样的:
```
rubick-system-plugin-demo
|-- package.json
|-- index.js
```
### 文件说明
#### package.json
用于指定插件最基础的配置,一个最基础的配置信息如下:
```json
{
"name": "rubick-system-plugin-demo",
"pluginName": "rubick 系统插件demo",
"version": "0.0.0",
"description": "rubick 系统插件demo",
"entry": "index.js",
"logo": "httpss://static.91jkys.com/upload/202112/08/5bac90649c5343cabb63930b131cf8e6.png",
"pluginType": "system",
"author": "muwoo",
"homepage": ""
}
```
核心字段说明:
* name 插件 `npm` 包名称,<Badge type="tip" text="必填" />
* pluginName 插件显示名称,用于展示给使用者 <Badge type="tip" text="必填" />
* description 插件描述,描述这个插件的作用 <Badge type="tip" text="必填" />
* author 插件作者
* entry 入口文件,一般为 `index.js`
* logo 尺寸建议 200 * 200, 插件的 logo, 需要是 http/https 在线地址不支持本地logo <Badge type="tip" text="必填" />
* version 插件的版本 <Badge type="tip" text="必填" />
* homePage: 插件 readme raw 地址
* pluginType: 插件类型枚举ui, system. 当前选 system <Badge type="tip" text="必填" />
#### index.js
插件的入口文件,用于 rubick 主进程进行加载执行:
```js
module.exports = () => {
return {
onReady(ctx) {
const { Notification } = ctx;
new Notification({
title: "测试系统插件",
body: "这是一个系统插件在rubick运行时立即被加载"
}).show()
}
}
}
```
`index.js` 需要返回一个包含 `onReady` 生命周期的函数,该函数接受 `ctx` 对象作为参数,我们可以通过 `ctx` 使用 `electron` 主进程所有能力。
同时也为 `ctx` 上扩展挂在了 `mainWindow` 对象。
#### 调试插件
由于 `rubick` 插件是基于 `npm` 的管理方式,所以开发者调试插件,也是基于 `npm` 的软连接的方式进行调试。
首先需要再插件 `package.json` 目录下执行:
```shell
$ npm link
```
然后将插件通过插件市场的 `开发者` 菜单进行安装,填写插件的 `name` 即可。由于插件依赖于主进程启动执行,所以安装完成后需要重启 rubick 后才能生效。
![](https://pic3.zhimg.com/80/v2-e218500a0686a8735d80f417aa53b7aa_720w.gif)
::: danger
系统插件目前无法直接通过 `devtools` 进行调试,后面会进行优化
:::
## 发布插件
这里介绍完了如何开发插件,最后非常欢迎为 `rubick` 贡献开源插件,发布插件也非常简单,首先需要把自己的插件发布到 `npm` 仓库:
```shell
$ npm publish
```
然后再给 [rubick-database/plugins/total-plugins.json](https://gitee.com/monkeyWang/rubick-database/blob/master/plugins/total-plugins.json) 仓库提个 `pull request`, 把你的 `package.json` 信息加入 `json` 文件内,等我们 merge 了您的提交,插件将会自动上架。

View File

@@ -1,9 +1,27 @@
## 下载 rubick
[rubick 下载安装地址](https://github.com/clouDr-f2e/rubick/releases)
## 赞助
`rubick` 是非盈利项目,开源不容易,如果该项目对你有用的话,可以打赏我们喝杯 coffee ☕️.
<img width=200 src=https://pic1.zhimg.com/80/v2-688385687a37e962fe32daf136139feb_720w.png />
<img width=200 src=https://pica.zhimg.com/80/v2-1ba296fd2cece45ee1094ee7c259035c_720w.png />
## 前言
rubick 之前的插件管理,依托于云服务器存储,我们需要为服务器存储支付一笔不小的开销。
由于项目完全开源,所以几乎无任何收入,所以为了让 rubick 先生存下去,我们再三抉择把插件包管理方式托管到了`npm` 上。
由于 rubick 的插件管理体系是基于 npm 的包管理体系,所以当您需要使用插件的时候,需要手动保证当前电脑已经安装好了`node`环境。
如果当前电脑已经安装过 `node`,那么您可以直接下载 `rubick` 进行使用啦!
[macos 下安装 nodejs 方法](https://juejin.cn/post/6844903886541553672)
[windows 下安装 nodejs 方法](https://juejin.cn/post/6892790243687137287)
## 下载 rubick
[rubick 下载安装地址](https://github.com/rubickCenter/rubick/releases)
macos 选择 `pkg` 文件windows 选择 `exe` 文件。
安装完成后打开 rubick 即可看到主搜索界面:
![](/rubick/images/1.png)
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/26f0fbe2c69246b6a3ed139b0df1ca0b~tplv-k3u1fbpfcp-watermark.image)
目前支持 windows 和 macos。linux 小伙伴正在开发中
@@ -11,46 +29,48 @@
接下来详细介绍 rubick 所包含和支持的功能
### 1. 搜索系统应用
`macos` 下支持搜索当前电脑内所安装的所有 app 和一些偏好设置,目前可搜索路径为
```json
[
"/System/Applications",
"/Applications",
"/System/Library/PreferencePanes"
]
```
也就是说只要当前系统软件安装到这些目录才会被检索到。支持中文搜索和拼音、拼音首字母搜索:
支持拼音和缩写来搜索系统安装应用
![](/rubick/images/2.gif)
![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba363e8f60f540e6a5c365c4317c4413~tplv-k3u1fbpfcp-watermark.image)
`Windows` 内由于安装目录太多不确定,有的在 C盘有的在D盘还有的在自定义其他位置所以该功能还在设计中如果您有好的方案也欢迎提供[issues](https://github.com/clouDr-f2e/rubick/issues)
### 2. rubick 内置功能
`rubick` 参考了钉钉、微信等 App 的基础功能,也设计内置了 `截图``取色``锁屏` 基础功能,通过搜索框输入对应关键词呼起。
#### 截屏
输入:`'截屏'` 或者 `'shortCut'` 或者 `'jp'`
#### 取色
输入:`'取色'` 或者 `'拾色'` 或者 `'Pick color'``'qs'``'ss'`
#### 锁屏
输入:`'锁屏'` 或者 `'lock screen'` 或者 `'sp'`
### 3. 使用插件
点击搜索框右侧 rubick 图标,进入插件市场,选择所需插件,点击下载按钮即可下载,下载完成后在已安装 tab 下可以找到安装插件。
### 2. UI类插件安装
点击搜索框右侧 `rubick` 图标,进入插件市场,选择所需插件,点击下载按钮即可下载,下载完成后在已安装 tab 下可以找到安装插件。
安装完成后,输入插件呼起命令即可使用对应插件:
![](/rubick/images/3.gif)
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7ae45c7ede1f4e3bb7d35ae845e60b64~tplv-k3u1fbpfcp-watermark.image)
### 4. 右击增强
通常我们需要使用鼠标右击来对桌面属性进行拓展,`Rubick` 支持对右击属性进行增强功能,长按鼠标右键即可呼起。如果安装的插件支持
特殊类型的文件操作,还可以在右键中唤起插件:
### 3. 系统类插件安装
系统插件安装方式和UI类一样在插件市场选择`系统分类`,寻找适合自己的系统插件安装即可。
::: danger
系统插件安装成功后,需要重启 `rubick` 才能生效
:::
### 4. 输入框聚焦自动根据剪切板内容匹配插件
`rubick` 内搜索`偏好设置`,然后开启`自动粘贴` 功能,即可匹配剪切板内容自动匹配适合插件进行使用。
![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01ef50fbfa064ba9a88bebe1531eacd4~tplv-k3u1fbpfcp-watermark.image)
### 内网部署
::: tip
如果把插件发布到公网 `npm` 如果不符合您的公司安全要求,`rubick` 支持内网私有源和私有插件库,如果您需要内网部署使用,可以自行配置以下规则。
:::
`rubick` 依赖 `npm` 仓库做插件管理,依赖 `gitee` 做插件数据存储所以如果要进行内网部署主要需要替换这2个设置。详细设置
`插件市场 -> 设置 -> 内网部署设置`
![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1319b177fb544017ae10b4a703e8efa6~tplv-k3u1fbpfcp-watermark.image?)
#### 1. 替换 npm 源
插件发布到私有 `npm` 源即可。
#### 2. 替换 `gitee` 源为内网 `gitlab`: database url
* clone 下载 rubick 插件库:[https://gitee.com/monkeyWang/rubick-database](https://gitee.com/monkeyWang/rubick-database)
* 提交仓库到私有 `gitlab` 库。
替换格式:`https://gitlab.xxx.com/api/v4/projects/{projectId}/repository/files/` 。因为接口为 `gitlab openAPI`,所以需要填写仓库 `access_token`
![](/rubick/images/4.gif)
### 更多功能
如果您还需要更多功能,欢迎来这里给我们提建议:[issues](https://github.com/clouDr-f2e/rubick/issues/20)
如果您还需要更多功能,欢迎来这里给我们提建议:[issues](https://github.com/rubickCenter/rubick/issues)
有价值的想法我们会加入到后期的开发当中。同时也欢迎一起加入共建。

View File

@@ -1,99 +0,0 @@
## windows 本地启动项目
**windows 项目地址目前在 `feat-win` 分支,需要切换到 `feat-win`。**
如果您是在 `windows` 系统下使用本项目进行启动的话,如果出现了一些跑不起的情况,下面的一些经验或许可以帮助您。
### 安装依赖
项目是基于 `electron-vue` 的,所以需要本地已经安装好 `nodejs`。当 `clone` 好项目到本地的时候,需要对项目依赖
进行手动安装:
```bash
$ cd rubick
$ npm install
```
由于本项目依赖 [iohook](https://wilix-team.github.io/iohook/) 和 [robotjs](http://robotjs.io/)
而这2个项目在安装时依赖 node gyp 的编译,所以你可能会遇到一些环境问题或者网络问题。
### iohook 安装
首先,我们先来安装 `iohook`,按照 `iohook` 的文档所示,安装前需要先确定当前 `electron` 的版本以及 `abi` 的版本,
现在我们使用的 `electron` 版本是 `v11.0.2` 所以只需要在 `package,json` 中加上以下配置,指定安装特点的`.node` 文件
```json
{
"iohook": {
"targets": [
"node-83",
"electron-85"
],
"platforms": [
"win32"
],
"arches": [
"x64",
"ia32"
]
}
}
```
接下来执行安装操作:
```shell
$ npm i iohook
```
如果此时一直停留在 `node install` 进度上,说明可能需要翻墙,这里就不介绍如何翻墙了。此时虽然 `.node` 文件没有下载成功,但是 `iohook` 源文件
已经安装成功,如果因为翻墙问题导致的安装失败,我们解决好翻墙问题后,可以接着运行一下:
```shell
$ npm run rebuild_win
```
到这里 `iohook` 应该就安装好了
### 安装 robotjs
和 iohook 不同, `robotjs` 需要 `node gyp` 重新编译 `C++`。所以第一步是先安装源文件:
```shell
npm i robotjs@git+https://github.com/Toinane/robotjs.git
```
此时如果报错,大多还是因为墙的问题。不管包不报错,我们接下来都需要执行一下下面的编译操作:
```shell
npm run rebuild_win
```
此时,可能会出现下这个错:
```text
gyp ERR! find VS msvs_version not set from command line or npm config
// ...
```
google 了一圈,大多数是说缺少 `visual studio`。需要安装,所以可以执行这个命令:
```shell
$ npm install --global --production windows-build-tools
```
到这里会进入正常的安装流程,理论上会一部到底,如果你卡在了 `Successfully installed Python 2.7` 不动了
可以去一下 `C:\Users\you username\.windows-build-tools` 找一下看看应该会有一个 `vs_BuildTools` 文件
双击后,会出现以下弹窗:
![](/rubick/images/6.png)
点击启动,如果一切正常那么可以方向关掉你的命令行了,此时 vs 已经安装成功,接下来要配置一下编译工具:
```shell
$ npm config set python python3.9
$ npm config set msvs_version 2017
```
到这里终于完事了,再执行一下 `npm run rebuild_win` 此时已经安装成功!
## macos
macos 下安装就简单不少了,首先先解决翻墙的问题,然后:
```shell
$ npm i
```
最后执行
```shell
$npm run rebuild
```
## 最后
如果您在启动过程中还有其他的问题,欢迎随时给我们反馈,我们会在第一时间回复:[issues](https://github.com/clouDr-f2e/rubick/issues/20)

105
docs/docs/run/README.md Normal file
View File

@@ -0,0 +1,105 @@
## 赞助
`rubick` 是非盈利项目,开源不容易,如果该项目对你有用的话,可以打赏我们喝杯 coffee ☕️.
<img width=200 src=https://pic1.zhimg.com/80/v2-688385687a37e962fe32daf136139feb_720w.png />
<img width=200 src=https://pica.zhimg.com/80/v2-1ba296fd2cece45ee1094ee7c259035c_720w.png />
## 贡献代码
### rubick 目录介绍
```shell
.
├── docs # 文档存方目录
│   ├── docs
│   ├── package-lock.json
│   ├── package.json
│   └── pnpm-lock.yaml
├── feature # 插件市场插件
│   ├── README.md
│   ├── babel.config.js
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   ├── src
│   ├── tsconfig.json
│   └── vue.config.js
├── public # rubick __static 目录
│   ├── favicon.ico
│   ├── feature
│   ├── icons
│   ├── index.html
│   ├── preload.js
│   └── tpl
├── src # rubick 核心源码
│   ├── common # 一些通用的函数
│   ├── core # 一些核心的能力,比如 app search
│   ├── main # 主进程
│   └── renderer # 渲染进程
├── tpl # rubick 模板插件
│   ├── README.md
│   ├── babel.config.js
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   ├── src
│   ├── tsconfig.json
│   └── vue.config.js
├── LICENSE # MIT 协议
├── README.md # 英文文档
├── README.zh-CN.md # 中文文档
├── babel.config.js
├── deploy-doc.sh # rubick doc 发布脚本
├── tsconfig.json
├── package-lock.json
├── package.json
└── vue.config.js
```
### 启动
#### 1. 安装依赖
`rubick` 启动主要涉及到3个目录
1. 根目录:`rubick` 核心进程
2. feature`rubick` 内置的插件市场插件
3. tpl: `rubick` 内置的模板插件
```shell
$ npm i
$ cd feature && npm i
$ cd tpl && npm i
```
#### 2. 启动核心进程
```shell
$ npm run electron:serve
```
#### 3. 启动插件中心 <Badge type="warning" text="非必须" vertical="top" />
```shell
$ cd feature && npm run serve
```
#### 4. 启动模板插件 <Badge type="warning" text="非必须" vertical="top" />
```shell
$ cd tpl && npm run serve
```
### 编译
```shell
$ cd feature && npm run build
$ cd tpl && npm run build
$ npm run electron:build
```
### PR
1. Create an issue about the features, such as new components.
2. Fork the repo to your own account.
3. Clone your fork.
4. Create a new branch base on dev, if you want to add new component, the branch name should be formatted as component-[Component Name]. (e.g. component-steps) And the commit info should be formatted as [Component Name]: Info about commit.
5. Make sure that running npm run prepublish outputs the correct files.
6. Rebase before creating a PR to keep commit history clear. (Merge request to branch dev)
7. Provide some description about your PR.

10220
docs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

23
feature/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
feature/README.md Normal file
View File

@@ -0,0 +1,24 @@
# feature
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

3
feature/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
};

13405
feature/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

62
feature/package.json Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "feature",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@ant-design/icons-vue": "^6.0.1",
"ant-design-vue": "^2.2.8",
"axios": "^0.24.0",
"core-js": "^3.6.5",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"markdown-it": "^12.2.0",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.0.0",
"prettier": "^2.2.1",
"typescript": "~4.1.5"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/prettier/@typescript-eslint"
],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
feature/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

17
feature/public/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -0,0 +1,31 @@
{
"name": "rubick-system-feature",
"pluginName": "rubick 系统菜单",
"description": "rubick 系统菜单",
"main": "index.html",
"logo": "https://static.91jkys.com/upload/202112/08/8a1abbb051bf4b05bbc9bbf66ade63f2.png",
"version": "0.0.0",
"preload":"preload.js",
"pluginType": "ui",
"features": [
{
"code": "market",
"explain": "rubick 插件市场",
"cmds":[
"插件市场"
]
},{
"code": "installed",
"explain": "rubick 已安装插件",
"cmds":[
"已安装插件"
]
},{
"code": "settings",
"explain": "rubick 偏好设置",
"cmds":[
"偏好设置"
]
}
]
}

16
feature/public/preload.js Normal file
View File

@@ -0,0 +1,16 @@
const {remote} = require("electron");
window.market = {
getLocalPlugins() {
return remote.getGlobal("LOCAL_PLUGINS").getLocalPlugins();
},
downloadPlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").downloadPlugin(plugin);
},
deletePlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").deletePlugin(plugin);
},
refreshPlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").refreshPlugin(plugin);
},
};

84
feature/src/App.vue Normal file
View File

@@ -0,0 +1,84 @@
<template>
<div class="main-container">
<div class="slider-bar">
<a-menu v-model:selectedKeys="active" mode="horizontal" @select="({key}) => changeMenu(key)">
<a-menu-item key="market">
<template #icon>
<AppstoreOutlined />
</template>
插件市场
</a-menu-item>
<a-menu-item key="installed">
<template #icon>
<HeartOutlined />
</template>
已安装
</a-menu-item>
<a-menu-item key="settings">
<template #icon>
<SettingOutlined />
</template>
设置
</a-menu-item>
<a-menu-item key="account">
<template #icon>
<UserOutlined />
</template>
账户
</a-menu-item>
<a-menu-item key="dev">
<template #icon>
<BugOutlined />
</template>
开发者
</a-menu-item>
</a-menu>
</div>
<router-view />
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useRouter } from "vue-router";
import {
HeartOutlined,
UserOutlined,
AppstoreOutlined,
SettingOutlined,
BugOutlined,
} from "@ant-design/icons-vue";
import { useStore } from "vuex";
const router = useRouter();
const active = ref(["market"]);
const changeMenu = (key: any) => {
router.push(key);
};
window.rubick.onPluginEnter(({ code }: { code: string }) => {
changeMenu(code);
active.value = [code];
});
const store = useStore();
const init = () => store.dispatch("init");
init();
</script>
<style lang="less">
* {
margin: 0;
padding: 0;
}
.main-container {
display: flex;
align-items: flex-start;
background: #F2EFEF;
flex-direction: column;
.slider-bar {
width: 100%;
}
}
</style>

View File

@@ -3,8 +3,3 @@
@primary-color: #ff4ea4; // 全局主色
@link-color: #ff4ea4; // 链接色
@error-color: #ff4ea4; // 错误色
.ant-tag-green {
color: #ff4ea4;
background: rgba(255, 159, 180, 0.3);
border-color: #ff9fb4;
}

BIN
feature/src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,75 @@
import axios from "axios";
let baseURL = "https://gitee.com/monkeyWang/rubick-database/raw/master";
let access_token = "";
try {
const dbdata = window.rubick.db.get("rubick-localhost-config");
baseURL = dbdata.data.database;
access_token = dbdata.data.access_token;
} catch (e) {
// ignore
}
const instance = axios.create({
baseURL: baseURL || "https://gitee.com/monkeyWang/rubick-database/raw/master",
});
export default {
async getTotalPlugins() {
let targetPath = "plugins/total-plugins.json";
if (access_token) {
targetPath = `${encodeURIComponent(targetPath)}/raw?access_token=${access_token}&ref=master`
}
const res = await instance.get(targetPath);
return res.data;
},
async getFinderDetail() {
let targetPath = "plugins/finder.json";
if (access_token) {
targetPath = `${encodeURIComponent(targetPath)}/raw?access_token=${access_token}&ref=master`
}
const res = await instance.get(targetPath);
return res.data;
},
async getSystemDetail() {
let targetPath = "/plugins/system.json";
if (access_token) {
targetPath = `${encodeURIComponent(targetPath)}/raw?access_token=${access_token}&ref=master`
}
const res = await instance.get(targetPath);
return res.data;
},
async getWorkerDetail() {
let targetPath = "/plugins/worker.json";
if (access_token) {
targetPath = `${encodeURIComponent(targetPath)}/raw?access_token=${access_token}&ref=master`
}
const res = await instance.get(targetPath);
return res.data;
},
async getPluginDetail(url: string) {
const res = await axios.get(url);
return res.data;
},
async getSearchDetail() {
let targetPath = "/plugins/search.json";
if (access_token) {
targetPath = `${encodeURIComponent(targetPath)}/raw?access_token=${access_token}&ref=master`
}
const res = await instance.get(targetPath);
return res.data;
},
async getDevDetail() {
let targetPath = "/plugins/dev.json";
if (access_token) {
targetPath = `${encodeURIComponent(targetPath)}/raw?access_token=${access_token}&ref=master`
}
const res = await instance.get(targetPath);
return res.data;
},
};

8
feature/src/main.ts Normal file
View File

@@ -0,0 +1,8 @@
import { createApp } from "vue";
import Antd from "ant-design-vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "./assets/ant-reset.less";
createApp(App).use(store).use(Antd).use(router).mount("#app");

View File

@@ -0,0 +1,46 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import Market from "../views/market/index.vue";
import Installed from "../views/installed/index.vue";
import Account from "../views/account/index.vue";
import Settings from "../views/settings/index.vue";
import Dev from "../views/dev/index.vue";
const routes: Array<RouteRecordRaw> = [
{
path: "/market",
name: "market",
component: Market,
},
{
path: "/installed",
name: "installed",
component: Installed,
},
{
path: "/account",
name: "account",
component: Account,
},
{
path: "/settings",
name: "settings",
component: Settings,
},
{
path: "/dev",
name: "dev",
component: Dev,
},
{
path: "/:catchAll(.*)",
name: "market",
component: Market,
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;

12
feature/src/shims-vue.d.ts vendored Normal file
View File

@@ -0,0 +1,12 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'axios'
interface Window {
rubick: any
}

View File

@@ -0,0 +1,81 @@
import { createStore } from "vuex";
import request from "@/assets/request";
const isDownload = (item: any, targets: any[]) => {
let isDownload = false;
targets.some((plugin) => {
if (plugin.name === item.name) {
isDownload = true;
}
return isDownload;
});
return isDownload;
};
export default createStore({
state: {
totalPlugins: [],
localPlugins: [],
},
mutations: {
commonUpdate(state: any, payload) {
Object.keys(payload).forEach((key) => {
state[key] = payload[key];
});
},
},
actions: {
async init({ commit }) {
const totalPlugins = await request.getTotalPlugins();
const localPlugins = (window as any).market.getLocalPlugins();
totalPlugins.forEach(
(origin: { isdwonload?: any; name?: any; isloading: boolean }) => {
origin.isdwonload = isDownload(origin, localPlugins);
origin.isloading = false;
}
);
commit("commonUpdate", {
localPlugins,
totalPlugins,
});
},
startDownload({ commit, state }, name) {
const totalPlugins = JSON.parse(JSON.stringify(state.totalPlugins));
totalPlugins.forEach(
(origin: { isdwonload?: any; name?: any; isloading: boolean }) => {
if (origin.name === name) {
origin.isloading = true;
}
}
);
commit("commonUpdate", {
totalPlugins,
});
},
successDownload({ commit, state }, name) {
const totalPlugins = JSON.parse(JSON.stringify(state.totalPlugins));
totalPlugins.forEach(
(origin: { isdwonload?: any; name?: any; isloading: boolean }) => {
if (origin.name === name) {
origin.isloading = false;
origin.isdwonload = true;
}
}
);
const localPlugins = (window as any).market.getLocalPlugins();
commit("commonUpdate", {
totalPlugins,
localPlugins,
});
},
updateLocalPlugin({ commit }) {
const localPlugins = (window as any).market.getLocalPlugins();
commit("commonUpdate", {
localPlugins,
});
},
},
modules: {},
});

View File

@@ -0,0 +1,21 @@
<template>
<div class="account">
<a-result status="404" title="玩命开发中" sub-title="个人中心正在开发中敬请期待...">
</a-result>
</div>
</template>
<script>
export default {
};
</script>
<style lang="less">
.account {
box-sizing: border-box;
width: 100%;
overflow-x: hidden;
background: #f3efef;
height: calc(~"100vh - 46px");
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<div class="dev">
<a-alert style="margin-bottom: 40px;" message="rubick 插件系统依托于 npm 管理,本地调试需要先在本地插件当前目录执行 npm link" type="warning" />
<a-form
ref="formRef"
:model="formState"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="插件名称" name="name">
<a-input v-model:value="formState.name" />
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button :loading="loading" type="primary" @click="onSubmit">安装</a-button>
<a-button @click="refresh" style="margin-left: 10px;">刷新插件</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import { message } from "ant-design-vue";
const formRef = ref();
const formState = reactive({
name: undefined,
});
const rules = {
name: {
required: true,
message: "Please input name",
},
};
const onSubmit = () => {
formRef.value.validate().then(() => {
downloadPlugin(formState.name);
});
};
const loading = ref(false);
const downloadPlugin = async (pluginName) => {
loading.value = true;
await window.market.downloadPlugin({
name: pluginName,
isDev: true,
});
message.success(`${pluginName}安装成功!`);
loading.value = false;
};
const refresh = () => {
formRef.value.validate().then(() => {
window.market.refreshPlugin({
name: formState.name,
});
message.success(`${formState.name}刷新成功!`);
});
};
const labelCol = { span: 4 };
const wrapperCol = { span: 14 };
</script>
<style lang="less">
.dev {
box-sizing: border-box;
width: 100%;
overflow-x: hidden;
background: #fff;
height: calc(~"100vh - 46px");
padding: 20px;
}
</style>

View File

@@ -0,0 +1,212 @@
<template>
<div class="installed">
<div v-if="!localPlugins.length">
<a-result status="404" title="暂无任何插件" sub-title="去插件市场选择安装合适的插件吧" />
</div>
<div class="container" v-else>
<div class="installed-list">
<div
:class="currentSelect[0] === index ? 'item active' : 'item'"
:key="index"
@click="currentSelect = [index]"
v-for="(plugin, index) in localPlugins"
>
<img :src="plugin.logo" />
<div class="info">
<div class="title">
{{ plugin.pluginName }}
<span class="desc">v{{ plugin.version }}</span>
</div>
<div class="desc">{{ plugin.description }}</div>
</div>
</div>
</div>
<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="deletePlugin(pluginDetail)"
>移除</a-button
>
</div>
</div>
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="功能关键字">
<div class="feature-container">
<div
class="desc-item"
:key="index"
v-for="(item, index) in pluginDetail.features"
>
<div>{{ item.explain }}</div>
<a-tag
:key="cmd"
@click="
openPlugin({
cmd,
plugin: pluginDetail,
feature: item,
router: $router,
})
"
v-for="cmd in item.cmds"
>
{{ cmd }}
</a-tag>
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="详情介绍">
<div class="detail-container" v-html="readme"></div>
</a-tab-pane>
</a-tabs>
</div>
</div>
</div>
</template>
<script setup>
import { useStore } from "vuex";
import { computed, ref } from "vue";
import path from "path";
import MarkdownIt from "markdown-it";
const { remote } = window.require("electron");
const fs = window.require("fs");
const md = new MarkdownIt();
const appPath = remote.app.getPath("cache");
const baseDir = path.join(appPath, "./rubick-plugins");
const store = useStore();
const localPlugins = computed(() =>
store.state.localPlugins.filter(
(plugin) => plugin.name !== "rubick-system-feature"
)
);
const updateLocalPlugin = () => store.dispatch("updateLocalPlugin");
const currentSelect = ref([0]);
const pluginDetail = computed(() => {
return localPlugins.value[currentSelect.value] || {};
});
const readme = computed(() => {
if (!pluginDetail.value.name) return "";
const readmePath = path.resolve(
baseDir,
"node_modules",
pluginDetail.value.name,
"readme.md"
);
if (fs.existsSync(readmePath)) {
const str = fs.readFileSync(readmePath, "utf-8");
return md.render(str);
}
return "";
});
const deletePlugin = async (plugin) => {
await window.market.deletePlugin(plugin);
updateLocalPlugin();
};
</script>
<style lang="less">
.installed {
box-sizing: border-box;
width: 100%;
overflow: hidden;
background: #f3efef;
height: calc(~"100vh - 46px");
.container {
box-sizing: border-box;
width: 100%;
overflow: hidden;
background: #f3efef;
height: 100%;
display: flex;
}
.installed-list {
width: 40%;
background: #fff;
height: 100%;
padding: 10px 0;
border-right: 1px solid #eee;
.item {
padding: 10px 20px;
display: flex;
align-items: center;
img {
width: 40px;
height: 40px;
margin-right: 20px;
}
.desc {
color: #999;
}
&.active {
background: #eee;
}
}
}
.plugin-detail {
padding: 20px 20px 0 20px;
box-sizing: border-box;
width: 60%;
height: 100%;
background: #fff;
.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;
}
}
.detail-container,
.feature-container {
height: 380px;
overflow: auto;
img {
width: 100%;
}
}
.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;
}
}
}
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<div class="system">
<PluginList
v-if="dev && !!dev.length"
@downloadSuccess="downloadSuccess"
title="开发"
:list="dev"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { useStore } from "vuex";
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const data = ref([]);
onBeforeMount(async () => {
data.value = await request.getDevDetail();
});
const dev = computed(() => {
const defaultData = data.value || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
</script>
<style lang="less">
.system {
width: 100%;
height: 100vh;
overflow-x: hidden;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<div class="finder">
<a-carousel arrows>
<template #prevArrow>
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
<left-circle-outlined />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow" style="right: 10px">
<right-circle-outlined />
</div>
</template>
<div :key="index" v-for="(banner, index) in (data.banners || [])">
<img @click="jumpTo(banner.link)" width="100%" :src="banner.src" />
</div>
</a-carousel>
<PluginList
v-if="recommend && !!recommend.length"
@downloadSuccess="downloadSuccess"
title="推荐"
:list="recommend"
/>
<PluginList
v-if="newList && !!newList.length"
title="最近更新"
:list="newList"
/>
</div>
</template>
<script setup>
import {
LeftCircleOutlined,
RightCircleOutlined,
} from "@ant-design/icons-vue";
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { useStore } from "vuex";
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const data = ref([]);
onBeforeMount(async () => {
data.value = await request.getFinderDetail();
});
const recommend = computed(() => {
const defaultData = data.value.recommend || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
const newList = computed(() => {
const defaultData = data.value.new || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
</script>
<style lang="less">
.finder {
width: 100%;
height: 100%;
overflow-x: hidden;
box-sizing: border-box;
&::-webkit-scrollbar {
width: 0;
}
.ant-carousel .slick-slide {
text-align: center;
height: 235px;
line-height: 160px;
overflow: hidden;
border-radius: 4px;
img {
width: 100%;
height: 235px;
}
}
.ant-carousel .custom-slick-arrow {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
background-color: rgba(31, 45, 61, 0.11);
opacity: 0.3;
}
.ant-carousel .custom-slick-arrow.slick-next:focus {
color: #fff;
}
.ant-carousel .custom-slick-arrow:before {
display: none;
}
.ant-carousel .custom-slick-arrow:hover {
opacity: 0.5;
}
.ant-carousel .slick-slide h3 {
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
</template>
<script>
export default {
name: "image"
};
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,201 @@
<template>
<div class="panel-item">
<h3 class="title">{{ title }}</h3>
<div class="list-item">
<a-list :grid="{ gutter: 16, column: 2 }" :data-source="list">
<template #renderItem="{ item, index }">
<a-list-item v-if="item" @click="showDetail(item)">
<template #actions>
<a-button style="color: #ff4ea4;" type="text" :loading="item.isloading">
<CloudDownloadOutlined
v-show="!item.isloading && !item.isdwonload"
@click.stop="downloadPlugin(item, index)"
style="font-size: 20px; cursor: pointer"
/>
</a-button>
</template>
<a-list-item-meta>
<template #description>
<span class="ellipse">{{ item.description }}</span>
</template>
<template #title>
<span class="ellipse">{{ item.pluginName }}</span>
</template>
<template #avatar>
<a-avatar :src="item.logo" />
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</div>
</div>
<a-drawer
width="100%"
placement="right"
:closable="false"
:visible="visible"
:get-container="false"
class="plugin-info"
:style="{ position: 'absolute' }"
@close="visible=false"
>
<template #title>
<div class="plugin-title-info">
<div class="back-icon" @click="visible=false">
<ArrowLeftOutlined />
</div>
<div class="info">
<img
:src="detail.logo"
class="plugin-icon"
/>
<div class="plugin-desc">
<div class="title">
{{ detail.pluginName }}
</div>
<div class="desc">
{{ detail.description }}
</div>
<a-button shape="round" type="primary">
<template #icon>
<CloudDownloadOutlined />
</template>
获取
</a-button>
</div>
</div>
</div>
</template>
<div v-html="content" class="home-page-container"></div>
</a-drawer>
</template>
<script setup>
import {
CloudDownloadOutlined,
ArrowLeftOutlined,
} from "@ant-design/icons-vue";
import { defineProps, ref } from "vue";
import { useStore } from "vuex";
import { message } from "ant-design-vue";
import MarkdownIt from "markdown-it";
import request from "../../../assets/request/index";
const store = useStore();
const startDownload = (name) => store.dispatch("startDownload", name);
const successDownload = (name) => store.dispatch("successDownload", name);
defineProps({
list: {
type: [Array],
default: () => [],
},
title: String,
});
const downloadPlugin = async (plugin) => {
startDownload(plugin.name);
await window.market.downloadPlugin(plugin);
message.success(`${plugin.name}安装成功!`);
successDownload(plugin.name);
};
const visible = ref(false);
const detail = ref({});
const markdown = new MarkdownIt();
const content = ref("");
const showDetail = async (item) => {
visible.value = true;
detail.value = item;
let mdContent = "暂无内容";
if (item.homePage) {
mdContent = await request.getPluginDetail(item.homePage);
}
content.value = markdown.render(mdContent);
};
</script>
<style lang="less">
&::-webkit-scrollbar {
width: 0;
}
.panel-item {
margin: 20px 0;
.title {
margin-bottom: 30px;
}
.ellipse {
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
&:after{
content: " ";
display: block;
width: 100%;
height: 1px;
border-bottom: 1px solid #eee;
transform: scaleY(0.5);
}
.ant-list-item {
display: flex !important;
}
&:last-child {
border-bottom: none;
}
}
.plugin-info {
width: 100%;
.ant-drawer-content-wrapper {
box-shadow: none !important;
}
}
.plugin-title-info {
display: flex;
align-items: flex-start;
.back-icon {
font-size: 16px;
margin-right: 40px;
}
.info {
display: flex;
align-items: center;
.plugin-icon {
width: 100px;
height: 100px;
margin-right: 20px;
}
.plugin-desc {
.title {
font-size: 18px;
font-weight: bold;
}
.desc {
font-size: 12px;
font-weight: normal;
margin-top: 5px;
margin-bottom: 20px;
}
}
}
}
.home-page-container {
img {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<div class="system">
<PluginList
v-if="system && !!system.length"
@downloadSuccess="downloadSuccess"
title="系统插件"
:list="system"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { useStore } from "vuex";
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const data = ref([]);
onBeforeMount(async () => {
data.value = await request.getSystemDetail();
});
const system = computed(() => {
const defaultData = data.value || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
</script>
<style lang="less">
.system {
width: 100%;
height: 100vh;
overflow-x: hidden;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<div class="tools">
<PluginList
v-if="tools && !!tools.length"
@downloadSuccess="downloadSuccess"
title="搜索工具"
:list="tools"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { useStore } from "vuex";
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const data = ref([]);
onBeforeMount(async () => {
data.value = await request.getSearchDetail();
});
const tools = computed(() => {
const defaultData = data.value || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
</script>
<style lang="less">
.worker {
width: 100%;
height: 100vh;
overflow-x: hidden;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,49 @@
<template>
<div class="worker">
<PluginList
v-if="system && !!system.length"
@downloadSuccess="downloadSuccess"
title="生产效率"
:list="system"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { useStore } from "vuex";
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const data = ref([]);
onBeforeMount(async () => {
data.value = await request.getWorkerDetail();
});
const system = computed(() => {
const defaultData = data.value || [];
if (!defaultData.length) return [];
return defaultData.map((plugin) => {
let searchInfo = null;
totalPlugins.value.forEach((t) => {
if (t.name === plugin) {
searchInfo = t;
}
});
return searchInfo;
});
});
</script>
<style lang="less">
.worker {
width: 100%;
height: 100vh;
overflow-x: hidden;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,133 @@
<template>
<div class="market">
<div class="left-menu">
<div class="search-container">
<a-input-search
v-model:value="searchValue"
placeholder="搜索插件"
style="width: 100%"
@search="onSearch"
/>
</div>
<a-menu v-model:selectedKeys="current" mode="inline">
<a-menu-item key="finder">
<template #icon>
<StarOutlined />
</template>
探索
</a-menu-item>
<a-menu-item key="worker">
<template #icon>
<SendOutlined style="transform: rotate(-45deg)" />
</template>
效率
</a-menu-item>
<a-menu-item key="tools">
<template #icon>
<SearchOutlined />
</template>
搜索工具
</a-menu-item>
<a-menu-item key="image">
<template #icon>
<FileImageOutlined />
</template>
图像
</a-menu-item>
<a-menu-item key="dev">
<template #icon>
<CodeOutlined />
</template>
开发
</a-menu-item>
<a-menu-item key="system">
<template #icon>
<DatabaseOutlined />
</template>
系统
</a-menu-item>
</a-menu>
</div>
<div class="container">
<component :totalPlugins="totalPlugins" :is="Components[current[0]]" />
</div>
</div>
</template>
<script lang="ts" setup>
import {
StarOutlined,
SendOutlined,
SearchOutlined,
FileImageOutlined,
DatabaseOutlined,
CodeOutlined,
} from "@ant-design/icons-vue";
import { reactive, toRefs, computed } from "vue";
import { useStore } from "vuex";
import Finder from "./components/finder.vue";
import System from "./components/system.vue";
import Worker from "./components/worker.vue";
import Tools from "./components/tools.vue";
import Dev from "./components/devlopment.vue";
const Components = {
finder: Finder,
system: System,
worker: Worker,
tools: Tools,
dev: Dev,
};
const state = reactive({
searchValue: "",
current: ["finder"],
});
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
const { searchValue, current } = toRefs(state);
</script>
<style lang="less">
.market {
display: flex;
box-sizing: border-box;
align-items: flex-start;
width: 100%;
overflow: hidden;
background: #F3EFEF;
height: calc(~"100vh - 46px");
.left-menu {
width: 200px;
height: 100vh;
.search-container {
padding: 10px;
}
.ant-input-affix-wrapper {
border: none;
}
.ant-menu {
background: #F3EFEF;
.ant-menu-item-selected {
background-color: #E2E2E2;
color: #141414;
&:after {
display: none;
}
}
}
}
.container {
background: #fff;
width: calc(~'100% - 200px');
height: 100%;
box-sizing: border-box;
padding: 10px 20px;
position: relative;
}
}
</style>

View File

@@ -0,0 +1,336 @@
<template>
<div class="settings">
<div class="left-menu">
<a-menu v-model:selectedKeys="currentSelect" mode="inline">
<a-menu-item key="normal">
<template #icon>
<ToolOutlined />
</template>
基本设置
</a-menu-item>
<a-menu-item key="global">
<template #icon>
<LaptopOutlined />
</template>
全局快捷键
</a-menu-item>
<a-menu-item key="localhost">
<template #icon>
<DatabaseOutlined />
</template>
内网部署配置
</a-menu-item>
</a-menu>
</div>
<div class="settings-detail">
<div v-if="currentSelect[0] === 'normal'">
<div class="setting-item">
<div class="title">
快捷键(需要使用 option/ctrl/shift/command 键修饰)
</div>
<div class="settings-item-li">
<div class="label">显示/隐藏快捷键</div>
<div
class="value"
tabIndex="-1"
@keyup="(e) => changeShortCut(e, 'showAndHidden')"
>
{{ shortCut.showAndHidden }}
</div>
</div>
</div>
<div class="setting-item">
<div class="title">通用</div>
<div class="settings-item-li">
<div class="label">输入框自动粘贴</div>
<a-switch
v-model:checked="common.autoPast"
checked-children=""
un-checked-children=""
></a-switch>
</div>
<div class="settings-item-li">
<div class="label">开机启动</div>
<a-switch
v-model:checked="common.start"
checked-children=""
un-checked-children=""
></a-switch>
</div>
<div class="settings-item-li">
<div class="label">空格执行</div>
<a-switch
v-model:checked="common.space"
checked-children=""
un-checked-children=""
></a-switch>
</div>
</div>
</div>
<div v-if="currentSelect[0] === 'global'">
<a-collapse>
<a-collapse-panel key="1" header="说明及示例">
<div>
按下快捷键自动搜索对应关键字当关键字结果完全匹配且结果唯一时会直接指向该功能
</div>
<h3 style="margin-top: 10px;">示例</h3>
<a-divider style="margin: 5px 0;" />
<a-list item-layout="horizontal" :data-source="examples">
<template #renderItem="{ item }">
<a-list-item>
<a-list-item-meta :description="item.desc">
<template #title>
<div>{{ item.title }}</div>
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-collapse-panel>
</a-collapse>
<div class="feature-container">
<div class="keywords item">
<div>快捷键</div>
<a-tooltip placement="top" trigger="click">
<template #title>
<span>先按功能键CtrlShiftAltOptionCommand再按其他普通键或按
F1-F12 单键
</span>
</template>
<div
:key="index"
v-for="(item, index) in global"
class="value"
tabIndex="2"
@keyup="(e) => changeGlobalKey(e, index)"
>
{{ item.key }}
</div>
</a-tooltip>
</div>
<div class="short-cut item">
<div>功能关键字</div>
<a-input
:key="index"
:value="item.value"
v-for="(item, index) in global"
class="value"
:disabled="!item.key"
@change="(e) => changeGlobalValue(index, e.target.value)"
/>
</div>
</div>
<div @click="addConfig" class="add-global">+ 新增全局快捷功能</div>
</div>
<Localhost v-if="currentSelect[0] === 'localhost'" />
</div>
</div>
</template>
<script setup>
import { ToolOutlined, LaptopOutlined, DatabaseOutlined } from "@ant-design/icons-vue";
import debounce from "lodash.debounce";
import { ref, reactive, watch, toRefs, toRaw } from "vue";
import keycodes from "./keycode";
import Localhost from "./localhost.vue";
const { remote, ipcRenderer } = window.require("electron");
const examples = [
{
title: "快捷键 「 Alt + W」 关键字 「 微信」",
desc: "按下Alt + W 直接打开本地微信应用",
},
{
title: "快捷键 「 Alt + Q」 关键字 「 取色」",
desc: "按下Alt + Q 直接打开屏幕取色功能",
},
];
const state = reactive({
shortCut: {},
common: {},
local: {},
global: [],
});
const currentSelect = ref(["normal"]);
const {perf, global: defaultGlobal} = remote.getGlobal("OP_CONFIG").get();
state.shortCut = perf.shortCut;
state.common = perf.common;
state.local = perf.local;
state.global = defaultGlobal;
const setConfig = debounce(() => {
remote.getGlobal("OP_CONFIG").set(JSON.parse(JSON.stringify({
perf: {
shortCut: state.shortCut,
common: state.common,
local: state.local,
},
global: state.global,
})));
ipcRenderer.send("re-register");
}, 2000);
watch(state, setConfig);
const changeShortCut = (e, key) => {
if (e.altKey && e.keyCode !== 18) {
const compose = `Option+${keycodes[e.keyCode].toUpperCase()}`;
state.shortCut[key] = compose;
}
if (e.ctrlKey && e.keyCode !== 17) {
const compose = `Ctrl+${keycodes[e.keyCode].toUpperCase()}`;
state.perf.shortCut[key] = compose;
}
if (e.shiftKey && e.keyCode !== 16) {
const compose = `Shift+${keycodes[e.keyCode].toUpperCase()}`;
state.perf.shortCut[key] = compose;
}
if (e.metaKey && e.keyCode !== 93) {
const compose = `Command+${keycodes[e.keyCode].toUpperCase()}`;
state.perf.shortCut[key] = compose;
}
};
const changeGlobalKey = (e, index) => {
let compose;
if (e.altKey && e.keyCode !== 18) {
compose = `Alt+${keycodes[e.keyCode].toUpperCase()}`;
}
if (e.ctrlKey && e.keyCode !== 17) {
compose = `Ctrl+${keycodes[e.keyCode].toUpperCase()}`;
}
if (e.shiftKey && e.keyCode !== 16) {
compose = `Shift+${keycodes[e.keyCode].toUpperCase()}`;
}
if (e.metaKey && e.keyCode !== 93) {
compose = `Command+${keycodes[e.keyCode].toUpperCase()}`;
}
if (compose) {
state.global[index].key = compose;
}
// f1 - f12
if (e.keyCode >= 112 && e.keyCode <= 123) {
compose = keycodes[e.keyCode].toUpperCase();
}
if (compose) {
state.global[index].key = compose;
}
};
const changeGlobalValue = (index, value) => {
state.global[index].value = value;
}
const addConfig = () => {
state.global.push({
key: "",
value: "",
});
};
const {shortCut, common, local, global} = toRefs(state);
</script>
<style lang="less">
.settings {
box-sizing: border-box;
width: 100%;
overflow-x: hidden;
background: #f3efef;
height: calc(~"100vh - 46px");
display: flex;
.left-menu {
width: 200px;
height: 100%;
.search-container {
padding: 10px;
}
.ant-input-affix-wrapper {
border: none;
}
.ant-menu {
background: #F3EFEF;
.ant-menu-item-selected {
background-color: #E2E2E2;
color: #141414;
&:after {
display: none;
}
}
}
}
.settings-detail {
padding: 20px;
box-sizing: border-box;
flex: 1;
overflow: auto;
height: 100%;
background: #fff;
.setting-item {
margin-bottom: 20px;
.ant-form-item {
margin-bottom: 0;
}
.title {
color: #6c9fe2;
font-size: 15px;
margin-bottom: 10px;
}
.settings-item-li {
padding-left: 20px;
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
.label {
color: #646464;
}
.value {
width: 300px;
text-align: center;
border: 1px solid #ddd;
color: #6c9fe2;
font-size: 14px;
height: 24px;
font-weight: lighter;
}
}
}
}
.feature-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 10px;
font-size: 14px;
.item {
flex: 1;
}
.short-cut {
margin-left: 20px;
}
.value {
text-align: center;
border: 1px solid #ddd;
color: #6c9fe2;
font-size: 14px;
height: 24px;
font-weight: lighter;
margin-top: 10px;
}
}
.add-global {
color: #6c9fe2;
margin-top: 20px;
width: 100%;
text-align: center;
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,103 @@
export default {
0: "That key has no keycode",
3: "break",
8: "backspace / delete",
9: "tab",
12: "clear",
13: "enter",
16: "shift",
17: "ctrl",
18: "alt",
19: "pause/break",
20: "caps lock",
21: "hangul",
25: "hanja",
27: "escape",
28: "conversion",
29: "non-conversion",
32: "space",
33: "page up",
34: "page down",
35: "End",
36: "Home",
37: "Left",
38: "Up",
39: "Right",
40: "Down",
45: "Insert",
46: "Delete",
48: "0",
49: "1",
50: "2",
51: "3",
52: "4",
53: "5",
54: "6",
55: "7",
56: "8",
57: "9",
65: "A",
66: "B",
67: "C",
68: "D",
69: "E",
70: "F",
71: "G",
72: "H",
73: "I",
74: "J",
75: "K",
76: "L",
77: "M",
78: "N",
79: "O",
80: "P",
81: "Q",
82: "R",
83: "S",
84: "T",
85: "U",
86: "V",
87: "W",
88: "X",
89: "Y",
90: "Z",
112: "F1",
113: "F2",
114: "F3",
115: "F4",
116: "F5",
117: "F6",
118: "F7",
119: "F8",
120: "F9",
121: "F10",
122: "F11",
123: "F12",
186: ";",
187: "=",
188: ",",
189: "-",
190: ".",
191: "/",
192: "`",
219: "[",
220: "\\",
221: "]",
222: "'",
223: "`",
224: "left or right ⌘ key (firefox)",
225: "altgr",
226: "< /git >, left back slash",
230: "GNOME Compose Key",
231: "ç",
233: "XF86Forward",
234: "XF86Back",
235: "non-conversion",
240: "alphanumeric",
242: "hiragana/katakana",
243: "half-width/full-width",
244: "kanji",
251: "unlock trackpad (Chrome/Edge)",
255: "toggle touchpad"
};

View File

@@ -0,0 +1,90 @@
<template>
<a-alert
message="把插件发布到公网 npm 如果不符合您的公司安全要求rubick 支持内网私有源和私有插件库,如果您需要内网部署使用,可以自行配置以下规则。"
type="warning"
style="margin-bottom: 20px"
/>
<a-form
name="custom-validation"
ref="formRef"
:model="formState"
:rules="rules"
v-bind="layout"
>
<a-form-item has-feedback label="npm 源" name="register">
<a-input
placeholder="https://registry.npm.taobao.org"
v-model:value="formState.register"
/>
</a-form-item>
<a-form-item has-feedback label="database url" name="database">
<a-input
placeholder="https://gitee.com/monkeyWang/rubick-database/raw/master"
v-model:value="formState.database"
/>
</a-form-item>
<a-form-item has-feedback label="access_token" name="access_token">
<a-input
placeholder="内网gitlab仓库必填"
v-model:value="formState.access_token"
/>
</a-form-item>
<a-form-item :wrapper-col="{ span: 18, offset: 6 }">
<a-button @click="submit" type="primary">确定</a-button>
<a-button style="margin-left: 10px" @click="resetForm">恢复默认</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts" setup>
import { ref, toRaw } from "vue";
import { message } from "ant-design-vue";
let _rev: any;
let defaultConfig = {
register: "https://registry.npm.taobao.org",
database: "https://gitee.com/monkeyWang/rubick-database/raw/master",
access_token: "",
};
try {
const dbdata = window.rubick.db.get("rubick-localhost-config");
defaultConfig = dbdata.data;
_rev = dbdata._rev;
} catch (e) {
// ignore
}
const formState = ref(JSON.parse(JSON.stringify(defaultConfig)));
const rules = {
register: [{ required: true, trigger: "change" }],
database: [{ required: true, trigger: "change" }],
};
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
const resetForm = () => {
formState.value = {
register: "https://registry.npm.taobao.org",
database: "https://gitee.com/monkeyWang/rubick-database/raw/master",
access_token: "",
};
};
const submit = () => {
const changeData: any = {
_id: "rubick-localhost-config",
data: toRaw(formState.value),
};
if (_rev) {
changeData._rev = _rev;
}
window.rubick.db.put(changeData);
message.success("设置成功!重启插件市场后生效!");
};
</script>

39
feature/tsconfig.json Normal file
View File

@@ -0,0 +1,39 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}

13
feature/vue.config.js Normal file
View File

@@ -0,0 +1,13 @@
const path = require("path");
module.exports = {
css: { // 配置css模块
loaderOptions: { // 向预处理器 Loader 传递配置选项
less: { // 配置less其他样式解析用法一致
javascriptEnabled: true // 设置为true
}
}
},
outputDir: path.join(__dirname, "../public/feature"),
publicPath: process.env.NODE_ENV === "production" ? "" : "/",
};

View File

@@ -1,155 +1,63 @@
{
"name": "rubick2",
"version": "0.0.3-beta.8",
"author": "muwoo <2424880409@qq.com>",
"description": "An electron-vue project",
"license": null,
"main": "./dist/electron/main.js",
"name": "rubick",
"version": "2.0.1-beta.16",
"private": true,
"scripts": {
"release": "node .electron-vue/build.js && electron-builder --publish always",
"build": "node .electron-vue/build.js && electron-builder",
"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"
},
"build": {
"asar": true,
"productName": "rubick2",
"appId": "com.muwoo.rubick",
"compression": "maximum",
"directories": {
"output": "build"
},
"files": [
"dist/electron/**/*"
],
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "build/icons/icon.icns",
"target": "pkg",
"extendInfo": {
"LSUIElement": 1
}
},
"win": {
"icon": "build/icons/icon.ico",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons/",
"publish": ["github"]
}
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"feature:dev": "cd feature & npm run serve",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"release": "vue-cli-service electron:build",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
},
"main": "background.js",
"dependencies": {
"ant-design-vue": "^1.7.5",
"axios": "^0.18.1",
"bplist-parser": "^0.3.0",
"download": "^8.0.0",
"download-git-repo": "^3.0.2",
"electron-is-dev": "^2.0.0",
"electron-store": "^8.0.0",
"iohook": "^0.9.3",
"is-chinese": "^1.4.2",
"jian-pinyin": "^0.2.3",
"keycode": "^2.2.0",
"marked": "^2.0.7",
"md5": "^2.3.0",
"mime-types": "^2.1.31",
"node-fetch": "^2.6.1",
"puppeteer-core": "^10.0.0",
"puppeteer-in-electron": "^3.0.3",
"query-string": "^7.0.0",
"request": "^2.88.2",
"request-promise": "^4.2.6",
"robotjs": "git+https://github.com/Toinane/robotjs.git",
"semver": "^7.3.5",
"sudo-prompt": "^9.2.1",
"systeminformation": "^5.8.0",
"unzip": "^0.1.11",
"uuid": "^8.3.2",
"vue": "^2.5.16",
"vue-electron": "^1.0.6",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-electron": "^1.0.0"
"@better-scroll/core": "^2.4.2",
"ant-design-vue": "^2.2.8",
"core-js": "^3.6.5",
"cross-spawn": "^7.0.3",
"electron-clipboard-ex": "^1.3.3",
"extract-file-icon": "^0.3.2",
"fix-path": "^3.0.0",
"get-mac-apps": "^1.0.2",
"got": "^11.8.3",
"lodash.throttle": "^4.1.1",
"pouchdb": "^7.2.2",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0",
"worker-loader": "^3.0.8"
},
"devDependencies": {
"ajv": "^6.5.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-minify-webpack-plugin": "^0.3.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"babel-runtime": "^6.26.0",
"cfonts": "^2.1.2",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^4.5.1",
"cross-env": "^5.1.6",
"css-loader": "^0.28.11",
"del": "^3.0.0",
"devtron": "^1.4.0",
"electron": "^12.0.15",
"electron-builder": "22.10.5",
"electron-debug": "^1.5.0",
"electron-devtools-installer": "^2.2.4",
"electron-rebuild": "^2.3.5",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"iconv-lite": "^0.6.3",
"less": "^2.7.3",
"@ts-type/package-dts": "^1.0.53",
"@types/electron-devtools-installer": "^2.2.0",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"babel-plugin-import": "^1.13.3",
"electron": "^13.0.0",
"electron-builder": "22.13.1",
"electron-devtools-installer": "^3.1.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-vue": "^7.0.0",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"listr": "^0.14.3",
"mini-css-extract-plugin": "0.4.0",
"multispinner": "^0.2.1",
"node-abi": "^2.30.0",
"node-loader": "^0.6.0",
"react": "^17.0.2",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.2.4",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4",
"webpack-hot-middleware": "^2.22.2",
"webpack-merge": "^4.1.3"
"prettier": "^2.2.1",
"typescript": "~4.1.5",
"vue-cli-plugin-electron-builder": "~2.1.1",
"worker-plugin": "^5.0.1"
},
"iohook": {
"targets": [
"node-83",
"electron-87"
],
"platforms": [
"darwin",
"win32",
"linux"
],
"arches": [
"x64",
"ia32"
]
}
"__npminstall_done": false
}

11829
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
body,html{margin:0;padding:0;font-family:system-ui,PingFang SC,Helvetica Neue,Microsoft Yahei,sans-serif;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden}.detach{width:100%;height:56px;background:#eee;flex:1;display:flex;align-items:center;font-size:18px;padding-left:10px;font-weight:500;box-sizing:border-box;justify-content:space-between}.detach.darwin{padding-left:80px}.detach.darwin,.detach.win32{-webkit-app-region:drag}.detach img{width:36px;height:36px;margin-right:10px}.detach input{background-color:#fff;color:#333;width:360px;height:36px;line-height:36px;border-radius:4px;font-size:14px;border:none;padding:0 10px;outline:none;-webkit-app-region:no-drag}.detach input::-webkit-input-placeholder{color:#aaa;-webkit-user-select:none;user-select:none}.detach .info{display:flex;align-items:center}.handle{display:flex;-webkit-app-region:no-drag}.handle>div{width:36px;height:36px;border-radius:18px;cursor:pointer;margin-right:6px}.handle>div:hover{background-color:#dee2e6}.detach .devtool{background:50%/18px no-repeat url(../img/devtool.87e078f5.svg)}

BIN
public/detach/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1576121932768" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2610" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M344.792 518.575L303.4 477.184a26.947 26.947 0 0 1 38.13-38.13l60.174 60.173a26.947 26.947 0 0 1 0.27 37.834L114.392 833.16a26.947 26.947 0 0 0 0.27 37.834l68.984 68.958a26.947 26.947 0 0 0 38.077 0l291.301-291.3a26.947 26.947 0 0 1 38.104 0l146.324 146.323a26.947 26.947 0 1 1-38.104 38.13L532.076 705.833 259.853 978.055a80.842 80.842 0 0 1-114.337 0L76.53 909.096a80.842 80.842 0 0 1-0.809-113.475l269.043-277.046z m473.546 155.54a26.947 26.947 0 1 1-38.104 38.104L597.288 529.273a26.947 26.947 0 0 1 0-38.103l148.13-148.103a26.947 26.947 0 0 1 15.36-7.653l88.603-12.18 89.627-170.927-56.697-60.39-167.37 97.254-16.546 85.53a26.947 26.947 0 0 1-7.384 13.96l-148.13 148.102a26.947 26.947 0 0 1-38.103 0l-77.474-77.474a26.947 26.947 0 1 1 38.104-38.103l58.422 58.422 123.23-123.23 17.273-89.466a26.947 26.947 0 0 1 12.935-18.19l196.5-114.175a26.947 26.947 0 0 1 33.173 4.85l84.48 90.004a26.947 26.947 0 0 1 4.203 30.963l-104.96 200.165a26.947 26.947 0 0 1-20.21 14.201l-93.346 12.854-122.637 122.637 163.867 163.894z" p-id="2611" fill="#888888"></path><path d="M610.816 784.573a26.947 26.947 0 0 1 38.104-38.104l52.089 52.09a26.947 26.947 0 0 1-38.104 38.103l-52.089-52.09zM368.371 543.42a26.947 26.947 0 1 1 37.995-38.185L705.671 803.22a26.947 26.947 0 0 1 7.814 21.45 111.373 111.373 0 0 0 31.475 87.471 107.79 107.79 0 1 0 68.662-183.727c-2.129 0.135-3.934 0.081-5.578-0.054a26.947 26.947 0 0 1-19.537-7.868L485.24 417.954a26.947 26.947 0 1 1 38.05-38.158l295.181 294.481A161.684 161.684 0 1 1 706.83 950.272a165.16 165.16 0 0 1-47.642-117.275L368.37 543.421z" p-id="2612" fill="#888888"></path><path d="M783.076 874.036a53.895 53.895 0 1 0 76.22-76.219 53.895 53.895 0 1 0-76.22 76.219zM421.807 588.989a26.947 26.947 0 0 1 38.104 38.13L221.723 865.28a26.947 26.947 0 1 1-38.104-38.104L421.807 588.99z m81.597-229.808a26.947 26.947 0 1 1-38.104 38.104l-37.996-37.996a26.947 26.947 0 0 1-5.847-29.345c0.808-1.914 1.05-2.426 3.368-7.06l0.189-0.432c0.754-1.509 1.24-2.506 1.159-2.263a188.632 188.632 0 0 0-43.601-198.818 187.877 187.877 0 0 0-129.698-55.215 189.736 189.736 0 0 0-73.135 13.15l-2.506 0.97-1.752 0.728a26.947 26.947 0 0 1-21.073-49.61c1.887-0.809 1.887-0.809 3.423-1.402l2.102-0.808a242.068 242.068 0 0 1 93.992-16.896 241.772 241.772 0 0 1 166.723 70.98 242.526 242.526 0 0 1 57.722 250.88l25.007 25.033zM25.869 160.013a26.947 26.947 0 0 1 49.61 21.02 187.284 187.284 0 0 0-14.74 65.374 188.039 188.039 0 0 0 55.054 141.743 188.632 188.632 0 0 0 44.463 33.037 26.947 26.947 0 1 1-25.411 47.536 242.526 242.526 0 0 1-57.129-42.47A241.907 241.907 0 0 1 6.9 244.035a243.443 243.443 0 0 1 18.97-84.022z m224.337 337.274a26.947 26.947 0 0 1-0.215-53.895 189.17 189.17 0 0 0 61.79-10.644c4.366-1.51 7.168-2.21 10.94-1.563a26.947 26.947 0 0 1 18.81 7.895l33.145 33.146a26.947 26.947 0 0 1-38.103 38.13l-21.99-22.016a243.308 243.308 0 0 1-64.377 8.947z" p-id="2613" fill="#888888"></path><path d="M148.48 77.824a26.947 26.947 0 1 1 38.104-38.104l161.792 161.82a26.947 26.947 0 0 1 7.087 25.6l-22.986 91.35a26.947 26.947 0 0 1-19.564 19.565L221.56 361.04a26.947 26.947 0 0 1-25.6-7.06L30.343 188.362a26.947 26.947 0 1 1 38.13-38.103L223.26 305.044l60.901-15.306 15.306-60.9L148.48 77.823z" p-id="2614" fill="#888888"></path></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

1
public/detach/index.html Normal file
View File

@@ -0,0 +1 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>detach</title><link href="css/app.9d15d34b.css" rel="preload" as="style"><link href="js/app.a7b22972.js" rel="preload" as="script"><link href="js/chunk-vendors.c073804a.js" rel="preload" as="script"><link href="css/app.9d15d34b.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but detach doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.c073804a.js"></script><script src="js/app.a7b22972.js"></script></body></html>

View File

@@ -0,0 +1,2 @@
(function(e){function t(t){for(var u,c,l=t[0],a=t[1],i=t[2],s=0,b=[];s<l.length;s++)c=l[s],Object.prototype.hasOwnProperty.call(r,c)&&r[c]&&b.push(r[c][0]),r[c]=0;for(u in a)Object.prototype.hasOwnProperty.call(a,u)&&(e[u]=a[u]);p&&p(t);while(b.length)b.shift()();return o.push.apply(o,i||[]),n()}function n(){for(var e,t=0;t<o.length;t++){for(var n=o[t],u=!0,l=1;l<n.length;l++){var a=n[l];0!==r[a]&&(u=!1)}u&&(o.splice(t--,1),e=c(c.s=n[0]))}return e}var u={},r={app:0},o=[];function c(t){if(u[t])return u[t].exports;var n=u[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,c),n.l=!0,n.exports}c.m=e,c.c=u,c.d=function(e,t,n){c.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},c.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,t){if(1&t&&(e=c(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(c.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var u in e)c.d(n,u,function(t){return e[t]}.bind(null,u));return n},c.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return c.d(t,"a",t),t},c.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},c.p="";var l=window["webpackJsonp"]=window["webpackJsonp"]||[],a=l.push.bind(l);l.push=t,l=l.slice();for(var i=0;i<l.length;i++)t(l[i]);var p=a;o.push([0,"chunk-vendors"]),n()})({0:function(e,t,n){e.exports=n("cd49")},"146a":function(e,t,n){"use strict";n("6b62")},"6b62":function(e,t,n){},cd49:function(e,t,n){"use strict";n.r(t);n("e260"),n("e6cf"),n("cca6"),n("a79d");var u=n("7a23"),r=n("84a2"),o=n.n(r),c={class:"info"},l=["src"],a=["value","placeholder"],i={key:1},p={setup:function(e){var t=window.require("electron"),n=t.ipcRenderer,r=Object(u["f"])(window.process.platform),p=Object(u["f"])({}),s=Object(u["f"])(!1);window.initDetach=function(e){p.value=e,s.value=e.subInput&&(!!e.subInput.value||!!e.subInput.placeholder),console.log(s.value)};var b=o()((function(e){n.send("msg-trigger",{type:"detachInputChange",data:{text:e.target.value}})}),500),f=function(){n.send("msg-trigger",{type:"openPluginDevTools"})};return Object.assign(window,{setSubInputValue:function(e){var t=e.value;p.value.subInput.value=t},setSubInput:function(e){p.value.subInput.placeholder=e},removeSubInput:function(){p.value.subInput=null}}),function(e,t){var n,o;return Object(u["e"])(),Object(u["b"])("div",{class:Object(u["d"])([r.value,"detach"])},[Object(u["c"])("div",c,[Object(u["c"])("img",{src:p.value.logo},null,8,l),s.value?(Object(u["e"])(),Object(u["b"])("input",{key:0,autofocus:"",onInput:t[0]||(t[0]=function(){return Object(u["h"])(b)&&Object(u["h"])(b).apply(void 0,arguments)}),value:null===(n=p.value.subInput)||void 0===n?void 0:n.value,placeholder:null===(o=p.value.subInput)||void 0===o?void 0:o.placeholder},null,40,a)):(Object(u["e"])(),Object(u["b"])("span",i,Object(u["g"])(p.value.pluginName),1))]),Object(u["c"])("div",{class:"handle"},[Object(u["c"])("div",{class:"devtool",onClick:f,title:"开发者工具"})])],2)}}};n("146a");const s=p;var b=s;Object(u["a"])(b).mount("#app")}});
//# sourceMappingURL=app.a7b22972.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

File diff suppressed because one or more lines are too long

BIN
public/feature/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>feature</title><link href="css/app.f8214d90.css" rel="preload" as="style"><link href="js/app.6418a244.js" rel="preload" as="script"><link href="js/chunk-vendors.335eb4e0.js" rel="preload" as="script"><link href="css/app.f8214d90.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but feature doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.335eb4e0.js"></script><script src="js/app.6418a244.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
{
"name": "rubick-system-feature",
"pluginName": "rubick 系统菜单",
"description": "rubick 系统菜单",
"main": "index.html",
"logo": "https://static.91jkys.com/upload/202112/08/8a1abbb051bf4b05bbc9bbf66ade63f2.png",
"version": "0.0.0",
"preload":"preload.js",
"pluginType": "ui",
"features": [
{
"code": "market",
"explain": "rubick 插件市场",
"cmds":[
"插件市场"
]
},{
"code": "installed",
"explain": "rubick 已安装插件",
"cmds":[
"已安装插件"
]
},{
"code": "settings",
"explain": "rubick 偏好设置",
"cmds":[
"偏好设置"
]
}
]
}

16
public/feature/preload.js Normal file
View File

@@ -0,0 +1,16 @@
const {remote} = require("electron");
window.market = {
getLocalPlugins() {
return remote.getGlobal("LOCAL_PLUGINS").getLocalPlugins();
},
downloadPlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").downloadPlugin(plugin);
},
deletePlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").deletePlugin(plugin);
},
refreshPlugin(plugin) {
return remote.getGlobal("LOCAL_PLUGINS").refreshPlugin(plugin);
},
};

Some files were not shown because too many files have changed in this diff Show More