Compare commits

..
240 Commits
Author SHA1 Message Date
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
PiEgg 2def54ec49 2.0.0 2019-01-13 16:30:39 +08:00
PiEgg 1d5142da79 Updated: README 2019-01-13 16:21:48 +08:00
PiEgg 8e3c51d0b9 Updated: homepage 2019-01-12 21:04:09 +08:00
Molunerfinn 0cc3f70cd7 Fixed: PATH in macOS 2019-01-12 19:19:26 +08:00
Molunerfinn a71ba33156 Fixed: bugs with imgFromClipboard 2019-01-12 16:09:24 +08:00
PiEgg 960e01f17f Added: ctx for guiApi 2019-01-12 13:47:44 +08:00
Molunerfinn 86265ee5c5 Updated: build version 2019-01-12 12:48:54 +08:00
PiEgg a6377696d9 Added: guiMenu for plugins 2019-01-11 21:13:16 +08:00
Molunerfinn 927d913f3b Added: guiApi for plugins 2019-01-11 17:22:33 +08:00
PiEgg 250a014fc1 Fixed: hide mini-window options in macOS 2019-01-07 19:37:20 +08:00
PiEgg 5e5a5f133a Fixed: transformer value 2019-01-02 22:56:26 +08:00
PiEgg 78d0baef7d Added: transformer setting 2019-01-01 23:44:00 +08:00
PiEgg 3490752e2f Fixed: title text-align style 2018-12-28 20:53:21 +08:00
Molunerfinn 2b94fe49a2 Finished: macOS new theme color 2018-12-28 20:30:47 +08:00
Molunerfinn d0fdbf1ea2 Updated: home page webpack config 2018-12-28 11:10:28 +08:00
Molunerfinn eddd39309e Updated: electron version->4 & webpack version -> 4 2018-12-25 17:28:12 +08:00
Molunerfinn 60ccd10e1a Finished: dynamic sync default picbed in menu 2018-12-25 16:11:10 +08:00
Molunerfinn 51cea7cdf0 Fixed: config sync between plugin page & picbed page 2018-12-25 15:24:32 +08:00
Molunerfinn f7dc41a927 Partly finished: plugin system 2018-12-24 16:05:30 +08:00
PiEgg ae9d8eee13 Finished: other picbed page 2018-12-23 23:15:00 +08:00
Molunerfinn 2d63f2ff42 Added: other-picbed page 2018-12-21 17:25:42 +08:00
PiEgg 778c910436 Fixed: #116 || added proxy option 2018-12-20 22:30:08 +08:00
Molunerfinn 405548426a Added: clipboard image handler for picgo-core 2018-12-20 17:23:03 +08:00
Molunerfinn 8ff7d15f8d Fixed: view-title style & open config file 2018-12-19 16:46:07 +08:00
Molunerfinn 5cf96d38b8 Added: open plugin hompage by click plugin name 2018-12-19 15:45:46 +08:00
PiEgg 8ec128121f Added: pasteStyle handler in gallery page 2018-12-19 11:52:27 +08:00
Molunerfinn be6cce9501 Added: picgo plugin handle states 2018-12-18 18:58:31 +08:00
PiEgg 72f2669cce Added: plugin reload state in localstorage 2018-12-17 23:56:11 +08:00
Molunerfinn cdf7e14548 Fixed: #145 || changed rename window display position for more reasonable 2018-12-13 17:05:08 +08:00
Molunerfinn 0fe28ce675 Changed: renderer files' structure 2018-12-10 17:13:48 +08:00
Molunerfinn 9d979a2b04 Fixed: progress display when upload from clipboard 2018-11-29 11:14:45 +08:00
PiEgg 9c12fb6ede Fixed: #140 || drag the image to app will be opened by Chrome & gallery close button 2018-11-25 23:53:26 +08:00
Molunerfinn 8a8b97a937 Fixed: #136 || multiple image timestamp rename bug 2018-11-22 16:55:34 +08:00
Molunerfinn 1af9af74ce Fixed: possible event listener memeory leak 2018-11-01 23:42:20 +08:00
Molunerfinn ca01d2c521 Fixed: #128 || tray window display bug in multi screen 2018-10-20 23:25:30 +08:00
Molunerfinn 094b83b30b Changed: npm search api url 2018-10-12 14:56:25 +08:00
Molunerfinn ea5b937c93 Fixed: some bugs 2018-10-10 17:25:44 +08:00
Molunerfinn c149211def Added: ipcRenderer remore listener 2018-10-09 17:11:24 +08:00
Molunerfinn 13c63ba66d Added: plugin install & uninstall handler 2018-10-09 17:01:33 +08:00
Molunerfinn dc1ba57a97 Fixed: plugin handler require bug 2018-09-29 11:32:44 +08:00
Molunerfinn 392af6c617 Added: plugin handler but with bugs 2018-09-28 17:31:28 +08:00
Molunerfinn 5909d35566 Fixed: image-size bug && change config all to settings 2018-09-27 17:22:29 +08:00
Molunerfinn 804072999e Fixed: aliyun imgUrl in list 2018-09-26 23:11:21 +08:00
Molunerfinn 17755b9809 Updated: vs-picgo 2018-09-26 20:13:48 +08:00
Molunerfinn c3b2d7b941 Fixed: bug with function in config 2018-09-20 15:14:19 +08:00
Molunerfinn 4a420dd5c5 Added: plugin config save 2018-09-20 14:49:20 +08:00
Molunerfinn 51281f345b Added: config form 2018-09-19 17:27:09 +08:00
Molunerfinn c8b4bebe1d Fixed: don't show mini-window in macOS 2018-09-18 19:08:26 +08:00
Molunerfinn aea0c3661d Added: picgo-core-ipc & fixed dynamic require bug in picgo 2018-09-18 18:48:55 +08:00
Molunerfinn cb32d43be4 Added: plugin view 2018-09-17 16:52:01 +08:00
Molunerfinn 4a0d398b4f Added: picgo-core support for uploading 2018-09-17 15:50:24 +08:00
Molunerfinn fb5a10d792 Updated: electron version 2018-09-14 18:49:12 +08:00
Molunerfinn 65dfb4ac05 Partly: added picgo package to PicGo 2018-09-14 17:31:57 +08:00
Molunerfinn f309d18aed Changed: auto start from settingWindow to miniWindow 2018-09-13 16:54:18 +08:00
Molunerfinn 6fca0e73c0 Added: miniWindowOntop option 2018-09-07 17:32:11 +08:00
Molunerfinn 0daafa3964 Updated: readme 2018-09-07 16:31:55 +08:00
Molunerfinn a2f1eec8d7 Fixed: some text error 2018-09-04 11:28:34 +08:00
Molunerfinn fc56d07ab2 Fixed: tray drag imgs 2018-08-08 14:43:49 +08:00
Molunerfinn cf504a0173 Fixed: sync upload img to gallery 2018-08-08 14:39:40 +08:00
Molunerfinn e0bb8480a2 1.6.2 2018-08-08 11:24:21 +08:00
Molunerfinn d4c04ee1e6 Updated: electron version & fixed smms pic type 2018-08-08 11:21:08 +08:00
Molunerfinn 4ab782e8dc 1.6.1 2018-07-25 15:07:46 +08:00
Molunerfinn a9ba87dc8e Updated: readme & FAQ 2018-07-25 00:04:14 +08:00
Molunerfinn 396faa2c60 Fixed: multi copy bug 2018-07-24 23:15:19 +08:00
Molunerfinn 930e4e663b Fixed: #84 && add multi copy 2018-07-23 22:24:18 +08:00
alanandPiEgg ce9c2aed71 Fixed: #82 2018-07-22 10:42:54 +08:00
Molunerfinn bba250b0d9 Fixed: #80 || mime-type in ali-oss 2018-07-18 15:34:35 +08:00
Molunerfinn b74fe8676b Updated: readme 2018-07-11 16:15:27 +08:00
Molunerfinn 8ad28f7fe5 Fixed: linux icon path 2018-07-11 16:04:07 +08:00
Molunerfinn 1d91730eb9 Fixed: word error 2018-07-11 15:38:15 +08:00
Molunerfinn 5a25aa0b52 Fuck: remove ali-oss 2018-07-11 15:28:56 +08:00
Molunerfinn 671c011feb Fixed: linux build bug 2018-07-11 11:42:39 +08:00
Molunerfinn 62b616bfc7 Added: linux build 2018-07-11 11:26:41 +08:00
Molunerfinn 2a801e6c19 1.6.0 2018-07-11 11:20:00 +08:00
Molunerfinn 313002cf9e Updated: readme & doc 2018-07-11 11:19:47 +08:00
Molunerfinn bcb1859eaa Fixed: choosed pic-bed bug 2018-07-11 10:52:02 +08:00
Molunerfinn 8bc0aef482 Fixed: linux version quit bug 2018-07-11 10:04:42 +08:00
Molunerfinn 353f553988 Fixed: linux mini-window tranparent bug 2018-07-11 09:39:23 +08:00
Molunerfinn f48021d928 Fixed: sync picbed status 2018-07-10 22:37:29 +08:00
Molunerfinn c9ecef60e3 Added: clipboard upload menu for mini page 2018-07-07 15:06:36 +08:00
Molunerfinn 0f4901bdbf Fixed: mini window & setting window toggle show or hide status 2018-07-07 14:45:14 +08:00
Molunerfinn f494e1486c Modified: error text for OSS 2018-07-07 00:31:53 +08:00
Molunerfinn 1faf29732c Fixed: tcyun error notice 2018-07-07 00:28:37 +08:00
Molunerfinn 5207326017 Added: icon for miniwindow 2018-07-07 00:01:06 +08:00
Molunerfinn 4d5b3fbdf6 Finished: miniwindow 2018-07-06 23:54:36 +08:00
Molunerfinn abcd7b848d Added: mini window upload progress 2018-07-06 00:43:53 +08:00
Molunerfinn 8785218b0d Added: transition for gallery handle-bar 2018-07-05 20:21:43 +08:00
Molunerfinn 61a2a047c7 Added: imgur support 2018-06-27 22:57:15 +08:00
Molunerfinn 6aed2d2a97 Added: aliYun OSS support 2018-06-26 23:50:16 +08:00
Molunerfinn 46189d2f34 Added: gallery lazy-load 2018-06-26 21:11:04 +08:00
Molunerfinn 4d55ac941f Fixed: updateDefaultPicBed bug 2018-06-26 17:12:21 +08:00
Molunerfinn 62b0c2113c Fixed: tray bug on ubuntu & hide tray icon on linux 2018-06-26 17:03:46 +08:00
Molunerfinn ed187f4438 Fixed: scrollbar bug in ubuntu 2018-06-26 00:19:40 +08:00
Molunerfinn 372f71bcd9 Fixed: not darwin platform setting window bug 2018-06-25 19:27:41 +08:00
Molunerfinn 415bdd31f3 Added: gallery edit functions 2018-06-20 20:45:12 +08:00
Molunerfinn 5d9e0a311d Updated: gallery functions 2018-06-19 13:45:07 +08:00
Molunerfinn 1781a8902c Fixed: scroll-bar style 2018-06-12 13:36:44 +08:00
Molunerfinn 1f95ef0ee2 Updated: docs 2018-06-05 21:10:36 +08:00
Molunerfinn a63e1717c7 Fixed: tray menu submenu item 2018-06-05 20:29:16 +08:00
Molunerfinn 1e5fa28789 1.5.1 2018-06-05 20:05:11 +08:00
Molunerfinn 3455512c47 Added: upload notification swtich 2018-06-05 20:03:58 +08:00
Molunerfinn 947e49ab4a Modified: pic-bed constructure for future 2018-06-05 19:49:01 +08:00
Molunerfinn a20299edaa Fixed: SMMS upload problem 2018-06-05 09:47:43 +08:00
Molunerfinn ffbbc74734 Added: smms picbed 2018-06-04 21:30:36 +08:00
Molunerfinn 1b8400a458 Added: manual check update button 2018-06-04 20:41:40 +08:00
Molunerfinn 9e2834c0f9 Added: support for upload notice & auto rename before upload 2018-06-04 20:13:17 +08:00
Molunerfinn 0b5981a27d Fixed: #61 || custom url can have more than one place 2018-05-28 18:39:43 +08:00
Molunerfinn d78de75c18 Fixed: #60 2018-05-26 23:23:36 +08:00
Molunerfinn f8eceae8e4 Finished: github custom url 2018-05-26 10:34:04 +08:00
Molunerfinn a0c1e95b15 Updated: readme 2018-05-23 20:33:02 +08:00
Molunerfinn 94f70cb8c6 Updated: readme 2018-05-09 15:17:03 +08:00
Molunerfinn f33df32312 Fixed: auto hide menu-bar in windows 2018-05-09 09:30:05 +08:00
Molunerfinn c1604fa3cb Updated: readme 2018-05-09 09:18:49 +08:00
Molunerfinn 0e5eee7876 1.5.0 2018-05-09 09:13:16 +08:00
Molunerfinn 00689a9931 Fixed: placeholder 2018-05-09 09:12:27 +08:00
Molunerfinn 06539d1513 Fixed: keyup enter for el-input & version check 2018-05-08 23:56:02 +08:00
Molunerfinn 74b94feb74 Finished: rename file before upload 2018-05-08 14:04:43 +08:00
Molunerfinn 757a7d1260 Finished: Tencent COS v5 version 2018-05-07 21:04:27 +08:00
Molunerfinn 2496c0b467 Added: v5 options 2018-05-07 19:55:33 +08:00
Molunerfinn 4a685e0474 Fixed: auto start setting 2018-05-02 19:34:24 +08:00
Molunerfinn 9fdd54ca16 Fixed: memeory out bug 2018-05-02 19:06:10 +08:00
Molunerfinn eaa348ad56 Added: autoStart 2018-05-02 17:17:55 +08:00
Molunerfinn 282b0dc25c Finished: custom picbed setting for displaying visible or none 2018-05-02 16:04:55 +08:00
Molunerfinn d99e45a021 Fixed: some styles 2018-05-02 14:24:59 +08:00
Molunerfinn ef8bf5476c Finished: picgo-setting page FE part 2018-04-28 16:22:35 +08:00
Molunerfinn d042c93bec Added: picgo-setting page 2018-04-28 00:16:17 +08:00
Molunerfinn ad71117876 Updated: sidebar 2018-04-14 11:02:02 +08:00
Molunerfinn fe7f688488 Finished: edit img-url in gallery page 2018-04-08 23:43:34 +08:00
Molunerfinn 65a15724a3 Fixed: #34 | macOS menubar drag file error 2018-04-08 10:27:41 +08:00
Molunerfinn 422aa52763 Added: display default picbed 2018-04-06 22:18:57 +08:00
Molunerfinn eecf98e452 Added: GitHub picbed support 2018-04-06 22:01:30 +08:00
Molunerfinn 975927ac68 Added: issue template 2018-03-12 21:26:03 +08:00
Molunerfinn 9ef615cab4 1.4.3 2018-03-12 11:03:27 +08:00
Molunerfinn fb236c3186 Fixed: qiniu uploader path undefined 2018-03-12 11:01:14 +08:00
Molunerfinn 458087a94e 1.4.2 2018-03-09 23:26:34 +08:00
Molunerfinn 4e8dfef6cb Added: qiniu upload custom path 2018-03-09 23:24:48 +08:00
Molunerfinn e1c151e310 Added: tcyun cos custom url 2018-03-09 21:08:10 +08:00
Molunerfinn 25068a8949 Added: upYun path option 2018-03-09 17:20:08 +08:00
Molunerfinn 4010a09fe4 Added: custom url rules 2018-03-09 16:18:17 +08:00
Molunerfinn f6536b1dc2 Updated: support custom paste link template 2018-03-09 14:35:41 +08:00
Molunerfinn df39511cdf Updated: filename & file extname with qiniu & weibo 2018-03-08 16:50:56 +08:00
Molunerfinn 504690b729 1.4.1 2018-01-30 19:08:00 +08:00
Molunerfinn 937979ae3d Fixed: can not use shortcut when settingWindow is closed 2018-01-30 19:05:13 +08:00
Molunerfinn 1f373fb325 1.4.0 2018-01-30 15:35:26 +08:00
Molunerfinn 92b9fbcee8 Added: quick set default picBed & quick upload clipboard files button 2018-01-30 15:35:05 +08:00
Molunerfinn 37a784225e Fixed: #17 2018-01-30 15:20:19 +08:00
Molunerfinn 16fe016c70 Fixed: readme 2018-01-29 10:55:50 +08:00
Molunerfinn 406cfffbc9 1.3.2 2018-01-18 09:31:52 +08:00
Molunerfinn e5d5bad115 Fixed: cookie error 2018-01-18 09:31:15 +08:00
Molunerfinn 7364ea6ddf Fixed: imgList error 2018-01-16 17:05:33 +08:00
Molunerfinn f657026820 Removed: some console & modified readme 2018-01-16 14:51:53 +08:00
Molunerfinn e8e78303a3 Added: weibo cookie uploader support 2018-01-16 13:55:16 +08:00
Molunerfinn 29fb9c742e Added: debug options for vscode debug 2018-01-12 17:24:56 +08:00
Molunerfinn 16f518b2ad Fixed: CI download speed 2018-01-10 20:39:08 +08:00
Molunerfinn 790367efd6 Updated: readme 2018-01-10 18:57:01 +08:00
Molunerfinn 7e9fd9d14e 1.3.1 2018-01-10 17:18:43 +08:00
Molunerfinn 9df9b547d1 Fixed: appveyor yml 2018-01-10 17:18:07 +08:00
Molunerfinn 26f6627b44 1.3.0 2018-01-10 17:14:01 +08:00
Molunerfinn dc9d5d6dfb Added: options for update helper 2018-01-10 17:12:24 +08:00
Molunerfinn b4c760c8cd Added: windows support 2018-01-10 16:51:09 +08:00
Molunerfinn 783adada58 1.2.1 2018-01-10 11:31:57 +08:00
Molunerfinn c6f2e16b80 Fixed: Chinese filename can't upload & add setting-info button 2018-01-10 11:31:07 +08:00
Molunerfinn ca6e72d4fc Updated: readme 2017-12-23 20:20:30 +08:00
Molunerfinn 3d21909d72 1.2.0 2017-12-23 20:07:19 +08:00
Molunerfinn 1d2389c9e7 Updated: readme 2017-12-23 20:06:44 +08:00
Molunerfinn a8a0b9b424 Added: globalshotcut for clipboard upload 2017-12-23 19:46:20 +08:00
Molunerfinn 7ada9fec39 Added: updateChecker 2017-12-23 16:38:19 +08:00
Molunerfinn 2ecb33ccb0 Added: upyun support 2017-12-22 22:30:16 +08:00
Molunerfinn d1602edf65 1.1.0 2017-12-21 11:29:30 +08:00
Molunerfinn bc31cab735 Updated: readme 2017-12-21 11:28:20 +08:00
Molunerfinn 63f8cb631a Added: cos support 2017-12-21 11:18:54 +08:00
Molunerfinn 6cf3a50023 Added: donation way 2017-12-20 10:26:19 +08:00
Molunerfinn 5fb11f5d89 1.0.3 2017-12-15 17:38:09 +08:00
Molunerfinn f78bfda258 Fixed: input paste bug 2017-12-15 17:37:38 +08:00
Molunerfinn 21ceafe649 Fixed: docs page width 2017-12-13 10:28:36 +08:00
Molunerfinn fecd35cb06 1.0.2 2017-12-12 19:57:42 +08:00
Molunerfinn 4a9623e49f Fixed: travis release config 2017-12-12 19:57:36 +08:00
Molunerfinn e1ed998153 1.0.1 2017-12-12 19:47:43 +08:00
Molunerfinn 3a97d019d1 Fixed: release script 2017-12-12 19:47:12 +08:00
Molunerfinn 4b725930d2 1.0.0 2017-12-12 18:48:16 +08:00
Molunerfinn 7204e05601 Added: version control 2017-12-12 18:46:56 +08:00
Molunerfinn 7c4b4f28e1 Fixed: gallery url 2017-12-12 17:21:12 +08:00
Molunerfinn f6752c6f8a Updated: docs build process & travis-ci config 2017-12-12 16:18:48 +08:00
Molunerfinn ad986689e5 Added: docs page 2017-12-11 21:52:14 +08:00
Molunerfinn cfa360bb09 Fixed: userData path not found error 2017-12-08 15:52:41 +08:00
Molunerfinn d87501212f Added icons 2017-12-08 14:07:27 +08:00
Molunerfinn 493d79d33d Added: iconfont 2017-12-07 14:33:14 +08:00
Molunerfinn 0964c1423b Added: gallery 2017-12-07 11:26:29 +08:00
Molunerfinn ec12ba4e0e Partly finished: qiniu upload 2017-11-29 23:13:35 +08:00
Molunerfinn d0fdb79821 Added: qiniu page 2017-11-29 16:23:05 +08:00
Molunerfinn 4dd6586cb4 Partly finished: weibo picbed 2017-11-28 23:56:15 +08:00
Molunerfinn 3814125986 Rm yarn-error 2017-11-28 08:22:40 +08:00
Molunerfinn d1b9e0f592 First beta version 2017-11-28 08:21:12 +08:00
108 changed files with 18907 additions and 16 deletions
+39
View File
@@ -0,0 +1,39 @@
{
"comments": false,
"env": {
"test": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
],
"plugins": ["istanbul"]
},
"main": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
]
},
"renderer": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
},
"production": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}
+132
View File
@@ -0,0 +1,132 @@
'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
@@ -0,0 +1,40 @@
const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(event => {
/**
* Reload browser when HTMLWebpackPlugin emits a new index.html
*
* Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
* https://github.com/SimulatedGREG/electron-vue/issues/437
* https://github.com/jantimon/html-webpack-plugin/issues/680
*/
// if (event.action === 'reload') {
// window.location.reload()
// }
/**
* Notify `mainWindow` when `main` process is compiling,
* giving notice for an expected reload of the `electron` process
*/
if (event.action === 'compiling') {
document.body.innerHTML += `
<style>
#dev-client {
background: #4fc08d;
border-radius: 4px;
bottom: 20px;
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
color: #fff;
font-family: 'Source Sans Pro', sans-serif;
left: 20px;
padding: 8px 12px;
position: absolute;
}
</style>
<div id="dev-client">
Compiling Main Process...
</div>
`
}
})
+190
View File
@@ -0,0 +1,190 @@
'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
@@ -0,0 +1,120 @@
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
@@ -0,0 +1,83 @@
'use strict'
process.env.BABEL_ENV = 'main'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const 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
@@ -0,0 +1,194 @@
'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
@@ -0,0 +1,139 @@
'use strict'
process.env.BABEL_ENV = 'web'
const path = require('path')
const webpack = require('webpack')
const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
let webConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
web: path.join(__dirname, '../src/renderer/main.js')
},
module: {
rules: [
{
test: /\.(js|vue)$/,
enforce: 'pre',
exclude: /node_modules/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-friendly-formatter')
}
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
include: [ path.resolve(__dirname, '../src/renderer') ],
exclude: /node_modules/
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: true,
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name].[ext]'
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
}
]
},
plugins: [
new ExtractTextPlugin('styles.css'),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: false
}),
new webpack.DefinePlugin({
'process.env.IS_WEB': 'true'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
path: path.join(__dirname, '../dist/web')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json', '.css']
},
target: 'web'
}
/**
* Adjust webConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
webConfig.devtool = ''
webConfig.plugins.push(
new BabiliWebpackPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = webConfig
+4
View File
@@ -0,0 +1,4 @@
test/unit/coverage/**
test/unit/*.js
test/e2e/*.js
dist/
+26
View File
@@ -0,0 +1,26 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
node: true
},
extends: 'standard',
globals: {
__static: true
},
plugins: [
'html'
],
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}
+30
View File
@@ -0,0 +1,30 @@
<!--
PicGo Issue 模板
请依照该模板来提交,否则将会被关闭。
**提问之前请注意你看过FAQ、配置手册以及那些被关闭的issues。否则同样的提问也会被关闭!**
-->
## 问题类型
<!-- 你要提交的是Bug Report还是Feature Request-->
## PicGo的相关信息
<!-- 版本、所在平台(Mac或者Windows -->
## 问题重现
<!-- 如果是Bug Report请填写本项 -->
<!-- 请附上相关截图 -->
## 功能请求
<!-- 如果是Feature Request请填写本项 -->
<!-- 详细描述你所预想的功能或者是现有功能的改进。 -->
---
<!--
最后,喜欢PicGo的话不妨给它点个star~
如果可以的话,请我喝杯咖啡?首页有赞助二维码,谢谢你的支持!
-->
+14
View File
@@ -0,0 +1,14 @@
.DS_Store
dist/electron/*
dist/web/*
build/*
!build/icons
!build/installer.nsh
coverage
node_modules/
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep
yarn-error.log
docs/dist/
+52
View File
@@ -0,0 +1,52 @@
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
osx_image: xcode8.3
sudo: required
dist: trusty
language: c
matrix:
include:
- os: osx
- os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
apt:
packages:
- libgnome-keyring-dev
- icnsutils
#- xvfb
before_install:
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
"$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
| tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
install:
#- export DISPLAY=':99.0'
#- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- nvm install 10
- curl -o- -L https://yarnpkg.com/install.sh | bash
- source ~/.bashrc
- npm install -g xvfb-maybe
- yarn
script:
#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
- npm run release
- yarn run build:docs
branches:
only:
- master
after_script:
- cd docs/dist
- git init
- git config user.name "Molunerfinn"
- git config user.email "marksz@teamsz.xyz"
- git add .
- git commit -m "Travis build docs"
- git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages
+38
View File
@@ -0,0 +1,38 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/src/main/index.dev.js",
"env": {
"DEBUG_ENV": "debug"
},
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",
// this points to the electron task runner
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"runtimeArgs": [
"--nolazy"
],
"sourceMaps": true
},
{
"name": "Attach",
"type": "node",
"request": "attach",
"port": 5858,
"sourceMaps": true,
"restart": true,
"outFiles": [],
"localRoot": "${workspaceRoot}",
"protocol": "inspector",
"remoteRoot": null
}
]
}
+4
View File
@@ -0,0 +1,4 @@
{
"eslint.enable": true,
"eslint.autoFixOnSave": true
}
+37
View File
@@ -0,0 +1,37 @@
## :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))
+58
View File
@@ -0,0 +1,58 @@
## 常见问题
> 在使用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),应该能找到答案。
## 1.七牛图床上传图片成功后无法显示或图片无`http://`前缀
通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。
参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79)
## 2.能否支持图床远端同步删除
不能。有些图床(比如微博图床、SM.MS、Imgur等)不支持后台管理,为了架构统一不支持远端删除。
## 3.能否支持上传视频文件
目前不能。2.0版本出来后将会支持插件系统,届时如果有人开发了相应的插件理论可以支持任意文件上传。
## 4.微博图床上传之后无法显示预览图
通常是挂了全局代理导致的。
参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36)
## 5.能否支持某某某图床
截止v1.6,PicGo支持了如下图床:
- `微博图床` v1.0
- `七牛图床` v1.0
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0
- `又拍云` v1.2.0
- `GitHub` v1.5.0
- `SM.MS` v1.5.1
- `阿里云OSS` v1.6.0
- `Imgur` v1.6.0
所以本体内将不会再支持其他图床。待2.0版本出来支持插件系统后可以由其他开发者开发相应的图床插件用以支持其他图床。
## 6.一个图床设置多个信息
不能。因为目前的架构只支持一个图床一份信息。
## 7.v2.0版本有什么新功能
在不改变PicGo现有使用界面和使用习惯的基础上,将会重构整个底层`传输->转换->上传`的架构,使其能够支持插件系统,方便其他开发者基于PicGo的底层进行二次开发。
理论上有了插件系统之后,其他开发者能够支持如下功能:
1. 能够实现上传前图片压缩
2. 能够通过图片url实现上传图片 -> 更深入地可以批量地实现图床的迁移
3. 能够支持其他图床甚至是自己的图床
等等。
届时底层架构将会独立出来成为一个独立的npm包,能够直接在命令行里调用。
目前开发进度可以参考[PicGo-Core](https://github.com/Molunerfinn/PicGo-Core)。
+13
View File
@@ -0,0 +1,13 @@
The 996ICU License (996ICU)
Version 0.1, March 2019
PACKAGE is distributed under LICENSE with the following restriction:
The above license is only granted to entities that act in concordance
with local labor laws. In addition, the following requirements must be
observed:
* The licencee must not, explicitly or implicitly, request or schedule
their employees to work more than 45 hours in any single week.
* The licencee must not, explicitly or implicitly, request or schedule
their employees to be at work consecutively for 10 hours.
+125
View File
@@ -0,0 +1,125 @@
# PicGo
> 图片上传+管理新体验
<p align="center">
<img src="https://raw.githubusercontent.com/Molunerfinn/test/master/picgo/New%20LOGO-150.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="">
</a>
<a href="https://travis-ci.org/Molunerfinn/PicGo/builds">
<img src="https://img.shields.io/travis/Molunerfinn/PicGo.svg?style=flat-square" alt="">
</a>
<a href="https://github.com/Molunerfinn/PicGo/releases">
<img src="https://img.shields.io/github/downloads/Molunerfinn/PicGo/total.svg?style=flat-square" alt="">
</a>
<a href="https://github.com/Molunerfinn/PicGo/releases/latest">
<img src="https://img.shields.io/github/release/Molunerfinn/PicGo.svg?style=flat-square" alt="">
</a>
<a href="https://github.com/PicGo/bump-version">
<img src="https://img.shields.io/badge/picgo-convention-blue.svg?style=flat-square" alt="">
</a>
</p>
## 应用说明
**PicGo在上传图片之后自动会将图片链接复制到你的剪贴板里,可选5种复制的链接格式。**
PicGo目前支持了
- `微博图床` v1.0
- `七牛图床` v1.0
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0
- `又拍云` v1.2.0
- `GitHub` v1.5.0
- `SM.MS` v1.5.1
- `阿里云OSS` v1.6.0
- `Imgur` v1.6.0
**本体不再增加默认的图床支持。你可以自行开发第三方图床插件。详见[PicGo-Core](https://picgo.github.io/PicGo-Core-Doc/)**
第三方插件以及使用了PicGo底层的应用可以在[Awesome-PicGo](https://github.com/PicGo/Awesome-PicGo)看到。欢迎贡献!
支持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)。
开发进度可以查看[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://github.com/Molunerfinn/PicGo/releases)。
macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版本的`exe`文件,linux用户请下载`AppImage`文件。
**如果你是Arch类的Linux用户,可以直接通过`aurman -S picgo-appimage`来安装PicGo。感谢 @houbaron 的贡献!**
**如果你是macOS用户,可以使用brew cask来安装PicGo: `brew cask install picgo`。感谢 @womeimingzi11 的贡献!**
## 应用截图
![](https://raw.githubusercontent.com/Molunerfinn/test/master/picgo/picgo-2.0.gif)
![picgo-menubar](https://user-images.githubusercontent.com/12621342/34242310-b5056510-e655-11e7-8568-60ffd4f71910.gif)
## 开发说明
> 目前仅针对Mac、Windows。Linux平台并未测试。
如果你想要学习、开发、修改或自行构建PicGo,可以依照下面的指示:
> 如果想学习Electron-vue的开发,可以查看我写的系列教程——[Electron-vue开发实战](https://molunerfinn.com/tags/Electron-vue/)
1. 你需要有node、git环境。需要了解npm的相关知识。
2. `git clone https://github.com/Molunerfinn/PicGo.git` 并进入项目
3. `npm install` 下载依赖
4. Mac需要有Xcode环境,Windows需要有VS环境。
### 开发模式
输入`npm run dev`进入开发模式,开发模式具有热重载特性。不过需要注意的是,开发模式不稳定,会有进程崩溃的情况。此时需要:
```bash
ctrl+c # 退出开发模式
npm run dev # 重新进入开发模式
```
### 生产模式
如果你需要自行构建,可以`npm run build`开始进行构建。构建成功后,会在`build`目录里出现构建成功的相应安装文件。
**注意**:如果你的网络环境不太好,可能会出现`electron-builder`下载`electron`二进制文件失败的情况。这个时候需要在`npm run build`之前指定一下`electron`的源为国内源:
```bash
export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
npm run build
```
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在`~/.electron/`目录下。如果想要更新`electron`构建版本,可以删除`~/.electron/`目录,然后重新运行上一步,让`electron-builder`去下载最新的`electron`二进制文件。
## 其他相关
- [vs-picgo](https://github.com/Spades-S/vs-picgo)picgo的VSCode版。
## 赞助
如果你喜欢PicGo并且它对你确实有帮助,欢迎给我打赏一杯咖啡哈~
支付宝:
![](https://user-images.githubusercontent.com/12621342/34188165-e7cdf372-e56f-11e7-8732-1338c88b9bb7.jpg)
微信:
![](https://user-images.githubusercontent.com/12621342/34188201-212cda84-e570-11e7-9b7a-abb298699d85.jpg)
## License
[MIT](http://opensource.org/licenses/MIT)
Copyright (c) 2017 - 2019 Molunerfinn
+32
View File
@@ -0,0 +1,32 @@
# Commented sections below can be used to run tests on the CI server
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- npm install
- node --version
build_script:
#- yarn test
- npm run release
test: off

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+13
View File
@@ -0,0 +1,13 @@
!macro customInstall
SetRegView 64
WriteRegStr HKCR "*\shell\PicGo" "" "Upload pictures w&ith PicGo"
WriteRegStr HKCR "*\shell\PicGo" "Icon" "$INSTDIR\PicGo.exe"
WriteRegStr HKCR "*\shell\PicGo\command" "" '"$INSTDIR\PicGo.exe" "upload" "%1"'
SetRegView 32
WriteRegStr HKCR "*\shell\PicGo" "" "Upload pictures w&ith PicGo"
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
+216
View File
@@ -0,0 +1,216 @@
<template lang='pug'>
#app(v-cloak)
#header
.mask
img.logo(src="~icons/256x256.png", alt="PicGo")
h1.title PicGo
small(v-if="version") {{ version }}
h2.desc 图片上传+管理新体验
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载
button.download(@click="goLink('https://picgo.github.io/PicGo-Doc/zh/guide/')") 查看文档
h3.desc
| 基于#[a(href="https://github.com/SimulatedGREG/electron-vue" target="_blank") electron-vue]开发
h3.desc
| 支持macOS,Windows,Linux
h3.desc
| 支持插件系统让PicGo更强大
#container.container-fluid
.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")
.row.ex-width.display-list
.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
img(:src="item.url")
.col-xs-10.col-xs-offset-1.col-md-5.col-md-offset-0.display-list__content
.display-list__title {{ item.title }}
.display-list__desc {{ item.desc }}
.row.ex-width.info
.col-xs-10.col-xs-offset-1
| &copy;2017 - {{ year }} #[a(href="https://github.com/Molunerfinn" target="_blank") Molunerfinn]
</template>
<script>
export default {
name: '',
data () {
return {
version: '',
year: new Date().getFullYear(),
itemList: [
{
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fma907llb5j20m30ed46a',
title: '精致设计',
desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。(Windows平台不支持)'
},
{
url: 'https://i.loli.net/2018/07/11/5b45768fb1276.png',
title: 'Mini小窗',
desc: 'Windows以及Linux系统下提供一个mini悬浮窗用于用户拖拽上传,节约你宝贵的桌面空间。'
},
{
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd56zm2nej218g0p0teb',
title: '便捷管理',
desc: '查看你的上传记录,重复使用更方便。支持点击图片大图查看。支持删除图片(仅本地记录),让界面更加干净。'
},
{
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd5ck9m0wj20lr0cxmzs',
title: '可选图床',
desc: '默认支持微博图床、七牛图床、腾讯云COS、又拍云、GitHub、SM.MS、阿里云OSS、Imgur。方便不同图床的上传需求。2.0版本开始更可以自己开发插件实现其他图床的上传需求。'
},
{
url: 'https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e',
title: '多样链接',
desc: '支持5种默认剪贴板链接格式,包括一种自定义格式,让你的文本编辑游刃有余。'
},
{
url: 'https://i.loli.net/2019/01/12/5c39a2f60a32a.png',
title: '插件系统',
desc: '2.0版本开始支持插件系统,让PicGo发挥无限潜能,成为一个极致的效率工具。'
}
]
}
},
created () {
this.getVersion()
},
methods: {
goLink (link) {
window.open(link, '_blank')
},
async getVersion () {
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const res = await this.$http.get(release)
this.version = res.data.name
}
}
}
</script>
<style lang='stylus'>
[v-cloak]
display none
*
box-sizing border-box
body,
html,
h1
margin 0
padding 0
font-family "Source Sans Pro","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
#app
position relative
.mask
position absolute
width 100%
height 100vh
top 0
left 0
background rgba(0,0,0, 0.7)
z-index -1
#header
height 100vh
width 100%
background-image url("https://ws1.sinaimg.cn/large/8700af19ly1fm9ru6fqvjj22p81stdta")
background-attachment fixed
background-size cover
background-position center
text-align center
padding 15vh
position relative
z-index 2
.logo
width 120px
.title
color #4BA2E2
font-size 36px
font-weight 300
margin 10px auto
text-align center
small
margin-left 10px
font-size 14px
.desc
font-weight 400
margin 20px auto 10px
color #ddd
a
text-decoration none
color #4BA2E2
.download
display inline-block
line-height 1
white-space nowrap
cursor pointer
background transparent
border 1px solid #d8dce5
color #ddd
-webkit-appearance none
text-align center
box-sizing border-box
outline none
margin 20px 12px
transition .1s
font-weight 500
user-select none
padding 12px 20px
font-size 14px
border-radius 20px
padding 12px 23px
transition .2s all ease-in-out
&:hover
background #ddd
color rgba(0,0,0, 0.7)
#container
position relative
text-align center
margin-top -10vh
z-index 3
.gallery
margin-bottom 60px
cursor pointer
transition all .2s ease-in-out
&:hover
transform scale(1.05)
.display-list
&__item
padding 48px
text-align left
background #2E2E2E
overflow hidden
&.o-item
background #fff
.display-list__desc
color #2E2E2E
img
width 100%
cursor pointer
transition all .2s ease-in-out
&:hover
transform scale(1.05)
&__content
padding-top 120px
&__title
color #4BA2E2
font-size 50px
&__desc
color #fff
margin-top 20px
.info
padding 48px 0
background #2E2E2E
color #fff
a
text-decoration none
color #fff
@media (max-width: 768px)
#header
padding 10vh
#container
.display-list
&__item
padding 24px 12px
&__content
padding-top 30px
&__title
font-size 25px
&__desc
margin-top 12px
</style>
+10
View File
@@ -0,0 +1,10 @@
import Vue from 'vue'
import App from './APP.vue'
import 'melody.css'
import axios from 'axios'
Vue.prototype.$http = axios
new Vue({
render: h => h(App)
}).$mount('#app')
+12
View File
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>PicGo</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
-1
View File
@@ -1 +0,0 @@
<!DOCTYPE html> <html lang=en> <head> <meta charset=UTF-8> <meta name=viewport content="width=device-width,initial-scale=1"> <meta http-equiv=X-UA-Compatible content="ie=edge"> <title>PicGo</title> </head> <body> <div id=app></div> <script type="text/javascript" src="index.js"></script></body> </html>
-14
View File
File diff suppressed because one or more lines are too long
+185
View File
@@ -0,0 +1,185 @@
{
"name": "picgo",
"version": "2.1.1",
"author": "Molunerfinn <marksz@teamsz.xyz>",
"description": "Easy to upload your pic & copy to write",
"license": "MIT",
"main": "./dist/electron/main.js",
"scripts": {
"render": "webpack-dev-server --hot --colors --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist",
"build": "node .electron-vue/build.js && electron-builder",
"release": "node .electron-vue/build.js && electron-builder",
"build:dir": "node .electron-vue/build.js && electron-builder --dir",
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
"dev": "node .electron-vue/dev-runner.js",
"e2e": "npm run pack && mocha test/e2e",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
"test": "npm run unit && npm run e2e",
"unit": "karma start test/unit/karma.conf.js",
"postinstall": "npm run lint:fix",
"build:docs": "cross-env NODE_ENV=production webpack -p --config .electron-vue/webpack.docs.config.js",
"docs": "webpack-dev-server --content-base docs/dist --config .electron-vue/webpack.docs.config.js --hot --inline",
"patch": "npm version patch && git push origin master && git push origin --tags",
"minor": "npm version minor && git push origin master && git push origin --tags",
"major": "npm version major && git push origin master && git push origin --tags",
"cz": "git-cz",
"bump": "bump-version"
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"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"
]
},
"build": {
"productName": "PicGo",
"appId": "com.molunerfinn.picgo",
"directories": {
"output": "build"
},
"files": [
"dist/electron/**/*"
],
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "build/icons/icon.icns",
"extendInfo": {
"LSUIElement": 1
}
},
"win": {
"icon": "build/icons/icon.ico",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons/"
}
},
"dependencies": {
"axios": "^0.16.1",
"dateformat": "^3.0.3",
"element-ui": "^2.4.11",
"fix-path": "^2.1.0",
"fs-extra": "^4.0.2",
"image-size": "^0.6.1",
"keycode": "^2.1.9",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"md5": "^2.2.1",
"melody.css": "^1.0.2",
"picgo": "^1.3.5",
"qiniu": "^7.1.1",
"vue": "^2.3.3",
"vue-electron": "^1.0.6",
"vue-gallery": "^1.2.4",
"vue-lazyload": "^1.2.6",
"vue-router": "^2.5.3",
"vuex": "^2.3.1"
},
"devDependencies": {
"@commitlint/cli": "^7.5.2",
"@picgo/bump-version": "^1.0.2",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.1",
"babel-plugin-istanbul": "^4.1.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.24.1",
"babili-webpack-plugin": "^0.1.2",
"cfonts": "^1.1.3",
"chai": "^4.0.0",
"chalk": "^2.1.0",
"commitizen": "^3.0.7",
"conventional-changelog": "^3.0.6",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^5.0.5",
"css-loader": "^0.28.4",
"cz-customizable": "^5.10.0",
"del": "^3.0.0",
"devtron": "^1.4.0",
"electron": "4.0.2",
"electron-builder": "^20.38.4",
"electron-debug": "^1.4.0",
"electron-devtools-installer": "^2.2.0",
"eslint": "^4.4.1",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^2.1.1",
"eslint-plugin-html": "^3.1.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.1.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"husky": "^1.3.1",
"inject-loader": "^3.0.0",
"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"
}
}
+81
View File
@@ -0,0 +1,81 @@
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
let clipboardFiles = getClipboardFiles()
if (!fs.pathExistsSync(path.join(STORE_PATH, 'windows.ps1'))) {
clipboardFiles.forEach(item => {
fs.copyFileSync(item.origin, item.dest)
})
} else {
clipboardFiles.forEach(item => {
diffFilesAndUpdate(item.origin, item.dest)
})
}
function diffFilesAndUpdate (filePath1, filePath2) {
let file1 = fs.readFileSync(filePath1)
let file2 = fs.readFileSync(filePath2)
if (!file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2)
}
}
function getClipboardFiles () {
let files = [
'/linux.sh',
'/mac.applescript',
'/windows.ps1'
]
return files.map(item => {
return {
origin: path.join(__static, item),
dest: path.join(STORE_PATH, item)
}
})
}
export default db
+56
View File
@@ -0,0 +1,56 @@
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
@@ -0,0 +1,24 @@
<!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>
+24
View File
@@ -0,0 +1,24 @@
/**
* 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')
+610
View File
@@ -0,0 +1,610 @@
'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 beforeOpen from './utils/beforeOpen'
import pasteTemplate from './utils/pasteTemplate'
import updateChecker from './utils/updateChecker'
import { getPicBeds } from './utils/getPicBeds'
import pkg from '../../package.json'
import picgoCoreIPC from './utils/picgoCoreIPC'
import fixPath from 'fix-path'
import { getUploadFiles } from './utils/handleArgv'
if (process.platform === 'darwin') {
beforeOpen()
}
/**
* Set `__static` path to static files in production
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
*/
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = require('path').join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
let window
let settingWindow
let miniWindow
let tray
let menu
let contextMenu
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080`
: `file://${__dirname}/index.html`
const settingWinURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/#setting/upload`
: `file://${__dirname}/index.html#setting/upload`
const miniWinURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/#mini-page`
: `file://${__dirname}/index.html#mini-page`
// 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()
}
},
{
label: '重启应用',
click () {
app.relaunch()
app.exit(0)
}
},
{
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()
})
return window
}
const createMiniWidow = () => {
if (miniWindow) {
return false
}
let obj = {
height: 64,
width: 64,
show: false,
frame: false,
fullscreenable: false,
skipTaskbar: true,
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('settings.miniWindowOntop').value()) {
obj.alwaysOnTop = true
}
miniWindow = new BrowserWindow(obj)
miniWindow.loadURL(miniWinURL)
miniWindow.on('closed', () => {
miniWindow = null
})
return miniWindow
}
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()
return settingWindow
}
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 || createSettingWindow()
}
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()
}
}
}
const uploadChoosedFiles = async (webContents, files) => {
const input = files.map(item => item.path)
const imgs = await new Uploader(input, webContents).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')
}
}
}
picgoCoreIPC(app, ipcMain)
// from macOS tray
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
const img = await new Uploader(undefined, 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', [])
if (settingWindow) {
settingWindow.webContents.send('updateGallery')
}
}
window.webContents.send('uploadFiles')
})
ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
uploadClipboardFiles()
})
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
return uploadChoosedFiles(evt.sender, files)
})
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 {
app.on('second-instance', (event, commandLine, workingDirectory) => {
let files = getUploadFiles(commandLine, workingDirectory)
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
if (files === null) {
uploadClipboardFiles()
} else {
let win
if (miniWindow && miniWindow.isVisible()) {
win = miniWindow
} else {
win = settingWindow || window || createSettingWindow()
}
uploadChoosedFiles(win.webContents, files)
}
} 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()
})
if (process.env.NODE_ENV !== 'development') {
let files = getUploadFiles()
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
if (files === null) {
uploadClipboardFiles()
} else {
let win
if (miniWindow && miniWindow.isVisible()) {
win = miniWindow
} else {
win = settingWindow || window || createSettingWindow()
}
uploadChoosedFiles(win.webContents, files)
}
}
}
})
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()
// }
// })
+23
View File
@@ -0,0 +1,23 @@
import fs from 'fs-extra'
import path from 'path'
import os from 'os'
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, '\\\\')
}
function beforeOpen () {
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)
}
}
}
export default beforeOpen
+27
View File
@@ -0,0 +1,27 @@
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
}
+125
View File
@@ -0,0 +1,125 @@
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 []
}
/**
* For notification
* @param {Object} options
*/
showNotification (options = {
title: '',
body: ''
}) {
const notification = new Notification({
title: options.title,
body: options.body
})
notification.show()
}
/**
*
* @param {Object} options
*/
showMessageBox (options = {
title: '',
message: '',
type: 'info',
buttons: ['Yes', 'No']
}) {
return new Promise((resolve, reject) => {
dialog.showMessageBox(
BrowserWindow.fromWebContents(this[WEBCONTENTS]),
options,
(result, checkboxChecked) => {
resolve({
result,
checkboxChecked
})
})
})
}
}
export default GuiApi
+37
View File
@@ -0,0 +1,37 @@
import path from 'path'
import fs from 'fs-extra'
const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
let files = argv.slice(1)
if (files.length > 0 && files[0] === 'upload') {
if (files.length === 1) {
return null // for uploading images in clipboard
} else if (files.length > 1) {
files = argv.slice(1)
let result = []
if (files.length > 0) {
result = files.map(item => {
if (path.isAbsolute(item)) {
return {
path: item
}
} else {
let tempPath = path.join(cwd, item)
if (fs.existsSync(tempPath)) {
return {
path: tempPath
}
} else {
return null
}
}
}).filter(item => item !== null)
}
return result
}
}
return []
}
export {
getUploadFiles
}
+13
View File
@@ -0,0 +1,13 @@
import db from '../../datastore'
export default (style, url) => {
const customLink = db.read().get('settings.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]
}
+200
View File
@@ -0,0 +1,200 @@
import path from 'path'
import GuiApi from './guiApi'
import { dialog, shell } from 'electron'
// 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)
}
let gui = false
if (pluginPKG.keywords && pluginPKG.keywords.length > 0) {
if (pluginPKG.keywords.includes('picgo-gui-plugin')) {
gui = true
}
}
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,
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(`picgoPlugins.${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', async (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-/, ''))
})
picgo.on('failed', () => {
handleNPMError()
})
await pluginHandler.uninstall([msg])
pluginHandler.install([msg])
picgo.cmd.program.removeAllListeners()
})
}
const handlePluginUninstall = (ipcMain, CONFIG_PATH) => {
ipcMain.on('uninstallPlugin', async (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-/, ''))
})
picgo.on('failed', () => {
handleNPMError()
})
await pluginHandler.uninstall([msg])
picgo.cmd.program.removeAllListeners()
})
}
const handlePluginUpdate = (ipcMain, CONFIG_PATH) => {
ipcMain.on('updatePlugin', async (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-/, ''))
})
picgo.on('failed', () => {
handleNPMError()
})
await pluginHandler.update([msg])
picgo.cmd.program.removeAllListeners()
})
}
const handleNPMError = () => {
dialog.showMessageBox({
title: '发生错误',
message: '请安装Node.js并重启PicGo再继续操作',
buttons: ['Yes']
}, (res) => {
if (res === 0) {
shell.openExternal('https://nodejs.org/')
}
})
}
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(picgo).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)
const guiApi = new GuiApi(ipcMain, event.sender, picgo)
setTimeout(() => {
picgo.emit('remove', files, guiApi)
}, 500)
})
}
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)
}
+59
View File
@@ -0,0 +1,59 @@
import { dialog, shell } from 'electron'
import db from '../../datastore'
import axios from 'axios'
import pkg from '../../../package.json'
const version = pkg.version
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
const checkVersion = async () => {
let showTip = db.read().get('settings.showUpdateTip').value()
if (showTip === undefined) {
db.read().set('settings.showUpdateTip', true).write()
showTip = true
}
if (showTip) {
const res = await axios.get(release)
if (res.status === 200) {
const latest = res.data.name
const result = compareVersion2Update(version, latest)
if (result) {
dialog.showMessageBox({
type: 'info',
title: '发现新版本',
buttons: ['Yes', 'No'],
message: `发现新版本${latest},更新了很多功能,是否去下载最新的版本?`,
checkboxLabel: '以后不再提醒',
checkboxChecked: false
}, (res, checkboxChecked) => {
if (res === 0) { // if selected yes
shell.openExternal(downloadUrl)
}
db.read().set('settings.showUpdateTip', !checkboxChecked).write()
})
}
} else {
return false
}
} else {
return false
}
}
// if true -> update else return false
const compareVersion2Update = (current, latest) => {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
for (let i = 0; i < 3; i++) {
if (currentVersion[i] < latestVersion[i]) {
return true
}
if (currentVersion[i] > latestVersion[i]) {
return false
}
}
return false
}
export default checkVersion
+156
View File
@@ -0,0 +1,156 @@
import {
app,
Notification,
BrowserWindow,
ipcMain
} from 'electron'
import path from 'path'
import dayjs from 'dayjs'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
const PicGo = requireFunc('picgo')
const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const renameURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080/#rename-page` : `file://${__dirname}/index.html#rename-page`
const createRenameWindow = (win) => {
let options = {
height: 175,
width: 300,
show: true,
fullscreenable: false,
resizable: false,
vibrancy: 'ultra-dark',
webPreferences: {
backgroundThrottling: false
}
}
if (process.platform !== 'darwin') {
options.show = true
options.backgroundColor = '#3f3c37'
options.autoHideMenuBar = true
options.transparent = false
}
const window = new BrowserWindow(options)
window.loadURL(renameURL)
// check if this window is visible
if (win.isVisible()) {
// bounds: { x: 821, y: 75, width: 800, height: 450 }
const bounds = win.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)
}
return window
}
const waitForShow = (webcontent) => {
return new Promise((resolve, reject) => {
webcontent.on('dom-ready', () => {
resolve()
})
})
}
const waitForRename = (window, id) => {
return new Promise((resolve, reject) => {
ipcMain.once(`rename${id}`, (evt, newName) => {
resolve(newName)
window.hide()
})
window.on('close', () => {
resolve(null)
ipcMain.removeAllListeners(`rename${id}`)
})
})
}
class Uploader {
constructor (img, webContents, picgo = undefined) {
this.img = img
this.webContents = webContents
this.picgo = picgo
}
upload () {
const win = BrowserWindow.fromWebContents(this.webContents)
const picgo = this.picgo || new PicGo(CONFIG_PATH)
picgo.config.debug = true
// for picgo-core
picgo.config.PICGO_ENV = 'GUI'
let input = this.img
picgo.helper.beforeUploadPlugins.register('renameFn', {
handle: async ctx => {
const rename = picgo.getConfig('settings.rename')
const autoRename = picgo.getConfig('settings.autoRename')
await Promise.all(ctx.output.map(async (item, index) => {
let name
let fileName
if (autoRename) {
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
} else {
fileName = item.fileName
}
if (rename) {
const window = createRenameWindow(win)
await waitForShow(window.webContents)
window.webContents.send('rename', fileName, window.webContents.id)
name = await waitForRename(window, window.webContents.id)
}
item.fileName = name || fileName
}))
}
})
picgo.on('beforeTransform', ctx => {
if (ctx.getConfig('settings.uploadNotification')) {
const notification = new Notification({
title: '上传进度',
body: '正在上传'
})
notification.show()
}
})
picgo.upload(input)
picgo.on('notification', message => {
const notification = new Notification(message)
notification.show()
})
picgo.on('uploadProgress', progress => {
this.webContents.send('uploadProgress', progress)
})
return new Promise((resolve) => {
picgo.on('finished', ctx => {
if (ctx.output.every(item => item.imgUrl)) {
resolve(ctx.output)
} else {
resolve(false)
}
})
picgo.on('failed', ctx => {
const notification = new Notification({
title: '上传失败',
body: '请检查配置和上传的文件是否符合要求'
})
notification.show()
resolve(false)
})
})
}
}
export default Uploader
+27
View File
@@ -0,0 +1,27 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'picgo'
}
</script>
<style lang="stylus">
body,
html
padding 0
margin 0
height 100%
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
#app
user-select none
overflow hidden
.el-button-group
width 100%
.el-button
width 50%
</style>
View File
+29
View File
@@ -0,0 +1,29 @@
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1523001890286'); /* IE9*/
src: url('iconfont.eot?t=1523001890286#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAApcAAsAAAAADqAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kmaY21hcAAAAYAAAACMAAAB5Jz6bNVnbHlmAAACDAAABisAAAfkf7GmJ2hlYWQAAAg4AAAALwAAADYQ+dpBaGhlYQAACGgAAAAcAAAAJAfeA4lobXR4AAAIhAAAABQAAAAgH+kAAGxvY2EAAAiYAAAAEgAAABII+gbebWF4cAAACKwAAAAfAAAAIAEYAMxuYW1lAAAIzAAAAUUAAAJtPlT+fXBvc3QAAAoUAAAASAAAAF2Rted1eJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/ss4gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDxfzdzwv4EhhrmBoQEozAiSAwAxwA0deJzFkdEJwzAMRE9ymoRiSv+TETpK6BQZwvSr+2QvZYz0ZLmFTpAzz/gOGRkLwAVAIg/SAfKGwPViKjVPuNa8w5M+Y4TyXExttmXfjoNpMbHp574SVmfccOdy12Ngv8TbStvjNMl5rf+V6742N5DS4BOtwX+DaeA1NgU+O5sDn6Etgc9x3wLoBwU0H794nG1VXYwcRxHu6p7unt+enf/9m9293buZde5u97w/M3Zs7xof2AoJPsLJ4JMR52AUEUgiJcLxQ2znEEIkEEQiJGOQIl0QCDlKpCgP5MXipAiJl5N44S0IAvjVQkGKBUIs9OzFEkjslGqner7pmar6vhpEEfr3n8htUkYe6qLD6JPoswgBW4a2wDEspKMeXoZggQaRL0jaSRd4p90jJyBqMz8cZKMkYpzZIKABw4VBlvZwCuPRBB+DQRgDVGrVTXep7pJXQC+njW/PPo1/CkGzU7cnq7OHVqb+oOWpV0zXrbjuyyqjVMVYsQU8FYUa1XQ2+xm1q8Ht5iHcBLOSVh/Zslo197EXR0/HS5EGsLMDXq0lfjF1qo60q9XQcyu8ZKnlqtVZ9OHKHaPsmXHyFyR/IHP9DXmP5GhdBgJ4A6IwS8csyXvAkyxqQNCAfAIyTEeZ9HkSRtkgmgAPmUQH83tiYOEBCKO9GcW3XirZ8MUJLrVwtaK0O62zgWLrG8+Tcki5A8dP5t/ITx4Hh9OwTK6eNV2wnRdvYTojOZ3t3bzTJ7a+KSY0rYOvAU3JGphhQ6UXri+diLRpn6iHbPuQSvpTLTqxdP0C1Q+1ntdt0r9zc29W5MXmed0jWyhEPfQw+gK6hJ6UGfqsnaR5RybDxz0YZVPZpOgwbyfjUZEh6xSL+bBYDHzGi/y5mJu8mmdunoWc8QnIdXpQrXnWRF6VJzIUkN6nAflIhAIA8EuPq5ppaurVn5MbhsDKtK9QQ8iFM9vYvtJIRj1Cnjj/4Ksbb/6TQfSvM0fOYyArF3vThdmPX7ilKLde2Cn87voWxlvrpwp/LozjlTjGC4ZtG8qTPyDyMSIQGJS3vtWwDUYPnyIY7FBg/JWHFcM+rWjidHZs90i3D+SNndJ3oOrw/indrNVVcWkDP3d+6zLGl7fOPze7CZuTySbM/W5ztSkNIVwUlXyEP0Cvob0DpgiwD/64wHNPAj8KZWGGgyyfKAUdRknao2lSKCDpQ/Ix3iapLJe0ZJRPyHAQNkHWawqyZH0oliV0LLswIVOYSM3ILaU1YVBsOcjmmLlJXBFAgwSSiwfvIoXHWmnRWAnOpjgL4XfNMyFXmKgtW1xlinfE1lvML5l0fXnoXvr97aeZs8itmpmugKYQsLnhirrgbFEjRjSo+HpDCybR5742XrBj3n3QUAittAdR/WhtrChO4HBDjWhiV5dMZlK93F4JAu61PSte9a2KZmCTVayKzhlRMYms0lo5wrxW/RSMa4stGzChhiq6PjXLq7O7Xs3fI3WdcuGwNn6f1koKlnsebdiVqARlTY1ZPU7a3vb6A7ZKACuawhy99aWhc1hjPNZc1yxToli44J3GlRIvHYk2Hh/JCPi4ay1bfrfu1ip9InxXxQb3adPwBCWgLOpuq1lmFMtxwyPHjG1HngLTbVUQtWG7S4aBsaYqs10eJN16ydZLoeZWFQwFrua/qwSUEEpBDhVF6m+XfEAuIhOV0SKaILQk2zicwCiRg5RBiKIM5QlKGYruK0dqLGey1ccg8EM5LbN8XHBJdjxpc/g1lKzjluNYx01ndoGCvb8PNqWzD/f3Zx/m9O4779ylc39PEFwyLsyBsGWUMBEvmy3zWRmBU3GklzfQ/97g/p3S/80JDXGjeMwNYYTO65ZVzEjJ/b/iX6I1yfyMFYOvXTAw6hXan7NTWh5KysohmRwwsQCEw8WMvO+VFfzHf2x8QnRjunMZcGhd+y5zTH9j8/PbluWDGrZyXVxa9mtC0hbt48t9UMR7wl47ybazHarYj7x76tFXGkcbGMdrR39icd1tf9+xHMu9tmGYf74/x39LfkVOo20ZJGky/wB1Pn4XKUt5yK+SfOEsTbK5kKTgChxnbnFxLpjhfIjLJJLikJWX048daKtTCKvA5RkmUK7oPntWq9i60X2o6vsYjDLFnhss6UIFJap2Lo4D68uU6tfunXti+XvVuubzZ7SqMHSJDzyJjyjxPD/RBFfC2hz+GGPa9b+f+2of/8GRfVF/ZJiStkmnNl2wqFn1mowGK55uPvDo4mofnG+qLul85uuvzt5+xi/9D7xt/l+4o3TOPvVD2JDl+g9+5y00AHicY2BkYGAA4vdhFhPj+W2+MnCzMIDAtbfeSgj6/wIWBuYEIJeDgQkkCgAriQovAHicY2BkYGBu+N/AEMPCAAJAkpEBFXAAAEcOAnF4nGNhYGBgfsnAwMKAHQMAGtcBCQAAAAAAdgDiAYoCogMIA1oD8gAAeJxjYGRgYOBgOMDAxgACTEDMBYQMDP/BfAYAHGwB5QB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxtyMEOQDAQBNCZslX+UhE2kbaSbvD3JK7e8cHhM+BfoGPDlkLPjoE9ePlV62ZRzkVjljrdlryVPY+zHJrUxMpbwANFHA6a') format('woff'),
url('iconfont.ttf?t=1523001890286') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1523001890286#iconfont') format('svg'); /* iOS 4.1- */
}
[class*=" el-icon-ui"], [class^=el-icon-ui] {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.el-icon-ui-github:before { content: "\e7ab"; }
.el-icon-ui-weibo:before { content: "\e61c"; }
.el-icon-ui-tcyun:before { content: "\e64c"; }
.el-icon-ui-upload:before { content: "\e61b"; }
.el-icon-ui-qiniu:before { content: "\e601"; }
.el-icon-ui-upyun:before { content: "\e602"; }
Binary file not shown.
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+37
View File
@@ -0,0 +1,37 @@
<template>
<div id="choose-pic-bed">
<span>选择 {{ label }} 作为你默认图床</span>
<el-switch
v-model="value"
@change="choosePicBed"
>
</el-switch>
</div>
</template>
<script>
export default {
name: 'choose-pic-bed',
props: {
type: String,
label: String
},
data () {
return {
value: false
}
},
created () {
if (this.type === this.$db.get('picBed.current').value()) {
this.value = true
}
},
methods: {
choosePicBed (val) {
this.$db.set('picBed.current', this.type)
this.$emit('update:choosed', this.type)
}
}
}
</script>
<style lang='stylus'>
</style>
+142
View File
@@ -0,0 +1,142 @@
<template>
<div id="config-form">
<el-form
label-position="right"
label-width="120px"
:model="ruleForm"
ref="form"
size="mini"
>
<el-form-item
v-for="(item, index) in configList"
:label="item.name"
:required="item.required"
:prop="item.name"
:key="item.name + index"
>
<el-input
v-if="item.type === 'input' || item.type === 'password'"
:type="item.type === 'password' ? 'password' : 'input'"
v-model="ruleForm[item.name]"
:placeholder="item.message || item.name"
></el-input>
<el-select
v-else-if="item.type === 'list'"
v-model="ruleForm[item.name]"
:placeholder="item.message || item.name"
>
<el-option
v-for="(choice, idx) in item.choices"
:label="choice.name || choice.value || choice"
:key="choice.name || choice.value || choice"
:value="choice.value || choice"
></el-option>
</el-select>
<el-select
v-else-if="item.type === 'checkbox'"
v-model="ruleForm[item.name]"
:placeholder="item.message || item.name"
multiple
collapse-tags
>
<el-option
v-for="(choice, idx) in item.choices"
:label="choice.name || choice.value || choice"
:key="choice.value || choice"
:value="choice.value || choice"
></el-option>
</el-select>
<el-switch
v-else-if="item.type === 'confirm'"
v-model="ruleForm[item.name]"
active-text="yes"
inactive-text="no"
>
</el-switch>
</el-form-item>
<slot></slot>
</el-form>
</div>
</template>
<script>
import { cloneDeep, union } from 'lodash'
export default {
name: 'config-form',
props: {
config: Array,
type: String,
id: {
type: String,
default: ''
}
},
data () {
return {
configList: [],
ruleForm: {}
}
},
created () {
},
watch: {
config: {
deep: true,
handler (val) {
this.ruleForm = Object.assign({}, {})
const config = this.$db.read().get(`picBed.${this.id}`).value()
if (val.length > 0) {
this.configList = cloneDeep(val).map(item => {
let defaultValue = item.default !== undefined
? item.default : item.type === 'checkbox'
? [] : null
if (item.type === 'checkbox') {
const defaults = item.choices.filter(i => {
return i.checked
}).map(i => i.value)
defaultValue = union(defaultValue, defaults)
}
if (config && config[item.name] !== undefined) {
defaultValue = config[item.name]
}
this.$set(this.ruleForm, item.name, defaultValue)
return item
})
}
},
immediate: true
}
},
methods: {
async validate () {
return new Promise((resolve, reject) => {
this.$refs.form.validate(valid => {
if (valid) {
resolve(this.ruleForm)
} else {
resolve(false)
return false
}
})
})
}
}
}
</script>
<style lang='stylus'>
#config-form
.el-form
label
line-height 22px
padding-bottom 0
.el-button-group
width 100%
.el-button
width 50%
.el-input__inner
border-radius 19px
.el-radio-group
margin-left 25px
.el-switch__label
&.is-active
color #409EFF
</style>
+456
View File
@@ -0,0 +1,456 @@
<template>
<div id="setting-page">
<div class="fake-title-bar" :class="{ 'darwin': os === 'darwin' }">
<div class="fake-title-bar__title">
PicGo - {{ version }}
</div>
<div class="handle-bar" v-if="os !== 'darwin'">
<i class="el-icon-minus" @click="minimizeWindow"></i>
<i class="el-icon-circle-plus-outline" @click="openMiniWindow"></i>
<i class="el-icon-close" @click="closeWindow"></i>
</div>
</div>
<el-row style="padding-top: 22px;" class="main-content">
<el-col :span="5" class="side-bar-menu">
<el-menu
class="picgo-sidebar"
:default-active="defaultActive"
@select="handleSelect"
:unique-opened="true"
>
<el-menu-item index="upload">
<i class="el-icon-upload"></i>
<span slot="title">上传区</span>
</el-menu-item>
<el-menu-item index="gallery">
<i class="el-icon-picture"></i>
<span slot="title">相册</span>
</el-menu-item>
<el-submenu
index="sub-menu"
>
<template slot="title">
<i class="el-icon-menu"></i>
<span>图床设置</span>
</template>
<template
v-for="item in picBed"
>
<el-menu-item
v-if="item.visible"
:index="`picbeds-${item.type}`"
:key="item.type"
>
<!-- <i :class="`el-icon-ui-${item.type}`"></i> -->
<span slot="title">{{ item.name }}</span>
</el-menu-item>
</template>
</el-submenu>
<el-menu-item index="setting">
<i class="el-icon-setting"></i>
<span slot="title">PicGo设置</span>
</el-menu-item>
<el-menu-item index="plugin">
<i class="el-icon-share"></i>
<span slot="title">插件设置</span>
</el-menu-item>
</el-menu>
<i class="el-icon-info setting-window" @click="openDialog"></i>
</el-col>
<el-col
:span="19"
:offset="5"
style="height: 428px"
class="main-wrapper"
:class="{ 'darwin': os === 'darwin' }">
<transition name="picgo-fade" mode="out-in">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
</transition>
<transition name="picgo-fade" mode="out-in">
<router-view :key="$route.path" v-if="!$route.meta.keepAlive"></router-view>
</transition>
</el-col>
</el-row>
<el-dialog
title="赞助PicGo"
:visible.sync="visible"
width="70%"
top="10vh"
>
PicGo是免费开源的软件如果你喜欢它对你有帮助不妨请我喝杯咖啡
<el-row class="support">
<el-col :span="12">
<img src="https://user-images.githubusercontent.com/12621342/34188165-e7cdf372-e56f-11e7-8732-1338c88b9bb7.jpg" alt="支付宝">
<div class="support-title">支付宝</div>
</el-col>
<el-col :span="12">
<img src="https://user-images.githubusercontent.com/12621342/34188201-212cda84-e570-11e7-9b7a-abb298699d85.jpg" alt="支付宝">
<div class="support-title">微信</div>
</el-col>
</el-row>
</el-dialog>
<el-dialog
title="修改快捷键"
:visible.sync="keyBindingVisible"
>
<el-form
label-width="80px"
>
<el-form-item
label="快捷上传"
>
<el-input
class="align-center"
@keydown.native.prevent="keyDetect('upload', $event)"
v-model="shortKey.upload"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelKeyBinding">取消</el-button>
<el-button type="primary" @click="confirmKeyBinding">确定</el-button>
</span>
</el-dialog>
<el-dialog
title="自定义链接格式"
:visible.sync="customLinkVisible"
>
<el-form
label-position="top"
:model="customLink"
ref="customLink"
:rules="rules"
>
<el-form-item
label="用占位符$url来表示url的位置"
prop="value"
>
<el-input
class="align-center"
v-model="customLink.value"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<div>
[]($url)
</div>
<span slot="footer">
<el-button @click="cancelCustomLink">取消</el-button>
<el-button type="primary" @click="confirmCustomLink">确定</el-button>
</span>
</el-dialog>
<el-dialog
:title="inputBoxOptions.title || '输入框'"
:visible.sync="showInputBoxVisible"
:modal-append-to-body="false"
@close="handleInputBoxClose"
>
<el-input
v-model="inputBoxValue"
:placeholder="inputBoxOptions.placeholder"></el-input>
<span slot="footer">
<el-button @click="showInputBoxVisible = false" round>取消</el-button>
<el-button type="primary" @click="showInputBoxVisible = false" round>确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import pkg from 'root/package.json'
import keyDetect from 'utils/key-binding'
import { remote } from 'electron'
import db from '~/datastore'
import mixin from '@/utils/mixin'
const { Menu, dialog, BrowserWindow } = remote
export default {
name: 'setting-page',
mixins: [mixin],
data () {
const customLinkRule = (rule, value, callback) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
return {
version: process.env.NODE_ENV === 'production' ? pkg.version : 'Dev',
defaultActive: 'upload',
menu: null,
visible: false,
keyBindingVisible: false,
customLinkVisible: false,
customLink: {
value: db.read().get('customLink').value() || '$url'
},
rules: {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
},
os: '',
shortKey: {
upload: db.read().get('shortKey.upload').value()
},
picBed: [],
// for showInputBox
showInputBoxVisible: false,
inputBoxValue: '',
inputBoxOptions: {
title: '',
placeholder: ''
}
}
},
created () {
this.os = process.platform
this.buildMenu()
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
this.$electron.ipcRenderer.on('showInputBox', (evt, options) => {
this.inputBoxValue = ''
this.inputBoxOptions.title = options.title || ''
this.inputBoxOptions.placeholder = options.placeholder || ''
this.showInputBoxVisible = true
})
},
methods: {
handleSelect (index) {
const type = index.match(/picbeds-/)
if (type === null) {
this.$router.push({
name: index
})
} else {
const picBed = index.replace(/picbeds-/, '')
if (this.$builtInPicBed.includes(picBed)) {
this.$router.push({
name: picBed
})
} else {
this.$router.push({
name: 'others',
params: {
type: picBed
}
})
}
}
},
minimizeWindow () {
const window = BrowserWindow.getFocusedWindow()
window.minimize()
},
closeWindow () {
const window = BrowserWindow.getFocusedWindow()
window.close()
},
buildMenu () {
const _this = this
const template = [
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '赞助PicGo',
click () {
_this.visible = true
}
}
]
this.menu = Menu.buildFromTemplate(template)
},
openDialog () {
this.menu.popup(remote.getCurrentWindow())
},
keyDetect (type, event) {
this.shortKey[type] = keyDetect(event).join('+')
},
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = db.read().get('shortKey').value()
},
confirmKeyBinding () {
const oldKey = db.read().get('shortKey').value()
db.read().set('shortKey', this.shortKey).write()
this.keyBindingVisible = false
this.$electron.ipcRenderer.send('updateShortKey', oldKey)
},
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = db.read().get('customLink').value() || '$url'
},
confirmCustomLink () {
this.$refs.customLink.validate((valid) => {
if (valid) {
db.read().set('customLink', this.customLink.value).write()
this.customLinkVisible = false
this.$electron.ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
},
openMiniWindow () {
this.$electron.ipcRenderer.send('openMiniWindow')
},
getPicBeds (event, picBeds) {
this.picBed = picBeds
},
handleInputBoxClose () {
this.$electron.ipcRenderer.send('showInputBox', this.inputBoxValue)
}
},
beforeRouteEnter: (to, from, next) => {
next(vm => {
vm.defaultActive = to.name
})
},
beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
this.$electron.ipcRenderer.removeAllListeners('showInputBox')
}
}
</script>
<style lang='stylus'>
$darwinBg = transparentify(#172426, #000, 0.7)
.picgo-fade
&-enter,
&-leave,
&-leave-active
opacity 0
&-enter-active,
&-leave-active
transition all 100ms linear
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
#setting-page
.fake-title-bar
-webkit-app-region drag
height h = 22px
width 100%
text-align center
color #eee
font-size 12px
line-height h
position fixed
z-index 100
&.darwin
background transparent
background-image linear-gradient(
to right,
transparent 0%,
transparent 167px,
$darwinBg 167px,
$darwinBg 100%
)
.fake-title-bar__title
padding-left 167px
.handle-bar
position absolute
top 2px
right 4px
width 60px
z-index 10000
-webkit-app-region no-drag
i
cursor pointer
font-size 16px
.el-icon-minus
&:hover
color #409EFF
.el-icon-close
&:hover
color #F15140
.el-icon-circle-plus-outline
&:hover
color #69C282
.main-wrapper
&.darwin
background $darwinBg
.side-bar-menu
position fixed
height calc(100vh - 22px)
overflow-x hidden
overflow-y auto
width 170px
.el-icon-info.setting-window
position fixed
bottom 4px
left 4px
cursor poiter
color #878d99
transition .2s all ease-in-out
&:hover
color #409EFF
.el-menu
border-right none
background transparent
width 170px
&-item
color #eee
position relative
&:focus,
&:hover
color #fff
background transparent
&.is-active
color active-color = #409EFF
&:before
content ''
position absolute
width 3px
height 20px
right 0
top 18px
background active-color
.el-submenu__title
span
color #eee
&:hover
background transparent
span
color #fff
.el-submenu
.el-menu-item
min-width 166px
&.is-active
&:before
top 16px
.main-content
padding-top 22px
position relative
z-index 10
.el-dialog__body
padding 20px
.support
text-align center
&-title
text-align center
color #878d99
.align-center
input
text-align center
*::-webkit-scrollbar
width 8px
height 8px
*::-webkit-scrollbar-thumb
border-radius 4px
background #6f6f6f
*::-webkit-scrollbar-track
background-color transparent
</style>
+40
View File
@@ -0,0 +1,40 @@
import Vue from 'vue'
import axios from 'axios'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App'
import router from './router'
import store from './store'
import db from '../datastore/index'
import { webFrame } from 'electron'
import './assets/fonts/iconfont.css'
import VueLazyLoad from 'vue-lazyload'
Vue.use(ElementUI)
Vue.use(VueLazyLoad)
webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0)
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
Vue.http = Vue.prototype.$http = axios
Vue.prototype.$db = db
Vue.prototype.$builtInPicBed = [
'smms',
'weibo',
'imgur',
'qiniu',
'tcyun',
'upyun',
'aliyun',
'github'
]
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app')
+462
View File
@@ -0,0 +1,462 @@
<template>
<div id="gallery-view">
<div class="view-title">
相册 - {{ filterList.length }} <i class="el-icon-caret-bottom" @click="toggleHandleBar" :class="{'active': handleBarActive}"></i>
</div>
<transition name="el-zoom-in-top">
<el-row v-show="handleBarActive">
<el-col :span="20" :offset="2">
<el-row class="handle-bar" :gutter="16">
<el-col :span="12">
<el-select
v-model="choosedPicBed"
multiple
collapse-tags
size="mini"
style="width: 100%"
placeholder="请选择显示的图床">
<el-option
v-for="item in picBed"
:key="item.type"
:label="item.name"
:value="item.type">
</el-option>
</el-select>
</el-col>
<el-col :span="12">
<el-select
v-model="pasteStyle"
size="mini"
style="width: 100%"
@change="handlePasteStyleChange"
placeholder="请选择粘贴的格式">
<el-option
v-for="(value, key) in pasteStyleMap"
:key="key"
:label="key"
:value="value">
</el-option>
</el-select>
</el-col>
</el-row>
<el-row class="handle-bar" :gutter="16">
<el-col :span="12">
<el-input
placeholder="搜索"
size="mini"
v-model="searchText">
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
</el-input>
</el-col>
<el-col :span="6">
<div class="item-base copy round" :class="{ active: isMultiple(choosedList)}" @click="multiCopy">
<i class="el-icon-document"></i> 批量复制
</div>
</el-col>
<el-col :span="6">
<div class="item-base delete round" :class="{ active: isMultiple(choosedList)}" @click="multiRemove">
<i class="el-icon-delete"></i> 批量删除
</div>
</el-col>
</el-row>
</el-col>
</el-row>
</transition>
<el-row class="gallery-list" :class="{ small: handleBarActive }">
<el-col :span="20" :offset="2">
<el-row :gutter="16">
<gallerys
:images="images"
:index="idx"
@close="handleClose"
:options="options"
></gallerys>
<el-col :span="6" v-for="(item, index) in images" :key="item.id" class="gallery-list__img">
<div
class="gallery-list__item"
@click="zoomImage(index)"
>
<img v-lazy="item.imgUrl" class="gallery-list__item-img">
</div>
<div class="gallery-list__tool-panel">
<i class="el-icon-document" @click="copy(item)"></i>
<i class="el-icon-edit-outline" @click="openDialog(item)"></i>
<i class="el-icon-delete" @click="remove(item.id)"></i>
<el-checkbox v-model="choosedList[item.id]" class="pull-right" @change=" handleBarActive = true"></el-checkbox>
</div>
</el-col>
</el-row>
</el-col>
</el-row>
<el-dialog
:visible.sync="dialogVisible"
title="修改图片URL"
width="500px"
:modal-append-to-body="false"
>
<el-input v-model="imgInfo.imgUrl"></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="confirmModify"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import gallerys from 'vue-gallery'
import pasteStyle from '~/main/utils/pasteTemplate'
export default {
name: 'gallery',
components: {
gallerys
},
data () {
return {
images: [],
idx: null,
options: {
titleProperty: 'fileName',
urlProperty: 'imgUrl',
closeOnSlideClick: true
},
dialogVisible: false,
imgInfo: {
id: null,
imgUrl: ''
},
choosedList: {},
choosedPicBed: [],
searchText: '',
handleBarActive: false,
pasteStyle: '',
pasteStyleMap: {
Markdown: 'markdown',
HTML: 'HTML',
URL: 'URL',
UBB: 'UBB',
Custom: 'Custom'
},
picBed: []
}
},
beforeRouteEnter (to, from, next) {
next(vm => {
vm.getGallery()
vm.getPasteStyle()
vm.getPicBeds()
})
},
created () {
this.$electron.ipcRenderer.on('updateGallery', (event) => {
this.$nextTick(() => {
this.filterList = this.getGallery()
})
})
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
computed: {
filterList: {
get () {
return this.getGallery()
},
set (val) {
return this.val
}
}
},
methods: {
getPicBeds (event, picBeds) {
this.picBed = picBeds
},
getGallery () {
if (this.choosedPicBed.length > 0) {
let arr = []
this.choosedPicBed.forEach(item => {
let obj = {
type: item
}
if (this.searchText) {
obj.fileName = this.searchText
}
arr = arr.concat(this.$db.read().get('uploaded').filter(obj => {
return obj.fileName.indexOf(this.searchText) !== -1 && obj.type === item
}).reverse().value())
})
this.images = arr
} else {
if (this.searchText) {
let data = this.$db.read().get('uploaded')
.filter(item => {
return item.fileName.indexOf(this.searchText) !== -1
}).reverse().value()
this.images = data
} else {
this.images = this.$db.read().get('uploaded').slice().reverse().value()
}
}
return this.images
},
zoomImage (index) {
this.idx = index
this.changeZIndexForGallery(true)
},
changeZIndexForGallery (isOpen) {
if (isOpen) {
document.querySelector('.main-content.el-row').style.zIndex = 101
} else {
document.querySelector('.main-content.el-row').style.zIndex = 10
}
},
handleClose () {
this.idx = null
this.changeZIndexForGallery(false)
},
copy (item) {
const url = item.url || item.imgUrl
const style = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
const copyLink = pasteStyle(style, url)
const obj = {
title: '复制链接成功',
body: copyLink,
icon: url
}
const myNotification = new window.Notification(obj.title, obj)
this.$electron.clipboard.writeText(copyLink)
myNotification.onclick = () => {
return true
}
},
remove (id) {
this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const file = this.$db.read().get('uploaded').getById(id).value()
this.$db.read().get('uploaded').removeById(id).write()
this.$electron.ipcRenderer.send('removeFiles', [file])
const obj = {
title: '操作结果',
body: '删除成功'
}
const myNotification = new window.Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
this.getGallery()
}).catch(() => {
return true
})
},
openDialog (item) {
this.imgInfo.id = item.id
this.imgInfo.imgUrl = item.imgUrl
this.dialogVisible = true
},
confirmModify () {
this.$db.read().get('uploaded')
.getById(this.imgInfo.id)
.assign({imgUrl: this.imgInfo.imgUrl})
.write()
const obj = {
title: '修改图片URL成功',
body: this.imgInfo.imgUrl,
icon: this.imgInfo.imgUrl
}
const myNotification = new window.Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
this.dialogVisible = false
this.getGallery()
},
choosePicBed (type) {
let idx = this.choosedPicBed.indexOf(type)
if (idx !== -1) {
this.choosedPicBed.splice(idx, 1)
} else {
this.choosedPicBed.push(type)
}
},
cleanSearch () {
this.searchText = ''
},
isMultiple (obj) {
return Object.values(obj).some(item => item)
},
multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
if (Object.values(this.choosedList).some(item => item)) {
this.$confirm('将删除刚才选中的图片,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let files = []
Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) {
const file = this.$db.read().get('uploaded').getById(key).value()
files.push(file)
this.$db.read().get('uploaded').removeById(key).write()
}
})
this.choosedList = {}
this.getGallery()
const obj = {
title: '操作结果',
body: '删除成功'
}
this.$electron.ipcRenderer.send('removeFiles', files)
const myNotification = new window.Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
}).catch(() => {
return true
})
}
},
multiCopy () {
if (Object.values(this.choosedList).some(item => item)) {
let copyString = ''
const style = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) {
const item = this.$db.read().get('uploaded').getById(key).value()
const url = item.url || item.imgUrl
copyString += pasteStyle(style, url) + '\n'
this.choosedList[key] = false
}
})
const obj = {
title: '批量复制链接成功',
body: copyString
}
const myNotification = new window.Notification(obj.title, obj)
this.$electron.clipboard.writeText(copyString)
myNotification.onclick = () => {
return true
}
}
},
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
},
getPasteStyle () {
this.pasteStyle = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
},
handlePasteStyleChange (val) {
this.$db.read().set('settings.pasteStyle', val)
.write()
this.pasteStyle = val
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('updateGallery')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
}
</script>
<style lang='stylus'>
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
.sub-title
font-size 14px
.el-icon-caret-bottom
cursor: pointer
transition all .2s ease-in-out
&.active
transform: rotate(180deg)
#gallery-view
height 100%
.item-base
background #2E2E2E
text-align center
padding 5px 0
cursor pointer
font-size 13px
transition all .2s ease-in-out
height: 28px
box-sizing: border-box
&.copy
cursor not-allowed
background #49B1F5
&.active
cursor pointer
background #1B9EF3
color #fff
&.delete
cursor not-allowed
background #F47466
&.active
cursor pointer
background #F15140
color #fff
#gallery-view
position relative
.round
border-radius 14px
.pull-right
float right
.gallery-list
height 360px
box-sizing border-box
padding 8px 0
overflow-y auto
overflow-x hidden
position absolute
top: 38px
transition all .2s ease-in-out .1s
width 100%
&.small
height: 287px
top: 113px
&__img
height 150px
position relative
margin-bottom 16px
&__item
width 100%
height 120px
transition all .2s ease-in-out
cursor pointer
margin-bottom 8px
overflow hidden
display flex
&-fake
position absolute
top 0
left 0
opacity 0
width 100%
z-index -1
&:hover
transform scale(1.1)
&-img
width 100%
object-fit fill
&__tool-panel
color #ddd
i
cursor pointer
transition all .2s ease-in-out
&.el-icon-document
&:hover
color #49B1F5
&.el-icon-edit-outline
&:hover
color #69C282
&.el-icon-delete
&:hover
color #F15140
.handle-bar
color #ddd
margin-bottom 10px
.el-input__inner
border-radius 14px
</style>
+228
View File
@@ -0,0 +1,228 @@
<template>
<div id="mini-page"
:style="{ backgroundImage: 'url(' + logo + ')' }"
:class="{ linux: os === 'linux' }"
>
<!-- <i class="el-icon-upload2"></i> -->
<div
id="upload-area"
:class="{ 'is-dragover': dragover, uploading: showProgress, linux: os === 'linux' }" @drop.prevent="onDrop" @dragover.prevent="dragover = true" @dragleave.prevent="dragover = false"
:style="{ backgroundPosition: '0 ' + progress + '%'}"
>
<div id="upload-dragger" @dblclick="openUploadWindow">
<input type="file" id="file-uploader" @change="onChange" multiple>
</div>
</div>
</div>
</template>
<script>
import mixin from '@/utils/mixin'
export default {
name: 'mini-page',
mixins: [mixin],
data () {
return {
logo: 'static/squareLogo.png',
dragover: false,
progress: 0,
showProgress: false,
showError: false,
dragging: false,
wX: '',
wY: '',
screenX: '',
screenY: '',
menu: null,
os: '',
picBed: []
}
},
created () {
this.os = process.platform
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => {
if (progress !== -1) {
this.showProgress = true
this.progress = progress
} else {
this.progress = 100
this.showError = true
}
})
this.getPicBeds()
},
mounted () {
window.addEventListener('mousedown', this.handleMouseDown, false)
window.addEventListener('mousemove', this.handleMouseMove, false)
window.addEventListener('mouseup', this.handleMouseUp, false)
},
watch: {
progress (val) {
if (val === 100) {
setTimeout(() => {
this.showProgress = false
this.showError = false
}, 1000)
setTimeout(() => {
this.progress = 0
}, 1200)
}
}
},
methods: {
getPicBeds () {
this.picBed = this.$electron.ipcRenderer.sendSync('getPicBeds')
this.buildMenu()
},
onDrop (e) {
this.dragover = false
this.ipcSendFiles(e.dataTransfer.files)
},
openUploadWindow () {
document.getElementById('file-uploader').click()
},
onChange (e) {
this.ipcSendFiles(e.target.files)
document.getElementById('file-uploader').value = ''
},
ipcSendFiles (files) {
let sendFiles = []
Array.from(files).forEach((item, index) => {
let obj = {
name: item.name,
path: item.path
}
sendFiles.push(obj)
})
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
},
handleMouseDown (e) {
this.dragging = true
this.wX = e.pageX
this.wY = e.pageY
this.screenX = e.screenX
this.screenY = e.screenY
},
handleMouseMove (e) {
e.preventDefault()
e.stopPropagation()
if (this.dragging) {
const xLoc = e.screenX - this.wX
const yLoc = e.screenY - this.wY
this.$electron.remote.BrowserWindow.getFocusedWindow().setBounds({
x: xLoc,
y: yLoc,
width: 64,
height: 64
})
}
},
handleMouseUp (e) {
this.dragging = false
if (this.screenX === e.screenX && this.screenY === e.screenY) {
if (e.button === 0) { // left mouse
this.openUploadWindow()
} else {
this.getPicBeds()
this.openContextMenu()
}
}
},
openContextMenu () {
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
buildMenu () {
const _this = this
const submenu = this.picBed.map(item => {
return {
label: item.name,
type: 'radio',
checked: this.$db.read().get('picBed.current').value() === item.type,
click () {
_this.$db.read().set('picBed.current', item.type).write()
_this.$electron.ipcRenderer.send('syncPicBed')
}
}
})
const template = [
{
label: '打开详细窗口',
click () {
_this.$electron.ipcRenderer.send('openSettingWindow')
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu
},
{
label: '剪贴板图片上传',
click () {
_this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
},
{
label: '最小化窗口',
role: 'minimize'
},
{
label: '重启应用',
click () {
_this.$electron.remote.app.relaunch()
_this.$electron.remote.app.exit(0)
}
},
{
role: 'quit',
label: '退出'
}
]
this.menu = this.$electron.remote.Menu.buildFromTemplate(template)
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
window.removeEventListener('mousedown', this.handleMouseDown, false)
window.removeEventListener('mousemove', this.handleMouseMove, false)
window.removeEventListener('mouseup', this.handleMouseUp, false)
}
}
</script>
<style lang='stylus'>
#mini-page
background #409EFF
color #FFF
height 100vh
width 100vw
border-radius 50%
text-align center
line-height 100vh
font-size 40px
background-size 90vh 90vw
background-position center center
background-repeat no-repeat
position relative
border 4px solid #fff
box-sizing border-box
cursor pointer
&.linux
border-radius 0
background-size 100vh 100vw
#upload-area
height 100%
width 100%
border-radius 50%
transition all .2s ease-in-out
&.linux
border-radius 0
&.uploading
background: linear-gradient(to top, #409EFF 50%, #fff 51%)
background-size 200%
#upload-dragger
height 100%
&.is-dragover
background rgba(0,0,0,0.3)
#file-uploader
display none
</style>
+549
View File
@@ -0,0 +1,549 @@
<template>
<div id="picgo-setting">
<div class="view-title">
PicGo设置
</div>
<el-row class="setting-list">
<el-col :span="15" :offset="4">
<el-row>
<el-form
label-width="120px"
label-position="right"
size="small"
>
<el-form-item
label="打开配置文件"
>
<el-button type="primary" round size="mini" @click="openFile('data.json')">点击打开</el-button>
</el-form-item>
<el-form-item
label="设置日志文件"
>
<el-button type="primary" round size="mini" @click="openLogSetting">点击设置</el-button>
</el-form-item>
<el-form-item
label="修改上传快捷键"
>
<el-button type="primary" round size="mini" @click="keyBindingVisible = true">点击设置</el-button>
</el-form-item>
<el-form-item
label="自定义链接格式"
>
<el-button type="primary" round size="mini" @click="customLinkVisible = true">点击设置</el-button>
</el-form-item>
<el-form-item
label="设置代理"
>
<el-button type="primary" round size="mini" @click="proxyVisible = true">点击设置</el-button>
</el-form-item>
<el-form-item
label="检查更新"
>
<el-button type="primary" round size="mini" @click="checkUpdate">点击检查</el-button>
</el-form-item>
<el-form-item
label="打开更新助手"
>
<el-switch
v-model="form.updateHelper"
active-text=""
inactive-text=""
@change="updateHelperChange"
></el-switch>
</el-form-item>
<el-form-item
label="开机自启"
>
<el-switch
v-model="form.autoStart"
active-text=""
inactive-text=""
@change="handleAutoStartChange"
></el-switch>
</el-form-item>
<el-form-item
label="上传前重命名"
>
<el-switch
v-model="form.rename"
active-text=""
inactive-text=""
@change="handleRename"
></el-switch>
</el-form-item>
<el-form-item
label="时间戳重命名"
>
<el-switch
v-model="form.autoRename"
active-text=""
inactive-text=""
@change="handleAutoRename"
></el-switch>
</el-form-item>
<el-form-item
label="开启上传提示"
>
<el-switch
v-model="form.uploadNotification"
active-text=""
inactive-text=""
@change="handleUploadNotification"
></el-switch>
</el-form-item>
<el-form-item
v-if="os !== 'darwin'"
label="Mini窗口置顶"
>
<el-switch
v-model="form.miniWindowOntop"
active-text=""
inactive-text=""
@change="handleMiniWindowOntop"
></el-switch>
</el-form-item>
<el-form-item
label="选择显示的图床"
>
<el-checkbox-group
v-model="form.showPicBedList"
@change="handleShowPicBedListChange"
>
<el-checkbox
v-for="item in picBed"
:label="item.name"
:key="item.name"
></el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
</el-row>
</el-col>
</el-row>
<el-dialog
title="修改上传快捷键"
:visible.sync="keyBindingVisible"
:modal-append-to-body="false"
>
<el-form
label-width="80px"
>
<el-form-item
label="快捷上传"
>
<el-input
class="align-center"
@keydown.native.prevent="keyDetect('upload', $event)"
v-model="shortKey.upload"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelKeyBinding" round>取消</el-button>
<el-button type="primary" @click="confirmKeyBinding" round>确定</el-button>
</span>
</el-dialog>
<el-dialog
title="自定义链接格式"
:visible.sync="customLinkVisible"
:modal-append-to-body="false"
>
<el-form
label-position="top"
:model="customLink"
ref="customLink"
:rules="rules"
>
<el-form-item
label="用占位符$url来表示url的位置"
prop="value"
>
<el-input
class="align-center"
v-model="customLink.value"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<div>
[]($url)
</div>
<span slot="footer">
<el-button @click="cancelCustomLink" round>取消</el-button>
<el-button type="primary" @click="confirmCustomLink" round>确定</el-button>
</span>
</el-dialog>
<el-dialog
title="设置代理"
:visible.sync="proxyVisible"
:modal-append-to-body="false"
>
<el-form
label-position="right"
:model="customLink"
ref="customLink"
:rules="rules"
label-width="80px"
>
<el-form-item
label="代理地址"
>
<el-input
v-model="proxy"
:autofocus="true"
placeholder="例如:http://127.0.0.1:1080"
></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelProxy" round>取消</el-button>
<el-button type="primary" @click="confirmProxy" round>确定</el-button>
</span>
</el-dialog>
<el-dialog
title="检查更新"
:visible.sync="checkUpdateVisible"
:modal-append-to-body="false"
>
<div>
当前版本{{ version }}
</div>
<div>
最新版本{{ latestVersion ? latestVersion : '正在获取中...' }}
</div>
<div v-if="needUpdate">
PicGo更新啦请点击确定打开下载页面
</div>
<span slot="footer">
<el-button @click="cancelCheckVersion" round>取消</el-button>
<el-button type="primary" @click="confirmCheckVersion" round>确定</el-button>
</span>
</el-dialog>
<el-dialog
title="设置日志文件"
:visible.sync="logFileVisible"
:modal-append-to-body="false"
>
<el-form
label-position="right"
label-width="100px"
>
<el-form-item
label="日志文件"
>
<el-button type="primary" round size="mini" @click="openFile('picgo.log')">点击打开</el-button>
</el-form-item>
<el-form-item
label="日志记录等级"
>
<el-select
v-model="form.logLevel"
multiple
collapse-tags
@change="handleLogLevelChange"
>
<el-option
v-for="(value, key) of logLevel"
:key="key"
:label="value"
:value="key"
:disabled="handleLevelDisabled(key)"
></el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelLogLevelSetting" round>取消</el-button>
<el-button type="primary" @click="confirmLogLevelSetting" round>确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import keyDetect from 'utils/key-binding'
import pkg from 'root/package.json'
import path from 'path'
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
export default {
name: 'picgo-setting',
computed: {
needUpdate () {
if (this.latestVersion) {
return this.compareVersion2Update(this.version, this.latestVersion)
} else {
return false
}
}
},
data () {
const customLinkRule = (rule, value, callback) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
return {
form: {
updateHelper: this.$db.read().get('settings.showUpdateTip').value(),
showPicBedList: [],
autoStart: this.$db.read().get('settings.autoStart').value() || false,
rename: this.$db.read().get('settings.rename').value() || false,
autoRename: this.$db.read().get('settings.autoRename').value() || false,
uploadNotification: this.$db.read().get('settings.uploadNotification').value() || false,
miniWindowOntop: this.$db.read().get('settings.miniWindowOntop').value() || false,
logLevel: this.$db.read().get('settings.logLevel').value() || ['all']
},
picBed: [],
logFileVisible: false,
keyBindingVisible: false,
customLinkVisible: false,
checkUpdateVisible: false,
proxyVisible: false,
customLink: {
value: this.$db.read().get('settings.customLink').value() || '$url'
},
shortKey: {
upload: this.$db.read().get('settings.shortKey.upload').value()
},
proxy: this.$db.read().get('picBed.proxy').value() || undefined,
rules: {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
},
logLevel: {
all: '全部-All',
success: '成功-Success',
error: '错误-Error',
info: '普通-Info',
warn: '提醒-Warn',
none: '不记录日志-None'
},
version: pkg.version,
latestVersion: '',
os: ''
}
},
created () {
this.os = process.platform
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
methods: {
getPicBeds (event, picBeds) {
this.picBed = picBeds
this.form.showPicBedList = this.picBed.map(item => {
if (item.visible) {
return item.name
}
})
},
openFile (file) {
const { app, shell } = this.$electron.remote
const STORE_PATH = app.getPath('userData')
const FILE = path.join(STORE_PATH, `/${file}`)
shell.openItem(FILE)
},
openLogSetting () {
this.logFileVisible = true
},
keyDetect (type, event) {
this.shortKey[type] = keyDetect(event).join('+')
},
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = this.$db.read().get('settings.shortKey').value()
},
confirmKeyBinding () {
const oldKey = this.$db.read().get('settings.shortKey').value()
this.$db.read().set('settings.shortKey', this.shortKey).write()
this.keyBindingVisible = false
this.$electron.ipcRenderer.send('updateShortKey', oldKey)
},
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = this.$db.read().get('settings.customLink').value() || '$url'
},
confirmCustomLink () {
this.$refs.customLink.validate((valid) => {
if (valid) {
this.$db.read().set('settings.customLink', this.customLink.value).write()
this.customLinkVisible = false
this.$electron.ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
},
cancelProxy () {
this.proxyVisible = false
this.proxy = this.$db.read().get('picBed.proxy').value() || undefined
},
confirmProxy () {
this.proxyVisible = false
this.$db.read().set('picBed.proxy', this.proxy).write()
const successNotification = new window.Notification('设置代理', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
},
updateHelperChange (val) {
this.$db.read().set('settings.showUpdateTip', val).write()
},
handleShowPicBedListChange (val) {
const list = this.picBed.map(item => {
if (!val.includes(item.name)) {
item.visible = false
} else {
item.visible = true
}
return item
})
this.$db.read().set('picBed.list', list).write()
this.$electron.ipcRenderer.send('getPicBeds')
},
handleAutoStartChange (val) {
this.$db.read().set('settings.autoStart', val).write()
this.$electron.ipcRenderer.send('autoStart', val)
},
handleRename (val) {
this.$db.read().set('settings.rename', val).write()
},
handleAutoRename (val) {
this.$db.read().set('settings.autoRename', val).write()
},
compareVersion2Update (current, latest) {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
for (let i = 0; i < 3; i++) {
if (currentVersion[i] < latestVersion[i]) {
return true
}
if (currentVersion[i] > latestVersion[i]) {
return false
}
}
return false
},
checkUpdate () {
this.checkUpdateVisible = true
this.$http.get(release)
.then(res => {
this.latestVersion = res.data.name
}).catch(err => {
console.log(err)
})
},
confirmCheckVersion () {
if (this.needUpdate) {
this.$electron.remote.shell.openExternal(downloadUrl)
}
this.checkUpdateVisible = false
},
cancelCheckVersion () {
this.checkUpdateVisible = false
},
handleUploadNotification (val) {
this.$db.read().set('settings.uploadNotification', val).write()
},
handleMiniWindowOntop (val) {
this.$db.read().set('settings.miniWindowOntop', val).write()
this.$message('需要重启生效')
},
confirmLogLevelSetting () {
if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级')
}
this.$db.read().set('settings.logLevel', this.form.logLevel).write()
const successNotification = new window.Notification('设置日志', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
this.logFileVisible = false
},
cancelLogLevelSetting () {
this.logFileVisible = false
this.form.logLevel = this.$db.read().get('settings.logLevel').value() || 'all'
},
handleLevelDisabled (val) {
let currentLevel = val
let flagLevel
let result = this.form.logLevel.some(item => {
if (item === 'all' || item === 'none') {
flagLevel = item
}
return (item === 'all' || item === 'none')
})
if (result) {
if (currentLevel !== flagLevel) {
return true
}
} else if (this.form.logLevel.length > 0) {
if (val === 'all' || val === 'none') {
return true
}
}
return false
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}
}
</script>
<style lang='stylus'>
.el-message
left 60%
#picgo-setting
.sub-title
font-size 14px
.setting-list
height 360px
box-sizing border-box
overflow-y auto
overflow-x hidden
.setting-list
.el-form
label
line-height 32px
padding-bottom 0
color #eee
.el-button-group
width 100%
.el-button
width 50%
.el-input__inner
border-radius 19px
.el-radio-group
margin-left 25px
.el-switch__label
color #eee
&.is-active
color #409EFF
.el-icon-question
font-size 20px
float right
margin-top 9px
color #eee
cursor pointer
transition .2s color ease-in-out
&:hover
color #409EFF
.el-checkbox-group
label
margin-right 30px
width 100px
.el-checkbox+.el-checkbox
margin-right 30px
margin-left 0
.confirm-button
width 100%
</style>
+562
View File
@@ -0,0 +1,562 @@
<template>
<div id="plugin-view">
<div class="view-title">
插件设置 - <i class="el-icon-goods" @click="goAwesomeList"></i>
</div>
<el-row class="handle-bar" :class="{ 'cut-width': pluginList.length > 6 }">
<el-input
v-model="searchText"
placeholder="搜索npm上的PicGo插件,或者点击上方按钮查看优秀插件列表"
size="small"
>
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
</el-input>
</el-row>
<el-row :gutter="10" class="plugin-list" v-loading="loading">
<el-col :span="12" v-for="item in pluginList" :key="item.name">
<div class="plugin-item" :class="{ 'darwin': os === 'darwin' }">
<div class="cli-only-badge" v-if="!item.gui" title="CLI only">CLI</div>
<img class="plugin-item__logo" :src="item.logo"
onerror="this.src='static/roundLogo.png'"
>
<div
class="plugin-item__content"
:class="{ disabled: !item.enabled }"
>
<div class="plugin-item__name" @click="openHomepage(item.homepage)">
{{ item.name }} <small>{{ ' ' + item.version }}</small>
</div>
<div class="plugin-item__desc" :title="item.description">
{{ item.description }}
</div>
<div class="plugin-item__info-bar">
<span class="plugin-item__author">
{{ item.author }}
</span>
<span class="plugin-item__config" >
<template v-if="searchText">
<template v-if="!item.hasInstall">
<span class="config-button install" v-if="!item.ing" @click="installPlugin(item)">
安装
</span>
<span v-else-if="item.ing" class="config-button ing">
安装中
</span>
</template>
<span v-else class="config-button ing">
已安装
</span>
</template>
<template v-else>
<span v-if="item.ing" class="config-button ing">
进行中
</span>
<template v-else>
<i
v-if="item.enabled"
class="el-icon-setting"
@click="buildContextMenu(item)"
></i>
<i
v-else
class="el-icon-remove-outline"
@click="buildContextMenu(item)"
></i>
</template>
</template>
</span>
</div>
</div>
</div>
</el-col>
</el-row>
<el-row v-show="needReload" class="reload-mask" :class="{ 'cut-width': pluginList.length > 6 }">
<el-button type="primary" @click="reloadApp" size="mini" round>重启以生效</el-button>
</el-row>
<el-dialog
:visible.sync="dialogVisible"
:modal-append-to-body="false"
:title="`配置${configName}`"
width="70%"
>
<config-form
:config="config"
:type="currentType"
:id="configName"
ref="configForm"
>
</config-form>
<span slot="footer">
<el-button @click="dialogVisible = false" round>取消</el-button>
<el-button type="primary" @click="handleConfirmConfig" round>确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import ConfigForm from '@/components/ConfigForm'
import { debounce } from 'lodash'
export default {
name: 'plugin',
components: {
ConfigForm
},
data () {
return {
searchText: '',
pluginList: [],
menu: null,
config: [],
currentType: '',
configName: '',
dialogVisible: false,
pluginNameList: [],
loading: true,
needReload: false,
id: '',
os: ''
}
},
computed: {
npmSearchText () {
return this.searchText.match('picgo-plugin-')
? this.searchText
: this.searchText !== ''
? `picgo-plugin-${this.searchText}`
: this.searchText
}
},
watch: {
npmSearchText (val) {
if (val) {
this.loading = true
this.pluginList = []
this.getSearchResult(val)
} else {
this.getPluginList()
}
}
},
created () {
this.os = process.platform
this.$electron.ipcRenderer.on('pluginList', (evt, list) => {
this.pluginList = list
this.pluginNameList = list.map(item => item.name)
this.loading = false
})
this.$electron.ipcRenderer.on('installSuccess', (evt, plugin) => {
this.loading = false
this.pluginList.forEach(item => {
if (item.name === plugin) {
item.ing = false
item.hasInstall = true
}
})
})
this.$electron.ipcRenderer.on('updateSuccess', (evt, plugin) => {
this.loading = false
this.pluginList.forEach(item => {
if (item.name === plugin) {
item.ing = false
item.hasInstall = true
}
this.getPicBeds()
})
this.handleReload()
this.getPluginList()
})
this.$electron.ipcRenderer.on('uninstallSuccess', (evt, plugin) => {
this.loading = false
this.pluginList = this.pluginList.filter(item => {
if (item.name === plugin) { // restore Uploader & Transformer after uninstalling
if (item.config.transformer.name) {
this.handleRestoreState('transformer', item.config.transformer.name)
}
if (item.config.uploader.name) {
this.handleRestoreState('uploader', item.config.uploader.name)
}
this.getPicBeds()
}
return item.name !== plugin
})
this.pluginNameList = this.pluginNameList.filter(item => item !== plugin)
})
this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = this.$db.read().get('needReload').value()
},
methods: {
buildContextMenu (plugin) {
const _this = this
let menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
_this.$db.read().set(`picgoPlugins.picgo-plugin-${plugin.name}`, true).write()
plugin.enabled = true
_this.getPicBeds()
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
_this.$db.read().set(`picgoPlugins.picgo-plugin-${plugin.name}`, false).write()
plugin.enabled = false
_this.getPicBeds()
if (plugin.config.transformer.name) {
_this.handleRestoreState('transformer', plugin.config.transformer.name)
}
if (plugin.config.uploader.name) {
_this.handleRestoreState('uploader', plugin.config.uploader.name)
}
}
}, {
label: '卸载插件',
click () {
_this.uninstallPlugin(plugin.name)
}
}, {
label: '更新插件',
click () {
_this.updatePlugin(plugin.name)
}
}]
for (let i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = {
label: `配置${i} - ${plugin.config[i].name}`,
click () {
_this.currentType = i
_this.configName = plugin.config[i].name
_this.dialogVisible = true
_this.config = plugin.config[i].config
}
}
menu.push(obj)
}
}
// handle transformer
if (plugin.config.transformer.name) {
let currentTransformer = this.$db.read().get('picBed.transformer').value() || 'path'
let pluginTransformer = plugin.config.transformer.name
const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
click () {
_this.toggleTransformer(plugin.config.transformer.name)
}
}
menu.push(obj)
}
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
type: 'separator'
})
for (let i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
_this.$electron.ipcRenderer.send('pluginActions', plugin.name, i.label)
}
})
}
}
this.menu = this.$electron.remote.Menu.buildFromTemplate(menu)
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
getPluginList () {
this.$electron.ipcRenderer.send('getPluginList')
},
getPicBeds () {
this.$electron.ipcRenderer.send('getPicBeds')
},
installPlugin (item) {
if (!item.gui) {
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
}).catch(() => {
console.log('Install canceled')
})
} else {
item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
}
},
uninstallPlugin (val) {
this.pluginList.forEach(item => {
if (item.name === val) {
item.ing = true
}
})
this.$electron.ipcRenderer.send('uninstallPlugin', val)
},
updatePlugin (val) {
this.pluginList.forEach(item => {
if (item.name === val) {
item.ing = true
}
})
this.$electron.ipcRenderer.send('updatePlugin', val)
},
reloadApp () {
this.$electron.remote.app.relaunch()
this.$electron.remote.app.exit(0)
},
handleReload () {
this.$db.read().set('needReload', true).write()
this.needReload = true
const successNotification = new window.Notification('更新成功', {
body: '请点击此通知重启应用以生效'
})
successNotification.onclick = () => {
this.reloadApp()
}
},
cleanSearch () {
this.searchText = ''
},
toggleTransformer (transformer) {
let currentTransformer = this.$db.read().get('picBed.transformer').value() || 'path'
if (currentTransformer === transformer) {
this.$db.read().set('picBed.transformer', 'path').write()
} else {
this.$db.read().set('picBed.transformer', transformer).write()
}
},
async handleConfirmConfig () {
const result = await this.$refs.configForm.validate()
if (result !== false) {
switch (this.currentType) {
case 'plugin':
this.$db.read().set(`picgo-plugin-${this.configName}`, result).write()
break
case 'uploader':
this.$db.read().set(`picBed.${this.configName}`, result).write()
break
case 'transformer':
this.$db.read().set(`transformer.${this.configName}`, result).write()
break
}
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
this.dialogVisible = false
this.getPluginList()
}
},
getSearchResult: function (val) {
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
.then(res => {
this.pluginList = res.data.objects.map(item => {
return this.handleSearchResult(item)
})
this.loading = false
})
.catch(err => {
console.log(err)
this.loading = false
})
},
handleSearchResult (item) {
const name = item.package.name.replace(/picgo-plugin-/, '')
let gui = false
if (item.package.keywords && item.package.keywords.length > 0) {
if (item.package.keywords.includes('picgo-gui-plugin')) {
gui = true
}
}
return {
name: name,
author: item.package.author.name,
description: item.package.description,
logo: `https://cdn.jsdelivr.net/npm/${item.package.name}/logo.png`,
config: {},
homepage: item.package.links ? item.package.links.homepage : '',
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')),
version: item.package.version,
gui,
ing: false // installing or uninstalling
}
},
// restore Uploader & Transformer
handleRestoreState (item, name) {
if (item === 'uploader') {
const current = this.$db.read().get('picBed.current').value()
if (current === name) {
this.$db.read().set('picBed.current', 'smms').write()
}
}
if (item === 'transformer') {
const current = this.$db.read().get('picBed.transformer').value()
if (current === name) {
this.$db.read().set('picBed.transformer', 'path').write()
}
}
},
openHomepage (url) {
if (url) {
this.$electron.remote.shell.openExternal(url)
}
},
goAwesomeList () {
this.$electron.remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('pluginList')
this.$electron.ipcRenderer.removeAllListeners('installSuccess')
this.$electron.ipcRenderer.removeAllListeners('uninstallSuccess')
this.$electron.ipcRenderer.removeAllListeners('updateSuccess')
}
}
</script>
<style lang='stylus'>
$darwinBg = #172426
#plugin-view
position relative
padding 0 20px 0
.el-loading-mask
background-color rgba(0, 0, 0, 0.8)
.plugin-list
height: 339px;
box-sizing: border-box;
padding: 8px 15px;
overflow-y: auto;
overflow-x: hidden;
position: absolute;
top: 70px;
left: 5px;
transition: all 0.2s ease-in-out 0.1s;
width: 100%
.el-loading-mask
left: 20px
width: calc(100% - 40px)
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
i.el-icon-goods
font-size 20px
vertical-align middle
cursor pointer
.handle-bar
margin-bottom 20px
&.cut-width
padding-right: 8px
.el-input__inner
border-radius 0
.plugin-item
box-sizing border-box
height 80px
background #444
padding 8px
user-select text
transition all .2s ease-in-out
margin-bottom 10px
position relative
.cli-only-badge
position absolute
right 0px
top 0
font-size 12px
padding 3px 8px
background #49B1F5
color #eee
&.darwin
background transparentify($darwinBg, #000, 0.75)
&:hover
background transparentify($darwinBg, #000, 0.85)
&:hover
background #333
&__logo
width 64px
height 64px
float left
&__content
float left
width calc(100% - 72px)
height 64px
color #ddd
margin-left 8px
display flex
flex-direction column
justify-content space-between
&.disabled
color #aaa
&__name
font-size 16px
height 22px
line-height 22px
// font-weight 600
font-weight 600
cursor pointer
transition all .2s ease-in-out
&:hover
color: #1B9EF3
&__desc
font-size 14px
height 21px
line-height 21px
overflow hidden
text-overflow ellipsis
white-space nowrap
&__info-bar
font-size 14px
height 21px
line-height 28px
position relative
&__author
overflow hidden
text-overflow ellipsis
white-space nowrap
&__config
float right
font-size 16px
cursor pointer
transition all .2s ease-in-out
&:hover
color: #1B9EF3
.config-button
font-size 12px
color #ddd
background #222
padding 1px 8px
height 18px
line-height 18px
text-align center
position absolute
top 4px
right 20px
transition all .2s ease-in-out
&.reload
right 0px
&.ing
right 0px
&.install
right 0px
&:hover
background: #1B9EF3
color #fff
.reload-mask
position absolute
width calc(100% - 40px)
bottom -320px
text-align center
background rgba(0,0,0,0.4)
padding 10px 0
&.cut-width
width calc(100% - 48px)
</style>
+61
View File
@@ -0,0 +1,61 @@
<template>
<div id="rename-page">
<el-form
@submit.native.prevent
>
<el-form-item
label="文件改名"
>
<el-input
v-model="fileName"
size="small"
@keyup.enter.native="confirmName"
></el-input>
</el-form-item>
</el-form>
<el-row>
<div class="pull-right">
<el-button @click="cancel" round size="mini">取消</el-button>
<el-button type="primary" @click="confirmName" round size="mini">确定</el-button>
</div>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/mixin'
export default {
name: 'rename-page',
mixins: [mixin],
data () {
return {
fileName: '',
id: null
}
},
created () {
this.$electron.ipcRenderer.on('rename', (event, name, id) => {
this.fileName = name
this.id = id
})
},
methods: {
confirmName () {
this.$electron.ipcRenderer.send(`rename${this.id}`, this.fileName)
},
cancel () {
this.$electron.ipcRenderer.send(`rename${this.id}`, null)
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('rename')
}
}
</script>
<style lang='stylus'>
#rename-page
padding 0 20px
.pull-right
float right
.el-form-item__label
color #ddd
</style>
+171
View File
@@ -0,0 +1,171 @@
<template>
<div id="tray-page">
<!-- <div class="header-arrow"></div> -->
<div class="content">
<div class="wait-upload-img" v-if="clipboardFiles.length > 0">
<div class="list-title">等待上传</div>
<div v-for="(item, index) in clipboardFiles" :key="index" class="img-list">
<div
class="upload-img__container"
:class="{ upload: uploadFlag }"
@click="uploadClipboardFiles">
<img :src="item.imgUrl" class="upload-img">
</div>
</div>
</div>
<div class="uploaded-img">
<div class="list-title">已上传</div>
<div v-for="item in files" :key="item.imgUrl" class="img-list">
<div class="upload-img__container" @click="copyTheLink(item)">
<img :src="item.imgUrl" class="upload-img">
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import mixin from '@/utils/mixin'
import pasteTemplate from '~/main/utils/pasteTemplate'
export default {
name: 'tray-page',
mixins: [mixin],
data () {
return {
files: [],
notification: {
title: '复制链接成功',
body: '',
icon: ''
},
clipboardFiles: [],
uploadFlag: false
}
},
computed: {
reverseList () {
return this.files.slice().reverse()
}
},
mounted () {
this.disableDragFile()
this.getData()
this.$electron.ipcRenderer.on('dragFiles', (event, files) => {
files.forEach(item => {
this.$db.read().get('uploaded').insert(item).write()
})
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
})
this.$electron.ipcRenderer.on('clipboardFiles', (event, files) => {
this.clipboardFiles = files
})
this.$electron.ipcRenderer.on('uploadFiles', (event) => {
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
console.log(this.files)
this.uploadFlag = false
})
this.$electron.ipcRenderer.on('updateFiles', (event) => {
this.getData()
})
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('dragFiles')
this.$electron.ipcRenderer.removeAllListeners('clipboardFiles')
this.$electron.ipcRenderer.removeAllListeners('uploadClipboardFiles')
this.$electron.ipcRenderer.removeAllListeners('updateFiles')
},
methods: {
getData () {
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
},
copyTheLink (item) {
this.notification.body = item.imgUrl
this.notification.icon = item.imgUrl
const myNotification = new window.Notification(this.notification.title, this.notification)
const pasteStyle = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
this.$electron.clipboard.writeText(pasteTemplate(pasteStyle, item.imgUrl))
myNotification.onclick = () => {
return true
}
},
calcHeight (width, height) {
return height * 160 / width
},
disableDragFile () {
window.addEventListener('dragover', (e) => {
e = e || event
e.preventDefault()
}, false)
window.addEventListener('drop', (e) => {
e = e || event
e.preventDefault()
}, false)
},
uploadClipboardFiles () {
if (this.uploadFlag) {
return
}
this.uploadFlag = true
this.$electron.ipcRenderer.send('uploadClipboardFiles')
}
}
}
</script>
<style lang="stylus">
body::-webkit-scrollbar
width 0px
#tray-page
background-color transparent
.list-title
text-align center
color #858585
font-size 12px
padding 6px 0
position relative
&:before
content ''
position absolute
height 1px
width calc(100% - 36px)
bottom 0
left 18px
background #858585
.header-arrow
position absolute
top 12px
left 50%
margin-left -10px
width: 0;
height: 0;
border-left: 10px solid transparent
border-right: 10px solid transparent
border-bottom: 10px solid rgba(255,255,255, 1)
.content
position absolute
top 0px
width 100%
.img-list
padding 8px 8px
display flex
justify-content space-between
align-items center
// height 45px
cursor pointer
transition all .2s ease-in-out
&:hover
background #49B1F5
.upload-img__index
color #fff
.upload-img
width 100%
object-fit scale-down
margin 0 auto
&__container
width 100%
padding 8px 10px
height 100%
&.upload
cursor not-allowed
</style>
+239
View File
@@ -0,0 +1,239 @@
<template>
<div id="upload-view">
<el-row :gutter="16">
<el-col :span="20" :offset="2">
<div class="view-title">
图片上传 - {{ picBedName }} <i class="el-icon-caret-bottom" @click="handleChangePicBed"></i>
</div>
<div
id="upload-area"
:class="{ 'is-dragover': dragover }"
@drop.prevent="onDrop"
@dragover.prevent="dragover = true"
@dragleave.prevent="dragover = false"
>
<div id="upload-dragger" @click="openUplodWindow">
<i class="el-icon-upload"></i>
<div class="upload-dragger__text">
将文件拖到此处 <span>点击上传</span>
</div>
<input type="file" id="file-uploader" @change="onChange" multiple>
</div>
</div>
<el-progress
:percentage="progress"
:show-text="false"
class="upload-progress"
:class="{ 'show': showProgress }"
:status="showError ? 'exception' : 'text'"
></el-progress>
<div class="paste-style">
<div class="el-col-16">
<div class="paste-style__text">
链接格式
</div>
<el-radio-group v-model="pasteStyle" size="mini"
@change="handlePasteStyleChange"
>
<el-radio-button label="markdown">
Markdown
</el-radio-button>
<el-radio-button label="HTML"></el-radio-button>
<el-radio-button label="URL"></el-radio-button>
<el-radio-button label="UBB"></el-radio-button>
<el-radio-button label="Custom" title="自定义"></el-radio-button>
</el-radio-group>
</div>
<div class="el-col-8">
<div class="paste-style__text">
快捷上传
</div>
<el-button type="primary" round size="mini" @click="uploadClipboardFiles" class="paste-upload">剪贴板图片上传</el-button>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: 'upload',
data () {
return {
dragover: false,
progress: 0,
showProgress: false,
showError: false,
pasteStyle: '',
picBed: [],
picBedName: '',
menu: null
}
},
mounted () {
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => {
if (progress !== -1) {
this.showProgress = true
this.progress = progress
} else {
this.progress = 100
this.showError = true
}
})
this.getPasteStyle()
this.getDefaultPicBed()
this.$electron.ipcRenderer.on('syncPicBed', () => {
this.getDefaultPicBed()
})
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
watch: {
progress (val) {
if (val === 100) {
setTimeout(() => {
this.showProgress = false
this.showError = false
}, 1000)
setTimeout(() => {
this.progress = 0
}, 1200)
}
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
this.$electron.ipcRenderer.removeAllListeners('syncPicBed')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
},
methods: {
onDrop (e) {
this.dragover = false
this.ipcSendFiles(e.dataTransfer.files)
},
openUplodWindow () {
document.getElementById('file-uploader').click()
},
onChange (e) {
this.ipcSendFiles(e.target.files)
document.getElementById('file-uploader').value = ''
},
ipcSendFiles (files) {
let sendFiles = []
Array.from(files).forEach((item, index) => {
let obj = {
name: item.name,
path: item.path
}
sendFiles.push(obj)
})
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
},
getPasteStyle () {
this.pasteStyle = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
},
handlePasteStyleChange (val) {
this.$db.read().set('settings.pasteStyle', val)
.write()
},
uploadClipboardFiles () {
this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
},
getDefaultPicBed () {
const current = this.$db.read().get('picBed.current').value()
this.picBed.forEach(item => {
if (item.type === current) {
this.picBedName = item.name
}
})
},
getPicBeds (event, picBeds) {
this.picBed = picBeds
this.getDefaultPicBed()
},
handleChangePicBed () {
this.buildMenu()
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
buildMenu () {
const _this = this
const submenu = this.picBed.map(item => {
return {
label: item.name,
type: 'radio',
checked: this.$db.read().get('picBed.current').value() === item.type,
click () {
_this.$db.read().set('picBed.current', item.type).write()
_this.$electron.ipcRenderer.send('syncPicBed')
}
}
})
this.menu = this.$electron.remote.Menu.buildFromTemplate(submenu)
}
}
}
</script>
<style lang='stylus'>
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
#upload-view
.view-title
margin 20px auto
#upload-area
height 220px
border 2px dashed #dddddd
border-radius 8px
text-align center
width 450px
margin 0 auto
color #dddddd
cursor pointer
transition all .2s ease-in-out
#upload-dragger
height 100%
&.is-dragover,
&:hover
border 2px dashed #A4D8FA
background-color rgba(164, 216, 250, 0.3)
color #fff
i
font-size 66px
margin 50px 0 16px
line-height 66px
span
color #409EFF
#file-uploader
display none
.upload-progress
opacity 0
transition all .2s ease-in-out
width 450px
margin 20px auto 0
&.show
opacity 1
.el-progress-bar__inner
transition all .2s ease-in-out
.paste-style
text-align center
margin-top 16px
&__text
font-size 12px
color #eeeeee
margin-bottom 4px
.el-radio-button:first-child
.el-radio-button__inner
border-left none
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
.paste-upload
width 100%
</style>
+149
View File
@@ -0,0 +1,149 @@
<template>
<div id="aliyun-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
阿里云OSS设置
</div>
<el-form
ref="aliyun"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="设定KeyId"
prop="accessKeyId"
:rules="{
required: true, message: 'AccessKeyId不能为空', trigger: 'blur'
}">
<el-input v-model="form.accessKeyId" placeholder="AccessKeyId" @keyup.native.enter="confirm"></el-input>
</el-form-item>
<el-form-item
label="设定KeySecret"
prop="accessKeySecret"
:rules="{
required: true, message: 'AccessKeySecret不能为空', trigger: 'blur'
}">
<el-input v-model="form.accessKeySecret" type="password" @keyup.native.enter="confirm" placeholder="AccessKeySecret"></el-input>
</el-form-item>
<el-form-item
label="设定存储空间名"
prop="bucket"
:rules="{
required: true, message: 'Bucket不能为空', trigger: 'blur'
}">
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
</el-form-item>
<el-form-item
label="确认存储区域"
prop="area"
:rules="{
required: true, message: '区域代码不能为空', trigger: 'blur'
}">
<el-input v-model="form.area" @keyup.native.enter="confirm" placeholder="例如oss-cn-beijing"></el-input>
</el-form-item>
<el-form-item
label="指定存储路径"
>
<el-input v-model="form.path" @keyup.native.enter="confirm" placeholder="例如img/"></el-input>
</el-form-item>
<el-form-item
label="设定自定义域名"
>
<el-input v-model="form.customUrl" @keyup.native.enter="confirm" placeholder="例如https://xxxx.com"></el-input>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('aliyun')" round :disabled="defaultPicBed === 'aliyun'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
mixins: [mixin],
name: 'aliyun',
data () {
return {
form: {
accessKeyId: '',
accessKeySecret: '',
bucket: '',
area: '',
path: '',
customUrl: ''
}
}
},
created () {
const config = this.$db.get('picBed.aliyun').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.aliyun.validate((valid) => {
if (valid) {
this.$db.set('picBed.aliyun', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
#aliyun-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-input__inner
border-radius 19px
&-item
margin-bottom 10.5px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
.el-switch__label
color #eee
&.is-active
color #409EFF
.el-icon-question
font-size 20px
float right
margin-top 9px
color #eee
cursor pointer
transition .2s color ease-in-out
&:hover
color #409EFF
</style>
+125
View File
@@ -0,0 +1,125 @@
<template>
<div id="github-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
GitHub设置
</div>
<el-form
ref="github"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="设定仓库名"
prop="repo"
:rules="{
required: true, message: '仓库名不能为空', trigger: 'blur'
}">
<el-input v-model="form.repo" @keyup.native.enter="confirm" placeholder="格式:username/repo"></el-input>
</el-form-item>
<el-form-item
label="设定分支名"
prop="branch"
:rules="{
required: true, message: '分支名不能为空', trigger: 'blur'
}">
<el-input v-model="form.branch" @keyup.native.enter="confirm" placeholder="例如:master"></el-input>
</el-form-item>
<el-form-item
label="设定Token"
prop="token"
:rules="{
required: true, message: 'Token不能为空', trigger: 'blur'
}">
<el-input v-model="form.token" @keyup.native.enter="confirm" placeholder="token" type="password"></el-input>
</el-form-item>
<el-form-item
label="指定存储路径"
>
<el-input v-model="form.path" @keyup.native.enter="confirm" placeholder="例如img/"></el-input>
</el-form-item>
<el-form-item
label="设定自定义域名"
>
<el-input v-model="form.customUrl" @keyup.native.enter="confirm" placeholder="例如https://xxxx.com"></el-input>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('github')" round :disabled="defaultPicBed === 'github'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
name: 'github',
mixins: [mixin],
data () {
return {
form: {
repo: '',
token: '',
path: '',
customUrl: '',
branch: ''
}
}
},
created () {
const config = this.$db.get('picBed.github').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.github.validate((valid) => {
if (valid) {
this.$db.set('picBed.github', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
#github-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-input__inner
border-radius 19px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
</style>
+117
View File
@@ -0,0 +1,117 @@
<template>
<div id="imgur-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
Imgur图床设置
</div>
<el-form
ref="imgur"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="设定ClientId"
prop="clientId"
:rules="{
required: true, message: 'ClientId不能为空', trigger: 'blur'
}">
<el-input v-model="form.clientId" placeholder="ClientId" @keyup.native.enter="confirm"></el-input>
</el-form-item>
<el-form-item
label="设定代理"
prop="proxy"
>
<el-input v-model="form.proxy" placeholder="例如:http://127.0.0.1:1080" @keyup.native.enter="confirm"></el-input>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('imgur')" round :disabled="defaultPicBed === 'imgur'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
name: 'imgur',
mixins: [mixin],
data () {
return {
form: {
clientId: '',
proxy: ''
}
}
},
created () {
const config = this.$db.get('picBed.imgur').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.imgur.validate((valid) => {
if (valid) {
this.$db.set('picBed.imgur', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
#imgur-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-input__inner
border-radius 19px
&-item
margin-bottom 10.5px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
.el-switch__label
color #eee
&.is-active
color #409EFF
.el-icon-question
font-size 20px
float right
margin-top 9px
color #eee
cursor pointer
transition .2s color ease-in-out
&:hover
color #409EFF
</style>
+109
View File
@@ -0,0 +1,109 @@
<template>
<div id="others-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
{{ picBedName }}设置
</div>
<config-form
v-if="config.length > 0"
:config="config"
type="uploader"
ref="configForm"
:id="type"
>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="handleConfirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed(type)" round :disabled="defaultPicBed === type">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</config-form>
<div v-else class="single">
<div class="notice">暂无配置项</div>
<el-button type="success" @click="setDefaultPicBed(type)" round :disabled="defaultPicBed === type" size="mini">设为默认图床</el-button>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import ConfigForm from '@/components/ConfigForm'
import mixin from '@/utils/ConfirmButtonMixin'
export default {
name: 'OtherPicBed',
mixins: [mixin],
components: {
ConfigForm
},
data () {
return {
type: '',
config: [],
picBedName: ''
}
},
created () {
this.type = this.$route.params.type
this.$electron.ipcRenderer.send('getPicBedConfig', this.$route.params.type)
this.$electron.ipcRenderer.on('getPicBedConfig', this.getPicBeds)
},
methods: {
async handleConfirm () {
const result = await this.$refs.configForm.validate()
if (result !== false) {
this.$db.read().set(`picBed.${this.type}`, result).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
},
setDefaultPicBed (type) {
this.$db.read().set('picBed.current', type).write()
this.defaultPicBed = type
const successNotification = new window.Notification('设置默认图床', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
},
getPicBeds (event, config, name) {
this.config = config
this.picBedName = name
}
},
beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBedConfig', this.getPicBeds)
}
}
</script>
<style lang='stylus'>
#others-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-button-group
width 100%
.el-button
width 50%
.el-input__inner
border-radius 19px
.el-radio-group
margin-left 25px
.el-switch__label
color #eee
&.is-active
color #409EFF
.notice
color #eee
text-align center
margin-bottom 10px
.single
text-align center
</style>
+144
View File
@@ -0,0 +1,144 @@
<template>
<div id="qiniu-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
七牛图床设置
</div>
<el-form
ref="qiniu"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="设定AccessKey"
prop="accessKey"
:rules="{
required: true, message: 'AccessKey不能为空', trigger: 'blur'
}">
<el-input v-model="form.accessKey" placeholder="AccessKey" @keyup.native.enter="confirm('weiboForm')"></el-input>
</el-form-item>
<el-form-item
label="设定SecretKey"
prop="secretKey"
:rules="{
required: true, message: 'SecretKey不能为空', trigger: 'blur'
}">
<el-input v-model="form.secretKey" type="password" @keyup.native.enter="confirm" placeholder="SecretKey"></el-input>
</el-form-item>
<el-form-item
label="设定存储空间名"
prop="bucket"
:rules="{
required: true, message: 'Bucket不能为空', trigger: 'blur'
}">
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
</el-form-item>
<el-form-item
label="设定访问网址"
prop="url"
:rules="{
required: true, message: '网址不能为空', trigger: 'blur'
}">
<el-input v-model="form.url" @keyup.native.enter="confirm" placeholder="例如:http://xxx.yyy.glb.clouddn.com"></el-input>
</el-form-item>
<el-form-item
label="确认存储区域"
:rules="{
required: true, message: '区域代码不能为空', trigger: 'blur'
}">
<el-input v-model="form.area" placeholder="例如z0"></el-input>
</el-form-item>
<el-form-item
label="设定网址后缀"
>
<el-input v-model="form.options" @keyup.native.enter="confirm" placeholder="例如?imageslim"></el-input>
</el-form-item>
<el-form-item
label="指定存储路径"
>
<el-input v-model="form.path" @keyup.native.enter="confirm" placeholder="例如img/"></el-input>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('qiniu')" round :disabled="defaultPicBed === 'qiniu'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
mixins: [mixin],
name: 'qiniu',
data () {
return {
form: {
accessKey: '',
secretKey: '',
bucket: '',
url: '',
area: '',
options: '',
path: ''
}
}
},
created () {
const config = this.$db.get('picBed.qiniu').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.qiniu.validate((valid) => {
if (valid) {
this.$db.set('picBed.qiniu', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
#qiniu-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-input__inner
border-radius 19px
&-item
margin-bottom 10.5px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
</style>
+46
View File
@@ -0,0 +1,46 @@
<template>
<div id="smms-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
SM.MS设置
</div>
<div class="content">
感谢SM.MS提供的优质服务
</div>
<div style="text-align: center; margin-top: 20px;">
<el-button type="success" @click="confirm" round :disabled="defaultPicBed === 'smms'" size="mini">设为默认图床</el-button>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
mixins: [mixin],
name: 'upyun',
data () {
return {}
},
methods: {
confirm () {
this.$db.set('picBed.smms', true).write()
this.setDefaultPicBed('smms')
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
}
}
</script>
<style lang='stylus'>
#smms-view
.content
text-align center
font-size 14px
color #eee
</style>
+175
View File
@@ -0,0 +1,175 @@
<template>
<div id="tcyun-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
腾讯云COS设置
</div>
<el-form
ref="tcyun"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="COS版本"
>
<el-switch
v-model="form.version"
active-text="v4"
inactive-text="v5"
active-value="v4"
inactive-value="v5"
inactive-color="#67C23A"
></el-switch>
<i class="el-icon-question" @click="openWiki"></i>
</el-form-item>
<el-form-item
label="设定SecretId"
prop="secretId"
:rules="{
required: true, message: 'SecretId不能为空', trigger: 'blur'
}">
<el-input v-model="form.secretId" placeholder="SecretId" @keyup.native.enter="confirm('weiboForm')"></el-input>
</el-form-item>
<el-form-item
label="设定SecretKey"
prop="secretKey"
:rules="{
required: true, message: 'SecretKey不能为空', trigger: 'blur'
}">
<el-input v-model="form.secretKey" type="password" @keyup.native.enter="confirm" placeholder="SecretKey"></el-input>
</el-form-item>
<el-form-item
label="设定APPID"
prop="appId"
:rules="{
required: true, message: 'APPID不能为空', trigger: 'blur'
}">
<el-input v-model="form.appId" @keyup.native.enter="confirm" placeholder="例如1234567890"></el-input>
</el-form-item>
<el-form-item
label="设定存储空间名"
prop="bucket"
:rules="{
required: true, message: 'Bucket不能为空', trigger: 'blur'
}">
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
</el-form-item>
<el-form-item
label="确认存储区域"
prop="area"
:rules="{
required: true, message: '区域代码不能为空', trigger: 'blur'
}">
<el-input v-model="form.area" @keyup.native.enter="confirm" placeholder="例如tj"></el-input>
</el-form-item>
<el-form-item
label="指定存储路径"
>
<el-input v-model="form.path" @keyup.native.enter="confirm" placeholder="例如img/"></el-input>
</el-form-item>
<el-form-item
label="设定自定义域名"
>
<el-input v-model="form.customUrl" @keyup.native.enter="confirm" placeholder="例如https://xxxx.com"></el-input>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('tcyun')" round :disabled="defaultPicBed === 'tcyun'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
mixins: [mixin],
name: 'tcyun',
data () {
return {
form: {
secretId: '',
secretKey: '',
bucket: '',
appId: '',
area: '',
path: '',
customUrl: '',
version: 'v4'
}
}
},
created () {
const config = this.$db.get('picBed.tcyun').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.tcyun.validate((valid) => {
if (valid) {
this.$db.set('picBed.tcyun', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
},
openWiki () {
this.$electron.remote.shell.openExternal('https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#腾讯云cos')
}
}
}
</script>
<style lang='stylus'>
#tcyun-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-input__inner
border-radius 19px
&-item
margin-bottom 10.5px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
.el-switch__label
color #eee
&.is-active
color #409EFF
.el-icon-question
font-size 20px
float right
margin-top 9px
color #eee
cursor pointer
transition .2s color ease-in-out
&:hover
color #409EFF
</style>
+133
View File
@@ -0,0 +1,133 @@
<template>
<div id="tcyun-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
又拍云设置
</div>
<el-form
ref="tcyun"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="设定存储空间名"
prop="bucket"
:rules="{
required: true, message: 'Bucket不能为空', trigger: 'blur'
}">
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
</el-form-item>
<el-form-item
label="设定操作员"
prop="operator"
:rules="{
required: true, message: '操作员不能为空', trigger: 'blur'
}">
<el-input v-model="form.operator" @keyup.native.enter="confirm" placeholder="例如:me"></el-input>
</el-form-item>
<el-form-item
label="设定操作员密码"
prop="password"
:rules="{
required: true, message: '操作员密码不能为空', trigger: 'blur'
}">
<el-input v-model="form.password" @keyup.native.enter="confirm" placeholder="输入操作员密码" type="password"></el-input>
</el-form-item>
<el-form-item
label="设定加速域名"
prop="url"
:rules="{
required: true, message: '加速域名不能为空', trigger: 'blur'
}">
<el-input v-model="form.url" placeholder="例如http://xxx.test.upcdn.net" @keyup.native.enter="confirm()"></el-input>
</el-form-item>
<el-form-item
label="设定网址后缀"
>
<el-input v-model="form.options" @keyup.native.enter="confirm" placeholder="例如!imgslim"></el-input>
</el-form-item>
<el-form-item
label="指定存储路径"
>
<el-input v-model="form.path" @keyup.native.enter="confirm" placeholder="例如img/"></el-input>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('upyun')" round :disabled="defaultPicBed === 'upyun'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
name: 'upyun',
mixins: [mixin],
data () {
return {
form: {
bucket: '',
operator: '',
password: '',
options: '',
path: ''
}
}
},
created () {
const config = this.$db.get('picBed.upyun').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.tcyun.validate((valid) => {
if (valid) {
this.$db.set('picBed.upyun', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
#tcyun-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-input__inner
border-radius 19px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
</style>
+151
View File
@@ -0,0 +1,151 @@
<template>
<div id="weibo-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
微博图床设置
</div>
<el-form
ref="weiboForm"
label-position="right"
label-width="120px"
size="small"
:model="form">
<el-form-item
label="设定用户名"
prop="username"
:rules="{
required: !chooseCookie, message: '用户名不能为空', trigger: 'blur'
}">
<el-input v-model="form.username" placeholder="用户名" @keyup.native.enter="confirm('weiboForm')" :disabled="chooseCookie"></el-input>
</el-form-item>
<el-form-item
label="设定密码"
prop="password"
:rules="{required: !chooseCookie,messsage: '密码不能为空',trigger: 'blur'}">
<el-input v-model="form.password" type="password" @keyup.native.enter="confirm('weiboForm')" placeholder="密码" :disabled="chooseCookie"></el-input>
</el-form-item>
<el-form-item
label="使用Cookie上传"
>
<el-switch
v-model="chooseCookie"
active-text="cookie模式"
@change="handleSwitchChange"
></el-switch>
<i class="el-icon-question" @click="openWiki"></i>
</el-form-item>
<el-form-item
label="设定Cookie"
prop="cookie"
:rules="{
required: chooseCookie, message: '密码不能为空', trigger: 'blur'
}">
<el-input v-model="form.cookie" @keyup.native.enter="confirm('weiboForm')" placeholder="Cookie" :disabled="!chooseCookie"></el-input>
</el-form-item>
<el-form-item label="* 图片质量">
<el-radio-group v-model="quality">
<el-radio label="thumbnail">缩略图</el-radio>
<el-radio label="mw690">中等尺寸</el-radio>
<el-radio label="large">原图</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button-group>
<el-button type="primary" @click="confirm('weiboForm')" round>确定</el-button>
<el-button type="success" @click="setDefaultPicBed('weibo')" round :disabled="defaultPicBed === 'weibo'">设为默认图床</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '@/utils/ConfirmButtonMixin'
export default {
name: 'weibo',
mixins: [mixin],
data () {
return {
form: {
username: '',
password: '',
cookie: ''
},
chooseCookie: false,
quality: 'large'
}
},
created () {
const config = this.$db.read().get('picBed.weibo').value()
if (config) {
this.form.username = config.username
this.form.password = config.password
this.quality = config.quality || 'large'
this.form.cookie = config.cookie
this.chooseCookie = config.chooseCookie
}
},
methods: {
confirm (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$db.read().set('picBed.weibo', {
username: this.form.username,
password: this.form.password,
quality: this.quality,
cookie: this.form.cookie,
chooseCookie: this.chooseCookie
}).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
},
handleSwitchChange () {
this.$refs['weiboForm'].resetFields()
},
openWiki () {
this.$electron.remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#微博图床')
}
}
}
</script>
<style lang='stylus'>
.el-message
left 60%
#weibo-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-button-group
width 100%
.el-button
width 50%
.el-input__inner
border-radius 19px
.el-radio-group
margin-left 25px
.el-switch__label
color #eee
&.is-active
color #409EFF
.el-icon-question
font-size 20px
float right
margin-top 9px
color #eee
cursor pointer
transition .2s color ease-in-out
&:hover
color #409EFF
</style>
+103
View File
@@ -0,0 +1,103 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'tray-page',
component: require('@/pages/TrayPage').default
},
{
path: '/rename-page',
name: 'rename-page',
component: require('@/pages/RenamePage').default
},
{
path: '/mini-page',
name: 'mini-page',
component: require('@/pages/MiniPage').default
},
{
path: '/setting',
name: 'setting-page',
component: require('@/layouts/SettingPage').default,
children: [
{
path: 'upload',
component: require('@/pages/Upload').default,
name: 'upload'
},
{
path: 'weibo',
component: require('@/pages/picbeds/Weibo').default,
name: 'weibo'
},
{
path: 'qiniu',
component: require('@/pages/picbeds/Qiniu').default,
name: 'qiniu'
},
{
path: 'tcyun',
component: require('@/pages/picbeds/TcYun').default,
name: 'tcyun'
},
{
path: 'upyun',
component: require('@/pages/picbeds/UpYun').default,
name: 'upyun'
},
{
path: 'github',
component: require('@/pages/picbeds/GitHub').default,
name: 'github'
},
{
path: 'smms',
component: require('@/pages/picbeds/SMMS').default,
name: 'smms'
},
{
path: 'aliyun',
component: require('@/pages/picbeds/AliYun').default,
name: 'aliyun'
},
{
path: 'imgur',
component: require('@/pages/picbeds/Imgur').default,
name: 'imgur'
},
{
path: 'others/:type',
component: require('@/pages/picbeds/Others').default,
name: 'others'
},
{
path: 'gallery',
component: require('@/pages/Gallery').default,
name: 'gallery',
meta: {
keepAlive: true
}
},
{
path: 'setting',
component: require('@/pages/PicGoSetting').default,
name: 'setting'
},
{
path: 'plugin',
component: require('@/pages/Plugin').default,
name: 'plugin'
}
]
},
{
path: '*',
redirect: '/'
}
]
})
+11
View File
@@ -0,0 +1,11 @@
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
strict: process.env.NODE_ENV !== 'production'
})
+25
View File
@@ -0,0 +1,25 @@
const state = {
main: 0
}
const mutations = {
DECREMENT_MAIN_COUNTER (state) {
state.main--
},
INCREMENT_MAIN_COUNTER (state) {
state.main++
}
}
const actions = {
someAsyncTask ({ commit }) {
// do something async
commit('INCREMENT_MAIN_COUNTER')
}
}
export default {
state,
mutations,
actions
}
+14
View File
@@ -0,0 +1,14 @@
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context('.', false, /\.js$/)
const modules = {}
files.keys().forEach(key => {
if (key === './index.js') return
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
export default modules
+20
View File
@@ -0,0 +1,20 @@
export default {
name: '',
data () {
return {
defaultPicBed: this.$db.read().get('picBed.current').value()
}
},
methods: {
setDefaultPicBed (type) {
this.$db.read().set('picBed.current', type).write()
this.defaultPicBed = type
const successNotification = new window.Notification('设置默认图床', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
class LS {
get (name) {
if (localStorage.getItem(name)) {
return JSON.parse(localStorage.getItem(name))
} else {
return {}
}
}
set (name, value) {
return localStorage.setItem(name, JSON.stringify(value))
}
}
export default new LS()
+38
View File
@@ -0,0 +1,38 @@
import keycode from 'keycode'
const isSpecialKey = (keyCode) => {
const keyArr = [
16, // Shift
17, // Ctrl
18, // Alt
91, // Left Meta
93 // Right Meta
]
return keyArr.includes(keyCode)
}
const keyDetect = (event) => {
const meta = process.platform === 'darwin' ? 'Cmd' : 'Super'
let specialKey = {
Ctrl: event.ctrlKey,
Shift: event.shiftKey,
Alt: event.altKey
}
specialKey[meta] = event.metaKey
let pressKey = []
for (let i in specialKey) {
if (specialKey[i]) {
pressKey.push(i)
}
}
if (!isSpecialKey(event.keyCode)) {
pressKey.push(keycode(event.keyCode).toUpperCase())
}
return pressKey
}
export default keyDetect
+25
View File
@@ -0,0 +1,25 @@
export default {
mounted () {
this.disableDragEvent()
},
methods: {
disableDragEvent () {
window.addEventListener('dragenter', this.disableDrag, false)
window.addEventListener('dragover', this.disableDrag)
window.addEventListener('drop', this.disableDrag)
},
disableDrag (e) {
const dropzone = document.getElementById('upload-area')
if (dropzone === null || !dropzone.contains(e.target)) {
e.preventDefault()
e.dataTransfer.effectAllowed = 'none'
e.dataTransfer.dropEffect = 'none'
}
}
},
beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag)
window.removeEventListener('drop', this.disableDrag)
}
}
View File
@@ -0,0 +1,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>
+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

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

+44
View File
@@ -0,0 +1,44 @@
# 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
}
try {
# For WIN10
$file = Get-Clipboard -Format FileDropList
if ($file -ne $null) {
Convert-Path $file
Exit 1
}
} catch {
# For WIN7 WIN8 WIN10
main
}
main
-1
View File
File diff suppressed because one or more lines are too long

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