mirror of
https://github.com/Molunerfinn/PicGo.git
synced 2024-04-21 10:31:33 +00:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a312d06b62 | ||
|
|
a1b70b4cb1 | ||
|
|
2d9e9c0a00 | ||
|
|
a657c51bb5 | ||
|
|
321e33967c | ||
|
|
0a986c8933 | ||
|
|
69e1dc8128 | ||
|
|
b05139fc9a | ||
|
|
992ff35936 | ||
|
|
06d40ef7f6 | ||
|
|
f53639153e | ||
|
|
3fd6e4e4c8 | ||
|
|
ce8f41516c | ||
|
|
f0e1fa12d7 | ||
|
|
3cad5f378d | ||
|
|
f9d6191d5e | ||
|
|
51d80a6a1d | ||
|
|
0fe3ade5c3 | ||
|
|
2129f86266 | ||
|
|
064f37d1a6 | ||
|
|
48592ec9b5 | ||
|
|
33fdb16ded | ||
|
|
bd2311bf50 | ||
|
|
e793599360 | ||
|
|
ce2b5cdeab | ||
|
|
2151857c89 | ||
|
|
32b8b976be | ||
|
|
eab014dc88 | ||
|
|
bb90e174f3 | ||
|
|
7b5e5ef2ed | ||
|
|
92ff282d2f | ||
|
|
2d42381f47 | ||
|
|
6e855384e0 | ||
|
|
21d3942d2b | ||
|
|
4f0809e6b3 |
+3
-1
@@ -15,7 +15,9 @@ module.exports = {
|
||||
'plugins': ['@typescript-eslint'],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
"indent": "off",
|
||||
"@typescript-eslint/indent": ["error", 2]
|
||||
},
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# main.yml
|
||||
|
||||
# Workflow's name
|
||||
name: Build
|
||||
|
||||
# Workflow's trigger
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
# 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-10.15]
|
||||
|
||||
# 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: |
|
||||
npm run release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
@@ -1,3 +1,87 @@
|
||||
# :tada: 2.3.0-beta.6 (2021-04-24)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add baidu tongji for analytics ([f536391](https://github.com/Molunerfinn/PicGo/commit/f536391))
|
||||
* add logs for picgo-server ([2d9e9c0](https://github.com/Molunerfinn/PicGo/commit/2d9e9c0)), closes [#627](https://github.com/Molunerfinn/PicGo/issues/627)
|
||||
* add privacy policy ([992ff35](https://github.com/Molunerfinn/PicGo/commit/992ff35))
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* add plugin install failed notice ([b05139f](https://github.com/Molunerfinn/PicGo/commit/b05139f))
|
||||
* default picBed using picBed.uploader instead of picBed.current ([0a986c8](https://github.com/Molunerfinn/PicGo/commit/0a986c8))
|
||||
* dev error with install vue-devtools ([a657c51](https://github.com/Molunerfinn/PicGo/commit/a657c51)), closes [#653](https://github.com/Molunerfinn/PicGo/issues/653) [#658](https://github.com/Molunerfinn/PicGo/issues/658)
|
||||
* disable plugin need reload app ([a1b70b4](https://github.com/Molunerfinn/PicGo/commit/a1b70b4))
|
||||
* fix analytics value ([06d40ef](https://github.com/Molunerfinn/PicGo/commit/06d40ef))
|
||||
* windows cli uploading bug ([321e339](https://github.com/Molunerfinn/PicGo/commit/321e339)), closes [#657](https://github.com/Molunerfinn/PicGo/issues/657)
|
||||
|
||||
|
||||
### :package: Chore
|
||||
|
||||
* add main process hot reload ([3fd6e4e](https://github.com/Molunerfinn/PicGo/commit/3fd6e4e))
|
||||
* fix eslint error notice in indent ([69e1dc8](https://github.com/Molunerfinn/PicGo/commit/69e1dc8))
|
||||
|
||||
|
||||
|
||||
# :tada: 2.3.0-beta.5 (2021-04-04)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add local plugin support && npm registry/proxy support ([f0e1fa1](https://github.com/Molunerfinn/PicGo/commit/f0e1fa1))
|
||||
* 为Linux系统适配桌面图标栏(Tray) ([#603](https://github.com/Molunerfinn/PicGo/issues/603)) ([0fe3ade](https://github.com/Molunerfinn/PicGo/commit/0fe3ade))
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* default github placeholder ([51d80a6](https://github.com/Molunerfinn/PicGo/commit/51d80a6))
|
||||
|
||||
|
||||
### :package: Chore
|
||||
|
||||
* change travis-ci -> GitHub Actions ([064f37d](https://github.com/Molunerfinn/PicGo/commit/064f37d))
|
||||
|
||||
|
||||
|
||||
# :tada: 2.3.0-beta.4 (2020-12-19)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* add global value for PicGo get GUI_VERSION & CORE_VERSION ([eab014d](https://github.com/Molunerfinn/PicGo/commit/eab014d))
|
||||
* **config:** auto configuration backup & fallback to avoid main process crash ([32b8b97](https://github.com/Molunerfinn/PicGo/commit/32b8b97)), closes [#568](https://github.com/Molunerfinn/PicGo/issues/568)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* disabled plugin won't be shown in plugin page ([33fdb16](https://github.com/Molunerfinn/PicGo/commit/33fdb16))
|
||||
* shortKeyConfig maybe undefined ([7b5e5ef](https://github.com/Molunerfinn/PicGo/commit/7b5e5ef)), closes [#557](https://github.com/Molunerfinn/PicGo/issues/557)
|
||||
* url encode before uploading by url ([ce2b5cd](https://github.com/Molunerfinn/PicGo/commit/ce2b5cd)), closes [#581](https://github.com/Molunerfinn/PicGo/issues/581)
|
||||
|
||||
|
||||
### :pencil: Documentation
|
||||
|
||||
* add flutter-picgo ([92ff282](https://github.com/Molunerfinn/PicGo/commit/92ff282))
|
||||
* add gitads ([2d42381](https://github.com/Molunerfinn/PicGo/commit/2d42381))
|
||||
* rm gitads ([bb90e17](https://github.com/Molunerfinn/PicGo/commit/bb90e17))
|
||||
* update discussions in doc ([e793599](https://github.com/Molunerfinn/PicGo/commit/e793599))
|
||||
* update install command by Homebrew ([#599](https://github.com/Molunerfinn/PicGo/issues/599)) ([bd2311b](https://github.com/Molunerfinn/PicGo/commit/bd2311b))
|
||||
* update README.md ([#555](https://github.com/Molunerfinn/PicGo/issues/555)) ([2151857](https://github.com/Molunerfinn/PicGo/commit/2151857))
|
||||
|
||||
|
||||
|
||||
# :tada: 2.3.0-beta.3 (2020-07-12)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* choose default picBed failure ([21d3942](https://github.com/Molunerfinn/PicGo/commit/21d3942)), closes [#537](https://github.com/Molunerfinn/PicGo/issues/537)
|
||||
* shortkey disabled failure ([4f0809e](https://github.com/Molunerfinn/PicGo/commit/4f0809e)), closes [#534](https://github.com/Molunerfinn/PicGo/issues/534)
|
||||
|
||||
|
||||
|
||||
# :tada: 2.3.0-beta.2 (2020-07-12)
|
||||
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<img src="https://raw.githubusercontent.com/Molunerfinn/test/master/picgo/New%20LOGO-150.png" alt="">
|
||||
<h1>PicGo</h1>
|
||||
<blockquote>图片上传+管理新体验 </blockquote>
|
||||
<a href="https://github.com/feross/standard">
|
||||
<a href="https://github.com/Molunerfinn/PicGo/actions">
|
||||
<img src="https://img.shields.io/badge/code%20style-standard-green.svg?style=flat-square" alt="">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/Molunerfinn/PicGo/builds">
|
||||
<img src="https://img.shields.io/travis/Molunerfinn/PicGo.svg?style=flat-square" alt="">
|
||||
<a href="https://github.com/Molunerfinn/PicGo/actions">
|
||||
<img src="https://github.com/Molunerfinn/PicGo/workflows/Build/badge.svg" alt="">
|
||||
</a>
|
||||
<a href="https://github.com/Molunerfinn/PicGo/releases">
|
||||
<img src="https://img.shields.io/github/downloads/Molunerfinn/PicGo/total.svg?style=flat-square" alt="">
|
||||
@@ -51,7 +51,7 @@ PicGo 本体支持如下图床:
|
||||
- 支持通过发送 HTTP 请求调用 PicGo 上传(v2.2.0+)
|
||||
- 更多功能等你自己去发现,同时也会不断开发新功能
|
||||
- 开发进度可以查看 [Projects](https://github.com/Molunerfinn/PicGo/projects),会同步更新开发进度
|
||||
- 欢迎加入 [官方 Gitter 频道](https://gitter.im/picgo-all/PicGo?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 与我交流
|
||||
- 欢迎加入 [官方讨论区](https://github.com/Molunerfinn/PicGo/discussions) 与我交流
|
||||
|
||||
**如果第一次使用,请参考应用 [使用文档](https://picgo.github.io/PicGo-Doc/zh/guide/getting-started.html)。遇到问题了还可以看看 [FAQ](https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md) 以及被关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed)。**
|
||||
|
||||
@@ -71,13 +71,13 @@ Windows 用户请下载最新版本的 `exe` 文件。
|
||||
|
||||
macOS 用户请下载最新版本的 `dmg` 文件。
|
||||
|
||||
**还可以使用 `brew cask` 来安装 PicGo: `brew cask install picgo`。感谢 @womeimingzi11 的贡献!**
|
||||
**还可以使用 [Homebrew](https://brew.sh/) 来安装 PicGo: `brew install picgo --cask`。感谢 @womeimingzi11 的贡献!**
|
||||
|
||||
### Linux
|
||||
|
||||
Linux 用户请下载 `AppImage` 文件。
|
||||
|
||||
**如果你是 Arch 类的 Linux 用户,可以直接通过 `aurman -S picgo-appimage` 来安装 PicGo。感谢 @houbaron 的贡献!**
|
||||
**如果你是 Arch 类的 Linux 用户,可以直接通过 `yay -S picgo-appimage` 来安装 PicGo (若是没有则先安装 yay ,是 aur 仓库助手)。感谢 @houbaron 的贡献!**
|
||||
|
||||
## 应用截图
|
||||
|
||||
@@ -125,7 +125,8 @@ npm run electron:build
|
||||
|
||||
## 其他相关
|
||||
|
||||
- [vs-picgo](https://github.com/Spades-S/vs-picgo):PicGo 的 VS Code 版。
|
||||
- [vs-picgo](https://github.com/PicGo/vs-picgo):PicGo 的 VS Code 版。
|
||||
- [flutter-picgo](https://github.com/PicGo/flutter-picgo):PicGo 的手机版(支持 Android 和 iOS )。
|
||||
|
||||
## 赞助
|
||||
|
||||
@@ -143,4 +144,4 @@ npm run electron:build
|
||||
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2017 - 2019 Molunerfinn
|
||||
Copyright (c) 2017 - 2019 Molunerfinn
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picgo",
|
||||
"version": "2.3.0-beta.2",
|
||||
"version": "2.3.0-beta.6",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service electron:serve",
|
||||
@@ -42,7 +42,7 @@
|
||||
"keycode": "^2.2.0",
|
||||
"lodash-id": "^0.14.0",
|
||||
"lowdb": "^1.0.0",
|
||||
"picgo": "^1.4.11",
|
||||
"picgo": "^1.4.19",
|
||||
"qrcode.vue": "^1.7.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-gallery": "^2.0.1",
|
||||
@@ -68,6 +68,7 @@
|
||||
"conventional-changelog": "^3.1.18",
|
||||
"cz-customizable": "^6.2.0",
|
||||
"electron": "^6.0.0",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"husky": "^3.1.0",
|
||||
|
||||
@@ -9,6 +9,7 @@ import VueLazyLoad from 'vue-lazyload'
|
||||
import axios from 'axios'
|
||||
import mainMixin from './renderer/utils/mainMixin'
|
||||
import bus from '@/utils/bus'
|
||||
import { initTalkingData } from './renderer/utils/analytics'
|
||||
|
||||
webFrame.setVisualZoomLevelLimits(1, 1)
|
||||
webFrame.setLayoutZoomLevelLimits(0, 0)
|
||||
@@ -36,3 +37,5 @@ new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
||||
initTalkingData()
|
||||
|
||||
@@ -24,14 +24,18 @@ class ShortKeyHandler {
|
||||
const commands = db.get('settings.shortKey') as IShortKeyConfigs
|
||||
Object.keys(commands)
|
||||
.filter(item => item.includes('picgo:'))
|
||||
.map(command => {
|
||||
.forEach(command => {
|
||||
const config = commands[command]
|
||||
globalShortcut.register(config.key, () => {
|
||||
this.handler(command)
|
||||
})
|
||||
// if disabled, don't register #534
|
||||
if (config.enable) {
|
||||
globalShortcut.register(config.key, () => {
|
||||
this.handler(command)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
private initPluginsShortKey () {
|
||||
// get enabled plugin
|
||||
const pluginList = picgo.pluginLoader.getList()
|
||||
for (let item of pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(item)
|
||||
@@ -46,7 +50,10 @@ class ShortKeyHandler {
|
||||
const command = `${item}:${cmd.name}`
|
||||
if (db.has(`settings.shortKey[${command}]`)) {
|
||||
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
|
||||
this.registerShortKey(commandConfig, command, cmd.handle, false)
|
||||
// if disabled, don't register #534
|
||||
if (commandConfig.enable) {
|
||||
this.registerShortKey(commandConfig, command, cmd.handle, false)
|
||||
}
|
||||
} else {
|
||||
this.registerShortKey(cmd, command, cmd.handle, true)
|
||||
}
|
||||
@@ -65,6 +72,8 @@ class ShortKeyHandler {
|
||||
} else {
|
||||
logger.warn(`${command} do not provide a key to bind`)
|
||||
}
|
||||
// if the configfile already had this command
|
||||
// then writeFlag -> false
|
||||
if (writeFlag) {
|
||||
picgo.saveConfig({
|
||||
[`settings.shortKey.${command}`]: {
|
||||
|
||||
+197
-132
@@ -16,40 +16,170 @@ import picgo from '@core/picgo'
|
||||
import pasteTemplate from '#/utils/pasteTemplate'
|
||||
import pkg from 'root/package.json'
|
||||
import { handleCopyUrl } from '~/main/utils/common'
|
||||
import { privacyManager } from '~/main/utils/privacyManager'
|
||||
let contextMenu: Menu | null
|
||||
let menu: Menu | null
|
||||
let tray: Tray | null
|
||||
export function createContextMenu () {
|
||||
const picBeds = getPicBeds()
|
||||
const submenu = picBeds.filter(item => item.visible).map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
type: 'radio',
|
||||
checked: db.get('picBed.current') === item.type,
|
||||
click () {
|
||||
picgo.saveConfig({
|
||||
'picBed.current': item.type
|
||||
})
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
const submenu = picBeds.filter(item => item.visible).map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
type: 'radio',
|
||||
checked: db.get('picBed.current') === item.type,
|
||||
click () {
|
||||
picgo.saveConfig({
|
||||
'picBed.current': item.type,
|
||||
'picBed.uploader': item.type
|
||||
})
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '关于',
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicGo',
|
||||
message: 'PicGo',
|
||||
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
|
||||
})
|
||||
})
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '关于',
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicGo',
|
||||
message: 'PicGo',
|
||||
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '选择默认图床',
|
||||
type: 'submenu',
|
||||
// @ts-ignore
|
||||
submenu
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
label: '打开更新助手',
|
||||
type: 'checkbox',
|
||||
checked: db.get('settings.showUpdateTip'),
|
||||
click () {
|
||||
const value = db.get('settings.showUpdateTip')
|
||||
db.set('settings.showUpdateTip', !value)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '隐私协议',
|
||||
click () {
|
||||
privacyManager.show(false)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '重启应用',
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
])
|
||||
} else if (process.platform === 'linux') {
|
||||
// TODO 图床选择功能
|
||||
// 由于在Linux难以像在Mac和Windows上那样在点击时构造ContextMenu,
|
||||
// 暂时取消这个选单,避免引起和设置中启用的图床不一致
|
||||
|
||||
// TODO 重启应用功能
|
||||
// 目前的实现无法正常工作
|
||||
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
label: '打开更新助手',
|
||||
type: 'checkbox',
|
||||
checked: db.get('settings.showUpdateTip'),
|
||||
click () {
|
||||
const value = db.get('settings.showUpdateTip')
|
||||
db.set('settings.showUpdateTip', !value)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '关于应用',
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicGo',
|
||||
message: 'PicGo',
|
||||
buttons: ['Ok'],
|
||||
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
|
||||
})
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export function createTray () {
|
||||
const menubarPic = process.platform === 'darwin' ? `${__static}/menubar.png` : `${__static}/menubar-nodarwin.png`
|
||||
tray = new Tray(menubarPic)
|
||||
// click事件在Mac和Windows上可以触发(在Ubuntu上无法触发,Unity不支持)
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
tray.on('right-click', () => {
|
||||
if (windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
|
||||
}
|
||||
createContextMenu()
|
||||
tray!.popUpContextMenu(contextMenu!)
|
||||
})
|
||||
tray.on('click', (event, bounds) => {
|
||||
if (process.platform === 'darwin') {
|
||||
toggleWindow(bounds)
|
||||
setTimeout(() => {
|
||||
let img = clipboard.readImage()
|
||||
let obj: ImgInfo[] = []
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
// @ts-ignore
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
obj.push({
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
})
|
||||
}
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('clipboardFiles', obj)
|
||||
}, 0)
|
||||
} else {
|
||||
if (windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
|
||||
}
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
@@ -57,116 +187,51 @@ export function createContextMenu () {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '选择默认图床',
|
||||
type: 'submenu',
|
||||
// @ts-ignore
|
||||
submenu
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
label: '打开更新助手',
|
||||
type: 'checkbox',
|
||||
checked: db.get('settings.showUpdateTip'),
|
||||
click () {
|
||||
const value = db.get('settings.showUpdateTip')
|
||||
db.set('settings.showUpdateTip', !value)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '重启应用',
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
// @ts-ignore
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
export function createTray () {
|
||||
const menubarPic = process.platform === 'darwin' ? `${__static}/menubar.png` : `${__static}/menubar-nodarwin.png`
|
||||
tray = new Tray(menubarPic)
|
||||
tray.on('right-click', () => {
|
||||
if (windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
|
||||
}
|
||||
createContextMenu()
|
||||
tray!.popUpContextMenu(contextMenu!)
|
||||
})
|
||||
tray.on('click', (event, bounds) => {
|
||||
if (process.platform === 'darwin') {
|
||||
toggleWindow(bounds)
|
||||
setTimeout(() => {
|
||||
let img = clipboard.readImage()
|
||||
let obj: ImgInfo[] = []
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
// @ts-ignore
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
obj.push({
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
tray.on('drag-enter', () => {
|
||||
if (systemPreferences.isDarkMode()) {
|
||||
tray!.setImage(`${__static}/upload-dark.png`)
|
||||
} else {
|
||||
tray!.setImage(`${__static}/upload.png`)
|
||||
}
|
||||
})
|
||||
|
||||
tray.on('drag-end', () => {
|
||||
tray!.setImage(`${__static}/menubar.png`)
|
||||
})
|
||||
|
||||
tray.on('drop-files', async (event: Event, files: string[]) => {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const imgs = await uploader
|
||||
.setWebContents(trayWindow.webContents)
|
||||
.upload(files)
|
||||
if (imgs !== false) {
|
||||
const pasteText: string[] = []
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl!,
|
||||
icon: files[i]
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.insert('uploaded', imgs[i])
|
||||
}
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('clipboardFiles', obj)
|
||||
}, 0)
|
||||
} else {
|
||||
if (windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
|
||||
handleCopyUrl(pasteText.join('\n'))
|
||||
trayWindow.webContents.send('dragFiles', imgs)
|
||||
}
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
|
||||
settingWindow!.show()
|
||||
settingWindow!.focus()
|
||||
if (windowManager.has(IWindowList.MINI_WINDOW)) {
|
||||
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
tray.on('drag-enter', () => {
|
||||
if (systemPreferences.isDarkMode()) {
|
||||
tray!.setImage(`${__static}/upload-dark.png`)
|
||||
} else {
|
||||
tray!.setImage(`${__static}/upload.png`)
|
||||
}
|
||||
})
|
||||
|
||||
tray.on('drag-end', () => {
|
||||
tray!.setImage(`${__static}/menubar.png`)
|
||||
})
|
||||
|
||||
tray.on('drop-files', async (event: Event, files: string[]) => {
|
||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const imgs = await uploader
|
||||
.setWebContents(trayWindow.webContents)
|
||||
.upload(files)
|
||||
if (imgs !== false) {
|
||||
const pasteText: string[] = []
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl!,
|
||||
icon: files[i]
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.insert('uploaded', imgs[i])
|
||||
}
|
||||
handleCopyUrl(pasteText.join('\n'))
|
||||
trayWindow.webContents.send('dragFiles', imgs)
|
||||
}
|
||||
})
|
||||
// toggleWindow()
|
||||
})
|
||||
// toggleWindow()
|
||||
} else if (process.platform === 'linux') {
|
||||
// click事件在Ubuntu上无法触发,Unity不支持(在Mac和Windows上可以触发)
|
||||
// 需要使用 setContextMenu 设置菜单
|
||||
createContextMenu()
|
||||
tray!.setContextMenu(contextMenu)
|
||||
}
|
||||
}
|
||||
|
||||
export function createMenu () {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
app,
|
||||
Notification,
|
||||
BrowserWindow,
|
||||
ipcMain,
|
||||
@@ -10,9 +9,14 @@ import picgo from '@core/picgo'
|
||||
import db from '#/datastore'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { IWindowList } from 'apis/app/window/constants'
|
||||
import util from 'util'
|
||||
import { IPicGo } from 'picgo/dist/src/types'
|
||||
import { showNotification, calcDurationRange } from '~/main/utils/common'
|
||||
import { TALKING_DATA_EVENT } from '~/universal/events/constants'
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
const waitForShow = (webcontent: WebContents) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
webcontent.on('did-finish-load', () => {
|
||||
resolve()
|
||||
})
|
||||
@@ -34,8 +38,23 @@ const waitForRename = (window: BrowserWindow, id: number): Promise<string|null>
|
||||
})
|
||||
}
|
||||
|
||||
const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) => {
|
||||
const data: ITalkingDataOptions = {
|
||||
EventId: 'upload',
|
||||
Label: options.type,
|
||||
MapKv: {
|
||||
by: options.fromClipboard ? 'clipboard' : 'files', // 上传剪贴板图片还是选择的文文件
|
||||
count: options.count, // 上传的数量
|
||||
duration: calcDurationRange(options.duration || 0), // 上传耗时
|
||||
type: options.type
|
||||
}
|
||||
}
|
||||
webContents.send(TALKING_DATA_EVENT, data)
|
||||
}
|
||||
|
||||
class Uploader {
|
||||
private webContents: WebContents | null = null
|
||||
private uploading: boolean = false
|
||||
constructor () {
|
||||
this.init()
|
||||
}
|
||||
@@ -59,7 +78,7 @@ class Uploader {
|
||||
}
|
||||
})
|
||||
picgo.helper.beforeUploadPlugins.register('renameFn', {
|
||||
handle: async ctx => {
|
||||
handle: async (ctx: IPicGo) => {
|
||||
const rename = db.get('settings.rename')
|
||||
const autoRename = db.get('settings.autoRename')
|
||||
if (autoRename || rename) {
|
||||
@@ -89,30 +108,44 @@ class Uploader {
|
||||
return this
|
||||
}
|
||||
|
||||
upload (img?: IUploadOption): Promise<ImgInfo[]|false> {
|
||||
picgo.upload(img)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
picgo.once('finished', ctx => {
|
||||
if (ctx.output.every((item: ImgInfo) => item.imgUrl)) {
|
||||
resolve(ctx.output)
|
||||
} else {
|
||||
resolve(false)
|
||||
async upload (img?: IUploadOption): Promise<ImgInfo[]|false> {
|
||||
if (this.uploading) {
|
||||
showNotification({
|
||||
title: '上传失败',
|
||||
body: '前序上传还在继续,请稍后再试'
|
||||
})
|
||||
return Promise.resolve(false)
|
||||
}
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
this.uploading = true
|
||||
const output = await picgo.upload(img)
|
||||
this.uploading = false
|
||||
if (Array.isArray(output) && output.every((item: ImgInfo) => item.imgUrl)) {
|
||||
if (this.webContents) {
|
||||
handleTalkingData(this.webContents, {
|
||||
fromClipboard: !img,
|
||||
type: db.get('picBed.current') || 'smms',
|
||||
count: img ? img.length : 1,
|
||||
duration: Date.now() - startTime
|
||||
} as IAnalyticsData)
|
||||
}
|
||||
picgo.removeAllListeners('failed')
|
||||
})
|
||||
picgo.once('failed', ctx => {
|
||||
setTimeout(() => {
|
||||
const notification = new Notification({
|
||||
title: '上传失败',
|
||||
body: '请检查配置和上传的文件是否符合要求'
|
||||
})
|
||||
notification.show()
|
||||
}, 500)
|
||||
picgo.removeAllListeners('finished')
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
return output
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
this.uploading = false
|
||||
logger.error(e)
|
||||
setTimeout(() => {
|
||||
showNotification({
|
||||
title: '上传失败',
|
||||
body: util.format(e.stack),
|
||||
clickToCopy: true
|
||||
})
|
||||
}, 500)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,4 +16,7 @@ picgo.saveConfig({
|
||||
PICGO_ENV: 'GUI'
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
picgo.GUI_VERSION = global.PICGO_GUI_VERSION
|
||||
|
||||
export default picgo! as PicGoCore
|
||||
|
||||
@@ -27,7 +27,7 @@ class GuiApi implements IGuiApi {
|
||||
return true
|
||||
}
|
||||
settingWindow.show()
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, 1000) // TODO: a better way to wait page loaded.
|
||||
|
||||
@@ -13,7 +13,9 @@ 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 } from '#/events/constants'
|
||||
import {
|
||||
TOGGLE_SHORTKEY_MODIFIED_MODE
|
||||
} from '#/events/constants'
|
||||
import {
|
||||
uploadClipboardFiles,
|
||||
uploadChoosedFiles
|
||||
|
||||
+128
-86
@@ -9,8 +9,13 @@ import {
|
||||
} from 'electron'
|
||||
import PicGoCore from '~/universal/types/picgo'
|
||||
import { IPicGoHelperType } from '#/types/enum'
|
||||
import shortKeyHandler from '../apis/app/shortKey/shortKeyHandler'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import picgo from '@core/picgo'
|
||||
import { handleStreamlinePluginName } from '~/universal/utils/common'
|
||||
import { IGuiMenuItem } from 'picgo/dist/src/types'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { IWindowList } from 'apis/app/window/constants'
|
||||
import { showNotification } from '~/main/utils/common'
|
||||
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
@@ -56,109 +61,119 @@ const handleConfigWithFunction = (config: any[]) => {
|
||||
return config
|
||||
}
|
||||
|
||||
const getPluginList = (): IPicGoPlugin[] => {
|
||||
const pluginList = picgo.pluginLoader.getFullList()
|
||||
const list = []
|
||||
for (let i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])!
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu: IGuiMenuItem[] = []
|
||||
if (plugin.guiMenu) {
|
||||
menu = plugin.guiMenu(picgo)
|
||||
}
|
||||
let gui = false
|
||||
if (pluginPKG.keywords && pluginPKG.keywords.length > 0) {
|
||||
if (pluginPKG.keywords.includes('picgo-gui-plugin')) {
|
||||
gui = true
|
||||
}
|
||||
}
|
||||
const obj: IPicGoPlugin = {
|
||||
name: handleStreamlinePluginName(pluginList[i]),
|
||||
fullName: pluginList[i],
|
||||
author: pluginPKG.author.name || pluginPKG.author,
|
||||
description: pluginPKG.description,
|
||||
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
|
||||
version: pluginPKG.version,
|
||||
gui,
|
||||
config: {
|
||||
plugin: {
|
||||
fullName: pluginList[i],
|
||||
name: handleStreamlinePluginName(pluginList[i]),
|
||||
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
|
||||
},
|
||||
uploader: {
|
||||
name: uploaderName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo))
|
||||
},
|
||||
transformer: {
|
||||
name: transformerName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo))
|
||||
}
|
||||
},
|
||||
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
|
||||
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
|
||||
guiMenu: menu,
|
||||
ing: false
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
const handleGetPluginList = () => {
|
||||
ipcMain.on('getPluginList', (event: IpcMainEvent) => {
|
||||
const pluginList = picgo.pluginLoader.getList()
|
||||
const list = []
|
||||
for (let i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu = []
|
||||
if (plugin.guiMenu) {
|
||||
menu = plugin.guiMenu(picgo)
|
||||
}
|
||||
let gui = false
|
||||
if (pluginPKG.keywords && pluginPKG.keywords.length > 0) {
|
||||
if (pluginPKG.keywords.includes('picgo-gui-plugin')) {
|
||||
gui = true
|
||||
}
|
||||
}
|
||||
const obj: IPicGoPlugin = {
|
||||
name: pluginList[i].replace(/picgo-plugin-/, ''),
|
||||
author: pluginPKG.author.name || pluginPKG.author,
|
||||
description: pluginPKG.description,
|
||||
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
|
||||
version: pluginPKG.version,
|
||||
gui,
|
||||
config: {
|
||||
plugin: {
|
||||
name: pluginList[i].replace(/picgo-plugin-/, ''),
|
||||
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
|
||||
},
|
||||
uploader: {
|
||||
name: uploaderName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo))
|
||||
},
|
||||
transformer: {
|
||||
name: transformerName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo))
|
||||
}
|
||||
},
|
||||
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
|
||||
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
|
||||
guiMenu: menu,
|
||||
ing: false
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
const list = getPluginList()
|
||||
event.sender.send('pluginList', list)
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginInstall = () => {
|
||||
ipcMain.on('installPlugin', async (event: IpcMainEvent, msg: string) => {
|
||||
ipcMain.on('installPlugin', async (event: IpcMainEvent, fullName: string) => {
|
||||
const dispose = handleNPMError()
|
||||
picgo.once('installSuccess', (notice: PicGoNotice) => {
|
||||
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
shortKeyHandler.registerPluginShortKey(notice.body[0])
|
||||
picgo.removeAllListeners('installFailed')
|
||||
dispose()
|
||||
const res = await picgo.pluginHandler.install([fullName])
|
||||
event.sender.send('installPlugin', {
|
||||
success: res.success,
|
||||
body: fullName,
|
||||
errMsg: res.success ? '' : res.body
|
||||
})
|
||||
picgo.once('installFailed', () => {
|
||||
picgo.removeAllListeners('installSuccess')
|
||||
dispose()
|
||||
})
|
||||
await picgo.pluginHandler.install([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
if (res.success) {
|
||||
shortKeyHandler.registerPluginShortKey(res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: '插件安装失败',
|
||||
body: res.body as string
|
||||
})
|
||||
}
|
||||
event.sender.send('hideLoading')
|
||||
dispose()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginUninstall = () => {
|
||||
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
|
||||
const dispose = handleNPMError()
|
||||
picgo.once('uninstallSuccess', (notice: PicGoNotice) => {
|
||||
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
shortKeyHandler.unregisterPluginShortKey(notice.body[0])
|
||||
picgo.removeAllListeners('uninstallFailed')
|
||||
dispose()
|
||||
})
|
||||
picgo.once('uninstallFailed', () => {
|
||||
picgo.removeAllListeners('uninstallSuccess')
|
||||
dispose()
|
||||
})
|
||||
await picgo.pluginHandler.uninstall([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
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 handlePluginUpdate = () => {
|
||||
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
|
||||
const dispose = handleNPMError()
|
||||
picgo.once('updateSuccess', (notice: { body: string[], title: string }) => {
|
||||
event.sender.send('updateSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
picgo.removeAllListeners('updateFailed')
|
||||
dispose()
|
||||
})
|
||||
picgo.once('updateFailed', () => {
|
||||
picgo.removeAllListeners('updateSuccess')
|
||||
dispose()
|
||||
})
|
||||
await picgo.pluginHandler.update([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,15 +204,14 @@ const handleGetPicBedConfig = () => {
|
||||
} else {
|
||||
event.sender.send('getPicBedConfig', [], name)
|
||||
}
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginActions = () => {
|
||||
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
|
||||
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
|
||||
const plugin = picgo.pluginLoader.getPlugin(name)
|
||||
const guiApi = new GuiApi()
|
||||
if (plugin.guiMenu && plugin.guiMenu(picgo).length > 0) {
|
||||
if (plugin?.guiMenu?.(picgo)?.length) {
|
||||
const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
|
||||
menu.forEach(item => {
|
||||
if (item.label === label) {
|
||||
@@ -223,6 +237,33 @@ const handlePicGoSaveData = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleImportLocalPlugin = () => {
|
||||
ipcMain.on('importLocalPlugin', (event: IpcMainEvent) => {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
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')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
handleGetPluginList()
|
||||
@@ -233,5 +274,6 @@ export default {
|
||||
handlePluginActions()
|
||||
handleRemoveFiles()
|
||||
handlePicGoSaveData()
|
||||
handleImportLocalPlugin()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { app } from 'electron'
|
||||
import fse from 'fs-extra'
|
||||
import path from 'path'
|
||||
import dayjs from 'dayjs'
|
||||
import util from 'util'
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const LOG_PATH = path.join(STORE_PATH, '/picgo.log')
|
||||
|
||||
// since the error may occur in picgo-core
|
||||
// so we can't use the log from picgo
|
||||
const loggerWriter = (error: Error) => {
|
||||
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ERROR] startup error`
|
||||
if (error?.stack) {
|
||||
log += `\n------Error Stack Begin------\n${util.format(error.stack)}\n-------Error Stack End-------\n`
|
||||
} else {
|
||||
const msg = JSON.stringify(error)
|
||||
log += `${msg}\n`
|
||||
}
|
||||
fse.appendFileSync(LOG_PATH, log)
|
||||
}
|
||||
|
||||
const handleProcessError = (error: Error) => {
|
||||
console.error(error)
|
||||
loggerWriter(error)
|
||||
}
|
||||
|
||||
process.on('uncaughtException', error => {
|
||||
handleProcessError(error)
|
||||
})
|
||||
|
||||
process.on('unhandledRejection', (error: any) => {
|
||||
handleProcessError(error)
|
||||
})
|
||||
+42
-26
@@ -1,12 +1,14 @@
|
||||
import './errorHandler'
|
||||
import {
|
||||
app,
|
||||
globalShortcut,
|
||||
protocol
|
||||
protocol,
|
||||
Notification
|
||||
} from 'electron'
|
||||
import {
|
||||
createProtocol,
|
||||
installVueDevtools
|
||||
createProtocol
|
||||
} from 'vue-cli-plugin-electron-builder/lib'
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||
import beforeOpen from '~/main/utils/beforeOpen'
|
||||
import fixPath from 'fix-path'
|
||||
import ipcList from '~/main/events/ipcList'
|
||||
@@ -29,8 +31,28 @@ import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import { getUploadFiles } from '~/main/utils/handleArgv'
|
||||
import db from '#/datastore'
|
||||
import bus from '@core/bus'
|
||||
import { privacyManager } from '~/main/utils/privacyManager'
|
||||
import logger from 'apis/core/picgo/logger'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
const files = getUploadFiles(argv, cwd, logger)
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
logger.info('cli -> uploading file from clipboard')
|
||||
uploadClipboardFiles()
|
||||
} else {
|
||||
logger.info('cli -> uploading files from cli', ...files.map(item => item.path))
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class LifeCycle {
|
||||
private beforeReady () {
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
@@ -47,16 +69,18 @@ class LifeCycle {
|
||||
if (isDevelopment && !process.env.IS_TEST) {
|
||||
// Install Vue Devtools
|
||||
try {
|
||||
await installVueDevtools()
|
||||
await installExtension(VUEJS_DEVTOOLS)
|
||||
} catch (e) {
|
||||
console.error('Vue Devtools failed to install:', e.toString())
|
||||
}
|
||||
}
|
||||
const res = await privacyManager.init()
|
||||
if (!res) {
|
||||
return app.quit()
|
||||
}
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
createTray()
|
||||
}
|
||||
createTray()
|
||||
db.set('needReload', false)
|
||||
updateChecker()
|
||||
// 不需要阻塞
|
||||
@@ -65,29 +89,23 @@ class LifeCycle {
|
||||
})
|
||||
server.startup()
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
let files = getUploadFiles()
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
uploadClipboardFiles()
|
||||
} else {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
}
|
||||
handleStartUpFiles(process.argv, process.cwd())
|
||||
}
|
||||
|
||||
if (global.notificationList?.length > 0) {
|
||||
while (global.notificationList.length) {
|
||||
const option = global.notificationList.pop()
|
||||
const notice = new Notification(option!)
|
||||
notice.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
private onRunning () {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
let files = getUploadFiles(commandLine, workingDirectory)
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
uploadClipboardFiles()
|
||||
} else {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
}
|
||||
} else {
|
||||
logger.info('detect second instance')
|
||||
const result = handleStartUpFiles(commandLine, workingDirectory)
|
||||
if (!result) {
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
if (settingWindow.isMinimized()) {
|
||||
@@ -135,13 +153,11 @@ class LifeCycle {
|
||||
process.on('message', data => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit()
|
||||
server.shutdown()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit()
|
||||
server.shutdown()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import DB from '#/datastore'
|
||||
// from v2.1.2
|
||||
const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
|
||||
let needUpgrade = false
|
||||
// #557 极端情况可能会出现配置不存在,需要重新写入
|
||||
if (shortKeyConfig === undefined) {
|
||||
const defaultShortKeyConfig = {
|
||||
enable: true,
|
||||
key: 'CommandOrControl+Shift+P',
|
||||
name: 'upload',
|
||||
label: '快捷上传'
|
||||
}
|
||||
db.set('settings.shortKey[picgo:upload]', defaultShortKeyConfig)
|
||||
return true
|
||||
}
|
||||
if (shortKeyConfig.upload) {
|
||||
needUpgrade = true
|
||||
// @ts-ignore
|
||||
shortKeyConfig['picgo:upload'] = {
|
||||
enable: true,
|
||||
@@ -11,14 +20,12 @@ const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: IShortKeyCo
|
||||
name: 'upload',
|
||||
label: '快捷上传'
|
||||
}
|
||||
// @ts-ignore
|
||||
delete shortKeyConfig.upload
|
||||
}
|
||||
if (needUpgrade) {
|
||||
db.set('settings.shortKey', shortKeyConfig)
|
||||
return shortKeyConfig
|
||||
} else {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -39,6 +39,7 @@ class Server {
|
||||
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
if (request.method === 'POST') {
|
||||
if (!routers.getHandler(request.url!)) {
|
||||
logger.warn(`[PicGo Server] don't support [${request.url}] url`)
|
||||
handleResponse({
|
||||
response,
|
||||
statusCode: 404,
|
||||
@@ -57,6 +58,7 @@ class Server {
|
||||
try {
|
||||
postObj = (body === '') ? {} : JSON.parse(body)
|
||||
} catch (err) {
|
||||
logger.error(`[PicGo Server]`, err)
|
||||
return handleResponse({
|
||||
response,
|
||||
body: {
|
||||
@@ -65,6 +67,7 @@ class Server {
|
||||
}
|
||||
})
|
||||
}
|
||||
logger.info(`[PicGo Server] get the request`)
|
||||
const handler = routers.getHandler(request.url!)
|
||||
handler!({
|
||||
...postObj,
|
||||
@@ -73,6 +76,7 @@ class Server {
|
||||
})
|
||||
}
|
||||
} else {
|
||||
logger.warn(`[PicGo Server] don't support [${request.method}] method`)
|
||||
response.statusCode = 404
|
||||
response.end()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ router.post('/upload', async ({
|
||||
try {
|
||||
if (list.length === 0) {
|
||||
// upload with clipboard
|
||||
logger.info('[PicGo Server] upload clipboard file')
|
||||
const res = await uploadWithClipboardFiles()
|
||||
if (res.success) {
|
||||
handleResponse({
|
||||
@@ -29,10 +30,15 @@ router.post('/upload', async ({
|
||||
})
|
||||
} else {
|
||||
handleResponse({
|
||||
response
|
||||
response,
|
||||
body: {
|
||||
success: false,
|
||||
message: 'upload error'
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
logger.info('[PicGo Server] upload files in list')
|
||||
// upload with files
|
||||
const pathList = list.map(item => {
|
||||
return {
|
||||
@@ -50,7 +56,11 @@ router.post('/upload', async ({
|
||||
})
|
||||
} else {
|
||||
handleResponse({
|
||||
response
|
||||
response,
|
||||
body: {
|
||||
success: false,
|
||||
message: 'upload error'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
export const handleResponse = ({
|
||||
response,
|
||||
statusCode = 200,
|
||||
@@ -13,6 +15,9 @@ export const handleResponse = ({
|
||||
header?: IObj,
|
||||
body?: any
|
||||
}) => {
|
||||
if (body?.success === false) {
|
||||
logger.warn('[PicGo Server] upload failed, see picgo.log for more detail ↑')
|
||||
}
|
||||
response.writeHead(statusCode, header)
|
||||
response.write(JSON.stringify(body))
|
||||
response.end()
|
||||
|
||||
@@ -2,11 +2,18 @@ import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import { remote, app } from 'electron'
|
||||
import pkg from 'root/package.json'
|
||||
|
||||
const APP = process.type === 'renderer' ? remote.app : app
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
|
||||
function injectPicGoVersion () {
|
||||
global.PICGO_GUI_VERSION = pkg.version
|
||||
global.PICGO_CORE_VERSION = pkg.dependencies.picgo.replace('^', '')
|
||||
}
|
||||
|
||||
function beforeOpen () {
|
||||
injectPicGoVersion()
|
||||
if (process.platform === 'darwin') {
|
||||
resolveMacWorkFlow()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,71 @@
|
||||
import db from '#/datastore'
|
||||
import { clipboard } from 'electron'
|
||||
import { clipboard, Notification, dialog } from 'electron'
|
||||
|
||||
export const handleCopyUrl = (str: string): void => {
|
||||
if (db.get('settings.autoCopy') !== false) {
|
||||
clipboard.writeText(str)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show notification
|
||||
* @param options
|
||||
*/
|
||||
export const showNotification = (options: IPrivateShowNotificationOption = {
|
||||
title: '',
|
||||
body: '',
|
||||
clickToCopy: false
|
||||
}) => {
|
||||
const notification = new Notification({
|
||||
title: options.title,
|
||||
body: options.body,
|
||||
icon: options.icon || undefined
|
||||
})
|
||||
const handleClick = () => {
|
||||
if (options.clickToCopy) {
|
||||
clipboard.writeText(options.body)
|
||||
}
|
||||
}
|
||||
notification.once('click', handleClick)
|
||||
notification.once('close', () => {
|
||||
notification.removeListener('click', handleClick)
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
export const showMessageBox = (options: any) => {
|
||||
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => {
|
||||
dialog.showMessageBox(
|
||||
options
|
||||
).then((res) => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const calcDurationRange = (duration: number) => {
|
||||
if (duration < 1000) {
|
||||
return 500
|
||||
} else if (duration < 1500) {
|
||||
return 1000
|
||||
} else if (duration < 3000) {
|
||||
return 2000
|
||||
} else if (duration < 5000) {
|
||||
return 3000
|
||||
} else if (duration < 7000) {
|
||||
return 5000
|
||||
} else if (duration < 10000) {
|
||||
return 8000
|
||||
} else if (duration < 12000) {
|
||||
return 10000
|
||||
} else if (duration < 20000) {
|
||||
return 15000
|
||||
} else if (duration < 30000) {
|
||||
return 20000
|
||||
}
|
||||
// max range
|
||||
return 100000
|
||||
}
|
||||
|
||||
@@ -1,35 +1,55 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
type ClipboardFileObject = {
|
||||
import Logger from 'picgo/dist/src/lib/Logger'
|
||||
interface IResultFileObject {
|
||||
path: string
|
||||
}
|
||||
type Result = ClipboardFileObject[]
|
||||
const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
|
||||
let files = argv.slice(1)
|
||||
if (files.length > 0 && files[0] === 'upload') {
|
||||
if (files.length === 1) {
|
||||
type Result = IResultFileObject[]
|
||||
|
||||
interface IHandleArgvResult {
|
||||
success: boolean
|
||||
fileList?: string[]
|
||||
}
|
||||
|
||||
const handleArgv = (argv: string[]): IHandleArgvResult => {
|
||||
const uploadIndex = argv.indexOf('upload')
|
||||
if (uploadIndex !== -1) {
|
||||
const fileList = argv.slice(1 + uploadIndex)
|
||||
return {
|
||||
success: true,
|
||||
fileList
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: false
|
||||
}
|
||||
}
|
||||
|
||||
const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger) => {
|
||||
const { success, fileList } = handleArgv(argv)
|
||||
if (!success) {
|
||||
return []
|
||||
} else {
|
||||
if (fileList?.length === 0) {
|
||||
return null // for uploading images in clipboard
|
||||
} else if (files.length > 1) {
|
||||
files = argv.slice(1)
|
||||
let result: Result = []
|
||||
if (files.length > 0) {
|
||||
result = files.map(item => {
|
||||
if (path.isAbsolute(item)) {
|
||||
} else if ((fileList?.length || 0) > 0) {
|
||||
const result = fileList!.map(item => {
|
||||
if (path.isAbsolute(item)) {
|
||||
return {
|
||||
path: item
|
||||
}
|
||||
} else {
|
||||
let tempPath = path.join(cwd, item)
|
||||
if (fs.existsSync(tempPath)) {
|
||||
return {
|
||||
path: item
|
||||
path: tempPath
|
||||
}
|
||||
} else {
|
||||
let tempPath = path.join(cwd, item)
|
||||
if (fs.existsSync(tempPath)) {
|
||||
return {
|
||||
path: tempPath
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
logger.warn(`cli -> can't get file: ${tempPath}, invalid path`)
|
||||
return null
|
||||
}
|
||||
}).filter(item => item !== null) as Result
|
||||
}
|
||||
}
|
||||
}).filter(item => item !== null) as Result
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import db from '#/datastore'
|
||||
import { ipcMain } from 'electron'
|
||||
import { showMessageBox } from '~/main/utils/common'
|
||||
import { SHOW_PRIVACY_MESSAGE } from '~/universal/events/constants'
|
||||
|
||||
class PrivacyManager {
|
||||
async init () {
|
||||
ipcMain.on(SHOW_PRIVACY_MESSAGE, () => {
|
||||
this.show(false)
|
||||
})
|
||||
if (db.get('settings.privacyEnsure') !== true) {
|
||||
const res = await this.show(true)
|
||||
// cancel
|
||||
if (res.result === 1) {
|
||||
return false
|
||||
} else {
|
||||
db.set('settings.privacyEnsure', true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async show (showCancel = true) {
|
||||
const res = await showMessageBox({
|
||||
type: 'info',
|
||||
buttons: showCancel ? ['Yes', 'No'] : ['Yes'],
|
||||
title: '隐私协议',
|
||||
message: `
|
||||
本软件尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更优质的服务,本软件会按照本隐私权政策的规定使用和收集您的一些行为信息。您在同意本软件服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本软件服务使用协议不可分割的一部分,如果不同意将无法使用。本协议会定期更新。
|
||||
|
||||
1.适用范围
|
||||
|
||||
a)在您使用本软件时,本软件会记录的您对本软件的一些操作行为信息,包括但不限于您使用本软件进行文件上传的耗时、类型、数量等信息。
|
||||
|
||||
2.信息的使用
|
||||
|
||||
a)在获得您的使用数据之后,本软件会将其上传至数据分析服务器,以便分析数据后,提供给您更好的服务。
|
||||
|
||||
3.信息披露
|
||||
|
||||
a)本软件不会将您的信息披露给不受信任的第三方。
|
||||
|
||||
b)根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;
|
||||
|
||||
c)如您出现违反中国有关法律、法规或者相关规则的情况,需要向第三方披露;
|
||||
|
||||
4.信息安全
|
||||
|
||||
a)本软件不会收集您的个人信息、密钥信息等隐私信息,所收集的信息仅仅作为改善软件、优化体验、了解软件日活等用途。
|
||||
`
|
||||
})
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
const privacyManager = new PrivacyManager()
|
||||
|
||||
export {
|
||||
privacyManager
|
||||
}
|
||||
@@ -28,7 +28,8 @@ export default {
|
||||
methods: {
|
||||
choosePicBed (val) {
|
||||
this.letPicGoSaveData({
|
||||
'picBed.current': this.type
|
||||
'picBed.current': this.type,
|
||||
'picBed.uploader': this.type
|
||||
})
|
||||
this.$emit('update:choosed', this.type)
|
||||
}
|
||||
|
||||
@@ -186,6 +186,9 @@ import {
|
||||
import db from '#/datastore'
|
||||
import mixin from '@/utils/mixin'
|
||||
import InputBoxDialog from '@/components/InputBoxDialog.vue'
|
||||
import {
|
||||
SHOW_PRIVACY_MESSAGE
|
||||
} from '~/universal/events/constants'
|
||||
const { Menu, dialog, BrowserWindow } = remote
|
||||
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
|
||||
if (!/\$url/.test(value)) {
|
||||
@@ -271,7 +274,11 @@ export default class extends Vue {
|
||||
}
|
||||
closeWindow () {
|
||||
const window = BrowserWindow.getFocusedWindow()
|
||||
window!.close()
|
||||
if (process.platform === 'linux') {
|
||||
window!.hide()
|
||||
} else {
|
||||
window!.close()
|
||||
}
|
||||
}
|
||||
buildMenu () {
|
||||
const _this = this
|
||||
@@ -297,6 +304,12 @@ export default class extends Vue {
|
||||
click () {
|
||||
_this.qrcodeVisible = true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '隐私协议',
|
||||
click () {
|
||||
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
|
||||
}
|
||||
}
|
||||
]
|
||||
this.menu = Menu.buildFromTemplate(template)
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
IpcRendererEvent,
|
||||
remote
|
||||
} from 'electron'
|
||||
import path from 'path'
|
||||
import { SHOW_PRIVACY_MESSAGE } from '~/universal/events/constants'
|
||||
@Component({
|
||||
name: 'mini-page',
|
||||
mixins: [mixin]
|
||||
@@ -145,7 +145,8 @@ export default class extends Vue {
|
||||
checked: this.$db.get('picBed.current') === item.type,
|
||||
click () {
|
||||
_this.letPicGoSaveData({
|
||||
'picBed.current': item.type
|
||||
'picBed.current': item.type,
|
||||
'picBed.uploader': item.type
|
||||
})
|
||||
ipcRenderer.send('syncPicBed')
|
||||
}
|
||||
@@ -175,6 +176,12 @@ export default class extends Vue {
|
||||
remote.BrowserWindow.getFocusedWindow()!.hide()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '隐私协议',
|
||||
click () {
|
||||
ipcRenderer.send(SHOW_PRIVACY_MESSAGE)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '重启应用',
|
||||
click () {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<el-button type="primary" round size="mini" @click="customLinkVisible = true">点击设置</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设置代理"
|
||||
label="设置代理和镜像地址"
|
||||
>
|
||||
<el-button type="primary" round size="mini" @click="proxyVisible = true">点击设置</el-button>
|
||||
</el-form-item>
|
||||
@@ -183,19 +183,20 @@
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
title="设置代理"
|
||||
title="设置代理和镜像地址"
|
||||
:visible.sync="proxyVisible"
|
||||
:modal-append-to-body="false"
|
||||
width="70%"
|
||||
>
|
||||
<el-form
|
||||
label-position="right"
|
||||
:model="customLink"
|
||||
ref="customLink"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-form-item
|
||||
label="代理地址"
|
||||
label="上传代理"
|
||||
>
|
||||
<el-input
|
||||
v-model="proxy"
|
||||
@@ -203,6 +204,24 @@
|
||||
placeholder="例如:http://127.0.0.1:1080"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="插件安装代理"
|
||||
>
|
||||
<el-input
|
||||
v-model="npmProxy"
|
||||
:autofocus="true"
|
||||
placeholder="例如:http://127.0.0.1:1080"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="插件镜像地址"
|
||||
>
|
||||
<el-input
|
||||
v-model="npmRegistry"
|
||||
:autofocus="true"
|
||||
placeholder="例如:https://registry.npm.taobao.org/"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="cancelProxy" round>取消</el-button>
|
||||
@@ -375,6 +394,8 @@ export default class extends Vue {
|
||||
upload: db.get('settings.shortKey.upload')
|
||||
}
|
||||
proxy = db.get('picBed.proxy') || ''
|
||||
npmRegistry = db.get('settings.registry') || ''
|
||||
npmProxy = db.get('settings.proxy') || ''
|
||||
rules = {
|
||||
value: [
|
||||
{ validator: customLinkRule, trigger: 'blur' }
|
||||
@@ -452,7 +473,9 @@ export default class extends Vue {
|
||||
confirmProxy () {
|
||||
this.proxyVisible = false
|
||||
this.letPicGoSaveData({
|
||||
'picBed.proxy': this.proxy
|
||||
'picBed.proxy': this.proxy,
|
||||
'settings.proxy': this.npmProxy,
|
||||
'settings.registry': this.npmRegistry
|
||||
})
|
||||
const successNotification = new Notification('设置代理', {
|
||||
body: '设置成功'
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<div id="plugin-view">
|
||||
<div class="view-title">
|
||||
插件设置 - <i class="el-icon-goods" @click="goAwesomeList"></i>
|
||||
插件设置 -
|
||||
<el-tooltip :content="pluginListToolTip" placement="right">
|
||||
<i class="el-icon-goods" @click="goAwesomeList"></i>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="importLocalPluginToolTip" placement="left">
|
||||
<i class="el-icon-download" @click="handleImportLocalPlugin"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-row class="handle-bar" :class="{ 'cut-width': pluginList.length > 6 }">
|
||||
<el-input
|
||||
@@ -13,7 +19,7 @@
|
||||
</el-input>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="plugin-list" v-loading="loading">
|
||||
<el-col :span="12" v-for="item in pluginList" :key="item.name">
|
||||
<el-col :span="12" v-for="item in pluginList" :key="item.fullName">
|
||||
<div class="plugin-item" :class="{ 'darwin': os === 'darwin' }">
|
||||
<div class="cli-only-badge" v-if="!item.gui" title="CLI only">CLI</div>
|
||||
<img class="plugin-item__logo" :src="item.logo"
|
||||
@@ -106,6 +112,7 @@ import {
|
||||
remote,
|
||||
IpcRendererEvent
|
||||
} from 'electron'
|
||||
import { handleStreamlinePluginName } from '~/universal/utils/common'
|
||||
const { Menu } = remote
|
||||
|
||||
@Component({
|
||||
@@ -125,6 +132,8 @@ export default class extends Vue {
|
||||
pluginNameList: string[] = []
|
||||
loading = true
|
||||
needReload = false
|
||||
pluginListToolTip = '插件列表'
|
||||
importLocalPluginToolTip = '导入本地插件'
|
||||
id = ''
|
||||
os = ''
|
||||
defaultLogo: string = 'this.src="https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@dev/public/roundLogo.png"'
|
||||
@@ -157,24 +166,30 @@ export default class extends Vue {
|
||||
}
|
||||
created () {
|
||||
this.os = process.platform
|
||||
ipcRenderer.on('pluginList', (evt: IpcRendererEvent, list: IPicGoPlugin[]) => {
|
||||
this.pluginList = list
|
||||
this.pluginNameList = list.map(item => item.name)
|
||||
ipcRenderer.on('hideLoading', () => {
|
||||
this.loading = false
|
||||
})
|
||||
ipcRenderer.on('installSuccess', (evt: IpcRendererEvent, plugin: string) => {
|
||||
ipcRenderer.on('pluginList', (evt: IpcRendererEvent, list: IPicGoPlugin[]) => {
|
||||
this.pluginList = list
|
||||
this.pluginNameList = list.map(item => item.fullName)
|
||||
this.loading = false
|
||||
})
|
||||
ipcRenderer.on('installPlugin', (evt: IpcRendererEvent, { success, body }: {
|
||||
success: boolean,
|
||||
body: string
|
||||
}) => {
|
||||
this.loading = false
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === plugin) {
|
||||
if (item.fullName === body) {
|
||||
item.ing = false
|
||||
item.hasInstall = true
|
||||
item.hasInstall = success
|
||||
}
|
||||
})
|
||||
})
|
||||
ipcRenderer.on('updateSuccess', (evt: IpcRendererEvent, plugin: string) => {
|
||||
this.loading = false
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === plugin) {
|
||||
if (item.fullName === plugin) {
|
||||
item.ing = false
|
||||
item.hasInstall = true
|
||||
}
|
||||
@@ -186,7 +201,7 @@ export default class extends Vue {
|
||||
ipcRenderer.on('uninstallSuccess', (evt: IpcRendererEvent, plugin: string) => {
|
||||
this.loading = false
|
||||
this.pluginList = this.pluginList.filter(item => {
|
||||
if (item.name === plugin) { // restore Uploader & Transformer after uninstalling
|
||||
if (item.fullName === plugin) { // restore Uploader & Transformer after uninstalling
|
||||
if (item.config.transformer.name) {
|
||||
this.handleRestoreState('transformer', item.config.transformer.name)
|
||||
}
|
||||
@@ -195,7 +210,7 @@ export default class extends Vue {
|
||||
}
|
||||
this.getPicBeds()
|
||||
}
|
||||
return item.name !== plugin
|
||||
return item.fullName !== plugin
|
||||
})
|
||||
this.pluginNameList = this.pluginNameList.filter(item => item !== plugin)
|
||||
})
|
||||
@@ -210,7 +225,7 @@ export default class extends Vue {
|
||||
enabled: !plugin.enabled,
|
||||
click () {
|
||||
_this.letPicGoSaveData({
|
||||
[`picgoPlugins.picgo-plugin-${plugin.name}`]: true
|
||||
[`picgoPlugins.${plugin.fullName}`]: true
|
||||
})
|
||||
plugin.enabled = true
|
||||
_this.getPicBeds()
|
||||
@@ -220,7 +235,7 @@ export default class extends Vue {
|
||||
enabled: plugin.enabled,
|
||||
click () {
|
||||
_this.letPicGoSaveData({
|
||||
[`picgoPlugins.picgo-plugin-${plugin.name}`]: false
|
||||
[`picgoPlugins.${plugin.fullName}`]: false
|
||||
})
|
||||
plugin.enabled = false
|
||||
_this.getPicBeds()
|
||||
@@ -230,25 +245,26 @@ export default class extends Vue {
|
||||
if (plugin.config.uploader.name) {
|
||||
_this.handleRestoreState('uploader', plugin.config.uploader.name)
|
||||
}
|
||||
_this.needReload = true
|
||||
}
|
||||
}, {
|
||||
label: '卸载插件',
|
||||
click () {
|
||||
_this.uninstallPlugin(plugin.name)
|
||||
_this.uninstallPlugin(plugin.fullName)
|
||||
}
|
||||
}, {
|
||||
label: '更新插件',
|
||||
click () {
|
||||
_this.updatePlugin(plugin.name)
|
||||
_this.updatePlugin(plugin.fullName)
|
||||
}
|
||||
}]
|
||||
for (let i in plugin.config) {
|
||||
if (plugin.config[i].config.length > 0) {
|
||||
const obj = {
|
||||
label: `配置${i} - ${plugin.config[i].name}`,
|
||||
label: `配置${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
|
||||
click () {
|
||||
_this.currentType = i
|
||||
_this.configName = plugin.config[i].name
|
||||
_this.configName = plugin.config[i].fullName || plugin.config[i].name
|
||||
_this.dialogVisible = true
|
||||
_this.config = plugin.config[i].config
|
||||
}
|
||||
@@ -280,7 +296,7 @@ export default class extends Vue {
|
||||
menu.push({
|
||||
label: i.label,
|
||||
click () {
|
||||
ipcRenderer.send('pluginActions', plugin.name, i.label)
|
||||
ipcRenderer.send('pluginActions', plugin.fullName, i.label)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -303,13 +319,13 @@ export default class extends Vue {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
item.ing = true
|
||||
ipcRenderer.send('installPlugin', item.name)
|
||||
ipcRenderer.send('installPlugin', item.fullName)
|
||||
}).catch(() => {
|
||||
console.log('Install canceled')
|
||||
})
|
||||
} else {
|
||||
item.ing = true
|
||||
ipcRenderer.send('installPlugin', item.name)
|
||||
ipcRenderer.send('installPlugin', item.fullName)
|
||||
}
|
||||
}
|
||||
uninstallPlugin (val: string) {
|
||||
@@ -318,14 +334,16 @@ export default class extends Vue {
|
||||
item.ing = true
|
||||
}
|
||||
})
|
||||
this.loading = true
|
||||
ipcRenderer.send('uninstallPlugin', val)
|
||||
}
|
||||
updatePlugin (val: string) {
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === val) {
|
||||
if (item.fullName === val) {
|
||||
item.ing = true
|
||||
}
|
||||
})
|
||||
this.loading = true
|
||||
ipcRenderer.send('updatePlugin', val)
|
||||
}
|
||||
reloadApp () {
|
||||
@@ -364,7 +382,7 @@ export default class extends Vue {
|
||||
switch (this.currentType) {
|
||||
case 'plugin':
|
||||
this.letPicGoSaveData({
|
||||
[`picgo-plugin-${this.configName}`]: result
|
||||
[`${this.configName}`]: result
|
||||
})
|
||||
break
|
||||
case 'uploader':
|
||||
@@ -407,7 +425,7 @@ export default class extends Vue {
|
||||
})
|
||||
}
|
||||
handleSearchResult (item: INPMSearchResultObject) {
|
||||
const name = item.package.name.replace(/picgo-plugin-/, '')
|
||||
const name = handleStreamlinePluginName(item.package.name)
|
||||
let gui = false
|
||||
if (item.package.keywords && item.package.keywords.length > 0) {
|
||||
if (item.package.keywords.includes('picgo-gui-plugin')) {
|
||||
@@ -416,12 +434,13 @@ export default class extends Vue {
|
||||
}
|
||||
return {
|
||||
name: name,
|
||||
fullName: item.package.name,
|
||||
author: item.package.author.name,
|
||||
description: item.package.description,
|
||||
logo: `https://cdn.jsdelivr.net/npm/${item.package.name}/logo.png`,
|
||||
config: {},
|
||||
homepage: item.package.links ? item.package.links.homepage : '',
|
||||
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')),
|
||||
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name),
|
||||
version: item.package.version,
|
||||
gui,
|
||||
ing: false // installing or uninstalling
|
||||
@@ -433,7 +452,8 @@ export default class extends Vue {
|
||||
const current = this.$db.get('picBed.current')
|
||||
if (current === name) {
|
||||
this.letPicGoSaveData({
|
||||
'picBed.current': 'smms'
|
||||
'picBed.current': 'smms',
|
||||
'picBed.uploader': 'smms'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -457,11 +477,16 @@ export default class extends Vue {
|
||||
letPicGoSaveData (data: IObj) {
|
||||
ipcRenderer.send('picgoSaveData', data)
|
||||
}
|
||||
handleImportLocalPlugin () {
|
||||
ipcRenderer.send('importLocalPlugin')
|
||||
this.loading = true
|
||||
}
|
||||
beforeDestroy () {
|
||||
ipcRenderer.removeAllListeners('pluginList')
|
||||
ipcRenderer.removeAllListeners('installSuccess')
|
||||
ipcRenderer.removeAllListeners('installPlugin')
|
||||
ipcRenderer.removeAllListeners('uninstallSuccess')
|
||||
ipcRenderer.removeAllListeners('updateSuccess')
|
||||
ipcRenderer.removeAllListeners('hideLoading')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -491,6 +516,7 @@ $darwinBg = #172426
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 10px auto
|
||||
position relative
|
||||
i.el-icon-goods
|
||||
font-size 20px
|
||||
vertical-align middle
|
||||
@@ -498,6 +524,16 @@ $darwinBg = #172426
|
||||
transition color .2s ease-in-out
|
||||
&:hover
|
||||
color #49B1F5
|
||||
i.el-icon-download
|
||||
position absolute
|
||||
right 0
|
||||
top 8px
|
||||
font-size 20px
|
||||
vertical-align middle
|
||||
cursor pointer
|
||||
transition color .2s ease-in-out
|
||||
&:hover
|
||||
color #49B1F5
|
||||
.handle-bar
|
||||
margin-bottom 20px
|
||||
&.cut-width
|
||||
|
||||
@@ -222,7 +222,8 @@ export default class extends Vue {
|
||||
checked: this.$db.get('picBed.current') === item.type,
|
||||
click () {
|
||||
_this.letPicGoSaveData({
|
||||
'picBed.current': item.type
|
||||
'picBed.current': item.type,
|
||||
'picBed.uploader': item.type
|
||||
})
|
||||
ipcRenderer.send('syncPicBed')
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
:rules="{
|
||||
required: true, message: '分支名不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.branch" @keyup.native.enter="confirm" placeholder="例如:master"></el-input>
|
||||
<el-input v-model="form.branch" @keyup.native.enter="confirm" placeholder="例如:main"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定Token"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="others-view">
|
||||
<el-row :gutter="16">
|
||||
<el-row :gutter="16" class="setting-list">
|
||||
<el-col :span="16" :offset="4">
|
||||
<div class="view-title">
|
||||
{{ picBedName }}设置
|
||||
@@ -69,7 +69,8 @@ export default class extends Vue {
|
||||
}
|
||||
setDefaultPicBed (type: string) {
|
||||
this.letPicGoSaveData({
|
||||
'picBed.current': type
|
||||
'picBed.current': type,
|
||||
'picBed.uploader': type
|
||||
})
|
||||
// @ts-ignore 来自mixin的数据
|
||||
this.defaultPicBed = type
|
||||
@@ -91,6 +92,10 @@ export default class extends Vue {
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#others-view
|
||||
.setting-list
|
||||
height 425px
|
||||
overflow-y auto
|
||||
overflow-x hidden
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
|
||||
@@ -2,10 +2,11 @@ import { Component, Vue } from 'vue-property-decorator'
|
||||
import { ipcRenderer } from 'electron'
|
||||
@Component
|
||||
export default class extends Vue {
|
||||
defaultPicBed = this.$db.get('picBed.current')
|
||||
defaultPicBed = this.$db.get('picBed.uploader') || this.$db.get('picBed.current') || 'smms'
|
||||
setDefaultPicBed (type: string) {
|
||||
this.letPicGoSaveData({
|
||||
'picBed.current': type
|
||||
'picBed.current': type,
|
||||
'picBed.uploader': type
|
||||
})
|
||||
this.defaultPicBed = type
|
||||
const successNotification = new Notification('设置默认图床', {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable camelcase */
|
||||
import {
|
||||
TALKING_DATA_APPID, TALKING_DATA_EVENT
|
||||
} from '~/universal/events/constants'
|
||||
import pkg from 'root/package.json'
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { handleTalkingDataEvent } from './common'
|
||||
const { version } = pkg
|
||||
|
||||
export const initTalkingData = () => {
|
||||
setTimeout(() => {
|
||||
const talkingDataScript = document.createElement('script')
|
||||
|
||||
talkingDataScript.src = `http://sdk.talkingdata.com/app/h5/v1?appid=${TALKING_DATA_APPID}&vn=${version}&vc=${version}`
|
||||
|
||||
const head = document.getElementsByTagName('head')[0]
|
||||
head.appendChild(talkingDataScript)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
ipcRenderer.on(TALKING_DATA_EVENT, (_, data: ITalkingDataOptions) => {
|
||||
handleTalkingDataEvent(data)
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
/* eslint-disable camelcase */
|
||||
export const handleTalkingDataEvent = (data: ITalkingDataOptions) => {
|
||||
const { EventId, Label = '', MapKv = {} } = data
|
||||
MapKv.from = window.location.href
|
||||
window.TDAPP.onEvent(EventId, Label, MapKv)
|
||||
if (isDevelopment) {
|
||||
console.log('talkingData', data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { app } from 'electron'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const errorMsg = {
|
||||
broken: 'PicGo 配置文件损坏,已经恢复为默认配置',
|
||||
brokenButBackup: 'PicGo 配置文件损坏,已经恢复为备份配置'
|
||||
}
|
||||
|
||||
function dbChecker () {
|
||||
if (process.type !== 'renderer') {
|
||||
if (!global.notificationList) global.notificationList = []
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const configFilePath = path.join(STORE_PATH, 'data.json')
|
||||
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')
|
||||
if (!fs.existsSync(configFilePath)) {
|
||||
return
|
||||
}
|
||||
let configFile: string = '{}'
|
||||
let optionsTpl = {
|
||||
title: '注意',
|
||||
body: ''
|
||||
}
|
||||
try {
|
||||
configFile = fs.readFileSync(configFilePath, { encoding: 'utf-8' })
|
||||
JSON.parse(configFile)
|
||||
} catch (e) {
|
||||
fs.unlinkSync(configFilePath)
|
||||
if (fs.existsSync(configFileBackupPath)) {
|
||||
try {
|
||||
configFile = fs.readFileSync(configFileBackupPath, { encoding: 'utf-8' })
|
||||
JSON.parse(configFile)
|
||||
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)
|
||||
return
|
||||
} catch (e) {
|
||||
optionsTpl.body = errorMsg.broken
|
||||
global.notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
}
|
||||
optionsTpl.body = errorMsg.broken
|
||||
global.notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
fs.writeFileSync(configFileBackupPath, configFile, { encoding: 'utf-8' })
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
dbChecker
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import FileSync from 'lowdb/adapters/FileSync'
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import { remote, app } from 'electron'
|
||||
import { dbChecker } from './dbChecker'
|
||||
|
||||
const APP = process.type === 'renderer' ? remote.app : app
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
@@ -13,6 +14,7 @@ if (process.type !== 'renderer') {
|
||||
if (!fs.pathExistsSync(STORE_PATH)) {
|
||||
fs.mkdirpSync(STORE_PATH)
|
||||
}
|
||||
dbChecker()
|
||||
}
|
||||
|
||||
class DB {
|
||||
@@ -29,7 +31,7 @@ class DB {
|
||||
|
||||
if (!this.db.has('picBed').value()) {
|
||||
this.db.set('picBed', {
|
||||
current: 'smms',
|
||||
current: 'smms', // deprecated
|
||||
uploader: 'smms',
|
||||
smms: {
|
||||
token: ''
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
export const SHOW_INPUT_BOX = 'SHOW_INPUT_BOX'
|
||||
export const SHOW_INPUT_BOX_RESPONSE = 'SHOW_INPUT_BOX_RESPONSE'
|
||||
export const TOGGLE_SHORTKEY_MODIFIED_MODE = 'TOGGLE_SHORTKEY_MODIFIED_MODE'
|
||||
export const TALKING_DATA_APPID = '7E6832BCE3F1438696579E541DFEBFDA'
|
||||
export const TALKING_DATA_EVENT = 'TALKING_DATA_EVENT'
|
||||
export const SHOW_PRIVACY_MESSAGE = 'SHOW_PRIVACY_MESSAGE'
|
||||
|
||||
Vendored
+11
@@ -20,3 +20,14 @@ declare interface IWindowManager {
|
||||
deleteById: (id: number) => void
|
||||
getAvailableWindow: () => BrowserWindow
|
||||
}
|
||||
|
||||
// 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[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -10,4 +10,10 @@ declare global {
|
||||
[elem: string]: any
|
||||
}
|
||||
}
|
||||
|
||||
interface Window {
|
||||
TDAPP: {
|
||||
onEvent: (EventId: string, Label?: string, MapKv?: IStringKeyMap) => void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+33
@@ -123,6 +123,7 @@ interface IBounds {
|
||||
type ICtx = import('picgo')
|
||||
interface IPicGoPlugin {
|
||||
name: string
|
||||
fullName: string
|
||||
author: string
|
||||
description: string
|
||||
logo: string
|
||||
@@ -145,6 +146,7 @@ interface IPicGoPlugin {
|
||||
|
||||
interface IPluginMenuConfig {
|
||||
name: string
|
||||
fullName?: string
|
||||
config: any[]
|
||||
}
|
||||
|
||||
@@ -194,6 +196,14 @@ type IUploadOption = string[]
|
||||
interface IShowNotificationOption {
|
||||
title: string
|
||||
body: string
|
||||
icon?: string | import('electron').NativeImage
|
||||
}
|
||||
|
||||
interface IPrivateShowNotificationOption extends IShowNotificationOption{
|
||||
/**
|
||||
* click notification to copy the body
|
||||
*/
|
||||
clickToCopy?: boolean
|
||||
}
|
||||
|
||||
interface IShowMessageBoxOption {
|
||||
@@ -279,3 +289,26 @@ interface IUpYunConfig {
|
||||
}
|
||||
|
||||
type ILoggerType = string | Error | boolean | number | undefined
|
||||
|
||||
interface IAppNotification {
|
||||
title: string
|
||||
body: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
interface ITalkingDataOptions {
|
||||
EventId: string
|
||||
Label?: string
|
||||
MapKv?: IStringKeyMap
|
||||
}
|
||||
|
||||
interface IAnalyticsData {
|
||||
fromClipboard: boolean
|
||||
type: string
|
||||
count: number
|
||||
duration?: number // 耗时
|
||||
}
|
||||
|
||||
interface IStringKeyMap {
|
||||
[propName: string]: any
|
||||
}
|
||||
|
||||
@@ -15,3 +15,18 @@ export const handleUrlEncode = (url: string): string => {
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
/**
|
||||
* streamline the full plugin name to a simple one
|
||||
* for example:
|
||||
* 1. picgo-plugin-xxx -> xxx
|
||||
* 2. @xxx/picgo-plugin-yyy -> yyy
|
||||
* @param name pluginFullName
|
||||
*/
|
||||
export const handleStreamlinePluginName = (name: string) => {
|
||||
if (/^@[^/]+\/picgo-plugin-/.test(name)) {
|
||||
return name.replace(/^@[^/]+\/picgo-plugin-/, '')
|
||||
} else {
|
||||
return name.replace(/picgo-plugin-/, '')
|
||||
}
|
||||
}
|
||||
|
||||
+22
-1
@@ -2,13 +2,22 @@ const path = require('path')
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, dir)
|
||||
}
|
||||
module.exports = {
|
||||
|
||||
const config = {
|
||||
configureWebpack: {
|
||||
devtool: 'nosources-source-map'
|
||||
},
|
||||
chainWebpack: config => {
|
||||
config.resolve.alias
|
||||
.set('@', resolve('src/renderer'))
|
||||
.set('~', resolve('src'))
|
||||
.set('root', resolve('./'))
|
||||
.set('#', resolve('src/universal'))
|
||||
// define
|
||||
// config.plugin('define')
|
||||
// .tap(args => {
|
||||
// return args
|
||||
// })
|
||||
},
|
||||
pluginOptions: {
|
||||
electronBuilder: {
|
||||
@@ -74,3 +83,15 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
config.configureWebpack = {
|
||||
devtool: 'eval-source-map'
|
||||
}
|
||||
// for dev main process hot reload
|
||||
config.pluginOptions.electronBuilder.mainProcessWatch = ['src/main/**/*']
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...config
|
||||
}
|
||||
|
||||
@@ -4189,6 +4189,16 @@ electron-chromedriver@^5.0.1:
|
||||
electron-download "^4.1.1"
|
||||
extract-zip "^1.6.7"
|
||||
|
||||
electron-devtools-installer@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-3.2.0.tgz#acc48d24eb7033fe5af284a19667e73b78d406d0"
|
||||
integrity sha512-t3UczsYugm4OAbqvdImMCImIMVdFzJAHgbwHpkl5jmfu1izVgUcP/mnrPqJIpEeCK1uZGpt+yHgWEN+9EwoYhQ==
|
||||
dependencies:
|
||||
rimraf "^3.0.2"
|
||||
semver "^7.2.1"
|
||||
tslib "^2.1.0"
|
||||
unzip-crx-3 "^0.2.0"
|
||||
|
||||
electron-download@^4.1.0, electron-download@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8"
|
||||
@@ -7109,6 +7119,13 @@ lru-cache@^5.1.1:
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
||||
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
@@ -8289,10 +8306,10 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
picgo@^1.4.11:
|
||||
version "1.4.11"
|
||||
resolved "https://registry.yarnpkg.com/picgo/-/picgo-1.4.11.tgz#759dde14232cd94b62aa2d3256669014f132babe"
|
||||
integrity sha512-OdhLpSycS/YjHgHSvWDh46eoW8hYPaMtwNQkW67QkNgji7VHU7rinKnaE5xmOhh6BsgFcgEt8bC+e2uKX3qXYA==
|
||||
picgo@^1.4.19:
|
||||
version "1.4.19"
|
||||
resolved "https://registry.yarnpkg.com/picgo/-/picgo-1.4.19.tgz#cbd4b39f1d1a2a5f231e62a7fdbf87f53298f2a8"
|
||||
integrity sha512-Y1BUrwq9rzEDQ06ZV5ZR8WRZqQe5hAHNVs4F/nEtwWBfr2YFwPxdce0b/rEPMZDSM7dfCWrts2M1qZC8VxB2+g==
|
||||
dependencies:
|
||||
chalk "^2.4.1"
|
||||
commander "^2.17.0"
|
||||
@@ -9454,6 +9471,13 @@ rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rimraf@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
|
||||
@@ -9607,6 +9631,13 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.2.1:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
send@0.17.1:
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
||||
@@ -10713,6 +10744,11 @@ tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
||||
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
||||
|
||||
tslib@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
|
||||
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
|
||||
|
||||
tslint@^5.20.1:
|
||||
version "5.20.1"
|
||||
resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d"
|
||||
@@ -10909,6 +10945,15 @@ unset-value@^1.0.0:
|
||||
has-value "^0.3.1"
|
||||
isobject "^3.0.0"
|
||||
|
||||
unzip-crx-3@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz#d5324147b104a8aed9ae8639c95521f6f7cda292"
|
||||
integrity sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==
|
||||
dependencies:
|
||||
jszip "^3.1.0"
|
||||
mkdirp "^0.5.1"
|
||||
yaku "^0.16.6"
|
||||
|
||||
unzip-crx@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/unzip-crx/-/unzip-crx-0.2.0.tgz#4c0baa8bdac756256754beca7843c13d7b858c18"
|
||||
|
||||
Reference in New Issue
Block a user