Compare commits
149 Commits
v0.0.3-bet
...
v2.0.1-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
357846d2e6 | ||
|
|
3b3ddf224c | ||
|
|
4439d0548f | ||
|
|
21163b2277 | ||
|
|
480aaf2970 | ||
|
|
06596d87ae | ||
|
|
1008e86fbb | ||
|
|
62ec316337 | ||
|
|
735a450260 | ||
|
|
417ab071df | ||
|
|
1e73ab5ee6 | ||
|
|
e5ff219685 | ||
|
|
2beac06e7c | ||
|
|
6b96df3da5 | ||
|
|
8521262344 | ||
|
|
4cf00f9270 | ||
|
|
bdae8c280b | ||
|
|
04e674d1cd | ||
|
|
371565744e | ||
|
|
58aabb9f1e | ||
|
|
19cd77b26c | ||
|
|
c69be6c24f | ||
|
|
8ca01d900d | ||
|
|
1be13e5aa1 | ||
|
|
56faae0e35 | ||
|
|
f94c52f490 | ||
|
|
a8eeac5f8f | ||
|
|
91ce71f139 | ||
|
|
ab8b616f6a | ||
|
|
905838235c | ||
|
|
7bebcf4d54 | ||
|
|
01df2041a6 | ||
|
|
ee4c8031c4 | ||
|
|
cf19617714 | ||
|
|
cfa4f80d63 | ||
|
|
e89cfd049a | ||
|
|
1644f5606e | ||
|
|
86a5a4bb23 | ||
|
|
1cac93f1d2 | ||
|
|
024aace801 | ||
|
|
acddbb724f | ||
|
|
dc656d3bca | ||
|
|
5cfaa70c67 | ||
|
|
2cd6bab58c | ||
|
|
581599614b | ||
|
|
3ba4be2e4a | ||
|
|
5101491cc1 | ||
|
|
7c692e28be | ||
|
|
571ff12730 | ||
|
|
ff118dfe2d | ||
|
|
ced8aa846b | ||
|
|
7cb78e00a8 | ||
|
|
8250dfdb59 | ||
|
|
ef33ff0a3d | ||
|
|
8bcc5d409c | ||
|
|
afca4f2b5a | ||
|
|
c855fb470e | ||
|
|
898e78080c | ||
|
|
2c6d0c4c15 | ||
|
|
777cf015ef | ||
|
|
3a05e13948 | ||
|
|
7fc60e2ca8 | ||
|
|
a9538f5d86 | ||
|
|
6f9830a181 | ||
|
|
6ee0b2a795 | ||
|
|
6310b5a1bd | ||
|
|
9f3397f369 | ||
|
|
c9d6b04a7d | ||
|
|
1ee379b288 | ||
|
|
b3a00c88ad | ||
|
|
fc7e3e91bd | ||
|
|
64f2eba2fa | ||
|
|
8a35e60e48 | ||
|
|
951f21f5fa | ||
|
|
a9827c6db1 | ||
|
|
1353c440aa | ||
|
|
cd41f0561c | ||
|
|
0132a11d7e | ||
|
|
c2f43bea39 | ||
|
|
766ba46dcc | ||
|
|
8fb01cd158 | ||
|
|
4a2b2ab82d | ||
|
|
072d57f068 | ||
|
|
a7cbc2c890 | ||
|
|
b3ad8343ca | ||
|
|
b198288c7f | ||
|
|
2315e095cc | ||
|
|
0fda62e82c | ||
|
|
b95c8fad6f | ||
|
|
c0adb7a03d | ||
|
|
81cc106452 | ||
|
|
566927ab7b | ||
|
|
4309a86eff | ||
|
|
28f8c47752 | ||
|
|
74e4956050 | ||
|
|
ba45c627ab | ||
|
|
747c5512a7 | ||
|
|
60b1138dd8 | ||
|
|
960b16bb4c | ||
|
|
ef6ac06c40 | ||
|
|
80476aadf7 | ||
|
|
c3fdbbc3a8 | ||
|
|
bcd89ec3c3 | ||
|
|
50452bc36b | ||
|
|
79b60b89a5 | ||
|
|
e4aafb8e60 | ||
|
|
7064f504de | ||
|
|
96f2f62787 | ||
|
|
26bd47cce7 | ||
|
|
daa422b8e7 | ||
|
|
243c954ab6 | ||
|
|
6e02163752 | ||
|
|
f9559ac1b0 | ||
|
|
fb6006d96b | ||
|
|
c89ecb9c66 | ||
|
|
0a1ba91792 | ||
|
|
858bec38b2 | ||
|
|
3bd9271403 | ||
|
|
69626ed69e | ||
|
|
a16b198798 | ||
|
|
988acd7638 | ||
|
|
6a9d3517c4 | ||
|
|
96f9d41460 | ||
|
|
ace106a231 | ||
|
|
6864836399 | ||
|
|
7a1777e083 | ||
|
|
fa30074019 | ||
|
|
5f4e77f88e | ||
|
|
2df00360e4 | ||
|
|
1c893542b9 | ||
|
|
e9fa7b2276 | ||
|
|
db12d66ff7 | ||
|
|
9d3e7fc4a5 | ||
|
|
be21302738 | ||
|
|
a535c5d89b | ||
|
|
1eeb0fad9a | ||
|
|
511b357e28 | ||
|
|
87e7501c0a | ||
|
|
9242f17cde | ||
|
|
d047119076 | ||
|
|
5118fa6ca4 | ||
|
|
424c40a99c | ||
|
|
651e202ab1 | ||
|
|
614d5ae369 | ||
|
|
f287d10ca9 | ||
|
|
a04efd0d4f | ||
|
|
153d9dc5d7 | ||
|
|
de138955b5 | ||
|
|
8d340cb76c |
30
.babelrc
@@ -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
@@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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>
|
||||
`
|
||||
}
|
||||
})
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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"
|
||||
},
|
||||
};
|
||||
20
.github/workflows/main.yml
vendored
@@ -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
@@ -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
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"0 debug pnpm:scope": {
|
||||
"selected": 1
|
||||
}
|
||||
}
|
||||
40
.travis.yml
@@ -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
@@ -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-ins,Finished the walk 。Very consistent with the design concept of this tool,So 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
|
||||
|
||||

|
||||
|
||||
## 使用文档
|
||||
|
||||
[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:
|
||||
|
||||
## 目前支持能力
|
||||

|
||||
|
||||
### 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
|
||||
|
||||

|
||||
After the installation is complete, enter the plug-in call up command to use the corresponding plug-in:
|
||||
|
||||

|
||||
|
||||
### 超级面板
|
||||
长按鼠标右键,即可呼起超级面板,可以根据当前鼠标选择内容,匹配对应插件能力。比如当前选择图片后长按右击,则会呼起上传图床插件:
|
||||
### 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
|
||||
```
|
||||
|
||||

|
||||
### 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 样式的功能插件。
|
||||

|
||||
|
||||
<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 ☕️.
|
||||
|
||||

|
||||
<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,帮助我们更好的成长:
|
||||
|
||||

|
||||
|
||||
|
||||
## 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
@@ -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. 搜索系统应用
|
||||
支持拼音和缩写来搜索系统安装应用:
|
||||
|
||||

|
||||
|
||||
### 2. UI类插件安装
|
||||
点击搜索框右侧 `rubick` 图标,进入插件市场,选择所需插件,点击下载按钮即可下载,下载完成后在已安装 tab 下可以找到安装插件。
|
||||
安装完成后,输入插件呼起命令即可使用对应插件:
|
||||
|
||||

|
||||
|
||||
### 3. 系统类插件安装
|
||||
系统插件安装方式和UI类一样,在插件市场选择`系统分类`,寻找适合自己的系统插件安装即可。
|
||||
|
||||
```
|
||||
系统插件安装成功后,需要重启 rubick 才能生效
|
||||
```
|
||||
|
||||
### 4. 输入框聚焦自动根据剪切板内容匹配插件
|
||||
在 `rubick` 内搜索`偏好设置`,然后开启`自动粘贴` 功能,即可匹配剪切板内容自动匹配适合插件进行使用。
|
||||
|
||||

