First beta version

This commit is contained in:
Molunerfinn 2017-11-28 08:21:12 +08:00
commit d1b9e0f592
54 changed files with 10526 additions and 0 deletions

39
.babelrc Normal file
View File

@ -0,0 +1,39 @@
{
"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"
]
},
"web": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}

130
.electron-vue/build.js Normal file
View File

@ -0,0 +1,130 @@
'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()
}

View File

@ -0,0 +1,40 @@
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>
`
}
})

178
.electron-vue/dev-runner.js Normal file
View File

@ -0,0 +1,178 @@
'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()

View File

@ -0,0 +1,83 @@
'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

View File

@ -0,0 +1,178 @@
'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

View File

@ -0,0 +1,139 @@
'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

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
test/unit/coverage/**
test/unit/*.js
test/e2e/*.js

26
.eslintrc.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
node: true
},
extends: 'standard',
globals: {
__static: true
},
plugins: [
'html'
],
'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
}
}

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.DS_Store
dist/electron/*
dist/web/*
build/*
!build/icons
coverage
node_modules/
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep

43
.travis.yml Normal file
View File

@ -0,0 +1,43 @@
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
osx_image: xcode8.3
sudo: required
dist: trusty
language: c
matrix:
include:
- os: osx
- os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
apt:
packages:
- libgnome-keyring-dev
- icnsutils
#- xvfb
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
install:
#- export DISPLAY=':99.0'
#- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- nvm install 7
- curl -o- -L https://yarnpkg.com/install.sh | bash
- source ~/.bashrc
- npm install -g xvfb-maybe
- yarn
script:
#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
- yarn run build
branches:
only:
- master

18
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach",
"type": "node",
"request": "attach",
"port": 5858,
"sourceMaps": false,
"outFiles": [],
"localRoot": "${workspaceRoot}",
"remoteRoot": null
}
]
}

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"eslint.enable": true,
"eslint.autoFixOnSave": true
}

8
LICENSE Normal file
View File

@ -0,0 +1,8 @@
MIT License
Copyright (c) <year> <copyright holders>
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 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.

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# picgo
> Easy to upload your pic & copy to write
#### Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:9080
npm run dev
# build electron application for production
npm run build
# run unit & end-to-end tests
npm test
# lint all JS/Vue component files in `src/`
npm run lint
```
---
This project was generated with [electron-vue](https://github.com/SimulatedGREG/electron-vue) using [vue-cli](https://github.com/vuejs/vue-cli). Documentation about the original structure can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/index.html).

33
appveyor.yml Normal file
View File

@ -0,0 +1,33 @@
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- choco install yarn --ignore-dependencies
- git reset --hard HEAD
- yarn
- node --version
build_script:
#- yarn test
- yarn build
test: off

BIN
build/icons/256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
build/icons/icon.icns Normal file

Binary file not shown.

BIN
build/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

0
dist/electron/.gitkeep vendored Normal file
View File

0
dist/web/.gitkeep vendored Normal file
View File

133
package.json Normal file
View File

@ -0,0 +1,133 @@
{
"name": "picgo",
"version": "0.0.0",
"author": "Molunerfinn <marksz@teamsz.xyz>",
"description": "Easy to upload your pic & copy to write",
"license": "MIT",
"main": "./dist/electron/main.js",
"scripts": {
"build": "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": {
"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"
},
"win": {
"icon": "build/icons/icon.ico"
},
"linux": {
"icon": "build/icons"
}
},
"dependencies": {
"axios": "^0.16.1",
"element-ui": "^2.0.5",
"fs-extra": "^4.0.2",
"image-size": "^0.6.1",
"lowdb": "^1.0.0",
"request": "^2.83.0",
"request-promise": "^4.2.2",
"vue": "^2.3.3",
"vue-electron": "^1.0.6",
"vue-router": "^2.5.3",
"vuex": "^2.3.1"
},
"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",
"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"
}
}

23
src/datastore/index.js Normal file
View File

@ -0,0 +1,23 @@
import Datastore from 'lowdb'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import { remote, app } from 'electron'
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
const db = Datastore(adapter)
if (!db.has('uploaded').value()) {
db.set('uploaded', []).write()
}
if (!db.has('picBed').value()) {
db.set('picBed', {
current: 'weibo'
}).write()
}
export default db

22
src/index.ejs Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<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>

27
src/main/index.dev.js Normal file
View File

