Compare commits

...
201 Commits
Author SHA1 Message Date
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
PiEgg 2d3e779e0b 🎉 Release: v2.3.0-beta.8 2021-08-13 20:38:50 +08:00
PiEgg 20d3cf987c 🐛 Fix: settings bug
ISSUES CLOSED: #710
2021-08-13 00:19:43 +08:00
PiEgg ae692632a5 🐛 Fix: upload clipboard images via http should return list
ISSUES CLOSED: #721
2021-08-12 22:17:14 +08:00
PiEgg 019efd354c 🎉 Release: v2.3.0-beta.7 2021-08-01 17:04:21 +08:00
PiEgg 7030f7a764 Feature: finish custom config path
ISSUES CLOSED: #255
2021-08-01 17:02:54 +08:00
PiEgg 96a63ea11a 🐛 Fix: bug of gallery db for plugin 2021-08-01 15:44:59 +08:00
PiEgg f1eb7f4d70 🐛 Fix: gallery db bug 2021-08-01 14:50:25 +08:00
PiEgg 6ddd660d89 Feature: add gallery db 2021-07-27 11:58:24 +08:00
PiEgg c70c3aff78 🚧 WIP: add gallery db 2021-07-27 00:15:11 +08:00
PiEgg 76964ff1a5 🚧 WIP: gallery db in progress 2021-07-25 23:25:36 +08:00
PiEgg 12cecc27e7 🐛 Fix: multiple uploading in the same time will cause rename failed 2021-07-14 23:57:09 +08:00
PiEgg bdf523a060 🐛 Fix: windows ia32 && x64 build options 2021-07-14 23:46:33 +08:00
PiEgg 49e5f343f4 🐛 Fix: enable plugin should reload
ISSUES CLOSED: #659
2021-07-10 19:38:05 +08:00
PiEgg 1657542144 Feature: add win32 support
ISSUES CLOSED: #632
2021-07-10 00:36:31 +08:00
PiEgg 8e5e9ec59a 🔨 Refactor: move guiApi to singleton 2021-07-10 00:25:34 +08:00
PiEgg 06b67e50b9 🐛 Fix: multiple uploading in the same time will cause output conflict
ISSUES CLOSED: #666
2021-05-09 10:52:14 +08:00
PiEgg cf895deb8d 🔨 Refactor: picgo server the way of uploading 2021-05-07 01:04:17 +08:00
PiEgg ab762ef465 🐛 Fix: uploader error in linux
ISSUES CLOSED: #627
2021-04-25 23:29:30 +08:00
PiEgg 92022a6e34 🐛 Fix: use uploader first 2021-04-24 18:16:13 +08:00
PiEgg a312d06b62 🎉 Release: v2.3.0-beta.6 2021-04-24 18:12:46 +08:00
PiEgg a1b70b4cb1 🐛 Fix: disable plugin need reload app 2021-04-24 18:11:50 +08:00
PiEgg 2d9e9c0a00 Feature: add logs for picgo-server
ISSUES CLOSED: #627
2021-04-24 17:56:56 +08:00
PiEgg a657c51bb5 🐛 Fix: dev error with install vue-devtools
ISSUES CLOSED: #653, #658
2021-04-24 12:14:15 +08:00
PiEgg 321e33967c 🐛 Fix: windows cli uploading bug
ISSUES CLOSED: #657
2021-04-24 00:44:49 +08:00
PiEgg 0a986c8933 🐛 Fix: default picBed using picBed.uploader instead of picBed.current 2021-04-21 23:20:21 +08:00
PiEgg 69e1dc8128 📦 Chore: fix eslint error notice in indent 2021-04-16 10:12:11 +08:00
PiEgg b05139fc9a 🐛 Fix: add plugin install failed notice 2021-04-11 11:11:22 +08:00
PiEgg 992ff35936 Feature: add privacy policy 2021-04-10 21:01:55 +08:00
PiEgg 06d40ef7f6 🐛 Fix: fix analytics value 2021-04-05 22:43:23 +08:00
PiEgg f53639153e Feature: add baidu tongji for analytics 2021-04-05 17:47:06 +08:00
PiEgg 3fd6e4e4c8 📦 Chore: add main process hot reload 2021-04-05 16:09:27 +08:00
PiEgg ce8f41516c 🎉 Release: v2.3.0-beta.5 2021-04-04 17:53:41 +08:00
PiEgg f0e1fa12d7 Feature: add local plugin support && npm registry/proxy support 2021-04-04 17:52:50 +08:00
PiEgg 3cad5f378d 🔨 Refactor: upload module && notification module 2021-03-28 17:44:20 +08:00
f9d6191d5e 🔨 Refactor: improve others picbed config ui to support scroll (#635)
Co-authored-by: jiawei <jiawei@parim.net>
2021-03-01 11:25:26 +08:00
PiEgg 51d80a6a1d 🐛 Fix: default github placeholder 2021-01-13 20:28:03 +08:00
刘沛源andGitHub 0fe3ade5c3 Feature: 为Linux系统适配桌面图标栏(Tray) (#603) 2020-12-29 09:59:50 +08:00
PiEgg 2129f86266 🔨 Refactor: plugin name -> fullName to support scope packages 2020-12-26 23:39:48 +08:00
PiEgg 064f37d1a6 📦 Chore: change travis-ci -> GitHub Actions 2020-12-20 11:44:38 +08:00
PiEgg 48592ec9b5 🎉 Release: v2.3.0-beta.4 2020-12-19 20:59:56 +08:00
PiEgg 33fdb16ded 🐛 Fix: disabled plugin won't be shown in plugin page 2020-12-19 20:52:01 +08:00
Huiyi.FYJandGitHub bd2311bf50 📝 Docs: update install command by Homebrew (#599) 2020-12-16 10:59:58 +08:00
PiEgg e793599360 📝 Docs: update discussions in doc 2020-12-09 11:50:00 +08:00
PiEgg ce2b5cdeab 🐛 Fix: url encode before uploading by url
ISSUES CLOSED: #581
2020-11-10 10:47:35 +08:00
偕臧andGitHub 2151857c89 📝 Docs: update README.md (#555)
aurman is easy to cause misunderstanding, but yay is a command that can be run directly (aur's most popular assistant)
2020-09-30 17:39:05 +08:00
PiEgg 32b8b976be Feature(config): auto configuration backup & fallback to avoid main process crash
ISSUES CLOSED: #568
2020-09-30 17:32:35 +08:00
PiEgg eab014dc88 Feature: add global value for PicGo get GUI_VERSION & CORE_VERSION 2020-09-28 19:41:02 +08:00
PiEgg bb90e174f3 📝 Docs: rm gitads 2020-09-02 14:27:20 +08:00
PiEgg 7b5e5ef2ed 🐛 Fix: shortKeyConfig maybe undefined
ISSUES CLOSED: #557
2020-08-22 22:15:46 +08:00
PiEgg 92ff282d2f 📝 Docs: add flutter-picgo 2020-08-02 11:48:22 +08:00
PiEgg 2d42381f47 📝 Docs: add gitads 2020-07-13 19:27:51 +08:00
PiEgg 6e855384e0 🎉 Release: v2.3.0-beta.3 2020-07-12 22:02:58 +08:00
PiEgg 21d3942d2b 🐛 Fix: choose default picBed failure
ISSUES CLOSED: #537
2020-07-12 22:01:37 +08:00
PiEgg 4f0809e6b3 🐛 Fix: shortkey disabled failure
ISSUES CLOSED: #534
2020-07-12 18:19:31 +08:00
PiEgg f40a1bb981 🎉 Release: v2.3.0-beta.2 2020-07-12 15:14:15 +08:00
PiEgg 5f87018af8 🐛 Fix: initialize db bugs 2020-07-12 15:13:21 +08:00
Molunerfinn 7fabc47bd2 Feature: add qrcode for picbeds' config 2020-06-30 16:17:18 +08:00
Molunerfinn db71139f5f 🐛 Fix: encoding the result of picgo-server 2020-06-30 09:53:35 +08:00
Molunerfinn 1c5880ab7c 📝 Docs: update readme 2020-06-28 15:57:36 +08:00
Molunerfinn b93dd05fd3 🎉 Release: v2.3.0-beta.1 2020-06-28 15:31:36 +08:00
Molunerfinn 59d3eba86d 🐛 Fix: paste url encoding bug
ISSUES CLOSED: #454
2020-06-28 15:30:58 +08:00
Molunerfinn a693544291 ⬆️ Upgrade: picgo version to latest
ISSUES CLOSED: #299,#525
2020-06-28 15:28:59 +08:00
Molunerfinn 2c57a27284 📝 Docs: update FAQ && ISSUE_TEMPLATE 2020-05-05 11:21:27 +08:00
Molunerfinn b6e3adb7af 🐛 Fix: auto-copy option && copy style
auto-copy option not work && clear rules for line breaks when copying
2020-05-04 16:50:29 +08:00
Molunerfinn 2adff1e788 📝 Docs: update readme 2020-05-01 08:34:41 +08:00
Molunerfinn 18ad54251e 🐛 Fix: beta version update bug 2020-04-30 18:54:48 +08:00
Molunerfinn 72387baf55 🎉 Release: v2.3.0-beta.0 2020-04-30 17:35:36 +08:00
Molunerfinn b357dfb243 🐛 Fix: travis-ci bug 2020-04-30 17:34:59 +08:00
Molunerfinn ad6acd8c4f Feature: add beta-version update support 2020-04-30 16:47:09 +08:00
Molunerfinn 2aeca509a8 Feature: finish all-select && shift multi-select
ISSUES CLOSED: #342
2020-04-28 18:35:59 +08:00
Molunerfinn b7cbd50b17 🔨 Refactor: lifeCycle codes 2020-04-25 11:34:04 +08:00
Molunerfinn b5ceaeda1e 🔨 Refactor: windowManager 2020-04-13 11:35:48 +08:00
Molunerfinn 7e866185e9 🐛 Fix: give a hint when node.js is not installed 2020-04-11 20:53:29 +08:00
Molunerfinn 96cdfea497 🐛 Fix: right-click menu upload fails with PicGo open
ISSUES CLOSED: #415
2020-04-11 11:05:58 +08:00
Molunerfinn 3df2253ae9 🔨 Refactor: improved code structure for greater clarity and understanding 2020-04-10 23:28:46 +08:00
Molunerfinn cc182b08f7 🐛 Fix: some win10 upload clipboard image crash 2020-04-08 22:50:24 +08:00
Molunerfinn 67e526f163 Feature: add autoCopy option for users to use or not 2020-03-19 21:03:21 +08:00
Molunerfinn cd70a1a5cc 🐛 Fix: confused port number auto increasing when opening a new PicGo app 2020-03-19 19:25:11 +08:00
Molunerfinn f3575575ac 📝 Docs: update choco install picgo 2020-03-10 19:29:13 +08:00
Molunerfinn 3f3ea69cd6 Feature: add smms-v2 support 2020-03-07 20:10:32 +08:00
Molunerfinn 32334e9bc1 🐛 Fix: correct inputbox value && remove listener 2020-02-25 20:58:39 +08:00
Molunerfinn 96544f5475 🐛 Fix: url uploader bug 2020-02-23 17:48:12 +08:00
Molunerfinn a28c67848d Feature: add uploading image from URL support 2020-02-23 15:20:07 +08:00
Molunerfinn 23ba2338c6 🚧 WIP: add support for URL uploading 2020-02-21 22:25:48 +08:00
Molunerfinn 024d9cf41e 📦 Chore: update funding url 2020-01-31 11:01:56 +08:00
Molunerfinn fd7673e4d1 📝 Docs: update docs 2020-01-31 11:00:42 +08:00
Molunerfinn fb014ddc9e 📝 Docs: update readme 2020-01-20 21:27:42 +08:00
Molunerfinn 962c9f2f37 🎉 Release: v2.2.2 2020-01-20 15:47:49 +08:00
Molunerfinn 4ebd76ff11 🐛 Fix: picgo-server upload clipboard file's result bug 2020-01-20 15:44:06 +08:00
Molunerfinn c3fe613696 🔨 Refactor: move more codes from background.ts -> apis/* 2020-01-10 22:34:08 +08:00
Molunerfinn ee46ab1fb7 🐛 Fix: releaseUrl may can't get latest version 2020-01-09 16:35:21 +08:00
Molunerfinn 3caeccc492 🎉 Release: v2.2.1 2020-01-09 11:07:43 +08:00
Molunerfinn c4f0a30c51 🐛 Fix: removeById handler error
ISSUES CLOSED: #382
2020-01-09 11:05:01 +08:00
Molunerfinn 762dc9b2cc 🔨 Refactor: move some utils to apis 2020-01-08 19:26:25 +08:00
Molunerfinn 73870a5326 🐛 Fix: server may never start 2020-01-08 10:57:19 +08:00
PiEgg 5f2b7c7802 🐛 Fix: miniWindow minimize bug 2020-01-07 23:16:35 +08:00
Molunerfinn 0d6fdc4727 🔨 Refactor(background): convergence of BrowserWindow creation to windowManager 2020-01-07 20:49:27 +08:00
Molunerfinn ded7998fbc 🔨 Refactor: fileName case 2020-01-06 20:21:49 +08:00
PiEgg 3913cef1e6 🎉 Release: v2.2.0 2020-01-01 20:03:02 +08:00
PiEgg b4f10c61f0 🐛 Fix: release script 2020-01-01 18:17:55 +08:00
PiEgg a04a25c7e0 🔨 Refactor: change shortKey page style 2020-01-01 18:01:38 +08:00
PiEgg e1a0cbb8a1 🐛 Fix: busApi event register first && emit later 2020-01-01 17:30:06 +08:00
PiEgg 6b57cf7981 Feature: add server config settings 2020-01-01 11:58:09 +08:00
Molunerfinn 9a8b95e356 🔨 Refactor: guiApi without creating with webContents 2020-01-01 00:47:47 +08:00
Molunerfinn 81e6acb29b 🐛 Fix: handle empty request-body 2020-01-01 00:06:48 +08:00
Molunerfinn c56d4efa79 Feature(server): add http server for uploading images by a http request
port 37766
2019-12-31 23:51:31 +08:00
PiEgg 3fd95725cf 🐛 Fix: settingPage && miniPage style in windows 2019-12-30 22:45:50 +08:00
Molunerfinn c6ead5b8cd 🐛 Fix: save debug mode && PICGO_ENV into config file 2019-12-30 11:06:00 +08:00
Molunerfinn f2750e1234 🐛 Fix: decrease title-bar z-index when config-form dialog shows 2019-12-28 16:50:29 +08:00
Molunerfinn 9d4d605acf Feature: only shows visible pic-beds
ISSUES CLOSED: #310
2019-12-28 16:23:16 +08:00
Molunerfinn cd30a6c70c 🐛 Fix: beforeOpen handler in windows 2019-12-28 15:19:27 +08:00
Molunerfinn a33f1adeb3 Feature: add aliyun oss options
ISSUES CLOSED: #347
2019-12-27 10:47:33 +08:00
Molunerfinn 1b3522e536 📝 Docs: update readme 2019-12-26 20:25:26 +08:00
Molunerfinn 3418560c63 🔨 Refactor: most config handle by picgo-core
for config syncing with picgo-core && picgo-plugins
2019-12-26 20:15:41 +08:00
Molunerfinn fec4360043 🚧 WIP: almost finish shortKey system 2019-12-22 17:22:32 +08:00
Molunerfinn b8ec879e23 🔨 Refactor: change vue-js -> vue-ts && add typos 2019-12-21 17:28:29 +08:00
Molunerfinn 29a55ed855 🐛 Fix: rename page not work 2019-12-21 00:50:13 +08:00
PiEgg 514fc40f71 🐛 Fix: plugin config-form && default plugin logo 2019-12-20 23:41:38 +08:00
Molunerfinn bc8e641fda 🐛 Fix: launch error in new structrue 2019-12-20 18:46:39 +08:00
Molunerfinn 4e3fa28be6 🐛 Fix: enum type error 2019-12-20 18:45:26 +08:00
Molunerfinn 87638a2882 ⬆️ Upgrade: ts & electron version 2019-12-20 10:56:57 +08:00
Molunerfinn 1d98601757 🔨 Refactor: remove .electron-vue folder 2019-12-19 19:19:04 +08:00
Molunerfinn 4e67a75be0 🔨 Refactor(ts): change js -> ts 2019-12-19 19:17:21 +08:00
PiEgg 72e6e2aed5 🚧 WIP: handle shortcut setting time 2019-09-25 16:34:29 +08:00
PiEgg b63ac03b84 🚧 WIP: finished shortKey page 2019-09-18 19:21:13 +08:00
PiEgg 554cc0dbf8 🔨 Refactor: db class 2019-09-11 19:30:08 +08:00
PiEgg 1274893154 🚧 WIP: shortcut setting page 2019-09-11 15:33:13 +08:00
Haoshen ZhongandPiEgg f491209bd6 📝 Docs: update README 2019-09-11 09:25:01 +08:00
PiEgg f21940ef23 🚧 WIP: shortcut system 2019-09-10 14:38:08 +08:00
Molunerfinn fe9e19a84d 📝 Docs: update site 2019-08-18 10:20:30 +08:00
Molunerfinn 91b397de9a 📝 Docs: update installation by scoop
ISSUES CLOSED: #295
2019-06-24 20:13:50 +08:00
Molunerfinn d06be462ff ⬆️ Upgrade(ws): for security issue 2019-06-05 10:47:37 +08:00
Molunerfinn 8fcbef5b0e ⬆️ Upgrade(axios): for security issue 2019-05-31 09:12:37 +08:00
Molunerfinn c5adf3b68c 🐛 Fix: add new tray icon for macOS dark-mode
ISSUES CLOSED: #267
2019-05-23 00:13:12 +08:00
Molunerfinn 5a064830c6 Feature: add alias for plugin config name 2019-05-12 19:48:11 +08:00
Molunerfinn e38e6f038c ⬆️ Upgrade(core): picgo-core -> 1.3.7
fix macOS clipboard upload error

ISSUES CLOSED: #264, #199
2019-05-12 13:26:34 +08:00
Molunerfinn c7ea9a8628 ⬆️ Upgrade: bump-version -> 1.0.3 2019-05-12 11:50:24 +08:00
Molunerfinn a5b6526fa4 🐛 Fix(website): website pictures error 2019-05-12 11:50:24 +08:00
Li YimingandMolunerfinn 46a49ed9d3 📝 Docs: add note for windows electron mirror 2019-05-12 11:49:58 +08:00
PiEgg 9344d7d762 ⬆️ Upgrade(electron): electron from 4 to 5 2019-05-05 22:22:03 +08:00
PiEgg e81b8f428c 📝 Docs: remove weibo picbed 2019-04-27 15:51:10 +08:00
PiEgg 6f1fca7f7f ⬆️ Upgrade: inquirer.js version for picgo bump-version 2019-04-20 11:59:47 +08:00
Molunerfinn 50f31bd438 🎉 Release: v2.1.2 2019-04-19 13:26:04 +08:00
Molunerfinn 5b0d596c6d 🔨 Refactor: improve picgo-setting page's thing
Add $fileName info
Add settings documentation url icon button
2019-04-19 11:00:51 +08:00
Molunerfinn c59e2bcc97 Feature: add file-name for customurl
add $fileName arg for customUrl

ISSUES CLOSED: #173
2019-04-18 17:25:10 +08:00
Molunerfinn 3c6b329927 🐛 Fix: log-level's reset value from 'all' -> ['all']
ISSUES CLOSED: #240, #237
2019-04-18 16:23:21 +08:00
Molunerfinn 466dbec4b7 🐛 Fix: mini window hidden bug in linux
ISSUES CLOSED: #239
2019-04-17 14:45:08 +08:00
Molunerfinn 4e02244ebe 🐛 Fix: log-level filter bug
if logLevel is not an array, it will throw an error to setting page

ISSUES CLOSED: #237
2019-04-17 09:28:36 +08:00
Molunerfinn 748a7fd07e 🎉 Release: v2.1.1 2019-04-16 10:45:21 +08:00
Molunerfinn 4000cea7c8 🐛 Fix: upload-area can't upload images 2019-04-16 10:45:09 +08:00
Molunerfinn 8978166328 🎉 Release: v2.1.0 2019-04-15 15:26:42 +08:00
Molunerfinn 7f1769733b Feature: add workflow for mac
for uploading images from context menu in macOS
2019-04-15 15:25:33 +08:00
Molunerfinn e5fbe750ae Feature: add windows context menu
for uploading pictures with picgo.exe
2019-04-15 14:10:01 +08:00
Molunerfinn ed539cf206 ⬆️ Upgrade: picgo-core -> 1.3.5 2019-04-15 10:28:40 +08:00
Molunerfinn 74c7016d2c Feature: support commandline -> upload images in clipboard 2019-04-15 10:28:11 +08:00
Molunerfinn 407b821aca Feature: add gui-api for remove event
ISSUES CLOSED: #201
2019-04-14 22:07:33 +08:00
PiEgg 6db86ec359 Feature: add commandline argvs support for picgo app
for example: picgo.exe ./xxx.jpg
2019-04-14 15:41:48 +08:00
PiEgg c64959a139 🐛 Fix: qiniu area option from select group -> input
ISSUES CLOSED: #230
2019-04-13 11:44:21 +08:00
Molunerfinn 6122e17d75 📝 Docs: add brew cask source
Thanks to @womeimingzi11!
2019-04-12 10:58:10 +08:00
Molunerfinn 37f1d34abc 📦 Chore: add picgo bump-version 2019-04-10 10:48:22 +08:00
Molunerfinn 3dd20857aa Fixed: #223 || change github token input type -> password 2019-04-04 17:09:42 +08:00
Molunerfinn 4a11d9830b Rm: showInputBox handler in plugin page 2019-04-04 16:58:36 +08:00
Molunerfinn 0165879f2d Changed: showInputBox from plugin page -> mainPage layout 2019-04-04 16:56:38 +08:00
Molunerfinn 17302fe3e4 Finished: log level setting 2019-04-04 15:08:47 +08:00
Molunerfinn f65d2d2df4 Fixed: npm ENOENT bug && mini window display -> false by default 2019-04-02 17:11:01 +08:00
PiEgg af19de7168 Changed: MIT LICENSE -> 996ICU LICENSE 2019-03-30 12:20:00 +08:00
Molunerfinn 9ab9e1973c Finished: guiApi.showMessageBox 2019-03-27 16:38:15 +08:00
Molunerfinn a4056cbe58 Fixed: #175 || improve gallery page scroll performance 2019-03-26 17:18:52 +08:00
zWingzandPiEgg 69d660fe7e Updated: trayPage add img-list object-fit 2019-03-16 14:47:48 +08:00
Molunerfinn 058e903789 Fixed: mime-types bug 2019-03-12 15:11:33 +08:00
Molunerfinn 53cdddf44a 2.0.5 2019-03-12 11:21:02 +08:00
Molunerfinn 2315aad732 Fixed: WIN10 powershell output encoding bug 2019-03-12 11:20:27 +08:00
Molunerfinn 7f8c6fd60e Fixed: #195 || windows7's bug of uploading image in clipboard 2019-03-11 13:40:39 +08:00
Molunerfinn 10959f3c54 2.0.4 2019-03-08 14:39:05 +08:00
Molunerfinn 1f4f40848e Updated: doc 2019-03-08 14:39:00 +08:00
Molunerfinn 7ef76b0a9b Fixed: #190 || copy image to upload correctly 2019-03-01 16:41:40 +08:00
Molunerfinn f87311760a Fixed: #189 || added change picbed button for upload page 2019-02-27 11:11:48 +08:00
Molunerfinn d597477a79 2.0.3 2019-01-31 16:41:06 +08:00
Molunerfinn 2bcbf749d1 Added: awesome list 2019-01-31 16:40:08 +08:00
PiEgg 59dd2c0b55 Fixed: weibo picbed docs link 2019-01-27 10:22:00 +08:00
Molunerfinn 43bace3212 Fixed: #176 || macOS tray window upload img failed 2019-01-25 19:16:52 +08:00
Molunerfinn 649e9f0695 Fixed: #175 || gallery page keep-alive 2019-01-24 11:29:48 +08:00
Molunerfinn e8f2185bbb Updated: copyright 2019-01-22 10:32:23 +08:00
Molunerfinn cc9dea5e71 2.0.2 2019-01-22 09:49:09 +08:00
Molunerfinn aabc0028af Fixed: custom url bugs & updated picgo version 2019-01-22 09:48:36 +08:00
Molunerfinn 193946c469 Updated: miniWindow style & add reload button for app 2019-01-21 16:46:33 +08:00
Molunerfinn 22c7b9a170 Fixed: #172 || miniWindowOntop option 2019-01-21 15:43:27 +08:00
PiEgg c5f6ae8993 Fixed: #171 || guiMenu is a function 2019-01-19 14:42:11 +08:00
Molunerfinn bb034677bd Fixed: logo url 2019-01-17 19:30:41 +08:00
Molunerfinn d2eb8999c5 Fixed: #168 || add new logo 2019-01-17 19:27:25 +08:00
Molunerfinn 4cb7acfec0 Updated: awesome-picgo 2019-01-16 10:27:34 +08:00
Molunerfinn 188b5e5148 2.0.1 2019-01-15 17:30:41 +08:00
Molunerfinn 0abdb3cf00 Fixed: #166 & add showNotification for guiApi 2019-01-15 17:28:50 +08:00
PiEgg 8704e212bf Updated: docs 2019-01-13 20:17:50 +08:00
PiEgg 4b92c90361 Updated: picgo-core version 2019-01-13 16:53:59 +08:00
161 changed files with 15999 additions and 9680 deletions
-39
View File
@@ -1,39 +0,0 @@
{
"comments": false,
"env": {
"test": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
],
"plugins": ["istanbul"]
},
"main": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
]
},
"renderer": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
},
"production": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}
-132
View File
@@ -1,132 +0,0 @@
'use strict'
process.env.NODE_ENV = 'production'
const { say } = require('cfonts')
const chalk = require('chalk')
const del = require('del')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Multispinner = require('multispinner')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
// const webConfig = require('./webpack.web.config')
const doneLog = chalk.bgGreen.white(' DONE ') + ' '
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
const isCI = process.env.CI || false
if (process.env.BUILD_TARGET === 'clean') clean()
else if (process.env.BUILD_TARGET === 'web') web()
else build()
function clean () {
del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
console.log(`\n${doneLog}\n`)
process.exit()
}
function build () {
greeting()
del.sync(['dist/electron/*', '!.gitkeep'])
const tasks = ['main', 'renderer']
const m = new Multispinner(tasks, {
preText: 'building',
postText: 'process'
})
let results = ''
m.on('success', () => {
process.stdout.write('\x1B[2J\x1B[0f')
console.log(`\n\n${results}`)
console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
process.exit()
})
pack(mainConfig).then(result => {
results += result + '\n\n'
m.success('main')
}).catch(err => {
m.error('main')
console.log(`\n ${errorLog}failed to build main process`)
console.error(`\n${err}\n`)
process.exit(1)
})
pack(rendererConfig).then(result => {
results += result + '\n\n'
m.success('renderer')
}).catch(err => {
m.error('renderer')
console.log(`\n ${errorLog}failed to build renderer process`)
console.error(`\n${err}\n`)
process.exit(1)
})
}
function pack (config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (err, stats) => {
if (err) reject(err.stack || err)
else if (stats.hasErrors()) {
let err = ''
stats.toString({
chunks: false,
colors: true
})
.split(/\r?\n/)
.forEach(line => {
err += ` ${line}\n`
})
reject(err)
} else {
resolve(stats.toString({
chunks: false,
colors: true
}))
}
})
})
}
// function web () {
// del.sync(['dist/web/*', '!.gitkeep'])
// webConfig.mode = 'production'
// webpack(webConfig, (err, stats) => {
// if (err || stats.hasErrors()) console.log(err)
// console.log(stats.toString({
// chunks: false,
// colors: true
// }))
// process.exit()
// })
// }
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 85) text = 'lets-build'
else if (cols > 60) text = 'lets-|build'
else text = false
if (text && !isCI) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n lets-build'))
console.log()
}
-40
View File
@@ -1,40 +0,0 @@
const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(event => {
/**
* Reload browser when HTMLWebpackPlugin emits a new index.html
*
* Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
* https://github.com/SimulatedGREG/electron-vue/issues/437
* https://github.com/jantimon/html-webpack-plugin/issues/680
*/
// if (event.action === 'reload') {
// window.location.reload()
// }
/**
* Notify `mainWindow` when `main` process is compiling,
* giving notice for an expected reload of the `electron` process
*/
if (event.action === 'compiling') {
document.body.innerHTML += `
<style>
#dev-client {
background: #4fc08d;
border-radius: 4px;
bottom: 20px;
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
color: #fff;
font-family: 'Source Sans Pro', sans-serif;
left: 20px;
padding: 8px 12px;
position: absolute;
}
</style>
<div id="dev-client">
Compiling Main Process...
</div>
`
}
})
-190
View File
@@ -1,190 +0,0 @@
'use strict'
const chalk = require('chalk')
const electron = require('electron')
const path = require('path')
const { say } = require('cfonts')
const { spawn } = require('child_process')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const webpackHotMiddleware = require('webpack-hot-middleware')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
let electronProcess = null
let manualRestart = false
let hotMiddleware
function logStats (proc, data) {
let log = ''
log += chalk.yellow.bold(`${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`)
log += '\n\n'
if (typeof data === 'object') {
data.toString({
colors: true,
chunks: false
}).split(/\r?\n/).forEach(line => {
log += ' ' + line + '\n'
})
} else {
log += ` ${data}\n`
}
log += '\n' + chalk.yellow.bold(`${new Array(28 + 1).join('-')}`) + '\n'
console.log(log)
}
function startRenderer () {
return new Promise((resolve, reject) => {
rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
rendererConfig.mode = 'development'
const compiler = webpack(rendererConfig)
hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500
})
compiler.hooks.compilation.tap('compilation', compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
compiler.hooks.done.tap('done', stats => {
logStats('Renderer', stats)
})
const server = new WebpackDevServer(
compiler,
{
contentBase: path.join(__dirname, '../'),
quiet: true,
before (app, ctx) {
app.use(hotMiddleware)
ctx.middleware.waitUntilValid(() => {
resolve()
})
}
}
)
server.listen(9080)
})
}
function startMain () {
return new Promise((resolve, reject) => {
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
mainConfig.mode = 'development'
const compiler = webpack(mainConfig)
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
logStats('Main', chalk.white.bold('compiling...'))
hotMiddleware.publish({ action: 'compiling' })
done()
})
compiler.watch({}, (err, stats) => {
if (err) {
console.log(err)
return
}
logStats('Main', stats)
if (electronProcess && electronProcess.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 5000)
}
resolve()
})
})
}
function startElectron () {
var args = [
'--inspect=5858',
path.join(__dirname, '../dist/electron/main.js')
]
// detect yarn or npm and process commandline args accordingly
if (process.env.npm_execpath.endsWith('yarn.js')) {
args = args.concat(process.argv.slice(3))
} else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
args = args.concat(process.argv.slice(2))
}
electronProcess = spawn(electron, args)
electronProcess.stdout.on('data', data => {
electronLog(data, 'blue')
})
electronProcess.stderr.on('data', data => {
electronLog(data, 'red')
})
electronProcess.on('close', () => {
if (!manualRestart) process.exit()
})
}
function electronLog (data, color) {
let log = ''
data = data.toString().split(/\r?\n/)
data.forEach(line => {
log += ` ${line}\n`
})
if (/[0-9A-z]+/.test(log)) {
console.log(
chalk[color].bold('┏ Electron -------------------') +
'\n\n' +
log +
chalk[color].bold('┗ ----------------------------') +
'\n'
)
}
}
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 104) text = 'electron-vue'
else if (cols > 76) text = 'electron-|vue'
else text = false
if (text) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n electron-vue'))
console.log(chalk.blue(' getting ready...') + '\n')
}
function init () {
greeting()
Promise.all([startRenderer(), startMain()])
.then(() => {
startElectron()
})
.catch(err => {
console.error(err)
})
}
init()
-120
View File
@@ -1,120 +0,0 @@
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const webpack = require('webpack')
module.exports = {
mode: 'development',
context: path.resolve(__dirname, '../docs'),
entry: './main.js',
output: {
path: path.resolve(__dirname, '../docs/dist'),
filename: "index.js"
},
resolve: {
alias: {
'icons': path.resolve(__dirname, '../build/icons')
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
enforce: 'pre',
exclude: /node_modules/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.styl(us)?$/,
use: [
'vue-style-loader',
'css-loader',
'stylus-loader'
]
},
{
test: /\.pug$/, loader: "pug-plain-loader"
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]'
}
}
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../docs/template.html')
}),
new MiniCssExtractPlugin({filename: 'styles.css'}),
]
}
if (process.env.NODE_ENV === 'production') {
// module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.mode = 'production'
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
-83
View File
@@ -1,83 +0,0 @@
'use strict'
process.env.BABEL_ENV = 'main'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const BabiliWebpackPlugin = require('babili-webpack-plugin')
let mainConfig = {
entry: {
main: path.join(__dirname, '../src/main/index.js')
},
externals: [
...Object.keys(dependencies || {})
],
module: {
rules: [
{
test: /\.(js)$/,
enforce: 'pre',
exclude: /node_modules/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
}
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
],
resolve: {
extensions: ['.js', '.json', '.node']
},
target: 'electron-main'
}
/**
* Adjust mainConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
mainConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust mainConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
mainConfig.plugins.push(
new BabiliWebpackPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
})
)
}
module.exports = mainConfig
-194
View File
@@ -1,194 +0,0 @@
'use strict'
process.env.BABEL_ENV = 'renderer'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
/**
* List of node_modules to include in webpack bundle
*
* Required for specific packages like Vue UI libraries
* that provide pure *.vue files that need compiling
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
*/
let whiteListedModules = ['vue']
let rendererConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
renderer: path.join(__dirname, '../src/renderer/main.js')
},
externals: [
// ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
],
module: {
rules: [
{
test: /\.(js|vue)$/,
enforce: 'pre',
exclude: /node_modules/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.styl(us)?$/,
use: [
'vue-style-loader',
'css-loader',
'stylus-loader'
]
},
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: process.env.NODE_ENV === 'production',
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader',
less: 'vue-style-loader!css-loader!less-loader',
stylus: 'vue-style-loader!css-loader!stylus-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]'
}
}
}
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: 'styles.css'}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: process.env.NODE_ENV !== 'production'
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js',
'utils': path.join(__dirname, '../src/renderer/utils'),
'~': path.join(__dirname, '../src'),
'root': path.join(__dirname, '../')
},
extensions: ['.js', '.vue', '.json', '.css', '.node']
},
target: 'electron-renderer'
}
/**
* Adjust rendererConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
rendererConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust rendererConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
new BabiliWebpackPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/electron/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = rendererConfig
-139
View File
@@ -1,139 +0,0 @@
'use strict'
process.env.BABEL_ENV = 'web'
const path = require('path')
const webpack = require('webpack')
const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
let webConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
web: path.join(__dirname, '../src/renderer/main.js')
},
module: {
rules: [
{
test: /\.(js|vue)$/,
enforce: 'pre',
exclude: /node_modules/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
include: [ path.resolve(__dirname, '../src/renderer') ],
exclude: /node_modules/
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: true,
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name].[ext]'
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
}
]
},
plugins: [
new ExtractTextPlugin('styles.css'),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: false
}),
new webpack.DefinePlugin({
'process.env.IS_WEB': 'true'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
path: path.join(__dirname, '../dist/web')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json', '.css']
},
target: 'web'
}
/**
* Adjust webConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
webConfig.devtool = ''
webConfig.plugins.push(
new BabiliWebpackPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = webConfig
+16 -17
View File
@@ -1,26 +1,25 @@
module.exports = { module.exports = {
root: true, root: true,
parser: 'babel-eslint', globals: {
parserOptions: { __static: 'readonly'
sourceType: 'module'
}, },
env: { env: {
browser: true,
node: true node: true
}, },
extends: 'standard', parser: "vue-eslint-parser",
globals: { 'extends': [
__static: true 'plugin:vue/essential',
}, '@vue/standard',
plugins: [ '@vue/typescript'
'html'
], ],
'rules': { 'plugins': ['@typescript-eslint'],
// allow paren-less arrow functions rules: {
'arrow-parens': 0, 'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
// allow async-await 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'generator-star-spacing': 0, "indent": "off",
// allow debugger during development "@typescript-eslint/indent": ["error", 2]
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 },
parserOptions: {
parser: '@typescript-eslint/parser'
} }
} }
+1
View File
@@ -0,0 +1 @@
custom: ["https://paypal.me/Molunerfinn"]
+11 -7
View File
@@ -1,30 +1,34 @@
<!-- <!--
PicGo Issue 模板 PicGo Issue 模板
请依照该模板来提交,否则将会被关闭。 请依照该模板来提交,否则将会被关闭。
**提问之前请注意你看过FAQ、配置手册以及那些被关闭的issues。否则同样的提问也会被关闭!** **提问之前请注意你看过 FAQ、配置手册以及那些被关闭的 issues。否则同样的提问也会被关闭!**
--> -->
**声明:我已经仔细看过 [文档](https://picgo.github.io/PicGo-Doc/)、[FAQ](https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md)、和搜索过已经关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed) 后依然没有找到答案,所以才发了一个新的 issue。**
## 问题类型 ## 问题类型
<!-- 你要提交的是Bug Report还是Feature Request--> <!-- 你要提交的是 Bug Report 还是 Feature Request-->
## PicGo的相关信息 ## PicGo 的相关信息
<!-- 版本、所在平台(Mac或者Windows --> <!-- PicGo 的版本、所在平台(Mac 或者 Windows 或者 Linux -->
## 问题重现 ## 问题重现
<!-- 如果是Bug Report请填写本项 --> <!-- 如果是 Bug Report 请填写本项 -->
<!-- 请附上相关截图 --> <!-- 请附上相关截图 -->
<!-- 请附上 PicGo 的相关报错日志(用文本的形式)否则无法判断原因-->
<!-- 报错日志可以在 PicGo 设置 -> 设置日志文件 -> 点击打开 后找到 -->
## 功能请求 ## 功能请求
<!-- 如果是Feature Request请填写本项 --> <!-- 如果是 Feature Request 请填写本项 -->
<!-- 详细描述你所预想的功能或者是现有功能的改进。 --> <!-- 详细描述你所预想的功能或者是现有功能的改进。 -->
--- ---
<!-- <!--
最后,喜欢PicGo的话不妨给它点个star~ 最后,喜欢 PicGo 的话不妨给它点个 star~
如果可以的话,请我喝杯咖啡?首页有赞助二维码,谢谢你的支持! 如果可以的话,请我喝杯咖啡?首页有赞助二维码,谢谢你的支持!
--> -->
+55
View File
@@ -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 }}
+5
View File
@@ -12,3 +12,8 @@ thumbs.db
!.gitkeep !.gitkeep
yarn-error.log yarn-error.log
docs/dist/ docs/dist/
# local env files
.env.local
.env.*.local
dist_electron/
test.js
+12 -12
View File
@@ -22,9 +22,7 @@ addons:
- icnsutils - icnsutils
#- xvfb #- xvfb
before_install: before_install:
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install git-lfs; fi
"$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
| tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
install: install:
#- export DISPLAY=':99.0' #- export DISPLAY=':99.0'
@@ -38,15 +36,17 @@ script:
#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js #- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e #- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
- npm run release - npm run release
- yarn run build:docs # - yarn run build:docs
before_script:
- git lfs pull
branches: branches:
only: only:
- master - master
after_script: # after_script:
- cd docs/dist # - cd docs/dist
- git init # - git init
- git config user.name "Molunerfinn" # - git config user.name "Molunerfinn"
- git config user.email "marksz@teamsz.xyz" # - git config user.email "marksz@teamsz.xyz"
- git add . # - git add .
- git commit -m "Travis build docs" # - git commit -m "Travis build docs"
- git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages # - git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages
+22 -1
View File
@@ -1,4 +1,25 @@
{ {
"eslint.enable": true, "eslint.enable": true,
"eslint.autoFixOnSave": true "eslint.alwaysShowStatus": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"vue",
"typescriptreact"
],
"[stylus]": {
"editor.formatOnSave": true
},
"stylusSupremacy.insertSemicolons": false,
"stylusSupremacy.insertBraces": false,
"stylusSupremacy.insertNewLineBetweenSelectors": true,
"stylusSupremacy.insertParenthesisAroundIfCondition": false,
"stylusSupremacy.alwaysUseNoneOverZero": true,
"stylusSupremacy.alwaysUseZeroWithoutUnit": true,
"stylusSupremacy.sortProperties": "grouped",
"stylusSupremacy.quoteChar": "\"",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
} }
+357
View File
@@ -0,0 +1,357 @@
# :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)
### :bug: Bug Fixes
* settings bug ([20d3cf9](https://github.com/Molunerfinn/PicGo/commit/20d3cf9)), closes [#710](https://github.com/Molunerfinn/PicGo/issues/710)
* upload clipboard images via http should return list ([ae69263](https://github.com/Molunerfinn/PicGo/commit/ae69263)), closes [#721](https://github.com/Molunerfinn/PicGo/issues/721)
# :tada: 2.3.0-beta.7 (2021-08-01)
### :sparkles: Features
* add gallery db ([6ddd660](https://github.com/Molunerfinn/PicGo/commit/6ddd660))
* add win32 support ([1657542](https://github.com/Molunerfinn/PicGo/commit/1657542)), closes [#632](https://github.com/Molunerfinn/PicGo/issues/632)
* finish custom config path ([7030f7a](https://github.com/Molunerfinn/PicGo/commit/7030f7a)), closes [#255](https://github.com/Molunerfinn/PicGo/issues/255)
### :bug: Bug Fixes
* bug of gallery db for plugin ([96a63ea](https://github.com/Molunerfinn/PicGo/commit/96a63ea))
* enable plugin should reload ([49e5f34](https://github.com/Molunerfinn/PicGo/commit/49e5f34)), closes [#659](https://github.com/Molunerfinn/PicGo/issues/659)
* gallery db bug ([f1eb7f4](https://github.com/Molunerfinn/PicGo/commit/f1eb7f4))
* multiple uploading in the same time will cause output conflict ([06b67e5](https://github.com/Molunerfinn/PicGo/commit/06b67e5)), closes [#666](https://github.com/Molunerfinn/PicGo/issues/666)
* multiple uploading in the same time will cause rename failed ([12cecc2](https://github.com/Molunerfinn/PicGo/commit/12cecc2))
* uploader error in linux ([ab762ef](https://github.com/Molunerfinn/PicGo/commit/ab762ef)), closes [#627](https://github.com/Molunerfinn/PicGo/issues/627)
* use uploader first ([92022a6](https://github.com/Molunerfinn/PicGo/commit/92022a6))
* windows ia32 && x64 build options ([bdf523a](https://github.com/Molunerfinn/PicGo/commit/bdf523a))
# :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)
### :sparkles: Features
* add qrcode for picbeds' config ([7fabc47](https://github.com/Molunerfinn/PicGo/commit/7fabc47))
### :bug: Bug Fixes
* encoding the result of picgo-server ([db71139](https://github.com/Molunerfinn/PicGo/commit/db71139))
* initialize db bugs ([5f87018](https://github.com/Molunerfinn/PicGo/commit/5f87018))
### :pencil: Documentation
* update readme ([1c5880a](https://github.com/Molunerfinn/PicGo/commit/1c5880a))
# :tada: 2.3.0-beta.1 (2020-06-28)
### :bug: Bug Fixes
* auto-copy option && copy style ([b6e3adb](https://github.com/Molunerfinn/PicGo/commit/b6e3adb))
* beta version update bug ([18ad542](https://github.com/Molunerfinn/PicGo/commit/18ad542))
* paste url encoding bug ([59d3eba](https://github.com/Molunerfinn/PicGo/commit/59d3eba)), closes [#454](https://github.com/Molunerfinn/PicGo/issues/454)
### :pencil: Documentation
* update FAQ && ISSUE_TEMPLATE ([2c57a27](https://github.com/Molunerfinn/PicGo/commit/2c57a27))
* update readme ([2adff1e](https://github.com/Molunerfinn/PicGo/commit/2adff1e))
# :tada: 2.3.0-beta.0 (2020-04-30)
### :sparkles: Features
* add autoCopy option for users to use or not ([67e526f](https://github.com/Molunerfinn/PicGo/commit/67e526f))
* add beta-version update support ([ad6acd8](https://github.com/Molunerfinn/PicGo/commit/ad6acd8))
* add smms-v2 support ([3f3ea69](https://github.com/Molunerfinn/PicGo/commit/3f3ea69))
* add uploading image from URL support ([a28c678](https://github.com/Molunerfinn/PicGo/commit/a28c678))
* finish all-select && shift multi-select ([2aeca50](https://github.com/Molunerfinn/PicGo/commit/2aeca50)), closes [#342](https://github.com/Molunerfinn/PicGo/issues/342)
### :bug: Bug Fixes
* confused port number auto increasing when opening a new PicGo app ([cd70a1a](https://github.com/Molunerfinn/PicGo/commit/cd70a1a))
* correct inputbox value && remove listener ([32334e9](https://github.com/Molunerfinn/PicGo/commit/32334e9))
* give a hint when node.js is not installed ([7e86618](https://github.com/Molunerfinn/PicGo/commit/7e86618))
* right-click menu upload fails with PicGo open ([96cdfea](https://github.com/Molunerfinn/PicGo/commit/96cdfea)), closes [#415](https://github.com/Molunerfinn/PicGo/issues/415)
* some win10 upload clipboard image crash ([cc182b0](https://github.com/Molunerfinn/PicGo/commit/cc182b0))
* travis-ci bug ([b357dfb](https://github.com/Molunerfinn/PicGo/commit/b357dfb))
* url uploader bug ([96544f5](https://github.com/Molunerfinn/PicGo/commit/96544f5))
### :pencil: Documentation
* update choco install picgo ([f357557](https://github.com/Molunerfinn/PicGo/commit/f357557))
* update docs ([fd7673e](https://github.com/Molunerfinn/PicGo/commit/fd7673e))
* update readme ([fb014dd](https://github.com/Molunerfinn/PicGo/commit/fb014dd))
### :package: Chore
* update funding url ([024d9cf](https://github.com/Molunerfinn/PicGo/commit/024d9cf))
## :tada: 2.2.2 (2020-01-20)
### :bug: Bug Fixes
* picgo-server upload clipboard file's result bug ([4ebd76f](https://github.com/Molunerfinn/PicGo/commit/4ebd76f))
* releaseUrl may can't get latest version ([ee46ab1](https://github.com/Molunerfinn/PicGo/commit/ee46ab1))
## :tada: 2.2.1 (2020-01-09)
### :sparkles: Features
* add alias for plugin config name ([5a06483](https://github.com/Molunerfinn/PicGo/commit/5a06483))
* add aliyun oss options ([a33f1ad](https://github.com/Molunerfinn/PicGo/commit/a33f1ad)), closes [#347](https://github.com/Molunerfinn/PicGo/issues/347)
* **server:** add http server for uploading images by a http request ([c56d4ef](https://github.com/Molunerfinn/PicGo/commit/c56d4ef))
* add server config settings ([6b57cf7](https://github.com/Molunerfinn/PicGo/commit/6b57cf7))
* only shows visible pic-beds ([9d4d605](https://github.com/Molunerfinn/PicGo/commit/9d4d605)), closes [#310](https://github.com/Molunerfinn/PicGo/issues/310)
### :bug: Bug Fixes
* decrease title-bar z-index when config-form dialog shows ([f2750e1](https://github.com/Molunerfinn/PicGo/commit/f2750e1))
* **website:** website pictures error ([a5b6526](https://github.com/Molunerfinn/PicGo/commit/a5b6526))
* add new tray icon for macOS dark-mode ([c5adf3b](https://github.com/Molunerfinn/PicGo/commit/c5adf3b)), closes [#267](https://github.com/Molunerfinn/PicGo/issues/267)
* beforeOpen handler in windows ([cd30a6c](https://github.com/Molunerfinn/PicGo/commit/cd30a6c))
* busApi event register first && emit later ([e1a0cbb](https://github.com/Molunerfinn/PicGo/commit/e1a0cbb))
* enum type error ([4e3fa28](https://github.com/Molunerfinn/PicGo/commit/4e3fa28))
* handle empty request-body ([81e6acb](https://github.com/Molunerfinn/PicGo/commit/81e6acb))
* launch error in new structrue ([bc8e641](https://github.com/Molunerfinn/PicGo/commit/bc8e641))
* miniWindow minimize bug ([5f2b7c7](https://github.com/Molunerfinn/PicGo/commit/5f2b7c7))
* plugin config-form && default plugin logo ([514fc40](https://github.com/Molunerfinn/PicGo/commit/514fc40))
* release script ([b4f10c6](https://github.com/Molunerfinn/PicGo/commit/b4f10c6))
* removeById handler error ([c4f0a30](https://github.com/Molunerfinn/PicGo/commit/c4f0a30)), closes [#382](https://github.com/Molunerfinn/PicGo/issues/382)
* rename page not work ([29a55ed](https://github.com/Molunerfinn/PicGo/commit/29a55ed))
* save debug mode && PICGO_ENV into config file ([c6ead5b](https://github.com/Molunerfinn/PicGo/commit/c6ead5b))
* server may never start ([73870a5](https://github.com/Molunerfinn/PicGo/commit/73870a5))
* settingPage && miniPage style in windows ([3fd9572](https://github.com/Molunerfinn/PicGo/commit/3fd9572))
### :pencil: Documentation
* add note for windows electron mirror ([46a49ed](https://github.com/Molunerfinn/PicGo/commit/46a49ed))
* remove weibo picbed ([e81b8f4](https://github.com/Molunerfinn/PicGo/commit/e81b8f4))
* update installation by scoop ([91b397d](https://github.com/Molunerfinn/PicGo/commit/91b397d)), closes [#295](https://github.com/Molunerfinn/PicGo/issues/295)
* update readme ([1b3522e](https://github.com/Molunerfinn/PicGo/commit/1b3522e))
* update README ([f491209](https://github.com/Molunerfinn/PicGo/commit/f491209))
* update site ([fe9e19a](https://github.com/Molunerfinn/PicGo/commit/fe9e19a))
# :tada: 2.2.0 (2020-01-01)
### :sparkles: Features
* add alias for plugin config name ([5a06483](https://github.com/Molunerfinn/PicGo/commit/5a06483))
* add aliyun oss options ([a33f1ad](https://github.com/Molunerfinn/PicGo/commit/a33f1ad)), closes [#347](https://github.com/Molunerfinn/PicGo/issues/347)
* **server:** add http server for uploading images by a http request ([c56d4ef](https://github.com/Molunerfinn/PicGo/commit/c56d4ef))
* add server config settings ([6b57cf7](https://github.com/Molunerfinn/PicGo/commit/6b57cf7))
* only shows visible pic-beds ([9d4d605](https://github.com/Molunerfinn/PicGo/commit/9d4d605)), closes [#310](https://github.com/Molunerfinn/PicGo/issues/310)
### :bug: Bug Fixes
* beforeOpen handler in windows ([cd30a6c](https://github.com/Molunerfinn/PicGo/commit/cd30a6c))
* **website:** website pictures error ([a5b6526](https://github.com/Molunerfinn/PicGo/commit/a5b6526))
* add new tray icon for macOS dark-mode ([c5adf3b](https://github.com/Molunerfinn/PicGo/commit/c5adf3b)), closes [#267](https://github.com/Molunerfinn/PicGo/issues/267)
* busApi event register first && emit later ([e1a0cbb](https://github.com/Molunerfinn/PicGo/commit/e1a0cbb))
* decrease title-bar z-index when config-form dialog shows ([f2750e1](https://github.com/Molunerfinn/PicGo/commit/f2750e1))
* enum type error ([4e3fa28](https://github.com/Molunerfinn/PicGo/commit/4e3fa28))
* handle empty request-body ([81e6acb](https://github.com/Molunerfinn/PicGo/commit/81e6acb))
* launch error in new structrue ([bc8e641](https://github.com/Molunerfinn/PicGo/commit/bc8e641))
* plugin config-form && default plugin logo ([514fc40](https://github.com/Molunerfinn/PicGo/commit/514fc40))
* release script ([b4f10c6](https://github.com/Molunerfinn/PicGo/commit/b4f10c6))
* rename page not work ([29a55ed](https://github.com/Molunerfinn/PicGo/commit/29a55ed))
* save debug mode && PICGO_ENV into config file ([c6ead5b](https://github.com/Molunerfinn/PicGo/commit/c6ead5b))
* settingPage && miniPage style in windows ([3fd9572](https://github.com/Molunerfinn/PicGo/commit/3fd9572))
### :pencil: Documentation
* add note for windows electron mirror ([46a49ed](https://github.com/Molunerfinn/PicGo/commit/46a49ed))
* remove weibo picbed ([e81b8f4](https://github.com/Molunerfinn/PicGo/commit/e81b8f4))
* update installation by scoop ([91b397d](https://github.com/Molunerfinn/PicGo/commit/91b397d)), closes [#295](https://github.com/Molunerfinn/PicGo/issues/295)
* update readme ([1b3522e](https://github.com/Molunerfinn/PicGo/commit/1b3522e))
* update README ([f491209](https://github.com/Molunerfinn/PicGo/commit/f491209))
* update site ([fe9e19a](https://github.com/Molunerfinn/PicGo/commit/fe9e19a))
## :tada: 2.1.2 (2019-04-19)
### :sparkles: Features
* add file-name for customurl ([c59e2bc](https://github.com/Molunerfinn/PicGo/commit/c59e2bc)), closes [#173](https://github.com/Molunerfinn/PicGo/issues/173)
### :bug: Bug Fixes
* log-level filter bug ([4e02244](https://github.com/Molunerfinn/PicGo/commit/4e02244)), closes [#237](https://github.com/Molunerfinn/PicGo/issues/237)
* log-level's reset value from 'all' -> ['all'] ([3c6b329](https://github.com/Molunerfinn/PicGo/commit/3c6b329)), closes [#240](https://github.com/Molunerfinn/PicGo/issues/240) [#237](https://github.com/Molunerfinn/PicGo/issues/237)
* mini window hidden bug in linux ([466dbec](https://github.com/Molunerfinn/PicGo/commit/466dbec)), closes [#239](https://github.com/Molunerfinn/PicGo/issues/239)
## :tada: 2.1.1 (2019-04-16)
### :bug: Bug Fixes
* upload-area can't upload images ([4000cea](https://github.com/Molunerfinn/PicGo/commit/4000cea))
# :tada: 2.1.0 (2019-04-15)
### :sparkles: Features
* add commandline argvs support for picgo app ([6db86ec](https://github.com/Molunerfinn/PicGo/commit/6db86ec))
* add gui-api for remove event ([407b821](https://github.com/Molunerfinn/PicGo/commit/407b821)), closes [#201](https://github.com/Molunerfinn/PicGo/issues/201)
* add windows context menu ([e5fbe75](https://github.com/Molunerfinn/PicGo/commit/e5fbe75))
* add workflow for mac ([7f17697](https://github.com/Molunerfinn/PicGo/commit/7f17697))
* support commandline -> upload images in clipboard ([74c7016](https://github.com/Molunerfinn/PicGo/commit/74c7016))
### :bug: Bug Fixes
* qiniu area option from select group -> input ([c64959a](https://github.com/Molunerfinn/PicGo/commit/c64959a)), closes [#230](https://github.com/Molunerfinn/PicGo/issues/230)
### :pencil: Documentation
* add brew cask source ([6122e17](https://github.com/Molunerfinn/PicGo/commit/6122e17))
### :package: Chore
* add picgo bump-version ([37f1d34](https://github.com/Molunerfinn/PicGo/commit/37f1d34))
+27 -22
View File
@@ -1,58 +1,63 @@
## 常见问题 ## 常见问题
> 在使用PicGo期间你会遇到很多问题,不过很多问题其实之前就有人提问过,也被解决,所以你可以先看看[使用文档](https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BF%AB%E6%8D%B7%E9%94%AE),这份FAQ,以及那些被关闭的[issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed),应该能找到答案。 > 在使用 PicGo 期间你会遇到很多问题,不过很多问题其实之前就有人提问过,也被解决,所以你可以先看看 [使用文档](https://picgo.github.io/PicGo-Doc/zh/guide/getting-started.html#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B),这份 FAQ,以及那些被关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed),应该能找到答案。
## 1.七牛图床上传图片成功后无法显示或图片无`http://`前缀 ## 1. 七牛图床上传图片成功后,相册里无法显示或图片无`http://`前缀
通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。 通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。
参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79) 参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79)
## 2.能否支持图床远端同步删除 ## 2. 能否支持图床远端同步删除
不能。有些图床(比如微博图床、SM.MS、Imgur等)不支持后台管理,为了架构统一不支持远端删除。 不能。有些图床(比如微博图床、SM.MS、Imgur 等)不支持后台管理,为了架构统一不支持远端删除。
## 3.能否支持上传视频文件 ## 3. 能否支持上传视频文件
目前不能。2.0版本出来后将会支持插件系统,届时如果有人开发了相应的插件理论可以支持任意文件上传。 目前不能。如果有人开发了相应的插件理论可以支持任意文件上传。
## 4.微博图床上传之后无法显示预览图 ## 4. 微博图床上传之后无法显示预览图
通常是挂了全局代理导致的。 通常是挂了全局代理导致的。
参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36) 参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36)
## 5.能否支持某某某图床 ## 5. 能否支持某某某图床
截止v1.6,PicGo支持了如下图床: 截止 v1.6PicGo 支持了如下图床:
- `微博图床` v1.0 - `微博图床` v1.0
- `七牛图床` v1.0 - `七牛图床` v1.0
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0 - `腾讯云 COS v4\v5 版本` v1.1 & v1.5.0
- `又拍云` v1.2.0 - `又拍云` v1.2.0
- `GitHub` v1.5.0 - `GitHub` v1.5.0
- `SM.MS` v1.5.1 - `SM.MS` v1.5.1
- `阿里云OSS` v1.6.0 - `阿里云 OSS` v1.6.0
- `Imgur` v1.6.0 - `Imgur` v1.6.0
所以本体内将不会再支持其他图床。待2.0版本出来支持插件系统后可以由其他开发者开发相应的图床插件用以支持其他图床 所以本体内将不会再支持其他图床。需要其他图床支持可以参考目前已有的三方 [插件](https://github.com/PicGo/Awesome-PicGo),如果还是没有你所需要的图床欢迎开发一个插件供大家使用
## 6.一个图床设置多个信息 ## 6. 一个图床设置多个信息
不能。因为目前的架构只支持一个图床一份信息。 不能。因为目前的架构只支持一个图床一份信息。
## 7.v2.0版本有什么新功能 ## 7. GitHub 图床有时能上传,有时上传失败
在不改变PicGo现有使用界面和使用习惯的基础上,将会重构整个底层`传输->转换->上传`的架构,使其能够支持插件系统,方便其他开发者基于PicGo的底层进行二次开发 1. GitHub 图床不支持上传同名文件,如果有同名文件上传,会报错。建议开启 `时间戳重命名` 避免同名文件
2. GitHub 服务器和国内 GFW 的问题会导致有时上传成功,有时上传失败,无解。想要稳定请使用付费云存储,如阿里云、腾讯云等,价格也不会贵。
理论上有了插件系统之后,其他开发者能够支持如下功能: ## 8. Mac 上无法打开 PicGo 的主窗口界面
1. 能够实现上传前图片压缩 PicGo 在 Mac 上是一个顶部栏应用,在 dock 栏是不会有图标的。要打开主窗口,请右键或者双指点按顶部栏 PicGo 图标,选择「打开详细窗口」即可打开主窗口。
2. 能够通过图片url实现上传图片 -> 更深入地可以批量地实现图床的迁移
3. 能够支持其他图床甚至是自己的图床
等等。 ## 9. 上传失败,或者是服务器出错
届时底层架构将会独立出来成为一个独立的npm包,能够直接在命令行里调用 1. PicGo 自带的图床都经过测试,上传出错一般都不是 PicGo 自身的原因。如果你用的是 GitHub 图床请参考上面的第 7 点
2. 检查 PicGo 的日志(报错日志可以在 PicGo 设置 -> 设置日志文件 -> 点击打开 后找到),看看 `[PicGo Error]` 的报错信息里有什么关键信息
1. 先自行搜索 error 里的报错信息,往往你能百度或者谷歌出问题原因,不必开 issue。
2. 如果有带有 `401``403``40X` 状态码字样的,不用怀疑,就是你配置写错了,仔细检查配置,看看是否多了空格之类的。
3. 如果带有 `HttpError``RequestError``socket hang up` 等字样的说明这是网络问题,我无法帮你解决网络问题,请检查你自己的网络,是否有代理,DNS 设置是否正常等。
目前开发进度可以参考[PicGo-Core](https://github.com/Molunerfinn/PicGo-Core)。 ## 10. macOS版本安装完之后没有主界面
请找到PicGo在顶部栏的图标,然后右键(触摸板双指点按,或者鼠标右键),即可找到「打开详细窗口」的菜单。
+18 -5
View File
@@ -1,8 +1,21 @@
MIT License The MIT License (MIT)
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Copyright (c) 2017-present, Molunerfinn
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+74 -45
View File
@@ -1,16 +1,12 @@
# PicGo <div align="center">
<img src="https://raw.githubusercontent.com/Molunerfinn/test/master/picgo/New%20LOGO-150.png" alt="">
> 图片上传+管理新体验 <h1>PicGo</h1>
<blockquote>图片上传+管理新体验 </blockquote>
<p align="center"> <a href="https://github.com/Molunerfinn/PicGo/actions">
<img src="https://user-images.githubusercontent.com/12621342/33876119-85a5148e-df5f-11e7-8843-46224e595d52.png" alt="">
</p>
<p align="center">
<a href="https://github.com/feross/standard">
<img src="https://img.shields.io/badge/code%20style-standard-green.svg?style=flat-square" alt=""> <img src="https://img.shields.io/badge/code%20style-standard-green.svg?style=flat-square" alt="">
</a> </a>
<a href="https://travis-ci.org/Molunerfinn/PicGo/builds"> <a href="https://github.com/Molunerfinn/PicGo/actions">
<img src="https://img.shields.io/travis/Molunerfinn/PicGo.svg?style=flat-square" alt=""> <img src="https://github.com/Molunerfinn/PicGo/workflows/Build/badge.svg" alt="">
</a> </a>
<a href="https://github.com/Molunerfinn/PicGo/releases"> <a href="https://github.com/Molunerfinn/PicGo/releases">
<img src="https://img.shields.io/github/downloads/Molunerfinn/PicGo/total.svg?style=flat-square" alt=""> <img src="https://img.shields.io/github/downloads/Molunerfinn/PicGo/total.svg?style=flat-square" alt="">
@@ -18,41 +14,70 @@
<a href="https://github.com/Molunerfinn/PicGo/releases/latest"> <a href="https://github.com/Molunerfinn/PicGo/releases/latest">
<img src="https://img.shields.io/github/release/Molunerfinn/PicGo.svg?style=flat-square" alt=""> <img src="https://img.shields.io/github/release/Molunerfinn/PicGo.svg?style=flat-square" alt="">
</a> </a>
</p> <a href="https://github.com/PicGo/bump-version">
<img src="https://img.shields.io/badge/picgo-convention-blue.svg?style=flat-square" alt="">
</a>
<a href="https://gitter.im/picgo-all/PicGo?utm_source=share-link&utm_medium=link&utm_campaign=share-link">
<img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-66ae93.svg?style=flat-square" alt="">
</a>
</div>
## 应用说明 ## 应用概述
**PicGo在上传图片之后自动会将图片链接复制到你的剪贴板里,可选5种复制的链接格式。** **PicGo: 一个用于快速上传图片并获取图片 URL 链接的工具**
PicGo目前支持了 PicGo 本体支持如下图床:
- `微博图床` v1.0
- `七牛图床` v1.0 - `七牛图床` v1.0
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0 - `腾讯云 COS v4\v5 版本` v1.1 & v1.5.0
- `又拍云` v1.2.0 - `又拍云` v1.2.0
- `GitHub` v1.5.0 - `GitHub` v1.5.0
- `SM.MS` v1.5.1 - `SM.MS V2` v2.3.0-beta.0
- `阿里云OSS` v1.6.0 - `阿里云 OSS` v1.6.0
- `Imgur` v1.6.0 - `Imgur` v1.6.0
**本体不再增加默认的图床支持。你可以自行开发第三方图床插件。** **本体不再增加默认的图床支持。你可以自行开发第三方图床插件。详见 [PicGo-Core](https://picgo.github.io/PicGo-Core-Doc/)**
支持macOS、windows 64位(>= v1.3.1),linux>= v1.6.0)。 ## 特色功能
支持快捷键`command+shift+p`macOS)或者`control+shift+p`windows\linux)用以支持快捷上传剪贴板里的图片(第一张)。 - 支持拖拽图片上传
PicGo支持自定义快捷键,使用方法见[配置手册](https://picgo.github.io/PicGo-Doc/zh/guide/config.html)。 - 支持快捷键上传剪贴板里第一张图片
- Windows 和 macOS 支持右键图片文件通过菜单上传 (v2.1.0+)
- 上传图片后自动复制链接到剪贴板
- 支持自定义复制到剪贴板的链接格式
- 支持修改快捷键,默认快速上传快捷键:`command+shift+p`macOS| `control+shift+p`Windows\Linux)
- 支持插件系统,已有插件支持 Gitee、青云等第三方图床
- 更多第三方插件以及使用了 PicGo 底层的应用可以在 [Awesome-PicGo](https://github.com/PicGo/Awesome-PicGo) 找到。欢迎贡献!
- 支持通过发送 HTTP 请求调用 PicGo 上传(v2.2.0+)
- 更多功能等你自己去发现,同时也会不断开发新功能
- 开发进度可以查看 [Projects](https://github.com/Molunerfinn/PicGo/projects),会同步更新开发进度
- 欢迎加入 [官方讨论区](https://github.com/Molunerfinn/PicGo/discussions) 与我交流
开发进度可以查看[Projects](https://github.com/Molunerfinn/PicGo/projects),会同步更新开发进度。 **如果第一次使用,请参考应用 [使用文档](https://picgo.github.io/PicGo-Doc/zh/guide/getting-started.html)。遇到问题了还可以看看 [FAQ](https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md) 以及被关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed)。**
**如果第一次使用,请参考应用使用[快速上手](https://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)。**
## 下载安装 ## 下载安装
macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版本的`exe`文件,linux用户请下载`AppImage`文件 点击此处下载 [应用](https://github.com/Molunerfinn/PicGo/releases)
点击此处下载[应用](https://github.com/Molunerfinn/PicGo/releases)。 ### Windows
**如果你是Arch类的Linux用户,可以直接通过`aurman -S picgo-appimage`来安装PicGo。感谢@houbaron的贡献!** Windows 用户请下载最新版本的 `exe` 文件。
**还可以使用 [Scoop](https://scoop.sh/) 来安装 PicGo: `scoop bucket add helbing https://github.com/helbing/scoop-bucket` & `scoop install picgo`。 感谢 @helbing 的贡献!**
**还可以使用 [Chocolatey](https://chocolatey.org/) 来安装 PicGo: `choco install picgo`。 感谢 @iYato 的贡献!**
### macOS
macOS 用户请下载最新版本的 `dmg` 文件。
**还可以使用 [Homebrew](https://brew.sh/) 来安装 PicGo: `brew install picgo --cask`。感谢 @womeimingzi11 的贡献!**
### Linux
Linux 用户请下载 `AppImage` 文件。
**如果你是 Arch 类的 Linux 用户,可以直接通过 `yay -S picgo-appimage` 来安装 PicGo (若是没有则先安装 yay ,是 aur 仓库助手)。感谢 @houbaron 的贡献!**
## 应用截图 ## 应用截图
@@ -62,46 +87,50 @@ macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版
## 开发说明 ## 开发说明
> 目前仅针对Mac、Windows。Linux平台并未测试。 > 目前仅针对 Mac、Windows。Linux 平台并未测试。
如果你想要学习、开发、修改或自行构建PicGo,可以依照下面的指示: 如果你想要学习、开发、修改或自行构建 PicGo,可以依照下面的指示:
> 如果想学习Electron-vue的开发,可以查看我写的系列教程——[Electron-vue开发实战](https://molunerfinn.com/tags/Electron-vue/) > 如果想学习 Electron-vue 的开发,可以查看我写的系列教程——[Electron-vue 开发实战](https://molunerfinn.com/tags/Electron-vue/)
1. 你需要有node、git环境。需要了解npm的相关知识。 1. 你需要有 Node、Git 环境了解 npm 的相关知识。
2. `git clone https://github.com/Molunerfinn/PicGo.git` 并进入项目 2. `git clone https://github.com/Molunerfinn/PicGo.git` 并进入项目
3. `npm install` 下载依赖 3. `yarn` 下载依赖。注意如果你没有 `yarn`,请去 [官网](https://classic.yarnpkg.com/en/docs/install) 下载安装后再使用。 **用 `npm install` 将导致未知错误!**
4. Mac需要有Xcode环境,Windows需要有VS环境。 4. Mac 需要有 Xcode 环境,Windows 需要有 VS 环境。
### 开发模式 ### 开发模式
输入`npm run dev`进入开发模式,开发模式具有热重载特性。不过需要注意的是,开发模式不稳定,会有进程崩溃的情况。此时需要: 输入 `npm run electron:serve` 进入开发模式,开发模式具有热重载特性。不过需要注意的是,开发模式不稳定,会有进程崩溃的情况。此时需要:
```bash ```bash
ctrl+c # 退出开发模式 ctrl+c # 退出开发模式
npm run dev # 重新进入开发模式 npm run electron:serve # 重新进入开发模式
``` ```
**注:Windows 开发模式运行之后会在底部任务栏的右下角应用区出现 PicGo 的应用图标。**
### 生产模式 ### 生产模式
如果你需要自行构建,可以`npm run build`开始进行构建。构建成功后,会在`build`目录里出现构建成功的相应安装文件。 如果你需要自行构建,可以 `npm run electron:build` 开始进行构建。构建成功后,会在 `dist_electron` 目录里出现构建成功的相应安装文件。
**注意**:如果你的网络环境不太好,可能会出现`electron-builder`下载`electron`二进制文件失败的情况。这个时候需要在`npm run build`之前指定一下`electron`的源为国内源: **注意**:如果你的网络环境不太好,可能会出现 `electron-builder` 下载 `electron` 二进制文件失败的情况。这个时候需要在 `npm run electron:build` 之前指定一下 `electron` 的源为国内源:
```bash ```bash
export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
npm run build # 在 Windows 上,则可以使用 set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ (无需引号)
npm run electron:build
``` ```
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在`~/.electron/`目录下。如果想要更新`electron`构建版本,可以删除`~/.electron/`目录,然后重新运行上一步,让`electron-builder`去下载最新的`electron`二进制文件。 只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在 `~/.electron/` 目录下。如果想要更新 `electron` 构建版本,可以删除 `~/.electron/` 目录,然后重新运行上一步,让 `electron-builder `去下载最新的 `electron` 二进制文件。
## 其他相关 ## 其他相关
- [vs-picgo](https://github.com/Spades-S/vs-picgo)picgo的VSCode版。 - [vs-picgo](https://github.com/PicGo/vs-picgo)PicGo 的 VS Code 版。
- [flutter-picgo](https://github.com/PicGo/flutter-picgo)PicGo 的手机版(支持 Android 和 iOS )。
## 赞助 ## 赞助
如果你喜欢PicGo并且它对你确实有帮助,欢迎给我打赏一杯咖啡哈~ 如果你喜欢 PicGo 并且它对你确实有帮助,欢迎给我打赏一杯咖啡哈~
支付宝: 支付宝:
@@ -115,4 +144,4 @@ npm run build
[MIT](http://opensource.org/licenses/MIT) [MIT](http://opensource.org/licenses/MIT)
Copyright (c) 2017 Molunerfinn Copyright (c) 2017 - 2019 Molunerfinn
+4 -4
View File
@@ -11,7 +11,6 @@ platform:
- x64 - x64
cache: cache:
- node_modules
- '%APPDATA%\npm-cache' - '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron' - '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache' - '%USERPROFILE%\AppData\Local\Yarn\cache'
@@ -20,13 +19,14 @@ init:
- git config --global core.autocrlf input - git config --global core.autocrlf input
install: install:
- ps: Install-Product node 8 x64 - ps: Install-Product node 12 x64
- git reset --hard HEAD - git reset --hard HEAD
- npm install - yarn
- node --version - node --version
build_script: build_script:
#- yarn test #- yarn test
- npm run release - yarn build --win --ia32
- yarn release
test: off test: off
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

+13 -8
View File
@@ -1,8 +1,13 @@
!macro preInit !macro customInstall
SetRegView 64 SetRegView 64
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\PicGo" WriteRegStr HKCR "*\shell\PicGo" "" "Upload pictures w&ith PicGo"
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\PicGo" WriteRegStr HKCR "*\shell\PicGo" "Icon" "$INSTDIR\PicGo.exe"
SetRegView 32 WriteRegStr HKCR "*\shell\PicGo\command" "" '"$INSTDIR\PicGo.exe" "upload" "%1"'
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\PicGo" SetRegView 32
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\PicGo" WriteRegStr HKCR "*\shell\PicGo" "" "Upload pictures w&ith PicGo"
!macroend WriteRegStr HKCR "*\shell\PicGo" "Icon" "$INSTDIR\PicGo.exe"
WriteRegStr HKCR "*\shell\PicGo\command" "" '"$INSTDIR\PicGo.exe" "upload" "%1"'
!macroend
!macro customUninstall
DeleteRegKey HKCR "*\shell\PicGo"
!macroend
View File
View File
+10 -10
View File
@@ -7,16 +7,16 @@
small(v-if="version") {{ version }} small(v-if="version") {{ version }}
h2.desc 图片上传+管理新体验 h2.desc 图片上传+管理新体验
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载 button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载
button.download(@click="goLink('https://github.com/Molunerfinn/picgo')") 在github上查看 button.download(@click="goLink('https://picgo.github.io/PicGo-Doc/zh/guide/')") 查看文档
h3.desc h3.desc
| 基于#[a(href="https://github.com/SimulatedGREG/electron-vue" target="_blank") electron-vue]开发 | 基于#[a(href="https://github.com/SimulatedGREG/electron-vue" target="_blank") electron-vue]开发
h3.desc h3.desc
| 支持macOS,Windows,Linux | 支持macOS,Windows,Linux
h3.desc h3.desc
| 支持插件系统让PicGo更强大 | 支持#[a(href="https://picgo.github.io/PicGo-Doc/zh/guide/config.html#%E6%8F%92%E4%BB%B6%E8%AE%BE%E7%BD%AE%EF%BC%88v2-0%EF%BC%89" target="_blank") 插件系统]让PicGo更强大
#container.container-fluid #container.container-fluid
.row.ex-width .row.ex-width
img.gallery.col-xs-10.col-xs-offset-1.col-md-offset-2.col-md-8(src="https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e") img.gallery.col-xs-10.col-xs-offset-1.col-md-offset-2.col-md-8(src="https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/first.png")
.row.ex-width.display-list .row.ex-width.display-list
.display-list__item(v-for="(item, index) in itemList" :key="index" :class="{ 'o-item': index % 2 !== 0 }") .display-list__item(v-for="(item, index) in itemList" :key="index" :class="{ 'o-item': index % 2 !== 0 }")
.col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0 .col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0
@@ -37,32 +37,32 @@ export default {
year: new Date().getFullYear(), year: new Date().getFullYear(),
itemList: [ itemList: [
{ {
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fma907llb5j20m30ed46a', url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/second.png',
title: '精致设计', title: '精致设计',
desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。(Windows平台不支持)' desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。(Windows平台不支持)'
}, },
{ {
url: 'https://i.loli.net/2018/07/11/5b45768fb1276.png', url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/third.png',
title: 'Mini小窗', title: 'Mini小窗',
desc: 'Windows以及Linux系统下提供一个mini悬浮窗用于用户拖拽上传,节约你宝贵的桌面空间。' desc: 'Windows以及Linux系统下提供一个mini悬浮窗用于用户拖拽上传,节约你宝贵的桌面空间。'
}, },
{ {
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd56zm2nej218g0p0teb', url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/forth.png',
title: '便捷管理', title: '便捷管理',
desc: '查看你的上传记录,重复使用更方便。支持点击图片大图查看。支持删除图片(仅本地记录),让界面更加干净。' desc: '查看你的上传记录,重复使用更方便。支持点击图片大图查看。支持删除图片(仅本地记录),让界面更加干净。'
}, },
{ {
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd5ck9m0wj20lr0cxmzs', url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/fifth.png',
title: '可选图床', title: '可选图床',
desc: '默认支持微博图床、七牛图床、腾讯云COS、又拍云、GitHub、SM.MS、阿里云OSS、Imgur。方便不同图床的上传需求。2.0版本开始更可以自己开发插件实现其他图床的上传需求。' desc: '默认支持微博图床、七牛图床、腾讯云COS、又拍云、GitHub、SM.MS、阿里云OSS、Imgur。方便不同图床的上传需求。2.0版本开始更可以自己开发插件实现其他图床的上传需求。'
}, },
{ {
url: 'https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e', url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/sixth.png',
title: '多样链接', title: '多样链接',
desc: '支持5种默认剪贴板链接格式,包括一种自定义格式,让你的文本编辑游刃有余。' desc: '支持5种默认剪贴板链接格式,包括一种自定义格式,让你的文本编辑游刃有余。'
}, },
{ {
url: 'https://i.loli.net/2019/01/12/5c39a2f60a32a.png', url: 'https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/seventh.png',
title: '插件系统', title: '插件系统',
desc: '2.0版本开始支持插件系统,让PicGo发挥无限潜能,成为一个极致的效率工具。' desc: '2.0版本开始支持插件系统,让PicGo发挥无限潜能,成为一个极致的效率工具。'
} }
@@ -108,7 +108,7 @@ h1
#header #header
height 100vh height 100vh
width 100% width 100%
background-image url("https://ws1.sinaimg.cn/large/8700af19ly1fm9ru6fqvjj22p81stdta") background-image url("https://cdn.jsdelivr.net/gh/Molunerfinn/test/picgo-site/bg.jpeg")
background-attachment fixed background-attachment fixed
background-size cover background-size cover
background-position center background-position center
+72 -144
View File
@@ -1,159 +1,87 @@
{ {
"name": "picgo", "name": "picgo",
"version": "2.0.0", "version": "2.3.0",
"author": "Molunerfinn <marksz@teamsz.xyz>", "private": true,
"description": "Easy to upload your pic & copy to write",
"license": "MIT",
"main": "./dist/electron/main.js",
"scripts": { "scripts": {
"render": "webpack-dev-server --hot --colors --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist", "dev": "vue-cli-service electron:serve",
"build": "node .electron-vue/build.js && electron-builder", "build": "vue-cli-service electron:build",
"release": "node .electron-vue/build.js && electron-builder", "lint": "vue-cli-service lint",
"build:dir": "node .electron-vue/build.js && electron-builder --dir", "electron:build": "vue-cli-service electron:build",
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", "electron:serve": "vue-cli-service electron:serve",
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", "postinstall": "electron-builder install-app-deps",
"dev": "node .electron-vue/dev-runner.js", "postuninstall": "electron-builder install-app-deps",
"e2e": "npm run pack && mocha test/e2e", "cz": "git-cz",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test", "bump": "bump-version",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test", "release": "vue-cli-service electron:build --publish always"
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
"test": "npm run unit && npm run e2e",
"unit": "karma start test/unit/karma.conf.js",
"postinstall": "npm run lint:fix",
"build:docs": "cross-env NODE_ENV=production webpack -p --config .electron-vue/webpack.docs.config.js",
"docs": "webpack-dev-server --content-base docs/dist --config .electron-vue/webpack.docs.config.js --hot --inline",
"patch": "npm version patch && git push origin master && git push origin --tags",
"minor": "npm version minor && git push origin master && git push origin --tags",
"major": "npm version major && git push origin master && git push origin --tags"
}, },
"build": { "main": "background.js",
"productName": "PicGo", "husky": {
"appId": "com.molunerfinn.picgo", "hooks": {
"directories": { "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
"output": "build"
},
"files": [
"dist/electron/**/*"
],
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "build/icons/icon.icns",
"extendInfo": {
"LSUIElement": 1
}
},
"win": {
"icon": "build/icons/icon.ico",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons/"
} }
}, },
"config": {
"commitizen": {
"path": "./node_modules/cz-customizable"
},
"cz-customizable": {
"config": "./node_modules/@picgo/bump-version/.cz-config.js"
}
},
"commitlint": {
"extends": [
"./node_modules/@picgo/bump-version/commitlint-picgo"
]
},
"dependencies": { "dependencies": {
"axios": "^0.16.1", "@picgo/store": "^1.0.3",
"dateformat": "^3.0.3", "axios": "^0.19.0",
"element-ui": "^2.4.11", "core-js": "^3.3.2",
"element-ui": "^2.13.0",
"fix-path": "^2.1.0", "fix-path": "^2.1.0",
"fs-extra": "^4.0.2", "fs-extra": "^8.1.0",
"image-size": "^0.6.1", "keycode": "^2.2.0",
"keycode": "^2.1.9",
"lodash-id": "^0.14.0", "lodash-id": "^0.14.0",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"md5": "^2.2.1", "picgo": "^1.4.24",
"melody.css": "^1.0.2", "qrcode.vue": "^1.7.0",
"picgo": "^1.1.15", "uuidv4": "^6.2.11",
"qiniu": "^7.1.1", "vue": "^2.6.10",
"vue": "^2.3.3", "vue-gallery": "^2.0.1",
"vue-electron": "^1.0.6",
"vue-gallery": "^1.2.4",
"vue-lazyload": "^1.2.6", "vue-lazyload": "^1.2.6",
"vue-router": "^2.5.3", "vue-router": "^3.1.3"
"vuex": "^2.3.1"
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.25.0", "@commitlint/cli": "^8.2.0",
"babel-eslint": "^7.2.3", "@picgo/bump-version": "^1.0.3",
"babel-loader": "^7.1.1", "@types/fs-extra": "^8.0.1",
"babel-plugin-istanbul": "^4.1.1", "@types/inquirer": "^6.5.0",
"babel-plugin-transform-runtime": "^6.23.0", "@types/lowdb": "^1.0.9",
"babel-preset-env": "^1.6.0", "@types/node": "10.17.6",
"babel-preset-stage-0": "^6.24.1", "@types/request-promise-native": "^1.0.17",
"babel-register": "^6.24.1", "@vue/cli-plugin-babel": "^4.0.0",
"babili-webpack-plugin": "^0.1.2", "@vue/cli-plugin-eslint": "^4.0.0",
"cfonts": "^1.1.3", "@vue/cli-plugin-router": "^4.0.0",
"chai": "^4.0.0", "@vue/cli-plugin-typescript": "^4.0.0",
"chalk": "^2.1.0", "@vue/cli-service": "^4.0.0",
"copy-webpack-plugin": "^4.0.1", "@vue/eslint-config-standard": "^4.0.0",
"cross-env": "^5.0.5", "@vue/eslint-config-typescript": "^4.0.0",
"css-loader": "^0.28.4", "commitizen": "^4.0.3",
"del": "^3.0.0", "conventional-changelog": "^3.1.18",
"devtron": "^1.4.0", "cz-customizable": "^6.2.0",
"electron": "4.0.0", "electron": "^6.0.0",
"electron-builder": "^20.38.4", "electron-devtools-installer": "^3.2.0",
"electron-debug": "^1.4.0", "eslint": "^5.16.0",
"electron-devtools-installer": "^2.2.0", "eslint-plugin-vue": "^5.0.0",
"eslint": "^4.4.1", "husky": "^3.1.0",
"eslint-config-standard": "^10.2.1", "stylus": "^0.54.7",
"eslint-friendly-formatter": "^3.0.0", "stylus-loader": "^3.0.2",
"eslint-loader": "^2.1.1", "typescript": "~3.7.3",
"eslint-plugin-html": "^3.1.1", "vue-cli-plugin-electron-builder": "^1.4.2",
"eslint-plugin-import": "^2.7.0", "vue-property-decorator": "^8.3.0",
"eslint-plugin-node": "^5.1.1", "vue-template-compiler": "^2.6.10"
"eslint-plugin-promise": "^3.5.0", },
"eslint-plugin-standard": "^3.0.1", "resolutions": {
"file-loader": "^3.0.1", "@types/node": "12.0.2"
"html-webpack-plugin": "^3.2.0",
"inject-loader": "^3.0.0",
"karma": "^1.3.0",
"karma-chai": "^0.1.0",
"karma-coverage": "^1.1.1",
"karma-electron": "^5.1.1",
"karma-mocha": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "^0.0.31",
"karma-webpack": "^2.0.1",
"mini-css-extract-plugin": "0.4.0",
"mocha": "^3.0.2",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"pug": "^2.0.0-rc.4",
"pug-loader": "^2.3.0",
"pug-plain-loader": "^1.0.0",
"require-dir": "^0.3.0",
"spectron": "^3.7.1",
"style-loader": "^0.23.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"url-loader": "^1.1.2",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.4.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.4.2",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4",
"webpack-hot-middleware": "^2.22.2",
"webpack-merge": "^4.1.3"
} }
} }
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSServices</key>
<array>
<dict>
<key>NSBackgroundColorName</key>
<string>background</string>
<key>NSBackgroundSystemColorName</key>
<string>systemBlueColor</string>
<key>NSIconName</key>
<string>NSTouchBarShare</string>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Upload pictures with PicGo</string>
</dict>
<key>NSMessage</key>
<string>runWorkflowAsService</string>
<key>NSRequiredContext</key>
<dict>
<key>NSApplicationIdentifier</key>
<string>com.apple.finder</string>
</dict>
<key>NSSendFileTypes</key>
<array>
<string>public.image</string>
</array>
</dict>
</array>
</dict>
</plist>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -0,0 +1,314 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AMApplicationBuild</key>
<string>444.42</string>
<key>AMApplicationVersion</key>
<string>2.9</string>
<key>AMDocumentVersion</key>
<string>2</string>
<key>actions</key>
<array>
<dict>
<key>action</key>
<dict>
<key>AMAccepts</key>
<dict>
<key>Container</key>
<string>List</string>
<key>Optional</key>
<true/>
<key>Types</key>
<array>
<string>com.apple.cocoa.string</string>
</array>
</dict>
<key>AMActionVersion</key>
<string>2.0.3</string>
<key>AMApplication</key>
<array>
<string>自动操作</string>
</array>
<key>AMParameterProperties</key>
<dict>
<key>COMMAND_STRING</key>
<dict/>
<key>CheckedForUserDefaultShell</key>
<dict/>
<key>inputMethod</key>
<dict/>
<key>shell</key>
<dict/>
<key>source</key>
<dict/>
</dict>
<key>AMProvides</key>
<dict>
<key>Container</key>
<string>List</string>
<key>Types</key>
<array>
<string>com.apple.cocoa.string</string>
</array>
</dict>
<key>ActionBundlePath</key>
<string>/System/Library/Automator/Run Shell Script.action</string>
<key>ActionName</key>
<string>运行 Shell 脚本</string>
<key>ActionParameters</key>
<dict>
<key>COMMAND_STRING</key>
<string>/Applications/PicGo.app/Contents/MacOS/PicGo upload "$@" &gt; /dev/null 2&gt;&amp;1 &amp;</string>
<key>CheckedForUserDefaultShell</key>
<true/>
<key>inputMethod</key>
<integer>1</integer>
<key>shell</key>
<string>/bin/bash</string>
<key>source</key>
<string></string>
</dict>
<key>BundleIdentifier</key>
<string>com.apple.RunShellScript</string>
<key>CFBundleVersion</key>
<string>2.0.3</string>
<key>CanShowSelectedItemsWhenRun</key>
<false/>
<key>CanShowWhenRun</key>
<true/>
<key>Category</key>
<array>
<string>AMCategoryUtilities</string>
</array>
<key>Class Name</key>
<string>RunShellScriptAction</string>
<key>InputUUID</key>
<string>79609224-28DD-4ADE-AA8F-5A6C68C18C18</string>
<key>Keywords</key>
<array>
<string>Shell</string>
<string>脚本</string>
<string>命令</string>
<string>运行</string>
<string>Unix</string>
</array>
<key>OutputUUID</key>
<string>35CD6B4C-A616-4F89-8D76-DCD3249C5B4E</string>
<key>UUID</key>
<string>4350A83B-E7E6-4D2B-9768-B1D676CF58F3</string>
<key>UnlocalizedApplications</key>
<array>
<string>Automator</string>
</array>
<key>arguments</key>
<dict>
<key>0</key>
<dict>
<key>default value</key>
<integer>0</integer>
<key>name</key>
<string>inputMethod</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>0</string>
</dict>
<key>1</key>
<dict>
<key>default value</key>
<string></string>
<key>name</key>
<string>source</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>1</string>
</dict>
<key>2</key>
<dict>
<key>default value</key>
<false/>
<key>name</key>
<string>CheckedForUserDefaultShell</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>2</string>
</dict>
<key>3</key>
<dict>
<key>default value</key>
<string></string>
<key>name</key>
<string>COMMAND_STRING</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>3</string>
</dict>
<key>4</key>
<dict>
<key>default value</key>
<string>/bin/sh</string>
<key>name</key>
<string>shell</string>
<key>required</key>
<string>0</string>
<key>type</key>
<string>0</string>
<key>uuid</key>
<string>4</string>
</dict>
</dict>
<key>isViewVisible</key>
<true/>
<key>location</key>
<string>449.000000:305.000000</string>
<key>nibPath</key>
<string>/System/Library/Automator/Run Shell Script.action/Contents/Resources/Base.lproj/main.nib</string>
</dict>
<key>isViewVisible</key>
<true/>
</dict>
</array>
<key>connectors</key>
<dict/>
<key>workflowMetaData</key>
<dict>
<key>applicationBundleID</key>
<string>com.apple.finder</string>
<key>applicationBundleIDsByPath</key>
<dict>
<key>/System/Library/CoreServices/Finder.app</key>
<string>com.apple.finder</string>
</dict>
<key>applicationPath</key>
<string>/System/Library/CoreServices/Finder.app</string>
<key>applicationPaths</key>
<array>
<string>/System/Library/CoreServices/Finder.app</string>
</array>
<key>backgroundColor</key>
<data>
YnBsaXN0MDDUAQIDBAUGNjdYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVy
VCR0b3ASAAGGoKoHCBMUFR4kKC8zVSRudWxs1QkKCwwNDg8QERJWJGNsYXNz
W05TQ29sb3JOYW1lXE5TQ29sb3JTcGFjZV1OU0NhdGFsb2dOYW1lV05TQ29s
b3KACYADEAaAAoAEVlN5c3RlbV8QD3N5c3RlbUJsdWVDb2xvctUWFwsYCRka
GxwOXE5TQ29tcG9uZW50c1VOU1JHQl8QEk5TQ3VzdG9tQ29sb3JTcGFjZUcw
IDAgMSAxTxARMCAwIDAuOTk4MTg4OTcyNQAQAYAFgAnTHyAJISIjVE5TSURV
TlNJQ0MQB4AGgAjSJQkmJ1dOUy5kYXRhTxEMSAAADEhMaW5vAhAAAG1udHJS
R0IgWFlaIAfOAAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAA
AAAAAAAAAAD21gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWNwcnQAAAFQAAAAM2Rlc2MAAAGE
AAAAbHd0cHQAAAHwAAAAFGJrcHQAAAIEAAAAFHJYWVoAAAIYAAAAFGdYWVoA
AAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQAAAJUAAAAcGRtZGQAAALEAAAAiHZ1
ZWQAAANMAAAAhnZpZXcAAAPUAAAAJGx1bWkAAAP4AAAAFG1lYXMAAAQMAAAA
JHRlY2gAAAQwAAAADHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJUUkMAAAQ8
AAAIDHRleHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAA
AAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYWVogAAAAAAAA81EAAQAA
AAEWzFhZWiAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAG+iAAA49QAAA5BY
WVogAAAAAAAAYpkAALeFAAAY2lhZWiAAAAAAAAAkoAAAD4QAALbPZGVzYwAA
AAAAAAAWSUVDIGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAWSUVDIGh0
dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2Ni0yLjEgRGVm
YXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAALklFQyA2
MTk2Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAAAAAA
AAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2lu
ZyBDb25kaXRpb24gaW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJl
bmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAAE6T+ABRfLgAQzxQAA+3MAAQT
CwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFzAAAAAAAAAAEAAAAA
AAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAAAENSVCBjdXJ2AAAAAAAABAAA
AAAFAAoADwAUABkAHgAjACgALQAyADcAOwBAAEUASgBPAFQAWQBeAGMAaABt
AHIAdwB8AIEAhgCLAJAAlQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA
4ADlAOsA8AD2APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFn
AW4BdQF8AYMBiwGSAZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIMAhQC
HQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2AsECywLVAuAC6wL1
AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOiA64DugPHA9MD4APsA/kE
BgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoEqAS2BMQE0wThBPAE/gUNBRwFKwU6
BUkFWAVnBXcFhgWWBaYFtQXFBdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0G
rwbABtEG4wb1BwcHGQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8IMghG
CFoIbgiCCJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEK
Jwo9ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvhC/kMEgwq
DEMMXAx1DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN3g34DhMOLg5JDmQO
fw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/sEAkQJhBDEGEQfhCbELkQ1xD1
ERMRMRFPEW0RjBGqEckR6BIHEiYSRRJkEoQSoxLDEuMTAxMjE0MTYxODE6QT
xRPlFAYUJxRJFGoUixStFM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxay
FtYW+hcdF0EXZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0a
BBoqGlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1HR4dRx1w
HZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBsIJggxCDwIRwh
SCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij8CQfJE0kfCSrJNolCSU4
JWgllyXHJfcmJyZXJocmtyboJxgnSSd6J6sn3CgNKD8ocSiiKNQpBik4KWsp
nSnQKgIqNSpoKpsqzysCKzYraSudK9EsBSw5LG4soizXLQwtQS12Last4S4W
Lkwugi63Lu4vJC9aL5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQz
DTNGM38zuDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgU
OFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuqO+g8JzxlPKQ84z0iPWE9
oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGsQe5CMEJyQrVC90M6
Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG8Ec1R3tHwEgFSEtIkUjXSR1J
Y0mpSfBKN0p9SsRLDEtTS5pL4kwqTHJMuk0CTUpNk03cTiVObk63TwBPSU+T
T91QJ1BxULtRBlFQUZtR5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9W
XFapVvdXRFeSV+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0n
XXhdyV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2PrZEBk
lGTpZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhqn2r3a09rp2v/
bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw4HE6cZVx8HJLcqZzAXNdc7h0
FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4bnjMeSp5iXnnekZ6pXsEe2N7wnwh
fIF84X1BfaF+AX5ifsJ/I3+Ef+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE
44VHhauGDoZyhteHO4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Y
jf+OZo7OjzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+X
Cpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8dn4uf+qBp
oNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n4KhSqMSpN6mpqhyq
j6sCq3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1sOqxYLHWskuywrM4s660JbSc
tRO1irYBtnm28Ldot+C4WbjRuUq5wro7urW7LrunvCG8m70VvY++Cr6Evv+/
er/1wHDA7MFnwePCX8Lbw1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJuco4
yrfLNsu2zDXMtc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V
0dZV1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4DbgveFE
4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl63Dr++yG7RHt
nO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD13vZt9vv3ivgZ+Kj5OPnH
+lf65/t3/Af8mP0p/br+S/7c/23//4AH0ikqKyxaJGNsYXNzbmFtZVgkY2xh
c3Nlc11OU011dGFibGVEYXRhoystLlZOU0RhdGFYTlNPYmplY3TSKSowMVxO
U0NvbG9yU3BhY2WiMi5cTlNDb2xvclNwYWNl0ikqNDVXTlNDb2xvcqI0Ll8Q
D05TS2V5ZWRBcmNoaXZlctE4OVRyb290gAEACAARABoAIwAtADIANwBCAEgA
UwBaAGYAcwCBAIkAiwCNAI8AkQCTAJoArAC3AMQAygDfAOcA+wD9AP8BAQEI
AQ0BEwEVARcBGQEeASYNcg10DXkNhA2NDZsNnw2mDa8NtA3BDcQN0Q3WDd4N
4Q3zDfYN+wAAAAAAAAIBAAAAAAAAADoAAAAAAAAAAAAAAAAAAA39
</data>
<key>backgroundColorName</key>
<string>systemBlueColor</string>
<key>inputTypeIdentifier</key>
<string>com.apple.Automator.fileSystemObject.image</string>
<key>outputTypeIdentifier</key>
<string>com.apple.Automator.nothing</string>
<key>presentationMode</key>
<integer>15</integer>
<key>processesInput</key>
<integer>0</integer>
<key>serviceApplicationBundleID</key>
<string>com.apple.finder</string>
<key>serviceApplicationPath</key>
<string>/System/Library/CoreServices/Finder.app</string>
<key>serviceInputTypeIdentifier</key>
<string>com.apple.Automator.fileSystemObject.image</string>
<key>serviceOutputTypeIdentifier</key>
<string>com.apple.Automator.nothing</string>
<key>serviceProcessesInput</key>
<integer>0</integer>
<key>systemImageName</key>
<string>NSTouchBarShare</string>
<key>useAutomaticInputType</key>
<integer>0</integer>
<key>workflowTypeIdentifier</key>
<string>com.apple.Automator.servicesMenu</string>
</dict>
</dict>
</plist>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="referrer" content="never">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>PicGo</title>
</head>
<body>
<noscript>
<strong>We're sorry but picgo-new doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# require xclip(see http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212)
command -v xclip >/dev/null 2>&1 || { echo >&1 "no xclip"; exit 1; }
# write image in clipboard to file (see http://unix.stackexchange.com/questions/145131/copy-image-from-clipboard-to-file)
filePath=`xclip -selection clipboard -o 2>/dev/null | grep ^file:// | cut -c8-`
if [ ! -n "$filePath" ] ;then
if
xclip -selection clipboard -target image/png -o >/dev/null 2>&1
then
xclip -selection clipboard -target image/png -o >$1 2>/dev/null
echo $1
else
echo "no image"
fi
else
echo $filePath
fi
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