|
||||
|
||||
### 更多功能
|
||||
如果您还需要更多功能,欢迎来这里给我们提建议:[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,帮助我们更好的成长:
|
||||
|
||||

|
||||
|
||||
## License
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/rubickCenter/rubick/blob/master/LICENSE) file for details.
|
||||
|
||||
29
appveyor.yml
@@ -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
@@ -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 文件
|
||||
],
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
};
|
||||
13240
detach/package-lock.json
generated
Normal file
57
detach/package.json
Normal 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
|
After Width: | Height: | Size: 4.2 KiB |
17
detach/public/index.html
Normal 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
@@ -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>
|
||||
1
detach/src/assets/devtool.svg
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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" ? "" : "/",
|
||||
};
|
||||
@@ -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',
|
||||
|
||||
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 471 KiB |
|
Before Width: | Height: | Size: 749 KiB |
|
Before Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 452 KiB |
|
Before Width: | Height: | Size: 54 KiB |
@@ -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
@@ -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"
|
||||
}]
|
||||
*/
|
||||
```
|
||||
@@ -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';
|
||||
})()
|
||||
};
|
||||
}
|
||||
```
|
||||
实现效果如下:
|
||||
|
||||
|
||||

|
||||
|
||||
### rubick 内搜索插件原理
|
||||
|
||||
接下来就是进行命令搜索插件:
|
||||
|
||||
|
||||

|
||||
|
||||
实现这个功能其实也就是对之前存储的`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)
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||

|
||||
|
||||
本页写的插件demo已上传 [github](https://github.com/clouDr-f2e/rubick-plugin-demo)
|
||||
@@ -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://', ''))
|
||||
```
|
||||
@@ -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插件`,进入插件主界面,
|
||||
开启插件后,在插件主窗口即可通过命令打开插件:
|
||||

|
||||
|
||||

|
||||
|
||||
本小节所有代码:[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 后才能生效。
|
||||
|
||||

|
||||
|
||||
::: 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 了您的提交,插件将会自动上架。
|
||||
|
||||
|
||||
|
||||
@@ -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 即可看到主搜索界面:
|
||||
|
||||

|
||||

|
||||
|
||||
目前支持 windows 和 macos。linux 小伙伴正在开发中
|
||||
|
||||
@@ -11,46 +29,48 @@
|
||||
接下来详细介绍 rubick 所包含和支持的功能
|
||||
|
||||
### 1. 搜索系统应用
|
||||
`macos` 下支持搜索当前电脑内所安装的所有 app 和一些偏好设置,目前可搜索路径为:
|
||||
```json
|
||||
[
|
||||
"/System/Applications",
|
||||
"/Applications",
|
||||
"/System/Library/PreferencePanes"
|
||||
]
|
||||
```
|
||||
也就是说只要当前系统软件安装到这些目录才会被检索到。支持中文搜索和拼音、拼音首字母搜索:
|
||||
支持拼音和缩写来搜索系统安装应用:
|
||||
|
||||

|
||||

|
||||
|
||||
`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 下可以找到安装插件。
|
||||
安装完成后,输入插件呼起命令即可使用对应插件:
|
||||
|
||||

|
||||

|
||||
|
||||
### 4. 右击增强
|
||||
通常我们需要使用鼠标右击来对桌面属性进行拓展,`Rubick` 支持对右击属性进行增强功能,长按鼠标右键即可呼起。如果安装的插件支持
|
||||
特殊类型的文件操作,还可以在右键中唤起插件:
|
||||
### 3. 系统类插件安装
|
||||
系统插件安装方式和UI类一样,在插件市场选择`系统分类`,寻找适合自己的系统插件安装即可。
|
||||
::: danger
|
||||
系统插件安装成功后,需要重启 `rubick` 才能生效
|
||||
:::
|
||||
|
||||
### 4. 输入框聚焦自动根据剪切板内容匹配插件
|
||||
在 `rubick` 内搜索`偏好设置`,然后开启`自动粘贴` 功能,即可匹配剪切板内容自动匹配适合插件进行使用。
|
||||
|
||||