@ -0,0 +1,27 @@
/**
* 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')

230
src/main/index.js Normal file
View File

@ -0,0 +1,230 @@
'use strict'
import { weiboUpload } from './utils/weiboUpload.js'
import { app, BrowserWindow, Tray, Menu, Notification, clipboard, ipcMain } from 'electron'
import db from '../datastore/index'
/**
* 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
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`
function createTray () {
tray = new Tray(`${__static}/menubarDefaultTemplate.png`)
const contextMenu = Menu.buildFromTemplate([
{
role: 'quit',
label: 'Quit'
},
{
label: 'Open setting Window',
click () {
if (settingWindow === null) {
createSettingWindow()
} else {
settingWindow.show()
settingWindow.focus()
}
}
}
])
tray.on('right-click', () => {
window.hide()
tray.popUpContextMenu(contextMenu)
})
tray.on('click', () => {
let img = clipboard.readImage()
let obj = []
if (!img.isEmpty()) {
obj.push({
width: img.getSize().width,
height: img.getSize().height,
imgUrl: img.toDataURL()
})
}
toggleWindow()
setTimeout(() => {
window.webContents.send('clipboardFiles', obj)
}, 0)
})
tray.on('drag-enter', () => {
tray.setImage(`${__static}/upload.png`)
})
tray.on('drag-end', () => {
tray.setImage(`${__static}/menubarDefaultTemplate.png`)
})
tray.on('drop-files', async (event, files) => {
const imgs = await weiboUpload(files, 'imgFromPath')
for (let i in imgs) {
clipboard.writeText(imgs[i].imgUrl)
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i]
})
setTimeout(() => {
notification.show()
}, i * 100)
}
console.log('drag-files')
window.webContents.send('dragFiles', imgs)
})
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()
}
const createSettingWindow = () => {
settingWindow = new BrowserWindow({
height: 450,
width: 800,
show: true,
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 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 toggleSettingWindow = () => {
// if (settingWindow.isVisible()) {
// settingWindow.hide()
// } else {
// settingWindow.show()
// settingWindow.focus()
// }
// }
const showWindow = () => {
const position = getWindowPosition()
window.setPosition(position.x, position.y, false)
window.show()
window.focus()
}
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
const img = await weiboUpload(file, 'imgFromClipboard')
clipboard.writeText(img[0].imgUrl)
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl,
icon: file[0]
})
notification.show()
clipboard.clear()
window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadClipboardFiles', img)
console.log('clipboard-upload')
})
app.on('ready', () => {
createWindow()
createTray()
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (window === null || settingWindow === null) {
createWindow()
createTray()
}
})
/**
* 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()
// }
// })

View File

@ -0,0 +1,35 @@
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 = []
result.push({
base64Image: file.imgUrl.replace(/^data\S+,/, ''),
width: file.width,
height: file.height
})
return result
}
export {
imgFromPath,
imgFromClipboard
}

View File

@ -0,0 +1,66 @@
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'
function 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) {
try {
const formData = {
username: db.read().get('picBed.weibo.username').value(),
password: db.read().get('picBed.weibo.password').value()
}
const options = postOptions(formData)
const res = await rp(options)
if (res.body.retcode === 20000000) {
for (let i in res.body.data.crossdomainlist) {
await rp.get(res.body.data.crossdomainlist[i])
}
const imgList = await img2Base64[type](img)
let resText = []
for (let i in imgList) {
let result = await rp.post(UPLOAD_URL, {
formData: {
b64_data: imgList[i].base64Image
}
})
resText.push(result.replace(/<.*?\/>/, '').replace(/<(\w+).*?>.*?<\/\1>/, ''))
}
for (let i in imgList) {
const resTextJson = JSON.parse(resText[i])
imgList[i]['imgUrl'] = `https://ws1.sinaimg.cn/large/${resTextJson.data.pics.pic_1.pid}`
delete imgList[i].base64Image
}
return imgList
} else {
const notification = new Notification({
title: '上传失败!',
body: res.body.msg
})
notification.show()
}
} catch (err) {
console.log('This is error', err, err.name === 'RequestError')
throw new Error(err)
}
}
export {
weiboUpload
}

20
src/renderer/App.vue Normal file
View File

@ -0,0 +1,20 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'picgo'
}
</script>
<style lang="stylus">
body,
html
padding 0
margin 0
height 100%
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
</style>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,83 @@
<template>
<div id="setting-page">
<div class="fake-title-bar">
PicGo
</div>
<el-row>
<el-col :span="5">
<el-menu
class="picgo-sidebar"
:default-active="defaultActive"
@select="handleSelect"
>
<el-menu-item index="upload">
<i class="el-icon-upload"></i>
<span slot="title">上传区</span>
</el-menu-item>
<el-menu-item index="weibo">
<i class="el-icon-setting"></i>
<span slot="title">微博设置</span>
</el-menu-item>
</el-menu>
</el-col>
<el-col :span="19">
<router-view></router-view>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'setting-page',
data () {
return {
defaultActive: 'upload'
}
},
methods: {
handleSelect (index) {
this.$router.push({
name: index
})
}
},
beforeRouteEnter: (to, from, next) => {
next(vm => {
vm.defaultActive = to.name
})
}
}
</script>
<style lang='stylus'>
#setting-page
.fake-title-bar
-webkit-app-region drag
height h = 22px
width 100%
text-align center
color #eee
font-size 12px
line-height h
.picgo-sidebar
height calc(100vh - 22px)
.el-menu
border-right none
background transparent
&-item
color #eee
position relative
&:focus,
&:hover
color #fff
background transparent
&.is-active
color active-color = #409EFF
&:before
content ''
position absolute
width 3px
height 20px
right 0
top 18px
background active-color
</style>

View File

@ -0,0 +1,37 @@
<template>
<div id="choose-pic-bed">
<span>选择 {{ label }} 作为你默认图床</span>
<el-switch
v-model="value"
@change="choosePicBed"
>
</el-switch>
</div>
</template>
<script>
export default {
name: 'choose-pic-bed',
props: {
type: String,
label: String
},
data () {
return {
value: false
}
},
created () {
if (this.type === this.$db.get('picBed.current').value()) {
this.value = true
}
},
methods: {
choosePicBed (val) {
this.$db.set('picBed.current', this.type)
this.$emit('update:choosed', this.type)
}
}
}
</script>
<style lang='stylus'>
</style>

View File

@ -0,0 +1,17 @@
<template>
<div id="upload-view">
upload
</div>
</template>
<script>
export default {
name: 'upload',
data () {
return {
msg: '123'
}
}
}
</script>
<style lang='stylus'>
</style>

View File

@ -0,0 +1,94 @@
<template>
<div id="weibo-view">
<el-row :gutter="16">
<el-col :span="12" :offset="6">
<div class="view-title">
微博图床设置
</div>
<el-form
ref="weiboForm"
label-position="top"
label-width="80px"
:model="form">
<el-form-item
label="设定用户名"
prop="username"
:rules="{
required: true, message: '用户名不能为空', trigger: 'blur'
}">
<el-input v-model="form.username" placeholder="用户名"></el-input>
</el-form-item>
<el-form-item
label="设定密码"
prop="password"
:rules="{
required: true, message: '密码不能为空', trigger: 'blur'
}">
<el-input v-model="form.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="confirm('weiboForm')">确定</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'weibo',
data () {
return {
form: {
username: '',
password: ''
}
}
},
created () {
const account = this.$db.get('picBed.weibo').value()
console.log(account)
if (account) {
this.form.username = account.username
this.form.password = account.password
}
},
methods: {
confirm (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$db.set('picBed.weibo', {
username: this.form.username,
password: this.form.password
}).write()
console.log(this.$db.get('picBed.weibo').value())
this.$message({
type: 'success',
message: '设置成功'
})
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
.el-message
left 60%
#weibo-view
.view-title
color #eee
font-size 20px
text-align center
margin 20px auto
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-button
width 100%
margin-top 10px
</style>

View File

@ -0,0 +1,142 @@
<template>
<div id="tray-page">
<div class="content">
<div class="wait-upload-img" v-if="clipboardFiles.length > 0">
<div class="list-title">等待上传</div>
<div v-for="(item, index) in clipboardFiles" :key="index" class="img-list" :style="{height: calcHeight(item.width, item.height) + 'px'}">
<div class="upload-img__container" @click="uploadClipboardFiles">
<img :src="item.imgUrl" class="upload-img">
</div>
</div>
</div>
<div class="uploaded-img">
<div class="list-title">已上传</div>
<div v-for="(item, index) in files" :key="index" class="img-list" :style="{height: calcHeight(item.width, item.height) + 'px'}">
<div class="upload-img__container" @click="copyTheLink(item)">
<img :src="item.imgUrl" class="upload-img">
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'tray-page',
data () {
return {
files: [],
notification: {
title: '复制链接成功',
body: '',
icon: ''
},
clipboardFiles: []
}
},
computed: {
reverseList () {
return this.files.slice().reverse()
}
},
mounted () {
this.disableDragFile()
this.getData()
this.$electron.ipcRenderer.on('dragFiles', (event, files) => {
this.$db.get('uploaded').push(...files).write()
this.files = this.$db.get('uploaded').slice().reverse().slice(0, 5).value()
})
this.$electron.ipcRenderer.on('clipboardFiles', (event, files) => {
this.clipboardFiles = files
})
this.$electron.ipcRenderer.on('uploadClipboardFiles', (event, files) => {
this.$db.get('uploaded').push(...files).write()
this.files = this.$db.get('uploaded').slice().reverse().slice(0, 5).value()
})
},
methods: {
getData () {
this.files = this.$db.get('uploaded').slice().reverse().slice(0, 5).value()
},
copyTheLink (item) {
this.notification.body = item.imgUrl
this.notification.icon = item.imgUrl
const myNotification = new window.Notification(this.notification.title, this.notification)
myNotification.onclick = () => {
this.$electron.clipboard.writeText(item.imgUrl)
}
},
calcHeight (width, height) {
return height * 160 / width
},
disableDragFile () {
window.addEventListener('dragover', (e) => {
e = e || event
e.preventDefault()
}, false)
window.addEventListener('drop', (e) => {
e = e || event
e.preventDefault()
}, false)
},
uploadClipboardFiles () {
this.$electron.ipcRenderer.send('uploadClipboardFiles', this.clipboardFiles[0])
}
}
}
</script>
<style lang="stylus">
#tray-page
.list-title
text-align center
color #858585
font-size 12px
padding 6px 0
position relative
&:before
content ''
position absolute
height 1px
width calc(100% - 36px)
bottom 0
left 18px
background #858585
.header-arrow
position absolute
top 12px
left 50%
margin-left -10px
width: 0;
height: 0;
border-left: 10px solid transparent
border-right: 10px solid transparent
border-bottom: 10px solid rgba(255,255,255, 1)
.content
position absolute
top 0px
width 100%
// padding-top 10px
// background-color rgba(255,255,255, 1)
.img-list
padding 16px 8px
display flex
justify-content space-between
align-items center
height 45px
cursor pointer
transition all .2s ease-in-out
&:hover
background #49B1F5
.upload-img__index
color #fff
.upload-img
height 100%
width 100%
margin 0 auto
&__container
width 100%
padding 8px 10px
height 100%
</style>

22
src/renderer/main.js Normal file
View File

@ -0,0 +1,22 @@
import Vue from 'vue'
import axios from 'axios'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App'
import router from './router'
import store from './store'
import db from '../datastore/index'
Vue.use(ElementUI)
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
Vue.http = Vue.prototype.$http = axios
Vue.prototype.$db = db
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app')

View File

@ -0,0 +1,35 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'tray-page',
component: require('@/components/TrayPage').default
},
{
path: '/setting',
name: 'setting-page',
component: require('@/components/SettingPage').default,
children: [
{
path: 'upload',
component: require('@/components/SettingView/Upload').default,
name: 'upload'
},
{
path: 'weibo',
component: require('@/components/SettingView/Weibo').default,
name: 'weibo'
}
]
},
{
path: '*',
redirect: '/'
}
]
})

View File

@ -0,0 +1,11 @@
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
strict: process.env.NODE_ENV !== 'production'
})

View File

@ -0,0 +1,25 @@
const state = {
main: 0
}
const mutations = {
DECREMENT_MAIN_COUNTER (state) {
state.main--
},
INCREMENT_MAIN_COUNTER (state) {
state.main++
}
}
const actions = {
someAsyncTask ({ commit }) {
// do something async
commit('INCREMENT_MAIN_COUNTER')
}
}
export default {
state,
mutations,
actions
}

View File

@ -0,0 +1,14 @@
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context('.', false, /\.js$/)
const modules = {}
files.keys().forEach(key => {
if (key === './index.js') return
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
export default modules

0
static/.gitkeep Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

BIN
static/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

11
test/.eslintrc Normal file
View File

@ -0,0 +1,11 @@
{
"env": {
"mocha": true
},
"globals": {
"assert": true,
"expect": true,
"should": true,
"__static": true
}
}

18
test/e2e/index.js Normal file
View File

@ -0,0 +1,18 @@
'use strict'
// Set BABEL_ENV to use proper env config
process.env.BABEL_ENV = 'test'
// Enable use of ES6+ on required files
require('babel-register')({
ignore: /node_modules/
})
// Attach Chai APIs to global scope
const { expect, should, assert } = require('chai')
global.expect = expect
global.should = should
global.assert = assert
// Require all JS files in `./specs` for Mocha to consume
require('require-dir')('./specs')

View File

@ -0,0 +1,13 @@
import utils from '../utils'
describe('Launch', function () {
beforeEach(utils.beforeEach)
afterEach(utils.afterEach)
it('shows the proper application title', function () {
return this.app.client.getTitle()
.then(title => {
expect(title).to.equal('picgo')
})
})
})

23
test/e2e/utils.js Normal file
View File

@ -0,0 +1,23 @@
import electron from 'electron'
import { Application } from 'spectron'
export default {
afterEach () {
this.timeout(10000)
if (this.app && this.app.isRunning()) {
return this.app.stop()
}
},
beforeEach () {
this.timeout(10000)
this.app = new Application({
path: electron,
args: ['dist/electron/main.js'],
startTimeout: 10000,
waitTimeout: 10000
})
return this.app.start()
}
}

13
test/unit/index.js Normal file
View File

@ -0,0 +1,13 @@
import Vue from 'vue'
Vue.config.devtools = false
Vue.config.productionTip = false
// require all test files (files that ends with .spec.js)
const testsContext = require.context('./specs', true, /\.spec$/)
testsContext.keys().forEach(testsContext)
// require all src files except main.js for coverage.
// you can also change this to match only the subset of files that
// you want coverage for.
const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/)
srcContext.keys().forEach(srcContext)

62
test/unit/karma.conf.js Normal file
View File

@ -0,0 +1,62 @@
'use strict'
const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
const baseConfig = require('../../.electron-vue/webpack.renderer.config')
const projectRoot = path.resolve(__dirname, '../../src/renderer')
// Set BABEL_ENV to use proper preset config
process.env.BABEL_ENV = 'test'
let webpackConfig = merge(baseConfig, {
devtool: '#inline-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"testing"'
})
]
})
// don't treat dependencies as externals
delete webpackConfig.entry
delete webpackConfig.externals
delete webpackConfig.output.libraryTarget
// apply vue option to apply isparta-loader on js
webpackConfig.module.rules
.find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader'
module.exports = config => {
config.set({
browsers: ['visibleElectron'],
client: {
useIframe: false
},
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
},
customLaunchers: {
'visibleElectron': {
base: 'Electron',
flags: ['--show']
}
},
frameworks: ['mocha', 'chai'],
files: ['./index.js'],
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
reporters: ['spec', 'coverage'],
singleRun: true,
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
}
})
}

View File

@ -0,0 +1,13 @@
import Vue from 'vue'
import LandingPage from '@/components/LandingPage'
describe('LandingPage.vue', () => {
it('should render correct contents', () => {
const vm = new Vue({
el: document.createElement('div'),
render: h => h(LandingPage)
}).$mount()
expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!')
})
})

150
yarn-error.log Normal file
View File

@ -0,0 +1,150 @@
Arguments:
/usr/local/bin/node /usr/local/Cellar/yarn/1.2.1/libexec/bin/yarn.js
PATH:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/aria2/bin
Yarn version:
1.2.1
Node version:
8.9.0
Platform:
darwin x64
npm manifest:
{
"name": "picgo",
"version": "0.0.0",
"author": "Molunerfinn <marksz@teamsz.xyz>",
"description": "Easy to upload your pic & copy to write",
"license": null,
"main": "./dist/electron/main.js",
"scripts": {
"build": "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": {
"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"
},
"win": {
"icon": "build/icons/icon.ico"
},
"linux": {
"icon": "build/icons"
}
},
"dependencies": {
"vue": "^2.3.3",
"axios": "^0.16.1",
"vue-electron": "^1.0.6",
"vue-router": "^2.5.3",
"vuex": "^2.3.1"
},
"devDependencies": {
"babel-core": "^6.25.0",
"babel-loader": "^7.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",
"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-debug": "^1.4.0",
"electron-devtools-installer": "^2.2.0",
"electron-builder": "^19.19.1",
"babel-eslint": "^7.2.3",
"eslint": "^4.4.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.9.0",
"eslint-plugin-html": "^3.1.1",
"eslint-config-standard": "^10.2.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",
"webpack-merge": "^4.1.0",
"require-dir": "^0.3.0",
"spectron": "^3.7.1",
"babel-plugin-istanbul": "^4.1.1",
"chai": "^4.0.0",
"mocha": "^3.0.2",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"style-loader": "^0.18.2",
"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"
}
}
yarn manifest:
No manifest
Lockfile:
No lockfile
Trace:
Error: http://registry.npm.taobao.org/caniuse-db/download/caniuse-db-1.0.30000766.tgz: unexpected end of file
at Gunzip.zlibOnError (zlib.js:153:15)

8226
yarn.lock Normal file

File diff suppressed because it is too large Load Diff