🔨 Refactor(ts): change js -> ts

This commit is contained in:
Molunerfinn 2019-12-19 19:17:21 +08:00
parent 72e6e2aed5
commit 4e67a75be0
91 changed files with 8223 additions and 6755 deletions

View File

@ -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"]
}

View File

@ -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'
}
}

4
.gitignore vendored
View File

@ -12,3 +12,7 @@ thumbs.db
!.gitkeep
yarn-error.log
docs/dist/
# local env files
.env.local
.env.*.local
dist_electron/

23
.vscode/settings.json vendored
View File

@ -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
}
}

28
LICENSE
View File

@ -1,13 +1,21 @@
The 996ICU License (996ICU)
Version 0.1, March 2019
The MIT License (MIT)
PACKAGE is distributed under LICENSE with the following restriction:
Copyright (c) 2017-present, Molunerfinn
The above license is only granted to entities that act in concordance
with local labor laws. In addition, the following requirements must be
observed:
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 licencee must not, explicitly or implicitly, request or schedule
their employees to work more than 45 hours in any single week.
* The licencee must not, explicitly or implicitly, request or schedule
their employees to be at work consecutively for 10 hours.
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.

5
babel.config.js Normal file
View File

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

View File

0
dist/web/.gitkeep vendored
View File

View File

@ -1,35 +1,20 @@
{
"name": "picgo",
"version": "2.1.2",
"author": "Molunerfinn <marksz@teamsz.xyz>",
"description": "Easy to upload your pic & copy to write",
"license": "MIT",
"main": "./dist/electron/main.js",
"private": true,
"scripts": {
"render": "webpack-dev-server --hot --colors --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist",
"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"
"bump": "bump-version",
"release": "electron:build"
},
"main": "background.js",
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
@ -44,144 +29,53 @@
}
},
"commitlint": {
"extends": [
"./node_modules/@picgo/bump-version/commitlint-picgo"
]
},
"build": {
"productName": "PicGo",
"appId": "com.molunerfinn.picgo",
"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",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons/"
}
"extends": ["./node_modules/@picgo/bump-version/commitlint-picgo"]
},
"dependencies": {
"axios": "^0.19.0",
"dateformat": "^3.0.3",
"element-ui": "^2.4.11",
"core-js": "^3.3.2",
"element-ui": "^2.13.0",
"fix-path": "^2.1.0",
"fs-extra": "^4.0.2",
"image-size": "^0.6.1",
"keycode": "^2.1.9",
"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",
"picgo": "^1.3.7",
"qiniu": "^7.1.1",
"vue": "^2.3.3",
"vue-electron": "^1.0.6",
"vue-gallery": "^1.2.4",
"vue": "^2.6.10",
"vue-gallery": "^2.0.1",
"vue-lazyload": "^1.2.6",
"vue-router": "^2.5.3",
"vuex": "^2.3.1",
"ws": "3.3.1"
"vue-router": "^3.1.3"
},
"devDependencies": {
"@commitlint/cli": "^7.5.2",
"@picgo/bump-version": "^1.0.2",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.1",
"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",
"commitizen": "^3.0.7",
"conventional-changelog": "^3.0.6",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^5.0.5",
"css-loader": "^0.28.4",
"cz-customizable": "^5.10.0",
"del": "^3.0.0",
"devtron": "^1.4.0",
"electron": "^5.0.1",
"electron-builder": "^20.38.4",
"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": "^2.1.1",
"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",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"husky": "^1.3.1",
"inject-loader": "^3.0.0",
"inquirer": "^6.3.1",
"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",
"mini-css-extract-plugin": "0.4.0",
"mocha": "^3.0.2",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"pug": "^2.0.0-rc.4",
"pug-loader": "^2.3.0",
"pug-plain-loader": "^1.0.0",
"require-dir": "^0.3.0",
"spectron": "^3.7.1",
"style-loader": "^0.23.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"url-loader": "^1.1.2",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.4.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.4.2",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4",
"webpack-hot-middleware": "^2.22.2",
"webpack-merge": "^4.1.3"
"@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.5.3",
"vue-cli-plugin-electron-builder": "^1.4.2",
"vue-property-decorator": "^8.3.0",
"vue-template-compiler": "^2.6.10"
},
"resolutions": {
"**/@types/node": "10.17.6"
}
}

5
postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

18
public/index.html Normal file
View File

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

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 497 B

View File

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 823 B

After

Width:  |  Height:  |  Size: 823 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 777 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,6 +1,77 @@
// 'use strict'
// import { app, protocol, BrowserWindow } from 'electron'
// import {
// createProtocol,
// installVueDevtools
// } from 'vue-cli-plugin-electron-builder/lib'
// const isDevelopment = process.env.NODE_ENV !== 'production'
// // Keep a global reference of the window object, if you don't, the window will
// // be closed automatically when the JavaScript object is garbage collected.
// let win: BrowserWindow | null
// // Scheme must be registered before the app is ready
// protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
// function createWindow () {
// // Create the browser window.
// win = new BrowserWindow({ width: 800,
// height: 600,
// webPreferences: {
// nodeIntegration: true
// } })
// if (process.env.WEBPACK_DEV_SERVER_URL) {
// // Load the url of the dev server if in development mode
// win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
// if (!process.env.IS_TEST) win.webContents.openDevTools()
// } else {
// createProtocol('app')
// // Load the index.html when not in development
// win.loadURL('app://./index.html')
// }
// win.on('closed', () => {
// win = null
// })
// }
// // Quit when all windows are closed.
// app.on('window-all-closed', () => {
// // On macOS it is common for applications and their menu bar
// // to stay active until the user quits explicitly with Cmd + Q
// if (process.platform !== 'darwin') {
// app.quit()
// }
// })
// app.on('activate', () => {
// // On macOS it's common to re-create a window in the app when the
// // dock icon is clicked and there are no other windows open.
// if (win === null) {
// createWindow()
// }
// })
// // This method will be called when Electron has finished
// // initialization and is ready to create browser windows.
// // Some APIs can only be used after this event occurs.
// app.on('ready', async () => {
// if (isDevelopment && !process.env.IS_TEST) {
// // Install Vue Devtools
// try {
// await installVueDevtools()
// } catch (e) {
// console.error('Vue Devtools failed to install:', e.toString())
// }
// }
// createWindow()
// })
'use strict'
import Uploader from './utils/uploader.js'
import Uploader from '~/main/utils/uploader'
import {
app,
BrowserWindow,
@ -11,54 +82,54 @@ import {
ipcMain,
globalShortcut,
dialog,
systemPreferences
systemPreferences,
WebContents,
IpcMainEvent,
protocol
} from 'electron'
import db from '../datastore'
import beforeOpen from './utils/beforeOpen'
import pasteTemplate from './utils/pasteTemplate'
import updateChecker from './utils/updateChecker'
import { getPicBeds } from './utils/getPicBeds'
import pkg from '../../package.json'
import picgoCoreIPC from './utils/picgoCoreIPC'
import {
createProtocol,
installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib'
import db from '#/datastore'
import beforeOpen from '~/main/utils/beforeOpen'
import pasteTemplate from '#/utils/pasteTemplate'
import updateChecker from '~/main/utils/updateChecker'
import { getPicBeds } from '~/main/utils/getPicBeds'
import pkg from 'root/package.json'
import picgoCoreIPC from '~/main/utils/picgoCoreIPC'
import fixPath from 'fix-path'
import { getUploadFiles } from './utils/handleArgv'
import bus from './utils/eventBus'
import { getUploadFiles } from '~/main/utils/handleArgv'
import bus from '~/main/utils/eventBus'
import {
updateShortKeyFromVersion212
} from './migrate/shortKeyUpdateHelper'
} from '~/main/migrate/shortKeyUpdateHelper'
import {
shortKeyUpdater,
initShortKeyRegister
} from './utils/shortKeyHandler'
} from '~/main/utils/shortKeyHandler'
const isDevelopment = process.env.NODE_ENV !== 'production'
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
if (process.platform === 'darwin') {
beforeOpen()
}
/**
* 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, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = require('path').join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
let window
let settingWindow
let miniWindow
let tray
let menu
let contextMenu
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 miniWinURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/#mini-page`
: `file://${__dirname}/index.html#mini-page`
let window: BrowserWindow | null
let settingWindow: BrowserWindow | null
let miniWindow: BrowserWindow | null
let tray: Tray | null
let menu: Menu | null
let contextMenu: Menu | null
const winURL = isDevelopment
? (process.env.WEBPACK_DEV_SERVER_URL as string)
: `picgo://./index.html`
const settingWinURL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#setting/upload`
: `picgo://./index.html#setting/upload`
const miniWinURL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
: `picgo://./index.html#mini-page`
// fix the $PATH in macOS
fixPath()
@ -94,7 +165,7 @@ function createContextMenu () {
click () {
if (settingWindow === null) {
createSettingWindow()
settingWindow.show()
settingWindow!.show()
} else {
settingWindow.show()
settingWindow.focus()
@ -107,8 +178,10 @@ function createContextMenu () {
{
label: '选择默认图床',
type: 'submenu',
// @ts-ignore
submenu
},
// @ts-ignore
{
label: '打开更新助手',
type: 'checkbox',
@ -125,6 +198,7 @@ function createContextMenu () {
app.exit(0)
}
},
// @ts-ignore
{
role: 'quit',
label: '退出'
@ -140,14 +214,15 @@ function createTray () {
window.hide()
}
createContextMenu()
tray.popUpContextMenu(contextMenu)
tray!.popUpContextMenu(contextMenu!)
})
tray.on('click', (event, bounds) => {
if (process.platform === 'darwin') {
let img = clipboard.readImage()
let obj = []
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,
@ -157,7 +232,7 @@ function createTray () {
}
toggleWindow(bounds)
setTimeout(() => {
window.webContents.send('clipboardFiles', obj)
window!.webContents.send('clipboardFiles', obj)
}, 0)
} else {
if (window) {
@ -165,7 +240,7 @@ function createTray () {
}
if (settingWindow === null) {
createSettingWindow()
settingWindow.show()
settingWindow!.show()
} else {
settingWindow.show()
settingWindow.focus()
@ -178,25 +253,25 @@ function createTray () {
tray.on('drag-enter', () => {
if (systemPreferences.isDarkMode()) {
tray.setImage(`${__static}/upload-dark.png`)
tray!.setImage(`${__static}/upload-dark.png`)
} else {
tray.setImage(`${__static}/upload.png`)
tray!.setImage(`${__static}/upload.png`)
}
})
tray.on('drag-end', () => {
tray.setImage(`${__static}/menubar.png`)
tray!.setImage(`${__static}/menubar.png`)
})
tray.on('drop-files', async (event, files) => {
tray.on('drop-files', async (event: Event, files: string[]) => {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const imgs = await new Uploader(files, window.webContents).upload()
const imgs = await new Uploader(files, window!.webContents).upload()
if (imgs !== false) {
for (let i in imgs) {
for (let i = 0; i < imgs.length; i++) {
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i]))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
body: imgs[i].imgUrl!,
icon: files[i]
})
setTimeout(() => {
@ -204,7 +279,7 @@ function createTray () {
}, i * 100)
db.insert('uploaded', imgs[i])
}
window.webContents.send('dragFiles', imgs)
window!.webContents.send('dragFiles', imgs)
}
})
// toggleWindow()
@ -237,7 +312,7 @@ const createWindow = () => {
})
window.on('blur', () => {
window.hide()
window!.hide()
})
return window
}
@ -246,7 +321,7 @@ const createMiniWidow = () => {
if (miniWindow) {
return false
}
let obj = {
let obj: BrowserWindowOptions = {
height: 64,
width: 64,
show: process.platform === 'linux',
@ -278,7 +353,7 @@ const createMiniWidow = () => {
}
const createSettingWindow = () => {
const options = {
const options: BrowserWindowOptions = {
height: 450,
width: 800,
show: false,
@ -306,9 +381,9 @@ const createSettingWindow = () => {
}
settingWindow = new BrowserWindow(options)
settingWindow.loadURL(settingWinURL)
settingWindow!.loadURL(settingWinURL)
settingWindow.on('closed', () => {
settingWindow!.on('closed', () => {
bus.emit('toggleShortKeyModifiedMode', false)
settingWindow = null
if (process.platform === 'linux') {
@ -343,47 +418,48 @@ const createMenu = () => {
}
]
}]
// @ts-ignore
menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
}
const toggleWindow = (bounds) => {
if (window.isVisible()) {
window.hide()
const toggleWindow = (bounds: Bounds) => {
if (window!.isVisible()) {
window!.hide()
} else {
showWindow(bounds)
}
}
const showWindow = (bounds) => {
window.setPosition(bounds.x - 98 + 11, bounds.y, false)
window.webContents.send('updateFiles')
window.show()
window.focus()
const showWindow = (bounds: Bounds) => {
window!.setPosition(bounds.x - 98 + 11, bounds.y, false)
window!.webContents.send('updateFiles')
window!.show()
window!.focus()
}
const uploadClipboardFiles = async () => {
let win
if (miniWindow.isVisible()) {
if (miniWindow!.isVisible()) {
win = miniWindow
} else {
win = settingWindow || window || createSettingWindow()
}
let img = await new Uploader(undefined, win.webContents).upload()
let img = await new Uploader(undefined, win!.webContents).upload()
if (img !== false) {
if (img.length > 0) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl,
body: img[0].imgUrl!,
icon: img[0].imgUrl
})
notification.show()
db.insert('uploaded', img[0])
window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles', img)
window!.webContents.send('clipboardFiles', [])
window!.webContents.send('uploadFiles', img)
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
@ -397,17 +473,17 @@ const uploadClipboardFiles = async () => {
}
}
const uploadChoosedFiles = async (webContents, files) => {
const uploadChoosedFiles = async (webContents: WebContents, files: FileWithPath[]) => {
const input = files.map(item => item.path)
const imgs = await new Uploader(input, webContents).upload()
if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
let pasteText = ''
for (let i in imgs) {
for (let i = 0; i < imgs.length; i++) {
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
body: imgs[i].imgUrl!,
icon: files[i].path
})
setTimeout(() => {
@ -416,7 +492,7 @@ const uploadChoosedFiles = async (webContents, files) => {
db.insert('uploaded', imgs[i])
}
clipboard.writeText(pasteText)
window.webContents.send('uploadFiles', imgs)
window!.webContents.send('uploadFiles', imgs)
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
@ -426,36 +502,36 @@ const uploadChoosedFiles = async (webContents, files) => {
picgoCoreIPC(app, ipcMain)
// from macOS tray
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
const img = await new Uploader(undefined, window.webContents).upload()
ipcMain.on('uploadClipboardFiles', async () => {
const img = await new Uploader(undefined, window!.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,
body: img[0].imgUrl!,
// icon: file[0]
icon: img[0].imgUrl
})
notification.show()
db.insert('uploaded', img[0])
window.webContents.send('clipboardFiles', [])
window!.webContents.send('clipboardFiles', [])
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
}
window.webContents.send('uploadFiles')
window!.webContents.send('uploadFiles')
})
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
uploadClipboardFiles()
})
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: FileWithPath[]) => {
return uploadChoosedFiles(evt.sender, files)
})
ipcMain.on('updateShortKey', (evt, item, oldKey) => {
ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: ShortKeyConfig, oldKey: string) => {
shortKeyUpdater(globalShortcut, item, oldKey)
const notification = new Notification({
title: '操作成功',
@ -464,7 +540,7 @@ ipcMain.on('updateShortKey', (evt, item, oldKey) => {
notification.show()
})
ipcMain.on('updateCustomLink', (evt, oldLink) => {
ipcMain.on('updateCustomLink', () => {
const notification = new Notification({
title: '操作成功',
body: '你的自定义链接格式已经修改成功'
@ -472,44 +548,46 @@ ipcMain.on('updateCustomLink', (evt, oldLink) => {
notification.show()
})
ipcMain.on('autoStart', (evt, val) => {
ipcMain.on('autoStart', (evt: IpcMainEvent, val: boolean) => {
app.setLoginItemSettings({
openAtLogin: val
})
})
ipcMain.on('openSettingWindow', (evt) => {
ipcMain.on('openSettingWindow', () => {
if (!settingWindow) {
createSettingWindow()
} else {
settingWindow.show()
}
if (miniWindow) {
miniWindow.hide()
}
})
ipcMain.on('openMiniWindow', (evt) => {
ipcMain.on('openMiniWindow', () => {
if (!miniWindow) {
createMiniWidow()
}
miniWindow.show()
miniWindow.focus()
settingWindow.hide()
miniWindow!.show()
miniWindow!.focus()
settingWindow!.hide()
})
// from mini window
ipcMain.on('syncPicBed', (evt) => {
ipcMain.on('syncPicBed', () => {
if (settingWindow) {
settingWindow.webContents.send('syncPicBed')
}
})
ipcMain.on('getPicBeds', (evt) => {
ipcMain.on('getPicBeds', (evt: IpcMainEvent) => {
const picBeds = getPicBeds(app)
evt.sender.send('getPicBeds', picBeds)
evt.returnValue = picBeds
})
ipcMain.on('toggleShortKeyModifiedMode', (evt, val) => {
ipcMain.on('toggleShortKeyModifiedMode', (evt: IpcMainEvent, val: boolean) => {
bus.emit('toggleShortKeyModifiedMode', val)
})
@ -548,14 +626,22 @@ if (!gotTheLock) {
}
if (process.platform === 'win32') {
app.setAppUserModelId(pkg.build.appId)
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', () => {
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installVueDevtools()
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
createSettingWindow()
if (process.platform === 'darwin' || process.platform === 'win32') {
@ -595,6 +681,7 @@ app.on('window-all-closed', () => {
})
app.on('activate', () => {
createProtocol('picgo')
if (window === null) {
createWindow()
}
@ -613,7 +700,7 @@ app.setLoginItemSettings({
})
function initEventCenter () {
const eventList = {
const eventList: any = {
'picgo:upload': uploadClipboardFiles
}
for (let i in eventList) {
@ -621,6 +708,21 @@ function initEventCenter () {
}
}
// 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()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
/**
* Auto Updater
*

View File

@ -1,56 +0,0 @@
import db from './index'
let picBed = [
{
type: 'weibo',
name: '微博图床',
visible: true
},
{
type: 'qiniu',
name: '七牛图床',
visible: true
},
{
type: 'tcyun',
name: '腾讯云COS',
visible: true
},
{
type: 'upyun',
name: '又拍云图床',
visible: true
},
{
type: 'github',
name: 'GitHub图床',
visible: true
},
{
type: 'smms',
name: 'SM.MS图床',
visible: true
},
{
type: 'aliyun',
name: '阿里云OSS',
visible: true
},
{
type: 'imgur',
name: 'Imgur图床',
visible: true
}
]
let picBedFromDB = db.get('picBed.list') || []
let oldLength = picBedFromDB.length
let newLength = picBed.length
if (oldLength !== newLength) {
for (let i = oldLength; i < newLength; i++) {
picBedFromDB.push(picBed[i])
}
}
export default picBedFromDB

View File

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

View File

@ -1,24 +1,16 @@
import Vue from 'vue'
import axios from 'axios'
import App from './renderer/App.vue'
import router from './renderer/router'
import db from '#/datastore/index'
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'
import { webFrame } from 'electron'
import './assets/fonts/iconfont.css'
import 'element-ui/lib/theme-chalk/index.css'
import VueLazyLoad from 'vue-lazyload'
Vue.use(ElementUI)
Vue.use(VueLazyLoad)
webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0)
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
Vue.http = Vue.prototype.$http = axios
Vue.prototype.$db = db
Vue.config.productionTip = false
Vue.prototype.$builtInPicBed = [
'smms',
'weibo',
@ -29,12 +21,12 @@ Vue.prototype.$builtInPicBed = [
'aliyun',
'github'
]
Vue.config.productionTip = false
Vue.prototype.$db = db
Vue.use(ElementUI)
Vue.use(VueLazyLoad)
/* eslint-disable no-new */
new Vue({
components: { App },
router,
store,
template: '<App/>'
render: h => h(App)
}).$mount('#app')

View File

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

View File

@ -1,8 +1,10 @@
import DB from '#/datastore'
// from v2.1.2
const updateShortKeyFromVersion212 = (db, shortKeyConfig) => {
const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: ShortKeyConfigs | OldShortKeyConfigs) => {
let needUpgrade = false
if (shortKeyConfig.upload) {
needUpgrade = true
// @ts-ignore
shortKeyConfig['picgo:upload'] = {
enable: true,
key: shortKeyConfig.upload,

View File

@ -2,12 +2,6 @@ import fs from 'fs-extra'
import path from 'path'
import os from 'os'
import { remote, app } from 'electron'
if (process.env.NODE_ENV !== 'development') {
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = path.join(__dirname, '../../../static').replace(/\\/g, '\\\\')
}
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
@ -50,7 +44,7 @@ function resolveClipboardImageGenerator () {
})
}
function diffFilesAndUpdate (filePath1, filePath2) {
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
let file1 = fs.readFileSync(filePath1)
let file2 = fs.readFileSync(filePath2)

View File

@ -1,23 +1,24 @@
import path from 'path'
import db from '../../datastore'
import db from '#/datastore'
import { App } from 'electron'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const getPicBeds = (app) => {
const getPicBeds = (app: App) => {
const PicGo = requireFunc('picgo')
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const picgo = new PicGo(CONFIG_PATH)
const picBedTypes = picgo.helper.uploader.getIdList()
const picBedFromDB = db.get('picBed.list') || []
const picBeds = picBedTypes.map(item => {
const visible = picBedFromDB.find(i => i.type === item) // object or undefined
const picBeds = picBedTypes.map((item: string) => {
const visible = picBedFromDB.find((i: PicBedType) => i.type === item) // object or undefined
return {
type: item,
name: picgo.helper.uploader.get(item).name || item,
visible: visible ? visible.visible : true
}
})
}) as PicBedType[]
picgo.cmd.program.removeAllListeners()
return picBeds
}

View File

@ -2,17 +2,23 @@ import {
dialog,
BrowserWindow,
clipboard,
Notification
Notification,
IpcMain,
WebContents
} from 'electron'
import db from '../../datastore'
import db from '#/datastore'
import Uploader from './uploader'
import pasteTemplate from './pasteTemplate'
import pasteTemplate from '#/utils/pasteTemplate'
import PicGoCore from '~/universal/types/picgo'
const WEBCONTENTS = Symbol('WEBCONTENTS')
const IPCMAIN = Symbol('IPCMAIN')
const PICGO = Symbol('PICGO')
class GuiApi {
constructor (ipcMain, webcontents, picgo) {
private [WEBCONTENTS]: WebContents
private [IPCMAIN]: IpcMain
private [PICGO]: PicGoCore
constructor (ipcMain: IpcMain, webcontents: WebContents, picgo: PicGoCore) {
this[WEBCONTENTS] = webcontents
this[IPCMAIN] = ipcMain
this[PICGO] = picgo
@ -23,7 +29,7 @@ class GuiApi {
* @param {object} options
* return type is string or ''
*/
showInputBox (options) {
showInputBox (options: IShowInputBoxOption) {
if (options === undefined) {
options = {
title: '',
@ -32,7 +38,7 @@ class GuiApi {
}
this[WEBCONTENTS].send('showInputBox', options)
return new Promise((resolve, reject) => {
this[IPCMAIN].once('showInputBox', (event, value) => {
this[IPCMAIN].once('showInputBox', (event: Event, value: string) => {
resolve(value)
})
})
@ -42,12 +48,12 @@ class GuiApi {
* for plugin show file explorer
* @param {object} options
*/
showFileExplorer (options) {
showFileExplorer (options: {}) {
if (options === undefined) {
options = {}
}
return new Promise((resolve, reject) => {
dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, filename => {
dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, (filename: string) => {
resolve(filename)
})
})
@ -57,16 +63,16 @@ class GuiApi {
* for plugin to upload file
* @param {array} input
*/
async upload (input) {
async upload (input: []) {
const imgs = await new Uploader(input, this[WEBCONTENTS], this[PICGO]).upload()
if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
let pasteText = ''
for (let i in imgs) {
for (let i = 0; i < imgs.length; i++) {
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
body: imgs[i].imgUrl as string,
icon: imgs[i].imgUrl
})
setTimeout(() => {
@ -110,11 +116,11 @@ class GuiApi {
return new Promise((resolve, reject) => {
dialog.showMessageBox(
BrowserWindow.fromWebContents(this[WEBCONTENTS]),
options,
(result, checkboxChecked) => {
options
).then((res) => {
resolve({
result,
checkboxChecked
result: res.response,
checkboxChecked: res.checkboxChecked
})
})
})

View File

@ -1,5 +1,9 @@
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') {
@ -7,7 +11,7 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
return null // for uploading images in clipboard
} else if (files.length > 1) {
files = argv.slice(1)
let result = []
let result: Result = []
if (files.length > 0) {
result = files.map(item => {
if (path.isAbsolute(item)) {
@ -24,7 +28,7 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
return null
}
}
}).filter(item => item !== null)
}).filter(item => item !== null) as Result
}
return result
}

View File

@ -1,16 +1,27 @@
import path from 'path'
import GuiApi from './guiApi'
import { dialog, shell } from 'electron'
import db from '../../datastore'
import { dialog, shell, IpcMain, IpcMainEvent, App } from 'electron'
import db from '#/datastore'
import PicGoCore from '~/universal/types/picgo'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo')
const PicGo = requireFunc('picgo') as typeof PicGoCore
const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
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, type, ctx) => {
let config = []
const getConfig = (name: string, type: PicGoHelperType, ctx: PicGoCore) => {
let config: any[] = []
if (name === '') {
return config
} else {
@ -24,7 +35,7 @@ const getConfig = (name, type, ctx) => {
}
}
const handleConfigWithFunction = config => {
const handleConfigWithFunction = (config: any[]) => {
for (let i in config) {
if (typeof config[i].default === 'function') {
config[i].default = config[i].default()
@ -36,8 +47,8 @@ const handleConfigWithFunction = config => {
return config
}
const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
ipcMain.on('getPluginList', event => {
const handleGetPluginList = (ipcMain: IpcMain, STORE_PATH: string, CONFIG_PATH: string) => {
ipcMain.on('getPluginList', (event: IpcMainEvent) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginList = picgo.pluginLoader.getList()
const list = []
@ -57,7 +68,7 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
gui = true
}
}
const obj = {
const obj: IPicGoPlugin = {
name: pluginList[i].replace(/picgo-plugin-/, ''),
author: pluginPKG.author.name || pluginPKG.author,
description: pluginPKG.description,
@ -71,11 +82,11 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
},
uploader: {
name: uploaderName,
config: handleConfigWithFunction(getConfig(uploaderName, 'uploader', picgo))
config: handleConfigWithFunction(getConfig(uploaderName, PicGoHelperType.uploader, picgo))
},
transformer: {
name: transformerName,
config: handleConfigWithFunction(getConfig(uploaderName, 'transformer', picgo))
config: handleConfigWithFunction(getConfig(uploaderName, PicGoHelperType.transformer, picgo))
}
},
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
@ -90,11 +101,11 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
})
}
const handlePluginInstall = (ipcMain, CONFIG_PATH) => {
ipcMain.on('installPlugin', async (event, msg) => {
const handlePluginInstall = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('installPlugin', async (event: IpcMainEvent, msg: string) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo)
picgo.on('installSuccess', notice => {
picgo.on('installSuccess', (notice: PicGoNotice) => {
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
})
picgo.on('failed', () => {
@ -106,11 +117,11 @@ const handlePluginInstall = (ipcMain, CONFIG_PATH) => {
})
}
const handlePluginUninstall = (ipcMain, CONFIG_PATH) => {
ipcMain.on('uninstallPlugin', async (event, msg) => {
const handlePluginUninstall = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo)
picgo.on('uninstallSuccess', notice => {
picgo.on('uninstallSuccess', (notice: PicGoNotice) => {
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
})
picgo.on('failed', () => {
@ -121,8 +132,8 @@ const handlePluginUninstall = (ipcMain, CONFIG_PATH) => {
})
}
const handlePluginUpdate = (ipcMain, CONFIG_PATH) => {
ipcMain.on('updatePlugin', async (event, msg) => {
const handlePluginUpdate = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo)
picgo.on('updateSuccess', notice => {
@ -141,15 +152,15 @@ const handleNPMError = () => {
title: '发生错误',
message: '请安装Node.js并重启PicGo再继续操作',
buttons: ['Yes']
}, (res) => {
if (res === 0) {
}).then((res) => {
if (res.response === 0) {
shell.openExternal('https://nodejs.org/')
}
})
}
const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => {
ipcMain.on('getPicBedConfig', (event, type) => {
const handleGetPicBedConfig = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
const picgo = new PicGo(CONFIG_PATH)
const name = picgo.helper.uploader.get(type).name || type
if (picgo.helper.uploader.get(type).config) {
@ -162,13 +173,13 @@ const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => {
})
}
const handlePluginActions = (ipcMain, CONFIG_PATH) => {
ipcMain.on('pluginActions', (event, name, label) => {
const handlePluginActions = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
const picgo = new PicGo(CONFIG_PATH)
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
const guiApi = new GuiApi(ipcMain, event.sender, picgo)
if (plugin.guiMenu && plugin.guiMenu(picgo).length > 0) {
const menu = plugin.guiMenu(picgo)
const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
menu.forEach(item => {
if (item.label === label) {
item.handle(picgo, guiApi)
@ -178,8 +189,8 @@ const handlePluginActions = (ipcMain, CONFIG_PATH) => {
})
}
const handleRemoveFiles = (ipcMain, CONFIG_PATH) => {
ipcMain.on('removeFiles', (event, files) => {
const handleRemoveFiles = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('removeFiles', (event: IpcMainEvent, files: ImgInfo[]) => {
const picgo = new PicGo(CONFIG_PATH)
const guiApi = new GuiApi(ipcMain, event.sender, picgo)
setTimeout(() => {
@ -188,18 +199,18 @@ const handleRemoveFiles = (ipcMain, CONFIG_PATH) => {
})
}
const handlePluginShortKeyRegister = (plugin) => {
if (plugin.shortKeys && plugin.shortKeys.length > 0) {
let shortKeyConfig = db.get('settings.shortKey')
plugin.shortKeys.forEach(item => {
if (!shortKeyConfig[item.name]) {
shortKeyConfig[item.name] = item
}
})
}
}
// const handlePluginShortKeyRegister = (plugin) => {
// if (plugin.shortKeys && plugin.shortKeys.length > 0) {
// let shortKeyConfig = db.get('settings.shortKey')
// plugin.shortKeys.forEach(item => {
// if (!shortKeyConfig[item.name]) {
// shortKeyConfig[item.name] = item
// }
// })
// }
// }
export default (app, ipcMain) => {
export default (app: App, ipcMain: IpcMain) => {
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
handleGetPluginList(ipcMain, STORE_PATH, CONFIG_PATH)
@ -209,5 +220,5 @@ export default (app, ipcMain) => {
handleGetPicBedConfig(ipcMain, CONFIG_PATH)
handlePluginActions(ipcMain, CONFIG_PATH)
handleRemoveFiles(ipcMain, CONFIG_PATH)
handlePluginShortKeyRegister({})
// handlePluginShortKeyRegister({})
}

View File

@ -1,4 +1,7 @@
import bus from './eventBus'
import {
GlobalShortcut
} from 'electron'
let isInModifiedMode = false // 修改快捷键模式
bus.on('toggleShortKeyModifiedMode', flag => {
isInModifiedMode = flag
@ -7,7 +10,7 @@ bus.on('toggleShortKeyModifiedMode', flag => {
*
* @param {string} name
*/
const shortKeyHandler = (name) => {
const shortKeyHandler = (name: string) => {
if (isInModifiedMode) {
return
}
@ -20,11 +23,8 @@ const shortKeyHandler = (name) => {
/**
*
* @param {globalShortcut} globalShortcut
* @param {keyObject} item
* @param {string} oldKey
*/
const shortKeyUpdater = (globalShortcut, item, oldKey) => {
const shortKeyUpdater = (globalShortcut: GlobalShortcut, item: ShortKeyConfig, oldKey: string) => {
// 如果提供了旧key则解绑
if (oldKey) {
globalShortcut.unregister(oldKey)
@ -39,7 +39,7 @@ const shortKeyUpdater = (globalShortcut, item, oldKey) => {
}
// 初始化阶段的注册
const initShortKeyRegister = (globalShortcut, shortKeys) => {
const initShortKeyRegister = (globalShortcut: GlobalShortcut, shortKeys: ShortKeyConfig[]) => {
let errorList = []
for (let i in shortKeys) {
try {

View File

@ -1,10 +1,15 @@
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'
let release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
const isDevelopment = process.env.NODE_ENV !== 'production'
if (isDevelopment) {
release = `${release}?access_token=${process.env.GITHUB_TOKEN}`
}
const checkVersion = async () => {
let showTip = db.get('settings.showUpdateTip')
@ -13,7 +18,12 @@ const checkVersion = async () => {
showTip = true
}
if (showTip) {
const res = await axios.get(release)
let res: any
try {
res = await axios.get(release)
} catch (err) {
console.log(err)
}
if (res.status === 200) {
const latest = res.data.name
const result = compareVersion2Update(version, latest)
@ -25,11 +35,11 @@ const checkVersion = async () => {
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.set('settings.showUpdateTip', !checkboxChecked)
db.set('settings.showUpdateTip', !res.checkboxChecked)
})
}
} else {
@ -41,7 +51,7 @@ 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))

View File

@ -2,20 +2,39 @@ import {
app,
Notification,
BrowserWindow,
ipcMain
ipcMain,
WebContents
} from 'electron'
import path from 'path'
import dayjs from 'dayjs'
import PicGoCore from '~/universal/types/picgo'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo')
const PicGo = requireFunc('picgo') as typeof PicGoCore
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const renameURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080/#rename-page` : `file://${__dirname}/index.html#rename-page`
const createRenameWindow = (win) => {
let options = {
// type BrowserWindowOptions = {
// height: number,
// width: number,
// show: boolean,
// fullscreenable: boolean,
// resizable: boolean,
// vibrancy: string | any,
// webPreferences: {
// nodeIntegration: boolean,
// nodeIntegrationInWorker: boolean,
// backgroundThrottling: boolean
// },
// backgroundColor?: string
// autoHideMenuBar?: boolean
// transparent?: boolean
// }
const createRenameWindow = (win: BrowserWindow) => {
let options: BrowserWindowOptions = {
height: 175,
width: 300,
show: true,
@ -55,7 +74,7 @@ const createRenameWindow = (win) => {
return window
}
const waitForShow = (webcontent) => {
const waitForShow = (webcontent: WebContents) => {
return new Promise((resolve, reject) => {
webcontent.on('dom-ready', () => {
resolve()
@ -63,9 +82,9 @@ const waitForShow = (webcontent) => {
})
}
const waitForRename = (window, id) => {
const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
return new Promise((resolve, reject) => {
ipcMain.once(`rename${id}`, (evt, newName) => {
ipcMain.once(`rename${id}`, (evt: Event, newName: string) => {
resolve(newName)
window.hide()
})
@ -77,15 +96,18 @@ const waitForRename = (window, id) => {
}
class Uploader {
constructor (img, webContents, picgo = undefined) {
private picgo: PicGoCore
private webContents: WebContents
private img: undefined | string[]
constructor (img: undefined | string[], webContents: WebContents, picgo: PicGoCore | undefined = undefined) {
this.img = img
this.webContents = webContents
this.picgo = picgo
this.picgo = picgo || new PicGo(CONFIG_PATH)
}
upload () {
upload (): Promise<ImgInfo[]|false> {
const win = BrowserWindow.fromWebContents(this.webContents)
const picgo = this.picgo || new PicGo(CONFIG_PATH)
const picgo = this.picgo
picgo.config.debug = true
// for picgo-core
picgo.config.PICGO_ENV = 'GUI'
@ -96,8 +118,8 @@ class Uploader {
const rename = picgo.getConfig('settings.rename')
const autoRename = picgo.getConfig('settings.autoRename')
await Promise.all(ctx.output.map(async (item, index) => {
let name
let fileName
let name: undefined | string | null
let fileName: string | undefined
if (autoRename) {
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
} else {
@ -137,7 +159,7 @@ class Uploader {
return new Promise((resolve) => {
picgo.on('finished', ctx => {
if (ctx.output.every(item => item.imgUrl)) {
if (ctx.output.every((item: ImgInfo) => item.imgUrl)) {
resolve(ctx.output)
} else {
resolve(false)

View File

@ -5,9 +5,9 @@
</template>
<script>
export default {
export default {
name: 'picgo'
}
}
</script>
<style lang="stylus">

View File

@ -58,41 +58,40 @@
</el-form>
</div>
</template>
<script>
<script lang="ts">
import {
Component,
Vue,
Prop,
Watch
} from 'vue-property-decorator'
import { cloneDeep, union } from 'lodash'
export default {
name: 'config-form',
props: {
config: Array,
type: String,
id: {
type: String,
default: ''
}
},
data () {
return {
configList: [],
ruleForm: {}
}
},
created () {
},
watch: {
config: {
@Component({
name: 'config-form'
})
export default class extends Vue {
@Prop(Array) readonly config: Array<any> = []
@Prop(String) readonly type: string = ''
@Prop(String) readonly id: string = ''
configList = []
ruleForm = {}
@Watch('config', {
deep: true,
handler (val) {
immediate: true
})
handleConfigChange (val: any) {
this.ruleForm = Object.assign({}, {})
const config = this.$db.get(`picBed.${this.id}`)
if (val.length > 0) {
this.configList = cloneDeep(val).map(item => {
this.configList = cloneDeep(val).map((item: any) => {
let defaultValue = item.default !== undefined
? item.default : item.type === 'checkbox'
? [] : null
if (item.type === 'checkbox') {
const defaults = item.choices.filter(i => {
const defaults = item.choices.filter((i: any) => {
return i.checked
}).map(i => i.value)
}).map((i: any) => i.value)
defaultValue = union(defaultValue, defaults)
}
if (config && config[item.name] !== undefined) {
@ -102,14 +101,11 @@ export default {
return item
})
}
},
immediate: true
}
},
methods: {
async validate () {
return new Promise((resolve, reject) => {
this.$refs.form.validate(valid => {
// @ts-ignore
this.$refs.form.validate((valid: boolean) => {
if (valid) {
resolve(this.ruleForm)
} else {
@ -119,7 +115,6 @@ export default {
})
})
}
}
}
</script>
<style lang='stylus'>

View File

@ -136,67 +136,65 @@
</el-dialog>
</div>
</template>
<script>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import pkg from 'root/package.json'
import keyDetect from 'utils/key-binding'
import { remote } from 'electron'
import db from '~/datastore'
import keyDetect from '@/utils/key-binding'
import { remote, ipcRenderer, IpcRendererEvent } from 'electron'
import db from '#/datastore'
import mixin from '@/utils/mixin'
const { Menu, dialog, BrowserWindow } = remote
export default {
name: 'setting-page',
mixins: [mixin],
data () {
const customLinkRule = (rule, value, callback) => {
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
return {
version: process.env.NODE_ENV === 'production' ? pkg.version : 'Dev',
defaultActive: 'upload',
menu: null,
visible: false,
keyBindingVisible: false,
customLinkVisible: false,
customLink: {
}
@Component({
name: 'setting-page',
mixins: [mixin]
})
export default class extends Vue {
version = process.env.NODE_ENV === 'production' ? pkg.version : 'Dev'
defaultActive = 'upload'
menu: Electron.Menu | null = null
visible = false
keyBindingVisible = false
customLinkVisible = false
customLink = {
value: db.get('customLink') || '$url'
},
rules: {
}
rules = {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
},
os: '',
shortKey: {
}
os = ''
shortKey: ShortKeyMap = {
upload: db.get('shortKey.upload')
},
picBed: [],
}
picBed: PicBedType[] = []
// for showInputBox
showInputBoxVisible: false,
inputBoxValue: '',
inputBoxOptions: {
showInputBoxVisible = false
inputBoxValue = ''
inputBoxOptions = {
title: '',
placeholder: ''
}
}
},
created () {
this.os = process.platform
this.buildMenu()
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
this.$electron.ipcRenderer.on('showInputBox', (evt, options) => {
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
ipcRenderer.on('showInputBox', (evt: IpcRendererEvent, options: IShowInputBoxOption) => {
this.inputBoxValue = ''
this.inputBoxOptions.title = options.title || ''
this.inputBoxOptions.placeholder = options.placeholder || ''
this.showInputBoxVisible = true
})
},
methods: {
handleSelect (index) {
}
handleSelect (index: string) {
const type = index.match(/picbeds-/)
if (type === null) {
this.$router.push({
@ -217,15 +215,15 @@ export default {
})
}
}
},
}
minimizeWindow () {
const window = BrowserWindow.getFocusedWindow()
window.minimize()
},
window!.minimize()
}
closeWindow () {
const window = BrowserWindow.getFocusedWindow()
window.close()
},
window!.close()
}
buildMenu () {
const _this = this
const template = [
@ -247,50 +245,51 @@ export default {
}
]
this.menu = Menu.buildFromTemplate(template)
},
}
openDialog () {
this.menu.popup(remote.getCurrentWindow())
},
keyDetect (type, event) {
// this.menu!.popup(remote.getCurrentWindow())
this.menu!.popup()
}
keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+')
},
}
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = db.get('shortKey')
},
}
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = db.get('customLink') || '$url'
},
}
confirmCustomLink () {
this.$refs.customLink.validate((valid) => {
// @ts-ignore
this.$refs.customLink.validate((valid: boolean) => {
if (valid) {
db.set('customLink', this.customLink.value)
this.customLinkVisible = false
this.$electron.ipcRenderer.send('updateCustomLink')
ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
},
openMiniWindow () {
this.$electron.ipcRenderer.send('openMiniWindow')
},
getPicBeds (event, picBeds) {
this.picBed = picBeds
},
handleInputBoxClose () {
this.$electron.ipcRenderer.send('showInputBox', this.inputBoxValue)
}
},
beforeRouteEnter: (to, from, next) => {
next(vm => {
openMiniWindow () {
ipcRenderer.send('openMiniWindow')
}
getPicBeds (event: IpcRendererEvent, picBeds: PicBedType[]) {
this.picBed = picBeds
}
handleInputBoxClose () {
ipcRenderer.send('showInputBox', this.inputBoxValue)
}
beforeRouteEnter (to: any, from: any, next: any) {
next((vm: this) => {
vm.defaultActive = to.name
})
},
}
beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
this.$electron.ipcRenderer.removeAllListeners('showInputBox')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
ipcRenderer.removeAllListeners('showInputBox')
}
}
</script>

View File

@ -102,83 +102,84 @@
</el-dialog>
</div>
</template>
<script>
<script lang="ts">
// @ts-ignore
import gallerys from 'vue-gallery'
import pasteStyle from '~/main/utils/pasteTemplate'
export default {
import pasteStyle from '#/utils/pasteTemplate'
import { Component, Vue } from 'vue-property-decorator'
import {
ipcRenderer,
clipboard,
IpcRendererEvent
} from 'electron'
@Component({
name: 'gallery',
components: {
gallerys
},
data () {
return {
images: [],
idx: null,
options: {
}
})
export default class extends Vue {
images: ImgInfo[] = []
idx: null | number = null
options = {
titleProperty: 'fileName',
urlProperty: 'imgUrl',
closeOnSlideClick: true
},
dialogVisible: false,
imgInfo: {
}
dialogVisible = false
imgInfo = {
id: null,
imgUrl: ''
},
choosedList: {},
choosedPicBed: [],
searchText: '',
handleBarActive: false,
pasteStyle: '',
pasteStyleMap: {
}
choosedList: ObjT<boolean> = {}
choosedPicBed: string[] = []
searchText = ''
handleBarActive = false
pasteStyle = ''
pasteStyleMap = {
Markdown: 'markdown',
HTML: 'HTML',
URL: 'URL',
UBB: 'UBB',
Custom: 'Custom'
},
picBed: []
}
},
beforeRouteEnter (to, from, next) {
next(vm => {
picBed: PicBedType[] = []
beforeRouteEnter (to: any, from: any, next: any) {
next((vm: any) => {
vm.getGallery()
vm.getPasteStyle()
vm.getPicBeds()
})
},
}
created () {
this.$electron.ipcRenderer.on('updateGallery', (event) => {
ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => {
this.$nextTick(() => {
this.filterList = this.getGallery()
})
})
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
computed: {
filterList: {
get () {
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
}
get filterList () {
return this.getGallery()
},
set (val) {
return this.val
}
set filterList (val) {
this.images = val
}
},
methods: {
getPicBeds (event, picBeds) {
getPicBeds (event: IpcRendererEvent, picBeds: PicBedType[]) {
this.picBed = picBeds
},
}
getGallery () {
if (this.choosedPicBed.length > 0) {
let arr = []
let arr: ImgInfo[] = []
this.choosedPicBed.forEach(item => {
let obj = {
let obj: Obj = {
type: item
}
if (this.searchText) {
obj.fileName = this.searchText
}
// @ts-ignore
arr = arr.concat(this.$db.read().get('uploaded').filter(obj => {
return obj.fileName.indexOf(this.searchText) !== -1 && obj.type === item
}).reverse().value())
@ -187,32 +188,36 @@ export default {
} else {
if (this.searchText) {
let data = this.$db.read().get('uploaded')
// @ts-ignore
.filter(item => {
return item.fileName.indexOf(this.searchText) !== -1
}).reverse().value()
this.images = data
} else {
// @ts-ignore
this.images = this.$db.read().get('uploaded').slice().reverse().value()
}
}
return this.images
},
zoomImage (index) {
}
zoomImage (index: number) {
this.idx = index
this.changeZIndexForGallery(true)
},
changeZIndexForGallery (isOpen) {
}
changeZIndexForGallery (isOpen: boolean) {
if (isOpen) {
// @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 101
} else {
// @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 10
}
},
}
handleClose () {
this.idx = null
this.changeZIndexForGallery(false)
},
copy (item) {
}
copy (item: ImgInfo) {
const style = this.$db.get('settings.pasteStyle') || 'markdown'
const copyLink = pasteStyle(style, item)
const obj = {
@ -220,26 +225,27 @@ export default {
body: copyLink,
icon: item.url || item.imgUrl
}
const myNotification = new window.Notification(obj.title, obj)
this.$electron.clipboard.writeText(copyLink)
const myNotification = new Notification(obj.title, obj)
clipboard.writeText(copyLink)
myNotification.onclick = () => {
return true
}
},
remove (id) {
}
remove (id: string) {
this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const file = this.$db.get('uploaded').getById(id)
// @ts-ignore
this.$db.read().get('uploaded').removeById(id).write()
this.$electron.ipcRenderer.send('removeFiles', [file])
ipcRenderer.send('removeFiles', [file])
const obj = {
title: '操作结果',
body: '删除成功'
}
const myNotification = new window.Notification(obj.title, obj)
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
@ -247,43 +253,44 @@ export default {
}).catch(() => {
return true
})
},
openDialog (item) {
}
openDialog (item: ImgInfo) {
this.imgInfo.id = item.id
this.imgInfo.imgUrl = item.imgUrl
this.imgInfo.imgUrl = item.imgUrl as string
this.dialogVisible = true
},
}
confirmModify () {
this.$db.read().get('uploaded')
// @ts-ignore
.getById(this.imgInfo.id)
.assign({imgUrl: this.imgInfo.imgUrl})
.assign({ imgUrl: this.imgInfo.imgUrl })
.write()
const obj = {
title: '修改图片URL成功',
body: this.imgInfo.imgUrl,
icon: this.imgInfo.imgUrl
}
const myNotification = new window.Notification(obj.title, obj)
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
this.dialogVisible = false
this.getGallery()
},
choosePicBed (type) {
}
choosePicBed (type: string) {
let idx = this.choosedPicBed.indexOf(type)
if (idx !== -1) {
this.choosedPicBed.splice(idx, 1)
} else {
this.choosedPicBed.push(type)
}
},
}
cleanSearch () {
this.searchText = ''
},
isMultiple (obj) {
}
isMultiple (obj: Obj) {
return Object.values(obj).some(item => item)
},
}
multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
if (Object.values(this.choosedList).some(item => item)) {
@ -292,11 +299,13 @@ export default {
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let files = []
let files: ImgInfo[] = []
Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) {
// @ts-ignore
const file = this.$db.read().get('uploaded').getById(key).value()
files.push(file)
// @ts-ignore
this.$db.read().get('uploaded').removeById(key).write()
}
})
@ -306,8 +315,8 @@ export default {
title: '操作结果',
body: '删除成功'
}
this.$electron.ipcRenderer.send('removeFiles', files)
const myNotification = new window.Notification(obj.title, obj)
ipcRenderer.send('removeFiles', files)
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
@ -315,7 +324,7 @@ export default {
return true
})
}
},
}
multiCopy () {
if (Object.values(this.choosedList).some(item => item)) {
let copyString = ''
@ -323,6 +332,7 @@ export default {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) {
// @ts-ignore
const item = this.$db.read().get('uploaded').getById(key).value()
copyString += pasteStyle(style, item) + '\n'
this.choosedList[key] = false
@ -332,27 +342,26 @@ export default {
title: '批量复制链接成功',
body: copyString
}
const myNotification = new window.Notification(obj.title, obj)
this.$electron.clipboard.writeText(copyString)
const myNotification = new Notification(obj.title, obj)
clipboard.writeText(copyString)
myNotification.onclick = () => {
return true
}
}
},
}
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
},
}
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
},
handlePasteStyleChange (val) {
}
handlePasteStyleChange (val: string) {
this.$db.set('settings.pasteStyle', val)
this.pasteStyle = val
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('updateGallery')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
ipcRenderer.removeAllListeners('updateGallery')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
}
</script>

View File

@ -15,31 +15,35 @@
</div>
</div>
</template>
<script>
<script lang="ts">
import mixin from '@/utils/mixin'
export default {
import { Component, Vue, Watch } from 'vue-property-decorator'
import {
ipcRenderer,
IpcRendererEvent,
remote
} from 'electron'
@Component({
name: 'mini-page',
mixins: [mixin],
data () {
return {
logo: 'static/squareLogo.png',
dragover: false,
progress: 0,
showProgress: false,
showError: false,
dragging: false,
wX: '',
wY: '',
screenX: '',
screenY: '',
menu: null,
os: '',
picBed: []
}
},
mixins: [mixin]
})
export default class extends Vue {
logo = 'static/squareLogo.png'
dragover = false
progress = 0
showProgress = false
showError = false
dragging = false
wX: number = -1
wY: number = -1
screenX: number = -1
screenY: number = -1
menu: Electron.Menu | null = null
os = ''
picBed: PicBedType[] = []
created () {
this.os = process.platform
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => {
ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => {
if (progress !== -1) {
this.showProgress = true
this.progress = progress
@ -49,14 +53,15 @@ export default {
}
})
this.getPicBeds()
},
}
mounted () {
window.addEventListener('mousedown', this.handleMouseDown, false)
window.addEventListener('mousemove', this.handleMouseMove, false)
window.addEventListener('mouseup', this.handleMouseUp, false)
},
watch: {
progress (val) {
}
@Watch('progress')
onProgressChange (val: number) {
if (val === 100) {
setTimeout(() => {
this.showProgress = false
@ -67,25 +72,25 @@ export default {
}, 1200)
}
}
},
methods: {
getPicBeds () {
this.picBed = this.$electron.ipcRenderer.sendSync('getPicBeds')
this.picBed = ipcRenderer.sendSync('getPicBeds')
this.buildMenu()
},
onDrop (e) {
}
onDrop (e: DragEvent) {
this.dragover = false
this.ipcSendFiles(e.dataTransfer.files)
},
this.ipcSendFiles(e.dataTransfer!.files)
}
openUploadWindow () {
// @ts-ignore
document.getElementById('file-uploader').click()
},
onChange (e) {
}
onChange (e: any) {
this.ipcSendFiles(e.target.files)
// @ts-ignore
document.getElementById('file-uploader').value = ''
},
ipcSendFiles (files) {
let sendFiles = []
}
ipcSendFiles (files: FileList) {
let sendFiles: FileWithPath[] = []
Array.from(files).forEach((item, index) => {
let obj = {
name: item.name,
@ -93,30 +98,30 @@ export default {
}
sendFiles.push(obj)
})
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
},
handleMouseDown (e) {
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
handleMouseDown (e: MouseEvent) {
this.dragging = true
this.wX = e.pageX
this.wY = e.pageY
this.screenX = e.screenX
this.screenY = e.screenY
},
handleMouseMove (e) {
}
handleMouseMove (e: MouseEvent) {
e.preventDefault()
e.stopPropagation()
if (this.dragging) {
const xLoc = e.screenX - this.wX
const yLoc = e.screenY - this.wY
this.$electron.remote.BrowserWindow.getFocusedWindow().setBounds({
remote.BrowserWindow.getFocusedWindow()!.setBounds({
x: xLoc,
y: yLoc,
width: 64,
height: 64
})
}
},
handleMouseUp (e) {
}
handleMouseUp (e: MouseEvent) {
this.dragging = false
if (this.screenX === e.screenX && this.screenY === e.screenY) {
if (e.button === 0) { // left mouse
@ -126,10 +131,10 @@ export default {
this.openContextMenu()
}
}
},
}
openContextMenu () {
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
this.menu!.popup()
}
buildMenu () {
const _this = this
const submenu = this.picBed.map(item => {
@ -139,7 +144,7 @@ export default {
checked: this.$db.get('picBed.current') === item.type,
click () {
_this.$db.set('picBed.current', item.type)
_this.$electron.ipcRenderer.send('syncPicBed')
ipcRenderer.send('syncPicBed')
}
}
})
@ -147,7 +152,7 @@ export default {
{
label: '打开详细窗口',
click () {
_this.$electron.ipcRenderer.send('openSettingWindow')
ipcRenderer.send('openSettingWindow')
}
},
{
@ -158,7 +163,7 @@ export default {
{
label: '剪贴板图片上传',
click () {
_this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
},
{
@ -168,8 +173,8 @@ export default {
{
label: '重启应用',
click () {
_this.$electron.remote.app.relaunch()
_this.$electron.remote.app.exit(0)
remote.app.relaunch()
remote.app.exit(0)
}
},
{
@ -177,12 +182,12 @@ export default {
label: '退出'
}
]
this.menu = this.$electron.remote.Menu.buildFromTemplate(template)
// @ts-ignore
this.menu = remote.Menu.buildFromTemplate(template)
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
window.removeEventListener('mousedown', this.handleMouseDown, false)
window.removeEventListener('mousemove', this.handleMouseMove, false)
window.removeEventListener('mouseup', this.handleMouseUp, false)

View File

@ -241,144 +241,148 @@
</el-dialog>
</div>
</template>
<script>
import keyDetect from 'utils/key-binding'
<script lang="ts">
import keyDetect from '@/utils/key-binding'
import pkg from 'root/package.json'
import path from 'path'
import {
ipcRenderer,
remote
} from 'electron'
import { Component, Vue } from 'vue-property-decorator'
import db from '#/datastore'
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
export default {
name: 'picgo-setting',
computed: {
needUpdate () {
if (this.latestVersion) {
return this.compareVersion2Update(this.version, this.latestVersion)
} else {
return false
}
}
},
data () {
const customLinkRule = (rule, value, callback) => {
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
let logLevel = this.$db.get('settings.logLevel')
if (!Array.isArray(logLevel)) {
}
let logLevel = db.get('settings.logLevel')
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
} else {
logLevel = ['all']
}
}
return {
form: {
updateHelper: this.$db.get('settings.showUpdateTip'),
}
@Component({
name: 'picgo-setting'
})
export default class extends Vue {
form: ISettingForm = {
updateHelper: db.get('settings.showUpdateTip'),
showPicBedList: [],
autoStart: this.$db.get('settings.autoStart') || false,
rename: this.$db.get('settings.rename') || false,
autoRename: this.$db.get('settings.autoRename') || false,
uploadNotification: this.$db.get('settings.uploadNotification') || false,
miniWindowOntop: this.$db.get('settings.miniWindowOntop') || false,
autoStart: db.get('settings.autoStart') || false,
rename: db.get('settings.rename') || false,
autoRename: db.get('settings.autoRename') || false,
uploadNotification: db.get('settings.uploadNotification') || false,
miniWindowOntop: db.get('settings.miniWindowOntop') || false,
logLevel
},
picBed: [],
logFileVisible: false,
keyBindingVisible: false,
customLinkVisible: false,
checkUpdateVisible: false,
proxyVisible: false,
customLink: {
value: this.$db.get('settings.customLink') || '$url'
},
shortKey: {
upload: this.$db.get('settings.shortKey.upload')
},
proxy: this.$db.get('picBed.proxy') || undefined,
rules: {
}
picBed: PicBedType[] = []
logFileVisible = false
keyBindingVisible = false
customLinkVisible = false
checkUpdateVisible = false
proxyVisible = false
customLink = {
value: db.get('settings.customLink') || '$url'
}
shortKey: ShortKeyMap = {
upload: db.get('settings.shortKey.upload')
}
proxy = db.get('picBed.proxy') || ''
rules = {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
},
logLevel: {
}
logLevel = {
all: '全部-All',
success: '成功-Success',
error: '错误-Error',
info: '普通-Info',
warn: '提醒-Warn',
none: '不记录日志-None'
},
version: pkg.version,
latestVersion: '',
os: ''
}
},
version = pkg.version
latestVersion = ''
os = ''
get needUpdate () {
if (this.latestVersion) {
return this.compareVersion2Update(this.version, this.latestVersion)
} else {
return false
}
}
created () {
this.os = process.platform
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
methods: {
getPicBeds (event, picBeds) {
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
}
getPicBeds (event: Event, picBeds: PicBedType[]) {
this.picBed = picBeds
this.form.showPicBedList = this.picBed.map(item => {
if (item.visible) {
return item.name
}
})
},
openFile (file) {
const { app, shell } = this.$electron.remote
}) as string[]
}
openFile (file: string) {
const { app, shell } = remote
const STORE_PATH = app.getPath('userData')
const FILE = path.join(STORE_PATH, `/${file}`)
shell.openItem(FILE)
},
}
openLogSetting () {
this.logFileVisible = true
},
keyDetect (type, event) {
}
keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+')
},
}
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = this.$db.get('settings.shortKey')
},
this.shortKey = db.get('settings.shortKey')
}
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = this.$db.get('settings.customLink') || '$url'
},
this.customLink.value = db.get('settings.customLink') || '$url'
}
confirmCustomLink () {
this.$refs.customLink.validate((valid) => {
// @ts-ignore
this.$refs.customLink.validate((valid: boolean) => {
if (valid) {
this.$db.set('settings.customLink', this.customLink.value)
db.set('settings.customLink', this.customLink.value)
this.customLinkVisible = false
this.$electron.ipcRenderer.send('updateCustomLink')
ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
},
}
cancelProxy () {
this.proxyVisible = false
this.proxy = this.$db.get('picBed.proxy') || undefined
},
this.proxy = db.get('picBed.proxy') || undefined
}
confirmProxy () {
this.proxyVisible = false
this.$db.set('picBed.proxy', this.proxy)
const successNotification = new window.Notification('设置代理', {
db.set('picBed.proxy', this.proxy)
const successNotification = new Notification('设置代理', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
},
updateHelperChange (val) {
this.$db.set('settings.showUpdateTip', val)
},
handleShowPicBedListChange (val) {
}
updateHelperChange (val: boolean) {
db.set('settings.showUpdateTip', val)
}
handleShowPicBedListChange (val: string[]) {
const list = this.picBed.map(item => {
if (!val.includes(item.name)) {
item.visible = false
@ -387,20 +391,20 @@ export default {
}
return item
})
this.$db.set('picBed.list', list)
this.$electron.ipcRenderer.send('getPicBeds')
},
handleAutoStartChange (val) {
this.$db.set('settings.autoStart', val)
this.$electron.ipcRenderer.send('autoStart', val)
},
handleRename (val) {
this.$db.set('settings.rename', val)
},
handleAutoRename (val) {
this.$db.set('settings.autoRename', val)
},
compareVersion2Update (current, latest) {
db.set('picBed.list', list)
ipcRenderer.send('getPicBeds')
}
handleAutoStartChange (val: boolean) {
db.set('settings.autoStart', val)
ipcRenderer.send('autoStart', val)
}
handleRename (val: boolean) {
db.set('settings.rename', val)
}
handleAutoRename (val: boolean) {
db.set('settings.autoRename', val)
}
compareVersion2Update (current: string, latest: string) {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
@ -413,7 +417,7 @@ export default {
}
}
return false
},
}
checkUpdate () {
this.checkUpdateVisible = true
this.$http.get(release)
@ -422,39 +426,39 @@ export default {
}).catch(err => {
console.log(err)
})
},
}
confirmCheckVersion () {
if (this.needUpdate) {
this.$electron.remote.shell.openExternal(downloadUrl)
remote.shell.openExternal(downloadUrl)
}
this.checkUpdateVisible = false
},
}
cancelCheckVersion () {
this.checkUpdateVisible = false
},
handleUploadNotification (val) {
this.$db.set('settings.uploadNotification', val)
},
handleMiniWindowOntop (val) {
this.$db.set('settings.miniWindowOntop', val)
this.$message('需要重启生效')
},
}
handleUploadNotification (val: boolean) {
db.set('settings.uploadNotification', val)
}
handleMiniWindowOntop (val: boolean) {
db.set('settings.miniWindowOntop', val)
this.$message.info('需要重启生效')
}
confirmLogLevelSetting () {
if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级')
}
this.$db.set('settings.logLevel', this.form.logLevel)
const successNotification = new window.Notification('设置日志', {
db.set('settings.logLevel', this.form.logLevel)
const successNotification = new Notification('设置日志', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
this.logFileVisible = false
},
}
cancelLogLevelSetting () {
this.logFileVisible = false
let logLevel = this.$db.get('settings.logLevel')
let logLevel = db.get('settings.logLevel')
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
@ -463,8 +467,8 @@ export default {
}
}
this.form.logLevel = logLevel
},
handleLevelDisabled (val) {
}
handleLevelDisabled (val: string) {
let currentLevel = val
let flagLevel
let result = this.form.logLevel.some(item => {
@ -483,16 +487,15 @@ export default {
}
}
return false
},
goConfigPage () {
this.$electron.remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
},
goShortCutPage () {
this.$router.push('shortcut-page')
}
},
goConfigPage () {
remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
}
goShortCutPage () {
this.$router.push('shortcut')
}
beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
}
</script>

View File

@ -93,41 +93,49 @@
</el-dialog>
</div>
</template>
<script>
import ConfigForm from '@/components/ConfigForm'
<script lang="ts">
import {
Component,
Vue,
Watch
} from 'vue-property-decorator'
import ConfigForm from '@/components/ConfigForm.vue'
import { debounce } from 'lodash'
export default {
import {
ipcRenderer,
remote,
IpcRendererEvent
} from 'electron'
const { Menu } = remote
@Component({
name: 'plugin',
components: {
ConfigForm
},
data () {
return {
searchText: '',
pluginList: [],
menu: null,
config: [],
currentType: '',
configName: '',
dialogVisible: false,
pluginNameList: [],
loading: true,
needReload: false,
id: '',
os: ''
}
},
computed: {
npmSearchText () {
})
export default class extends Vue {
searchText = ''
pluginList: IPicGoPlugin[] = []
menu: Electron.Menu | null = null
config: any[] = []
currentType = ''
configName = ''
dialogVisible = false
pluginNameList: string[] = []
loading = true
needReload = false
id = ''
os = ''
get npmSearchText () {
return this.searchText.match('picgo-plugin-')
? this.searchText
: this.searchText !== ''
? `picgo-plugin-${this.searchText}`
: this.searchText
}
},
watch: {
npmSearchText (val) {
@Watch('npmSearchText')
onNpmSearchTextChange (val: string) {
if (val) {
this.loading = true
this.pluginList = []
@ -136,15 +144,14 @@ export default {
this.getPluginList()
}
}
},
created () {
this.os = process.platform
this.$electron.ipcRenderer.on('pluginList', (evt, list) => {
ipcRenderer.on('pluginList', (evt: IpcRendererEvent, list: IPicGoPlugin[]) => {
this.pluginList = list
this.pluginNameList = list.map(item => item.name)
this.loading = false
})
this.$electron.ipcRenderer.on('installSuccess', (evt, plugin) => {
ipcRenderer.on('installSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false
this.pluginList.forEach(item => {
if (item.name === plugin) {
@ -153,7 +160,7 @@ export default {
}
})
})
this.$electron.ipcRenderer.on('updateSuccess', (evt, plugin) => {
ipcRenderer.on('updateSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false
this.pluginList.forEach(item => {
if (item.name === plugin) {
@ -165,7 +172,7 @@ export default {
this.handleReload()
this.getPluginList()
})
this.$electron.ipcRenderer.on('uninstallSuccess', (evt, plugin) => {
ipcRenderer.on('uninstallSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false
this.pluginList = this.pluginList.filter(item => {
if (item.name === plugin) { // restore Uploader & Transformer after uninstalling
@ -184,9 +191,8 @@ export default {
this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = this.$db.get('needReload')
},
methods: {
buildContextMenu (plugin) {
}
buildContextMenu (plugin: IPicGoPlugin) {
const _this = this
let menu = [{
label: '启用插件',
@ -252,28 +258,29 @@ export default {
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (let i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
_this.$electron.ipcRenderer.send('pluginActions', plugin.name, i.label)
ipcRenderer.send('pluginActions', plugin.name, i.label)
}
})
}
}
this.menu = this.$electron.remote.Menu.buildFromTemplate(menu)
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
this.menu = Menu.buildFromTemplate(menu)
this.menu.popup()
}
getPluginList () {
this.$electron.ipcRenderer.send('getPluginList')
},
ipcRenderer.send('getPluginList')
}
getPicBeds () {
this.$electron.ipcRenderer.send('getPicBeds')
},
installPlugin (item) {
ipcRenderer.send('getPicBeds')
}
installPlugin (item: IPicGoPlugin) {
if (!item.gui) {
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
confirmButtonText: '确定',
@ -281,57 +288,58 @@ export default {
type: 'warning'
}).then(() => {
item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
ipcRenderer.send('installPlugin', item.name)
}).catch(() => {
console.log('Install canceled')
})
} else {
item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
ipcRenderer.send('installPlugin', item.name)
}
},
uninstallPlugin (val) {
}
uninstallPlugin (val: string) {
this.pluginList.forEach(item => {
if (item.name === val) {
item.ing = true
}
})
this.$electron.ipcRenderer.send('uninstallPlugin', val)
},
updatePlugin (val) {
ipcRenderer.send('uninstallPlugin', val)
}
updatePlugin (val: string) {
this.pluginList.forEach(item => {
if (item.name === val) {
item.ing = true
}
})
this.$electron.ipcRenderer.send('updatePlugin', val)
},
ipcRenderer.send('updatePlugin', val)
}
reloadApp () {
this.$electron.remote.app.relaunch()
this.$electron.remote.app.exit(0)
},
remote.app.relaunch()
remote.app.exit(0)
}
handleReload () {
this.$db.set('needReload', true)
this.needReload = true
const successNotification = new window.Notification('更新成功', {
const successNotification = new Notification('更新成功', {
body: '请点击此通知重启应用以生效'
})
successNotification.onclick = () => {
this.reloadApp()
}
},
}
cleanSearch () {
this.searchText = ''
},
toggleTransformer (transformer) {
}
toggleTransformer (transformer: string) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.$db.set('picBed.transformer', 'path')
} else {
this.$db.set('picBed.transformer', transformer)
}
},
}
async handleConfirmConfig () {
// @ts-ignore
const result = await this.$refs.configForm.validate()
if (result !== false) {
switch (this.currentType) {
@ -345,7 +353,7 @@ export default {
this.$db.set(`transformer.${this.configName}`, result)
break
}
const successNotification = new window.Notification('设置结果', {
const successNotification = new Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
@ -354,12 +362,12 @@ export default {
this.dialogVisible = false
this.getPluginList()
}
},
getSearchResult: function (val) {
}
getSearchResult (val: string) {
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
.then(res => {
this.pluginList = res.data.objects.map(item => {
.then((res: INPMSearchResult) => {
this.pluginList = res.data.objects.map((item: INPMSearchResultObject) => {
return this.handleSearchResult(item)
})
this.loading = false
@ -368,8 +376,8 @@ export default {
console.log(err)
this.loading = false
})
},
handleSearchResult (item) {
}
handleSearchResult (item: INPMSearchResultObject) {
const name = item.package.name.replace(/picgo-plugin-/, '')
let gui = false
if (item.package.keywords && item.package.keywords.length > 0) {
@ -389,9 +397,9 @@ export default {
gui,
ing: false // installing or uninstalling
}
},
}
// restore Uploader & Transformer
handleRestoreState (item, name) {
handleRestoreState (item: string, name: string) {
if (item === 'uploader') {
const current = this.$db.get('picBed.current')
if (current === name) {
@ -404,21 +412,20 @@ export default {
this.$db.set('picBed.transformer', 'path')
}
}
},
openHomepage (url) {
}
openHomepage (url: string) {
if (url) {
this.$electron.remote.shell.openExternal(url)
remote.shell.openExternal(url)
}
}
},
goAwesomeList () {
this.$electron.remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('pluginList')
this.$electron.ipcRenderer.removeAllListeners('installSuccess')
this.$electron.ipcRenderer.removeAllListeners('uninstallSuccess')
this.$electron.ipcRenderer.removeAllListeners('updateSuccess')
ipcRenderer.removeAllListeners('pluginList')
ipcRenderer.removeAllListeners('installSuccess')
ipcRenderer.removeAllListeners('uninstallSuccess')
ipcRenderer.removeAllListeners('updateSuccess')
}
}
</script>

View File

@ -21,33 +21,34 @@
</el-row>
</div>
</template>
<script>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/mixin'
export default {
import {
ipcRenderer,
IpcRendererEvent
} from 'electron'
@Component({
name: 'rename-page',
mixins: [mixin],
data () {
return {
fileName: '',
id: null
}
},
mixins: [mixin]
})
export default class extends Vue {
fileName: string = ''
id: string | null = null
created () {
this.$electron.ipcRenderer.on('rename', (event, name, id) => {
ipcRenderer.on('rename', (event: IpcRendererEvent, name: string, id: string) => {
this.fileName = name
this.id = id
})
},
methods: {
confirmName () {
this.$electron.ipcRenderer.send(`rename${this.id}`, this.fileName)
},
cancel () {
this.$electron.ipcRenderer.send(`rename${this.id}`, null)
}
},
confirmName () {
ipcRenderer.send(`rename${this.id}`, this.fileName)
}
cancel () {
ipcRenderer.send(`rename${this.id}`, null)
}
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('rename')
ipcRenderer.removeAllListeners('rename')
}
}
</script>

View File

@ -0,0 +1,181 @@
<template>
<div id="shortcut-page">
<div class="view-title">
快捷键设置
</div>
<el-row>
<el-col :span="20" :offset="2">
<el-table
:data="list"
size="mini"
>
<el-table-column
label="快捷键名称"
>
<template slot-scope="scope">
{{ scope.row.label ? scope.row.label : scope.row.name }}
</template>
</el-table-column>
<el-table-column
width="180px"
label="快捷键绑定"
prop="key"
>
</el-table-column>
<el-table-column
label="状态"
>
<template slot-scope="scope">
<el-tag
size="mini"
:type="scope.row.enable ? 'success' : 'danger'"
>
{{ scope.row.enable ? '已启用' : '已禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="来源"
>
<template slot-scope="scope">
{{ calcOrigin(scope.row.name) }}
</template>
</el-table-column>
<el-table-column
label="操作"
>
<template slot-scope="scope">
<el-button
@click="toggleEnable(scope.row)"
size="mini"
:class="{
disabled: scope.row.enable
}"
type="text">
{{ scope.row.enable ? '禁用' : '启用' }}
</el-button>
<el-button
class="edit"
size="mini"
@click="openKeyBindingDialog(scope.row.name, scope.$index)"
type="text">
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-dialog
title="修改上传快捷键"
:visible.sync="keyBindingVisible"
:modal-append-to-body="false"
>
<el-form
label-position="top"
label-width="80px"
>
<el-form-item
label="快捷上传"
>
<el-input
class="align-center"
@keydown.native.prevent="keyDetect($event)"
v-model="shortKey"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelKeyBinding" round>取消</el-button>
<el-button type="primary" @click="confirmKeyBinding" round>确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import keyDetect from '@/utils/key-binding'
import { ipcRenderer } from 'electron'
@Component({
name: 'shortcut-page'
})
export default class extends Vue {
list: ShortKeyConfig[] = []
keyBindingVisible = false
shortKeyName = ''
shortKey = ''
currentIndex = 0
created () {
const shortKeyConfig = this.$db.get('settings.shortKey') as ShortKeyConfigs
this.list = Object.keys(shortKeyConfig).map(item => shortKeyConfig[item])
}
@Watch('keyBindingVisible')
onKeyBindingVisibleChange (val: boolean) {
ipcRenderer.send('toggleShortKeyModifiedMode', val)
}
calcOrigin (item: string) {
const [origin] = item.split(':')
return origin
}
toggleEnable (item: ShortKeyConfig) {
const status = !item.enable
item.enable = status
this.$db.set(`settings.shortKey.${item.name}.enable`, status)
ipcRenderer.send('updateShortKey', item)
}
keyDetect (event: KeyboardEvent) {
this.shortKey = keyDetect(event).join('+')
}
openKeyBindingDialog (name: string, index: number) {
this.shortKeyName = name
this.shortKey = this.$db.get(`settings.shortKey.${name}.key`)
this.currentIndex = index
this.keyBindingVisible = true
}
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = this.$db.get(`settings.shortKey.${this.shortKeyName}.key`)
}
confirmKeyBinding () {
const oldKey = this.$db.get(`settings.shortKey.${this.shortKeyName}.key`)
this.$db.set(`settings.shortKey.${this.shortKeyName}.key`, this.shortKey)
const newKey = this.$db.get(`settings.shortKey.${this.shortKeyName}`)
ipcRenderer.send('updateShortKey', newKey, oldKey)
this.list[this.currentIndex].key = this.shortKey
this.keyBindingVisible = false
}
beforeDestroy () {
ipcRenderer.send('toggleShortKeyModifiedMode', false)
}
}
</script>
<style lang='stylus'>
#shortcut-page
.el-dialog__body
padding 10px 20px
.el-form-item
margin-bottom 0
.el-button
&.disabled
color: #F56C6C
&.edit
color: #67C23A
.el-table
background-color: transparent
color #ddd
thead
color #bbb
th,tr
background-color: transparent
&__body
tr.el-table__row--striped
td
background transparent
&--enable-row-hover
.el-table__body
tr:hover
&>td
background #333
</style>

View File

@ -25,73 +25,44 @@
</div>
</template>
<script>
import mixin from '@/utils/mixin'
import pasteTemplate from '~/main/utils/pasteTemplate'
export default {
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/mixin'
import pasteTemplate from '#/utils/pasteTemplate'
import { ipcRenderer, clipboard } from 'electron'
@Component({
name: 'tray-page',
mixins: [mixin],
data () {
return {
files: [],
notification: {
mixins: [mixin]
})
export default class extends Vue {
files = []
notification = {
title: '复制链接成功',
body: '',
icon: ''
},
clipboardFiles: [],
uploadFlag: false
}
},
computed: {
reverseList () {
clipboardFiles: ImgInfo[] = []
uploadFlag = false
get reverseList () {
return this.files.slice().reverse()
}
},
mounted () {
this.disableDragFile()
this.getData()
this.$electron.ipcRenderer.on('dragFiles', (event, files) => {
files.forEach(item => {
this.$db.insert('uploaded', item)
})
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
})
this.$electron.ipcRenderer.on('clipboardFiles', (event, files) => {
this.clipboardFiles = files
})
this.$electron.ipcRenderer.on('uploadFiles', (event) => {
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
console.log(this.files)
this.uploadFlag = false
})
this.$electron.ipcRenderer.on('updateFiles', (event) => {
this.getData()
})
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('dragFiles')
this.$electron.ipcRenderer.removeAllListeners('clipboardFiles')
this.$electron.ipcRenderer.removeAllListeners('uploadClipboardFiles')
this.$electron.ipcRenderer.removeAllListeners('updateFiles')
},
methods: {
getData () {
// @ts-ignore
this.files = this.$db.read().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)
}
copyTheLink (item: ImgInfo) {
this.notification.body = item.imgUrl!
this.notification.icon = item.imgUrl!
const myNotification = new Notification(this.notification.title, this.notification)
const pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
this.$electron.clipboard.writeText(pasteTemplate(pasteStyle, item))
clipboard.writeText(pasteTemplate(pasteStyle, item))
myNotification.onclick = () => {
return true
}
},
calcHeight (width, height) {
}
calcHeight (width: number, height: number): number {
return height * 160 / width
},
}
disableDragFile () {
window.addEventListener('dragover', (e) => {
e = e || event
@ -101,16 +72,44 @@
e = e || event
e.preventDefault()
}, false)
},
}
uploadClipboardFiles () {
if (this.uploadFlag) {
return
}
this.uploadFlag = true
this.$electron.ipcRenderer.send('uploadClipboardFiles')
ipcRenderer.send('uploadClipboardFiles')
}
mounted () {
this.disableDragFile()
this.getData()
ipcRenderer.on('dragFiles', (event: Event, files: string[]) => {
files.forEach(item => {
this.$db.insert('uploaded', item)
})
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
})
ipcRenderer.on('clipboardFiles', (event: Event, files: ImgInfo[]) => {
this.clipboardFiles = files
})
ipcRenderer.on('uploadFiles', (event: Event) => {
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
console.log(this.files)
this.uploadFlag = false
})
ipcRenderer.on('updateFiles', (event: Event) => {
this.getData()
})
}
beforeDestroy () {
ipcRenderer.removeAllListeners('dragFiles')
ipcRenderer.removeAllListeners('clipboardFiles')
ipcRenderer.removeAllListeners('uploadClipboardFiles')
ipcRenderer.removeAllListeners('updateFiles')
}
}
</script>
<style lang="stylus">

View File

@ -25,7 +25,7 @@
:show-text="false"
class="upload-progress"
:class="{ 'show': showProgress }"
:status="showError ? 'exception' : 'text'"
:status="showError ? 'exception' : undefined"
></el-progress>
<div class="paste-style">
<div class="el-col-16">
@ -55,23 +55,28 @@
</el-row>
</div>
</template>
<script>
export default {
name: 'upload',
data () {
return {
dragover: false,
progress: 0,
showProgress: false,
showError: false,
pasteStyle: '',
picBed: [],
picBedName: '',
menu: null
}
},
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import {
ipcRenderer,
IpcRendererEvent,
remote
} from 'electron'
const { Menu } = remote
@Component({
name: 'upload'
})
export default class extends Vue {
dragover = false
progress = 0
showProgress = false
showError = false
pasteStyle = ''
picBed: PicBedType[] = []
picBedName = ''
menu: Electron.Menu | null= null
mounted () {
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => {
ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => {
if (progress !== -1) {
this.showProgress = true
this.progress = progress
@ -82,14 +87,14 @@ export default {
})
this.getPasteStyle()
this.getDefaultPicBed()
this.$electron.ipcRenderer.on('syncPicBed', () => {
ipcRenderer.on('syncPicBed', () => {
this.getDefaultPicBed()
})
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
watch: {
progress (val) {
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
}
@Watch('progress')
onProgressChange (val: number) {
if (val === 100) {
setTimeout(() => {
this.showProgress = false
@ -100,26 +105,24 @@ export default {
}, 1200)
}
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
this.$electron.ipcRenderer.removeAllListeners('syncPicBed')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
},
methods: {
onDrop (e) {
ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeAllListeners('syncPicBed')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
onDrop (e: DragEvent) {
this.dragover = false
this.ipcSendFiles(e.dataTransfer.files)
},
this.ipcSendFiles(e.dataTransfer!.files)
}
openUplodWindow () {
document.getElementById('file-uploader').click()
},
onChange (e) {
this.ipcSendFiles(e.target.files)
document.getElementById('file-uploader').value = ''
},
ipcSendFiles (files) {
let sendFiles = []
document.getElementById('file-uploader')!.click()
}
onChange (e: any) {
this.ipcSendFiles(e.target.files);
(document.getElementById('file-uploader') as HTMLInputElement).value = ''
}
ipcSendFiles (files: FileList) {
let sendFiles: FileWithPath[] = []
Array.from(files).forEach((item, index) => {
let obj = {
name: item.name,
@ -127,33 +130,34 @@ export default {
}
sendFiles.push(obj)
})
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
},
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
},
handlePasteStyleChange (val) {
}
handlePasteStyleChange (val: string) {
this.$db.set('settings.pasteStyle', val)
},
}
uploadClipboardFiles () {
this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
},
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
getDefaultPicBed () {
const current = this.$db.get('picBed.current')
const current: string = this.$db.get('picBed.current')
this.picBed.forEach(item => {
if (item.type === current) {
this.picBedName = item.name
}
})
},
getPicBeds (event, picBeds) {
}
getPicBeds (event: Event, picBeds: PicBedType[]) {
this.picBed = picBeds
this.getDefaultPicBed()
},
}
handleChangePicBed () {
this.buildMenu()
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
// this.menu.popup(remote.getCurrentWindow())
this.menu!.popup()
}
buildMenu () {
const _this = this
const submenu = this.picBed.map(item => {
@ -163,12 +167,12 @@ export default {
checked: this.$db.get('picBed.current') === item.type,
click () {
_this.$db.set('picBed.current', item.type)
_this.$electron.ipcRenderer.send('syncPicBed')
ipcRenderer.send('syncPicBed')
}
}
})
this.menu = this.$electron.remote.Menu.buildFromTemplate(submenu)
}
// @ts-ignore
this.menu = Menu.buildFromTemplate(submenu)
}
}
</script>
@ -235,4 +239,6 @@ export default {
border-radius 0 14px 14px 0
.paste-upload
width 100%
.el-icon-caret-bottom
cursor pointer
</style>

View File

@ -1,108 +0,0 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'tray-page',
component: require('@/pages/TrayPage').default
},
{
path: '/rename-page',
name: 'rename-page',
component: require('@/pages/RenamePage').default
},
{
path: '/mini-page',
name: 'mini-page',
component: require('@/pages/MiniPage').default
},
{
path: '/setting',
name: 'setting-page',
component: require('@/layouts/SettingPage').default,
children: [
{
path: 'upload',
component: require('@/pages/Upload').default,
name: 'upload'
},
{
path: 'weibo',
component: require('@/pages/picbeds/Weibo').default,
name: 'weibo'
},
{
path: 'qiniu',
component: require('@/pages/picbeds/Qiniu').default,
name: 'qiniu'
},
{
path: 'tcyun',
component: require('@/pages/picbeds/TcYun').default,
name: 'tcyun'
},
{
path: 'upyun',
component: require('@/pages/picbeds/UpYun').default,
name: 'upyun'
},
{
path: 'github',
component: require('@/pages/picbeds/GitHub').default,
name: 'github'
},
{
path: 'smms',
component: require('@/pages/picbeds/SMMS').default,
name: 'smms'
},
{
path: 'aliyun',
component: require('@/pages/picbeds/AliYun').default,
name: 'aliyun'
},
{
path: 'imgur',
component: require('@/pages/picbeds/Imgur').default,
name: 'imgur'
},
{
path: 'others/:type',
component: require('@/pages/picbeds/Others').default,
name: 'others'
},
{
path: 'gallery',
component: require('@/pages/Gallery').default,
name: 'gallery',
meta: {
keepAlive: true
}
},
{
path: 'setting',
component: require('@/pages/PicGoSetting').default,
name: 'setting'
},
{
path: 'plugin',
component: require('@/pages/Plugin').default,
name: 'plugin'
},
{
path: 'shortcut-page',
component: require('@/pages/ShortCutPage').default,
name: 'shortcut-page'
}
]
},
{
path: '*',
redirect: '/'
}
]
})

View File

@ -0,0 +1,108 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'tray-page',
component: () => import(/* webpackChunkName: "tray" */ '@/pages/TrayPage.vue')
},
{
path: '/rename-page',
name: 'rename-page',
component: () => import(/* webpackChunkName: "RenamePage" */ '@/pages/RenamePage.vue')
},
{
path: '/mini-page',
name: 'mini-page',
component: () => import(/* webpackChunkName: "MiniPage" */ '@/pages/MiniPage.vue')
},
{
path: '/setting',
name: 'setting-page',
component: () => import(/* webpackChunkName: "SettingPage" */ '@/layouts/SettingPage.vue'),
children: [
{
path: 'upload',
component: () => import(/* webpackChunkName: "Upload" */ '@/pages/Upload.vue'),
name: 'upload'
},
{
path: 'weibo',
component: () => import(/* webpackChunkName: "Weibo" */ '@/pages/picbeds/Weibo.vue'),
name: 'weibo'
},
{
path: 'qiniu',
component: () => import(/* webpackChunkName: "Qiniu" */ '@/pages/picbeds/Qiniu.vue'),
name: 'qiniu'
},
{
path: 'tcyun',
component: () => import(/* webpackChunkName: "TcYun" */ '@/pages/picbeds/TcYun.vue'),
name: 'tcyun'
},
{
path: 'upyun',
component: () => import(/* webpackChunkName: "UpYun" */ '@/pages/picbeds/UpYun.vue'),
name: 'upyun'
},
{
path: 'github',
component: () => import(/* webpackChunkName: "GitHub" */ '@/pages/picbeds/GitHub.vue'),
name: 'github'
},
{
path: 'smms',
component: () => import(/* webpackChunkName: "SMMS" */ '@/pages/picbeds/SMMS.vue'),
name: 'smms'
},
{
path: 'aliyun',
component: () => import(/* webpackChunkName: "AliYun" */ '@/pages/picbeds/AliYun.vue'),
name: 'aliyun'
},
{
path: 'imgur',
component: () => import(/* webpackChunkName: "Imgur" */ '@/pages/picbeds/Imgur.vue'),
name: 'imgur'
},
{
path: 'others/:type',
component: () => import(/* webpackChunkName: "Other" */ '@/pages/picbeds/Others.vue'),
name: 'others'
},
{
path: 'gallery',
component: () => import(/* webpackChunkName: "Gallery" */ '@/pages/Gallery.vue'),
name: 'gallery',
meta: {
keepAlive: true
}
},
{
path: 'setting',
component: () => import(/* webpackChunkName: "setting" */ '@/pages/PicGoSetting.vue'),
name: 'setting'
},
{
path: 'plugin',
component: () => import(/* webpackChunkName: "Plugin" */ '@/pages/Plugin.vue'),
name: 'plugin'
},
{
path: 'shortcut',
component: () => import(/* webpackChunkName: "ShortkeyPage" */ '@/pages/ShortCut.vue'),
name: 'shortcut'
}
]
},
{
path: '*',
redirect: '/'
}
]
})

View File

@ -1,20 +0,0 @@
export default {
name: '',
data () {
return {
defaultPicBed: this.$db.get('picBed.current')
}
},
methods: {
setDefaultPicBed (type) {
this.$db.set('picBed.current', type)
this.defaultPicBed = type
const successNotification = new window.Notification('设置默认图床', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
}
}

View File

@ -0,0 +1,15 @@
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class extends Vue {
defaultPicBed = this.$db.get('picBed.current')
setDefaultPicBed (type: string) {
this.$db.set('picBed.current', type)
this.defaultPicBed = type
const successNotification = new Notification('设置默认图床', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
}

View File

@ -1,13 +1,13 @@
class LS {
get (name) {
get (name: string) {
if (localStorage.getItem(name)) {
return JSON.parse(localStorage.getItem(name))
return JSON.parse(localStorage.getItem(name) as string)
} else {
return {}
}
}
set (name, value) {
set (name: string, value: any) {
return localStorage.setItem(name, JSON.stringify(value))
}
}

View File

@ -1,6 +1,6 @@
import keycode from 'keycode'
const isSpecialKey = (keyCode) => {
const isSpecialKey = (keyCode: number) => {
const keyArr = [
16, // Shift
17, // Ctrl
@ -12,14 +12,14 @@ const isSpecialKey = (keyCode) => {
return keyArr.includes(keyCode)
}
const keyDetect = (event) => {
const keyDetect = (event: KeyboardEvent) => {
const meta = process.platform === 'darwin' ? 'Cmd' : 'Super'
let specialKey = {
Ctrl: event.ctrlKey,
Shift: event.shiftKey,
Alt: event.altKey
Alt: event.altKey,
[meta]: event.metaKey
}
specialKey[meta] = event.metaKey
let pressKey = []

View File

@ -1,25 +0,0 @@
export default {
mounted () {
this.disableDragEvent()
},
methods: {
disableDragEvent () {
window.addEventListener('dragenter', this.disableDrag, false)
window.addEventListener('dragover', this.disableDrag)
window.addEventListener('drop', this.disableDrag)
},
disableDrag (e) {
const dropzone = document.getElementById('upload-area')
if (dropzone === null || !dropzone.contains(e.target)) {
e.preventDefault()
e.dataTransfer.effectAllowed = 'none'
e.dataTransfer.dropEffect = 'none'
}
}
},
beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag)
window.removeEventListener('drop', this.disableDrag)
}
}

View File

@ -0,0 +1,25 @@
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class extends Vue {
mounted () {
this.disableDragEvent()
}
disableDragEvent () {
window.addEventListener('dragenter', this.disableDrag, false)
window.addEventListener('dragover', this.disableDrag)
window.addEventListener('drop', this.disableDrag)
}
disableDrag (e: DragEvent) {
const dropzone = document.getElementById('upload-area')
if (dropzone === null || !dropzone.contains(<Node>e.target)) {
e.preventDefault()
e.dataTransfer!.effectAllowed = 'none'
e.dataTransfer!.dropEffect = 'none'
}
}
beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag)
window.removeEventListener('drop', this.disableDrag)
}
}

View File

@ -1,17 +1,11 @@
import Datastore from 'lowdb'
// @ts-ignore
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'
if (process.env.NODE_ENV !== 'development') {
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = path.join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
@ -22,6 +16,7 @@ if (process.type !== 'renderer') {
}
class DB {
private db: Datastore.LowdbSync<Datastore.AdapterSync>
constructor () {
const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
@ -53,13 +48,14 @@ class DB {
get (key = '') {
return this.read().get(key).value()
}
set (key, value) {
set (key: string, value: any) {
return this.read().set(key, value).write()
}
has (key) {
has (key: string) {
return this.read().has(key).value()
}
insert (key, value) {
insert (key: string, value: any): void {
// @ts-ignore
return this.read().get(key).insert(value).write()
}
}

12
src/universal/types/extra-vue.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
import VueRouter, { Route } from 'vue-router'
import db from '#/datastore'
import axios from 'axios'
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter,
$route: Route,
$db: typeof db
$http: typeof axios
$builtInPicBed: string[]
}
}

2
src/universal/types/picgo.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
import PicGoCore from 'picgo'
export default PicGoCore

13
src/universal/types/shims-tsx.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}

4
src/universal/types/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}

161
src/universal/types/types.d.ts vendored Normal file
View File

@ -0,0 +1,161 @@
// global
interface Obj {
[propName: string]: any
}
interface ObjT<T> {
[propName: string]: T
}
// Image && PicBed
interface ImgInfo {
buffer?: Buffer
base64Image?: string
fileName?: string
width?: number
height?: number
extname?: string
imgUrl?: string
[propName: string]: any
}
interface PicBedType {
type: string
name: string
visible: boolean
}
// Config Settings
interface ShortKeyConfig {
enable: boolean
key: string // 按键
name: string
label: string
}
interface ShortKeyConfigs {
[propName: string]: ShortKeyConfig
}
interface OldShortKeyConfigs {
upload: string
}
// Main process
interface BrowserWindowOptions {
height: number,
width: number,
show: boolean,
fullscreenable: boolean,
resizable: boolean,
webPreferences: {
nodeIntegration: boolean,
nodeIntegrationInWorker: boolean,
backgroundThrottling: boolean
webSecurity?: boolean
},
vibrancy?: string | any,
frame?: boolean
center?: boolean
title?: string
titleBarStyle?: string | any
backgroundColor?: string
autoHideMenuBar?: boolean
transparent?: boolean
icon?: string
skipTaskbar?: boolean
alwaysOnTop?: boolean
}
interface FileWithPath {
path: string
name?: string
}
interface Bounds {
x: number
y: number
}
declare enum PasteStyle {
MARKDOWN = 'markdown',
HTML = 'HTML',
URL = 'URL',
UBB = 'UBB',
CUSTOM = 'Custom'
}
// global value
declare var __static: string
// third-party
declare module 'fix-path' {
function fixPath(): void
export default fixPath
}
// PicGo Types
declare enum PicGoHelperType {
afterUploadPlugins = 'afterUploadPlugins',
beforeTransformPlugins = 'beforeTransformPlugins',
beforeUploadPlugins = 'beforeUploadPlugins',
uploader = 'uploader',
transformer = 'transformer'
}
interface IPicGoPlugin {
name: string
author: string
description: string
logo: string
version: string | number
gui: boolean
config: {
plugin: IPluginMenuConfig
uploader: IPluginMenuConfig
transformer: IPluginMenuConfig
[index: string]: IPluginMenuConfig
} | {
[propName: string]: any
}
enabled?: boolean
homepage: string
guiMenu?: any[]
ing: boolean
hasInstall?: boolean
}
interface IPluginMenuConfig {
name: string
config: any[]
}
interface INPMSearchResult {
data: {
objects: INPMSearchResultObject[]
}
}
interface INPMSearchResultObject {
package: {
name: string
scope: string
version: string
description: string
keywords: string[]
author: {
name: string
}
links: {
npm: string
homepage: string
}
}
}
// GuiApi
interface IShowInputBoxOption {
title: string
placeholder: string
}

14
src/universal/types/view.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
interface ISettingForm {
updateHelper: boolean
showPicBedList: string[]
autoStart: boolean
rename: boolean
autoRename: boolean
uploadNotification: boolean
miniWindowOntop: boolean
logLevel: string[]
}
interface ShortKeyMap {
[propName: string]: string
}

View File

@ -1,13 +1,13 @@
import db from '../../datastore'
import db from '#/datastore'
const formatCustomLink = (customLink, item) => {
let fileName = item.fileName.replace(new RegExp(`\\${item.extname}$`), '')
const formatCustomLink = (customLink: string, item: ImgInfo) => {
let fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
let url = item.url || item.imgUrl
let formatObj = {
const formatObj = {
url,
fileName
}
let keys = Object.keys(formatObj)
const keys = Object.keys(formatObj) as ['url', 'fileName']
keys.forEach(item => {
if (customLink.indexOf(`$${item}`) !== -1) {
let reg = new RegExp(`\\$${item}`, 'g')
@ -17,7 +17,7 @@ const formatCustomLink = (customLink, item) => {
return customLink
}
export default (style, item) => {
export default (style: PasteStyle, item: ImgInfo) => {
let url = item.url || item.imgUrl
const customLink = db.get('settings.customLink') || '$url'
const tpl = {

View File

52
tsconfig.json Normal file
View File

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

61
vue.config.js Normal file
View File

@ -0,0 +1,61 @@
const path = require('path')
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src/renderer'))
.set('~', resolve('src'))
.set('root', resolve('./'))
.set('#', resolve('src/universal'))
},
pluginOptions: {
electronBuilder: {
chainWebpackMainProcess: config => {
config.resolve.alias
.set('@', resolve('src/renderer'))
.set('~', resolve('src'))
.set('root', resolve('./'))
.set('#', resolve('src/universal'))
},
builderOptions: {
productName: 'PicGo',
appId: 'com.molunerfinn.picgo',
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',
target: 'nsis'
},
nsis: {
shortcutName: 'PicGo',
oneClick: false,
allowToChangeInstallationDirectory: true
},
linux: {
icon: 'build/icons/'
}
}
}
}
}

10506
yarn.lock

File diff suppressed because it is too large Load Diff