|
||||
|
||||
### 内网部署
|
||||
::: tip
|
||||
如果把插件发布到公网 `npm` 如果不符合您的公司安全要求,`rubick` 支持内网私有源和私有插件库,如果您需要内网部署使用,可以自行配置以下规则。
|
||||
:::
|
||||
`rubick` 依赖 `npm` 仓库做插件管理,依赖 `gitee` 做插件数据存储,所以如果要进行内网部署,主要需要替换这2个设置。详细设置:
|
||||
`插件市场 -> 设置 -> 内网部署设置`
|
||||
|
||||

|
||||
|
||||
#### 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`
|
||||
|
||||

|
||||
|
||||
### 更多功能
|
||||
如果您还需要更多功能,欢迎来这里给我们提建议:[issues](https://github.com/clouDr-f2e/rubick/issues/20)
|
||||
如果您还需要更多功能,欢迎来这里给我们提建议:[issues](https://github.com/rubickCenter/rubick/issues) 。
|
||||
有价值的想法我们会加入到后期的开发当中。同时也欢迎一起加入共建。
|
||||
|
||||
|
||||
@@ -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` 文件
|
||||
双击后,会出现以下弹窗:
|
||||
|
||||

|
||||
|
||||
点击启动,如果一切正常那么可以方向关掉你的命令行了,此时 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
@@ -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
23
feature/.gitignore
vendored
Normal 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
@@ -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
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
};
|
||||
13405
feature/package-lock.json
generated
Normal file
62
feature/package.json
Normal 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
|
After Width: | Height: | Size: 4.2 KiB |
17
feature/public/index.html
Normal 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>
|
||||
31
feature/public/package.json
Normal 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
@@ -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
@@ -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>
|
||||
@@ -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
|
After Width: | Height: | Size: 6.7 KiB |
75
feature/src/assets/request/index.ts
Normal 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
@@ -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");
|
||||
46
feature/src/router/index.ts
Normal 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
@@ -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
|
||||
}
|
||||
81
feature/src/store/index.ts
Normal 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: {},
|
||||
});
|
||||
21
feature/src/views/account/index.vue
Normal 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>
|
||||
76
feature/src/views/dev/index.vue
Normal 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>
|
||||
212
feature/src/views/installed/index.vue
Normal 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>
|
||||
49
feature/src/views/market/components/devlopment.vue
Normal 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>
|
||||
126
feature/src/views/market/components/finder.vue
Normal 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>
|
||||
13
feature/src/views/market/components/image.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "image"
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
201
feature/src/views/market/components/plugin-list.vue
Normal 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>
|
||||
49
feature/src/views/market/components/system.vue
Normal 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>
|
||||
49
feature/src/views/market/components/tools.vue
Normal 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>
|
||||
49
feature/src/views/market/components/worker.vue
Normal 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>
|
||||
133
feature/src/views/market/index.vue
Normal 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>
|
||||
336
feature/src/views/settings/index.vue
Normal 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>先按功能键(Ctrl、Shift、Alt、Option、Command),再按其他普通键。或按
|
||||
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>
|
||||
103
feature/src/views/settings/keycode.js
Normal 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"
|
||||
};
|
||||
90
feature/src/views/settings/localhost.vue
Normal 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
@@ -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
@@ -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" ? "" : "/",
|
||||
};
|
||||
200
package.json
@@ -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
1
public/detach/css/app.9d15d34b.css
Normal 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
|
After Width: | Height: | Size: 4.2 KiB |
1
public/detach/img/devtool.87e078f5.svg
Normal 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
@@ -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>
|
||||
2
public/detach/js/app.a7b22972.js
Normal 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
|
||||
1
public/detach/js/app.a7b22972.js.map
Normal file
2
public/detach/js/chunk-vendors.c073804a.js
Normal file
1
public/detach/js/chunk-vendors.c073804a.js.map
Normal file
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
2
public/feature/css/app.f8214d90.css
Normal file
BIN
public/feature/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
1
public/feature/index.html
Normal 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>
|
||||
2
public/feature/js/app.6418a244.js
Normal file
1
public/feature/js/app.6418a244.js.map
Normal file
285
public/feature/js/chunk-vendors.335eb4e0.js
Normal file
1
public/feature/js/chunk-vendors.335eb4e0.js.map
Normal file
31
public/feature/package.json
Normal 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
@@ -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);
|
||||
},
|
||||
};
|
||||