+45
View File
@@ -0,0 +1,45 @@
# Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1
param($imagePath)
# https://github.com/PowerShell/PowerShell/issues/7233
# fix the output encoding bug
[console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Add-Type -Assembly PresentationCore
function main {
$img = [Windows.Clipboard]::GetImage()
if ($img -eq $null) {
"no image"
Exit 1
}
if (-not $imagePath) {
"no image"
Exit 1
}
$fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0)
$stream = [IO.File]::Open($imagePath, "OpenOrCreate")
$encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder
$encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null
$encoder.Save($stream) | out-null
$stream.Dispose() | out-null
$imagePath
Exit 1
}
try {
# For WIN10
$file = Get-Clipboard -Format FileDropList
if ($file -ne $null) {
Convert-Path $file
Exit 1
}
} catch {
# For WIN7 WIN8 WIN10
main
}
main
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# grab the paths
scriptPath=$(echo $0 | awk '{ print substr( $0, 1, length($0)-6 ) }')"windows10.ps1"
imagePath=$(echo $1 | awk '{ print substr( $0, 1, length($0)-18 ) }')
imageName=$(echo $1 | awk '{ print substr( $0, length($0)-17, length($0) ) }')
# run the powershell script
res=$(powershell.exe -noprofile -noninteractive -nologo -sta -executionpolicy unrestricted -file $(wslpath -w $scriptPath) $(wslpath -w $imagePath)"\\"$imageName)
# note that there is a return symbol in powershell result
noImage=$(echo "no image\r")
# check whether image exists
if [ "$res" = "$noImage" ] ;then
echo "no image"
else
echo $(wslpath -u $res)
fi
+23
View File
@@ -0,0 +1,23 @@
import { bootstrap } from '~/main/lifeCycle'
bootstrap.launchApp()
/**
* Auto Updater
*
* Uncomment the following code below and install `electron-updater` to
* support auto updating. Code Signing with a valid certificate is required.
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
*/
// import { autoUpdater } from 'electron-updater'
// autoUpdater.on('update-downloaded', () => {
// autoUpdater.quitAndInstall()
// })
// app.on('ready', () => {
// if (process.env.NODE_ENV === 'production') {
// autoUpdater.checkForUpdates()
// }
// })
-52
View File
@@ -1,52 +0,0 @@
import Datastore from 'lowdb'
import LodashId from 'lodash-id'
import FileSync from 'lowdb/adapters/FileSync'
import path from 'path'
import fs from 'fs-extra'
import { remote, app } from 'electron'
if (process.env.NODE_ENV !== 'development') {
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = path.join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')
if (process.type !== 'renderer') {
if (!fs.pathExistsSync(STORE_PATH)) {
fs.mkdirpSync(STORE_PATH)
}
}
const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
const db = Datastore(adapter)
db._.mixin(LodashId)
if (!db.has('uploaded').value()) {
db.set('uploaded', []).write()
}
if (!db.has('picBed').value()) {
db.set('picBed', {
current: 'weibo'
}).write()
}
if (!db.has('settings.shortKey').value()) {
db.set('settings.shortKey', {
upload: 'CommandOrControl+Shift+P'
}).write()
}
// init generate clipboard image files
if (!fs.pathExistsSync(path.join(STORE_PATH, 'windows.ps1'))) {
fs.copySync(path.join(__static, '/linux.sh'), path.join(STORE_PATH, '/linux.sh'))
fs.copySync(path.join(__static, '/mac.applescript'), path.join(STORE_PATH, '/mac.applescript'))
fs.copySync(path.join(__static, '/windows.ps1'), path.join(STORE_PATH, '/windows.ps1'))
}
export default db
-56
View File
@@ -1,56 +0,0 @@
import db from './index'
let picBed = [
{
type: 'weibo',
name: '微博图床',
visible: true
},
{
type: 'qiniu',
name: '七牛图床',
visible: true
},
{
type: 'tcyun',
name: '腾讯云COS',
visible: true
},
{
type: 'upyun',
name: '又拍云图床',
visible: true
},
{
type: 'github',
name: 'GitHub图床',
visible: true
},
{
type: 'smms',
name: 'SM.MS图床',
visible: true
},
{
type: 'aliyun',
name: '阿里云OSS',
visible: true
},
{
type: 'imgur',
name: 'Imgur图床',
visible: true
}
]
let picBedFromDB = db.read().get('picBed.list').value() || []
let oldLength = picBedFromDB.length
let newLength = picBed.length
if (oldLength !== newLength) {
for (let i = oldLength; i < newLength; i++) {
picBedFromDB.push(picBed[i])
}
}
export default picBedFromDB
-24
View File
@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="referrer" content="never">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>PicGo</title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
<script>
require('module').globalPaths.push("<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>")
</script>
<% } %>
</head>
<body>
<div id="app"></div>
<!-- Set `__static` path to static files in production -->
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<!-- webpack builds are automatically injected -->
</body>
</html>
+19 -19
View File
@@ -1,27 +1,22 @@
import Vue from 'vue' import Vue from 'vue'
import axios from 'axios' import App from './renderer/App.vue'
import router from './renderer/router'
import ElementUI from 'element-ui' import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App'
import router from './router'
import store from './store'
import db from '../datastore/index'
import { webFrame } from 'electron' import { webFrame } from 'electron'
import './assets/fonts/iconfont.css' import 'element-ui/lib/theme-chalk/index.css'
import VueLazyLoad from 'vue-lazyload' import VueLazyLoad from 'vue-lazyload'
import axios from 'axios'
Vue.use(ElementUI) import mainMixin from './renderer/utils/mainMixin'
Vue.use(VueLazyLoad) import bus from '@/utils/bus'
import { initTalkingData } from './renderer/utils/analytics'
import db from './renderer/utils/db'
webFrame.setVisualZoomLevelLimits(1, 1) webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0) webFrame.setLayoutZoomLevelLimits(0, 0)
if (!process.env.IS_WEB) Vue.use(require('vue-electron')) Vue.config.productionTip = false
Vue.http = Vue.prototype.$http = axios
Vue.prototype.$db = db
Vue.prototype.$builtInPicBed = [ Vue.prototype.$builtInPicBed = [
'smms', 'smms',
'weibo',
'imgur', 'imgur',
'qiniu', 'qiniu',
'tcyun', 'tcyun',
@@ -29,12 +24,17 @@ Vue.prototype.$builtInPicBed = [
'aliyun', 'aliyun',
'github' 'github'
] ]
Vue.config.productionTip = false Vue.prototype.$$db = db
Vue.prototype.$http = axios
Vue.prototype.$bus = bus
Vue.use(ElementUI)
Vue.use(VueLazyLoad)
Vue.mixin(mainMixin)
/* eslint-disable no-new */
new Vue({ new Vue({
components: { App },
router, router,
store, render: h => h(App)
template: '<App/>'
}).$mount('#app') }).$mount('#app')
initTalkingData()
+13
View File
@@ -0,0 +1,13 @@
# apis folder
## core
The lowest level APIs that are not dependent on each other. The upper APIs depend on them.
## app
Provide key API interfaces for PicGo application, including uploader, window management, shortcut key system, etc
## gui
GuiApi for PicGo plugins.
@@ -0,0 +1,176 @@
import bus from '@core/bus'
import {
globalShortcut
} from 'electron'
import logger from '@core/picgo/logger'
import GuiApi from '../../gui'
import db from '~/main/apis/core/datastore'
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
import shortKeyService from './shortKeyService'
import picgo from '@core/picgo'
class ShortKeyHandler {
private isInModifiedMode: boolean = false
constructor () {
bus.on(TOGGLE_SHORTKEY_MODIFIED_MODE, flag => {
this.isInModifiedMode = flag
})
}
init () {
this.initBuiltInShortKey()
this.initPluginsShortKey()
}
private initBuiltInShortKey () {
const commands = db.get('settings.shortKey') as IShortKeyConfigs
Object.keys(commands)
.filter(item => item.includes('picgo:'))
.forEach(command => {
const config = commands[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)
// if a plugin has commands
if (plugin && plugin.commands) {
if (typeof plugin.commands !== 'function') {
logger.warn(`${item}'s commands is not a function`)
continue
}
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
for (let cmd of commands) {
const command = `${item}:${cmd.name}`
if (db.has(`settings.shortKey[${command}]`)) {
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
// if disabled, don't register #534
if (commandConfig.enable) {
this.registerShortKey(commandConfig, command, cmd.handle, false)
}
} else {
this.registerShortKey(cmd, command, cmd.handle, true)
}
}
} else {
continue
}
}
}
private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) {
shortKeyService.registerCommand(command, handler)
if (config.key) {
globalShortcut.register(config.key, () => {
this.handler(command)
})
} else {
logger.warn(`${command} do not provide a key to bind`)
}
// if the configfile already had this command
// then writeFlag -> false
if (writeFlag) {
picgo.saveConfig({
[`settings.shortKey.${command}`]: {
enable: true,
name: config.name,
label: config.label,
key: config.key
}
})
}
}
// enable or disable shortKey
bindOrUnbindShortKey (item: IShortKeyConfig, from: string): boolean {
const command = `${from}:${item.name}`
if (item.enable === false) {
globalShortcut.unregister(item.key)
picgo.saveConfig({
[`settings.shortKey.${command}.enable`]: false
})
return true
} else {
if (globalShortcut.isRegistered(item.key)) {
return false
} else {
picgo.saveConfig({
[`settings.shortKey.${command}.enable`]: true
})
globalShortcut.register(item.key, () => {
this.handler(command)
})
return true
}
}
}
// update shortKey bindings
updateShortKey (item: IShortKeyConfig, oldKey: string, from: string): boolean {
const command = `${from}:${item.name}`
if (globalShortcut.isRegistered(item.key)) return false
globalShortcut.unregister(oldKey)
picgo.saveConfig({
[`settings.shortKey.${command}.key`]: item.key
})
globalShortcut.register(item.key, () => {
this.handler(`${from}:${item.name}`)
})
return true
}
private async handler (command: string) {
if (this.isInModifiedMode) {
return
}
if (command.includes('picgo:')) {
bus.emit(command)
} else if (command.includes('picgo-plugin-')) {
const handler = shortKeyService.getShortKeyHandler(command)
if (handler) {
return handler(picgo, GuiApi.getInstance())
}
} else {
logger.warn(`can not find command: ${command}`)
}
}
registerPluginShortKey (pluginName: string) {
const plugin = picgo.pluginLoader.getPlugin(pluginName)
if (plugin && plugin.commands) {
if (typeof plugin.commands !== 'function') {
logger.warn(`${pluginName}'s commands is not a function`)
return
}
const commands = plugin.commands(picgo) as IPluginShortKeyConfig[]
for (let cmd of commands) {
const command = `${pluginName}:${cmd.name}`
if (db.has(`settings.shortKey[${command}]`)) {
const commandConfig = db.get(`settings.shortKey[${command}]`) as IShortKeyConfig
this.registerShortKey(commandConfig, command, cmd.handle, false)
} else {
this.registerShortKey(cmd, command, cmd.handle, true)
}
}
}
}
unregisterPluginShortKey (pluginName: string) {
const commands = db.get('settings.shortKey') as IShortKeyConfigs
const keyList = Object.keys(commands)
.filter(command => command.includes(pluginName))
.map(command => {
return {
command,
key: commands[command].key
}
}) as IKeyCommandType[]
keyList.forEach(item => {
globalShortcut.unregister(item.key)
shortKeyService.unregisterCommand(item.command)
picgo.unsetConfig('settings.shortKey', item.command)
})
}
}
export default new ShortKeyHandler()
@@ -0,0 +1,21 @@
import logger from '@core/picgo/logger'
class ShortKeyService {
private commandList: Map<string, IShortKeyHandler> = new Map()
registerCommand (command: string, handler: IShortKeyHandler) {
this.commandList.set(command, handler)
}
unregisterCommand (command: string) {
this.commandList.delete(command)
}
getShortKeyHandler (command: string): IShortKeyHandler | null {
const handler = this.commandList.get(command)
if (handler) return handler
logger.warn(`cannot find command: ${command}`)
return null
}
getCommandList () {
return [...this.commandList.keys()]
}
}
export default new ShortKeyService()
+276
View File
@@ -0,0 +1,276 @@
import {
app,
Menu,
Tray,
dialog,
clipboard,
systemPreferences,
Notification
} from 'electron'
import uploader from 'apis/app/uploader'
import getPicBeds from '~/main/utils/getPicBeds'
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 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()
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`
})
}
},
{
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: '退出'
}
])
} 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()
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`)
})
// drop-files only be supported in macOS
// so the tray window must be available
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], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl!,
icon: files[i]
})
setTimeout(() => {
notification.show()
}, i * 100)
await GalleryDB.getInstance().insert(imgs[i])
}
handleCopyUrl(pasteText.join('\n'))
trayWindow.webContents.send('dragFiles', imgs)
}
})
// toggleWindow()
} else if (process.platform === 'linux') {
// click事件在Ubuntu上无法触发,Unity不支持(在Mac和Windows上可以触发)
// 需要使用 setContextMenu 设置菜单
createContextMenu()
tray!.setContextMenu(contextMenu)
}
}
export function createMenu () {
if (process.env.NODE_ENV !== 'development') {
const template = [{
label: 'Edit',
submenu: [
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
{ type: 'separator' },
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' },
{
label: 'Quit',
accelerator: 'CmdOrCtrl+Q',
click () {
app.quit()
}
}
]
}]
// @ts-ignore
menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
}
const toggleWindow = (bounds: IBounds) => {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
if (trayWindow.isVisible()) {
trayWindow.hide()
} else {
trayWindow.setPosition(bounds.x - 98 + 11, bounds.y, false)
trayWindow.webContents.send('updateFiles')
trayWindow.show()
trayWindow.focus()
}
}
+77
View File
@@ -0,0 +1,77 @@
import {
Notification,
WebContents
} from 'electron'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from 'apis/app/window/constants'
import uploader from '.'
import pasteTemplate from '#/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()
if (img !== false) {
if (img.length > 0) {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
handleCopyUrl(pasteTemplate(pasteStyle, img[0], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl!,
icon: img[0].imgUrl
})
notification.show()
await GalleryDB.getInstance().insert(img[0])
// trayWindow just be created in mac/windows, not in linux
trayWindow?.webContents?.send('clipboardFiles', [])
trayWindow?.webContents?.send('uploadFiles', img)
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
}
return handleUrlEncode(img[0].imgUrl as string)
} else {
const notification = new Notification({
title: '上传不成功',
body: '你剪贴板最新的一条记录不是图片哦'
})
notification.show()
return ''
}
} else {
return ''
}
}
export const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<string[]> => {
const input = files.map(item => item.path)
const imgs = await uploader.setWebContents(webContents).upload(input)
const result = []
if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const pasteText: string[] = []
for (let i = 0; i < imgs.length; i++) {
pasteText.push(pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl!,
icon: files[i].path
})
setTimeout(() => {
notification.show()
}, i * 100)
await GalleryDB.getInstance().insert(imgs[i])
result.push(handleUrlEncode(imgs[i].imgUrl!))
}
handleCopyUrl(pasteText.join('\n'))
// trayWindow just be created in mac/windows, not in linux
windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles', imgs)
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
}
return result
} else {
return []
}
}
+142
View File
@@ -0,0 +1,142 @@
import {
Notification,
BrowserWindow,
ipcMain,
WebContents
} from 'electron'
import dayjs from 'dayjs'
import picgo from '@core/picgo'
import db from '~/main/apis/core/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<void>((resolve, reject) => {
webcontent.on('did-finish-load', () => {
resolve()
})
})
}
const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
return new Promise((resolve, reject) => {
const windowId = window.id
ipcMain.once(`rename${id}`, (evt: Event, newName: string) => {
resolve(newName)
window.close()
})
window.on('close', () => {
resolve(null)
ipcMain.removeAllListeners(`rename${id}`)
windowManager.deleteById(windowId)
})
})
}
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()
}
init () {
picgo.on('notification', message => {
const notification = new Notification(message)
notification.show()
})
picgo.on('uploadProgress', progress => {
this.webContents?.send('uploadProgress', progress)
})
picgo.on('beforeTransform', ctx => {
if (db.get('settings.uploadNotification')) {
const notification = new Notification({
title: '上传进度',
body: '正在上传'
})
notification.show()
}
})
picgo.helper.beforeUploadPlugins.register('renameFn', {
handle: async (ctx: IPicGo) => {
const rename = db.get('settings.rename')
const autoRename = db.get('settings.autoRename')
if (autoRename || rename) {
await Promise.all(ctx.output.map(async (item, index) => {
let name: undefined | string | null
let fileName: string | undefined
if (autoRename) {
fileName = dayjs().add(index, 'ms').format('YYYYMMDDHHmmSSS') + item.extname
} else {
fileName = item.fileName
}
if (rename) {
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
await waitForShow(window.webContents)
window.webContents.send('rename', fileName, window.webContents.id)
name = await waitForRename(window, window.webContents.id)
}
item.fileName = name || fileName
}))
}
}
})
}
setWebContents (webContents: WebContents) {
this.webContents = webContents
return this
}
async upload (img?: IUploadOption): Promise<ImgInfo[]|false> {
try {
const startTime = Date.now()
const output = await picgo.upload(img)
if (Array.isArray(output) && output.some((item: ImgInfo) => item.imgUrl)) {
if (this.webContents) {
handleTalkingData(this.webContents, {
fromClipboard: !img,
type: db.get('picBed.uploader') || db.get('picBed.current') || 'smms',
count: img ? img.length : 1,
duration: Date.now() - startTime
} as IAnalyticsData)
}
return output.filter(item => item.imgUrl)
} else {
return false
}
} catch (e) {
logger.error(e)
setTimeout(() => {
showNotification({
title: '上传失败',
body: util.format(e.stack),
clickToCopy: true
})
}, 500)
return false
}
}
}
export default new Uploader()
+24
View File
@@ -0,0 +1,24 @@
export enum IWindowList {
SETTING_WINDOW = 'SETTING_WINDOW',
TRAY_WINDOW = 'TRAY_WINDOW',
MINI_WINDOW = 'MINI_WINDOW',
RENAME_WINDOW = 'RENAME_WINDOW'
}
const isDevelopment = process.env.NODE_ENV !== 'production'
export const TRAY_WINDOW_URL = isDevelopment
? (process.env.WEBPACK_DEV_SERVER_URL as string)
: `picgo://./index.html`
export const SETTING_WINDOW_URL = isDevelopment
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#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`
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`
+167
View File
@@ -0,0 +1,167 @@
import {
IWindowList,
SETTING_WINDOW_URL,
TRAY_WINDOW_URL,
MINI_WINDOW_URL,
RENAME_WINDOW_URL
} from './constants'
import { IWindowListItem } from '#/types/electron'
import bus from '@core/bus'
import { CREATE_APP_MENU } from '@core/bus/constants'
import db from '~/main/apis/core/datastore'
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
import { app } from 'electron'
const windowList = new Map<IWindowList, IWindowListItem>()
windowList.set(IWindowList.TRAY_WINDOW, {
isValid: process.platform !== 'linux',
multiple: false,
options () {
return {
height: 350,
width: 196, // 196
show: false,
frame: false,
fullscreenable: false,
resizable: false,
transparent: true,
vibrancy: 'ultra-dark',
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
backgroundThrottling: false
}
}
},
callback (window) {
window.loadURL(TRAY_WINDOW_URL)
window.on('blur', () => {
window.hide()
})
}
})
windowList.set(IWindowList.SETTING_WINDOW, {
isValid: true,
multiple: false,
options () {
const options: IBrowserWindowOptions = {
height: 450,
width: 800,
show: false,
frame: true,
center: true,
fullscreenable: false,
resizable: false,
title: 'PicGo',
vibrancy: 'ultra-dark',
transparent: true,
titleBarStyle: 'hidden',
webPreferences: {
backgroundThrottling: false,
nodeIntegration: true,
nodeIntegrationInWorker: true,
webSecurity: false
}
}
if (process.platform !== 'darwin') {
options.show = false
options.frame = false
options.backgroundColor = '#3f3c37'
options.transparent = false
options.icon = `${__static}/logo.png`
}
return options
},
callback (window, windowManager) {
window.loadURL(SETTING_WINDOW_URL)
window.on('closed', () => {
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
if (process.platform === 'linux') {
process.nextTick(() => {
app.quit()
})
}
})
bus.emit(CREATE_APP_MENU)
windowManager.create(IWindowList.MINI_WINDOW)
}
})
windowList.set(IWindowList.MINI_WINDOW, {
isValid: process.platform !== 'darwin',
multiple: false,
options () {
let obj: IBrowserWindowOptions = {
height: 64,
width: 64,
show: process.platform === 'linux',
frame: false,
fullscreenable: false,
skipTaskbar: true,
resizable: false,
transparent: process.platform !== 'linux',
icon: `${__static}/logo.png`,
webPreferences: {
backgroundThrottling: false,
nodeIntegration: true,
nodeIntegrationInWorker: true
}
}
if (db.get('settings.miniWindowOntop')) {
obj.alwaysOnTop = true
}
return obj
},
callback (window) {
window.loadURL(MINI_WINDOW_URL)
}
})
windowList.set(IWindowList.RENAME_WINDOW, {
isValid: true,
multiple: true,
options () {
let options: IBrowserWindowOptions = {
height: 175,
width: 300,
show: true,
fullscreenable: false,
resizable: false,
vibrancy: 'ultra-dark',
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
backgroundThrottling: false
}
}
if (process.platform !== 'darwin') {
options.show = true
options.backgroundColor = '#3f3c37'
options.autoHideMenuBar = true
options.transparent = false
}
return options
},
async callback (window, windowManager) {
window.loadURL(RENAME_WINDOW_URL)
const currentWindow = windowManager.getAvailableWindow()
if (currentWindow && currentWindow.isVisible()) {
// bounds: { x: 821, y: 75, width: 800, height: 450 }
const bounds = currentWindow.getBounds()
const positionX = bounds.x + bounds.width / 2 - 150
let positionY
// if is the settingWindow
if (bounds.height > 400) {
positionY = bounds.y + bounds.height / 2 - 88
} else { // if is the miniWindow
positionY = bounds.y + bounds.height / 2
}
window.setPosition(positionX, positionY, false)
}
}
})
export default windowList
+75
View File
@@ -0,0 +1,75 @@
import {
BrowserWindow
} from 'electron'
import { IWindowManager, IWindowListItem } from '#/types/electron'
import windowList from './windowList'
import {
IWindowList
} from './constants'
class WindowManager implements IWindowManager {
private windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
private windowIdMap: Map<number, IWindowList | string> = new Map()
create (name: IWindowList) {
const windowConfig: IWindowListItem = windowList.get(name)!
if (windowConfig.isValid) {
if (!windowConfig.multiple) {
if (this.has(name)) return this.windowMap.get(name)!
}
const window = new BrowserWindow(windowConfig.options())
const id = window.id
if (windowConfig.multiple) {
this.windowMap.set(`${name}_${window.id}`, window)
this.windowIdMap.set(window.id, `${name}_${window.id}`)
} else {
this.windowMap.set(name, window)
this.windowIdMap.set(window.id, name)
}
windowConfig.callback(window, this)
window.on('close', () => {
this.deleteById(id)
})
return window
} else {
return null
}
}
get (name: IWindowList) {
if (this.has(name)) {
return this.windowMap.get(name)!
} else {
const window = this.create(name)
return window
}
}
has (name: IWindowList) {
return this.windowMap.has(name)
}
// useless
// delete (name: IWindowList) {
// const window = this.windowMap.get(name)
// if (window) {
// this.windowIdMap.delete(window.id)
// this.windowMap.delete(name)
// }
// }
deleteById = (id: number) => {
const name = this.windowIdMap.get(id)
if (name) {
this.windowMap.delete(name)
this.windowIdMap.delete(id)
}
}
getAvailableWindow () {
const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW)
if (miniWindow && miniWindow.isVisible()) {
return miniWindow
} else {
const settingWindow = this.windowMap.get(IWindowList.SETTING_WINDOW)
const trayWindow = this.windowMap.get(IWindowList.TRAY_WINDOW)
return settingWindow || trayWindow || this.create(IWindowList.SETTING_WINDOW)!
}
}
}
export default new WindowManager()
+74
View File
@@ -0,0 +1,74 @@
import bus from '.'
import {
UPLOAD_WITH_FILES,
UPLOAD_WITH_FILES_RESPONSE,
UPLOAD_WITH_CLIPBOARD_FILES,
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
GET_WINDOW_ID,
GET_WINDOW_ID_REPONSE,
GET_SETTING_WINDOW_ID,
GET_SETTING_WINDOW_ID_RESPONSE
} from './constants'
export const uploadWithClipboardFiles = (): Promise<{
success: boolean,
result?: string[]
}> => {
return new Promise((resolve, reject) => {
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
if (result) {
return resolve({
success: true,
result: [result]
})
} else {
return resolve({
success: false
})
}
})
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES)
})
}
export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
success: boolean,
result?: string[]
}> => {
return new Promise((resolve, reject) => {
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
if (result.length) {
return resolve({
success: true,
result
})
} else {
return resolve({
success: false
})
}
})
bus.emit(UPLOAD_WITH_FILES, pathList)
})
}
// get available window id:
// miniWindow or settingWindow or trayWindow
export const getWindowId = (): Promise<number> => {
return new Promise((resolve, reject) => {
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
resolve(id)
})
bus.emit(GET_WINDOW_ID)
})
}
// get settingWindow id:
export const getSettingWindowId = (): Promise<number> => {
return new Promise((resolve, reject) => {
bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => {
resolve(id)
})
bus.emit(GET_SETTING_WINDOW_ID)
})
}
+9
View File
@@ -0,0 +1,9 @@
export const GET_WINDOW_ID = 'GET_WINDOW_ID' // get a current window
export const GET_WINDOW_ID_REPONSE = 'GET_WINDOW_ID_REPONSE'
export const GET_SETTING_WINDOW_ID = 'GET_SETTING_WINDOW_ID' // get setting window
export const GET_SETTING_WINDOW_ID_RESPONSE = 'GET_SETTING_WINDOW_ID_RESPONSE'
export const UPLOAD_WITH_FILES = 'UPLOAD_WITH_FILES'
export const UPLOAD_WITH_FILES_RESPONSE = 'UPLOAD_WITH_FILES_RESPONSE'
export const UPLOAD_WITH_CLIPBOARD_FILES = 'UPLOAD_WITH_CLIPBOARD_FILES'
export const UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE = 'UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE'
export const CREATE_APP_MENU = 'CREATE_APP_MENU'
+5
View File
@@ -0,0 +1,5 @@
import { EventEmitter } from 'events'
const bus = new EventEmitter()
export default bus
+137
View File
@@ -0,0 +1,137 @@
import fs from 'fs-extra'
import path from 'path'
import { remote, app } from 'electron'
import dayjs from 'dayjs'
import { getLogger } from '@core/utils/localLogger'
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')
export const defaultConfigPath = configFilePath
let _configFilePath = ''
let hasCheckPath = false
const errorMsg = {
broken: 'PicGo 配置文件损坏,已经恢复为默认配置',
brokenButBackup: 'PicGo 配置文件损坏,已经恢复为备份配置'
}
/** ensure notification list */
if (!global.notificationList) global.notificationList = []
function dbChecker () {
if (process.type !== 'renderer') {
// db save bak
try {
const { dbPath, dbBackupPath } = getGalleryDBPath()
if (fs.existsSync(dbPath)) {
fs.copyFileSync(dbPath, dbBackupPath)
}
} catch (e) {
console.error(e)
}
const configFilePath = dbPathChecker()
if (!fs.existsSync(configFilePath)) {
return
}
let configFile: string = '{}'
let optionsTpl = {
title: '注意',
body: ''
}
// config save bak
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' })
}
}
/**
* Get config path
*/
function dbPathChecker (): string {
if (_configFilePath) {
return _configFilePath
}
// defaultConfigPath
_configFilePath = defaultConfigPath
// if defaultConfig path is not exit
// do not parse the content of config
if (!fs.existsSync(defaultConfigPath)) {
return _configFilePath
}
try {
const configString = fs.readFileSync(defaultConfigPath, { encoding: 'utf-8' })
const config = JSON.parse(configString)
const userConfigPath: string = config.configPath || ''
if (userConfigPath) {
if (fs.existsSync(userConfigPath) && userConfigPath.endsWith('.json')) {
_configFilePath = userConfigPath
return _configFilePath
}
}
return _configFilePath
} catch (e) {
const picgoLogPath = path.join(defaultConfigPath, 'picgo.log')
const logger = getLogger(picgoLogPath)
if (!hasCheckPath) {
let optionsTpl = {
title: '注意',
body: '自定义文件解析出错,请检查路径内容是否正确'
}
global.notificationList?.push(optionsTpl)
hasCheckPath = true
}
logger('error', e)
console.error(e)
_configFilePath = defaultConfigPath
return _configFilePath
}
}
function dbPathDir () {
return path.dirname(dbPathChecker())
}
function getGalleryDBPath (): {
dbPath: string
dbBackupPath: string
} {
const configPath = dbPathChecker()
const dbPath = path.join(path.dirname(configPath), 'picgo.db')
const dbBackupPath = path.join(path.dirname(dbPath), 'picgo.bak.db')
return {
dbPath,
dbBackupPath
}
}
export {
dbChecker,
dbPathChecker,
dbPathDir,
getGalleryDBPath
}
+95
View File
@@ -0,0 +1,95 @@
import Datastore from 'lowdb'
// @ts-ignore
import LodashId from 'lodash-id'
import FileSync from 'lowdb/adapters/FileSync'
import fs from 'fs-extra'
import { dbPathChecker, dbPathDir, getGalleryDBPath } from './dbChecker'
import { DBStore } from '@picgo/store'
const STORE_PATH = dbPathDir()
if (!fs.pathExistsSync(STORE_PATH)) {
fs.mkdirpSync(STORE_PATH)
}
const CONFIG_PATH: string = dbPathChecker()
const DB_PATH: string = getGalleryDBPath().dbPath
// TODO: use JSONStore with @picgo/store
class ConfigStore {
private db: Datastore.LowdbSync<Datastore.AdapterSync>
constructor () {
const adapter = new FileSync(CONFIG_PATH)
this.db = Datastore(adapter)
this.db._.mixin(LodashId)
if (!this.db.has('picBed').value()) {
this.db.set('picBed', {
current: 'smms', // deprecated
uploader: 'smms',
smms: {
token: ''
}
}).write()
}
if (!this.db.has('settings.shortKey').value()) {
this.db.set('settings.shortKey[picgo:upload]', {
enable: true,
key: 'CommandOrControl+Shift+P',
name: 'upload',
label: '快捷上传'
}).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
}
}
export default new ConfigStore()
// v2.3.0 add gallery db
class GalleryDB {
private static instance: DBStore
private constructor () {
console.log('init gallery db')
}
public static getInstance (): DBStore {
if (!GalleryDB.instance) {
GalleryDB.instance = new DBStore(DB_PATH, 'gallery')
}
return GalleryDB.instance
}
}
export {
GalleryDB
}
+21
View File
@@ -0,0 +1,21 @@
import PicGoCore from '~/universal/types/picgo'
import { dbChecker, dbPathChecker } from 'apis/core/datastore/dbChecker'
import pkg from 'root/package.json'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo') as typeof PicGoCore
const CONFIG_PATH = dbPathChecker()
dbChecker()
const picgo = new PicGo(CONFIG_PATH)
picgo.saveConfig({
debug: true,
PICGO_ENV: 'GUI'
})
global.PICGO_GUI_VERSION = pkg.version
picgo.GUI_VERSION = global.PICGO_GUI_VERSION
export default picgo! as PicGoCore
+5
View File
@@ -0,0 +1,5 @@
import picgo from '.'
const logger = picgo.log
export default logger
+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
}
+179
View File
@@ -0,0 +1,179 @@
import {
dialog,
BrowserWindow,
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 { handleCopyUrl } from '~/main/utils/common'
import {
getWindowId,
getSettingWindowId
} from '@core/bus/apis'
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 {
private static instance: GuiApi
private windowId: number = -1
private settingWindowId: number = -1
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()) {
return true
}
settingWindow.show()
return new Promise<void>((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000) // TODO: a better way to wait page loaded.
})
}
private getWebcontentsByWindowId (id: number) {
return BrowserWindow.fromId(id).webContents
}
async showInputBox (options: IShowInputBoxOption = {
title: '',
placeholder: ''
}) {
await this.showSettingWindow()
this.getWebcontentsByWindowId(this.settingWindowId)
.send(SHOW_INPUT_BOX, options)
return new Promise<string>((resolve, reject) => {
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 upload (input: IUploadOption) {
this.windowId = await getWindowId()
const webContents = this.getWebcontentsByWindowId(this.windowId)
const imgs = await uploader.setWebContents(webContents).upload(input)
if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const pasteText: string[] = []
for (let i = 0; i < imgs.length; i++) {
pasteText.push(pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl as string,
icon: imgs[i].imgUrl
})
setTimeout(() => {
notification.show()
}, i * 100)
await GalleryDB.getInstance().insert(imgs[i])
}
handleCopyUrl(pasteText.join('\n'))
webContents.send('uploadFiles', imgs)
webContents.send('updateGallery')
return imgs
}
return []
}
showNotification (options: IShowNotificationOption = {
title: '',
body: ''
}) {
const notification = new Notification({
title: options.title,
body: options.body
})
notification.show()
}
showMessageBox (options: IShowMessageBoxOption = {
title: '',
message: '',
type: 'info',
buttons: ['Yes', 'No']
}) {
return new Promise<IShowMessageBoxResult>(async (resolve, reject) => {
this.windowId = await getWindowId()
dialog.showMessageBox(
BrowserWindow.fromId(this.windowId),
options
).then((res) => {
resolve({
result: res.response,
checkboxChecked: res.checkboxChecked
})
})
})
}
/**
* get picgo config/data path
*/
async getConfigPath () {
const currentConfigPath = dbPathChecker()
const galleryDBPath = getGalleryDBPath().dbPath
return {
defaultConfigPath,
currentConfigPath,
galleryDBPath
}
}
get galleryDB (): DBStore {
return new Proxy<DBStore>(GalleryDB.getInstance(), {
get (target, prop: keyof DBStore) {
if (prop === 'removeById') {
return new Proxy(GalleryDB.getInstance().removeById, {
apply (target, ctx, args) {
return new Promise((resolve) => {
const guiApi = GuiApi.getInstance()
guiApi.showMessageBox({
title: '警告',
message: '有插件正在试图删除一些相册图片,是否继续',
type: 'info',
buttons: ['Yes', 'No']
}).then(res => {
if (res.result === 0) {
resolve(Reflect.apply(target, ctx, args))
} else {
resolve(undefined)
}
})
})
}
})
}
return Reflect.get(target, prop)
}
})
}
}
export default GuiApi
+61
View File
@@ -0,0 +1,61 @@
import bus from '@core/bus'
import {
uploadClipboardFiles,
uploadChoosedFiles
} from 'apis/app/uploader/apis'
import {
createMenu
} from 'apis/app/system'
import { IWindowList } from 'apis/app/window/constants'
import windowManager from 'apis/app/window/windowManager'
import {
UPLOAD_WITH_FILES,
UPLOAD_WITH_FILES_RESPONSE,
UPLOAD_WITH_CLIPBOARD_FILES,
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
GET_WINDOW_ID,
GET_WINDOW_ID_REPONSE,
GET_SETTING_WINDOW_ID,
GET_SETTING_WINDOW_ID_RESPONSE,
CREATE_APP_MENU
} from '@core/bus/constants'
function initEventCenter () {
const eventList: any = {
'picgo:upload': uploadClipboardFiles,
[UPLOAD_WITH_CLIPBOARD_FILES]: busCallUploadClipboardFiles,
[UPLOAD_WITH_FILES]: busCallUploadFiles,
[GET_WINDOW_ID]: busCallGetWindowId,
[GET_SETTING_WINDOW_ID]: busCallGetSettingWindowId,
[CREATE_APP_MENU]: createMenu
}
for (let i in eventList) {
bus.on(i, eventList[i])
}
}
async function busCallUploadClipboardFiles () {
const imgUrl = await uploadClipboardFiles()
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, imgUrl)
}
async function busCallUploadFiles (pathList: IFileWithPath[]) {
const win = windowManager.getAvailableWindow()
const urls = await uploadChoosedFiles(win.webContents, pathList)
bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls)
}
function busCallGetWindowId () {
const win = windowManager.getAvailableWindow()
bus.emit(GET_WINDOW_ID_REPONSE, win.id)
}
function busCallGetSettingWindowId () {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
bus.emit(GET_SETTING_WINDOW_ID_RESPONSE, settingWindow.id)
}
export default {
listen () {
initEventCenter()
}
}
+150
View File
@@ -0,0 +1,150 @@
import {
app,
ipcMain,
Notification,
IpcMainEvent
} 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 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,
OPEN_DEVTOOLS
} from '#/events/constants'
import {
uploadClipboardFiles,
uploadChoosedFiles
} from '~/main/apis/app/uploader/apis'
import picgoCoreIPC from './picgoCoreIPC'
import { handleCopyUrl } from '~/main/utils/common'
export default {
listen () {
picgoCoreIPC.listen()
// from macOS tray
ipcMain.on('uploadClipboardFiles', async () => {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
const img = await uploader.setWebContents(trayWindow.webContents).upload()
if (img !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
handleCopyUrl(pasteTemplate(pasteStyle, img[0], db.get('settings.customLink')))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl!,
// icon: file[0]
icon: img[0].imgUrl
})
notification.show()
await GalleryDB.getInstance().insert(img[0])
trayWindow.webContents.send('clipboardFiles', [])
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
}
}
trayWindow.webContents.send('uploadFiles')
})
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
uploadClipboardFiles()
})
ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: IFileWithPath[]) => {
return uploadChoosedFiles(evt.sender, files)
})
ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, oldKey: string, from: string) => {
const result = shortKeyHandler.updateShortKey(item, oldKey, from)
evt.sender.send('updateShortKeyResponse', result)
if (result) {
const notification = new Notification({
title: '操作成功',
body: '你的快捷键已经修改成功'
})
notification.show()
} else {
const notification = new Notification({
title: '操作失败',
body: '快捷键冲突,请重新设置'
})
notification.show()
}
})
ipcMain.on('bindOrUnbindShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, from: string) => {
const result = shortKeyHandler.bindOrUnbindShortKey(item, from)
if (result) {
const notification = new Notification({
title: '操作成功',
body: '你的快捷键已经修改成功'
})
notification.show()
} else {
const notification = new Notification({
title: '操作失败',
body: '快捷键冲突,请重新设置'
})
notification.show()
}
})
ipcMain.on('updateCustomLink', () => {
const notification = new Notification({
title: '操作成功',
body: '你的自定义链接格式已经修改成功'
})
notification.show()
})
ipcMain.on('autoStart', (evt: IpcMainEvent, val: boolean) => {
app.setLoginItemSettings({
openAtLogin: val
})
})
ipcMain.on('openSettingWindow', () => {
windowManager.get(IWindowList.SETTING_WINDOW)!.show()
if (windowManager.has(IWindowList.MINI_WINDOW)) {
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
}
})
ipcMain.on('openMiniWindow', () => {
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
miniWindow.show()
miniWindow.focus()
settingWindow.hide()
})
// from mini window
ipcMain.on('syncPicBed', () => {
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
})
ipcMain.on('getPicBeds', (evt: IpcMainEvent) => {
const picBeds = getPicBeds()
evt.sender.send('getPicBeds', picBeds)
evt.returnValue = picBeds
})
ipcMain.on(TOGGLE_SHORTKEY_MODIFIED_MODE, (evt: IpcMainEvent, val: boolean) => {
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, val)
})
ipcMain.on('updateServer', () => {
server.restart()
})
ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => {
event.sender.openDevTools()
})
},
dispose () {}
}
+346
View File
@@ -0,0 +1,346 @@
import path from 'path'
import GuiApi from 'apis/gui'
import {
dialog,
shell,
IpcMainEvent,
ipcMain
} from 'electron'
import PicGoCore from '~/universal/types/picgo'
import { IPicGoHelperType } from '#/types/enum'
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'
import { dbPathChecker } from 'apis/core/datastore/dbChecker'
import {
PICGO_SAVE_CONFIG,
PICGO_GET_CONFIG,
PICGO_GET_DB,
PICGO_INSERT_DB,
PICGO_INSERT_MANY_DB,
PICGO_UPDATE_BY_ID_DB,
PICGO_GET_BY_ID_DB,
PICGO_REMOVE_BY_ID_DB,
PICGO_OPEN_FILE
} from '#/events/constants'
import { GalleryDB } from 'apis/core/datastore'
import { IObject, IFilter } from '@picgo/store/dist/types'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
// const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
const STORE_PATH = 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>
}
// get uploader or transformer config
const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
let config: any[] = []
if (name === '') {
return config
} else {
const handler = ctx.helper[type].get(name)
if (handler) {
if (handler.config) {
config = handler.config(ctx)
}
}
return config
}
}
const handleConfigWithFunction = (config: any[]) => {
for (let i in config) {
if (typeof config[i].default === 'function') {
config[i].default = config[i].default()
}
if (typeof config[i].choices === 'function') {
config[i].choices = config[i].choices()
}
}
return config
}
const 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 list = getPluginList()
event.sender.send('pluginList', list)
})
}
const handlePluginInstall = () => {
ipcMain.on('installPlugin', async (event: IpcMainEvent, fullName: string) => {
const dispose = handleNPMError()
const res = await picgo.pluginHandler.install([fullName])
event.sender.send('installPlugin', {
success: res.success,
body: fullName,
errMsg: res.success ? '' : res.body
})
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()
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()
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 handleNPMError = (): IDispose => {
const handler = (msg: string) => {
if (msg === 'NPM is not installed') {
dialog.showMessageBox({
title: '发生错误',
message: '请安装Node.js并重启PicGo再继续操作',
buttons: ['Yes']
}).then((res) => {
if (res.response === 0) {
shell.openExternal('https://nodejs.org/')
}
})
}
}
picgo.once('failed', handler)
return () => picgo.off('failed', handler)
}
const handleGetPicBedConfig = () => {
ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
const name = picgo.helper.uploader.get(type)?.name || type
if (picgo.helper.uploader.get(type)?.config) {
const config = handleConfigWithFunction(picgo.helper.uploader.get(type)!.config(picgo))
event.sender.send('getPicBedConfig', config, name)
} else {
event.sender.send('getPicBedConfig', [], name)
}
})
}
const handlePluginActions = () => {
ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
const plugin = picgo.pluginLoader.getPlugin(name)
if (plugin?.guiMenu?.(picgo)?.length) {
const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
menu.forEach(item => {
if (item.label === label) {
item.handle(picgo, GuiApi.getInstance())
}
})
}
})
}
const handleRemoveFiles = () => {
ipcMain.on('removeFiles', (event: IpcMainEvent, files: ImgInfo[]) => {
setTimeout(() => {
picgo.emit('remove', files, GuiApi.getInstance())
}, 500)
})
}
const handlePicGoSaveConfig = () => {
ipcMain.on(PICGO_SAVE_CONFIG, (event: IpcMainEvent, data: IObj) => {
picgo.saveConfig(data)
})
}
const handlePicGoGetConfig = () => {
ipcMain.on(PICGO_GET_CONFIG, (event: IpcMainEvent, key: string | undefined, callbackId: string) => {
const result = picgo.getConfig(key)
event.sender.send(PICGO_GET_CONFIG, result, callbackId)
})
}
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')
})
})
}
const handlePicGoGalleryDB = () => {
ipcMain.on(PICGO_GET_DB, async (event: IpcMainEvent, filter: IFilter, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.get(filter)
event.sender.send(PICGO_GET_DB, res, callbackId)
})
ipcMain.on(PICGO_INSERT_DB, async (event: IpcMainEvent, value: IObject, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.insert(value)
event.sender.send(PICGO_INSERT_DB, res, callbackId)
})
ipcMain.on(PICGO_INSERT_MANY_DB, async (event: IpcMainEvent, value: IObject[], callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.insertMany(value)
event.sender.send(PICGO_INSERT_MANY_DB, res, callbackId)
})
ipcMain.on(PICGO_UPDATE_BY_ID_DB, async (event: IpcMainEvent, id: string, value: IObject[], callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.updateById(id, value)
event.sender.send(PICGO_UPDATE_BY_ID_DB, res, callbackId)
})
ipcMain.on(PICGO_GET_BY_ID_DB, async (event: IpcMainEvent, id: string, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.getById(id)
event.sender.send(PICGO_GET_BY_ID_DB, res, callbackId)
})
ipcMain.on(PICGO_REMOVE_BY_ID_DB, async (event: IpcMainEvent, id: string, callbackId: string) => {
const dbStore = GalleryDB.getInstance()
const res = await dbStore.removeById(id)
event.sender.send(PICGO_REMOVE_BY_ID_DB, res, callbackId)
})
}
const handleOpenFile = () => {
ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => {
const abFilePath = path.join(STORE_PATH, fileName)
shell.openItem(abFilePath)
})
}
export default {
listen () {
handleGetPluginList()
handlePluginInstall()
handlePluginUninstall()
handlePluginUpdate()
handleGetPicBedConfig()
handlePluginActions()
handleRemoveFiles()
handlePicGoSaveConfig()
handlePicGoGetConfig()
handlePicGoGalleryDB()
handleImportLocalPlugin()
handleOpenFile()
}
}
-24
View File
@@ -1,24 +0,0 @@
/**
* This file is used specifically and only for development. It installs
* `electron-debug` & `vue-devtools`. There shouldn't be any need to
* modify this file, but it can be used to extend your development
* environment.
*/
/* eslint-disable */
// Install `electron-debug` with `devtron`
require('electron-debug')({ showDevTools: false })
// Install `vue-devtools`
require('electron').app.on('ready', () => {
let installExtension = require('electron-devtools-installer')
installExtension.default(installExtension.VUEJS_DEVTOOLS)
.then(() => {})
.catch(err => {
console.log('Unable to install `vue-devtools`: \n', err)
})
})
// Require `main` process to boot app
require('./index')
-557
View File
@@ -1,557 +0,0 @@
'use strict'
import Uploader from './utils/uploader.js'
import { app, BrowserWindow, Tray, Menu, Notification, clipboard, ipcMain, globalShortcut, dialog } from 'electron'
import db from '../datastore'
import pasteTemplate from './utils/pasteTemplate'
import updateChecker from './utils/updateChecker'
import { getPicBeds } from './utils/getPicBeds'
import pkg from '../../package.json'
import picgoCoreIPC from './utils/picgoCoreIPC'
import fixPath from 'fix-path'
/**
* Set `__static` path to static files in production
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
*/
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = require('path').join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
let window
let settingWindow
let miniWindow
let tray
let menu
let contextMenu
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080`
: `file://${__dirname}/index.html`
const settingWinURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/#setting/upload`
: `file://${__dirname}/index.html#setting/upload`
const miniWinURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/#mini-page`
: `file://${__dirname}/index.html#mini-page`
// fix the $PATH in macOS
fixPath()
function createContextMenu () {
const picBeds = getPicBeds(app)
const submenu = picBeds.map(item => {
return {
label: item.name,
type: 'radio',
checked: db.read().get('picBed.current').value() === item.type,
click () {
db.read().set('picBed.current', item.type).write()
if (settingWindow) {
settingWindow.webContents.send('syncPicBed')
}
}
}
})
contextMenu = Menu.buildFromTemplate([
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '打开详细窗口',
click () {
if (settingWindow === null) {
createSettingWindow()
settingWindow.show()
} else {
settingWindow.show()
settingWindow.focus()
}
if (miniWindow) {
miniWindow.hide()
}
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu
},
{
label: '打开更新助手',
type: 'checkbox',
checked: db.get('settings.showUpdateTip').value(),
click () {
const value = db.read().get('settings.showUpdateTip').value()
db.read().set('settings.showUpdateTip', !value).write()
}
},
{
role: 'quit',
label: '退出'
}
])
}
function createTray () {
const menubarPic = process.platform === 'darwin' ? `${__static}/menubar.png` : `${__static}/menubar-nodarwin.png`
tray = new Tray(menubarPic)
tray.on('right-click', () => {
if (window) {
window.hide()
}
createContextMenu()
tray.popUpContextMenu(contextMenu)
})
tray.on('click', (event, bounds) => {
if (process.platform === 'darwin') {
let img = clipboard.readImage()
let obj = []
if (!img.isEmpty()) {
// 从剪贴板来的图片默认转为png
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
obj.push({
width: img.getSize().width,
height: img.getSize().height,
imgUrl
})
}
toggleWindow(bounds)
setTimeout(() => {
window.webContents.send('clipboardFiles', obj)
}, 0)
} else {
if (window) {
window.hide()
}
if (settingWindow === null) {
createSettingWindow()
settingWindow.show()
} else {
settingWindow.show()
settingWindow.focus()
}
if (miniWindow) {
miniWindow.hide()
}
}
})
tray.on('drag-enter', () => {
tray.setImage(`${__static}/upload.png`)
})
tray.on('drag-end', () => {
tray.setImage(`${__static}/menubar.png`)
})
tray.on('drop-files', async (event, files) => {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
const imgs = await new Uploader(files, window.webContents).upload()
if (imgs !== false) {
for (let i in imgs) {
const url = imgs[i].url || imgs[i].imgUrl
clipboard.writeText(pasteTemplate(pasteStyle, url))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i]
})
setTimeout(() => {
notification.show()
}, i * 100)
db.read().get('uploaded').insert(imgs[i]).write()
}
window.webContents.send('dragFiles', imgs)
}
})
// toggleWindow()
}
const createWindow = () => {
if (process.platform !== 'darwin' && process.platform !== 'win32') {
return
}
window = new BrowserWindow({
height: 350,
width: 196, // 196
show: false,
frame: false,
fullscreenable: false,
resizable: false,
transparent: true,
vibrancy: 'ultra-dark',
webPreferences: {
backgroundThrottling: false
}
})
window.loadURL(winURL)
window.on('closed', () => {
window = null
})
window.on('blur', () => {
window.hide()
})
}
const createMiniWidow = () => {
if (miniWindow) {
return false
}
let obj = {
height: 64,
width: 64,
show: true,
frame: false,
fullscreenable: false,
resizable: false,
transparent: true,
icon: `${__static}/logo.png`,
webPreferences: {
backgroundThrottling: false
}
}
if (process.platform === 'linux') {
obj.transparent = false
}
if (process.platform === 'darwin') {
obj.show = false
}
if (db.read().get('miniWindowOntop').value()) {
obj.alwaysOnTop = true
}
miniWindow = new BrowserWindow(obj)
miniWindow.loadURL(miniWinURL)
miniWindow.on('closed', () => {
miniWindow = null
})
}
const createSettingWindow = () => {
const options = {
height: 450,
width: 800,
show: false,
frame: true,
center: true,
fullscreenable: false,
resizable: false,
title: 'PicGo',
vibrancy: 'ultra-dark',
transparent: true,
titleBarStyle: 'hidden',
webPreferences: {
backgroundThrottling: false,
webSecurity: false
}
}
if (process.platform !== 'darwin') {
options.show = false
options.frame = false
options.backgroundColor = '#3f3c37'
options.transparent = false
options.icon = `${__static}/logo.png`
}
settingWindow = new BrowserWindow(options)
settingWindow.loadURL(settingWinURL)
settingWindow.on('closed', () => {
settingWindow = null
if (process.platform === 'linux') {
app.quit()
}
})
createMenu()
createMiniWidow()
}
const createMenu = () => {
if (process.env.NODE_ENV !== 'development') {
const template = [{
label: 'Edit',
submenu: [
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
{ type: 'separator' },
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' },
{
label: 'Quit',
accelerator: 'CmdOrCtrl+Q',
click () {
app.quit()
}
}
]
}]
menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
}
const toggleWindow = (bounds) => {
if (window.isVisible()) {
window.hide()
} else {
showWindow(bounds)
}
}
const showWindow = (bounds) => {
window.setPosition(bounds.x - 98 + 11, bounds.y, false)
window.webContents.send('updateFiles')
window.show()
window.focus()
}
const uploadClipboardFiles = async () => {
let win
if (miniWindow.isVisible()) {
win = miniWindow
} else {
win = settingWindow || window
}
let img = await new Uploader(undefined, win.webContents).upload()
if (img !== false) {
if (img.length > 0) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
const url = img[0].url || img[0].imgUrl
clipboard.writeText(pasteTemplate(pasteStyle, url))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl,
icon: img[0].imgUrl
})
notification.show()
db.read().get('uploaded').insert(img[0]).write()
window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles', img)
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
} else {
const notification = new Notification({
title: '上传不成功',
body: '你剪贴板最新的一条记录不是图片哦'
})
notification.show()
}
}
}
picgoCoreIPC(app, ipcMain)
// from macOS tray
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
const img = await new Uploader(file, window.webContents).upload()
if (img !== false) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
const url = img[0].url || img[0].imgUrl
clipboard.writeText(pasteTemplate(pasteStyle, url))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl,
// icon: file[0]
icon: img[0].imgUrl
})
notification.show()
db.read().get('uploaded').insert(img[0]).write()
window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles')
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
}
})
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
uploadClipboardFiles()
})
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
const input = files.map(item => item.path)
const imgs = await new Uploader(input, evt.sender).upload()
if (imgs !== false) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
let pasteText = ''
for (let i in imgs) {
const url = imgs[i].url || imgs[i].imgUrl
pasteText += pasteTemplate(pasteStyle, url) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i].path
})
setTimeout(() => {
notification.show()
}, i * 100)
db.read().get('uploaded').insert(imgs[i]).write()
}
clipboard.writeText(pasteText)
window.webContents.send('uploadFiles', imgs)
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
}
})
ipcMain.on('updateShortKey', (evt, oldKey) => {
globalShortcut.unregisterAll()
for (let key in oldKey) {
globalShortcut.register(db.read().get('settings.shortKey').value()[key], () => {
return shortKeyHash[key]()
})
}
const notification = new Notification({
title: '操作成功',
body: '你的快捷键已经修改成功'
})
notification.show()
})
ipcMain.on('updateCustomLink', (evt, oldLink) => {
const notification = new Notification({
title: '操作成功',
body: '你的自定义链接格式已经修改成功'
})
notification.show()
})
ipcMain.on('autoStart', (evt, val) => {
app.setLoginItemSettings({
openAtLogin: val
})
})
ipcMain.on('openSettingWindow', (evt) => {
if (!settingWindow) {
createSettingWindow()
} else {
settingWindow.show()
}
miniWindow.hide()
})
ipcMain.on('openMiniWindow', (evt) => {
if (!miniWindow) {
createMiniWidow()
}
miniWindow.show()
miniWindow.focus()
settingWindow.hide()
})
// from mini window
ipcMain.on('syncPicBed', (evt) => {
if (settingWindow) {
settingWindow.webContents.send('syncPicBed')
}
})
ipcMain.on('getPicBeds', (evt) => {
const picBeds = getPicBeds(app)
evt.sender.send('getPicBeds', picBeds)
evt.returnValue = picBeds
})
const shortKeyHash = {
upload: uploadClipboardFiles
}
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
if (settingWindow) {
if (settingWindow.isMinimized()) {
settingWindow.restore()
}
settingWindow.focus()
}
}
if (process.platform === 'win32') {
app.setAppUserModelId(pkg.build.appId)
}
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
process.env.XDG_CURRENT_DESKTOP = 'Unity'
}
app.on('ready', () => {
createWindow()
createSettingWindow()
if (process.platform === 'darwin' || process.platform === 'win32') {
createTray()
}
db.read().set('needReload', false).write()
updateChecker()
globalShortcut.register(db.read().get('settings.shortKey.upload').value(), () => {
uploadClipboardFiles()
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (window === null) {
createWindow()
}
if (settingWindow === null) {
createSettingWindow()
}
})
app.on('will-quit', () => {
globalShortcut.unregisterAll()
})
app.setLoginItemSettings({
openAtLogin: db.read().get('settings.autoStart').value() || false
})
/**
* Auto Updater
*
* Uncomment the following code below and install `electron-updater` to
* support auto updating. Code Signing with a valid certificate is required.
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
*/
// import { autoUpdater } from 'electron-updater'
// autoUpdater.on('update-downloaded', () => {
// autoUpdater.quitAndInstall()
// })
// app.on('ready', () => {
// if (process.env.NODE_ENV === 'production') {
// autoUpdater.checkForUpdates()
// }
// })
+33
View File
@@ -0,0 +1,33 @@
import fse from 'fs-extra'
import path from 'path'
import dayjs from 'dayjs'
import util from 'util'
import { dbPathDir } from 'apis/core/datastore/dbChecker'
const STORE_PATH = dbPathDir()
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
export 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)
})
+191
View File
@@ -0,0 +1,191 @@
import './errorHandler'
import {
app,
globalShortcut,
protocol,
Notification
} from 'electron'
import {
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'
import busEventList from '~/main/events/busEventList'
import { IWindowList } from 'apis/app/window/constants'
import windowManager from 'apis/app/window/windowManager'
import {
updateShortKeyFromVersion212,
migrateGalleryFromVersion230
} from '~/main/migrate'
import {
uploadChoosedFiles,
uploadClipboardFiles
} from 'apis/app/uploader/apis'
import {
createTray
} from 'apis/app/system'
import server from '~/main/server/index'
import updateChecker from '~/main/utils/updateChecker'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import { getUploadFiles } from '~/main/utils/handleArgv'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import bus from '@core/bus'
import { privacyManager } from '~/main/utils/privacyManager'
import logger from 'apis/core/picgo/logger'
import picgo from 'apis/core/picgo'
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 async beforeReady () {
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
// fix the $PATH in macOS
fixPath()
beforeOpen()
ipcList.listen()
busEventList.listen()
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
await migrateGalleryFromVersion230(db, GalleryDB.getInstance(), picgo)
}
private onReady () {
const readyFunction = async () => {
console.log('on ready')
createProtocol('picgo')
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
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)
createTray()
db.set('needReload', false)
updateChecker()
// 不需要阻塞
process.nextTick(() => {
shortKeyHandler.init()
})
server.startup()
if (process.env.NODE_ENV !== 'development') {
handleStartUpFiles(process.argv, process.cwd())
}
if (global.notificationList && global.notificationList?.length > 0) {
while (global.notificationList?.length) {
const option = global.notificationList.pop()
const notice = new Notification(option!)
notice.show()
}
}
}
if (!app.isReady()) {
app.on('ready', readyFunction)
} else {
readyFunction()
}
}
private onRunning () {
app.on('second-instance', (event, commandLine, workingDirectory) => {
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()) {
settingWindow.restore()
}
settingWindow.focus()
}
}
})
app.on('activate', () => {
createProtocol('picgo')
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
windowManager.create(IWindowList.TRAY_WINDOW)
}
if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.create(IWindowList.SETTING_WINDOW)
}
})
app.setLoginItemSettings({
openAtLogin: db.get('settings.autoStart') || false
})
if (process.platform === 'win32') {
app.setAppUserModelId('com.molunerfinn.picgo')
}
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
process.env.XDG_CURRENT_DESKTOP = 'Unity'
}
}
private onQuit () {
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('will-quit', () => {
globalShortcut.unregisterAll()
bus.removeAllListeners()
server.shutdown()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', data => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
}
async launchApp () {
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
await this.beforeReady()
this.onReady()
this.onRunning()
this.onQuit()
}
}
}
const bootstrap = new LifeCycle()
export {
bootstrap
}
+54
View File
@@ -0,0 +1,54 @@
import { DBStore } from '@picgo/store'
import ConfigStore from '~/main/apis/core/datastore'
import path from 'path'
import fse from 'fs-extra'
import PicGoCore from '#/types/picgo'
// from v2.1.2
const updateShortKeyFromVersion212 = (db: typeof ConfigStore, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
// #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) {
// @ts-ignore
shortKeyConfig['picgo:upload'] = {
enable: true,
key: shortKeyConfig.upload,
name: 'upload',
label: '快捷上传'
}
// @ts-ignore
delete shortKeyConfig.upload
db.set('settings.shortKey', shortKeyConfig)
return true
}
return false
}
const migrateGalleryFromVersion230 = async (configDB: typeof ConfigStore, galleryDB: DBStore, picgo: PicGoCore) => {
const originGallery: ImgInfo[] = configDB.get('uploaded')
const configPath = configDB.getConfigPath()
const configBakPath = path.join(path.dirname(configPath), 'config.bak.json')
// migrate gallery from config to gallery db
if (originGallery && Array.isArray(originGallery) && originGallery?.length > 0) {
if (fse.existsSync(configBakPath)) {
fse.copyFileSync(configPath, configBakPath)
}
await galleryDB.insertMany(originGallery)
picgo.saveConfig({
uploaded: []
})
}
}
export {
updateShortKeyFromVersion212,
migrateGalleryFromVersion230
}
+124
View File
@@ -0,0 +1,124 @@
import http from 'http'
import routers from './routerManager'
import {
handleResponse,
ensureHTTPLink
} from './utils'
import picgo from '@core/picgo'
import logger from '@core/picgo/logger'
import axios from 'axios'
class Server {
private httpServer: http.Server
private config: IServerConfig
constructor () {
let config = picgo.getConfig<IServerConfig>('settings.server')
const result = this.checkIfConfigIsValid(config)
if (result) {
this.config = config
} else {
config = {
port: 36677,
host: '127.0.0.1',
enable: true
}
this.config = config
picgo.saveConfig({
'settings.server': config
})
}
this.httpServer = http.createServer(this.handleRequest)
}
private checkIfConfigIsValid (config: IObj | undefined) {
if (config && config.port && config.host && (config.enable !== undefined)) {
return true
} else {
return false
}
}
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
if (request.method === 'POST') {
if (!routers.getHandler(request.url!)) {
logger.warn(`[PicGo Server] don't support [${request.url}] url`)
handleResponse({
response,
statusCode: 404,
header: {},
body: {
success: false
}
})
} else {
let body: string = ''
let postObj: IObj
request.on('data', chunk => {
body += chunk
})
request.on('end', () => {
try {
postObj = (body === '') ? {} : JSON.parse(body)
} catch (err) {
logger.error(`[PicGo Server]`, err)
return handleResponse({
response,
body: {
success: false,
message: 'Not sending data in JSON format'
}
})
}
logger.info(`[PicGo Server] get the request`, body)
const handler = routers.getHandler(request.url!)
handler!({
...postObj,
response
})
})
}
} else {
logger.warn(`[PicGo Server] don't support [${request.method}] method`)
response.statusCode = 404
response.end()
}
}
// port as string is a bug
private listen = (port: number | string) => {
logger.info(`[PicGo Server] is listening at ${port}`)
if (typeof port === 'string') {
port = parseInt(port, 10)
}
this.httpServer.listen(port, this.config.host).on('error', async (err: ErrnoException) => {
if (err.errno === 'EADDRINUSE') {
try {
// make sure the system has a PicGo Server instance
await axios.post(ensureHTTPLink(`${this.config.host}:${port}/heartbeat`))
this.shutdown(true)
} catch (e) {
logger.warn(`[PicGo Server] ${port} is busy, trying with port ${(port as number) + 1}`)
// fix a bug: not write an increase number to config file
// to solve the auto number problem
this.listen((port as number) + 1)
}
}
})
}
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()
this.startup()
}
}
export default new Server()
+20
View File
@@ -0,0 +1,20 @@
class Router {
private router = new Map<string, routeHandler>()
get (url: string, callback: routeHandler): void {
this.router.set(url, callback)
}
post (url: string, callback: routeHandler): void {
this.router.set(url, callback)
}
getHandler (url: string) {
if (this.router.has(url)) {
return this.router.get(url)
} else {
return null
}
}
}
export default new Router()
+98
View File
@@ -0,0 +1,98 @@
import router from './router'
import {
handleResponse
} from './utils'
import logger from '@core/picgo/logger'
import windowManager from 'apis/app/window/windowManager'
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
import path from 'path'
import { dbPathDir } from 'apis/core/datastore/dbChecker'
const STORE_PATH = dbPathDir()
const LOG_PATH = path.join(STORE_PATH, 'picgo.log')
const errorMessage = `upload error. see ${LOG_PATH} for more detail.`
router.post('/upload', async ({
response,
list = []
} : {
response: IHttpResponse,
list?: string[]
}): Promise<void> => {
try {
if (list.length === 0) {
// upload with clipboard
logger.info('[PicGo Server] upload clipboard file')
const res = await uploadClipboardFiles()
if (res) {
handleResponse({
response,
body: {
success: true,
result: [res]
}
})
} else {
handleResponse({
response,
body: {
success: false,
message: errorMessage
}
})
}
} else {
logger.info('[PicGo Server] upload files in list')
// upload with files
const pathList = list.map(item => {
return {
path: item
}
})
const win = windowManager.getAvailableWindow()
const res = await uploadChoosedFiles(win.webContents, pathList)
if (res.length) {
handleResponse({
response,
body: {
success: true,
result: res
}
})
} else {
handleResponse({
response,
body: {
success: false,
message: errorMessage
}
})
}
}
} catch (err) {
logger.error(err)
handleResponse({
response,
body: {
success: false,
message: errorMessage
}
})
}
})
router.post('/heartbeat', async ({
response
} : {
response: IHttpResponse,
}) => {
handleResponse({
response,
body: {
success: true,
result: 'alive'
}
})
})
export default router
+30
View File
@@ -0,0 +1,30 @@
import logger from '@core/picgo/logger'
export const handleResponse = ({
response,
statusCode = 200,
header = {
'Content-Type': 'application/json'
},
body = {
success: false
}
} : {
response: IHttpResponse,
statusCode?: number,
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()
}
export const ensureHTTPLink = (url: string): string => {
return url.startsWith('http')
? url
: `http://${url}`
}
+79
View File
@@ -0,0 +1,79 @@
import fs from 'fs-extra'
import path from 'path'
import os from 'os'
import { dbPathChecker } from 'apis/core/datastore/dbChecker'
const configPath = dbPathChecker()
const CONFIG_DIR = path.dirname(configPath)
function beforeOpen () {
if (process.platform === 'darwin') {
resolveMacWorkFlow()
}
resolveClipboardImageGenerator()
}
/**
* macOS 右键菜单
*/
function resolveMacWorkFlow () {
const dest = `${os.homedir}/Library/Services/Upload pictures with PicGo.workflow`
if (fs.existsSync(dest)) {
return true
} else {
try {
fs.copySync(path.join(__static, 'Upload pictures with PicGo.workflow'), dest)
} catch (e) {
console.log(e)
}
}
}
/**
* 初始化剪贴板生成图片的脚本
*/
function resolveClipboardImageGenerator () {
let clipboardFiles = getClipboardFiles()
if (!fs.pathExistsSync(path.join(CONFIG_DIR, 'windows10.ps1'))) {
clipboardFiles.forEach(item => {
fs.copyFileSync(item.origin, item.dest)
})
} else {
clipboardFiles.forEach(item => {
diffFilesAndUpdate(item.origin, item.dest)
})
}
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
try {
let file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
let file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
if (!file1 || !file2 || !file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2)
}
} catch (e) {
console.error(e)
fs.copyFileSync(filePath1, filePath2)
}
}
function getClipboardFiles () {
let files = [
'/linux.sh',
'/mac.applescript',
'/windows.ps1',
'/windows10.ps1',
'/wsl.sh'
]
return files.map(item => {
return {
origin: path.join(__static, item),
dest: path.join(CONFIG_DIR, item)
}
})
}
}
export default beforeOpen
+71
View File
@@ -0,0 +1,71 @@
import db from '~/main/apis/core/datastore'
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
}
-27
View File
@@ -1,27 +0,0 @@
import path from 'path'
import db from '../../datastore'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const getPicBeds = (app) => {
const PicGo = requireFunc('picgo')
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const picgo = new PicGo(CONFIG_PATH)
const picBedTypes = picgo.helper.uploader.getIdList()
const picBedFromDB = db.read().get('picBed.list').value() || []
const picBeds = picBedTypes.map(item => {
const visible = picBedFromDB.find(i => i.type === item) // object or undefined
return {
type: item,
name: picgo.helper.uploader.get(item).name || item,
visible: visible ? visible.visible : true
}
})
picgo.cmd.program.removeAllListeners()
return picBeds
}
export {
getPicBeds
}
+17
View File
@@ -0,0 +1,17 @@
import picgo from '@core/picgo'
const getPicBeds = () => {
const picBedTypes = picgo.helper.uploader.getIdList()
const picBedFromDB = picgo.getConfig<IPicBedType[]>('picBed.list') || []
const picBeds = picBedTypes.map((item: string) => {
const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined
return {
type: item,
name: picgo.helper.uploader.get(item)!.name || item,
visible: visible ? visible.visible : true
}
}) as IPicBedType[]
return picBeds
}
export default getPicBeds
-87
View File
@@ -1,87 +0,0 @@
import {
dialog,
BrowserWindow,
clipboard,
Notification
} from 'electron'
import db from '../../datastore'
import Uploader from './uploader'
import pasteTemplate from './pasteTemplate'
const WEBCONTENTS = Symbol('WEBCONTENTS')
const IPCMAIN = Symbol('IPCMAIN')
const PICGO = Symbol('PICGO')
class GuiApi {
constructor (ipcMain, webcontents, picgo) {
this[WEBCONTENTS] = webcontents
this[IPCMAIN] = ipcMain
this[PICGO] = picgo
}
/**
* for plugin showInputBox
* @param {object} options
* return type is string or ''
*/
showInputBox (options) {
if (options === undefined) {
options = {
title: '',
placeholder: ''
}
}
this[WEBCONTENTS].send('showInputBox', options)
return new Promise((resolve, reject) => {
this[IPCMAIN].once('showInputBox', (event, value) => {
resolve(value)
})
})
}
/**
* for plugin show file explorer
* @param {object} options
*/
showFileExplorer (options) {
if (options === undefined) {
options = {}
}
return new Promise((resolve, reject) => {
dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, filename => {
resolve(filename)
})
})
}
/**
* for plugin to upload file
* @param {array} input
*/
async upload (input) {
const imgs = await new Uploader(input, this[WEBCONTENTS], this[PICGO]).upload()
if (imgs !== false) {
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
let pasteText = ''
for (let i in imgs) {
const url = imgs[i].url || imgs[i].imgUrl
pasteText += pasteTemplate(pasteStyle, url) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: imgs[i].imgUrl
})
setTimeout(() => {
notification.show()
}, i * 100)
db.read().get('uploaded').insert(imgs[i]).write()
}
clipboard.writeText(pasteText)
this[WEBCONTENTS].send('uploadFiles', imgs)
this[WEBCONTENTS].send('updateGallery')
return imgs
}
return []
}
}
export default GuiApi
+61
View File
@@ -0,0 +1,61 @@
import path from 'path'
import fs from 'fs-extra'
import Logger from 'picgo/dist/src/lib/Logger'
interface IResultFileObject {
path: string
}
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 ((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: tempPath
}
} else {
logger.warn(`cli -> can't get file: ${tempPath}, invalid path`)
return null
}
}
}).filter(item => item !== null) as Result
return result
}
}
return []
}
export {
getUploadFiles
}
-13
View File
@@ -1,13 +0,0 @@
import db from '../../datastore'
export default (style, url) => {
const customLink = db.read().get('customLink').value() || '$url'
const tpl = {
'markdown': `![](${url})`,
'HTML': `<img src="${url}"/>`,
'URL': url,
'UBB': `[IMG]${url}[/IMG]`,
'Custom': customLink.replace(/\$url/g, url)
}
return tpl[style]
}
-168
View File
@@ -1,168 +0,0 @@
import path from 'path'
import GuiApi from './guiApi'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo')
const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
// get uploader or transformer config
const getConfig = (name, type, ctx) => {
let config = []
if (name === '') {
return config
} else {
const handler = ctx.helper[type].get(name)
if (handler) {
if (handler.config) {
config = handler.config(ctx)
}
}
return config
}
}
const handleConfigWithFunction = config => {
for (let i in config) {
if (typeof config[i].default === 'function') {
config[i].default = config[i].default()
}
if (typeof config[i].choices === 'function') {
config[i].choices = config[i].choices()
}
}
return config
}
const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
ipcMain.on('getPluginList', event => {
const picgo = new PicGo(CONFIG_PATH)
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)
}
const obj = {
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: pluginPKG.gui || false,
config: {
plugin: {
name: pluginList[i].replace(/picgo-plugin-/, ''),
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
},
uploader: {
name: uploaderName,
config: handleConfigWithFunction(getConfig(uploaderName, 'uploader', picgo))
},
transformer: {
name: transformerName,
config: handleConfigWithFunction(getConfig(uploaderName, 'transformer', picgo))
}
},
enabled: picgo.getConfig(`plugins.${pluginList[i]}`),
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
guiMenu: menu,
ing: false
}
list.push(obj)
}
event.sender.send('pluginList', list)
picgo.cmd.program.removeAllListeners()
})
}
const handlePluginInstall = (ipcMain, CONFIG_PATH) => {
ipcMain.on('installPlugin', (event, msg) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo)
picgo.on('installSuccess', notice => {
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
})
pluginHandler.install([msg])
picgo.cmd.program.removeAllListeners()
})
}
const handlePluginUninstall = (ipcMain, CONFIG_PATH) => {
ipcMain.on('uninstallPlugin', (event, msg) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo)
picgo.on('uninstallSuccess', notice => {
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
})
pluginHandler.uninstall([msg])
picgo.cmd.program.removeAllListeners()
})
}
const handlePluginUpdate = (ipcMain, CONFIG_PATH) => {
ipcMain.on('updatePlugin', (event, msg) => {
const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo)
picgo.on('updateSuccess', notice => {
event.sender.send('updateSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
})
pluginHandler.update([msg])
picgo.cmd.program.removeAllListeners()
})
}
const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => {
ipcMain.on('getPicBedConfig', (event, type) => {
const picgo = new PicGo(CONFIG_PATH)
const name = picgo.helper.uploader.get(type).name || type
if (picgo.helper.uploader.get(type).config) {
const config = handleConfigWithFunction(picgo.helper.uploader.get(type).config(picgo))
event.sender.send('getPicBedConfig', config, name)
} else {
event.sender.send('getPicBedConfig', [], name)
}
picgo.cmd.program.removeAllListeners()
})
}
const handlePluginActions = (ipcMain, CONFIG_PATH) => {
ipcMain.on('pluginActions', (event, name, label) => {
const picgo = new PicGo(CONFIG_PATH)
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
const guiApi = new GuiApi(ipcMain, event.sender, picgo)
if (plugin.guiMenu && plugin.guiMenu.length > 0) {
const menu = plugin.guiMenu(picgo)
menu.forEach(item => {
if (item.label === label) {
item.handle(picgo, guiApi)
}
})
}
})
}
const handleRemoveFiles = (ipcMain, CONFIG_PATH) => {
ipcMain.on('removeFiles', (event, files) => {
const picgo = new PicGo(CONFIG_PATH)
picgo.emit('remove', files)
})
}
export default (app, ipcMain) => {
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
handleGetPluginList(ipcMain, STORE_PATH, CONFIG_PATH)
handlePluginInstall(ipcMain, CONFIG_PATH)
handlePluginUninstall(ipcMain, CONFIG_PATH)
handlePluginUpdate(ipcMain, CONFIG_PATH)
handleGetPicBedConfig(ipcMain, CONFIG_PATH)
handlePluginActions(ipcMain, CONFIG_PATH)
handleRemoveFiles(ipcMain, CONFIG_PATH)
}
+61
View File
@@ -0,0 +1,61 @@
import db from '~/main/apis/core/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
}

Some files were not shown because too many files have changed in this diff Show More