Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
962c9f2f37 | ||
|
|
4ebd76ff11 | ||
|
|
c3fe613696 | ||
|
|
ee46ab1fb7 | ||
|
|
3caeccc492 | ||
|
|
c4f0a30c51 | ||
|
|
762dc9b2cc | ||
|
|
73870a5326 | ||
|
|
5f2b7c7802 | ||
|
|
0d6fdc4727 | ||
|
|
ded7998fbc | ||
|
|
3913cef1e6 | ||
|
|
b4f10c61f0 | ||
|
|
a04a25c7e0 | ||
|
|
e1a0cbb8a1 | ||
|
|
6b57cf7981 | ||
|
|
9a8b95e356 | ||
|
|
81e6acb29b | ||
|
|
c56d4efa79 | ||
|
|
3fd95725cf | ||
|
|
c6ead5b8cd | ||
|
|
f2750e1234 | ||
|
|
9d4d605acf | ||
|
|
cd30a6c70c | ||
|
|
a33f1adeb3 | ||
|
|
1b3522e536 | ||
|
|
3418560c63 | ||
|
|
fec4360043 | ||
|
|
b8ec879e23 | ||
|
|
29a55ed855 | ||
|
|
514fc40f71 | ||
|
|
bc8e641fda | ||
|
|
4e3fa28be6 | ||
|
|
87638a2882 | ||
|
|
1d98601757 | ||
|
|
4e67a75be0 | ||
|
|
72e6e2aed5 | ||
|
|
b63ac03b84 | ||
|
|
554cc0dbf8 | ||
|
|
1274893154 | ||
|
|
f491209bd6 | ||
|
|
f21940ef23 | ||
|
|
fe9e19a84d | ||
|
|
91b397de9a | ||
|
|
d06be462ff | ||
|
|
8fcbef5b0e | ||
|
|
c5adf3b68c | ||
|
|
5a064830c6 | ||
|
|
e38e6f038c | ||
|
|
c7ea9a8628 | ||
|
|
a5b6526fa4 | ||
|
|
46a49ed9d3 | ||
|
|
9344d7d762 | ||
|
|
e81b8f428c | ||
|
|
6f1fca7f7f | ||
|
|
50f31bd438 | ||
|
|
5b0d596c6d | ||
|
|
c59e2bcc97 | ||
|
|
3c6b329927 | ||
|
|
466dbec4b7 | ||
|
|
4e02244ebe | ||
|
|
748a7fd07e | ||
|
|
4000cea7c8 | ||
|
|
8978166328 | ||
|
|
7f1769733b | ||
|
|
e5fbe750ae | ||
|
|
ed539cf206 | ||
|
|
74c7016d2c | ||
|
|
407b821aca | ||
|
|
6db86ec359 | ||
|
|
c64959a139 | ||
|
|
6122e17d75 | ||
|
|
37f1d34abc | ||
|
|
3dd20857aa | ||
|
|
4a11d9830b | ||
|
|
0165879f2d | ||
|
|
17302fe3e4 | ||
|
|
f65d2d2df4 | ||
|
|
af19de7168 | ||
|
|
9ab9e1973c | ||
|
|
a4056cbe58 | ||
|
|
69d660fe7e | ||
|
|
058e903789 | ||
|
|
53cdddf44a | ||
|
|
2315aad732 | ||
|
|
7f8c6fd60e | ||
|
|
10959f3c54 | ||
|
|
1f4f40848e | ||
|
|
7ef76b0a9b | ||
|
|
f87311760a | ||
|
|
d597477a79 | ||
|
|
2bcbf749d1 | ||
|
|
59dd2c0b55 | ||
|
|
43bace3212 | ||
|
|
649e9f0695 | ||
|
|
e8f2185bbb | ||
|
|
cc9dea5e71 | ||
|
|
aabc0028af | ||
|
|
193946c469 | ||
|
|
22c7b9a170 | ||
|
|
c5f6ae8993 | ||
|
|
bb034677bd | ||
|
|
d2eb8999c5 | ||
|
|
4cb7acfec0 | ||
|
|
188b5e5148 | ||
|
|
0abdb3cf00 | ||
|
|
8704e212bf | ||
|
|
4b92c90361 | ||
|
|
2def54ec49 | ||
|
|
1d5142da79 | ||
|
|
8e3c51d0b9 | ||
|
|
0cc3f70cd7 | ||
|
|
a71ba33156 | ||
|
|
960e01f17f | ||
|
|
86265ee5c5 | ||
|
|
a6377696d9 | ||
|
|
927d913f3b | ||
|
|
250a014fc1 | ||
|
|
5e5a5f133a | ||
|
|
78d0baef7d | ||
|
|
3490752e2f | ||
|
|
2b94fe49a2 | ||
|
|
d0fdbf1ea2 | ||
|
|
eddd39309e | ||
|
|
60ccd10e1a | ||
|
|
51cea7cdf0 | ||
|
|
f7dc41a927 | ||
|
|
ae9d8eee13 | ||
|
|
2d63f2ff42 | ||
|
|
778c910436 | ||
|
|
405548426a | ||
|
|
8ff7d15f8d | ||
|
|
5cf96d38b8 | ||
|
|
8ec128121f | ||
|
|
be6cce9501 | ||
|
|
72f2669cce | ||
|
|
cdf7e14548 | ||
|
|
0fe28ce675 | ||
|
|
9d979a2b04 | ||
|
|
9c12fb6ede | ||
|
|
8a8b97a937 | ||
|
|
1af9af74ce | ||
|
|
ca01d2c521 | ||
|
|
094b83b30b | ||
|
|
ea5b937c93 | ||
|
|
c149211def | ||
|
|
13c63ba66d | ||
|
|
dc1ba57a97 | ||
|
|
392af6c617 | ||
|
|
5909d35566 | ||
|
|
804072999e | ||
|
|
17755b9809 | ||
|
|
c3b2d7b941 | ||
|
|
4a420dd5c5 | ||
|
|
51281f345b | ||
|
|
c8b4bebe1d | ||
|
|
aea0c3661d | ||
|
|
cb32d43be4 | ||
|
|
4a0d398b4f | ||
|
|
fb5a10d792 | ||
|
|
65dfb4ac05 | ||
|
|
f309d18aed | ||
|
|
6fca0e73c0 | ||
|
|
0daafa3964 | ||
|
|
a2f1eec8d7 | ||
|
|
fc56d07ab2 | ||
|
|
cf504a0173 | ||
|
|
e0bb8480a2 | ||
|
|
d4c04ee1e6 | ||
|
|
4ab782e8dc | ||
|
|
a9ba87dc8e | ||
|
|
396faa2c60 | ||
|
|
930e4e663b | ||
|
|
ce9c2aed71 | ||
|
|
bba250b0d9 | ||
|
|
b74fe8676b | ||
|
|
8ad28f7fe5 | ||
|
|
1d91730eb9 | ||
|
|
5a25aa0b52 | ||
|
|
671c011feb | ||
|
|
62b616bfc7 | ||
|
|
2a801e6c19 | ||
|
|
313002cf9e | ||
|
|
bcb1859eaa | ||
|
|
8bc0aef482 | ||
|
|
353f553988 | ||
|
|
f48021d928 | ||
|
|
c9ecef60e3 | ||
|
|
0f4901bdbf | ||
|
|
f494e1486c | ||
|
|
1faf29732c | ||
|
|
5207326017 | ||
|
|
4d5b3fbdf6 | ||
|
|
abcd7b848d | ||
|
|
8785218b0d | ||
|
|
61a2a047c7 | ||
|
|
6aed2d2a97 | ||
|
|
46189d2f34 | ||
|
|
4d55ac941f | ||
|
|
62b0c2113c | ||
|
|
ed187f4438 | ||
|
|
372f71bcd9 | ||
|
|
415bdd31f3 | ||
|
|
5d9e0a311d | ||
|
|
1781a8902c | ||
|
|
1f95ef0ee2 | ||
|
|
a63e1717c7 | ||
|
|
1e5fa28789 | ||
|
|
3455512c47 | ||
|
|
947e49ab4a | ||
|
|
a20299edaa | ||
|
|
ffbbc74734 | ||
|
|
1b8400a458 | ||
|
|
9e2834c0f9 | ||
|
|
0b5981a27d | ||
|
|
d78de75c18 | ||
|
|
f8eceae8e4 | ||
|
|
a0c1e95b15 | ||
|
|
94f70cb8c6 | ||
|
|
f33df32312 | ||
|
|
c1604fa3cb | ||
|
|
0e5eee7876 | ||
|
|
00689a9931 | ||
|
|
06539d1513 | ||
|
|
74b94feb74 | ||
|
|
757a7d1260 | ||
|
|
2496c0b467 | ||
|
|
4a685e0474 | ||
|
|
9fdd54ca16 | ||
|
|
eaa348ad56 | ||
|
|
282b0dc25c | ||
|
|
d99e45a021 | ||
|
|
ef8bf5476c | ||
|
|
d042c93bec | ||
|
|
ad71117876 | ||
|
|
fe7f688488 | ||
|
|
65a15724a3 | ||
|
|
422aa52763 | ||
|
|
eecf98e452 | ||
|
|
975927ac68 | ||
|
|
9ef615cab4 | ||
|
|
fb236c3186 | ||
|
|
458087a94e | ||
|
|
4e8dfef6cb | ||
|
|
e1c151e310 | ||
|
|
25068a8949 | ||
|
|
4010a09fe4 | ||
|
|
f6536b1dc2 | ||
|
|
df39511cdf | ||
|
|
504690b729 | ||
|
|
937979ae3d | ||
|
|
1f373fb325 | ||
|
|
92b9fbcee8 | ||
|
|
37a784225e | ||
|
|
16fe016c70 | ||
|
|
406cfffbc9 | ||
|
|
e5d5bad115 | ||
|
|
7364ea6ddf | ||
|
|
f657026820 | ||
|
|
e8e78303a3 | ||
|
|
29fb9c742e | ||
|
|
16f518b2ad | ||
|
|
790367efd6 | ||
|
|
7e9fd9d14e | ||
|
|
9df9b547d1 | ||
|
|
26f6627b44 | ||
|
|
dc9d5d6dfb | ||
|
|
b4c760c8cd |
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"comments": false,
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": { "node": 7 }
|
||||
}],
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": ["istanbul"]
|
||||
},
|
||||
"main": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": { "node": 7 }
|
||||
}],
|
||||
"stage-0"
|
||||
]
|
||||
},
|
||||
"renderer": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false
|
||||
}],
|
||||
"stage-0"
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false
|
||||
}],
|
||||
"stage-0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": ["transform-runtime"]
|
||||
}
|
||||
@@ -1,130 +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) => {
|
||||
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'])
|
||||
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,178 +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)
|
||||
|
||||
const compiler = webpack(rendererConfig)
|
||||
hotMiddleware = webpackHotMiddleware(compiler, {
|
||||
log: false,
|
||||
heartbeat: 2500
|
||||
})
|
||||
|
||||
compiler.plugin('compilation', compilation => {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
compiler.plugin('done', stats => {
|
||||
logStats('Renderer', stats)
|
||||
})
|
||||
|
||||
const server = new WebpackDevServer(
|
||||
compiler,
|
||||
{
|
||||
contentBase: path.join(__dirname, '../'),
|
||||
quiet: 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)
|
||||
|
||||
const compiler = webpack(mainConfig)
|
||||
|
||||
compiler.plugin('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 () {
|
||||
electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')])
|
||||
|
||||
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,114 +0,0 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
context: path.resolve(__dirname, '../docs'),
|
||||
entry: './main.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../docs/dist'),
|
||||
filename: "index.js"
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'icons': path.resolve(__dirname, '../build/icons')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|vue)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: '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',
|
||||
}
|
||||
},
|
||||
{
|
||||
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]'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, '../docs/template.html')
|
||||
}),
|
||||
new ExtractTextPlugin("styles.css")
|
||||
]
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// module.exports.devtool = '#source-map'
|
||||
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
])
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
process.env.BABEL_ENV = 'main'
|
||||
|
||||
const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const babelMinifyWebpackPlugin = require('babel-minify-webpack-plugin')
|
||||
|
||||
let mainConfig = {
|
||||
entry: {
|
||||
main: path.join(__dirname, '../src/main/index.js')
|
||||
},
|
||||
externals: [
|
||||
// ...Object.keys(dependencies || {})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: 'babel-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.node$/,
|
||||
use: 'node-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
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 babelMinifyWebpackPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = mainConfig
|
||||
@@ -1,178 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
process.env.BABEL_ENV = 'renderer'
|
||||
|
||||
const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
/**
|
||||
* 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: /\.(js|vue)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: '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'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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 ExtractTextPlugin('styles.css'),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true
|
||||
},
|
||||
nodeModules: process.env.NODE_ENV !== 'production'
|
||||
? path.resolve(__dirname, '../node_modules')
|
||||
: false
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
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.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 BabiliWebpackPlugin(),
|
||||
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,139 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
process.env.BABEL_ENV = 'web'
|
||||
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
let webConfig = {
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
entry: {
|
||||
web: path.join(__dirname, '../src/renderer/main.js')
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|vue)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: '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'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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 ExtractTextPlugin('styles.css'),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
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 BabiliWebpackPlugin(),
|
||||
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
|
||||
@@ -1,4 +1,4 @@
|
||||
test/unit/coverage/**
|
||||
test/unit/*.js
|
||||
test/e2e/*.js
|
||||
dist/
|
||||
dist/
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
globals: {
|
||||
__static: 'readonly'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true
|
||||
},
|
||||
extends: 'standard',
|
||||
globals: {
|
||||
__static: true
|
||||
},
|
||||
plugins: [
|
||||
'html'
|
||||
parser: "vue-eslint-parser",
|
||||
'extends': [
|
||||
'plugin:vue/essential',
|
||||
'@vue/standard',
|
||||
'@vue/typescript'
|
||||
],
|
||||
'rules': {
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 0,
|
||||
// allow async-await
|
||||
'generator-star-spacing': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
||||
'plugins': ['@typescript-eslint'],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
PicGo Issue 模板
|
||||
请依照该模板来提交,否则将会被关闭。
|
||||
**提问之前请注意你看过FAQ、配置手册以及那些被关闭的issues。否则同样的提问也会被关闭!**
|
||||
-->
|
||||
|
||||
## 问题类型
|
||||
|
||||
<!-- 你要提交的是Bug Report还是Feature Request?-->
|
||||
|
||||
## PicGo的相关信息
|
||||
|
||||
<!-- 版本、所在平台(Mac或者Windows) -->
|
||||
|
||||
## 问题重现
|
||||
|
||||
<!-- 如果是Bug Report请填写本项 -->
|
||||
<!-- 请附上相关截图 -->
|
||||
|
||||
## 功能请求
|
||||
|
||||
<!-- 如果是Feature Request请填写本项 -->
|
||||
<!-- 详细描述你所预想的功能或者是现有功能的改进。 -->
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
最后,喜欢PicGo的话不妨给它点个star~
|
||||
如果可以的话,请我喝杯咖啡?首页有赞助二维码,谢谢你的支持!
|
||||
-->
|
||||
@@ -3,6 +3,7 @@ dist/electron/*
|
||||
dist/web/*
|
||||
build/*
|
||||
!build/icons
|
||||
!build/installer.nsh
|
||||
coverage
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
@@ -11,3 +12,7 @@ thumbs.db
|
||||
!.gitkeep
|
||||
yarn-error.log
|
||||
docs/dist/
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
dist_electron/
|
||||
|
||||
@@ -7,7 +7,7 @@ language: c
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
# - os: linux
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ npm_config_clang=1
|
||||
compiler: clang
|
||||
cache:
|
||||
@@ -25,11 +25,11 @@ 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
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
|
||||
install:
|
||||
#- export DISPLAY=':99.0'
|
||||
#- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
|
||||
- nvm install 8.9
|
||||
- nvm install 10
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
- source ~/.bashrc
|
||||
- npm install -g xvfb-maybe
|
||||
@@ -38,15 +38,15 @@ script:
|
||||
#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
|
||||
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
|
||||
- npm run release
|
||||
- yarn run build:docs
|
||||
# - yarn run build:docs
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
after_script:
|
||||
- cd docs/dist
|
||||
- git init
|
||||
- git config user.name "Molunerfinn"
|
||||
- git config user.email "marksz@teamsz.xyz"
|
||||
- git add .
|
||||
- git commit -m "Travis build docs"
|
||||
- git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages
|
||||
# after_script:
|
||||
# - cd docs/dist
|
||||
# - git init
|
||||
# - git config user.name "Molunerfinn"
|
||||
# - git config user.email "marksz@teamsz.xyz"
|
||||
# - git add .
|
||||
# - git commit -m "Travis build docs"
|
||||
# - git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages
|
||||
|
||||
@@ -4,14 +4,34 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/src/main/index.dev.js",
|
||||
"env": {
|
||||
"DEBUG_ENV": "debug"
|
||||
},
|
||||
"stopOnEntry": false,
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
// this points to the electron task runner
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 5858,
|
||||
"sourceMaps": false,
|
||||
"sourceMaps": true,
|
||||
"restart": true,
|
||||
"outFiles": [],
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"remoteRoot": null
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
{
|
||||
"eslint.enable": true,
|
||||
"eslint.autoFixOnSave": true
|
||||
"eslint.alwaysShowStatus": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"vue",
|
||||
"typescriptreact"
|
||||
],
|
||||
"[stylus]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"stylusSupremacy.insertSemicolons": false,
|
||||
"stylusSupremacy.insertBraces": false,
|
||||
"stylusSupremacy.insertNewLineBetweenSelectors": true,
|
||||
"stylusSupremacy.insertParenthesisAroundIfCondition": false,
|
||||
"stylusSupremacy.alwaysUseNoneOverZero": true,
|
||||
"stylusSupremacy.alwaysUseZeroWithoutUnit": true,
|
||||
"stylusSupremacy.sortProperties": "grouped",
|
||||
"stylusSupremacy.quoteChar": "\"",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
## :tada: 2.2.2 (2020-01-20)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* picgo-server upload clipboard file's result bug ([4ebd76f](https://github.com/Molunerfinn/PicGo/commit/4ebd76f))
|
||||
* releaseUrl may can't get latest version ([ee46ab1](https://github.com/Molunerfinn/PicGo/commit/ee46ab1))
|
||||
|
||||
|
||||
|
||||
## :tada: 2.2.1 (2020-01-09)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add alias for plugin config name ([5a06483](https://github.com/Molunerfinn/PicGo/commit/5a06483))
|
||||
* add aliyun oss options ([a33f1ad](https://github.com/Molunerfinn/PicGo/commit/a33f1ad)), closes [#347](https://github.com/Molunerfinn/PicGo/issues/347)
|
||||
* **server:** add http server for uploading images by a http request ([c56d4ef](https://github.com/Molunerfinn/PicGo/commit/c56d4ef))
|
||||
* add server config settings ([6b57cf7](https://github.com/Molunerfinn/PicGo/commit/6b57cf7))
|
||||
* only shows visible pic-beds ([9d4d605](https://github.com/Molunerfinn/PicGo/commit/9d4d605)), closes [#310](https://github.com/Molunerfinn/PicGo/issues/310)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* decrease title-bar z-index when config-form dialog shows ([f2750e1](https://github.com/Molunerfinn/PicGo/commit/f2750e1))
|
||||
* **website:** website pictures error ([a5b6526](https://github.com/Molunerfinn/PicGo/commit/a5b6526))
|
||||
* add new tray icon for macOS dark-mode ([c5adf3b](https://github.com/Molunerfinn/PicGo/commit/c5adf3b)), closes [#267](https://github.com/Molunerfinn/PicGo/issues/267)
|
||||
* beforeOpen handler in windows ([cd30a6c](https://github.com/Molunerfinn/PicGo/commit/cd30a6c))
|
||||
* busApi event register first && emit later ([e1a0cbb](https://github.com/Molunerfinn/PicGo/commit/e1a0cbb))
|
||||
* enum type error ([4e3fa28](https://github.com/Molunerfinn/PicGo/commit/4e3fa28))
|
||||
* handle empty request-body ([81e6acb](https://github.com/Molunerfinn/PicGo/commit/81e6acb))
|
||||
* launch error in new structrue ([bc8e641](https://github.com/Molunerfinn/PicGo/commit/bc8e641))
|
||||
* miniWindow minimize bug ([5f2b7c7](https://github.com/Molunerfinn/PicGo/commit/5f2b7c7))
|
||||
* plugin config-form && default plugin logo ([514fc40](https://github.com/Molunerfinn/PicGo/commit/514fc40))
|
||||
* release script ([b4f10c6](https://github.com/Molunerfinn/PicGo/commit/b4f10c6))
|
||||
* removeById handler error ([c4f0a30](https://github.com/Molunerfinn/PicGo/commit/c4f0a30)), closes [#382](https://github.com/Molunerfinn/PicGo/issues/382)
|
||||
* rename page not work ([29a55ed](https://github.com/Molunerfinn/PicGo/commit/29a55ed))
|
||||
* save debug mode && PICGO_ENV into config file ([c6ead5b](https://github.com/Molunerfinn/PicGo/commit/c6ead5b))
|
||||
* server may never start ([73870a5](https://github.com/Molunerfinn/PicGo/commit/73870a5))
|
||||
* settingPage && miniPage style in windows ([3fd9572](https://github.com/Molunerfinn/PicGo/commit/3fd9572))
|
||||
|
||||
|
||||
### :pencil: Documentation
|
||||
|
||||
* add note for windows electron mirror ([46a49ed](https://github.com/Molunerfinn/PicGo/commit/46a49ed))
|
||||
* remove weibo picbed ([e81b8f4](https://github.com/Molunerfinn/PicGo/commit/e81b8f4))
|
||||
* update installation by scoop ([91b397d](https://github.com/Molunerfinn/PicGo/commit/91b397d)), closes [#295](https://github.com/Molunerfinn/PicGo/issues/295)
|
||||
* update readme ([1b3522e](https://github.com/Molunerfinn/PicGo/commit/1b3522e))
|
||||
* update README ([f491209](https://github.com/Molunerfinn/PicGo/commit/f491209))
|
||||
* update site ([fe9e19a](https://github.com/Molunerfinn/PicGo/commit/fe9e19a))
|
||||
|
||||
|
||||
|
||||
# :tada: 2.2.0 (2020-01-01)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add alias for plugin config name ([5a06483](https://github.com/Molunerfinn/PicGo/commit/5a06483))
|
||||
* add aliyun oss options ([a33f1ad](https://github.com/Molunerfinn/PicGo/commit/a33f1ad)), closes [#347](https://github.com/Molunerfinn/PicGo/issues/347)
|
||||
* **server:** add http server for uploading images by a http request ([c56d4ef](https://github.com/Molunerfinn/PicGo/commit/c56d4ef))
|
||||
* add server config settings ([6b57cf7](https://github.com/Molunerfinn/PicGo/commit/6b57cf7))
|
||||
* only shows visible pic-beds ([9d4d605](https://github.com/Molunerfinn/PicGo/commit/9d4d605)), closes [#310](https://github.com/Molunerfinn/PicGo/issues/310)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* beforeOpen handler in windows ([cd30a6c](https://github.com/Molunerfinn/PicGo/commit/cd30a6c))
|
||||
* **website:** website pictures error ([a5b6526](https://github.com/Molunerfinn/PicGo/commit/a5b6526))
|
||||
* add new tray icon for macOS dark-mode ([c5adf3b](https://github.com/Molunerfinn/PicGo/commit/c5adf3b)), closes [#267](https://github.com/Molunerfinn/PicGo/issues/267)
|
||||
* busApi event register first && emit later ([e1a0cbb](https://github.com/Molunerfinn/PicGo/commit/e1a0cbb))
|
||||
* decrease title-bar z-index when config-form dialog shows ([f2750e1](https://github.com/Molunerfinn/PicGo/commit/f2750e1))
|
||||
* enum type error ([4e3fa28](https://github.com/Molunerfinn/PicGo/commit/4e3fa28))
|
||||
* handle empty request-body ([81e6acb](https://github.com/Molunerfinn/PicGo/commit/81e6acb))
|
||||
* launch error in new structrue ([bc8e641](https://github.com/Molunerfinn/PicGo/commit/bc8e641))
|
||||
* plugin config-form && default plugin logo ([514fc40](https://github.com/Molunerfinn/PicGo/commit/514fc40))
|
||||
* release script ([b4f10c6](https://github.com/Molunerfinn/PicGo/commit/b4f10c6))
|
||||
* rename page not work ([29a55ed](https://github.com/Molunerfinn/PicGo/commit/29a55ed))
|
||||
* save debug mode && PICGO_ENV into config file ([c6ead5b](https://github.com/Molunerfinn/PicGo/commit/c6ead5b))
|
||||
* settingPage && miniPage style in windows ([3fd9572](https://github.com/Molunerfinn/PicGo/commit/3fd9572))
|
||||
|
||||
|
||||
### :pencil: Documentation
|
||||
|
||||
* add note for windows electron mirror ([46a49ed](https://github.com/Molunerfinn/PicGo/commit/46a49ed))
|
||||
* remove weibo picbed ([e81b8f4](https://github.com/Molunerfinn/PicGo/commit/e81b8f4))
|
||||
* update installation by scoop ([91b397d](https://github.com/Molunerfinn/PicGo/commit/91b397d)), closes [#295](https://github.com/Molunerfinn/PicGo/issues/295)
|
||||
* update readme ([1b3522e](https://github.com/Molunerfinn/PicGo/commit/1b3522e))
|
||||
* update README ([f491209](https://github.com/Molunerfinn/PicGo/commit/f491209))
|
||||
* update site ([fe9e19a](https://github.com/Molunerfinn/PicGo/commit/fe9e19a))
|
||||
|
||||
|
||||
|
||||
## :tada: 2.1.2 (2019-04-19)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add file-name for customurl ([c59e2bc](https://github.com/Molunerfinn/PicGo/commit/c59e2bc)), closes [#173](https://github.com/Molunerfinn/PicGo/issues/173)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* log-level filter bug ([4e02244](https://github.com/Molunerfinn/PicGo/commit/4e02244)), closes [#237](https://github.com/Molunerfinn/PicGo/issues/237)
|
||||
* log-level's reset value from 'all' -> ['all'] ([3c6b329](https://github.com/Molunerfinn/PicGo/commit/3c6b329)), closes [#240](https://github.com/Molunerfinn/PicGo/issues/240) [#237](https://github.com/Molunerfinn/PicGo/issues/237)
|
||||
* mini window hidden bug in linux ([466dbec](https://github.com/Molunerfinn/PicGo/commit/466dbec)), closes [#239](https://github.com/Molunerfinn/PicGo/issues/239)
|
||||
|
||||
|
||||
|
||||
## :tada: 2.1.1 (2019-04-16)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* upload-area can't upload images ([4000cea](https://github.com/Molunerfinn/PicGo/commit/4000cea))
|
||||
|
||||
|
||||
|
||||
# :tada: 2.1.0 (2019-04-15)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add commandline argvs support for picgo app ([6db86ec](https://github.com/Molunerfinn/PicGo/commit/6db86ec))
|
||||
* add gui-api for remove event ([407b821](https://github.com/Molunerfinn/PicGo/commit/407b821)), closes [#201](https://github.com/Molunerfinn/PicGo/issues/201)
|
||||
* add windows context menu ([e5fbe75](https://github.com/Molunerfinn/PicGo/commit/e5fbe75))
|
||||
* add workflow for mac ([7f17697](https://github.com/Molunerfinn/PicGo/commit/7f17697))
|
||||
* support commandline -> upload images in clipboard ([74c7016](https://github.com/Molunerfinn/PicGo/commit/74c7016))
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* qiniu area option from select group -> input ([c64959a](https://github.com/Molunerfinn/PicGo/commit/c64959a)), closes [#230](https://github.com/Molunerfinn/PicGo/issues/230)
|
||||
|
||||
|
||||
### :pencil: Documentation
|
||||
|
||||
* add brew cask source ([6122e17](https://github.com/Molunerfinn/PicGo/commit/6122e17))
|
||||
|
||||
|
||||
### :package: Chore
|
||||
|
||||
* add picgo bump-version ([37f1d34](https://github.com/Molunerfinn/PicGo/commit/37f1d34))
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
## 常见问题
|
||||
|
||||
> 在使用PicGo期间你会遇到很多问题,不过很多问题其实之前就有人提问过,也被解决,所以你可以先看看[使用文档](https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BF%AB%E6%8D%B7%E9%94%AE),这份FAQ,以及那些被关闭的[issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed),应该能找到答案。
|
||||
|
||||
## 1.七牛图床上传图片成功后无法显示或图片无`http://`前缀
|
||||
|
||||
通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。
|
||||
|
||||
参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79)
|
||||
|
||||
## 2.能否支持图床远端同步删除
|
||||
|
||||
不能。有些图床(比如微博图床、SM.MS、Imgur等)不支持后台管理,为了架构统一不支持远端删除。
|
||||
|
||||
## 3.能否支持上传视频文件
|
||||
|
||||
目前不能。2.0版本出来后将会支持插件系统,届时如果有人开发了相应的插件理论可以支持任意文件上传。
|
||||
|
||||
## 4.微博图床上传之后无法显示预览图
|
||||
|
||||
通常是挂了全局代理导致的。
|
||||
|
||||
参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36)
|
||||
|
||||
## 5.能否支持某某某图床
|
||||
|
||||
截止v1.6,PicGo支持了如下图床:
|
||||
|
||||
- `微博图床` v1.0
|
||||
- `七牛图床` v1.0
|
||||
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0
|
||||
- `又拍云` v1.2.0
|
||||
- `GitHub` v1.5.0
|
||||
- `SM.MS` v1.5.1
|
||||
- `阿里云OSS` v1.6.0
|
||||
- `Imgur` v1.6.0
|
||||
|
||||
所以本体内将不会再支持其他图床。待2.0版本出来支持插件系统后可以由其他开发者开发相应的图床插件用以支持其他图床。
|
||||
|
||||
## 6.一个图床设置多个信息
|
||||
|
||||
不能。因为目前的架构只支持一个图床一份信息。
|
||||
|
||||
## 7.v2.0版本有什么新功能
|
||||
|
||||
在不改变PicGo现有使用界面和使用习惯的基础上,将会重构整个底层`传输->转换->上传`的架构,使其能够支持插件系统,方便其他开发者基于PicGo的底层进行二次开发。
|
||||
|
||||
理论上有了插件系统之后,其他开发者能够支持如下功能:
|
||||
|
||||
1. 能够实现上传前图片压缩
|
||||
2. 能够通过图片url实现上传图片 -> 更深入地可以批量地实现图床的迁移
|
||||
3. 能够支持其他图床甚至是自己的图床
|
||||
|
||||
等等。
|
||||
|
||||
届时底层架构将会独立出来成为一个独立的npm包,能够直接在命令行里调用。
|
||||
|
||||
目前开发进度可以参考[PicGo-Core](https://github.com/Molunerfinn/PicGo-Core)。
|
||||
@@ -1,8 +1,21 @@
|
||||
MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Copyright (c) 2017-present, Molunerfinn
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,11 +1,7 @@
|
||||
# PicGo
|
||||
|
||||
> 图片上传+管理新体验
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/12621342/33876119-85a5148e-df5f-11e7-8843-46224e595d52.png" alt="">
|
||||
</p>
|
||||
<p align="center">
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/Molunerfinn/test/master/picgo/New%20LOGO-150.png" alt="">
|
||||
<h1>PicGo</h1>
|
||||
<blockquote>图片上传+管理新体验 </blockquote>
|
||||
<a href="https://github.com/feross/standard">
|
||||
<img src="https://img.shields.io/badge/code%20style-standard-green.svg?style=flat-square" alt="">
|
||||
</a>
|
||||
@@ -18,35 +14,73 @@
|
||||
<a href="https://github.com/Molunerfinn/PicGo/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/Molunerfinn/PicGo.svg?style=flat-square" alt="">
|
||||
</a>
|
||||
</p>
|
||||
<a href="https://github.com/PicGo/bump-version">
|
||||
<img src="https://img.shields.io/badge/picgo-convention-blue.svg?style=flat-square" alt="">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 应用说明
|
||||
|
||||
PicGo目前支持了`微博图床`,`七牛图床`,`腾讯云COS`,`又拍云`。未来将支持更多图床。
|
||||
**PicGo 在上传图片之后自动会将图片链接复制到你的剪贴板里,可选 5 种复制的链接格式。**
|
||||
|
||||
暂时只支持macOS系统,未来将支持windows和linux。
|
||||
PicGo 目前支持了
|
||||
|
||||
点击此处下载[应用](https://github.com/Molunerfinn/PicGo/releases),macOS用户请下载最新版本的`dmg`文件。
|
||||
- ~~`微博图床` v1.0~~ **微博图床从 2019 年 4 月开始进行防盗链,不建议继续使用**
|
||||
- `七牛图床` v1.0
|
||||
- `腾讯云 COS v4\v5版本` v1.1 & v1.5.0
|
||||
- `又拍云` v1.2.0
|
||||
- `GitHub` v1.5.0
|
||||
- `SM.MS` v1.5.1
|
||||
- `阿里云 OSS` v1.6.0
|
||||
- `Imgur` v1.6.0
|
||||
|
||||
如果第一次使用,请参考应用使用[手册](https://github.com/Molunerfinn/PicGo/wiki)。
|
||||
**本体不再增加默认的图床支持。你可以自行开发第三方图床插件。详见 [PicGo-Core](https://picgo.github.io/PicGo-Core-Doc/)**。
|
||||
|
||||

|
||||
第三方插件以及使用了 PicGo 底层的应用可以在 [Awesome-PicGo](https://github.com/PicGo/Awesome-PicGo) 找到。欢迎贡献!
|
||||
|
||||
PicGo 支持 macOS、Windows 64位(>= v1.3.1),Linux(>= v1.6.0)。
|
||||
|
||||
支持快捷键`command+shift+p`(macOS)或者`control+shift+p`(Windows\Linux)用以支持快捷上传剪贴板里的图片(第一张)。
|
||||
PicGo 支持自定义快捷键,使用方法见[配置手册](https://picgo.github.io/PicGo-Doc/zh/guide/config.html)。
|
||||
|
||||
开发进度可以查看 [Projects](https://github.com/Molunerfinn/PicGo/projects),会同步更新开发进度。
|
||||
|
||||
**如果第一次使用,请参考应用使用[快速上手](https://picgo.github.io/PicGo-Doc/zh/guide/getting-started.html)。遇到问题了还可以看看 [FAQ](https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md) 以及被关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed)。**
|
||||
|
||||
## 下载安装
|
||||
|
||||
点击此处下载[应用](https://github.com/Molunerfinn/PicGo/releases)。
|
||||
|
||||
macOS 用户请下载最新版本的 `dmg` 文件,Windows 用户请下载最新版本的 `exe` 文件,Linux用户请下载 `AppImage` 文件。
|
||||
|
||||
**如果你是 Arch 类的 Linux 用户,可以直接通过 `aurman -S picgo-appimage` 来安装 PicGo。感谢 @houbaron 的贡献!**
|
||||
|
||||
**如果你是 macOS 用户,可以使用 `brew cask` 来安装 PicGo: `brew cask install picgo`。感谢 @womeimingzi11 的贡献!**
|
||||
|
||||
**如果你是 Windows 用户,还可以使用 [Scoop](https://scoop.sh/) 来安装 PicGo: `scoop bucket add helbing https://github.com/helbing/scoop-bucket` & `scoop install picgo`。 感谢 @helbing 的贡献!**
|
||||
|
||||
## 应用截图
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 开发说明
|
||||
|
||||
> 目前仅针对Mac平台。Windows和Linux平台并未测试。
|
||||
> 目前仅针对 Mac、Windows。Linux 平台并未测试。
|
||||
|
||||
如果你想要学习、开发、修改或自行构建PicGo,可以依照下面的指示:
|
||||
如果你想要学习、开发、修改或自行构建 PicGo,可以依照下面的指示:
|
||||
|
||||
1. 你需要有node、git环境。需要了解npm的相关知识。
|
||||
> 如果想学习 Electron-vue 的开发,可以查看我写的系列教程——[Electron-vue 开发实战](https://molunerfinn.com/tags/Electron-vue/)
|
||||
|
||||
1. 你需要有 Node、Git环境,了解 npm 的相关知识。
|
||||
2. `git clone https://github.com/Molunerfinn/PicGo.git` 并进入项目
|
||||
3. `npm install` 下载依赖
|
||||
4. Mac 需要有 Xcode 环境,Windows 需要有 VS 环境。
|
||||
|
||||
### 开发模式
|
||||
|
||||
输入`npm run dev`进入开发模式,开发模式具有热重载特性。不过需要注意的是,开发模式不稳定,会有进程崩溃的情况。此时需要:
|
||||
输入 `npm run electron:serve` 进入开发模式,开发模式具有热重载特性。不过需要注意的是,开发模式不稳定,会有进程崩溃的情况。此时需要:
|
||||
|
||||
```bash
|
||||
ctrl+c # 退出开发模式
|
||||
@@ -55,20 +89,25 @@ npm run dev # 重新进入开发模式
|
||||
|
||||
### 生产模式
|
||||
|
||||
如果你需要自行构建,可以`npm run build`开始进行构建。构建成功后,会在`build`目录里出现构建成功的相应安装文件。
|
||||
如果你需要自行构建,可以 `npm run electron:build` 开始进行构建。构建成功后,会在 `dist_electron` 目录里出现构建成功的相应安装文件。
|
||||
|
||||
**注意**:如果你的网络环境不太好,可能会出现`electron-builder`下载`electron`二进制文件失败的情况。这个时候需要在`npm run build`之前指定一下`electron`的源为国内源:
|
||||
**注意**:如果你的网络环境不太好,可能会出现 `electron-builder` 下载 `electron` 二进制文件失败的情况。这个时候需要在 `npm run build` 之前指定一下 `electron` 的源为国内源:
|
||||
|
||||
```bash
|
||||
export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
|
||||
# 在 Windows 上,则可以使用 set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ (无需引号)
|
||||
npm run build
|
||||
```
|
||||
|
||||
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在`~/.electron/`目录下。如果想要更新`electron`构建版本,可以删除`~/.electron/`目录,然后重新运行上一步,让`electron-builder`去下载最新的`electron`二进制文件。
|
||||
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在 `~/.electron/` 目录下。如果想要更新 `electron` 构建版本,可以删除 `~/.electron/` 目录,然后重新运行上一步,让 `electron-builder `去下载最新的 `electron` 二进制文件。
|
||||
|
||||
## 其他相关
|
||||
|
||||
- [vs-picgo](https://github.com/Spades-S/vs-picgo):PicGo 的 VS Code 版。
|
||||
|
||||
## 赞助
|
||||
|
||||
如果你喜欢PicGo并且它对你确实有帮助,欢迎给我打赏一杯咖啡的钱哈~
|
||||
如果你喜欢 PicGo 并且它对你确实有帮助,欢迎给我打赏一杯咖啡哈~
|
||||
|
||||
支付宝:
|
||||
|
||||
@@ -82,4 +121,4 @@ npm run build
|
||||
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2017 Molunerfinn
|
||||
Copyright (c) 2017 - 2019 Molunerfinn
|
||||
@@ -11,7 +11,6 @@ platform:
|
||||
- x64
|
||||
|
||||
cache:
|
||||
- node_modules
|
||||
- '%APPDATA%\npm-cache'
|
||||
- '%USERPROFILE%\.electron'
|
||||
- '%USERPROFILE%\AppData\Local\Yarn\cache'
|
||||
@@ -20,14 +19,13 @@ init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 8 x64
|
||||
- choco install yarn --ignore-dependencies
|
||||
- ps: Install-Product node 12 x64
|
||||
- git reset --hard HEAD
|
||||
- yarn
|
||||
- node --version
|
||||
|
||||
build_script:
|
||||
#- yarn test
|
||||
- yarn build
|
||||
- npm run release
|
||||
|
||||
test: off
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,13 @@
|
||||
!macro customInstall
|
||||
SetRegView 64
|
||||
WriteRegStr HKCR "*\shell\PicGo" "" "Upload pictures w&ith PicGo"
|
||||
WriteRegStr HKCR "*\shell\PicGo" "Icon" "$INSTDIR\PicGo.exe"
|
||||
WriteRegStr HKCR "*\shell\PicGo\command" "" '"$INSTDIR\PicGo.exe" "upload" "%1"'
|
||||
SetRegView 32
|
||||
WriteRegStr HKCR "*\shell\PicGo" "" "Upload pictures w&ith PicGo"
|
||||
WriteRegStr HKCR "*\shell\PicGo" "Icon" "$INSTDIR\PicGo.exe"
|
||||
WriteRegStr HKCR "*\shell\PicGo\command" "" '"$INSTDIR\PicGo.exe" "upload" "%1"'
|
||||
!macroend
|
||||
!macro customUninstall
|
||||
DeleteRegKey HKCR "*\shell\PicGo"
|
||||
!macroend
|
||||
@@ -4,14 +4,19 @@
|
||||
.mask
|
||||
img.logo(src="~icons/256x256.png", alt="PicGo")
|
||||
h1.title PicGo
|
||||
small(v-if="version") {{ version }}
|
||||
h2.desc 图片上传+管理新体验
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载Mac版
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo')") 在github上查看
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载
|
||||
button.download(@click="goLink('https://picgo.github.io/PicGo-Doc/zh/guide/')") 查看文档
|
||||
h3.desc
|
||||
| 基于#[a(href="https://github.com/SimulatedGREG/electron-vue" target="_blank") electron-vue]开发
|
||||
h3.desc
|
||||
| 支持macOS,Windows,Linux
|
||||
h3.desc
|
||||
| 支持#[a(href="https://picgo.github.io/PicGo-Doc/zh/guide/config.html#%E6%8F%92%E4%BB%B6%E8%AE%BE%E7%BD%AE%EF%BC%88v2-0%EF%BC%89" target="_blank") 插件系统],让PicGo更强大
|
||||
#container.container-fluid
|
||||
.row.ex-width
|
||||
img.gallery.col-xs-10.col-xs-offset-1.col-md-offset-2.col-md-8(src="https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e")
|
||||
img.gallery.col-xs-10.col-xs-offset-1.col-md-offset-2.col-md-8(src="https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/first.png")
|
||||
.row.ex-width.display-list
|
||||
.display-list__item(v-for="(item, index) in itemList" :key="index" :class="{ 'o-item': index % 2 !== 0 }")
|
||||
.col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0
|
||||
@@ -21,40 +26,60 @@
|
||||
.display-list__desc {{ item.desc }}
|
||||
.row.ex-width.info
|
||||
.col-xs-10.col-xs-offset-1
|
||||
| ©2017 #[a(href="https://github.com/Molunerfinn" target="_blank") Molunerfinn]
|
||||
| ©2017 - {{ year }} #[a(href="https://github.com/Molunerfinn" target="_blank") Molunerfinn]
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
data () {
|
||||
return {
|
||||
version: '',
|
||||
year: new Date().getFullYear(),
|
||||
itemList: [
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fma907llb5j20m30ed46a',
|
||||
url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/second.png',
|
||||
title: '精致设计',
|
||||
desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。'
|
||||
desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。(Windows平台不支持)'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd56zm2nej218g0p0teb',
|
||||
url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/third.png',
|
||||
title: 'Mini小窗',
|
||||
desc: 'Windows以及Linux系统下提供一个mini悬浮窗用于用户拖拽上传,节约你宝贵的桌面空间。'
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/forth.png',
|
||||
title: '便捷管理',
|
||||
desc: '查看你的上传记录,重复使用更方便。支持点击图片大图查看。支持删除图片(仅本地记录),让界面更加干净。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd5ck9m0wj20lr0cxmzs',
|
||||
url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/fifth.png',
|
||||
title: '可选图床',
|
||||
desc: '目前支持微博图床和七牛图床。未来将支持更多。方便不同图床的上传需求。'
|
||||
desc: '默认支持微博图床、七牛图床、腾讯云COS、又拍云、GitHub、SM.MS、阿里云OSS、Imgur。方便不同图床的上传需求。2.0版本开始更可以自己开发插件实现其他图床的上传需求。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e',
|
||||
url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/sixth.png',
|
||||
title: '多样链接',
|
||||
desc: '支持4种剪贴板链接格式,让你的文本编辑游刃有余。'
|
||||
desc: '支持5种默认剪贴板链接格式,包括一种自定义格式,让你的文本编辑游刃有余。'
|
||||
},
|
||||
{
|
||||
url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/seventh.png',
|
||||
title: '插件系统',
|
||||
desc: '2.0版本开始支持插件系统,让PicGo发挥无限潜能,成为一个极致的效率工具。'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getVersion()
|
||||
},
|
||||
methods: {
|
||||
goLink (link) {
|
||||
window.open(link, '_blank')
|
||||
},
|
||||
async getVersion () {
|
||||
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||
const res = await this.$http.get(release)
|
||||
this.version = res.data.name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,7 +108,7 @@ h1
|
||||
#header
|
||||
height 100vh
|
||||
width 100%
|
||||
background-image url("https://ws1.sinaimg.cn/large/8700af19ly1fm9ru6fqvjj22p81stdta")
|
||||
background-image url("https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/bg.jpeg")
|
||||
background-attachment fixed
|
||||
background-size cover
|
||||
background-position center
|
||||
@@ -98,6 +123,10 @@ h1
|
||||
font-size 36px
|
||||
font-weight 300
|
||||
margin 10px auto
|
||||
text-align center
|
||||
small
|
||||
margin-left 10px
|
||||
font-size 14px
|
||||
.desc
|
||||
font-weight 400
|
||||
margin 20px auto 10px
|
||||
@@ -172,6 +201,8 @@ h1
|
||||
text-decoration none
|
||||
color #fff
|
||||
@media (max-width: 768px)
|
||||
#header
|
||||
padding 10vh
|
||||
#container
|
||||
.display-list
|
||||
&__item
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import Vue from 'vue'
|
||||
import App from './APP.vue'
|
||||
import 'melody.css'
|
||||
import axios from 'axios'
|
||||
|
||||
Vue.prototype.$http = axios
|
||||
|
||||
new Vue({
|
||||
render: h => h(App)
|
||||
|
||||
@@ -1,149 +1,83 @@
|
||||
{
|
||||
"name": "picgo",
|
||||
"version": "1.2.1",
|
||||
"author": "Molunerfinn <marksz@teamsz.xyz>",
|
||||
"description": "Easy to upload your pic & copy to write",
|
||||
"license": "MIT",
|
||||
"main": "./dist/electron/main.js",
|
||||
"version": "2.2.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "node .electron-vue/build.js && electron-builder",
|
||||
"release": "node .electron-vue/build.js && electron-builder",
|
||||
"build:dir": "node .electron-vue/build.js && electron-builder --dir",
|
||||
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
|
||||
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
|
||||
"dev": "node .electron-vue/dev-runner.js",
|
||||
"e2e": "npm run pack && mocha test/e2e",
|
||||
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
|
||||
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
|
||||
"pack": "npm run pack:main && npm run pack:renderer",
|
||||
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
|
||||
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
|
||||
"test": "npm run unit && npm run e2e",
|
||||
"unit": "karma start test/unit/karma.conf.js",
|
||||
"postinstall": "npm run lint:fix",
|
||||
"build:docs": "cross-env NODE_ENV=production webpack -p --config .electron-vue/webpack.docs.config.js",
|
||||
"docs": "webpack-dev-server --content-base docs/dist --config .electron-vue/webpack.docs.config.js --hot --inline",
|
||||
"patch": "npm version patch && git push origin master && git push origin --tags",
|
||||
"minor": "npm version minor && git push origin master && git push origin --tags",
|
||||
"major": "npm version major && git push origin master && git push origin --tags"
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
"electron:serve": "vue-cli-service electron:serve",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps",
|
||||
"cz": "git-cz",
|
||||
"bump": "bump-version",
|
||||
"release": "vue-cli-service electron:build --publish always"
|
||||
},
|
||||
"build": {
|
||||
"productName": "PicGo",
|
||||
"appId": "org.simulatedgreg.electron-vue",
|
||||
"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",
|
||||
"extendInfo": {
|
||||
"LSUIElement": 1
|
||||
}
|
||||
},
|
||||
"win": {
|
||||
"icon": "build/icons/icon.ico"
|
||||
},
|
||||
"linux": {
|
||||
"icon": "build/icons"
|
||||
"main": "background.js",
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-customizable"
|
||||
},
|
||||
"cz-customizable": {
|
||||
"config": "./node_modules/@picgo/bump-version/.cz-config.js"
|
||||
}
|
||||
},
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"./node_modules/@picgo/bump-version/commitlint-picgo"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.16.1",
|
||||
"element-ui": "^2.0.5",
|
||||
"fs-extra": "^4.0.2",
|
||||
"image-size": "^0.6.1",
|
||||
"axios": "^0.19.0",
|
||||
"core-js": "^3.3.2",
|
||||
"element-ui": "^2.13.0",
|
||||
"fix-path": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"keycode": "^2.2.0",
|
||||
"lodash-id": "^0.14.0",
|
||||
"lowdb": "^1.0.0",
|
||||
"md5": "^2.2.1",
|
||||
"melody.css": "^1.0.2",
|
||||
"qiniu": "^7.1.1",
|
||||
"request": "^2.83.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"vue": "^2.3.3",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-gallery": "^1.2.4",
|
||||
"vue-router": "^2.5.3",
|
||||
"vuex": "^2.3.1"
|
||||
"picgo": "^1.4.4",
|
||||
"vue": "^2.6.10",
|
||||
"vue-gallery": "^2.0.1",
|
||||
"vue-lazyload": "^1.2.6",
|
||||
"vue-router": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-minify-webpack-plugin": "^0.2.0",
|
||||
"babel-plugin-istanbul": "^4.1.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-register": "^6.24.1",
|
||||
"babili-webpack-plugin": "^0.1.2",
|
||||
"cfonts": "^1.1.3",
|
||||
"chai": "^4.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"cross-env": "^5.0.5",
|
||||
"css-loader": "^0.28.4",
|
||||
"del": "^3.0.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^1.7.5",
|
||||
"electron-builder": "^19.19.1",
|
||||
"electron-debug": "^1.4.0",
|
||||
"electron-devtools-installer": "^2.2.0",
|
||||
"eslint": "^4.4.1",
|
||||
"eslint-config-standard": "^10.2.1",
|
||||
"eslint-friendly-formatter": "^3.0.0",
|
||||
"eslint-loader": "^1.9.0",
|
||||
"eslint-plugin-html": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-node": "^5.1.1",
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"inject-loader": "^3.0.0",
|
||||
"karma": "^1.3.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-electron": "^5.1.1",
|
||||
"karma-mocha": "^1.2.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "^0.0.31",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"mocha": "^3.0.2",
|
||||
"multispinner": "^0.2.1",
|
||||
"node-loader": "^0.6.0",
|
||||
"pug": "^2.0.0-rc.4",
|
||||
"pug-loader": "^2.3.0",
|
||||
"require-dir": "^0.3.0",
|
||||
"spectron": "^3.7.1",
|
||||
"style-loader": "^0.18.2",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus-loader": "^3.0.1",
|
||||
"url-loader": "^0.5.9",
|
||||
"vue-html-loader": "^1.2.4",
|
||||
"vue-loader": "^13.0.5",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.4.2",
|
||||
"webpack": "^3.5.2",
|
||||
"webpack-dev-server": "^2.7.1",
|
||||
"webpack-hot-middleware": "^2.18.2",
|
||||
"webpack-merge": "^4.1.0"
|
||||
"@commitlint/cli": "^8.2.0",
|
||||
"@picgo/bump-version": "^1.0.3",
|
||||
"@types/fs-extra": "^8.0.1",
|
||||
"@types/inquirer": "^6.5.0",
|
||||
"@types/lowdb": "^1.0.9",
|
||||
"@types/node": "10.17.6",
|
||||
"@types/request-promise-native": "^1.0.17",
|
||||
"@vue/cli-plugin-babel": "^4.0.0",
|
||||
"@vue/cli-plugin-eslint": "^4.0.0",
|
||||
"@vue/cli-plugin-router": "^4.0.0",
|
||||
"@vue/cli-plugin-typescript": "^4.0.0",
|
||||
"@vue/cli-service": "^4.0.0",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"@vue/eslint-config-typescript": "^4.0.0",
|
||||
"commitizen": "^4.0.3",
|
||||
"conventional-changelog": "^3.1.18",
|
||||
"cz-customizable": "^6.2.0",
|
||||
"electron": "^6.0.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"husky": "^3.1.0",
|
||||
"stylus": "^0.54.7",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"typescript": "~3.7.3",
|
||||
"vue-cli-plugin-electron-builder": "^1.4.2",
|
||||
"vue-property-decorator": "^8.3.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/node": "12.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSServices</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSBackgroundColorName</key>
|
||||
<string>background</string>
|
||||
<key>NSBackgroundSystemColorName</key>
|
||||
<string>systemBlueColor</string>
|
||||
<key>NSIconName</key>
|
||||
<string>NSTouchBarShare</string>
|
||||
<key>NSMenuItem</key>
|
||||
<dict>
|
||||
<key>default</key>
|
||||
<string>Upload pictures with PicGo</string>
|
||||
</dict>
|
||||
<key>NSMessage</key>
|
||||
<string>runWorkflowAsService</string>
|
||||
<key>NSRequiredContext</key>
|
||||
<dict>
|
||||
<key>NSApplicationIdentifier</key>
|
||||
<string>com.apple.finder</string>
|
||||
</dict>
|
||||
<key>NSSendFileTypes</key>
|
||||
<array>
|
||||
<string>public.image</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,314 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AMApplicationBuild</key>
|
||||
<string>444.42</string>
|
||||
<key>AMApplicationVersion</key>
|
||||
<string>2.9</string>
|
||||
<key>AMDocumentVersion</key>
|
||||
<string>2</string>
|
||||
<key>actions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>action</key>
|
||||
<dict>
|
||||
<key>AMAccepts</key>
|
||||
<dict>
|
||||
<key>Container</key>
|
||||
<string>List</string>
|
||||
<key>Optional</key>
|
||||
<true/>
|
||||
<key>Types</key>
|
||||
<array>
|
||||
<string>com.apple.cocoa.string</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>AMActionVersion</key>
|
||||
<string>2.0.3</string>
|
||||
<key>AMApplication</key>
|
||||
<array>
|
||||
<string>自动操作</string>
|
||||
</array>
|
||||
<key>AMParameterProperties</key>
|
||||
<dict>
|
||||
<key>COMMAND_STRING</key>
|
||||
<dict/>
|
||||
<key>CheckedForUserDefaultShell</key>
|
||||
<dict/>
|
||||
<key>inputMethod</key>
|
||||
<dict/>
|
||||
<key>shell</key>
|
||||
<dict/>
|
||||
<key>source</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>AMProvides</key>
|
||||
<dict>
|
||||
<key>Container</key>
|
||||
<string>List</string>
|
||||
<key>Types</key>
|
||||
<array>
|
||||
<string>com.apple.cocoa.string</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>ActionBundlePath</key>
|
||||
<string>/System/Library/Automator/Run Shell Script.action</string>
|
||||
<key>ActionName</key>
|
||||
<string>运行 Shell 脚本</string>
|
||||
<key>ActionParameters</key>
|
||||
<dict>
|
||||
<key>COMMAND_STRING</key>
|
||||
<string>/Applications/PicGo.app/Contents/MacOS/PicGo upload "$@" > /dev/null 2>&1 &</string>
|
||||
<key>CheckedForUserDefaultShell</key>
|
||||
<true/>
|
||||
<key>inputMethod</key>
|
||||
<integer>1</integer>
|
||||
<key>shell</key>
|
||||
<string>/bin/bash</string>
|
||||
<key>source</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<key>BundleIdentifier</key>
|
||||
<string>com.apple.RunShellScript</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0.3</string>
|
||||
<key>CanShowSelectedItemsWhenRun</key>
|
||||
<false/>
|
||||
<key>CanShowWhenRun</key>
|
||||
<true/>
|
||||
<key>Category</key>
|
||||
<array>
|
||||
<string>AMCategoryUtilities</string>
|
||||
</array>
|
||||
<key>Class Name</key>
|
||||
<string>RunShellScriptAction</string>
|
||||
<key>InputUUID</key>
|
||||
<string>79609224-28DD-4ADE-AA8F-5A6C68C18C18</string>
|
||||
<key>Keywords</key>
|
||||
<array>
|
||||
<string>Shell</string>
|
||||
<string>脚本</string>
|
||||
<string>命令</string>
|
||||
<string>运行</string>
|
||||
<string>Unix</string>
|
||||
</array>
|
||||
<key>OutputUUID</key>
|
||||
<string>35CD6B4C-A616-4F89-8D76-DCD3249C5B4E</string>
|
||||
<key>UUID</key>
|
||||
<string>4350A83B-E7E6-4D2B-9768-B1D676CF58F3</string>
|
||||
<key>UnlocalizedApplications</key>
|
||||
<array>
|
||||
<string>Automator</string>
|
||||
</array>
|
||||
<key>arguments</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<integer>0</integer>
|
||||
<key>name</key>
|
||||
<string>inputMethod</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>0</string>
|
||||
</dict>
|
||||
<key>1</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<string></string>
|
||||
<key>name</key>
|
||||
<string>source</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
<key>2</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<false/>
|
||||
<key>name</key>
|
||||
<string>CheckedForUserDefaultShell</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>2</string>
|
||||
</dict>
|
||||
<key>3</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<string></string>
|
||||
<key>name</key>
|
||||
<string>COMMAND_STRING</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>3</string>
|
||||
</dict>
|
||||
<key>4</key>
|
||||
<dict>
|
||||
<key>default value</key>
|
||||
<string>/bin/sh</string>
|
||||
<key>name</key>
|
||||
<string>shell</string>
|
||||
<key>required</key>
|
||||
<string>0</string>
|
||||
<key>type</key>
|
||||
<string>0</string>
|
||||
<key>uuid</key>
|
||||
<string>4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>isViewVisible</key>
|
||||
<true/>
|
||||
<key>location</key>
|
||||
<string>449.000000:305.000000</string>
|
||||
<key>nibPath</key>
|
||||
<string>/System/Library/Automator/Run Shell Script.action/Contents/Resources/Base.lproj/main.nib</string>
|
||||
</dict>
|
||||
<key>isViewVisible</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>connectors</key>
|
||||
<dict/>
|
||||
<key>workflowMetaData</key>
|
||||
<dict>
|
||||
<key>applicationBundleID</key>
|
||||
<string>com.apple.finder</string>
|
||||
<key>applicationBundleIDsByPath</key>
|
||||
<dict>
|
||||
<key>/System/Library/CoreServices/Finder.app</key>
|
||||
<string>com.apple.finder</string>
|
||||
</dict>
|
||||
<key>applicationPath</key>
|
||||
<string>/System/Library/CoreServices/Finder.app</string>
|
||||
<key>applicationPaths</key>
|
||||
<array>
|
||||
<string>/System/Library/CoreServices/Finder.app</string>
|
||||
</array>
|
||||
<key>backgroundColor</key>
|
||||
<data>
|
||||
YnBsaXN0MDDUAQIDBAUGNjdYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVy
|
||||
VCR0b3ASAAGGoKoHCBMUFR4kKC8zVSRudWxs1QkKCwwNDg8QERJWJGNsYXNz
|
||||
W05TQ29sb3JOYW1lXE5TQ29sb3JTcGFjZV1OU0NhdGFsb2dOYW1lV05TQ29s
|
||||
b3KACYADEAaAAoAEVlN5c3RlbV8QD3N5c3RlbUJsdWVDb2xvctUWFwsYCRka
|
||||
GxwOXE5TQ29tcG9uZW50c1VOU1JHQl8QEk5TQ3VzdG9tQ29sb3JTcGFjZUcw
|
||||
IDAgMSAxTxARMCAwIDAuOTk4MTg4OTcyNQAQAYAFgAnTHyAJISIjVE5TSURV
|
||||
TlNJQ0MQB4AGgAjSJQkmJ1dOUy5kYXRhTxEMSAAADEhMaW5vAhAAAG1udHJS
|
||||
R0IgWFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAA
|
||||
AAAAAAAAAAD21gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGE
|
||||
AAAAbHd0cHQAAAHwAAAAFGJrcHQAAAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoA
|
||||
AAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJUAAAAcGRtZGQAAALEAAAAiHZ1
|
||||
ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAAFG1lYXMAAAQMAAAA
|
||||
JHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJUUkMAAAQ8
|
||||
AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
|
||||
ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAA
|
||||
AAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAA
|
||||
AAEWzFhZWiAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BY
|
||||
WVogAAAAAAAAYpkAALeFAAAY2lhZWiAAAAAAAAAkoAAAD4QAALbPZGVzYwAA
|
||||
AAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAWSUVDIGh0
|
||||
dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEgRGVm
|
||||
YXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2
|
||||
MTk2Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2lu
|
||||
ZyBDb25kaXRpb24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJl
|
||||
bmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAAE6T+ABRfLgAQzxQAA+3MAAQT
|
||||
CwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFzAAAAAAAAAAEAAAAA
|
||||
AAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAAAAAABAAA
|
||||
AAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
|
||||
AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA
|
||||
4ADlAOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFn
|
||||
AW4BdQF8AYMBiwGSAZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQC
|
||||
HQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1
|
||||
AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kE
|
||||
BgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6
|
||||
BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0G
|
||||
rwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8IMghG
|
||||
CFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEK
|
||||
Jwo9ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwq
|
||||
DEMMXAx1DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQO
|
||||
fw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1
|
||||
ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QT
|
||||
xRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxay
|
||||
FtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0a
|
||||
BBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1HR4dRx1w
|
||||
HZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
|
||||
SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4
|
||||
JWgllyXHJfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWsp
|
||||
nSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4W
|
||||
Lkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQz
|
||||
DTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgU
|
||||
OFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9
|
||||
oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5CMEJyQrVC90M6
|
||||
Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1J
|
||||
Y0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+T
|
||||
T91QJ1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9W
|
||||
XFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0n
|
||||
XXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBk
|
||||
lGTpZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/
|
||||
bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0
|
||||
FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnnekZ6pXsEe2N7wnwh
|
||||
fIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE
|
||||
44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
|
||||
jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+X
|
||||
Cpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBp
|
||||
oNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyq
|
||||
j6sCq3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSc
|
||||
tRO1irYBtnm28Ldot+C4WbjRuUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/
|
||||
er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJuco4
|
||||
yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V
|
||||
0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE
|
||||
4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHt
|
||||
nO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH
|
||||
+lf65/t3/Af8mP0p/br+S/7c/23//4AH0ikqKyxaJGNsYXNzbmFtZVgkY2xh
|
||||
c3Nlc11OU011dGFibGVEYXRhoystLlZOU0RhdGFYTlNPYmplY3TSKSowMVxO
|
||||
U0NvbG9yU3BhY2WiMi5cTlNDb2xvclNwYWNl0ikqNDVXTlNDb2xvcqI0Ll8Q
|
||||
D05TS2V5ZWRBcmNoaXZlctE4OVRyb290gAEACAARABoAIwAtADIANwBCAEgA
|
||||
UwBaAGYAcwCBAIkAiwCNAI8AkQCTAJoArAC3AMQAygDfAOcA+wD9AP8BAQEI
|
||||
AQ0BEwEVARcBGQEeASYNcg10DXkNhA2NDZsNnw2mDa8NtA3BDcQN0Q3WDd4N
|
||||
4Q3zDfYN+wAAAAAAAAIBAAAAAAAAADoAAAAAAAAAAAAAAAAAAA39
|
||||
</data>
|
||||
<key>backgroundColorName</key>
|
||||
<string>systemBlueColor</string>
|
||||
<key>inputTypeIdentifier</key>
|
||||
<string>com.apple.Automator.fileSystemObject.image</string>
|
||||
<key>outputTypeIdentifier</key>
|
||||
<string>com.apple.Automator.nothing</string>
|
||||
<key>presentationMode</key>
|
||||
<integer>15</integer>
|
||||
<key>processesInput</key>
|
||||
<integer>0</integer>
|
||||
<key>serviceApplicationBundleID</key>
|
||||
<string>com.apple.finder</string>
|
||||
<key>serviceApplicationPath</key>
|
||||
<string>/System/Library/CoreServices/Finder.app</string>
|
||||
<key>serviceInputTypeIdentifier</key>
|
||||
<string>com.apple.Automator.fileSystemObject.image</string>
|
||||
<key>serviceOutputTypeIdentifier</key>
|
||||
<string>com.apple.Automator.nothing</string>
|
||||
<key>serviceProcessesInput</key>
|
||||
<integer>0</integer>
|
||||
<key>systemImageName</key>
|
||||
<string>NSTouchBarShare</string>
|
||||
<key>useAutomaticInputType</key>
|
||||
<integer>0</integer>
|
||||
<key>workflowTypeIdentifier</key>
|
||||
<string>com.apple.Automator.servicesMenu</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="referrer" content="never">
|
||||
<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>PicGo</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but picgo-new 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>
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# require xclip(see http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212)
|
||||
command -v xclip >/dev/null 2>&1 || { echo >&1 "no xclip"; exit 1; }
|
||||
|
||||
# write image in clipboard to file (see http://unix.stackexchange.com/questions/145131/copy-image-from-clipboard-to-file)
|
||||
filePath=`xclip -selection clipboard -o 2>/dev/null | grep ^file:// | cut -c8-`
|
||||
if [ ! -n "$filePath" ] ;then
|
||||
if
|
||||
xclip -selection clipboard -target image/png -o >/dev/null 2>&1
|
||||
then
|
||||
xclip -selection clipboard -target image/png -o >$1 2>/dev/null
|
||||
echo $1
|
||||
else
|
||||
echo "no image"
|
||||
fi
|
||||
else
|
||||
echo $filePath
|
||||
fi
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 497 B |
|
After Width: | Height: | Size: 915 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 823 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 777 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,26 @@
|
||||
|
||||
param($imagePath)
|
||||
|
||||
# Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1
|
||||
|
||||
Add-Type -Assembly PresentationCore
|
||||
$img = [Windows.Clipboard]::GetImage()
|
||||
|
||||
if ($img -eq $null) {
|
||||
"no image"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
if (-not $imagePath) {
|
||||
"no image"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0)
|
||||
$stream = [IO.File]::Open($imagePath, "OpenOrCreate")
|
||||
$encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder
|
||||
$encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null
|
||||
$encoder.Save($stream) | out-null
|
||||
$stream.Dispose() | out-null
|
||||
|
||||
$imagePath
|
||||
@@ -0,0 +1,44 @@
|
||||
# Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1
|
||||
param($imagePath)
|
||||
|
||||
# https://github.com/PowerShell/PowerShell/issues/7233
|
||||
# fix the output encoding bug
|
||||
[console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
|
||||
|
||||
Add-Type -Assembly PresentationCore
|
||||
function main {
|
||||
$img = [Windows.Clipboard]::GetImage()
|
||||
|
||||
if ($img -eq $null) {
|
||||
"no image"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
if (-not $imagePath) {
|
||||
"no image"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0)
|
||||
$stream = [IO.File]::Open($imagePath, "OpenOrCreate")
|
||||
$encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder
|
||||
$encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null
|
||||
$encoder.Save($stream) | out-null
|
||||
$stream.Dispose() | out-null
|
||||
|
||||
$imagePath
|
||||
}
|
||||
|
||||
try {
|
||||
# For WIN10
|
||||
$file = Get-Clipboard -Format FileDropList
|
||||
if ($file -ne $null) {
|
||||
Convert-Path $file
|
||||
Exit 1
|
||||
}
|
||||
} catch {
|
||||
# For WIN7 WIN8 WIN10
|
||||
main
|
||||
}
|
||||
|
||||
main
|
||||
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
app,
|
||||
globalShortcut,
|
||||
protocol
|
||||
} from 'electron'
|
||||
import {
|
||||
createProtocol,
|
||||
installVueDevtools
|
||||
} from 'vue-cli-plugin-electron-builder/lib'
|
||||
import db from '#/datastore'
|
||||
import { IWindowList } from '~/main/apis/window/constants'
|
||||
import windowManager from '~/main/apis/window/windowManager'
|
||||
import {
|
||||
uploadChoosedFiles,
|
||||
uploadClipboardFiles
|
||||
} from '~/main/apis/uploader/api'
|
||||
import beforeOpen from '~/main/utils/beforeOpen'
|
||||
import updateChecker from '~/main/utils/updateChecker'
|
||||
import ipcList from '~/main/apis/eventCenter/ipcList'
|
||||
import busEventList from '~/main/apis/eventCenter/busEventList'
|
||||
import fixPath from 'fix-path'
|
||||
import { getUploadFiles } from '~/main/utils/handleArgv'
|
||||
import bus from '~/main/utils/eventBus'
|
||||
import {
|
||||
updateShortKeyFromVersion212
|
||||
} from '~/main/migrate'
|
||||
import shortKeyHandler from '~/main/apis/shortKey/shortKeyHandler'
|
||||
import server from '~/main/server/index'
|
||||
import {
|
||||
createTray
|
||||
} from '~/main/apis/app'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
|
||||
beforeOpen()
|
||||
|
||||
// fix the $PATH in macOS
|
||||
fixPath()
|
||||
|
||||
ipcList.listen()
|
||||
busEventList.listen()
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
let files = getUploadFiles(commandLine, workingDirectory)
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
uploadClipboardFiles()
|
||||
} else {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
}
|
||||
} else {
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
if (settingWindow.isMinimized()) {
|
||||
settingWindow.restore()
|
||||
}
|
||||
settingWindow.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
app.setAppUserModelId('com.molunerfinn.picgo')
|
||||
}
|
||||
|
||||
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
|
||||
process.env.XDG_CURRENT_DESKTOP = 'Unity'
|
||||
}
|
||||
|
||||
app.on('ready', async () => {
|
||||
createProtocol('picgo')
|
||||
if (isDevelopment && !process.env.IS_TEST) {
|
||||
// Install Vue Devtools
|
||||
try {
|
||||
await installVueDevtools()
|
||||
} catch (e) {
|
||||
console.error('Vue Devtools failed to install:', e.toString())
|
||||
}
|
||||
}
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
createTray()
|
||||
}
|
||||
db.set('needReload', false)
|
||||
updateChecker()
|
||||
// 不需要阻塞
|
||||
process.nextTick(() => {
|
||||
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
|
||||
shortKeyHandler.init()
|
||||
})
|
||||
server.startup()
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
let files = getUploadFiles()
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
uploadClipboardFiles()
|
||||
} else {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
createProtocol('picgo')
|
||||
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
}
|
||||
if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
}
|
||||
})
|
||||
|
||||
app.on('will-quit', () => {
|
||||
globalShortcut.unregisterAll()
|
||||
bus.removeAllListeners()
|
||||
server.shutdown()
|
||||
})
|
||||
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: db.get('settings.autoStart') || false
|
||||
})
|
||||
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', data => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit()
|
||||
server.shutdown()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit()
|
||||
server.shutdown()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto Updater
|
||||
*
|
||||
* Uncomment the following code below and install `electron-updater` to
|
||||
* support auto updating. Code Signing with a valid certificate is required.
|
||||
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
|
||||
*/
|
||||
|
||||
// import { autoUpdater } from 'electron-updater'
|
||||
|
||||
// autoUpdater.on('update-downloaded', () => {
|
||||
// autoUpdater.quitAndInstall()
|
||||
// })
|
||||
|
||||
// app.on('ready', () => {
|
||||
// if (process.env.NODE_ENV === 'production') {
|
||||
// autoUpdater.checkForUpdates()
|
||||
// }
|
||||
// })
|
||||
@@ -1,32 +0,0 @@
|
||||
import Datastore from 'lowdb'
|
||||
import LodashId from 'lodash-id'
|
||||
import FileSync from 'lowdb/adapters/FileSync'
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import { remote, app } from 'electron'
|
||||
|
||||
const APP = process.type === 'renderer' ? remote.app : app
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
|
||||
if (process.type !== 'renderer') {
|
||||
if (!fs.pathExistsSync(STORE_PATH)) {
|
||||
fs.mkdirpSync(STORE_PATH)
|
||||
}
|
||||
}
|
||||
|
||||
const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
|
||||
|
||||
const db = Datastore(adapter)
|
||||
db._.mixin(LodashId)
|
||||
|
||||
if (!db.has('uploaded').value()) {
|
||||
db.set('uploaded', []).write()
|
||||
}
|
||||
|
||||
if (!db.has('picBed').value()) {
|
||||
db.set('picBed', {
|
||||
current: 'weibo'
|
||||
}).write()
|
||||
}
|
||||
|
||||
export default db
|
||||
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="referrer" content="never">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>PicGo</title>
|
||||
<% if (htmlWebpackPlugin.options.nodeModules) { %>
|
||||
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
|
||||
<script>
|
||||
require('module').globalPaths.push("<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>")
|
||||
</script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- Set `__static` path to static files in production -->
|
||||
<script>
|
||||
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
|
||||
</script>
|
||||
|
||||
<!-- webpack builds are automatically injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,36 @@
|
||||
import Vue from 'vue'
|
||||
import App from './renderer/App.vue'
|
||||
import router from './renderer/router'
|
||||
import db from '#/datastore/index'
|
||||
import ElementUI from 'element-ui'
|
||||
import { webFrame } from 'electron'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import VueLazyLoad from 'vue-lazyload'
|
||||
import axios from 'axios'
|
||||
import mainMixin from './renderer/utils/mainMixin'
|
||||
|
||||
webFrame.setVisualZoomLevelLimits(1, 1)
|
||||
webFrame.setLayoutZoomLevelLimits(0, 0)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$builtInPicBed = [
|
||||
'smms',
|
||||
'weibo',
|
||||
'imgur',
|
||||
'qiniu',
|
||||
'tcyun',
|
||||
'upyun',
|
||||
'aliyun',
|
||||
'github'
|
||||
]
|
||||
Vue.prototype.$db = db
|
||||
Vue.prototype.$http = axios
|
||||
|
||||
Vue.use(ElementUI)
|
||||
Vue.use(VueLazyLoad)
|
||||
Vue.mixin(mainMixin)
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,206 @@
|
||||
import {
|
||||
app,
|
||||
Menu,
|
||||
Tray,
|
||||
dialog,
|
||||
clipboard,
|
||||
systemPreferences,
|
||||
Notification
|
||||
} from 'electron'
|
||||
import uploader from '~/main/apis/uploader'
|
||||
import getPicBeds from '~/main/utils/getPicBeds'
|
||||
import db from '#/datastore'
|
||||
import windowManager from '~/main/apis/window/windowManager'
|
||||
import { IWindowList } from '~/main/apis/window/constants'
|
||||
import picgo from '~/main/apis/picgo'
|
||||
import pasteTemplate from '#/utils/pasteTemplate'
|
||||
import pkg from 'root/package.json'
|
||||
let contextMenu: Menu | null
|
||||
let menu: Menu | null
|
||||
let tray: Tray | null
|
||||
export function createContextMenu () {
|
||||
const picBeds = getPicBeds()
|
||||
const submenu = picBeds.filter(item => item.visible).map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
type: 'radio',
|
||||
checked: db.get('picBed.current') === item.type,
|
||||
click () {
|
||||
picgo.saveConfig({
|
||||
'picBed.current': item.type
|
||||
})
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '关于',
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicGo',
|
||||
message: 'PicGo',
|
||||
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '选择默认图床',
|
||||
type: 'submenu',
|
||||
// @ts-ignore
|
||||
submenu
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
label: '打开更新助手',
|
||||
type: 'checkbox',
|
||||
checked: db.get('settings.showUpdateTip'),
|
||||
click () {
|
||||
const value = db.get('settings.showUpdateTip')
|
||||
db.set('settings.showUpdateTip', !value)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '重启应用',
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
export function createTray () {
|
||||
const menubarPic = process.platform === 'darwin' ? `${__static}/menubar.png` : `${__static}/menubar-nodarwin.png`
|
||||
tray = new Tray(menubarPic)
|
||||
tray.on('right-click', () => {
|
||||
if (windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
|
||||
}
|
||||
createContextMenu()
|
||||
tray!.popUpContextMenu(contextMenu!)
|
||||
})
|
||||
tray.on('click', (event, bounds) => {
|
||||
if (process.platform === 'darwin') {
|
||||
toggleWindow(bounds)
|
||||
setTimeout(() => {
|
||||
let img = clipboard.readImage()
|
||||
let obj: ImgInfo[] = []
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
// @ts-ignore
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
obj.push({
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
})
|
||||
}
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('clipboardFiles', obj)
|
||||
}, 0)
|
||||
} else {
|
||||
if (windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
|
||||
}
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
tray.on('drag-enter', () => {
|
||||
if (systemPreferences.isDarkMode()) {
|
||||
tray!.setImage(`${__static}/upload-dark.png`)
|
||||
} else {
|
||||
tray!.setImage(`${__static}/upload.png`)
|
||||
}
|
||||
})
|
||||
|
||||
tray.on('drag-end', () => {
|
||||
tray!.setImage(`${__static}/menubar.png`)
|
||||
})
|
||||
|
||||
tray.on('drop-files', async (event: Event, files: string[]) => {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const imgs = await uploader
|
||||
.setWebContents(trayWindow.webContents)
|
||||
.upload(files)
|
||||
if (imgs !== false) {
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i]))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl!,
|
||||
icon: files[i]
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.insert('uploaded', imgs[i])
|
||||
}
|
||||
trayWindow.webContents.send('dragFiles', imgs)
|
||||
}
|
||||
})
|
||||
// toggleWindow()
|
||||
}
|
||||
|
||||
export function createMenu () {
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
const template = [{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
|
||||
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
|
||||
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
|
||||
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
|
||||
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' },
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'CmdOrCtrl+Q',
|
||||
click () {
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]
|
||||
// @ts-ignore
|
||||
menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
}
|
||||
}
|
||||
|
||||
const toggleWindow = (bounds: IBounds) => {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
if (trayWindow.isVisible()) {
|
||||
trayWindow.hide()
|
||||
} else {
|
||||
trayWindow.setPosition(bounds.x - 98 + 11, bounds.y, false)
|
||||
trayWindow.webContents.send('updateFiles')
|
||||
trayWindow.show()
|
||||
trayWindow.focus()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const GET_WINDOW_ID = 'GET_WINDOW_ID' // get a current window
|
||||
export const GET_WINDOW_ID_REPONSE = 'GET_WINDOW_ID_REPONSE'
|
||||
export const GET_SETTING_WINDOW_ID = 'GET_SETTING_WINDOW_ID' // get setting window
|
||||
export const GET_SETTING_WINDOW_ID_RESPONSE = 'GET_SETTING_WINDOW_ID_RESPONSE'
|
||||
export const UPLOAD_WITH_FILES = 'UPLOAD_WITH_FILES'
|
||||
export const UPLOAD_WITH_FILES_RESPONSE = 'UPLOAD_WITH_FILES_RESPONSE'
|
||||
export const UPLOAD_WITH_CLIPBOARD_FILES = 'UPLOAD_WITH_CLIPBOARD_FILES'
|
||||
export const UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE = 'UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE'
|
||||
export const CREATE_APP_MENU = 'CREATE_APP_MENU'
|
||||
@@ -0,0 +1,74 @@
|
||||
import bus from '../../utils/eventBus'
|
||||
import {
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
|
||||
GET_WINDOW_ID,
|
||||
GET_WINDOW_ID_REPONSE,
|
||||
GET_SETTING_WINDOW_ID,
|
||||
GET_SETTING_WINDOW_ID_RESPONSE
|
||||
} from './constants'
|
||||
|
||||
export const uploadWithClipboardFiles = (): Promise<{
|
||||
success: boolean,
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
|
||||
if (result) {
|
||||
return resolve({
|
||||
success: true,
|
||||
result: [result]
|
||||
})
|
||||
} else {
|
||||
return resolve({
|
||||
success: false
|
||||
})
|
||||
}
|
||||
})
|
||||
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES)
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
success: boolean,
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
|
||||
if (result.length) {
|
||||
return resolve({
|
||||
success: true,
|
||||
result
|
||||
})
|
||||
} else {
|
||||
return resolve({
|
||||
success: false
|
||||
})
|
||||
}
|
||||
})
|
||||
bus.emit(UPLOAD_WITH_FILES, pathList)
|
||||
})
|
||||
}
|
||||
|
||||
// get available window id:
|
||||
// miniWindow or settingWindow or trayWindow
|
||||
export const getWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
bus.emit(GET_WINDOW_ID)
|
||||
})
|
||||
}
|
||||
|
||||
// get settingWindow id:
|
||||
export const getSettingWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
bus.emit(GET_SETTING_WINDOW_ID)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import bus from '~/main/utils/eventBus'
|
||||
import {
|
||||
uploadClipboardFiles,
|
||||
uploadChoosedFiles
|
||||
} from '~/main/apis/uploader/api'
|
||||
import {
|
||||
createMenu
|
||||
} from '~/main/apis/app'
|
||||
import { IWindowList } from '~/main/apis/window/constants'
|
||||
import windowManager from '~/main/apis/window/windowManager'
|
||||
import {
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
|
||||
GET_WINDOW_ID,
|
||||
GET_WINDOW_ID_REPONSE,
|
||||
GET_SETTING_WINDOW_ID,
|
||||
GET_SETTING_WINDOW_ID_RESPONSE,
|
||||
CREATE_APP_MENU
|
||||
} from '~/main/apis/bus/constants'
|
||||
function initEventCenter () {
|
||||
const eventList: any = {
|
||||
'picgo:upload': uploadClipboardFiles,
|
||||
[UPLOAD_WITH_CLIPBOARD_FILES]: busCallUploadClipboardFiles,
|
||||
[UPLOAD_WITH_FILES]: busCallUploadFiles,
|
||||
[GET_WINDOW_ID]: busCallGetWindowId,
|
||||
[GET_SETTING_WINDOW_ID]: busCallGetSettingWindowId,
|
||||
[CREATE_APP_MENU]: createMenu
|
||||
}
|
||||
for (let i in eventList) {
|
||||
bus.on(i, eventList[i])
|
||||
}
|
||||
}
|
||||
|
||||
async function busCallUploadClipboardFiles () {
|
||||
const imgUrl = await uploadClipboardFiles()
|
||||
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, imgUrl)
|
||||
}
|
||||
|
||||
async function busCallUploadFiles (pathList: IFileWithPath[]) {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
const urls = await uploadChoosedFiles(win.webContents, pathList)
|
||||
bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls)
|
||||
}
|
||||
|
||||
function busCallGetWindowId () {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
bus.emit(GET_WINDOW_ID_REPONSE, win.id)
|
||||
}
|
||||
|
||||
function busCallGetSettingWindowId () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
bus.emit(GET_SETTING_WINDOW_ID_RESPONSE, settingWindow.id)
|
||||
}
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
initEventCenter()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
import {
|
||||
app,
|
||||
ipcMain,
|
||||
clipboard,
|
||||
Notification,
|
||||
IpcMainEvent
|
||||
} from 'electron'
|
||||
import windowManager from '~/main/apis/window/windowManager'
|
||||
import { IWindowList } from '~/main/apis/window/constants'
|
||||
import uploader from '~/main/apis/uploader'
|
||||
import pasteTemplate from '#/utils/pasteTemplate'
|
||||
import db from '#/datastore'
|
||||
import server from '~/main/server'
|
||||
import getPicBeds from '~/main/utils/getPicBeds'
|
||||
import shortKeyHandler from '~/main/apis/shortKey/shortKeyHandler'
|
||||
import bus from '~/main/utils/eventBus'
|
||||
import {
|
||||
uploadClipboardFiles,
|
||||
uploadChoosedFiles
|
||||
} from '~/main/apis/uploader/api'
|
||||
import picgoCoreIPC from './picgoCoreIPC'
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
picgoCoreIPC.listen()
|
||||
// from macOS tray
|
||||
ipcMain.on('uploadClipboardFiles', async () => {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const img = await uploader.setWebContents(trayWindow.webContents).upload()
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl!,
|
||||
// icon: file[0]
|
||||
icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
db.insert('uploaded', img[0])
|
||||
trayWindow.webContents.send('clipboardFiles', [])
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||
}
|
||||
}
|
||||
trayWindow.webContents.send('uploadFiles')
|
||||
})
|
||||
|
||||
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
|
||||
ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: IFileWithPath[]) => {
|
||||
return uploadChoosedFiles(evt.sender, files)
|
||||
})
|
||||
|
||||
ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, oldKey: string, from: string) => {
|
||||
const result = shortKeyHandler.updateShortKey(item, oldKey, from)
|
||||
evt.sender.send('updateShortKeyResponse', result)
|
||||
if (result) {
|
||||
const notification = new Notification({
|
||||
title: '操作成功',
|
||||
body: '你的快捷键已经修改成功'
|
||||
})
|
||||
notification.show()
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: '操作失败',
|
||||
body: '快捷键冲突,请重新设置'
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('bindOrUnbindShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, from: string) => {
|
||||
const result = shortKeyHandler.bindOrUnbindShortKey(item, from)
|
||||
if (result) {
|
||||
const notification = new Notification({
|
||||
title: '操作成功',
|
||||
body: '你的快捷键已经修改成功'
|
||||
})
|
||||
notification.show()
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: '操作失败',
|
||||
body: '快捷键冲突,请重新设置'
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('updateCustomLink', () => {
|
||||
const notification = new Notification({
|
||||
title: '操作成功',
|
||||
body: '你的自定义链接格式已经修改成功'
|
||||
})
|
||||
notification.show()
|
||||
})
|
||||
|
||||
ipcMain.on('autoStart', (evt: IpcMainEvent, val: boolean) => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: val
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('openSettingWindow', () => {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.show()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('openMiniWindow', () => {
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
settingWindow.hide()
|
||||
})
|
||||
|
||||
// from mini window
|
||||
ipcMain.on('syncPicBed', () => {
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('getPicBeds', (evt: IpcMainEvent) => {
|
||||
const picBeds = getPicBeds()
|
||||
evt.sender.send('getPicBeds', picBeds)
|
||||
evt.returnValue = picBeds
|
||||
})
|
||||
|
||||
ipcMain.on('toggleShortKeyModifiedMode', (evt: IpcMainEvent, val: boolean) => {
|
||||
bus.emit('toggleShortKeyModifiedMode', val)
|
||||
})
|
||||
|
||||
ipcMain.on('updateServer', () => {
|
||||
server.restart()
|
||||
})
|
||||
},
|
||||
dispose () {}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import path from 'path'
|
||||
import GuiApi from '../gui'
|
||||
import {
|
||||
dialog,
|
||||
shell,
|
||||
IpcMainEvent,
|
||||
ipcMain,
|
||||
app
|
||||
} from 'electron'
|
||||
import PicGoCore from '~/universal/types/picgo'
|
||||
import { IPicGoHelperType } from '#/types/enum'
|
||||
import shortKeyHandler from '../shortKey/shortKeyHandler'
|
||||
import picgo from '~/main/apis/picgo'
|
||||
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
// const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
// const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
|
||||
|
||||
type PicGoNotice = {
|
||||
title: string,
|
||||
body: string[]
|
||||
}
|
||||
|
||||
interface GuiMenuItem {
|
||||
label: string
|
||||
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
|
||||
}
|
||||
|
||||
// get uploader or transformer config
|
||||
const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
|
||||
let config: any[] = []
|
||||
if (name === '') {
|
||||
return config
|
||||
} else {
|
||||
const handler = ctx.helper[type].get(name)
|
||||
if (handler) {
|
||||
if (handler.config) {
|
||||
config = handler.config(ctx)
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfigWithFunction = (config: any[]) => {
|
||||
for (let i in config) {
|
||||
if (typeof config[i].default === 'function') {
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
config[i].choices = config[i].choices()
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const handleGetPluginList = () => {
|
||||
ipcMain.on('getPluginList', (event: IpcMainEvent) => {
|
||||
const pluginList = picgo.pluginLoader.getList()
|
||||
const list = []
|
||||
for (let i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu = []
|
||||
if (plugin.guiMenu) {
|
||||
menu = plugin.guiMenu(picgo)
|
||||
}
|
||||
let gui = false
|
||||
if (pluginPKG.keywords && pluginPKG.keywords.length > 0) {
|
||||
if (pluginPKG.keywords.includes('picgo-gui-plugin')) {
|
||||
gui = true
|
||||
}
|
||||
}
|
||||
const obj: IPicGoPlugin = {
|
||||
name: pluginList[i].replace(/picgo-plugin-/, ''),
|
||||
author: pluginPKG.author.name || pluginPKG.author,
|
||||
description: pluginPKG.description,
|
||||
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
|
||||
version: pluginPKG.version,
|
||||
gui,
|
||||
config: {
|
||||
plugin: {
|
||||
name: pluginList[i].replace(/picgo-plugin-/, ''),
|
||||
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
|
||||
},
|
||||
uploader: {
|
||||
name: uploaderName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo))
|
||||
},
|
||||
transformer: {
|
||||
name: transformerName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo))
|
||||
}
|
||||
},
|
||||
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
|
||||
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
|
||||
guiMenu: menu,
|
||||
ing: false
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
event.sender.send('pluginList', list)
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginInstall = () => {
|
||||
ipcMain.on('installPlugin', async (event: IpcMainEvent, msg: string) => {
|
||||
picgo.once('installSuccess', (notice: PicGoNotice) => {
|
||||
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
shortKeyHandler.registerPluginShortKey(notice.body[0])
|
||||
picgo.removeAllListeners('installFailed')
|
||||
})
|
||||
picgo.once('installFailed', () => {
|
||||
handleNPMError()
|
||||
picgo.removeAllListeners('installSuccess')
|
||||
})
|
||||
await picgo.pluginHandler.install([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginUninstall = () => {
|
||||
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
|
||||
picgo.once('uninstallSuccess', (notice: PicGoNotice) => {
|
||||
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
shortKeyHandler.unregisterPluginShortKey(notice.body[0])
|
||||
picgo.removeAllListeners('uninstallFailed')
|
||||
})
|
||||
picgo.once('uninstallFailed', () => {
|
||||
handleNPMError()
|
||||
picgo.removeAllListeners('uninstallSuccess')
|
||||
})
|
||||
await picgo.pluginHandler.uninstall([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginUpdate = () => {
|
||||
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
|
||||
picgo.once('updateSuccess', (notice: { body: string[], title: string }) => {
|
||||
event.sender.send('updateSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
picgo.removeAllListeners('updateFailed')
|
||||
})
|
||||
picgo.once('updateFailed', () => {
|
||||
handleNPMError()
|
||||
picgo.removeAllListeners('updateSuccess')
|
||||
})
|
||||
await picgo.pluginHandler.update([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handleNPMError = () => {
|
||||
dialog.showMessageBox({
|
||||
title: '发生错误',
|
||||
message: '请安装Node.js并重启PicGo再继续操作',
|
||||
buttons: ['Yes']
|
||||
}).then((res) => {
|
||||
if (res.response === 0) {
|
||||
shell.openExternal('https://nodejs.org/')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleGetPicBedConfig = () => {
|
||||
ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
|
||||
const name = picgo.helper.uploader.get(type).name || type
|
||||
if (picgo.helper.uploader.get(type).config) {
|
||||
const config = handleConfigWithFunction(picgo.helper.uploader.get(type).config(picgo))
|
||||
event.sender.send('getPicBedConfig', config, name)
|
||||
} else {
|
||||
event.sender.send('getPicBedConfig', [], name)
|
||||
}
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginActions = () => {
|
||||
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
|
||||
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
|
||||
const guiApi = new GuiApi()
|
||||
if (plugin.guiMenu && plugin.guiMenu(picgo).length > 0) {
|
||||
const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
|
||||
menu.forEach(item => {
|
||||
if (item.label === label) {
|
||||
item.handle(picgo, guiApi)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoveFiles = () => {
|
||||
ipcMain.on('removeFiles', (event: IpcMainEvent, files: ImgInfo[]) => {
|
||||
const guiApi = new GuiApi()
|
||||
setTimeout(() => {
|
||||
picgo.emit('remove', files, guiApi)
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePicGoSaveData = () => {
|
||||
ipcMain.on('picgoSaveData', (event: IpcMainEvent, data: IObj) => {
|
||||
picgo.saveConfig(data)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
handleGetPluginList()
|
||||
handlePluginInstall()
|
||||
handlePluginUninstall()
|
||||
handlePluginUpdate()
|
||||
handleGetPicBedConfig()
|
||||
handlePluginActions()
|
||||
handleRemoveFiles()
|
||||
handlePicGoSaveData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import {
|
||||
dialog,
|
||||
BrowserWindow,
|
||||
clipboard,
|
||||
Notification,
|
||||
ipcMain
|
||||
} from 'electron'
|
||||
import db from '#/datastore'
|
||||
import uploader from '../uploader'
|
||||
import pasteTemplate from '#/utils/pasteTemplate'
|
||||
import {
|
||||
getWindowId,
|
||||
getSettingWindowId
|
||||
} from '~/main/apis/bus'
|
||||
|
||||
class GuiApi implements IGuiApi {
|
||||
private windowId: number = -1
|
||||
private settingWindowId: number = -1
|
||||
private async showSettingWindow () {
|
||||
this.settingWindowId = await getSettingWindowId()
|
||||
const settingWindow = BrowserWindow.fromId(this.settingWindowId)
|
||||
if (settingWindow.isVisible()) {
|
||||
return true
|
||||
}
|
||||
settingWindow.show()
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, 1000) // TODO: a better way to wait page loaded.
|
||||
})
|
||||
}
|
||||
|
||||
private getWebcontentsByWindowId (id: number) {
|
||||
return BrowserWindow.fromId(id).webContents
|
||||
}
|
||||
|
||||
async showInputBox (options: IShowInputBoxOption = {
|
||||
title: '',
|
||||
placeholder: ''
|
||||
}) {
|
||||
await this.showSettingWindow()
|
||||
this.getWebcontentsByWindowId(this.settingWindowId)
|
||||
.send('showInputBox', options)
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
ipcMain.once('showInputBox', (event: Event, value: string) => {
|
||||
resolve(value)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
showFileExplorer (options: IShowFileExplorerOption = {}) {
|
||||
return new Promise<string>(async (resolve, reject) => {
|
||||
this.windowId = await getWindowId()
|
||||
dialog.showOpenDialog(BrowserWindow.fromId(this.windowId), options, (filename: string) => {
|
||||
resolve(filename)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async upload (input: IUploadOption) {
|
||||
this.windowId = await getWindowId()
|
||||
const webContents = this.getWebcontentsByWindowId(this.windowId)
|
||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||
if (imgs !== false) {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
let pasteText = ''
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl as string,
|
||||
icon: imgs[i].imgUrl
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.insert('uploaded', imgs[i])
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
webContents.send('uploadFiles', imgs)
|
||||
webContents.send('updateGallery')
|
||||
return imgs
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
showNotification (options: IShowNotificationOption = {
|
||||
title: '',
|
||||
body: ''
|
||||
}) {
|
||||
const notification = new Notification({
|
||||
title: options.title,
|
||||
body: options.body
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
showMessageBox (options: IShowMessageBoxOption = {
|
||||
title: '',
|
||||
message: '',
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}) {
|
||||
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => {
|
||||
this.windowId = await getWindowId()
|
||||
dialog.showMessageBox(
|
||||
BrowserWindow.fromId(this.windowId),
|
||||
options
|
||||
).then((res) => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default GuiApi
|
||||
@@ -0,0 +1,19 @@
|
||||
import PicGoCore from '~/universal/types/picgo'
|
||||
import {
|
||||
app
|
||||
} from 'electron'
|
||||
import path from 'path'
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
const PicGo = requireFunc('picgo') as typeof PicGoCore
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
|
||||
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
|
||||
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
picgo.saveConfig({
|
||||
debug: true,
|
||||
PICGO_ENV: 'GUI'
|
||||
})
|
||||
|
||||
export default picgo as PicGoCore
|
||||
@@ -0,0 +1,171 @@
|
||||
import bus from '../../utils/eventBus'
|
||||
import PicGoCore from '~/universal/types/picgo'
|
||||
import path from 'path'
|
||||
import {
|
||||
app,
|
||||
globalShortcut,
|
||||
BrowserWindow
|
||||
} from 'electron'
|
||||
import logger from '../../utils/logger'
|
||||
import GuiApi from '../gui'
|
||||
import db from '#/datastore'
|
||||
import shortKeyService from './shortKeyService'
|
||||
import picgo from '../picgo'
|
||||
|
||||
class ShortKeyHandler {
|
||||
private isInModifiedMode: boolean = false
|
||||
constructor () {
|
||||
bus.on('toggleShortKeyModifiedMode', flag => {
|
||||
this.isInModifiedMode = flag
|
||||
})
|
||||
}
|
||||
init () {
|
||||
this.initBuiltInShortKey()
|
||||
this.initPluginsShortKey()
|
||||
}
|
||||
private initBuiltInShortKey () {
|
||||
const commands = db.get('settings.shortKey') as IShortKeyConfigs
|
||||
Object.keys(commands)
|
||||
.filter(item => item.includes('picgo:'))
|
||||
.map(command => {
|
||||
const config = commands[command]
|
||||
globalShortcut.register(config.key, () => {
|
||||
this.handler(command)
|
||||
})
|
||||
})
|
||||
}
|
||||
private initPluginsShortKey () {
|
||||
const pluginList = picgo.pluginLoader.getList()
|
||||
for (let item of pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(item)
|
||||
// if a plugin has commands
|
||||
if (plugin && plugin.commands) {
|
||||
if (typeof plugin.commands !== 'function') {
|
||||
logger.warn(`${item}'s commands is not a function`)
|
||||
continue
|
||||
}
|
||||
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
|
||||
for (let cmd of commands) {
|
||||
const command = `${item}:${cmd.name}`
|
||||
if (db.has(`settings.shortKey[${command}]`)) {
|
||||
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
|
||||
this.registerShortKey(commandConfig, command, cmd.handle, false)
|
||||
} else {
|
||||
this.registerShortKey(cmd, command, cmd.handle, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) {
|
||||
shortKeyService.registerCommand(command, handler)
|
||||
if (config.key) {
|
||||
globalShortcut.register(config.key, () => {
|
||||
this.handler(command)
|
||||
})
|
||||
} else {
|
||||
logger.warn(`${command} do not provide a key to bind`)
|
||||
}
|
||||
if (writeFlag) {
|
||||
picgo.saveConfig({
|
||||
[`settings.shortKey.${command}`]: {
|
||||
enable: true,
|
||||
name: config.name,
|
||||
label: config.label,
|
||||
key: config.key
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// enable or disable shortKey
|
||||
bindOrUnbindShortKey (item: IShortKeyConfig, from: string): boolean {
|
||||
const command = `${from}:${item.name}`
|
||||
if (item.enable === false) {
|
||||
globalShortcut.unregister(item.key)
|
||||
picgo.saveConfig({
|
||||
[`settings.shortKey.${command}.enable`]: false
|
||||
})
|
||||
return true
|
||||
} else {
|
||||
if (globalShortcut.isRegistered(item.key)) {
|
||||
return false
|
||||
} else {
|
||||
picgo.saveConfig({
|
||||
[`settings.shortKey.${command}.enable`]: true
|
||||
})
|
||||
globalShortcut.register(item.key, () => {
|
||||
this.handler(command)
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
// update shortKey bindings
|
||||
updateShortKey (item: IShortKeyConfig, oldKey: string, from: string): boolean {
|
||||
const command = `${from}:${item.name}`
|
||||
if (globalShortcut.isRegistered(item.key)) return false
|
||||
globalShortcut.unregister(oldKey)
|
||||
picgo.saveConfig({
|
||||
[`settings.shortKey.${command}.key`]: item.key
|
||||
})
|
||||
globalShortcut.register(item.key, () => {
|
||||
this.handler(`${from}:${item.name}`)
|
||||
})
|
||||
return true
|
||||
}
|
||||
private async handler (command: string) {
|
||||
if (this.isInModifiedMode) {
|
||||
return
|
||||
}
|
||||
if (command.includes('picgo:')) {
|
||||
bus.emit(command)
|
||||
} else if (command.includes('picgo-plugin-')) {
|
||||
const handler = shortKeyService.getShortKeyHandler(command)
|
||||
if (handler) {
|
||||
const guiApi = new GuiApi()
|
||||
return handler(picgo, guiApi)
|
||||
}
|
||||
} else {
|
||||
logger.warn(`can not find command: ${command}`)
|
||||
}
|
||||
}
|
||||
registerPluginShortKey (pluginName: string) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginName)
|
||||
if (plugin && plugin.commands) {
|
||||
if (typeof plugin.commands !== 'function') {
|
||||
logger.warn(`${pluginName}'s commands is not a function`)
|
||||
return
|
||||
}
|
||||
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
|
||||
for (let cmd of commands) {
|
||||
const command = `${pluginName}:${cmd.name}`
|
||||
if (db.has(`settings.shortKey[${command}]`)) {
|
||||
const commandConfig = db.get(`settings.shortKey[${command}]`) as IShortKeyConfig
|
||||
this.registerShortKey(commandConfig, command, cmd.handle, false)
|
||||
} else {
|
||||
this.registerShortKey(cmd, command, cmd.handle, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unregisterPluginShortKey (pluginName: string) {
|
||||
const commands = db.get('settings.shortKey') as IShortKeyConfigs
|
||||
const keyList = Object.keys(commands)
|
||||
.filter(command => command.includes(pluginName))
|
||||
.map(command => {
|
||||
return {
|
||||
command,
|
||||
key: commands[command].key
|
||||
}
|
||||
}) as IKeyCommandType[]
|
||||
keyList.forEach(item => {
|
||||
globalShortcut.unregister(item.key)
|
||||
shortKeyService.unregisterCommand(item.command)
|
||||
picgo.unsetConfig('settings.shortKey', item.command)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new ShortKeyHandler()
|
||||
@@ -0,0 +1,21 @@
|
||||
import logger from '../../utils/logger'
|
||||
class ShortKeyService {
|
||||
private commandList: Map<string, IShortKeyHandler> = new Map()
|
||||
registerCommand (command: string, handler: IShortKeyHandler) {
|
||||
this.commandList.set(command, handler)
|
||||
}
|
||||
unregisterCommand (command: string) {
|
||||
this.commandList.delete(command)
|
||||
}
|
||||
getShortKeyHandler (command: string): IShortKeyHandler | null {
|
||||
const handler = this.commandList.get(command)
|
||||
if (handler) return handler
|
||||
logger.warn(`cannot find command: ${command}`)
|
||||
return null
|
||||
}
|
||||
getCommandList () {
|
||||
return [...this.commandList.keys()]
|
||||
}
|
||||
}
|
||||
|
||||
export default new ShortKeyService()
|
||||
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
clipboard,
|
||||
Notification,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import windowManager from '~/main/apis/window/windowManager'
|
||||
import { IWindowList } from '~/main/apis/window/constants'
|
||||
import uploader from './'
|
||||
import pasteTemplate from '#/utils/pasteTemplate'
|
||||
import db from '#/datastore'
|
||||
export const uploadClipboardFiles = async (): Promise<string> => {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
let img = await uploader.setWebContents(win!.webContents).upload()
|
||||
if (img !== false) {
|
||||
if (img.length > 0) {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl!,
|
||||
icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
db.insert('uploaded', img[0])
|
||||
trayWindow.webContents.send('clipboardFiles', [])
|
||||
trayWindow.webContents.send('uploadFiles', img)
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||
}
|
||||
return img[0].imgUrl as string
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: '上传不成功',
|
||||
body: '你剪贴板最新的一条记录不是图片哦'
|
||||
})
|
||||
notification.show()
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<string[]> => {
|
||||
const input = files.map(item => item.path)
|
||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||
const result = []
|
||||
if (imgs !== false) {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
let pasteText = ''
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl!,
|
||||
icon: files[i].path
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.insert('uploaded', imgs[i])
|
||||
result.push(imgs[i].imgUrl!)
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('uploadFiles', imgs)
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
app,
|
||||
Notification,
|
||||
BrowserWindow,
|
||||
ipcMain,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import dayjs from 'dayjs'
|
||||
import picgo from '~/main/apis/picgo'
|
||||
import db from '#/datastore'
|
||||
import windowManager from '~/main/apis/window/windowManager'
|
||||
import { IWindowList } from '~/main/apis/window/constants'
|
||||
|
||||
const waitForShow = (webcontent: WebContents) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
webcontent.on('did-finish-load', () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const windowId = window.id
|
||||
ipcMain.once(`rename${id}`, (evt: Event, newName: string) => {
|
||||
resolve(newName)
|
||||
window.close()
|
||||
})
|
||||
window.on('close', () => {
|
||||
resolve(null)
|
||||
ipcMain.removeAllListeners(`rename${id}`)
|
||||
windowManager.deleteById(windowId)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
class Uploader {
|
||||
private webContents: WebContents | null = null
|
||||
constructor () {
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {
|
||||
picgo.on('notification', message => {
|
||||
const notification = new Notification(message)
|
||||
notification.show()
|
||||
})
|
||||
|
||||
picgo.on('uploadProgress', progress => {
|
||||
this.webContents?.send('uploadProgress', progress)
|
||||
})
|
||||
picgo.on('beforeTransform', ctx => {
|
||||
if (db.get('settings.uploadNotification')) {
|
||||
const notification = new Notification({
|
||||
title: '上传进度',
|
||||
body: '正在上传'
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
picgo.helper.beforeUploadPlugins.register('renameFn', {
|
||||
handle: async ctx => {
|
||||
const rename = db.get('settings.rename')
|
||||
const autoRename = db.get('settings.autoRename')
|
||||
await Promise.all(ctx.output.map(async (item, index) => {
|
||||
let name: undefined | string | null
|
||||
let fileName: string | undefined
|
||||
if (autoRename) {
|
||||
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
|
||||
} else {
|
||||
fileName = item.fileName
|
||||
}
|
||||
if (rename) {
|
||||
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
||||
await waitForShow(window.webContents)
|
||||
window.webContents.send('rename', fileName, window.webContents.id)
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
item.fileName = name || fileName
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setWebContents (webContents: WebContents) {
|
||||
this.webContents = webContents
|
||||
return this
|
||||
}
|
||||
|
||||
upload (img?: IUploadOption): Promise<ImgInfo[]|false> {
|
||||
picgo.upload(img)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
picgo.once('finished', ctx => {
|
||||
if (ctx.output.every((item: ImgInfo) => item.imgUrl)) {
|
||||
resolve(ctx.output)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
picgo.removeAllListeners('failed')
|
||||
})
|
||||
picgo.once('failed', ctx => {
|
||||
setTimeout(() => {
|
||||
const notification = new Notification({
|
||||
title: '上传失败',
|
||||
body: '请检查配置和上传的文件是否符合要求'
|
||||
})
|
||||
notification.show()
|
||||
}, 500)
|
||||
picgo.removeAllListeners('finished')
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new Uploader()
|
||||
@@ -0,0 +1,27 @@
|
||||
export enum IWindowList {
|
||||
SETTING_WINDOW = 'SETTING_WINDOW',
|
||||
TRAY_WINDOW = 'TRAY_WINDOW',
|
||||
MINI_WINDOW = 'MINI_WINDOW',
|
||||
RENAME_WINDOW = 'RENAME_WINDOW'
|
||||
}
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
export const TRAY_WINDOW_URL = isDevelopment
|
||||
? (process.env.WEBPACK_DEV_SERVER_URL as string)
|
||||
: `picgo://./index.html`
|
||||
|
||||
export const SETTING_WINDOW_URL = isDevelopment
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#setting/upload`
|
||||
: `picgo://./index.html#setting/upload`
|
||||
|
||||
export const MINI_WINDOW_URL = isDevelopment
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
|
||||
: `picgo://./index.html#mini-page`
|
||||
|
||||
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
|
||||
: `picgo://./index.html#rename-page`
|
||||
|
||||
export const DELETE_WINDOW_EVENT = 'DELETE_WINDOW_EVENT'
|
||||
export const CREATE_WINDOW_EVENT = 'CREATE_WINDOW_EVENT'
|
||||
@@ -0,0 +1,181 @@
|
||||
import {
|
||||
IWindowList,
|
||||
DELETE_WINDOW_EVENT,
|
||||
CREATE_WINDOW_EVENT,
|
||||
SETTING_WINDOW_URL,
|
||||
TRAY_WINDOW_URL,
|
||||
MINI_WINDOW_URL,
|
||||
RENAME_WINDOW_URL
|
||||
} from './constants'
|
||||
import { IWindowListItem } from '#/types/electron'
|
||||
import bus from '~/main/utils/eventBus'
|
||||
import db from '#/datastore'
|
||||
import { getWindowId } from '~/main/apis/bus'
|
||||
import { BrowserWindow, app } from 'electron'
|
||||
import { CREATE_APP_MENU } from '~/main/apis/bus/constants'
|
||||
|
||||
const windowList = new Map<IWindowList, IWindowListItem>()
|
||||
|
||||
windowList.set(IWindowList.TRAY_WINDOW, {
|
||||
isValid: process.platform !== 'linux',
|
||||
multiple: false,
|
||||
options () {
|
||||
return {
|
||||
height: 350,
|
||||
width: 196, // 196
|
||||
show: false,
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
transparent: true,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
backgroundThrottling: false
|
||||
}
|
||||
}
|
||||
},
|
||||
callback (window) {
|
||||
const id = window!.id
|
||||
window!.loadURL(TRAY_WINDOW_URL)
|
||||
window!.on('closed', () => {
|
||||
bus.emit(DELETE_WINDOW_EVENT, id)
|
||||
})
|
||||
|
||||
window!.on('blur', () => {
|
||||
window!.hide()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
windowList.set(IWindowList.SETTING_WINDOW, {
|
||||
isValid: true,
|
||||
multiple: false,
|
||||
options () {
|
||||
const options: IBrowserWindowOptions = {
|
||||
height: 450,
|
||||
width: 800,
|
||||
show: false,
|
||||
frame: true,
|
||||
center: true,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
title: 'PicGo',
|
||||
vibrancy: 'ultra-dark',
|
||||
transparent: true,
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false
|
||||
}
|
||||
}
|
||||
if (process.platform !== 'darwin') {
|
||||
options.show = false
|
||||
options.frame = false
|
||||
options.backgroundColor = '#3f3c37'
|
||||
options.transparent = false
|
||||
options.icon = `${__static}/logo.png`
|
||||
}
|
||||
return options
|
||||
},
|
||||
callback (window) {
|
||||
const id = window!.id
|
||||
window!.loadURL(SETTING_WINDOW_URL)
|
||||
window!.on('closed', () => {
|
||||
bus.emit('toggleShortKeyModifiedMode', false)
|
||||
bus.emit(DELETE_WINDOW_EVENT, id)
|
||||
if (process.platform === 'linux') {
|
||||
process.nextTick(() => {
|
||||
app.quit()
|
||||
})
|
||||
}
|
||||
})
|
||||
bus.emit(CREATE_APP_MENU)
|
||||
bus.emit(CREATE_WINDOW_EVENT, IWindowList.MINI_WINDOW)
|
||||
}
|
||||
})
|
||||
|
||||
windowList.set(IWindowList.MINI_WINDOW, {
|
||||
isValid: process.platform !== 'darwin',
|
||||
multiple: false,
|
||||
options () {
|
||||
let obj: IBrowserWindowOptions = {
|
||||
height: 64,
|
||||
width: 64,
|
||||
show: process.platform === 'linux',
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
skipTaskbar: true,
|
||||
resizable: false,
|
||||
transparent: process.platform !== 'linux',
|
||||
icon: `${__static}/logo.png`,
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true
|
||||
}
|
||||
}
|
||||
|
||||
if (db.get('settings.miniWindowOntop')) {
|
||||
obj.alwaysOnTop = true
|
||||
}
|
||||
return obj
|
||||
},
|
||||
callback (window) {
|
||||
const id = window!.id
|
||||
window!.loadURL(MINI_WINDOW_URL)
|
||||
window!.on('closed', () => {
|
||||
bus.emit(DELETE_WINDOW_EVENT, id)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
windowList.set(IWindowList.RENAME_WINDOW, {
|
||||
isValid: true,
|
||||
multiple: true,
|
||||
options () {
|
||||
let options: IBrowserWindowOptions = {
|
||||
height: 175,
|
||||
width: 300,
|
||||
show: true,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
backgroundThrottling: false
|
||||
}
|
||||
}
|
||||
if (process.platform !== 'darwin') {
|
||||
options.show = true
|
||||
options.backgroundColor = '#3f3c37'
|
||||
options.autoHideMenuBar = true
|
||||
options.transparent = false
|
||||
}
|
||||
return options
|
||||
},
|
||||
async callback (window) {
|
||||
window!.loadURL(RENAME_WINDOW_URL)
|
||||
const currentWindowId = await getWindowId()
|
||||
const currentWindow = BrowserWindow.fromId(currentWindowId)
|
||||
if (currentWindow && currentWindow.isVisible()) {
|
||||
// bounds: { x: 821, y: 75, width: 800, height: 450 }
|
||||
const bounds = currentWindow.getBounds()
|
||||
const positionX = bounds.x + bounds.width / 2 - 150
|
||||
let positionY
|
||||
// if is the settingWindow
|
||||
if (bounds.height > 400) {
|
||||
positionY = bounds.y + bounds.height / 2 - 88
|
||||
} else { // if is the miniWindow
|
||||
positionY = bounds.y + bounds.height / 2
|
||||
}
|
||||
window!.setPosition(positionX, positionY, false)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default windowList
|
||||
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
BrowserWindow
|
||||
} from 'electron'
|
||||
import { IWindowManager, IWindowListItem } from '#/types/electron'
|
||||
import windowList from './windowList'
|
||||
import {
|
||||
IWindowList,
|
||||
DELETE_WINDOW_EVENT,
|
||||
CREATE_WINDOW_EVENT
|
||||
} from './constants'
|
||||
import bus from '~/main/utils/eventBus'
|
||||
|
||||
class WindowManager implements IWindowManager {
|
||||
private windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
||||
private windowIdMap: Map<number, IWindowList | string> = new Map()
|
||||
constructor () {
|
||||
bus.on(DELETE_WINDOW_EVENT, this.deleteById)
|
||||
bus.on(CREATE_WINDOW_EVENT, this.create)
|
||||
}
|
||||
create = (name: IWindowList) => {
|
||||
const windowConfig: IWindowListItem = windowList.get(name)!
|
||||
if (windowConfig.isValid) {
|
||||
if (!windowConfig.multiple) {
|
||||
if (this.windowMap.has(name)) return this.windowMap.get(name)!
|
||||
}
|
||||
const window = new BrowserWindow(windowConfig.options())
|
||||
if (windowConfig.multiple) {
|
||||
this.windowMap.set(`${name}_${window.id}`, window)
|
||||
this.windowIdMap.set(window.id, `${name}_${window.id}`)
|
||||
} else {
|
||||
this.windowMap.set(name, window)
|
||||
this.windowIdMap.set(window.id, name)
|
||||
}
|
||||
windowConfig.callback(window)
|
||||
return window
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
get (name: IWindowList) {
|
||||
if (this.windowMap.has(name)) {
|
||||
return this.windowMap.get(name)!
|
||||
} else {
|
||||
const window = this.create(name)
|
||||
return window
|
||||
}
|
||||
}
|
||||
has (name: IWindowList) {
|
||||
return this.windowMap.has(name)
|
||||
}
|
||||
delete = (name: IWindowList) => {
|
||||
const window = this.windowMap.get(name)
|
||||
if (window) {
|
||||
this.windowIdMap.delete(window.id)
|
||||
this.windowMap.delete(name)
|
||||
}
|
||||
}
|
||||
deleteById = (id: number) => {
|
||||
const name = this.windowIdMap.get(id)
|
||||
if (name) {
|
||||
this.windowMap.delete(name)
|
||||
this.windowIdMap.delete(id)
|
||||
}
|
||||
}
|
||||
getAvailableWindow () {
|
||||
const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW)
|
||||
if (miniWindow && miniWindow.isVisible()) {
|
||||
return miniWindow
|
||||
} else {
|
||||
const settingWindow = this.windowMap.get(IWindowList.SETTING_WINDOW)
|
||||
const trayWindow = this.windowMap.get(IWindowList.TRAY_WINDOW)
|
||||
return settingWindow || trayWindow || this.create(IWindowList.SETTING_WINDOW)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new WindowManager()
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* This file is used specifically and only for development. It installs
|
||||
* `electron-debug` & `vue-devtools`. There shouldn't be any need to
|
||||
* modify this file, but it can be used to extend your development
|
||||
* environment.
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// Set environment for development
|
||||
process.env.NODE_ENV = 'development'
|
||||
|
||||
// Install `electron-debug` with `devtron`
|
||||
require('electron-debug')({ showDevTools: false })
|
||||
|
||||
// Install `vue-devtools`
|
||||
require('electron').app.on('ready', () => {
|
||||
let installExtension = require('electron-devtools-installer')
|
||||
installExtension.default(installExtension.VUEJS_DEVTOOLS)
|
||||
.then(() => {})
|
||||
.catch(err => {
|
||||
console.log('Unable to install `vue-devtools`: \n', err)
|
||||
})
|
||||
})
|
||||
|
||||
// Require `main` process to boot app
|
||||
require('./index')
|
||||
@@ -1,398 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import uploader from './utils/uploader.js'
|
||||
import { app, BrowserWindow, Tray, Menu, Notification, clipboard, ipcMain, globalShortcut, dialog } from 'electron'
|
||||
import db from '../datastore'
|
||||
import pasteTemplate from './utils/pasteTemplate'
|
||||
import updateChecker from './utils/updateChecker'
|
||||
import pkg from '../../package.json'
|
||||
/**
|
||||
* Set `__static` path to static files in production
|
||||
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
|
||||
*/
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
|
||||
}
|
||||
|
||||
let window
|
||||
let settingWindow
|
||||
let tray
|
||||
let menu
|
||||
const winURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080`
|
||||
: `file://${__dirname}/index.html`
|
||||
const settingWinURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080/#setting/upload`
|
||||
: `file://${__dirname}/index.html#setting/upload`
|
||||
|
||||
const uploadFailed = () => {
|
||||
const notification = new Notification({
|
||||
title: '上传失败',
|
||||
body: '请检查你的图床配置!'
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
function createTray () {
|
||||
tray = new Tray(`${__static}/menubar.png`)
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '关于',
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicGo',
|
||||
message: 'PicGo',
|
||||
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
if (settingWindow === null) {
|
||||
createSettingWindow()
|
||||
settingWindow.show()
|
||||
} else {
|
||||
settingWindow.show()
|
||||
settingWindow.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '选择默认图床',
|
||||
type: 'submenu',
|
||||
submenu: [
|
||||
{
|
||||
label: '微博图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'weibo',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'weibo')
|
||||
.write()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '七牛图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'qiniu',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'qiniu')
|
||||
.write()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '腾讯云COS',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'tcyun',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'tcyun')
|
||||
.write()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '又拍云图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'upyun',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'upyun')
|
||||
.write()
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
])
|
||||
tray.on('right-click', () => {
|
||||
window.hide()
|
||||
tray.popUpContextMenu(contextMenu)
|
||||
})
|
||||
tray.on('click', () => {
|
||||
let img = clipboard.readImage()
|
||||
let obj = []
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
obj.push({
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
})
|
||||
}
|
||||
toggleWindow()
|
||||
setTimeout(() => {
|
||||
window.webContents.send('clipboardFiles', obj)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
tray.on('drag-enter', () => {
|
||||
tray.setImage(`${__static}/upload.png`)
|
||||
})
|
||||
|
||||
tray.on('drag-end', () => {
|
||||
tray.setImage(`${__static}/menubar.png`)
|
||||
})
|
||||
|
||||
tray.on('drop-files', async (event, files) => {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
const imgs = await uploader(files, 'imgFromPath', window.webContents)
|
||||
if (imgs !== false) {
|
||||
for (let i in imgs) {
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i].imgUrl))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
icon: files[i]
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
}
|
||||
window.webContents.send('dragFiles', imgs)
|
||||
} else {
|
||||
uploadFailed()
|
||||
}
|
||||
})
|
||||
// toggleWindow()
|
||||
}
|
||||
|
||||
const createWindow = () => {
|
||||
window = new BrowserWindow({
|
||||
height: 350,
|
||||
width: 196, // 196
|
||||
show: false,
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
transparent: true,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
})
|
||||
|
||||
window.loadURL(winURL)
|
||||
|
||||
window.on('closed', () => {
|
||||
window = null
|
||||
})
|
||||
|
||||
window.on('blur', () => {
|
||||
window.hide()
|
||||
})
|
||||
|
||||
createSettingWindow()
|
||||
createMenu()
|
||||
}
|
||||
|
||||
const createSettingWindow = () => {
|
||||
settingWindow = new BrowserWindow({
|
||||
height: 450,
|
||||
width: 800,
|
||||
show: false,
|
||||
frame: true,
|
||||
center: true,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
title: 'Pic',
|
||||
vibrancy: 'ultra-dark',
|
||||
transparent: true,
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
})
|
||||
|
||||
settingWindow.loadURL(settingWinURL)
|
||||
|
||||
settingWindow.on('closed', () => {
|
||||
settingWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
const createMenu = () => {
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
const template = [{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
|
||||
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
|
||||
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
|
||||
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
|
||||
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' },
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'CmdOrCtrl+Q',
|
||||
click () {
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]
|
||||
menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
}
|
||||
}
|
||||
|
||||
const getWindowPosition = () => {
|
||||
const windowBounds = window.getBounds()
|
||||
const trayBounds = tray.getBounds()
|
||||
const x = Math.round(trayBounds.x + (trayBounds.width / 2) - (windowBounds.width / 2))
|
||||
const y = Math.round(trayBounds.y + trayBounds.height - 10)
|
||||
|
||||
return {
|
||||
x,
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
const toggleWindow = () => {
|
||||
if (window.isVisible()) {
|
||||
window.hide()
|
||||
} else {
|
||||
showWindow()
|
||||
}
|
||||
}
|
||||
|
||||
const showWindow = () => {
|
||||
const position = getWindowPosition()
|
||||
window.setPosition(position.x, position.y, false)
|
||||
window.webContents.send('updateFiles')
|
||||
window.show()
|
||||
window.focus()
|
||||
}
|
||||
|
||||
const uploadClipboardFiles = async () => {
|
||||
let img = clipboard.readImage()
|
||||
let uploadImg = null
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
uploadImg = {
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
}
|
||||
}
|
||||
img = await uploader(uploadImg, 'imgFromClipboard', window.webContents)
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl,
|
||||
icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
window.webContents.send('clipboardFiles', [])
|
||||
window.webContents.send('uploadFiles', img)
|
||||
} else {
|
||||
uploadFailed()
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
|
||||
const img = await uploader(file, 'imgFromClipboard', window.webContents)
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl,
|
||||
// icon: file[0]
|
||||
icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
window.webContents.send('clipboardFiles', [])
|
||||
window.webContents.send('uploadFiles', img)
|
||||
} else {
|
||||
uploadFailed()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
|
||||
const imgs = await uploader(files, 'imgFromUploader', settingWindow.webContents)
|
||||
if (imgs !== false) {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
let pasteText = ''
|
||||
for (let i in imgs) {
|
||||
pasteText += pasteTemplate(pasteStyle, imgs[i].imgUrl) + '\r\n'
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
icon: files[i].path
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
window.webContents.send('uploadFiles', imgs)
|
||||
} else {
|
||||
uploadFailed()
|
||||
}
|
||||
})
|
||||
|
||||
const isSecondInstance = app.makeSingleInstance(() => {
|
||||
if (settingWindow) {
|
||||
if (settingWindow.isMinimized()) {
|
||||
settingWindow.restore()
|
||||
}
|
||||
settingWindow.focus()
|
||||
}
|
||||
})
|
||||
|
||||
if (isSecondInstance) {
|
||||
console.log('is 2')
|
||||
app.quit()
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
createTray()
|
||||
updateChecker()
|
||||
|
||||
globalShortcut.register('CommandOrControl+Shift+P', () => {
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (window === null || settingWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('will-quit', () => {
|
||||
globalShortcut.unregisterAll()
|
||||
})
|
||||
|
||||
/**
|
||||
* Auto Updater
|
||||
*
|
||||
* Uncomment the following code below and install `electron-updater` to
|
||||
* support auto updating. Code Signing with a valid certificate is required.
|
||||
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
|
||||
*/
|
||||
|
||||
// import { autoUpdater } from 'electron-updater'
|
||||
|
||||
// autoUpdater.on('update-downloaded', () => {
|
||||
// autoUpdater.quitAndInstall()
|
||||
// })
|
||||
|
||||
// app.on('ready', () => {
|
||||
// if (process.env.NODE_ENV === 'production') {
|
||||
// autoUpdater.checkForUpdates()
|
||||
// }
|
||||
// })
|
||||
@@ -0,0 +1,26 @@
|
||||
import DB from '#/datastore'
|
||||
// from v2.1.2
|
||||
const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
|
||||
let needUpgrade = false
|
||||
if (shortKeyConfig.upload) {
|
||||
needUpgrade = true
|
||||
// @ts-ignore
|
||||
shortKeyConfig['picgo:upload'] = {
|
||||
enable: true,
|
||||
key: shortKeyConfig.upload,
|
||||
name: 'upload',
|
||||
label: '快捷上传'
|
||||
}
|
||||
delete shortKeyConfig.upload
|
||||
}
|
||||
if (needUpgrade) {
|
||||
db.set('settings.shortKey', shortKeyConfig)
|
||||
return shortKeyConfig
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
updateShortKeyFromVersion212
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import http from 'http'
|
||||
import routers from './routerManager'
|
||||
import {
|
||||
handleResponse
|
||||
} from './utils'
|
||||
import picgo from '~/main/apis/picgo'
|
||||
import logger from '~/main/utils/logger'
|
||||
|
||||
class Server {
|
||||
private httpServer: http.Server
|
||||
private config: IServerConfig
|
||||
constructor () {
|
||||
let config = picgo.getConfig('settings.server')
|
||||
const result = this.checkIfConfigIsValid(config)
|
||||
if (result) {
|
||||
this.config = config
|
||||
} else {
|
||||
config = {
|
||||
port: 36677,
|
||||
host: '127.0.0.1',
|
||||
enable: true
|
||||
}
|
||||
this.config = config
|
||||
picgo.saveConfig({
|
||||
'settings.server': config
|
||||
})
|
||||
}
|
||||
this.httpServer = http.createServer(this.handleRequest)
|
||||
}
|
||||
private checkIfConfigIsValid (config: IObj | undefined) {
|
||||
if (config && config.port && config.host && (config.enable !== undefined)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
if (request.method === 'POST') {
|
||||
if (!routers.getHandler(request.url!)) {
|
||||
handleResponse({
|
||||
response,
|
||||
statusCode: 404,
|
||||
header: {},
|
||||
body: {
|
||||
success: false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let body: string = ''
|
||||
let postObj: IObj
|
||||
request.on('data', chunk => {
|
||||
body += chunk
|
||||
})
|
||||
request.on('end', () => {
|
||||
try {
|
||||
postObj = (body === '') ? {} : JSON.parse(body)
|
||||
} catch (err) {
|
||||
return handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: false,
|
||||
message: 'Not sending data in JSON format'
|
||||
}
|
||||
})
|
||||
}
|
||||
const handler = routers.getHandler(request.url!)
|
||||
handler!({
|
||||
...postObj,
|
||||
response
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
response.statusCode = 404
|
||||
response.end()
|
||||
}
|
||||
}
|
||||
private listen = (port: number) => {
|
||||
logger.info(`[PicGo Server] is listening at ${port}`)
|
||||
this.httpServer.listen(port, this.config.host).on('error', (err: ErrnoException) => {
|
||||
if (err.errno === 'EADDRINUSE') {
|
||||
logger.warn(`[PicGo Server] ${port} is busy, trying with port ${port + 1}`)
|
||||
this.config.port += 1
|
||||
picgo.saveConfig({
|
||||
'settings.server': this.config
|
||||
})
|
||||
this.listen(this.config.port)
|
||||
}
|
||||
})
|
||||
}
|
||||
startup () {
|
||||
if (this.config.enable) {
|
||||
this.listen(this.config.port)
|
||||
}
|
||||
}
|
||||
shutdown () {
|
||||
this.httpServer.close()
|
||||
logger.info('[PicGo Server] shutdown')
|
||||
}
|
||||
restart () {
|
||||
this.config = picgo.getConfig('settings.server')
|
||||
this.shutdown()
|
||||
this.startup()
|
||||
}
|
||||
}
|
||||
|
||||
export default new Server()
|
||||
@@ -0,0 +1,20 @@
|
||||
class Router {
|
||||
private router = new Map<string, routeHandler>()
|
||||
|
||||
get (url: string, callback: routeHandler): void {
|
||||
this.router.set(url, callback)
|
||||
}
|
||||
post (url: string, callback: routeHandler): void {
|
||||
this.router.set(url, callback)
|
||||
}
|
||||
|
||||
getHandler (url: string) {
|
||||
if (this.router.has(url)) {
|
||||
return this.router.get(url)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Router()
|
||||
@@ -0,0 +1,69 @@
|
||||
import router from './router'
|
||||
import {
|
||||
uploadWithClipboardFiles,
|
||||
uploadWithFiles
|
||||
} from '~/main/apis/bus/index'
|
||||
import {
|
||||
handleResponse
|
||||
} from './utils'
|
||||
import logger from '../utils/logger'
|
||||
|
||||
router.get('/upload', async ({
|
||||
response,
|
||||
list = []
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
list?: string[]
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
if (list.length === 0) {
|
||||
// upload with clipboard
|
||||
const res = await uploadWithClipboardFiles()
|
||||
if (res.success) {
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: true,
|
||||
result: res.result
|
||||
}
|
||||
})
|
||||
} else {
|
||||
handleResponse({
|
||||
response
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// upload with files
|
||||
const pathList = list.map(item => {
|
||||
return {
|
||||
path: item
|
||||
}
|
||||
})
|
||||
const res = await uploadWithFiles(pathList)
|
||||
if (res.success) {
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: true,
|
||||
result: res.result
|
||||
}
|
||||
})
|
||||
} else {
|
||||
handleResponse({
|
||||
response
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err)
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
success: false,
|
||||
message: err
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,19 @@
|
||||
export const handleResponse = ({
|
||||
response,
|
||||
statusCode = 200,
|
||||
header = {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body = {
|
||||
success: false
|
||||
}
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
statusCode?: number,
|
||||
header?: IObj,
|
||||
body?: any
|
||||
}) => {
|
||||
response.writeHead(statusCode, header)
|
||||
response.write(JSON.stringify(body))
|
||||
response.end()
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import { remote, app } from 'electron'
|
||||
|
||||
const APP = process.type === 'renderer' ? remote.app : app
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
|
||||
function beforeOpen () {
|
||||
if (process.platform === 'darwin') {
|
||||
resolveMacWorkFlow()
|
||||
}
|
||||
resolveClipboardImageGenerator()
|
||||
}
|
||||
|
||||
/**
|
||||
* macOS 右键菜单
|
||||
*/
|
||||
function resolveMacWorkFlow () {
|
||||
const dest = `${os.homedir}/Library/Services/Upload pictures with PicGo.workflow`
|
||||
if (fs.existsSync(dest)) {
|
||||
return true
|
||||
} else {
|
||||
try {
|
||||
fs.copySync(path.join(__static, 'Upload pictures with PicGo.workflow'), dest)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化剪贴板生成图片的脚本
|
||||
*/
|
||||
function resolveClipboardImageGenerator () {
|
||||
let clipboardFiles = getClipboardFiles()
|
||||
if (!fs.pathExistsSync(path.join(STORE_PATH, 'windows10.ps1'))) {
|
||||
clipboardFiles.forEach(item => {
|
||||
fs.copyFileSync(item.origin, item.dest)
|
||||
})
|
||||
} else {
|
||||
clipboardFiles.forEach(item => {
|
||||
diffFilesAndUpdate(item.origin, item.dest)
|
||||
})
|
||||
}
|
||||
|
||||
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
|
||||
let file1 = fs.readFileSync(filePath1)
|
||||
let file2 = fs.readFileSync(filePath2)
|
||||
|
||||
if (!file1.equals(file2)) {
|
||||
fs.copyFileSync(filePath1, filePath2)
|
||||
}
|
||||
}
|
||||
|
||||
function getClipboardFiles () {
|
||||
let files = [
|
||||
'/linux.sh',
|
||||
'/mac.applescript',
|
||||
'/windows.ps1',
|
||||
'/windows10.ps1'
|
||||
]
|
||||
|
||||
return files.map(item => {
|
||||
return {
|
||||
origin: path.join(__static, item),
|
||||
dest: path.join(STORE_PATH, item)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default beforeOpen
|
||||
@@ -0,0 +1,5 @@
|
||||
import { EventEmitter } from 'events'
|
||||
|
||||
const bus = new EventEmitter()
|
||||
|
||||
export default bus
|
||||
@@ -0,0 +1,17 @@
|
||||
import picgo from '../apis/picgo'
|
||||
|
||||
const getPicBeds = () => {
|
||||
const picBedTypes = picgo.helper.uploader.getIdList()
|
||||
const picBedFromDB = picgo.getConfig('picBed.list') || []
|
||||
const picBeds = picBedTypes.map((item: string) => {
|
||||
const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined
|
||||
return {
|
||||
type: item,
|
||||
name: picgo.helper.uploader.get(item).name || item,
|
||||
visible: visible ? visible.visible : true
|
||||
}
|
||||
}) as IPicBedType[]
|
||||
return picBeds
|
||||
}
|
||||
|
||||
export default getPicBeds
|
||||
@@ -0,0 +1,41 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
type ClipboardFileObject = {
|
||||
path: string
|
||||
}
|
||||
type Result = ClipboardFileObject[]
|
||||
const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
|
||||
let files = argv.slice(1)
|
||||
if (files.length > 0 && files[0] === 'upload') {
|
||||
if (files.length === 1) {
|
||||
return null // for uploading images in clipboard
|
||||
} else if (files.length > 1) {
|
||||
files = argv.slice(1)
|
||||
let result: Result = []
|
||||
if (files.length > 0) {
|
||||
result = files.map(item => {
|
||||
if (path.isAbsolute(item)) {
|
||||
return {
|
||||
path: item
|
||||
}
|
||||
} else {
|
||||
let tempPath = path.join(cwd, item)
|
||||
if (fs.existsSync(tempPath)) {
|
||||
return {
|
||||
path: tempPath
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}).filter(item => item !== null) as Result
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export {
|
||||
getUploadFiles
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import sizeOf from 'image-size'
|
||||
|
||||
const imgFromPath = async (imgPath) => {
|
||||
let results = []
|
||||
await Promise.all(imgPath.map(async item => {
|
||||
let buffer = await fs.readFile(item)
|
||||
let base64Image = Buffer.from(buffer, 'binary').toString('base64')
|
||||
let fileName = path.basename(item)
|
||||
let imgSize = sizeOf(item)
|
||||
results.push({
|
||||
base64Image,
|
||||
fileName,
|
||||
width: imgSize.width,
|
||||
height: imgSize.height
|
||||
})
|
||||
}))
|
||||
return results
|
||||
}
|
||||
|
||||
const imgFromClipboard = (file) => {
|
||||
let result = []
|
||||
if (file !== null) {
|
||||
const today = new Date().toLocaleString().replace(/[ ]+/g, '-').replace(/\/+/g, '-')
|
||||
result.push({
|
||||
base64Image: file.imgUrl.replace(/^data\S+,/, ''),
|
||||
fileName: `${today}.png`,
|
||||
width: file.width,
|
||||
height: file.height
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const imgFromUploader = async (files) => {
|
||||
let results = []
|
||||
await Promise.all(files.map(async item => {
|
||||
let buffer = await fs.readFile(item.path)
|
||||
let base64Image = Buffer.from(buffer, 'binary').toString('base64')
|
||||
let fileName = item.name
|
||||
let imgSize = sizeOf(item.path)
|
||||
results.push({
|
||||
base64Image,
|
||||
fileName,
|
||||
width: imgSize.width,
|
||||
height: imgSize.height
|
||||
})
|
||||
}))
|
||||
return results
|
||||
}
|
||||
|
||||
export {
|
||||
imgFromPath,
|
||||
imgFromClipboard,
|
||||
imgFromUploader
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import chalk from 'chalk'
|
||||
import dayjs from 'dayjs'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import util from 'util'
|
||||
import db from '#/datastore'
|
||||
import { app } from 'electron'
|
||||
import { IChalkType } from '#/types/enum'
|
||||
const baseDir = app.getPath('userData')
|
||||
|
||||
class Logger {
|
||||
private level = {
|
||||
success: IChalkType.success,
|
||||
info: IChalkType.info,
|
||||
warn: IChalkType.warn,
|
||||
error: IChalkType.error
|
||||
}
|
||||
protected handleLog (type: ILogType, msg: ILoggerType): ILoggerType {
|
||||
// if configPath is invalid then this.ctx.config === undefined
|
||||
// if not then check config.silent
|
||||
const log = chalk[this.level[type]](`[PicGo ${type.toUpperCase()}]:`)
|
||||
console.log(log, msg)
|
||||
process.nextTick(() => {
|
||||
this.handleWriteLog(type, msg)
|
||||
})
|
||||
return msg
|
||||
}
|
||||
|
||||
protected handleWriteLog (type: string, msg: ILoggerType): void {
|
||||
try {
|
||||
const logLevel = db.get('settings.logLevel')
|
||||
const logPath = db.get('settings.logPath') || path.join(baseDir, './picgo.log')
|
||||
if (this.checkLogLevel(type, logLevel)) {
|
||||
const picgoLog = fs.createWriteStream(logPath, { flags: 'a', encoding: 'utf8' })
|
||||
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ${type.toUpperCase()}] ${msg}`
|
||||
const logger = new console.Console(picgoLog)
|
||||
if (typeof msg === 'object' && type === 'error') {
|
||||
log += `\n------Error Stack Begin------\n${util.format(msg.stack)}\n-------Error Stack End-------`
|
||||
}
|
||||
logger.log(log)
|
||||
picgoLog.destroy()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
protected checkLogLevel (type: string, level: undefined | string | string[]): boolean {
|
||||
if (level === undefined || level === 'all') {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(level)) {
|
||||
return level.some((item: string) => (item === type || item === 'all'))
|
||||
} else {
|
||||
return type === level
|
||||
}
|
||||
}
|
||||
|
||||
success (msg: ILoggerType): ILoggerType {
|
||||
return this.handleLog('success', msg)
|
||||
}
|
||||
|
||||
info (msg: ILoggerType): ILoggerType {
|
||||
return this.handleLog('info', msg)
|
||||
}
|
||||
|
||||
error (msg: ILoggerType): ILoggerType {
|
||||
return this.handleLog('error', msg)
|
||||
}
|
||||
|
||||
warn (msg: ILoggerType): ILoggerType {
|
||||
return this.handleLog('warn', msg)
|
||||
}
|
||||
}
|
||||
|
||||
export default new Logger()
|
||||
@@ -1,9 +0,0 @@
|
||||
export default (style, url) => {
|
||||
const tpl = {
|
||||
'markdown': ``,
|
||||
'HTML': `<img src="${url}"/>`,
|
||||
'URL': url,
|
||||
'UBB': `[IMG]${url}[/IMG]`
|
||||
}
|
||||
return tpl[style]
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import * as qiniu from 'qiniu'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
function postOptions (fileName, token, imgBase64) {
|
||||
const area = selectArea(db.read().get('picBed.qiniu.area').value() || 'z0')
|
||||
const base64FileName = Buffer.from(fileName, 'utf-8').toString('base64').replace(/\+/g, '-').replace(/\//g, '_')
|
||||
return {
|
||||
method: 'POST',
|
||||
url: `http://upload${area}.qiniu.com/putb64/-1/key/${base64FileName}`,
|
||||
headers: {
|
||||
Authorization: `UpToken ${token}`,
|
||||
contentType: 'application/octet-stream'
|
||||
},
|
||||
body: imgBase64
|
||||
}
|
||||
}
|
||||
|
||||
function selectArea (area) {
|
||||
return area === 'z0' ? '' : '-' + area
|
||||
}
|
||||
|
||||
function getToken () {
|
||||
const accessKey = db.read().get('picBed.qiniu.accessKey').value()
|
||||
const secretKey = db.read().get('picBed.qiniu.secretKey').value()
|
||||
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
|
||||
const options = {
|
||||
scope: db.read().get('picBed.qiniu.bucket').value()
|
||||
}
|
||||
const putPolicy = new qiniu.rs.PutPolicy(options)
|
||||
return putPolicy.uploadToken(mac)
|
||||
}
|
||||
|
||||
const qiniuUpload = async function (img, type, webContents) {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
const length = imgList.length
|
||||
for (let i in imgList) {
|
||||
const options = postOptions(imgList[i].fileName, getToken(), imgList[i].base64Image)
|
||||
const res = await request(options)
|
||||
const body = JSON.parse(res)
|
||||
if (body.key) {
|
||||
delete imgList[i].base64Image
|
||||
const baseUrl = db.get('picBed.qiniu.url').value()
|
||||
const options = db.get('picBed.qiniu.options').value()
|
||||
imgList[i]['imgUrl'] = `${baseUrl}/${body.key}${options}`
|
||||
imgList[i]['type'] = 'qiniu'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const error = JSON.parse(err.response.body)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: error.error
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default qiniuUpload
|
||||
@@ -1,90 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification, clipboard } from 'electron'
|
||||
import crypto from 'crypto'
|
||||
|
||||
// generate COS signature string
|
||||
const generateSignature = () => {
|
||||
const options = db.read().get('picBed.tcyun').value()
|
||||
const secretId = options.secretId
|
||||
const secretKey = options.secretKey
|
||||
const appId = options.appId
|
||||
const bucket = options.bucket
|
||||
const random = Math.floor(Math.random() * 10000000000)
|
||||
const current = parseInt(new Date().getTime() / 1000) - 1
|
||||
const expired = current + 3600
|
||||
|
||||
const multiSignature = `a=${appId}&b=${bucket}&k=${secretId}&e=${expired}&t=${current}&r=${random}&f=`
|
||||
|
||||
const signHexKey = crypto.createHmac('sha1', secretKey).update(multiSignature).digest()
|
||||
const tempString = Buffer.concat([signHexKey, Buffer.from(multiSignature)])
|
||||
const signature = Buffer.from(tempString).toString('base64')
|
||||
return {
|
||||
signature,
|
||||
appId,
|
||||
bucket
|
||||
}
|
||||
}
|
||||
|
||||
const postOptions = (fileName, signature, imgBase64) => {
|
||||
const area = db.read().get('picBed.tcyun.area').value()
|
||||
const path = db.read().get('picBed.tcyun.path').value()
|
||||
return {
|
||||
method: 'POST',
|
||||
url: `http://${area}.file.myqcloud.com/files/v2/${signature.appId}/${signature.bucket}/${path}${fileName}`,
|
||||
headers: {
|
||||
Host: `${area}.file.myqcloud.com`,
|
||||
Authorization: signature.signature,
|
||||
contentType: 'multipart/form-data'
|
||||
},
|
||||
formData: {
|
||||
op: 'upload',
|
||||
filecontent: Buffer.from(imgBase64, 'base64')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tcYunUpload = async (img, type, webContents) => {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
const singature = generateSignature()
|
||||
const length = imgList.length
|
||||
for (let i in imgList) {
|
||||
const options = postOptions(imgList[i].fileName, singature, imgList[i].base64Image)
|
||||
const res = await request(options)
|
||||
const body = JSON.parse(res)
|
||||
if (body.message === 'SUCCESS') {
|
||||
delete imgList[i].base64Image
|
||||
imgList[i]['imgUrl'] = body.data.source_url
|
||||
imgList[i]['type'] = 'tcyun'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
const body = JSON.parse(err.error)
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: `错误码:${body.code},请打开浏览器粘贴地址查看相关原因。`
|
||||
})
|
||||
notification.show()
|
||||
clipboard.writeText('https://cloud.tencent.com/document/product/436/8432')
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default tcYunUpload
|
||||
@@ -1,80 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification, clipboard } from 'electron'
|
||||
import crypto from 'crypto'
|
||||
import MD5 from 'md5'
|
||||
|
||||
// generate COS signature string
|
||||
const generateSignature = (fileName) => {
|
||||
const options = db.read().get('picBed.upyun').value()
|
||||
// const apiKey = options.apiKey
|
||||
const operator = options.operator
|
||||
const password = options.password
|
||||
const md5Password = MD5(password)
|
||||
const date = new Date().toGMTString()
|
||||
const uri = `/${options.bucket}/${encodeURI(fileName)}`
|
||||
const value = `PUT&${uri}&${date}`
|
||||
const sign = crypto.createHmac('sha1', md5Password).update(value).digest('base64')
|
||||
return `UPYUN ${operator}:${sign}`
|
||||
}
|
||||
|
||||
const postOptions = (fileName, signature, imgBase64) => {
|
||||
const options = db.read().get('picBed.upyun').value()
|
||||
const bucket = options.bucket
|
||||
return {
|
||||
method: 'PUT',
|
||||
url: `https://v0.api.upyun.com/${bucket}/${encodeURI(fileName)}`,
|
||||
headers: {
|
||||
Authorization: signature,
|
||||
Date: new Date().toGMTString()
|
||||
},
|
||||
body: Buffer.from(imgBase64, 'base64'),
|
||||
resolveWithFullResponse: true
|
||||
}
|
||||
}
|
||||
|
||||
const upYunUpload = async (img, type, webContents) => {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
const length = imgList.length
|
||||
const upyunOptions = db.read().get('picBed.upyun').value()
|
||||
for (let i in imgList) {
|
||||
const singature = generateSignature(imgList[i].fileName)
|
||||
const options = postOptions(imgList[i].fileName, singature, imgList[i].base64Image)
|
||||
const body = await request(options)
|
||||
if (body.statusCode === 200) {
|
||||
delete imgList[i].base64Image
|
||||
imgList[i]['imgUrl'] = `${upyunOptions.url}/${imgList[i].fileName}${upyunOptions.options}`
|
||||
imgList[i]['type'] = 'upyun'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '服务端出错,请稍后再试'
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
const body = JSON.parse(err.error)
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: `错误码:${body.code},请打开浏览器粘贴地址查看相关原因。`
|
||||
})
|
||||
notification.show()
|
||||
clipboard.writeText('http://docs.upyun.com/api/errno/')
|
||||
// throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default upYunUpload
|
||||
@@ -1,35 +1,44 @@
|
||||
import { dialog, shell } from 'electron'
|
||||
import db from '../../datastore'
|
||||
import db from '#/datastore'
|
||||
import axios from 'axios'
|
||||
import pkg from '../../../package.json'
|
||||
import pkg from 'root/package.json'
|
||||
const version = pkg.version
|
||||
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||
const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||
const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json'
|
||||
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
|
||||
|
||||
const checkVersion = async () => {
|
||||
let showTip = db.read().get('picBed.showUpdateTip').value()
|
||||
let showTip = db.get('settings.showUpdateTip')
|
||||
if (showTip === undefined) {
|
||||
db.read().set('picBed.showUpdateTip', true).write()
|
||||
db.set('settings.showUpdateTip', true)
|
||||
showTip = true
|
||||
}
|
||||
if (showTip) {
|
||||
const res = await axios.get(release)
|
||||
let res: any
|
||||
try {
|
||||
res = await axios.get(releaseUrl).catch(async () => {
|
||||
const result = await axios.get(releaseUrlBackup)
|
||||
return result
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
if (res.status === 200) {
|
||||
const latest = res.data.name
|
||||
const latest = res.data.version || res.data.name
|
||||
const result = compareVersion2Update(version, latest)
|
||||
if (result) {
|
||||
dialog.showMessageBox({
|
||||
type: 'info',
|
||||
title: '发现新版本',
|
||||
buttons: ['Yes', 'No'],
|
||||
message: '发现新版本,是否去下载最新的版本?',
|
||||
message: `发现新版本${latest},更新了很多功能,是否去下载最新的版本?`,
|
||||
checkboxLabel: '以后不再提醒',
|
||||
checkboxChecked: false
|
||||
}, (res, checkboxChecked) => {
|
||||
if (res === 0) { // if selected yes
|
||||
}).then(res => {
|
||||
if (res.response === 0) { // if selected yes
|
||||
shell.openExternal(downloadUrl)
|
||||
}
|
||||
db.read().set('picBed.showUpdateTip', !checkboxChecked).write()
|
||||
db.set('settings.showUpdateTip', !res.checkboxChecked)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
@@ -41,18 +50,19 @@ const checkVersion = async () => {
|
||||
}
|
||||
|
||||
// if true -> update else return false
|
||||
const compareVersion2Update = (current, latest) => {
|
||||
const compareVersion2Update = (current: string, latest: string) => {
|
||||
const currentVersion = current.split('.').map(item => parseInt(item))
|
||||
const latestVersion = latest.split('.').map(item => parseInt(item))
|
||||
let flag = false
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (currentVersion[i] < latestVersion[i]) {
|
||||
flag = true
|
||||
return true
|
||||
}
|
||||
if (currentVersion[i] > latestVersion[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return flag
|
||||
return false
|
||||
}
|
||||
|
||||
export default checkVersion
|
||||
@@ -1,34 +0,0 @@
|
||||
import weiboUpload from './weiboUpload'
|
||||
import qiniuUpload from './qiniuUpload'
|
||||
import tcYunUpload from './tcYunUpload'
|
||||
import upYunUpload from './upYunUpload'
|
||||
import db from '../../datastore/index'
|
||||
|
||||
const checkUploader = (type) => {
|
||||
const currentUploader = db.read().get(`picBed.${type}`).value()
|
||||
if (currentUploader) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const uploader = (img, type, webContents) => {
|
||||
const uploadType = db.read().get('picBed.current').value()
|
||||
if (checkUploader(uploadType)) {
|
||||
switch (uploadType) {
|
||||
case 'weibo':
|
||||
return weiboUpload(img, type, webContents)
|
||||
case 'qiniu':
|
||||
return qiniuUpload(img, type, webContents)
|
||||
case 'tcyun':
|
||||
return tcYunUpload(img, type, webContents)
|
||||
case 'upyun':
|
||||
return upYunUpload(img, type, webContents)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export default uploader
|
||||
@@ -1,73 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification } from 'electron'
|
||||
const j = request.jar()
|
||||
const rp = request.defaults({jar: j})
|
||||
const UPLOAD_URL = 'http://picupload.service.weibo.com/interface/pic_upload.php?ori=1&mime=image%2Fjpeg&data=base64&url=0&markpos=1&logo=&nick=0&marks=1&app=miniblog'
|
||||
|
||||
const postOptions = (formData) => {
|
||||
return {
|
||||
method: 'POST',
|
||||
url: 'https://passport.weibo.cn/sso/login',
|
||||
headers: {
|
||||
Referer: 'https://passport.weibo.cn/signin/login',
|
||||
contentType: 'application/x-www-form-urlencoded'
|
||||
},
|
||||
formData,
|
||||
json: true,
|
||||
resolveWithFullResponse: true
|
||||
}
|
||||
}
|
||||
|
||||
const weiboUpload = async function (img, type, webContents) {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const formData = {
|
||||
username: db.read().get('picBed.weibo.username').value(),
|
||||
password: db.read().get('picBed.weibo.password').value()
|
||||
}
|
||||
const quality = db.read().get('picBed.weibo.quality').value()
|
||||
const options = postOptions(formData)
|
||||
const res = await rp(options)
|
||||
webContents.send('uploadProgress', 30)
|
||||
if (res.body.retcode === 20000000) {
|
||||
for (let i in res.body.data.crossdomainlist) {
|
||||
await rp.get(res.body.data.crossdomainlist[i])
|
||||
}
|
||||
webContents.send('uploadProgress', 60)
|
||||
const imgList = await img2Base64[type](img)
|
||||
for (let i in imgList) {
|
||||
let result = await rp.post(UPLOAD_URL, {
|
||||
formData: {
|
||||
b64_data: imgList[i].base64Image
|
||||
}
|
||||
})
|
||||
result = result.replace(/<.*?\/>/, '').replace(/<(\w+).*?>.*?<\/\1>/, '')
|
||||
delete imgList[i].base64Image
|
||||
const resTextJson = JSON.parse(result)
|
||||
imgList[i]['imgUrl'] = `https://ws1.sinaimg.cn/${quality}/${resTextJson.data.pics.pic_1.pid}`
|
||||
imgList[i]['type'] = 'weibo'
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '服务端出错,请重试'
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default weiboUpload
|
||||
@@ -5,9 +5,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'picgo'
|
||||
}
|
||||
export default {
|
||||
name: 'picgo'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
@@ -18,6 +18,10 @@
|
||||
height 100%
|
||||
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
|
||||
#app
|
||||
overflow-x hidden
|
||||
user-select none
|
||||
overflow hidden
|
||||
.el-button-group
|
||||
width 100%
|
||||
.el-button
|
||||
width 50%
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
@font-face {font-family: "iconfont";
|
||||
src: url('iconfont.eot?t=1513753554908'); /* IE9*/
|
||||
src: url('iconfont.eot?t=1513753554908#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAmkAAsAAAAADaAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kg7Y21hcAAAAYAAAACAAAABzpdE03RnbHlmAAACAAAABYcAAAcM5PoEemhlYWQAAAeIAAAALwAAADYP352haGhlYQAAB7gAAAAcAAAAJAfeA4hobXR4AAAH1AAAABMAAAAcG+kAAGxvY2EAAAfoAAAAEAAAABAFmgdAbWF4cAAAB/gAAAAfAAAAIAEXAMxuYW1lAAAIGAAAAUUAAAJtPlT+fXBvc3QAAAlgAAAAQgAAAFQeKhOyeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/ss4gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDzzYW7438AQw9zA0AAUZgTJAQAqnwy9eJzFkdENhSAMRU9BzQsxLqAjvDHepwM4xvsyTNw1tKX8OIGXHNJeSkooMALZ+BoDyIng+psrzc+U5g/8LC98SLaqJt10vy7zqoquPQ6J1RVmlhaPTHY7eR+ZeE3yXuun5rYfPfMfqR17onZ8MpoCr9E18CnpFuDne0C+AcMNGRh4nG1UXYhkxRWuU3Wr6v7W7fvf/93TPXNv68x07/bP7V3XdCe7cRd/4miWNTsImTUrookKCuu+uDoSgiYqUYTVBAKTkCCGBIIP+rJECIG8DOTFN0M0ia+LKLgYxDbn9u6CQvoeTt869VXdqu985xBOyJf/YpdYmQSkRw6Sb5O7CAGxDh1FG7CSjft0HaIVHiWhYlk3W5HdTp99A5KOCONhPk4TIYULCpowWhnmWZ9mMBnP6BEYxg2ASq160l+r++wlMMtZ8yeL2+hvIGp16+5sc3HrxjwctgP9vO37Fd9/QRec65RqroJHktjghikWv+VuNbrUuoG2wK5k1Tu2nXbNv++58aONtcQA2N2FoNZWr8+9qof2ZDUO/IosOXq56nRXQzj/oVUO7Eb6H4I/gXf9G7vCtklM+uR28j1yljyMtw1FJ82m3T5kctKHcT7HyyQHZSedjKcYFN0iOB0VwSgUMk+aINXScHaa+9M8lkLOAOMcg4ibAS5kOIsvOFSQXaeLfapiBQD0pw/ohm0b+pO/YxctRbX5QOOWwsCJHeqeb6bjPmMPnb7p5a0/fC4g+eLEodMU2MaZ/nxl8Yun39C0N57eLfzesW1Kt48dLfypuNHYaDToiuW6lvbwzxl+RkWKgvbHHzddS/CDRxkFN1aU3n+7ZrnHNUMdz4/sHeoNgP1+t/QsVD05OGratbquzm7RJ05vn6P03PbpJxavwcnZ7CQs/V5rs4VGCC1IZZ/SD8ivyDvI45IT9+qfVHTpWRQmMRIzGubTmTZFPsZp1udZWiglHUB6De+yDOlCS8fTGRsN4xYgX3NAygZQhBE6wSzM2BxmqC3cEq0Fw2LLYb7ELA1xxQCaLIrFtTShQEU7KxKL4HxO8xjebZ2IpSZUbd2RutCCQ67ZFmHJ5sfWR/7Zf1x6VHir0qnZ2QYYGgNXWr6qKylWDWYlw0poNo1olnz3h5MVtyF7N1ka45XOMKkfrk00zYs8aekJT93qmi1sbpY7G1Ekg07gNDZDp2JY1BYVp2JKwXTKEqd0oJxQWaveApPaatsFyrilq17I7fLm4nJQC99hdZNL5YkOfY/XShrFPQ833UpSgrKhN0S9kXaCnWM3ujoDqhma8Mz290feQUPIhuH7dpkzzaGF7gyplWTpULL1wBhHICc9Z90Je3W/VhkwFfo6tWTIW1agOANt1fTbrbLgFMtSJp7dcD18BWG6umJ60/XXLItSQ9cWezJKe/WSa5Ziw69qFApcLXxbizhjnAMQomH97bEP2BlikzJZJTNC1jCNoxmMU2w4AmKS5GSakkyQ5HrlYI1NBab6CERhjF0ln04KLWHG046Ev0LJudnxPOdm21vcy8Hd3weX88XH+/uLj6f88ptvXuZLf0UxWrLuXQJh2ypRpl6w2/bjOAKv4qHHBfyrG1xfif4TL7bUxeIzF5UVe792HAJL7X9E3yIHUPm5kCi2TqHApF/U/lKdaNMYJQvZOL2qxAIQj1Zz9l5Q1uj7/936luo1+O45oLFz4WfCs8Otk/fsOE4IetyemurselhTKFuyT88NQFN/Ue6Bb4qdfJdr7h1vH737pebhJqWNA4d/6UjT77zoOZ7jX9iy7H8Xx8Mzfvl39md2nOzgIM3SZaPuXjsLliU+2L3xwHmW5stCwoIrcFL4xeSyYEZxMYOXSIsHmcfuJ67WVrcorAI3zSmDcsUMxeNGxTWt3q3VMKRglTkN/GjNVDpoSbV7ZhI5P+DcvHDl1EPrz1frRigfM6rKMhEfBYhPOAuCMDWU1OLaEn6fEMZTn516cED/6WFe9FctG2WbdmvzFYfb1aAleLQRmPaNd69uDsB7RvdZ9zs/ennxp8fC0tfgHfv/wj2te+cjr8AW0vU/1nQJqwB4nGNgZGBgAGINu5sC8fw2Xxm4WRhA4FqCziUE/X8BCwNzApDLwcAEEgUAB9YJeAB4nGNgZGBgbvjfwBDDwgACQJKRARWwAwBHDQJweJxjYWBgYH7JwMDCgIkBFrMBBQAAAAAAAHYBHgI2ApwC7gOGeJxjYGRgYGBnOMDAxgACTEDMBYQMDP/BfAYAHFEB5AB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxjYGKAAC4G7ICdkYmRmZGFkZWRjZGdkYOBsYK1PDUzKZ+1JLmyNI+ttCAnPzGFtTAzL7OUtbQAKMTAAADg/QwGAAA=') format('woff'),
|
||||
url('iconfont.ttf?t=1513753554908') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
url('iconfont.svg?t=1513753554908#iconfont') format('svg'); /* iOS 4.1- */
|
||||
src: url('iconfont.eot?t=1523001890286'); /* IE9*/
|
||||
src: url('iconfont.eot?t=1523001890286#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAApcAAsAAAAADqAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kmaY21hcAAAAYAAAACMAAAB5Jz6bNVnbHlmAAACDAAABisAAAfkf7GmJ2hlYWQAAAg4AAAALwAAADYQ+dpBaGhlYQAACGgAAAAcAAAAJAfeA4lobXR4AAAIhAAAABQAAAAgH+kAAGxvY2EAAAiYAAAAEgAAABII+gbebWF4cAAACKwAAAAfAAAAIAEYAMxuYW1lAAAIzAAAAUUAAAJtPlT+fXBvc3QAAAoUAAAASAAAAF2Rted1eJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/ss4gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDxfzdzwv4EhhrmBoQEozAiSAwAxwA0deJzFkdEJwzAMRE9ymoRiSv+TETpK6BQZwvSr+2QvZYz0ZLmFTpAzz/gOGRkLwAVAIg/SAfKGwPViKjVPuNa8w5M+Y4TyXExttmXfjoNpMbHp574SVmfccOdy12Ngv8TbStvjNMl5rf+V6742N5DS4BOtwX+DaeA1NgU+O5sDn6Etgc9x3wLoBwU0H794nG1VXYwcRxHu6p7unt+enf/9m9293buZde5u97w/M3Zs7xof2AoJPsLJ4JMR52AUEUgiJcLxQ2znEEIkEEQiJGOQIl0QCDlKpCgP5MXipAiJl5N44S0IAvjVQkGKBUIs9OzFEkjslGqner7pmar6vhpEEfr3n8htUkYe6qLD6JPoswgBW4a2wDEspKMeXoZggQaRL0jaSRd4p90jJyBqMz8cZKMkYpzZIKABw4VBlvZwCuPRBB+DQRgDVGrVTXep7pJXQC+njW/PPo1/CkGzU7cnq7OHVqb+oOWpV0zXrbjuyyqjVMVYsQU8FYUa1XQ2+xm1q8Ht5iHcBLOSVh/Zslo197EXR0/HS5EGsLMDXq0lfjF1qo60q9XQcyu8ZKnlqtVZ9OHKHaPsmXHyFyR/IHP9DXmP5GhdBgJ4A6IwS8csyXvAkyxqQNCAfAIyTEeZ9HkSRtkgmgAPmUQH83tiYOEBCKO9GcW3XirZ8MUJLrVwtaK0O62zgWLrG8+Tcki5A8dP5t/ITx4Hh9OwTK6eNV2wnRdvYTojOZ3t3bzTJ7a+KSY0rYOvAU3JGphhQ6UXri+diLRpn6iHbPuQSvpTLTqxdP0C1Q+1ntdt0r9zc29W5MXmed0jWyhEPfQw+gK6hJ6UGfqsnaR5RybDxz0YZVPZpOgwbyfjUZEh6xSL+bBYDHzGi/y5mJu8mmdunoWc8QnIdXpQrXnWRF6VJzIUkN6nAflIhAIA8EuPq5ppaurVn5MbhsDKtK9QQ8iFM9vYvtJIRj1Cnjj/4Ksbb/6TQfSvM0fOYyArF3vThdmPX7ilKLde2Cn87voWxlvrpwp/LozjlTjGC4ZtG8qTPyDyMSIQGJS3vtWwDUYPnyIY7FBg/JWHFcM+rWjidHZs90i3D+SNndJ3oOrw/indrNVVcWkDP3d+6zLGl7fOPze7CZuTySbM/W5ztSkNIVwUlXyEP0Cvob0DpgiwD/64wHNPAj8KZWGGgyyfKAUdRknao2lSKCDpQ/Ix3iapLJe0ZJRPyHAQNkHWawqyZH0oliV0LLswIVOYSM3ILaU1YVBsOcjmmLlJXBFAgwSSiwfvIoXHWmnRWAnOpjgL4XfNMyFXmKgtW1xlinfE1lvML5l0fXnoXvr97aeZs8itmpmugKYQsLnhirrgbFEjRjSo+HpDCybR5742XrBj3n3QUAittAdR/WhtrChO4HBDjWhiV5dMZlK93F4JAu61PSte9a2KZmCTVayKzhlRMYms0lo5wrxW/RSMa4stGzChhiq6PjXLq7O7Xs3fI3WdcuGwNn6f1koKlnsebdiVqARlTY1ZPU7a3vb6A7ZKACuawhy99aWhc1hjPNZc1yxToli44J3GlRIvHYk2Hh/JCPi4ay1bfrfu1ip9InxXxQb3adPwBCWgLOpuq1lmFMtxwyPHjG1HngLTbVUQtWG7S4aBsaYqs10eJN16ydZLoeZWFQwFrua/qwSUEEpBDhVF6m+XfEAuIhOV0SKaILQk2zicwCiRg5RBiKIM5QlKGYruK0dqLGey1ccg8EM5LbN8XHBJdjxpc/g1lKzjluNYx01ndoGCvb8PNqWzD/f3Zx/m9O4779ylc39PEFwyLsyBsGWUMBEvmy3zWRmBU3GklzfQ/97g/p3S/80JDXGjeMwNYYTO65ZVzEjJ/b/iX6I1yfyMFYOvXTAw6hXan7NTWh5KysohmRwwsQCEw8WMvO+VFfzHf2x8QnRjunMZcGhd+y5zTH9j8/PbluWDGrZyXVxa9mtC0hbt48t9UMR7wl47ybazHarYj7x76tFXGkcbGMdrR39icd1tf9+xHMu9tmGYf74/x39LfkVOo20ZJGky/wB1Pn4XKUt5yK+SfOEsTbK5kKTgChxnbnFxLpjhfIjLJJLikJWX048daKtTCKvA5RkmUK7oPntWq9i60X2o6vsYjDLFnhss6UIFJap2Lo4D68uU6tfunXti+XvVuubzZ7SqMHSJDzyJjyjxPD/RBFfC2hz+GGPa9b+f+2of/8GRfVF/ZJiStkmnNl2wqFn1mowGK55uPvDo4mofnG+qLul85uuvzt5+xi/9D7xt/l+4o3TOPvVD2JDl+g9+5y00AHicY2BkYGAA4vdhFhPj+W2+MnCzMIDAtbfeSgj6/wIWBuYEIJeDgQkkCgAriQovAHicY2BkYGBu+N/AEMPCAAJAkpEBFXAAAEcOAnF4nGNhYGBgfsnAwMKAHQMAGtcBCQAAAAAAdgDiAYoCogMIA1oD8gAAeJxjYGRgYOBgOMDAxgACTEDMBYQMDP/BfAYAHGwB5QB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxtyMEOQDAQBNCZslX+UhE2kbaSbvD3JK7e8cHhM+BfoGPDlkLPjoE9ePlV62ZRzkVjljrdlryVPY+zHJrUxMpbwANFHA6a') format('woff'),
|
||||
url('iconfont.ttf?t=1523001890286') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
url('iconfont.svg?t=1523001890286#iconfont') format('svg'); /* iOS 4.1- */
|
||||
}
|
||||
|
||||
[class*=" el-icon-ui"], [class^=el-icon-ui] {
|
||||
@@ -15,6 +15,8 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.el-icon-ui-github:before { content: "\e7ab"; }
|
||||
|
||||
.el-icon-ui-weibo:before { content: "\e61c"; }
|
||||
|
||||
.el-icon-ui-tcyun:before { content: "\e64c"; }
|
||||
|
||||
@@ -27,6 +27,9 @@ t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-
|
||||
|
||||
|
||||
|
||||
<glyph glyph-name="github" unicode="" d="M512 822.894c-248.508 0-450-201.456-450-450 0-198.828 128.934-367.506 307.728-426.996 22.482-4.158 30.762 9.756 30.762 21.654 0 10.728-0.414 46.188-0.612 83.772-125.19-27.216-151.614 53.1-151.614 53.1-20.466 52.002-49.968 65.844-49.968 65.844-40.824 27.936 3.078 27.36 3.078 27.36 45.18-3.168 68.976-46.368 68.976-46.368 40.14-68.796 105.264-48.906 130.968-37.404 4.032 29.088 15.696 48.942 28.566 60.174-99.954 11.376-205.02 49.968-205.02 222.39 0 49.122 17.586 89.28 46.368 120.78-4.68 11.34-20.088 57.096 4.356 119.088 0 0 37.8 12.078 123.786-46.134 35.892 9.972 74.394 14.976 112.644 15.156 38.232-0.18 76.752-5.166 112.716-15.156 85.896 58.23 123.624 46.134 123.624 46.134 24.498-61.992 9.09-107.748 4.428-119.088 28.854-31.518 46.314-71.658 46.314-120.78 0-172.836-105.282-210.888-205.488-222.03 16.146-13.968 30.528-41.346 30.528-83.322 0-60.21-0.522-108.666-0.522-123.498 0-11.97 8.1-26.01 30.906-21.6 178.704 59.562 307.476 228.186 307.476 426.942 0 248.544-201.474 450-450 450z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="weibo" unicode="" d="M1013.46529415 545.47540804c0-14.8526376-11.46535652-26.88232091-25.58940469-26.8823209-14.12272255 0-25.56953354 12.03100762-25.56953352 26.8823209 0 1.34457994 0.18810806 2.64941756 0.37224319 3.94895792l-0.37224319 0c0 139.92901597-108.14394722 253.3466299-241.5222759 253.3466299-2.53151878 0-4.99680252-0.32587799-7.5296456-0.39211435l0 0.7842274c-12.33966437 1.93142522-21.81663342 13.00334437-21.81663343 26.49020656 0 13.48421356 9.47564343 24.53891019 21.81663343 26.4703354l0 0.02251979c0.06358643 0 0.14571841 0 0.20532929 0 1.16441905 0.17486105 2.34605934 0.36959327 3.56744197 0.36959325 0.39608859 0 0.79085155-0.08610622 1.18164029-0.10862599 0.79217586 0.04371526 1.57640327 0.10862599 2.38712599 0.10862599 161.74962231 0 292.86932347-137.4968504 292.86932348-307.08875061l-0.39608859 0C1013.25863924 548.1235012899999 1013.46529415 546.8199879900001 1013.46529415 545.47540804zM863.34794096 518.59176283c-14.12139824 0-25.56953354 12.03100762-25.56953354 26.8823209 0 3.56081781 0.68487418 6.96797006 1.88770995 10.0730871-2.98854387 64.2960783-52.1948614 115.73982917-113.60042345 118.60649873-2.81765575-1.13130087-5.84726626-1.80292934-9.0384895-1.80292935-14.11874832 0-25.58940468 12.02968331-25.5894047 26.86112543 0 13.50540903 9.47564343 24.55878134 21.81663342 26.49020656l0 0c0.06358643 0 0.12319732 0.0238454 0.18678375 0.0238454 1.18429021 0.17221243 2.3646062 0.36694464 3.58731185 0.36694464 0.39608859 0 0.79085155-0.10862599 1.18164028-0.12849716 0.79217586 0.06358643 1.57640327 0.12849716 2.38712598 0.12849716 92.9614561 0 168.34005063-79.10500064 168.34005064-176.6711449l-0.39078874 0c0.18810806-1.30086468 0.39078875-2.60437798 0.39078874-3.94895792C888.93602132 530.6227704400001 877.47066481 518.59176283 863.34794096 518.59176283zM754.87281718 449.10547541c20.4879504 2.58450683 43.02787472 1.45453025 68.32981684-5.21405452 70.0267688-18.47969046 98.02449494-65.7929995 94.95381909-137.84392388-3.10909257-73.41934842-80.46017767-204.8583047-196.12979627-275.14471646-77.16430135-51.85441078-177.86470788-87.75137581-282.42001895-87.7513758-221.16017403 0-429.07193204 134.69774021-429.07193204 300.83612299 0 2.19239377 0.20797922 4.40730732 0.30070958 6.60367404 0 0.0834563 0.01059709 0.17221243 0.01059709 0.23712316 0.15499118 3.17135339 0.35237202 6.34138245 0.70474535 9.51406017C25.08266092 424.84872796 281.96361331 686.07605117 358.85634901 693.93819893c71.77008594 7.33756332 148.28925417-16.54694094 142.21546312-106.1635069-0.4583494-6.73084688-1.94997206-13.94123735-3.85755316-21.34636133 57.46322827 34.46230593 125.08565114 58.5640622 185.94940747 46.83906271C766.77400333 597.1774775700001 787.13875511 513.92348823 754.87281718 449.10547541zM420.4857885 420.8110152c-165.81383039 0-300.19098985-93.13366851-300.19098984-208.04555373 0-114.89068974 134.37715946-208.02700817 300.19098984-208.02700818 165.77276377 0 300.16979438 93.13631843 300.16979438 208.02700818C720.6569072 327.67734540000004 586.25855228 420.8110152 420.4857885 420.8110152zM387.57869037 58.58919872000001c-86.91018613 0-157.33436782 65.55852496-157.33436783 146.3988943 0 80.84566786 70.4241817 146.39889428 157.33436783 146.39889429 86.90753621 0 157.35291338-65.5519021 157.35291337-146.39889429C544.93160374 124.1490493 474.48622658 58.58919872000001 387.57869037 58.58919872000001zM369.22484715 220.25404044000004c-28.36864372 0-51.34307333-24.08056079-51.34307335-53.76331621 0-29.68407973 22.97442832-53.74741927 51.34307335-53.74741926 28.36731941 0 51.36559312 24.06333954 51.36559313 53.74741926C420.59044028 196.17480396999997 397.59216656 220.25404044000004 369.22484715 220.25404044000004z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |