Compare commits

..
Author SHA1 Message Date
PiEgg f3ec0cb4be 🎉 Release: v2.3.1-beta.2 2022-01-06 10:42:08 +08:00
PiEgg 5dd6e72b50 🐛 Fix: electron builder actions bug 2022-01-06 00:01:26 +08:00
PiEgg 5cb8151df7 🐛 Fix: linux github actions workflow bug 2022-01-05 23:18:41 +08:00
PiEgg 1848bff091 🎉 Release: v2.3.1-beta.1 2022-01-05 23:13:59 +08:00
PiEgg 56e814a96a 📦 Chore: update ci build scripts 2022-01-05 23:13:24 +08:00
PiEgg b93b4cffe2 🎉 Release: v2.3.1-beta.0 2022-01-05 22:58:53 +08:00
PiEgg f2a4197ca2 📦 Chore: add mac-arm64 build support 2022-01-05 12:54:02 +08:00
PiEgg 34b3656605 🐛 Fix: mini window drag bug 2022-01-05 12:18:50 +08:00
PiEgg ea20d3b971 📦 Chore: update electron from v6 -> v16 2022-01-04 23:59:35 +08:00
PiEgg 459953f391 🎉 Release: v2.3.0 2021-09-11 11:29:50 +08:00
PiEgg 75e3edcd87 Feature: add open devtool option 2021-08-29 19:04:43 +08:00
PiEgg 5895889059 🐛 Fix: shift key function in gallery page 2021-08-28 21:50:46 +08:00
PiEgg 58420c8c3b 📝 Docs: update FAQ 2021-08-28 17:36:14 +08:00
PiEgg 6c6f84779a 🐛 Fix: urlEncode bug when copy
ISSUES CLOSED: #731
2021-08-23 23:21:58 +08:00
PiEgg a676c083fe 🐛 Fix: some bugs
ISSUES CLOSED: #722
2021-08-21 10:42:52 +08:00
68 changed files with 7451 additions and 5489 deletions
+18 -6
View File
@@ -6,20 +6,32 @@ module.exports = {
env: {
node: true
},
parser: "vue-eslint-parser",
'extends': [
parser: 'vue-eslint-parser',
extends: [
'plugin:vue/essential',
'@vue/standard',
'@vue/typescript'
],
'plugins': ['@typescript-eslint'],
plugins: ['@typescript-eslint'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
"indent": "off",
"@typescript-eslint/indent": ["error", 2]
indent: 'off',
'no-async-promise-executor': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/indent': ['error', 2]
},
parserOptions: {
parser: '@typescript-eslint/parser'
}
},
overrides: [
{
files: ['*.ts', '*.vue'],
rules: {
'no-undef': 'off' // https://typescript-eslint.io/docs/linting/troubleshooting/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
}
}
],
ignorePatterns: ['src/**/*.d.ts']
}
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-10.15]
os: [ubuntu-latest, macos-11]
# create steps
steps:
@@ -50,6 +50,6 @@ jobs:
- name: Build & release app
run: |
npm run release
yarn release
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
+52
View File
@@ -0,0 +1,52 @@
# main.yml
# Workflow's name
name: Build
# Workflow's trigger
on: workflow_dispatch
# Workflow's jobs
jobs:
# job's id
release:
# job's name
name: build and release electron app
# the type of machine to run the job on
runs-on: ${{ matrix.os }}
# create a build matrix for jobs
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-11]
# create steps
steps:
# step1: check out repository
- name: Check out git repository
uses: actions/checkout@v2
# step2: install node env
- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- name: Install system deps
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils
# step3: yarn
- name: Yarn install
run: |
yarn
yarn global add xvfb-maybe
- name: Build & release app
run: |
yarn release
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
+55
View File
@@ -1,3 +1,58 @@
## :tada: 2.3.1-beta.2 (2022-01-06)
### :bug: Bug Fixes
* electron builder actions bug ([5dd6e72](https://github.com/Molunerfinn/PicGo/commit/5dd6e72))
* linux github actions workflow bug ([5cb8151](https://github.com/Molunerfinn/PicGo/commit/5cb8151))
## :tada: 2.3.1-beta.1 (2022-01-05)
### :package: Chore
* update ci build scripts ([56e814a](https://github.com/Molunerfinn/PicGo/commit/56e814a))
## :tada: 2.3.1-beta.0 (2022-01-05)
### :bug: Bug Fixes
* mini window drag bug ([34b3656](https://github.com/Molunerfinn/PicGo/commit/34b3656))
### :package: Chore
* add mac-arm64 build support ([f2a4197](https://github.com/Molunerfinn/PicGo/commit/f2a4197))
* update electron from v6 -> v16 ([ea20d3b](https://github.com/Molunerfinn/PicGo/commit/ea20d3b))
# :tada: 2.3.0 (2021-09-11)
### :sparkles: Features
* add open devtool option ([75e3edc](https://github.com/Molunerfinn/PicGo/commit/75e3edc))
### :bug: Bug Fixes
* shift key function in gallery page ([5895889](https://github.com/Molunerfinn/PicGo/commit/5895889))
* some bugs ([a676c08](https://github.com/Molunerfinn/PicGo/commit/a676c08)), closes [#722](https://github.com/Molunerfinn/PicGo/issues/722)
* urlEncode bug when copy ([6c6f847](https://github.com/Molunerfinn/PicGo/commit/6c6f847)), closes [#731](https://github.com/Molunerfinn/PicGo/issues/731)
### :pencil: Documentation
* update FAQ ([58420c8](https://github.com/Molunerfinn/PicGo/commit/58420c8))
# :tada: 2.3.0-beta.8 (2021-08-13)
+4
View File
@@ -57,3 +57,7 @@ PicGo 在 Mac 上是一个顶部栏应用,在 dock 栏是不会有图标的。
1. 先自行搜索 error 里的报错信息,往往你能百度或者谷歌出问题原因,不必开 issue。
2. 如果有带有 `401``403``40X` 状态码字样的,不用怀疑,就是你配置写错了,仔细检查配置,看看是否多了空格之类的。
3. 如果带有 `HttpError``RequestError``socket hang up` 等字样的说明这是网络问题,我无法帮你解决网络问题,请检查你自己的网络,是否有代理,DNS 设置是否正常等。
## 10. macOS版本安装完之后没有主界面
请找到PicGo在顶部栏的图标,然后右键(触摸板双指点按,或者鼠标右键),即可找到「打开详细窗口」的菜单。
+2 -3
View File
@@ -19,14 +19,13 @@ init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 12 x64
- ps: Install-Product node 16 x64
- git reset --hard HEAD
- yarn
- node --version
build_script:
#- yarn test
- yarn build --win --ia32
- yarn release
test: off
test: false
+2 -1
View File
@@ -1,5 +1,6 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
],
plugins: ['@babel/plugin-proposal-optional-chaining']
}
+3 -3
View File
@@ -22,7 +22,7 @@
.col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0
img(:src="item.url")
.col-xs-10.col-xs-offset-1.col-md-5.col-md-offset-0.display-list__content
.display-list__title {{ item.title }}
.display-list__title {{ item.title }}
.display-list__desc {{ item.desc }}
.row.ex-width.info
.col-xs-10.col-xs-offset-1
@@ -94,7 +94,7 @@ html,
h1
margin 0
padding 0
font-family "Source Sans Pro","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
font-family "Source Sans Pro","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
#app
position relative
.mask
@@ -213,4 +213,4 @@ h1
font-size 25px
&__desc
margin-top 12px
</style>
</style>
+23 -16
View File
@@ -1,11 +1,12 @@
{
"name": "picgo",
"version": "2.3.0-beta.8",
"version": "2.3.1-beta.2",
"private": true,
"scripts": {
"dev": "vue-cli-service electron:serve",
"build": "vue-cli-service electron:build",
"lint": "vue-cli-service lint",
"lint:fix": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue src/",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
@@ -39,7 +40,7 @@
"core-js": "^3.3.2",
"element-ui": "^2.13.0",
"fix-path": "^2.1.0",
"fs-extra": "^8.1.0",
"fs-extra": "^10.0.0",
"keycode": "^2.2.0",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
@@ -52,36 +53,42 @@
"vue-router": "^3.1.3"
},
"devDependencies": {
"@commitlint/cli": "^8.2.0",
"@picgo/bump-version": "^1.0.3",
"@types/fs-extra": "^8.0.1",
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
"@picgo/bump-version": "^1.1.2",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^6.5.0",
"@types/lowdb": "^1.0.9",
"@types/node": "10.17.6",
"@types/node": "^16.10.2",
"@types/request-promise-native": "^1.0.17",
"@types/semver": "^7.3.8",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@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-plugin-typescript": "^4.5.13",
"@vue/cli-service": "^4.0.0",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/eslint-config-typescript": "^4.0.0",
"commitizen": "^4.0.3",
"@vue/eslint-config-standard": "^6.1.0",
"@vue/eslint-config-typescript": "^7.0.0",
"conventional-changelog": "^3.1.18",
"cz-customizable": "^6.2.0",
"electron": "^6.0.0",
"electron": "^16.0.6",
"electron-devtools-installer": "^3.2.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"eslint": "^7.32.0",
"eslint-config-standard": ">=16.0.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-vue": "^7.0.0",
"husky": "^3.1.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"typescript": "~3.7.3",
"vue-cli-plugin-electron-builder": "^1.4.2",
"typescript": "^4.4.3",
"vue-cli-plugin-electron-builder": "^2.1.1",
"vue-property-decorator": "^8.3.0",
"vue-template-compiler": "^2.6.10"
},
"resolutions": {
"@types/node": "12.0.2"
"@types/node": "^16.10.2"
}
}
-1
View File
@@ -12,7 +12,6 @@ import { initTalkingData } from './renderer/utils/analytics'
import db from './renderer/utils/db'
webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0)
Vue.config.productionTip = false
Vue.prototype.$builtInPicBed = [
+1 -1
View File
@@ -6,7 +6,7 @@ The lowest level APIs that are not dependent on each other. The upper APIs depen
## app
Provide key API interfaces for PicGo application, including uploader, window management, shortcut key system, etc
Provide key API interfaces for PicGo application, including uploader, window management, shortcut key system, remotes handler, etc
## gui
+12 -3
View File
@@ -16,10 +16,12 @@ class ShortKeyHandler {
this.isInModifiedMode = flag
})
}
init () {
this.initBuiltInShortKey()
this.initPluginsShortKey()
}
private initBuiltInShortKey () {
const commands = db.get('settings.shortKey') as IShortKeyConfigs
Object.keys(commands)
@@ -34,10 +36,11 @@ class ShortKeyHandler {
}
})
}
private initPluginsShortKey () {
// get enabled plugin
const pluginList = picgo.pluginLoader.getList()
for (let item of pluginList) {
for (const item of pluginList) {
const plugin = picgo.pluginLoader.getPlugin(item)
// if a plugin has commands
if (plugin && plugin.commands) {
@@ -46,7 +49,7 @@ class ShortKeyHandler {
continue
}
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
for (let cmd of commands) {
for (const cmd of commands) {
const command = `${item}:${cmd.name}`
if (db.has(`settings.shortKey[${command}]`)) {
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
@@ -63,6 +66,7 @@ class ShortKeyHandler {
}
}
}
private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) {
shortKeyService.registerCommand(command, handler)
if (config.key) {
@@ -85,6 +89,7 @@ class ShortKeyHandler {
})
}
}
// enable or disable shortKey
bindOrUnbindShortKey (item: IShortKeyConfig, from: string): boolean {
const command = `${from}:${item.name}`
@@ -108,6 +113,7 @@ class ShortKeyHandler {
}
}
}
// update shortKey bindings
updateShortKey (item: IShortKeyConfig, oldKey: string, from: string): boolean {
const command = `${from}:${item.name}`
@@ -121,6 +127,7 @@ class ShortKeyHandler {
})
return true
}
private async handler (command: string) {
if (this.isInModifiedMode) {
return
@@ -136,6 +143,7 @@ class ShortKeyHandler {
logger.warn(`can not find command: ${command}`)
}
}
registerPluginShortKey (pluginName: string) {
const plugin = picgo.pluginLoader.getPlugin(pluginName)
if (plugin && plugin.commands) {
@@ -144,7 +152,7 @@ class ShortKeyHandler {
return
}
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
for (let cmd of commands) {
for (const cmd of commands) {
const command = `${pluginName}:${cmd.name}`
if (db.has(`settings.shortKey[${command}]`)) {
const commandConfig = db.get(`settings.shortKey[${command}]`) as IShortKeyConfig
@@ -155,6 +163,7 @@ class ShortKeyHandler {
}
}
}
unregisterPluginShortKey (pluginName: string) {
const commands = db.get('settings.shortKey') as IShortKeyConfigs
const keyList = Object.keys(commands)
@@ -4,15 +4,18 @@ class ShortKeyService {
registerCommand (command: string, handler: IShortKeyHandler) {
this.commandList.set(command, handler)
}
unregisterCommand (command: string) {
this.commandList.delete(command)
}
getShortKeyHandler (command: string): IShortKeyHandler | null {
const handler = this.commandList.get(command)
if (handler) return handler
logger.warn(`cannot find command: ${command}`)
return null
}
getCommandList () {
return [...this.commandList.keys()]
}
+3 -3
View File
@@ -13,7 +13,7 @@ import db, { GalleryDB } from '~/main/apis/core/datastore'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import picgo from '@core/picgo'
import pasteTemplate from '#/utils/pasteTemplate'
import pasteTemplate from '~/main/utils/pasteTemplate'
import pkg from 'root/package.json'
import { handleCopyUrl } from '~/main/utils/common'
import { privacyManager } from '~/main/utils/privacyManager'
@@ -162,8 +162,8 @@ export function createTray () {
if (process.platform === 'darwin') {
toggleWindow(bounds)
setTimeout(() => {
let img = clipboard.readImage()
let obj: ImgInfo[] = []
const img = clipboard.readImage()
const obj: ImgInfo[] = []
if (!img.isEmpty()) {
// 从剪贴板来的图片默认转为png
// @ts-ignore
+2 -2
View File
@@ -5,13 +5,13 @@ import {
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import uploader from '.'
import pasteTemplate from '#/utils/pasteTemplate'
import pasteTemplate from '~/main/utils/pasteTemplate'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import { handleCopyUrl } from '~/main/utils/common'
import { handleUrlEncode } from '#/utils/common'
export const uploadClipboardFiles = async (): Promise<string> => {
const win = windowManager.getAvailableWindow()
let img = await uploader.setWebContents(win!.webContents).upload()
const img = await uploader.setWebContents(win!.webContents).upload()
if (img !== false) {
if (img.length > 0) {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
+4 -4
View File
@@ -16,7 +16,7 @@ import { TALKING_DATA_EVENT } from '~/universal/events/constants'
import logger from '@core/picgo/logger'
const waitForShow = (webcontent: WebContents) => {
return new Promise<void>((resolve, reject) => {
return new Promise<void>((resolve) => {
webcontent.on('did-finish-load', () => {
resolve()
})
@@ -24,7 +24,7 @@ const waitForShow = (webcontent: WebContents) => {
}
const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
const windowId = window.id
ipcMain.once(`rename${id}`, (evt: Event, newName: string) => {
resolve(newName)
@@ -68,7 +68,7 @@ class Uploader {
picgo.on('uploadProgress', progress => {
this.webContents?.send('uploadProgress', progress)
})
picgo.on('beforeTransform', ctx => {
picgo.on('beforeTransform', () => {
if (db.get('settings.uploadNotification')) {
const notification = new Notification({
title: '上传进度',
@@ -125,7 +125,7 @@ class Uploader {
} else {
return false
}
} catch (e) {
} catch (e: any) {
logger.error(e)
setTimeout(() => {
showNotification({
+4 -4
View File
@@ -9,16 +9,16 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
export const TRAY_WINDOW_URL = isDevelopment
? (process.env.WEBPACK_DEV_SERVER_URL as string)
: `picgo://./index.html`
: 'picgo://./index.html'
export const SETTING_WINDOW_URL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload`
: `picgo://./index.html#main-page/upload`
: 'picgo://./index.html#main-page/upload'
export const MINI_WINDOW_URL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
: `picgo://./index.html#mini-page`
: 'picgo://./index.html#mini-page'
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
: `picgo://./index.html#rename-page`
: 'picgo://./index.html#rename-page'
+10 -6
View File
@@ -28,7 +28,8 @@ windowList.set(IWindowList.TRAY_WINDOW, {
transparent: true,
vibrancy: 'ultra-dark',
webPreferences: {
nodeIntegration: true,
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true,
backgroundThrottling: false
}
@@ -60,7 +61,8 @@ windowList.set(IWindowList.SETTING_WINDOW, {
titleBarStyle: 'hidden',
webPreferences: {
backgroundThrottling: false,
nodeIntegration: true,
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true,
webSecurity: false
}
@@ -93,7 +95,7 @@ windowList.set(IWindowList.MINI_WINDOW, {
isValid: process.platform !== 'darwin',
multiple: false,
options () {
let obj: IBrowserWindowOptions = {
const obj: IBrowserWindowOptions = {
height: 64,
width: 64,
show: process.platform === 'linux',
@@ -105,7 +107,8 @@ windowList.set(IWindowList.MINI_WINDOW, {
icon: `${__static}/logo.png`,
webPreferences: {
backgroundThrottling: false,
nodeIntegration: true,
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true
}
}
@@ -124,7 +127,7 @@ windowList.set(IWindowList.RENAME_WINDOW, {
isValid: true,
multiple: true,
options () {
let options: IBrowserWindowOptions = {
const options: IBrowserWindowOptions = {
height: 175,
width: 300,
show: true,
@@ -132,7 +135,8 @@ windowList.set(IWindowList.RENAME_WINDOW, {
resizable: false,
vibrancy: 'ultra-dark',
webPreferences: {
nodeIntegration: true,
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
nodeIntegrationInWorker: true,
backgroundThrottling: false
}
@@ -34,6 +34,7 @@ class WindowManager implements IWindowManager {
return null
}
}
get (name: IWindowList) {
if (this.has(name)) {
return this.windowMap.get(name)!
@@ -42,9 +43,11 @@ class WindowManager implements IWindowManager {
return window
}
}
has (name: IWindowList) {
return this.windowMap.has(name)
}
// useless
// delete (name: IWindowList) {
// const window = this.windowMap.get(name)
@@ -60,6 +63,7 @@ class WindowManager implements IWindowManager {
this.windowIdMap.delete(id)
}
}
getAvailableWindow () {
const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW)
if (miniWindow && miniWindow.isVisible()) {
+4 -4
View File
@@ -14,7 +14,7 @@ export const uploadWithClipboardFiles = (): Promise<{
success: boolean,
result?: string[]
}> => {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
if (result) {
return resolve({
@@ -35,7 +35,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
success: boolean,
result?: string[]
}> => {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
if (result.length) {
return resolve({
@@ -55,7 +55,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
// get available window id:
// miniWindow or settingWindow or trayWindow
export const getWindowId = (): Promise<number> => {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
resolve(id)
})
@@ -65,7 +65,7 @@ export const getWindowId = (): Promise<number> => {
// get settingWindow id:
export const getSettingWindowId = (): Promise<number> => {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => {
resolve(id)
})
+14 -10
View File
@@ -1,8 +1,8 @@
import fs from 'fs-extra'
import path from 'path'
import { remote, app } from 'electron'
import { app as APP } from 'electron'
import { getLogger } from '@core/utils/localLogger'
import dayjs from 'dayjs'
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
const configFilePath = path.join(STORE_PATH, 'data.json')
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')
@@ -15,9 +15,11 @@ const errorMsg = {
brokenButBackup: 'PicGo 配置文件损坏,已经恢复为备份配置'
}
/** ensure notification list */
if (!global.notificationList) global.notificationList = []
function dbChecker () {
if (process.type !== 'renderer') {
if (!global.notificationList) global.notificationList = []
// db save bak
try {
const { dbPath, dbBackupPath } = getGalleryDBPath()
@@ -33,7 +35,7 @@ function dbChecker () {
return
}
let configFile: string = '{}'
let optionsTpl = {
const optionsTpl = {
title: '注意',
body: ''
}
@@ -50,16 +52,16 @@ function dbChecker () {
fs.writeFileSync(configFilePath, configFile, { encoding: 'utf-8' })
const stats = fs.statSync(configFileBackupPath)
optionsTpl.body = `${errorMsg.brokenButBackup}\n备份文件版本:${dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')}`
global.notificationList.push(optionsTpl)
global.notificationList?.push(optionsTpl)
return
} catch (e) {
optionsTpl.body = errorMsg.broken
global.notificationList.push(optionsTpl)
global.notificationList?.push(optionsTpl)
return
}
}
optionsTpl.body = errorMsg.broken
global.notificationList.push(optionsTpl)
global.notificationList?.push(optionsTpl)
return
}
fs.writeFileSync(configFileBackupPath, configFile, { encoding: 'utf-8' })
@@ -92,15 +94,17 @@ function dbPathChecker (): string {
}
return _configFilePath
} catch (e) {
// TODO: local logger is needed
const picgoLogPath = path.join(defaultConfigPath, 'picgo.log')
const logger = getLogger(picgoLogPath)
if (!hasCheckPath) {
let optionsTpl = {
const optionsTpl = {
title: '注意',
body: '自定义文件解析出错,请检查路径内容是否正确'
}
global.notificationList.push(optionsTpl)
global.notificationList?.push(optionsTpl)
hasCheckPath = true
}
logger('error', e)
console.error(e)
_configFilePath = defaultConfigPath
return _configFilePath
+10
View File
@@ -42,33 +42,42 @@ class ConfigStore {
}).write()
}
}
read () {
return this.db.read()
}
get (key = '') {
return this.read().get(key).value()
}
set (key: string, value: any) {
return this.read().set(key, value).write()
}
has (key: string) {
return this.read().has(key).value()
}
insert (key: string, value: any): void {
// @ts-ignore
return this.read().get(key).insert(value).write()
}
unset (key: string, value: any): boolean {
return this.read().get(key).unset(value).value()
}
getById (key: string, id: string) {
// @ts-ignore
return this.read().get(key).getById(id).value()
}
removeById (key: string, id: string) {
// @ts-ignore
return this.read().get(key).removeById(id).write()
}
getConfigPath () {
return CONFIG_PATH
}
@@ -82,6 +91,7 @@ class GalleryDB {
private constructor () {
console.log('init gallery db')
}
public static getInstance (): DBStore {
if (!GalleryDB.instance) {
GalleryDB.instance = new DBStore(DB_PATH, 'gallery')
+32
View File
@@ -0,0 +1,32 @@
import fs from 'fs-extra'
import dayjs from 'dayjs'
import util from 'util'
/**
* for local log before picgo inited
*/
const getLogger = (logPath: string) => {
if (!fs.existsSync(logPath)) {
fs.ensureFileSync(logPath)
}
return (type: string, ...msg: any[]) => {
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ${type.toUpperCase()}] `
msg.forEach((item: ILogArgvTypeWithError) => {
if (typeof item === 'object' && type === 'error') {
log += `\n------Error Stack Begin------\n${util.format(item.stack)}\n-------Error Stack End------- `
} else {
if (typeof item === 'object') {
item = JSON.stringify(item)
}
log += `${item} `
}
})
log += '\n'
// A synchronized approach to avoid log msg sequence errors
fs.appendFileSync(logPath, log)
}
}
export {
getLogger
}
+18 -22
View File
@@ -4,11 +4,10 @@ import {
Notification,
ipcMain
} from 'electron'
import path from 'path'
import db, { GalleryDB } from 'apis/core/datastore'
import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from 'apis/core/datastore/dbChecker'
import uploader from 'apis/app/uploader'
import pasteTemplate from '#/utils/pasteTemplate'
import pasteTemplate from '~/main/utils/pasteTemplate'
import { handleCopyUrl } from '~/main/utils/common'
import {
getWindowId,
@@ -18,7 +17,6 @@ import {
SHOW_INPUT_BOX
} from '~/universal/events/constants'
import { DBStore } from '@picgo/store'
type PromiseResType<T> = T extends Promise<infer R> ? R : T
// Cross-process support may be required in the future
class GuiApi implements IGuiApi {
@@ -28,20 +26,22 @@ class GuiApi implements IGuiApi {
private constructor () {
console.log('init guiapi')
}
public static getInstance (): GuiApi {
if (!GuiApi.instance) {
GuiApi.instance = new GuiApi()
}
return GuiApi.instance
}
private async showSettingWindow () {
this.settingWindowId = await getSettingWindowId()
const settingWindow = BrowserWindow.fromId(this.settingWindowId)
if (settingWindow.isVisible()) {
if (settingWindow?.isVisible()) {
return true
}
settingWindow.show()
return new Promise<void>((resolve, reject) => {
settingWindow?.show()
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve()
}, 1000) // TODO: a better way to wait page loaded.
@@ -49,7 +49,7 @@ class GuiApi implements IGuiApi {
}
private getWebcontentsByWindowId (id: number) {
return BrowserWindow.fromId(id).webContents
return BrowserWindow.fromId(id)?.webContents
}
async showInputBox (options: IShowInputBoxOption = {
@@ -57,28 +57,24 @@ class GuiApi implements IGuiApi {
placeholder: ''
}) {
await this.showSettingWindow()
this.getWebcontentsByWindowId(this.settingWindowId)
.send(SHOW_INPUT_BOX, options)
return new Promise<string>((resolve, reject) => {
this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options)
return new Promise<string>((resolve) => {
ipcMain.once(SHOW_INPUT_BOX, (event: Event, value: string) => {
resolve(value)
})
})
}
showFileExplorer (options: IShowFileExplorerOption = {}) {
return new Promise<string>(async (resolve, reject) => {
this.windowId = await getWindowId()
dialog.showOpenDialog(BrowserWindow.fromId(this.windowId), options, (filename: string) => {
resolve(filename)
})
})
async showFileExplorer (options: IShowFileExplorerOption = {}) {
this.windowId = await getWindowId()
const res = await dialog.showOpenDialog(BrowserWindow.fromId(this.windowId)!, options)
return res.filePaths?.[0]
}
async upload (input: IUploadOption) {
this.windowId = await getWindowId()
const webContents = this.getWebcontentsByWindowId(this.windowId)
const imgs = await uploader.setWebContents(webContents).upload(input)
const imgs = await uploader.setWebContents(webContents!).upload(input)
if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const pasteText: string[] = []
@@ -95,8 +91,8 @@ class GuiApi implements IGuiApi {
await GalleryDB.getInstance().insert(imgs[i])
}
handleCopyUrl(pasteText.join('\n'))
webContents.send('uploadFiles', imgs)
webContents.send('updateGallery')
webContents?.send('uploadFiles', imgs)
webContents?.send('updateGallery')
return imgs
}
return []
@@ -119,10 +115,10 @@ class GuiApi implements IGuiApi {
type: 'info',
buttons: ['Yes', 'No']
}) {
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => {
return new Promise<IShowMessageBoxResult>(async (resolve) => {
this.windowId = await getWindowId()
dialog.showMessageBox(
BrowserWindow.fromId(this.windowId),
BrowserWindow.fromId(this.windowId)!,
options
).then((res) => {
resolve({
+1 -1
View File
@@ -28,7 +28,7 @@ function initEventCenter () {
[GET_SETTING_WINDOW_ID]: busCallGetSettingWindowId,
[CREATE_APP_MENU]: createMenu
}
for (let i in eventList) {
for (const i in eventList) {
bus.on(i, eventList[i])
}
}
+79 -3
View File
@@ -1,20 +1,33 @@
import {
app,
ipcMain,
shell,
Notification,
IpcMainEvent
IpcMainEvent,
BrowserWindow
} from 'electron'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import uploader from 'apis/app/uploader'
import pasteTemplate from '#/utils/pasteTemplate'
import pasteTemplate from '~/main/utils/pasteTemplate'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import server from '~/main/server'
import getPicBeds from '~/main/utils/getPicBeds'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import bus from '@core/bus'
import {
TOGGLE_SHORTKEY_MODIFIED_MODE
TOGGLE_SHORTKEY_MODIFIED_MODE,
OPEN_DEVTOOLS,
SHOW_MINI_PAGE_MENU,
MINIMIZE_WINDOW,
CLOSE_WINDOW,
SHOW_MAIN_PAGE_MENU,
SHOW_UPLOAD_PAGE_MENU,
OPEN_USER_STORE_FILE,
OPEN_URL,
RELOAD_APP,
SHOW_PLUGIN_PAGE_MENU,
SET_MINI_WINDOW_POS
} from '#/events/constants'
import {
uploadClipboardFiles,
@@ -22,6 +35,10 @@ import {
} from '~/main/apis/app/uploader/apis'
import picgoCoreIPC from './picgoCoreIPC'
import { handleCopyUrl } from '~/main/utils/common'
import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildUploadPageMenu } from './remotes/menu'
import path from 'path'
const STORE_PATH = app.getPath('userData')
export default {
listen () {
@@ -141,6 +158,65 @@ export default {
ipcMain.on('updateServer', () => {
server.restart()
})
ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => {
event.sender.openDevTools()
})
// menu & window methods
ipcMain.on(SHOW_MINI_PAGE_MENU, () => {
const window = windowManager.get(IWindowList.MINI_WINDOW)!
const menu = buildMiniPageMenu()
menu.popup({
window
})
})
ipcMain.on(SHOW_MAIN_PAGE_MENU, () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const menu = buildMainPageMenu()
menu.popup({
window
})
})
ipcMain.on(SHOW_UPLOAD_PAGE_MENU, () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const menu = buildUploadPageMenu()
menu.popup({
window
})
})
ipcMain.on(SHOW_PLUGIN_PAGE_MENU, (evt: IpcMainEvent, plugin: IPicGoPlugin) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const menu = buildPluginPageMenu(plugin)
menu.popup({
window
})
})
ipcMain.on(MINIMIZE_WINDOW, () => {
const window = BrowserWindow.getFocusedWindow()
window?.minimize()
})
ipcMain.on(CLOSE_WINDOW, () => {
const window = BrowserWindow.getFocusedWindow()
if (process.platform === 'linux') {
window?.hide()
} else {
window?.close()
}
})
ipcMain.on(OPEN_USER_STORE_FILE, (evt: IpcMainEvent, filePath: string) => {
const abFilePath = path.join(STORE_PATH, filePath)
shell.openPath(abFilePath)
})
ipcMain.on(OPEN_URL, (evt: IpcMainEvent, url: string) => {
shell.openExternal(url)
})
ipcMain.on(RELOAD_APP, () => {
app.relaunch()
app.exit(0)
})
ipcMain.on(SET_MINI_WINDOW_POS, (evt: IpcMainEvent, pos: IMiniWindowPos) => {
const window = BrowserWindow.getFocusedWindow()
window?.setBounds(pos)
})
},
dispose () {}
}
+73 -65
View File
@@ -4,10 +4,11 @@ import {
dialog,
shell,
IpcMainEvent,
ipcMain
ipcMain,
clipboard
} from 'electron'
import PicGoCore from '~/universal/types/picgo'
import { IPicGoHelperType } from '#/types/enum'
import { IPasteStyle, IPicGoHelperType } from '#/types/enum'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import picgo from '@core/picgo'
import { handleStreamlinePluginName } from '~/universal/utils/common'
@@ -25,11 +26,13 @@ import {
PICGO_UPDATE_BY_ID_DB,
PICGO_GET_BY_ID_DB,
PICGO_REMOVE_BY_ID_DB,
PICGO_OPEN_FILE
PICGO_OPEN_FILE,
PASTE_TEXT
} from '#/events/constants'
import { GalleryDB } from 'apis/core/datastore'
import { IObject, IFilter } from '@picgo/store/dist/types'
import pasteTemplate from '../utils/pasteTemplate'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
@@ -37,11 +40,6 @@ const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_re
const STORE_PATH = path.dirname(dbPathChecker())
// const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
type PicGoNotice = {
title: string,
body: string[]
}
interface GuiMenuItem {
label: string
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
@@ -64,7 +62,7 @@ const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
}
const handleConfigWithFunction = (config: any[]) => {
for (let i in config) {
for (const i in config) {
if (typeof config[i].default === 'function') {
config[i].default = config[i].default()
}
@@ -78,7 +76,7 @@ const handleConfigWithFunction = (config: any[]) => {
const getPluginList = (): IPicGoPlugin[] => {
const pluginList = picgo.pluginLoader.getFullList()
const list = []
for (let i in pluginList) {
for (const i in pluginList) {
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])!
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
@@ -156,39 +154,37 @@ const handlePluginInstall = () => {
})
}
const handlePluginUninstall = () => {
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
const dispose = handleNPMError()
const res = await picgo.pluginHandler.uninstall([msg])
if (res.success) {
event.sender.send('uninstallSuccess', res.body[0])
shortKeyHandler.unregisterPluginShortKey(res.body[0])
} else {
showNotification({
title: '插件卸载失败',
body: res.body as string
})
}
event.sender.send('hideLoading')
dispose()
})
const handlePluginUninstall = async (fullName: string) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const dispose = handleNPMError()
const res = await picgo.pluginHandler.uninstall([fullName])
if (res.success) {
window.webContents.send('uninstallSuccess', res.body[0])
shortKeyHandler.unregisterPluginShortKey(res.body[0])
} else {
showNotification({
title: '插件卸载失败',
body: res.body as string
})
}
window.webContents.send('hideLoading')
dispose()
}
const handlePluginUpdate = () => {
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
const dispose = handleNPMError()
const res = await picgo.pluginHandler.update([msg])
if (res.success) {
event.sender.send('updateSuccess', res.body[0])
} else {
showNotification({
title: '插件更新失败',
body: res.body as string
})
}
event.sender.send('hideLoading')
dispose()
})
const handlePluginUpdate = async (fullName: string) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const dispose = handleNPMError()
const res = await picgo.pluginHandler.update([fullName])
if (res.success) {
window.webContents.send('updateSuccess', res.body[0])
} else {
showNotification({
title: '插件更新失败',
body: res.body as string
})
}
window.webContents.send('hideLoading')
dispose()
}
const handleNPMError = (): IDispose => {
@@ -221,6 +217,7 @@ const handleGetPicBedConfig = () => {
})
}
// TODO: remove it
const handlePluginActions = () => {
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
const plugin = picgo.pluginLoader.getPlugin(name)
@@ -257,29 +254,29 @@ const handlePicGoGetConfig = () => {
}
const handleImportLocalPlugin = () => {
ipcMain.on('importLocalPlugin', (event: IpcMainEvent) => {
ipcMain.on('importLocalPlugin', async (event: IpcMainEvent) => {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
dialog.showOpenDialog(settingWindow, {
const res = await dialog.showOpenDialog(settingWindow, {
properties: ['openDirectory']
}, async (filePath: string[]) => {
if (filePath.length > 0) {
const res = await picgo.pluginHandler.install(filePath)
if (res.success) {
const list = getPluginList()
event.sender.send('pluginList', list)
showNotification({
title: '导入插件成功',
body: ''
})
} else {
showNotification({
title: '导入插件失败',
body: res.body as string
})
}
}
event.sender.send('hideLoading')
})
const filePaths = res.filePaths
if (filePaths.length > 0) {
const res = await picgo.pluginHandler.install(filePaths)
if (res.success) {
const list = getPluginList()
event.sender.send('pluginList', list)
showNotification({
title: '导入插件成功',
body: ''
})
} else {
showNotification({
title: '导入插件失败',
body: res.body as string
})
}
}
event.sender.send('hideLoading')
})
}
@@ -319,12 +316,22 @@ const handlePicGoGalleryDB = () => {
const res = await dbStore.removeById(id)
event.sender.send(PICGO_REMOVE_BY_ID_DB, res, callbackId)
})
ipcMain.handle(PASTE_TEXT, async (item: ImgInfo, copy = true) => {
const pasteStyle = picgo.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = picgo.getConfig<string>('settings.customLink')
const txt = pasteTemplate(pasteStyle, item, customLink)
if (copy) {
clipboard.writeText(txt)
}
return txt
})
}
const handleOpenFile = () => {
ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => {
const abFilePath = path.join(STORE_PATH, fileName)
shell.openItem(abFilePath)
shell.openPath(abFilePath)
})
}
@@ -332,8 +339,6 @@ export default {
listen () {
handleGetPluginList()
handlePluginInstall()
handlePluginUninstall()
handlePluginUpdate()
handleGetPicBedConfig()
handlePluginActions()
handleRemoveFiles()
@@ -342,5 +347,8 @@ export default {
handlePicGoGalleryDB()
handleImportLocalPlugin()
handleOpenFile()
}
},
// TODO: separate to single file
handlePluginUninstall,
handlePluginUpdate
}
+286
View File
@@ -0,0 +1,286 @@
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import { Menu, BrowserWindow, app, dialog } from 'electron'
import getPicBeds from '~/main/utils/getPicBeds'
import picgo from '@core/picgo'
import {
uploadClipboardFiles
} from '~/main/apis/app/uploader/apis'
import { privacyManager } from '~/main/utils/privacyManager'
import pkg from 'root/package.json'
import GuiApi from 'apis/gui'
import PicGoCore from '~/universal/types/picgo'
import { PICGO_CONFIG_PLUGIN, PICGO_HANDLE_PLUGIN_ING, PICGO_TOGGLE_PLUGIN } from '~/universal/events/constants'
import picgoCoreIPC from '~/main/events/picgoCoreIPC'
interface GuiMenuItem {
label: string
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
}
const buildMiniPageMenu = () => {
const picBeds = getPicBeds()
const current = picgo.getConfig('picBed.uploader')
const submenu = picBeds.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: current === item.type,
click () {
picgo.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
}
}
})
const template = [
{
label: '打开详细窗口',
click () {
windowManager.get(IWindowList.SETTING_WINDOW)!.show()
if (windowManager.has(IWindowList.MINI_WINDOW)) {
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
}
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu
},
{
label: '剪贴板图片上传',
click () {
uploadClipboardFiles()
}
},
{
label: '隐藏窗口',
click () {
BrowserWindow.getFocusedWindow()!.hide()
}
},
{
label: '隐私协议',
click () {
privacyManager.show(false)
}
},
{
label: '重启应用',
click () {
app.relaunch()
app.exit(0)
}
},
{
role: 'quit',
label: '退出'
}
]
// @ts-ignore
return Menu.buildFromTemplate(template)
}
const buildMainPageMenu = () => {
const template = [
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '赞助PicGo',
click () {
// TODO: show donation
}
},
{
label: '生成图床配置二维码',
click () {
// TODO: qrcode
// _this.qrcodeVisible = true
}
},
{
label: '隐私协议',
click () {
privacyManager.show(false)
}
}
]
// @ts-ignore
return Menu.buildFromTemplate(template)
}
const buildUploadPageMenu = () => {
const picBeds = getPicBeds()
const currentPicBed = picgo.getConfig('picBed.uploader')
const submenu = picBeds.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: currentPicBed === item.type,
click () {
picgo.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
}
}
})
// @ts-ignore
return Menu.buildFromTemplate(submenu)
}
// TODO: separate to single file
const handleRestoreState = (item: string, name: string): void => {
if (item === 'uploader') {
const current = picgo.getConfig('picBed.current')
if (current === name) {
picgo.saveConfig({
'picBed.current': 'smms',
'picBed.uploader': 'smms'
})
}
}
if (item === 'transformer') {
const current = picgo.getConfig('picBed.transformer')
if (current === name) {
picgo.saveConfig({
'picBed.transformer': 'path'
})
}
}
}
const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
const menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
picgo.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: true
})
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, true)
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
picgo.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: false
})
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, false)
if (plugin.config.transformer.name) {
handleRestoreState('transformer', plugin.config.transformer.name)
}
if (plugin.config.uploader.name) {
handleRestoreState('uploader', plugin.config.uploader.name)
}
}
}, {
label: '卸载插件',
click () {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
picgoCoreIPC.handlePluginUninstall(plugin.fullName)
}
}, {
label: '更新插件',
click () {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
picgoCoreIPC.handlePluginUpdate(plugin.fullName)
}
}]
for (const i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = {
label: `配置${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
click () {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const currentType = i
const configName = plugin.config[i].fullName || plugin.config[i].name
const config = plugin.config[i].config
window.webContents.send(PICGO_CONFIG_PLUGIN, currentType, configName, config)
}
}
menu.push(obj)
}
}
// handle transformer
if (plugin.config.transformer.name) {
const currentTransformer = picgo.getConfig<string>('picBed.transformer') || 'path'
const pluginTransformer = plugin.config.transformer.name
const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
click () {
const transformer = plugin.config.transformer.name
const currentTransformer = picgo.getConfig<string>('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
picgo.saveConfig({
'picBed.transformer': 'path'
})
} else {
picgo.saveConfig({
'picBed.transformer': transformer
})
}
}
}
menu.push(obj)
}
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (const i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
// ipcRenderer.send('pluginActions', plugin.fullName, i.label)
const picgPlugin = picgo.pluginLoader.getPlugin(plugin.fullName)
if (picgPlugin?.guiMenu?.(picgo)?.length) {
const menu: GuiMenuItem[] = picgPlugin.guiMenu(picgo)
menu.forEach(item => {
if (item.label === i.label) {
item.handle(picgo, GuiApi.getInstance())
}
})
}
}
})
}
}
// @ts-ignore
return Menu.buildFromTemplate(menu)
}
export {
buildMiniPageMenu,
buildMainPageMenu,
buildUploadPageMenu,
buildPluginPageMenu
}
+7 -3
View File
@@ -66,6 +66,7 @@ class LifeCycle {
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
await migrateGalleryFromVersion230(db, GalleryDB.getInstance(), picgo)
}
private onReady () {
const readyFunction = async () => {
console.log('on ready')
@@ -74,7 +75,7 @@ class LifeCycle {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
} catch (e: any) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
@@ -96,8 +97,8 @@ class LifeCycle {
handleStartUpFiles(process.argv, process.cwd())
}
if (global.notificationList?.length > 0) {
while (global.notificationList.length) {
if (global.notificationList && global.notificationList?.length > 0) {
while (global.notificationList?.length) {
const option = global.notificationList.pop()
const notice = new Notification(option!)
notice.show()
@@ -110,6 +111,7 @@ class LifeCycle {
readyFunction()
}
}
private onRunning () {
app.on('second-instance', (event, commandLine, workingDirectory) => {
logger.info('detect second instance')
@@ -144,6 +146,7 @@ class LifeCycle {
process.env.XDG_CURRENT_DESKTOP = 'Unity'
}
}
private onQuit () {
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
@@ -171,6 +174,7 @@ class LifeCycle {
}
}
}
async launchApp () {
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
+1 -1
View File
@@ -37,7 +37,7 @@ const migrateGalleryFromVersion230 = async (configDB: typeof ConfigStore, galler
const configPath = configDB.getConfigPath()
const configBakPath = path.join(path.dirname(configPath), 'config.bak.json')
// migrate gallery from config to gallery db
if (originGallery && originGallery?.length > 0) {
if (originGallery && Array.isArray(originGallery) && originGallery?.length > 0) {
if (fse.existsSync(configBakPath)) {
fse.copyFileSync(configPath, configBakPath)
}
+9 -3
View File
@@ -29,6 +29,7 @@ class Server {
}
this.httpServer = http.createServer(this.handleRequest)
}
private checkIfConfigIsValid (config: IObj | undefined) {
if (config && config.port && config.host && (config.enable !== undefined)) {
return true
@@ -36,6 +37,7 @@ class Server {
return false
}
}
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
if (request.method === 'POST') {
if (!routers.getHandler(request.url!)) {
@@ -57,8 +59,8 @@ class Server {
request.on('end', () => {
try {
postObj = (body === '') ? {} : JSON.parse(body)
} catch (err) {
logger.error(`[PicGo Server]`, err)
} catch (err: any) {
logger.error('[PicGo Server]', err)
return handleResponse({
response,
body: {
@@ -67,7 +69,7 @@ class Server {
}
})
}
logger.info(`[PicGo Server] get the request`, body)
logger.info('[PicGo Server] get the request', body)
const handler = routers.getHandler(request.url!)
handler!({
...postObj,
@@ -81,6 +83,7 @@ class Server {
response.end()
}
}
// port as string is a bug
private listen = (port: number | string) => {
logger.info(`[PicGo Server] is listening at ${port}`)
@@ -102,18 +105,21 @@ class Server {
}
})
}
startup () {
console.log('startup', this.config.enable)
if (this.config.enable) {
this.listen(this.config.port)
}
}
shutdown (hasStarted?: boolean) {
this.httpServer.close()
if (!hasStarted) {
logger.info('[PicGo Server] shutdown')
}
}
restart () {
this.config = picgo.getConfig('settings.server')
this.shutdown()
+1
View File
@@ -4,6 +4,7 @@ class Router {
get (url: string, callback: routeHandler): void {
this.router.set(url, callback)
}
post (url: string, callback: routeHandler): void {
this.router.set(url, callback)
}
+1 -1
View File
@@ -69,7 +69,7 @@ router.post('/upload', async ({
})
}
}
} catch (err) {
} catch (err: any) {
logger.error(err)
handleResponse({
response,
+4 -4
View File
@@ -33,7 +33,7 @@ function resolveMacWorkFlow () {
* 初始化剪贴板生成图片的脚本
*/
function resolveClipboardImageGenerator () {
let clipboardFiles = getClipboardFiles()
const clipboardFiles = getClipboardFiles()
if (!fs.pathExistsSync(path.join(CONFIG_DIR, 'windows10.ps1'))) {
clipboardFiles.forEach(item => {
fs.copyFileSync(item.origin, item.dest)
@@ -46,8 +46,8 @@ function resolveClipboardImageGenerator () {
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
try {
let file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
let file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
const file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
const file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
if (!file1 || !file2 || !file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2)
@@ -59,7 +59,7 @@ function resolveClipboardImageGenerator () {
}
function getClipboardFiles () {
let files = [
const files = [
'/linux.sh',
'/mac.applescript',
'/windows.ps1',
+1 -1
View File
@@ -34,7 +34,7 @@ export const showNotification = (options: IPrivateShowNotificationOption = {
}
export const showMessageBox = (options: any) => {
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => {
return new Promise<IShowMessageBoxResult>(async (resolve) => {
dialog.showMessageBox(
options
).then((res) => {
+1 -1
View File
@@ -39,7 +39,7 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger
path: item
}
} else {
let tempPath = path.join(cwd, item)
const tempPath = path.join(cwd, item)
if (fs.existsSync(tempPath)) {
return {
path: tempPath
@@ -1,9 +1,8 @@
import { IPasteStyle } from '#/types/enum'
import { handleUrlEncode } from './common'
const formatCustomLink = (customLink: string, item: ImgInfo) => {
let fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
const url = handleUrlEncode(item.url || item.imgUrl)
const fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
const url = item.url || item.imgUrl
const formatObj = {
url,
fileName
@@ -11,7 +10,7 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => {
const keys = Object.keys(formatObj) as ['url', 'fileName']
keys.forEach(item => {
if (customLink.indexOf(`$${item}`) !== -1) {
let reg = new RegExp(`\\$${item}`, 'g')
const reg = new RegExp(`\\$${item}`, 'g')
customLink = customLink.replace(reg, formatObj[item])
}
})
@@ -19,14 +18,14 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => {
}
export default (style: IPasteStyle, item: ImgInfo, customLink: string | undefined) => {
const url = handleUrlEncode(item.url || item.imgUrl)
const url = item.url || item.imgUrl
const _customLink = customLink || '$url'
const tpl = {
'markdown': `![](${url})`,
'HTML': `<img src="${url}"/>`,
'URL': url,
'UBB': `[IMG]${url}[/IMG]`,
'Custom': formatCustomLink(_customLink, item)
markdown: `![](${url})`,
HTML: `<img src="${url}"/>`,
URL: url,
UBB: `[IMG]${url}[/IMG]`,
Custom: formatCustomLink(_customLink, item)
}
return tpl[style]
}
+2 -1
View File
@@ -23,7 +23,8 @@ export default class extends Vue {
this.value = true
}
}
choosePicBed (val: string) {
choosePicBed () {
this.saveConfig({
'picBed.current': this.type,
'picBed.uploader': this.type
+8 -5
View File
@@ -83,8 +83,9 @@ export default class extends Vue {
handleConfigChange (val: any) {
this.handleConfig(val)
}
async validate () {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
// @ts-ignore
this.$refs.form.validate((valid: boolean) => {
if (valid) {
@@ -109,18 +110,20 @@ export default class extends Vue {
return `transformer.${this.id}`
}
default:
return `unknown`
return 'unknown'
}
}
async handleConfig (val: any) {
this.ruleForm = Object.assign({}, {})
const config = await this.getConfig<IPicGoPluginConfig>(this.getConfigType())
if (val.length > 0 && config) {
if (val.length > 0) {
this.configList = cloneDeep(val).map((item: any) => {
let defaultValue = item.default !== undefined
? item.default : item.type === 'checkbox'
? [] : null
? item.default
: item.type === 'checkbox'
? []
: null
if (item.type === 'checkbox') {
const defaults = item.choices.filter((i: any) => {
return i.checked
+7 -1
View File
@@ -15,7 +15,7 @@
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { remote, ipcRenderer, IpcRendererEvent } from 'electron'
import { ipcRenderer, IpcRendererEvent } from 'electron'
import {
SHOW_INPUT_BOX,
SHOW_INPUT_BOX_RESPONSE
@@ -30,30 +30,36 @@ export default class extends Vue {
title: '',
placeholder: ''
}
created () {
ipcRenderer.on(SHOW_INPUT_BOX, this.ipcEventHandler)
this.$bus.$on(SHOW_INPUT_BOX, this.initInputBoxValue)
}
ipcEventHandler (evt: IpcRendererEvent, options: IShowInputBoxOption) {
this.initInputBoxValue(options)
}
initInputBoxValue (options: IShowInputBoxOption) {
this.inputBoxValue = options.value || ''
this.inputBoxOptions.title = options.title || ''
this.inputBoxOptions.placeholder = options.placeholder || ''
this.showInputBoxVisible = true
}
handleInputBoxCancel () {
// TODO: RPCServer
this.showInputBoxVisible = false
ipcRenderer.send(SHOW_INPUT_BOX, '')
this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, '')
}
handleInputBoxConfirm () {
this.showInputBoxVisible = false
ipcRenderer.send(SHOW_INPUT_BOX, this.inputBoxValue)
this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, this.inputBoxValue)
}
beforeDestroy () {
ipcRenderer.removeListener(SHOW_INPUT_BOX, this.ipcEventHandler)
this.$bus.$off(SHOW_INPUT_BOX)
+22 -61
View File
@@ -17,6 +17,7 @@
:default-active="defaultActive"
@select="handleSelect"
:unique-opened="true"
@open="handleGetPicPeds"
>
<el-menu-item index="upload">
<i class="el-icon-upload"></i>
@@ -147,28 +148,18 @@ import { Component, Vue, Watch } from 'vue-property-decorator'
import QrcodeVue from 'qrcode.vue'
import pick from 'lodash/pick'
import pkg from 'root/package.json'
import keyDetect from '@/utils/key-binding'
import {
remote,
ipcRenderer,
IpcRendererEvent,
clipboard
} from 'electron'
// import db from '#/datastore'
import mixin from '@/utils/mixin'
import InputBoxDialog from '@/components/InputBoxDialog.vue'
import {
SHOW_PRIVACY_MESSAGE
MINIMIZE_WINDOW,
CLOSE_WINDOW,
SHOW_MAIN_PAGE_MENU
} from '~/universal/events/constants'
import { IConfig } from 'picgo/dist/src/types/index'
const { Menu, dialog, BrowserWindow } = remote
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
@Component({
name: 'main-page',
mixins: [mixin],
@@ -180,7 +171,6 @@ const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) =>
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
@@ -191,13 +181,13 @@ export default class extends Vue {
choosedPicBedForQRCode: string[] = []
created () {
this.os = process.platform
this.buildMenu()
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.handleGetPicPeds()
}
@Watch('choosedPicBedForQRCode')
choosedPicBedForQRCodeChange (val: string[], oldVal: string[]) {
choosedPicBedForQRCodeChange (val: string[]) {
if (val.length > 0) {
this.$nextTick(async () => {
const picBedConfig = await this.getConfig('picBed')
@@ -207,6 +197,10 @@ export default class extends Vue {
}
}
handleGetPicPeds = () => {
ipcRenderer.send('getPicBeds')
}
handleSelect (index: string) {
const type = index.match(/picbeds-/)
if (type === null) {
@@ -229,71 +223,38 @@ export default class extends Vue {
}
}
}
minimizeWindow () {
const window = BrowserWindow.getFocusedWindow()
window!.minimize()
ipcRenderer.send(MINIMIZE_WINDOW)
}
closeWindow () {
const window = BrowserWindow.getFocusedWindow()
if (process.platform === 'linux') {
window!.hide()
} else {
window!.close()
}
}
buildMenu () {
const _this = this
const template = [
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '赞助PicGo',
click () {
_this.visible = true
}
},
{
label: '生成图床配置二维码',
click () {
_this.qrcodeVisible = true
}
},
{
label: '隐私协议',
click () {
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
}
}
]
this.menu = Menu.buildFromTemplate(template)
ipcRenderer.send(CLOSE_WINDOW)
}
openDialog () {
// this.menu!.popup(remote.getCurrentWindow())
this.menu!.popup()
ipcRenderer.send(SHOW_MAIN_PAGE_MENU)
}
openMiniWindow () {
ipcRenderer.send('openMiniWindow')
}
handleCopyPicBedConfig () {
clipboard.writeText(this.picBedConfigString)
this.$message.success('图床配置复制成功')
}
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
this.picBed = picBeds
}
beforeRouteEnter (to: any, from: any, next: any) {
beforeRouteEnter (to: any, next: any) {
next((vm: this) => {
vm.defaultActive = to.name
})
}
beforeDestroy () {
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
+44 -16
View File
@@ -110,10 +110,9 @@
<script lang="ts">
// @ts-ignore
import gallerys from 'vue-gallery'
import pasteStyle from '#/utils/pasteTemplate'
import { IPasteStyle } from '#/types/enum'
import { Component, Vue, Watch } from 'vue-property-decorator'
import { IResult } from '@picgo/store/dist/types'
import { PASTE_TEXT } from '#/events/constants'
import {
ipcRenderer,
clipboard,
@@ -133,11 +132,13 @@ export default class extends Vue {
urlProperty: 'imgUrl',
closeOnSlideClick: true
}
dialogVisible = false
imgInfo = {
id: '',
imgUrl: ''
}
choosedList: IObjT<boolean> = {}
choosedPicBed: string[] = []
lastChoosed: number = -1
@@ -152,6 +153,7 @@ export default class extends Vue {
UBB: 'UBB',
Custom: 'Custom'
}
picBed: IPicBedType[] = []
@Watch('$route')
handleRouteUpdate (to: any, from: any) {
@@ -163,8 +165,9 @@ export default class extends Vue {
this.updateGallery()
}
}
async created () {
ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => {
ipcRenderer.on('updateGallery', () => {
this.$nextTick(async () => {
this.updateGallery()
})
@@ -173,18 +176,26 @@ export default class extends Vue {
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.updateGallery()
}
mounted () {
document.addEventListener('keydown', this.handleDetectShiftKey)
document.addEventListener('keyup', this.handleDetectShiftKey)
}
handleDetectShiftKey (event: KeyboardEvent) {
if (event.keyCode === 16) {
this.isShiftKeyPress = !this.isShiftKeyPress
if (event.key === 'Shift') {
if (event.type === 'keydown') {
this.isShiftKeyPress = true
} else if (event.type === 'keyup') {
this.isShiftKeyPress = false
}
}
}
get filterList () {
return this.getGallery()
}
get isAllSelected () {
const values = Object.values(this.choosedList)
if (values.length === 0) {
@@ -195,9 +206,11 @@ export default class extends Vue {
})
}
}
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
this.picBed = picBeds
}
getGallery (): ImgInfo[] {
if (this.searchText || this.choosedPicBed.length > 0) {
return this.images
@@ -216,6 +229,7 @@ export default class extends Vue {
return this.images
}
}
async updateGallery () {
this.images = (await this.$$db.get({ orderBy: 'desc' })).data
}
@@ -224,12 +238,13 @@ export default class extends Vue {
handleFilterListChange () {
this.clearChoosedList()
}
handleChooseImage (val: boolean, index: number) {
if (val === true) {
this.handleBarActive = true
if (this.lastChoosed !== -1 && this.isShiftKeyPress) {
let min = Math.min(this.lastChoosed, index)
let max = Math.max(this.lastChoosed, index)
const min = Math.min(this.lastChoosed, index)
const max = Math.max(this.lastChoosed, index)
for (let i = min + 1; i < max; i++) {
const id = this.filterList[i].id!
this.$set(this.choosedList, id, true)
@@ -238,6 +253,7 @@ export default class extends Vue {
this.lastChoosed = index
}
}
clearChoosedList () {
this.isShiftKeyPress = false
Object.keys(this.choosedList).forEach(key => {
@@ -245,10 +261,12 @@ export default class extends Vue {
})
this.lastChoosed = -1
}
zoomImage (index: number) {
this.idx = index
this.changeZIndexForGallery(true)
}
changeZIndexForGallery (isOpen: boolean) {
if (isOpen) {
// @ts-ignore
@@ -258,25 +276,25 @@ export default class extends Vue {
document.querySelector('.main-content.el-row').style.zIndex = 10
}
}
handleClose () {
this.idx = null
this.changeZIndexForGallery(false)
}
async copy (item: ImgInfo) {
const style = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = await this.getConfig<string>('settings.customLink')
const copyLink = pasteStyle(style, item, customLink)
const copyLink = await ipcRenderer.invoke(PASTE_TEXT, item)
const obj = {
title: '复制链接成功',
body: copyLink,
icon: item.url || item.imgUrl
}
const myNotification = new Notification(obj.title, obj)
clipboard.writeText(copyLink)
myNotification.onclick = () => {
return true
}
}
remove (id: string) {
this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', {
confirmButtonText: '确定',
@@ -300,11 +318,13 @@ export default class extends Vue {
return true
})
}
openDialog (item: ImgInfo) {
this.imgInfo.id = item.id!
this.imgInfo.imgUrl = item.imgUrl as string
this.dialogVisible = true
}
async confirmModify () {
await this.$$db.updateById(this.imgInfo.id, {
imgUrl: this.imgInfo.imgUrl
@@ -321,26 +341,31 @@ export default class extends Vue {
this.dialogVisible = false
this.updateGallery()
}
choosePicBed (type: string) {
let idx = this.choosedPicBed.indexOf(type)
const idx = this.choosedPicBed.indexOf(type)
if (idx !== -1) {
this.choosedPicBed.splice(idx, 1)
} else {
this.choosedPicBed.push(type)
}
}
cleanSearch () {
this.searchText = ''
}
isMultiple (obj: IObj) {
return Object.values(obj).some(item => item)
}
toggleSelectAll () {
const result = !this.isAllSelected
this.filterList.forEach(item => {
this.$set(this.choosedList, item.id!, result)
})
}
multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
const multiRemoveNumber = Object.values(this.choosedList).filter(item => item).length
@@ -350,7 +375,7 @@ export default class extends Vue {
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
let files: IResult<ImgInfo>[] = []
const files: IResult<ImgInfo>[] = []
const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i]
@@ -379,11 +404,10 @@ export default class extends Vue {
})
}
}
async multiCopy () {
if (Object.values(this.choosedList).some(item => item)) {
const copyString: string[] = []
const style = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = await this.getConfig<string>('settings.customLink')
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
const imageIDList = Object.keys(this.choosedList)
for (let i = 0; i < imageIDList.length; i++) {
@@ -391,7 +415,8 @@ export default class extends Vue {
if (this.choosedList[key]) {
const item = await this.$$db.getById<ImgInfo>(key)
if (item) {
copyString.push(pasteStyle(style, item, customLink))
const txt = await ipcRenderer.invoke(PASTE_TEXT, item)
copyString.push(txt)
this.choosedList[key] = false
}
}
@@ -407,9 +432,11 @@ export default class extends Vue {
}
}
}
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
}
// getPasteStyle () {
// this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
// }
@@ -417,6 +444,7 @@ export default class extends Vue {
this.saveConfig('settings.pasteStyle', val)
this.pasteStyle = val
}
beforeDestroy () {
ipcRenderer.removeAllListeners('updateGallery')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
+24 -71
View File
@@ -20,10 +20,9 @@ import mixin from '@/utils/mixin'
import { Component, Vue, Watch } from 'vue-property-decorator'
import {
ipcRenderer,
IpcRendererEvent,
remote
IpcRendererEvent
} from 'electron'
import { SHOW_PRIVACY_MESSAGE } from '~/universal/events/constants'
import { SHOW_MINI_PAGE_MENU, SET_MINI_WINDOW_POS } from '~/universal/events/constants'
@Component({
name: 'mini-page',
mixins: [mixin]
@@ -55,6 +54,7 @@ export default class extends Vue {
})
this.getPicBeds()
}
mounted () {
window.addEventListener('mousedown', this.handleMouseDown, false)
window.addEventListener('mousemove', this.handleMouseMove, false)
@@ -73,27 +73,31 @@ export default class extends Vue {
}, 1200)
}
}
getPicBeds () {
this.picBed = ipcRenderer.sendSync('getPicBeds')
this.buildMenu()
}
onDrop (e: DragEvent) {
this.dragover = false
this.ipcSendFiles(e.dataTransfer!.files)
}
openUploadWindow () {
// @ts-ignore
document.getElementById('file-uploader').click()
}
onChange (e: any) {
this.ipcSendFiles(e.target.files)
// @ts-ignore
document.getElementById('file-uploader').value = ''
}
ipcSendFiles (files: FileList) {
let sendFiles: IFileWithPath[] = []
Array.from(files).forEach((item, index) => {
let obj = {
const sendFiles: IFileWithPath[] = []
Array.from(files).forEach((item) => {
const obj = {
name: item.name,
path: item.path
}
@@ -101,6 +105,7 @@ export default class extends Vue {
})
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
handleMouseDown (e: MouseEvent) {
this.dragging = true
this.wX = e.pageX
@@ -108,20 +113,28 @@ export default class extends Vue {
this.screenX = e.screenX
this.screenY = e.screenY
}
handleMouseMove (e: MouseEvent) {
e.preventDefault()
e.stopPropagation()
if (this.dragging) {
const xLoc = e.screenX - this.wX
const yLoc = e.screenY - this.wY
remote.BrowserWindow.getFocusedWindow()!.setBounds({
ipcRenderer.send(SET_MINI_WINDOW_POS, {
x: xLoc,
y: yLoc,
width: 64,
height: 64
})
// remote.BrowserWindow.getFocusedWindow()!.setBounds({
// x: xLoc,
// y: yLoc,
// width: 64,
// height: 64
// })
}
}
handleMouseUp (e: MouseEvent) {
this.dragging = false
if (this.screenX === e.screenX && this.screenY === e.screenY) {
@@ -133,71 +146,11 @@ export default class extends Vue {
}
}
}
openContextMenu () {
this.menu!.popup()
}
async buildMenu () {
const _this = this
const current = await this.getConfig('picBed.current')
const submenu = this.picBed.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: current === item.type,
click () {
_this.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
ipcRenderer.send('syncPicBed')
}
}
})
const template = [
{
label: '打开详细窗口',
click () {
ipcRenderer.send('openSettingWindow')
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu
},
{
label: '剪贴板图片上传',
click () {
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
},
{
label: '隐藏窗口',
click () {
remote.BrowserWindow.getFocusedWindow()!.hide()
}
},
{
label: '隐私协议',
click () {
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
}
},
{
label: '重启应用',
click () {
remote.app.relaunch()
remote.app.exit(0)
}
},
{
role: 'quit',
label: '退出'
}
]
// @ts-ignore
this.menu = remote.Menu.buildFromTemplate(template)
ipcRenderer.send(SHOW_MINI_PAGE_MENU)
}
beforeDestroy () {
ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
+44 -8
View File
@@ -339,10 +339,9 @@
import keyDetect from '@/utils/key-binding'
import pkg from 'root/package.json'
import { IConfig } from 'picgo/dist/src/types/index'
import { PICGO_OPEN_FILE } from '#/events/constants'
import { PICGO_OPEN_FILE, OPEN_URL } from '#/events/constants'
import {
ipcRenderer,
remote
ipcRenderer
} from 'electron'
import { Component, Vue } from 'vue-property-decorator'
// import db from '#/datastore'
@@ -373,6 +372,7 @@ export default class extends Vue {
autoCopyUrl: true,
checkBetaUpdate: true
}
picBed: IPicBedType[] = []
logFileVisible = false
keyBindingVisible = false
@@ -383,9 +383,11 @@ export default class extends Vue {
customLink = {
value: '$url'
}
shortKey: IShortKeyMap = {
upload: ''
}
proxy = ''
npmRegistry = ''
npmProxy = ''
@@ -394,6 +396,7 @@ export default class extends Vue {
{ validator: customLinkRule, trigger: 'blur' }
]
}
logLevel = {
all: '全部-All',
success: '成功-Success',
@@ -402,11 +405,13 @@ export default class extends Vue {
warn: '提醒-Warn',
none: '不记录日志-None'
}
server = {
port: 36677,
host: '127.0.0.1',
enable: true
}
version = pkg.version
latestVersion = ''
os = ''
@@ -418,12 +423,14 @@ export default class extends Vue {
return false
}
}
created () {
this.os = process.platform
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.initData()
}
async initData () {
const config = (await this.getConfig<IConfig>())!
if (config !== undefined) {
@@ -469,21 +476,27 @@ export default class extends Vue {
if (item.visible) {
return item.name
}
}) as string[]
return null
}).filter(item => item) as string[]
}
openFile (file: string) {
ipcRenderer.send(PICGO_OPEN_FILE, file)
}
openLogSetting () {
this.logFileVisible = true
}
keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+')
}
async cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = await this.getConfig<string>('settings.customLink') || '$url'
}
confirmCustomLink () {
// @ts-ignore
this.$refs.customLink.validate((valid: boolean) => {
@@ -496,10 +509,12 @@ export default class extends Vue {
}
})
}
async cancelProxy () {
this.proxyVisible = false
this.proxy = await this.getConfig<string>('picBed.proxy') || ''
}
confirmProxy () {
this.proxyVisible = false
this.saveConfig({
@@ -514,12 +529,15 @@ export default class extends Vue {
return true
}
}
updateHelperChange (val: boolean) {
this.saveConfig('settings.showUpdateTip', val)
}
checkBetaUpdateChange (val: boolean) {
this.saveConfig('settings.checkBetaUpdate', val)
}
handleShowPicBedListChange (val: string[]) {
const list = this.picBed.map(item => {
if (!val.includes(item.name)) {
@@ -534,20 +552,24 @@ export default class extends Vue {
})
ipcRenderer.send('getPicBeds')
}
handleAutoStartChange (val: boolean) {
this.saveConfig('settings.autoStart', val)
ipcRenderer.send('autoStart', val)
}
handleRename (val: boolean) {
this.saveConfig({
'settings.rename': val
})
}
handleAutoRename (val: boolean) {
this.saveConfig({
'settings.autoRename': val
})
}
compareVersion2Update (current: string, latest: string) {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
@@ -562,6 +584,7 @@ export default class extends Vue {
}
return false
}
checkUpdate () {
this.checkUpdateVisible = true
this.$http.get(releaseUrl)
@@ -576,24 +599,29 @@ export default class extends Vue {
})
})
}
confirmCheckVersion () {
if (this.needUpdate) {
remote.shell.openExternal(downloadUrl)
ipcRenderer.send(OPEN_URL, downloadUrl)
}
this.checkUpdateVisible = false
}
cancelCheckVersion () {
this.checkUpdateVisible = false
}
handleUploadNotification (val: boolean) {
this.saveConfig({
'settings.uploadNotification': val
})
}
handleMiniWindowOntop (val: boolean) {
this.saveConfig('settings.miniWindowOntop', val)
this.$message.info('需要重启生效')
}
handleAutoCopyUrl (val: boolean) {
this.saveConfig('settings.autoCopy', val)
const successNotification = new Notification('设置自动复制链接', {
@@ -603,6 +631,7 @@ export default class extends Vue {
return true
}
}
confirmLogLevelSetting () {
if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级')
@@ -618,6 +647,7 @@ export default class extends Vue {
}
this.logFileVisible = false
}
async cancelLogLevelSetting () {
this.logFileVisible = false
let logLevel = await this.getConfig<string | string[]>('settings.logLevel')
@@ -630,6 +660,7 @@ export default class extends Vue {
}
this.form.logLevel = logLevel
}
confirmServerSetting () {
// @ts-ignore
this.server.port = parseInt(this.server.port, 10)
@@ -645,6 +676,7 @@ export default class extends Vue {
this.serverVisible = false
ipcRenderer.send('updateServer')
}
async cancelServerSetting () {
this.serverVisible = false
this.server = await this.getConfig('settings.server') || {
@@ -653,10 +685,11 @@ export default class extends Vue {
enable: true
}
}
handleLevelDisabled (val: string) {
let currentLevel = val
const currentLevel = val
let flagLevel
let result = this.form.logLevel.some(item => {
const result = this.form.logLevel.some(item => {
if (item === 'all' || item === 'none') {
flagLevel = item
}
@@ -673,12 +706,15 @@ export default class extends Vue {
}
return false
}
goConfigPage () {
remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
ipcRenderer.send(OPEN_URL, 'https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
}
goShortCutPage () {
this.$router.push('shortKey')
}
beforeDestroy () {
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
+58 -105
View File
@@ -109,11 +109,17 @@ import ConfigForm from '@/components/ConfigForm.vue'
import { debounce } from 'lodash'
import {
ipcRenderer,
remote,
IpcRendererEvent
} from 'electron'
import { handleStreamlinePluginName } from '~/universal/utils/common'
const { Menu } = remote
import {
OPEN_URL,
RELOAD_APP,
PICGO_CONFIG_PLUGIN,
PICGO_HANDLE_PLUGIN_ING,
PICGO_TOGGLE_PLUGIN,
SHOW_PLUGIN_PAGE_MENU
} from '#/events/constants'
@Component({
name: 'plugin',
@@ -144,6 +150,7 @@ export default class extends Vue {
? `picgo-plugin-${this.searchText}`
: this.searchText
}
@Watch('npmSearchText')
onNpmSearchTextChange (val: string) {
if (val) {
@@ -154,6 +161,7 @@ export default class extends Vue {
this.getPluginList()
}
}
@Watch('dialogVisible')
onDialogVisible (val: boolean) {
if (val) {
@@ -164,6 +172,7 @@ export default class extends Vue {
document.querySelector('.main-content.el-row').style.zIndex = 10
}
}
async created () {
this.os = process.platform
ipcRenderer.on('hideLoading', () => {
@@ -214,105 +223,45 @@ export default class extends Vue {
})
this.pluginNameList = this.pluginNameList.filter(item => item !== plugin)
})
ipcRenderer.on(PICGO_CONFIG_PLUGIN, (evt: IpcRendererEvent, currentType: string, configName: string, config: any) => {
this.currentType = currentType
this.configName = configName
this.dialogVisible = true
this.config = config
})
ipcRenderer.on(PICGO_HANDLE_PLUGIN_ING, (evt: IpcRendererEvent, fullName: string) => {
this.pluginList.forEach(item => {
if (item.fullName === fullName || (item.name === fullName)) {
item.ing = true
}
})
this.loading = true
})
ipcRenderer.on(PICGO_TOGGLE_PLUGIN, (evt: IpcRendererEvent, fullName: string, enabled: boolean) => {
const plugin = this.pluginList.find(item => item.fullName === fullName)
if (plugin) {
plugin.enabled = enabled
this.getPicBeds()
this.needReload = true
}
})
this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = await this.getConfig<boolean>('needReload') || false
}
async buildContextMenu (plugin: IPicGoPlugin) {
const _this = this
let menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
_this.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: true
})
plugin.enabled = true
_this.getPicBeds()
_this.needReload = true
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
_this.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: false
})
plugin.enabled = false
_this.getPicBeds()
if (plugin.config.transformer.name) {
_this.handleRestoreState('transformer', plugin.config.transformer.name)
}
if (plugin.config.uploader.name) {
_this.handleRestoreState('uploader', plugin.config.uploader.name)
}
_this.needReload = true
}
}, {
label: '卸载插件',
click () {
_this.uninstallPlugin(plugin.fullName)
}
}, {
label: '更新插件',
click () {
_this.updatePlugin(plugin.fullName)
}
}]
for (let i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = {
label: `配置${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
click () {
_this.currentType = i
_this.configName = plugin.config[i].fullName || plugin.config[i].name
_this.dialogVisible = true
_this.config = plugin.config[i].config
},
enabled: plugin.enabled
}
menu.push(obj)
}
}
// handle transformer
if (plugin.config.transformer.name) {
let currentTransformer = await this.getConfig<string>('picBed.transformer') || 'path'
let pluginTransformer = plugin.config.transformer.name
const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
click () {
_this.toggleTransformer(plugin.config.transformer.name)
}
}
menu.push(obj)
}
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (let i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
ipcRenderer.send('pluginActions', plugin.fullName, i.label)
}
})
}
}
this.menu = Menu.buildFromTemplate(menu)
this.menu.popup()
ipcRenderer.send(SHOW_PLUGIN_PAGE_MENU, plugin)
}
getPluginList () {
ipcRenderer.send('getPluginList')
}
getPicBeds () {
ipcRenderer.send('getPicBeds')
}
installPlugin (item: IPicGoPlugin) {
if (!item.gui) {
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
@@ -330,6 +279,7 @@ export default class extends Vue {
ipcRenderer.send('installPlugin', item.fullName)
}
}
uninstallPlugin (val: string) {
this.pluginList.forEach(item => {
if (item.name === val) {
@@ -339,6 +289,7 @@ export default class extends Vue {
this.loading = true
ipcRenderer.send('uninstallPlugin', val)
}
updatePlugin (val: string) {
this.pluginList.forEach(item => {
if (item.fullName === val) {
@@ -348,10 +299,11 @@ export default class extends Vue {
this.loading = true
ipcRenderer.send('updatePlugin', val)
}
reloadApp () {
remote.app.relaunch()
remote.app.exit(0)
ipcRenderer.send(RELOAD_APP)
}
async handleReload () {
this.saveConfig({
needReload: true
@@ -364,21 +316,11 @@ export default class extends Vue {
this.reloadApp()
}
}
cleanSearch () {
this.searchText = ''
}
async toggleTransformer (transformer: string) {
let currentTransformer = await this.getConfig<string>('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.saveConfig({
'picBed.transformer': 'path'
})
} else {
this.saveConfig({
'picBed.transformer': transformer
})
}
}
async handleConfirmConfig () {
// @ts-ignore
const result = await this.$refs.configForm.validate()
@@ -410,6 +352,7 @@ export default class extends Vue {
this.getPluginList()
}
}
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}`)
@@ -428,6 +371,7 @@ export default class extends Vue {
this.loading = false
})
}
handleSearchResult (item: INPMSearchResultObject) {
const name = handleStreamlinePluginName(item.package.name)
let gui = false
@@ -450,6 +394,7 @@ export default class extends Vue {
ing: false // installing or uninstalling
}
}
// restore Uploader & Transformer
async handleRestoreState (item: string, name: string) {
if (item === 'uploader') {
@@ -470,18 +415,26 @@ export default class extends Vue {
}
}
}
openHomepage (url: string) {
if (url) {
remote.shell.openExternal(url)
ipcRenderer.send(OPEN_URL, url)
}
}
goAwesomeList () {
remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
ipcRenderer.send(OPEN_URL, 'https://github.com/PicGo/Awesome-PicGo')
}
saveConfig (data: IObj) {
ipcRenderer.send('picgoSaveData', data)
}
handleImportLocalPlugin () {
ipcRenderer.send('importLocalPlugin')
this.loading = true
}
beforeDestroy () {
ipcRenderer.removeAllListeners('pluginList')
ipcRenderer.removeAllListeners('installPlugin')
+3
View File
@@ -41,12 +41,15 @@ export default class extends Vue {
this.id = id
})
}
confirmName () {
ipcRenderer.send(`rename${this.id}`, this.fileName)
}
cancel () {
ipcRenderer.send(`rename${this.id}`, null)
}
beforeDestroy () {
ipcRenderer.removeAllListeners('rename')
}
+9
View File
@@ -118,35 +118,43 @@ export default class extends Vue {
}
})
}
@Watch('keyBindingVisible')
onKeyBindingVisibleChange (val: boolean) {
ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, val)
}
calcOrigin (item: string) {
const [origin] = item.split(':')
return origin
}
calcOriginShowName (item: string) {
return item.replace('picgo-plugin-', '')
}
toggleEnable (item: IShortKeyConfig) {
const status = !item.enable
item.enable = status
ipcRenderer.send('bindOrUnbindShortKey', item, item.from)
}
keyDetect (event: KeyboardEvent) {
this.shortKey = keyDetect(event).join('+')
}
async openKeyBindingDialog (config: IShortKeyConfig, index: number) {
this.command = `${config.from}:${config.name}`
this.shortKey = await this.getConfig(`settings.shortKey.${this.command}.key`) || ''
this.currentIndex = index
this.keyBindingVisible = true
}
async cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`) || ''
}
async confirmKeyBinding () {
const oldKey = await this.getConfig<string>(`settings.shortKey.${this.command}.key`)
const config = Object.assign({}, this.list[this.currentIndex])
@@ -159,6 +167,7 @@ export default class extends Vue {
}
})
}
beforeDestroy () {
ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
}
+13 -8
View File
@@ -28,10 +28,9 @@
<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'
import { IPasteStyle } from '#/types/enum'
import { ipcRenderer } from 'electron'
import { IResult } from '@picgo/store/dist/types'
import { PASTE_TEXT } from '#/events/constants'
@Component({
name: 'tray-page',
@@ -44,28 +43,31 @@ export default class extends Vue {
body: '',
icon: ''
}
clipboardFiles: ImgInfo[] = []
uploadFlag = false
get reverseList () {
return this.files.slice().reverse()
}
async getData () {
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
}
async copyTheLink (item: ImgInfo) {
this.notification.body = item.imgUrl!
this.notification.icon = item.imgUrl!
const myNotification = new Notification(this.notification.title, this.notification)
const pasteStyle = await this.getConfig<IPasteStyle>('settings.pasteStyle') || IPasteStyle.MARKDOWN
const customLink = await this.getConfig<string>('settings.customLink')
clipboard.writeText(pasteTemplate(pasteStyle, item, customLink))
ipcRenderer.invoke(PASTE_TEXT, item)
myNotification.onclick = () => {
return true
}
}
calcHeight (width: number, height: number): number {
return height * 160 / width
}
disableDragFile () {
window.addEventListener('dragover', (e) => {
e = e || event
@@ -76,6 +78,7 @@ export default class extends Vue {
e.preventDefault()
}, false)
}
uploadClipboardFiles () {
if (this.uploadFlag) {
return
@@ -83,6 +86,7 @@ export default class extends Vue {
this.uploadFlag = true
ipcRenderer.send('uploadClipboardFiles')
}
mounted () {
this.disableDragFile()
this.getData()
@@ -96,15 +100,16 @@ export default class extends Vue {
ipcRenderer.on('clipboardFiles', (event: Event, files: ImgInfo[]) => {
this.clipboardFiles = files
})
ipcRenderer.on('uploadFiles', async (event: Event) => {
ipcRenderer.on('uploadFiles', async () => {
this.files = (await this.$$db.get<ImgInfo>({ orderBy: 'desc', limit: 5 })).data
console.log(this.files)
this.uploadFlag = false
})
ipcRenderer.on('updateFiles', (event: Event) => {
ipcRenderer.on('updateFiles', () => {
this.getData()
})
}
beforeDestroy () {
ipcRenderer.removeAllListeners('dragFiles')
ipcRenderer.removeAllListeners('clipboardFiles')
+22 -31
View File
@@ -60,17 +60,16 @@
import { Component, Vue, Watch } from 'vue-property-decorator'
import {
ipcRenderer,
IpcRendererEvent,
remote
IpcRendererEvent
} from 'electron'
import {
SHOW_INPUT_BOX,
SHOW_INPUT_BOX_RESPONSE
SHOW_INPUT_BOX_RESPONSE,
SHOW_UPLOAD_PAGE_MENU
} from '~/universal/events/constants'
import {
isUrl
} from '~/universal/utils/common'
const { Menu } = remote
@Component({
name: 'upload'
})
@@ -82,7 +81,6 @@ export default class extends Vue {
pasteStyle = ''
picBed: IPicBedType[] = []
picBedName = ''
menu: Electron.Menu | null= null
mounted () {
ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => {
if (progress !== -1) {
@@ -102,6 +100,7 @@ export default class extends Vue {
ipcRenderer.on('getPicBeds', this.getPicBeds)
this.$bus.$on(SHOW_INPUT_BOX_RESPONSE, this.handleInputBoxValue)
}
@Watch('progress')
onProgressChange (val: number) {
if (val === 100) {
@@ -114,12 +113,14 @@ export default class extends Vue {
}, 1200)
}
}
beforeDestroy () {
this.$bus.$off(SHOW_INPUT_BOX_RESPONSE)
ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeAllListeners('syncPicBed')
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
onDrop (e: DragEvent) {
this.dragover = false
const items = e.dataTransfer!.items
@@ -136,6 +137,7 @@ export default class extends Vue {
this.ipcSendFiles(e.dataTransfer!.files)
}
}
handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
// text/html
// Use this data to get a more precise URL
@@ -151,17 +153,20 @@ export default class extends Vue {
this.$message.error('请拖入合法的图片文件或者图片URL地址')
}
}
openUplodWindow () {
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: IFileWithPath[] = []
Array.from(files).forEach((item, index) => {
let obj = {
const sendFiles: IFileWithPath[] = []
Array.from(files).forEach((item) => {
const obj = {
name: item.name,
path: item.path
}
@@ -169,17 +174,21 @@ export default class extends Vue {
})
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
async getPasteStyle () {
this.pasteStyle = await this.getConfig('settings.pasteStyle') || 'markdown'
}
handlePasteStyleChange (val: string) {
this.saveConfig({
'settings.pasteStyle': val
})
}
uploadClipboardFiles () {
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
async uploadURLFiles () {
const str = await navigator.clipboard.readText()
this.$bus.$emit(SHOW_INPUT_BOX, {
@@ -188,6 +197,7 @@ export default class extends Vue {
placeholder: 'http://或者https://开头'
})
}
handleInputBoxValue (val: string) {
if (val === '') return false
if (isUrl(val)) {
@@ -198,6 +208,7 @@ export default class extends Vue {
this.$message.error('请输入合法的URL')
}
}
async getDefaultPicBed () {
const currentPicBed = await this.getConfig<string>('picBed.current')
this.picBed.forEach(item => {
@@ -206,34 +217,14 @@ export default class extends Vue {
}
})
}
getPicBeds (event: Event, picBeds: IPicBedType[]) {
this.picBed = picBeds
this.getDefaultPicBed()
}
async handleChangePicBed () {
await this.buildMenu()
// this.menu.popup(remote.getCurrentWindow())
this.menu!.popup()
}
async buildMenu () {
const _this = this
const currentPicBed = await this.getConfig<string>('picBed.current')
const submenu = this.picBed.filter(item => item.visible).map(item => {
return {
label: item.name,
type: 'radio',
checked: currentPicBed === item.type,
click () {
_this.saveConfig({
'picBed.current': item.type,
'picBed.uploader': item.type
})
ipcRenderer.send('syncPicBed')
}
}
})
// @ts-ignore
this.menu = Menu.buildFromTemplate(submenu)
ipcRenderer.send(SHOW_UPLOAD_PAGE_MENU)
}
}
</script>
+2
View File
@@ -86,12 +86,14 @@ export default class extends Vue {
customUrl: '',
options: ''
}
async created () {
const config = await this.getConfig<IAliYunConfig>('picBed.aliyun')
if (config) {
this.form = Object.assign({}, config)
}
}
confirm () {
// @ts-ignore
this.$refs.aliyun.validate((valid) => {
+2
View File
@@ -71,12 +71,14 @@ export default class extends Vue {
customUrl: '',
branch: ''
}
async created () {
const config = await this.getConfig<IGitHubConfig>('picBed.github')
if (config) {
this.form = Object.assign({}, config)
}
}
confirm () {
// @ts-ignore
this.$refs.github.validate((valid) => {
+2
View File
@@ -48,12 +48,14 @@ export default class extends Vue {
clientId: '',
proxy: ''
}
async created () {
const config = await this.getConfig<IImgurConfig>('picBed.imgur')
if (config) {
this.form = Object.assign({}, config)
}
}
confirm () {
// @ts-ignore
this.$refs.imgur.validate((valid) => {
+4
View File
@@ -52,6 +52,7 @@ export default class extends Vue {
ipcRenderer.send('getPicBedConfig', this.$route.params.type)
ipcRenderer.on('getPicBedConfig', this.getPicBeds)
}
async handleConfirm () {
// @ts-ignore
const result = await this.$refs.configForm.validate()
@@ -67,6 +68,7 @@ export default class extends Vue {
}
}
}
setDefaultPicBed (type: string) {
this.saveConfig({
'picBed.current': type,
@@ -81,10 +83,12 @@ export default class extends Vue {
return true
}
}
getPicBeds (event: IpcRendererEvent, config: any[], name: string) {
this.config = config
this.picBedName = name
}
beforeDestroy () {
ipcRenderer.removeListener('getPicBedConfig', this.getPicBeds)
}
+2
View File
@@ -88,12 +88,14 @@ export default class extends Vue {
options: '',
path: ''
}
async created () {
const config = await this.getConfig<IQiniuConfig>('picBed.qiniu')
if (config) {
this.form = Object.assign({}, config)
}
}
confirm () {
// @ts-ignore
this.$refs.qiniu.validate((valid) => {
+2
View File
@@ -41,12 +41,14 @@ export default class extends Vue {
form: ISMMSConfig = {
token: ''
}
async created () {
const config = await this.getConfig<string | boolean>('picBed.smms.token')
if (typeof config !== 'boolean') {
this.form.token = config || ''
}
}
confirm () {
// @ts-ignore
this.$refs.smms.validate((valid) => {
+6 -2
View File
@@ -86,9 +86,10 @@
</div>
</template>
<script lang="ts">
import { ipcRenderer } from 'electron'
import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/ConfirmButtonMixin'
import { remote } from 'electron'
import { OPEN_URL } from '#/events/constants'
@Component({
name: 'tcyun',
mixins: [mixin]
@@ -104,12 +105,14 @@ export default class extends Vue {
customUrl: '',
version: 'v4'
}
async created () {
const config = await this.getConfig<ITcYunConfig>('picBed.tcyun')
if (config) {
this.form = Object.assign({}, config)
}
}
confirm () {
// @ts-ignore
this.$refs.tcyun.validate((valid) => {
@@ -128,8 +131,9 @@ export default class extends Vue {
}
})
}
openWiki () {
remote.shell.openExternal('https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#腾讯云cos')
ipcRenderer.send(OPEN_URL, 'https://picgo.github.io/PicGo-Doc/zh/guide/config.html#%E8%85%BE%E8%AE%AF%E4%BA%91cos')
}
}
</script>
+2
View File
@@ -79,12 +79,14 @@ export default class extends Vue {
options: '',
path: ''
}
async created () {
const config = await this.getConfig<IUpYunConfig>('picBed.upyun')
if (config) {
this.form = Object.assign({}, config)
}
}
confirm () {
// @ts-ignore
this.$refs.tcyun.validate((valid) => {
+1 -1
View File
@@ -1,5 +1,4 @@
import { Component, Vue } from 'vue-property-decorator'
import { ipcRenderer } from 'electron'
import { IConfig } from 'picgo/dist/src/types'
@Component
export default class extends Vue {
@@ -10,6 +9,7 @@ export default class extends Vue {
this.defaultPicBed = config?.picBed?.uploader || config?.picBed?.current || 'smms'
}
}
setDefaultPicBed (type: string) {
this.saveConfig({
'picBed.current': type,
+6
View File
@@ -15,26 +15,32 @@ export class GalleryDB implements IGalleryDB {
const res = await this.msgHandler<IGetResult<T>>(PICGO_GET_DB, filter)
return res
}
async insert<T> (value: T): Promise<IResult<T>> {
const res = await this.msgHandler<IResult<T>>(PICGO_INSERT_DB, value)
return res
}
async insertMany<T> (value: T[]): Promise<IResult<T>[]> {
const res = await this.msgHandler<IResult<T>[]>(PICGO_INSERT_MANY_DB, value)
return res
}
async updateById (id: string, value: IObject): Promise<boolean> {
const res = await this.msgHandler<boolean>(PICGO_UPDATE_BY_ID_DB, id, value)
return res
}
async getById<T> (id: string): Promise<IResult<T> | undefined> {
const res = await this.msgHandler<IResult<T> | undefined>(PICGO_GET_BY_ID_DB, id)
return res
}
async removeById (id: string): Promise<void> {
const res = await this.msgHandler<void>(PICGO_REMOVE_BY_ID_DB, id)
return res
}
private msgHandler<T> (method: string, ...args: any[]): Promise<T> {
return new Promise((resolve) => {
const callbackId = uuid()
+3 -3
View File
@@ -14,16 +14,16 @@ const isSpecialKey = (keyCode: number) => {
const keyDetect = (event: KeyboardEvent) => {
const meta = process.platform === 'darwin' ? 'Cmd' : 'Super'
let specialKey = {
const specialKey = {
Ctrl: event.ctrlKey,
Shift: event.shiftKey,
Alt: event.altKey,
[meta]: event.metaKey
}
let pressKey = []
const pressKey = []
for (let i in specialKey) {
for (const i in specialKey) {
if (specialKey[i]) {
pressKey.push(i)
}
+1
View File
@@ -13,6 +13,7 @@ export default class extends Vue {
}
ipcRenderer.send(PICGO_SAVE_CONFIG, config)
}
getConfig<T> (key?: string): Promise<T | undefined> {
return new Promise((resolve) => {
const callbackId = uuid()
+3
View File
@@ -4,11 +4,13 @@ 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)) {
@@ -17,6 +19,7 @@ export default class extends Vue {
e.dataTransfer!.dropEffect = 'none'
}
}
beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag)
+15
View File
@@ -13,3 +13,18 @@ export const PICGO_UPDATE_BY_ID_DB = 'PICGO_UPDATE_BY_ID_DB'
export const PICGO_GET_BY_ID_DB = 'PICGO_GET_BY_ID_DB'
export const PICGO_REMOVE_BY_ID_DB = 'PICGO_REMOVE_BY_ID_DB'
export const PICGO_OPEN_FILE = 'PICGO_OPEN_FILE'
export const OPEN_DEVTOOLS = 'OPEN_DEVTOOLS'
export const SHOW_MINI_PAGE_MENU = 'SHOW_MINI_PAGE_MENU'
export const SHOW_MAIN_PAGE_MENU = 'SHOW_MAIN_PAGE_MENU'
export const SHOW_UPLOAD_PAGE_MENU = 'SHOW_UPLOAD_PAGE_MENU'
export const SHOW_PLUGIN_PAGE_MENU = 'SHOW_PLUGIN_PAGE_MENU'
export const MINIMIZE_WINDOW = 'MINIMIZE_WINDOW'
export const CLOSE_WINDOW = 'CLOSE_WINDOW'
export const OPEN_USER_STORE_FILE = 'OPEN_USER_STORE_FILE'
export const OPEN_URL = 'OPEN_URL'
export const RELOAD_APP = 'RELOAD_APP'
export const PICGO_CONFIG_PLUGIN = 'PICGO_CONFIG_PLUGIN'
export const PICGO_HANDLE_PLUGIN_ING = 'PICGO_HANDLE_PLUGIN_ING'
export const PICGO_TOGGLE_PLUGIN = 'PICGO_TOGGLE_PLUGIN'
export const PASTE_TEXT = 'PASTE_TEXT'
export const SET_MINI_WINDOW_POS = 'SET_MINI_WINDOW_POS'
+3 -7
View File
@@ -23,11 +23,7 @@ declare interface IWindowManager {
// https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js/44387594#44387594
declare global {
namespace NodeJS {
interface Global {
PICGO_GUI_VERSION: string
PICGO_CORE_VERSION: string
notificationList: IAppNotification[]
}
}
var PICGO_GUI_VERSION: string
var PICGO_CORE_VERSION: string
var notificationList: IAppNotification[]
}
+15 -1
View File
@@ -15,7 +15,7 @@ declare interface ErrnoException extends Error {
stack?: string;
}
declare var __static: string
declare let __static: string
declare type ILogType = 'success' | 'info' | 'warn' | 'error'
@@ -94,6 +94,7 @@ interface IBrowserWindowOptions {
webPreferences: {
nodeIntegration: boolean,
nodeIntegrationInWorker: boolean,
contextIsolation: boolean,
backgroundThrottling: boolean
webSecurity?: boolean
},
@@ -321,3 +322,16 @@ interface IAnalyticsData {
interface IStringKeyMap {
[propName: string]: any
}
type ILogArgvType = string | number
type ILogArgvTypeWithError = ILogArgvType | Error
interface IMiniWindowPos {
x: number,
y: number,
height: number,
width: number
}
type PromiseResType<T> = T extends Promise<infer R> ? R : T
+1 -1
View File
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es2020",
"target": "es2019", // https://github.com/TypeStrong/ts-loader/issues/1061
"module": "esnext",
"strict": true,
"jsx": "preserve",
+14 -5
View File
@@ -3,8 +3,6 @@ function resolve (dir) {
return path.join(__dirname, dir)
}
const arch = process.argv.includes('--ia32') ? 'ia32' : 'x64'
const config = {
configureWebpack: {
devtool: 'nosources-source-map'
@@ -23,6 +21,7 @@ const config = {
},
pluginOptions: {
electronBuilder: {
nodeIntegration: true, // will remove in the future
customFileProtocol: 'picgo://./',
externals: ['picgo'],
chainWebpackMainProcess: config => {
@@ -64,16 +63,26 @@ const config = {
icon: 'build/icons/icon.icns',
extendInfo: {
LSUIElement: 1
}
},
target: [{
target: 'dmg',
arch: [
'x64',
'arm64'
]
}],
// eslint-disable-next-line no-template-curly-in-string
artifactName: 'PicGo-${version}-${arch}.dmg'
},
win: {
icon: 'build/icons/icon.ico',
// eslint-disable-next-line no-template-curly-in-string
artifactName: `PicGo Setup \${version}-${arch}.exe`,
artifactName: 'PicGo Setup ${version}-${arch}.exe',
target: [{
target: 'nsis',
arch: [
arch
'x64',
'ia32'
]
}]
},
+6356 -4978
View File
File diff suppressed because it is too large Load Diff