├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── feature_request.yml ├── FUNDING.yml └── workflows │ └── send_secret.yml ├── imgs ├── dark.png ├── gallery.png ├── settings.png ├── upload.png ├── cloud_storage.png └── image_editing.png ├── resources ├── icon.icns ├── icon.ico ├── logo.png ├── 256x256.png ├── menubar.png ├── square.png ├── upload.png ├── menubar@2x.png ├── menubar@3x.png ├── roundLogo.png ├── squareLogo.png ├── upload@2x.png ├── upload@3x.png ├── mac.applescript ├── menubar 256.png ├── picbed │ ├── aliyun.png │ ├── github.png │ ├── imgur.png │ ├── qiniu.png │ ├── sftp.png │ ├── smms.png │ ├── tcyun.png │ └── upyun.png ├── upload-dark.png ├── upload-dark@2x.png ├── upload-dark@3x.png ├── menubar-newdarwin.png ├── menubar-nodarwin.png ├── menubar-newdarwin@2.png ├── menubar-newdarwin@3.png ├── menubar-nodarwin@2x.png ├── menubar-nodarwin@3x.png ├── menubar-newdarwinTemplate.png ├── menubar-newdarwinTemplate@2x.png ├── menubar-newdarwinTemplate@3x.png ├── menubar-newdarwinTemplate@4x.png ├── menubar-newdarwinTemplate@512w.png ├── Upload pictures with PicList.workflow │ └── Contents │ │ ├── QuickLook │ │ └── Thumbnail.png │ │ ├── Resources │ │ └── background.color │ │ └── Info.plist ├── wsl.sh ├── windows.ps1 ├── linux.sh └── windows10.ps1 ├── src ├── main │ ├── index.ts │ ├── apis │ │ ├── core │ │ │ ├── picgo │ │ │ │ ├── logger.ts │ │ │ │ └── index.ts │ │ │ └── bus │ │ │ │ ├── constants.ts │ │ │ │ ├── index.ts │ │ │ │ └── apis.ts │ │ ├── README.md │ │ ├── delete │ │ │ ├── awss3.ts │ │ │ ├── dogecloud.ts │ │ │ ├── huaweiyun.ts │ │ │ ├── sftpplist.ts │ │ │ ├── local.ts │ │ │ ├── piclist.ts │ │ │ ├── aliyun.ts │ │ │ ├── upyun.ts │ │ │ ├── smms.ts │ │ │ ├── tcyun.ts │ │ │ ├── alist.ts │ │ │ ├── webdav.ts │ │ │ ├── imgur.ts │ │ │ ├── lskyplist.ts │ │ │ ├── qiniu.ts │ │ │ ├── github.ts │ │ │ ├── allApi.ts │ │ │ └── alistplist.ts │ │ └── app │ │ │ ├── shortKey │ │ │ └── shortKeyService.ts │ │ │ └── window │ │ │ └── windowManager.ts │ ├── utils │ │ ├── notification.ts │ │ ├── handleI18n.ts │ │ ├── deleteLog.ts │ │ ├── updateChecker.ts │ │ ├── static.ts │ │ ├── getPicBeds.ts │ │ ├── handleArgv.ts │ │ ├── performanceOptimizer.ts │ │ ├── clipboardPoll.ts │ │ ├── pasteTemplate.ts │ │ └── autoStart.ts │ ├── manage │ │ ├── Main.ts │ │ ├── apis │ │ │ └── api.ts │ │ └── datastore │ │ │ └── db.ts │ ├── lifeCycle │ │ ├── fixPath.ts │ │ └── errorHandler.ts │ ├── events │ │ ├── rpc │ │ │ ├── routes │ │ │ │ ├── system │ │ │ │ │ ├── index.ts │ │ │ │ │ └── app.ts │ │ │ │ ├── toolbox │ │ │ │ │ ├── utils.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── picbed │ │ │ │ │ └── delete.ts │ │ │ │ ├── manage │ │ │ │ │ ├── index.ts │ │ │ │ │ └── config.ts │ │ │ │ ├── setting │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── advanced.ts │ │ │ │ │ └── shortKey.ts │ │ │ │ ├── plugin │ │ │ │ │ └── index.ts │ │ │ │ └── upload │ │ │ │ │ └── index.ts │ │ │ └── router.ts │ │ ├── constant.ts │ │ └── busEventList.ts │ ├── fileServer │ │ └── index.ts │ └── server │ │ ├── router.ts │ │ └── apiDoc.ts ├── renderer │ ├── public │ │ ├── logo.png │ │ ├── loading.jpg │ │ ├── roundLogo.png │ │ ├── squareLogo.png │ │ ├── assets │ │ │ ├── imgur.webp │ │ │ ├── local.webp │ │ │ ├── qiniu.webp │ │ │ ├── sftp.webp │ │ │ ├── smms.webp │ │ │ ├── tcyun.webp │ │ │ ├── upyun.webp │ │ │ ├── aliyun.webp │ │ │ ├── github.webp │ │ │ ├── icons │ │ │ │ ├── c.webp │ │ │ │ ├── h.webp │ │ │ │ ├── z.webp │ │ │ │ ├── 3g2.webp │ │ │ │ ├── 3gp.webp │ │ │ │ ├── 7z.webp │ │ │ │ ├── aac.webp │ │ │ │ ├── adt.webp │ │ │ │ ├── ai.webp │ │ │ │ ├── aiff.webp │ │ │ │ ├── aly.webp │ │ │ │ ├── amr.webp │ │ │ │ ├── ape.webp │ │ │ │ ├── apk.webp │ │ │ │ ├── arj.webp │ │ │ │ ├── asf.webp │ │ │ │ ├── asm.webp │ │ │ │ ├── asx.webp │ │ │ │ ├── au.webp │ │ │ │ ├── avc.webp │ │ │ │ ├── avi.webp │ │ │ │ ├── avif.webp │ │ │ │ ├── avs.webp │ │ │ │ ├── bak.webp │ │ │ │ ├── bas.webp │ │ │ │ ├── bat.webp │ │ │ │ ├── bmp.webp │ │ │ │ ├── bom.webp │ │ │ │ ├── cda.webp │ │ │ │ ├── cdr.webp │ │ │ │ ├── chm.webp │ │ │ │ ├── cmd.webp │ │ │ │ ├── com.webp │ │ │ │ ├── cpp.webp │ │ │ │ ├── css.webp │ │ │ │ ├── csv.webp │ │ │ │ ├── dart.webp │ │ │ │ ├── dat.webp │ │ │ │ ├── ddb.webp │ │ │ │ ├── dif.webp │ │ │ │ ├── divx.webp │ │ │ │ ├── dll.webp │ │ │ │ ├── dmg.webp │ │ │ │ ├── doc.webp │ │ │ │ ├── docm.webp │ │ │ │ ├── docx.webp │ │ │ │ ├── dot.webp │ │ │ │ ├── dotm.webp │ │ │ │ ├── dotx.webp │ │ │ │ ├── dsl.webp │ │ │ │ ├── dv.webp │ │ │ │ ├── dvd.webp │ │ │ │ ├── dwg.webp │ │ │ │ ├── dxf.webp │ │ │ │ ├── emf.webp │ │ │ │ ├── env.webp │ │ │ │ ├── eot.webp │ │ │ │ ├── eps.webp │ │ │ │ ├── exe.webp │ │ │ │ ├── exif.webp │ │ │ │ ├── flc.webp │ │ │ │ ├── fli.webp │ │ │ │ ├── flv.webp │ │ │ │ ├── fon.webp │ │ │ │ ├── font.webp │ │ │ │ ├── for.webp │ │ │ │ ├── fpx.webp │ │ │ │ ├── fv.webp │ │ │ │ ├── gif.webp │ │ │ │ ├── gz.webp │ │ │ │ ├── hdri.webp │ │ │ │ ├── hlp.webp │ │ │ │ ├── hpp.webp │ │ │ │ ├── htm.webp │ │ │ │ ├── html.webp │ │ │ │ ├── ico.webp │ │ │ │ ├── ics.webp │ │ │ │ ├── int.webp │ │ │ │ ├── iso.webp │ │ │ │ ├── java.webp │ │ │ │ ├── jpeg.webp │ │ │ │ ├── jpg.webp │ │ │ │ ├── js.webp │ │ │ │ ├── json.webp │ │ │ │ ├── key.webp │ │ │ │ ├── ksp.webp │ │ │ │ ├── less.webp │ │ │ │ ├── lib.webp │ │ │ │ ├── lic.webp │ │ │ │ ├── log.webp │ │ │ │ ├── lst.webp │ │ │ │ ├── lua.webp │ │ │ │ ├── mac.webp │ │ │ │ ├── map.webp │ │ │ │ ├── md.webp │ │ │ │ ├── mdf.webp │ │ │ │ ├── mht.webp │ │ │ │ ├── mid.webp │ │ │ │ ├── midi.webp │ │ │ │ ├── mkv.webp │ │ │ │ ├── mmf.webp │ │ │ │ ├── mod.webp │ │ │ │ ├── mov.webp │ │ │ │ ├── mp2.webp │ │ │ │ ├── mp3.webp │ │ │ │ ├── mp4.webp │ │ │ │ ├── mpa.webp │ │ │ │ ├── mpe.webp │ │ │ │ ├── mpeg.webp │ │ │ │ ├── mpg.webp │ │ │ │ ├── msg.webp │ │ │ │ ├── mts.webp │ │ │ │ ├── mux.webp │ │ │ │ ├── mv.webp │ │ │ │ ├── navi.webp │ │ │ │ ├── obj.webp │ │ │ │ ├── odf.webp │ │ │ │ ├── ods.webp │ │ │ │ ├── odt.webp │ │ │ │ ├── ogg.webp │ │ │ │ ├── one.webp │ │ │ │ ├── otf.webp │ │ │ │ ├── otp.webp │ │ │ │ ├── ots.webp │ │ │ │ ├── ott.webp │ │ │ │ ├── pas.webp │ │ │ │ ├── pcd.webp │ │ │ │ ├── pcx.webp │ │ │ │ ├── pdf.webp │ │ │ │ ├── php.webp │ │ │ │ ├── pic.webp │ │ │ │ ├── png.webp │ │ │ │ ├── ppt.webp │ │ │ │ ├── pptx.webp │ │ │ │ ├── proe.webp │ │ │ │ ├── prt.webp │ │ │ │ ├── psd.webp │ │ │ │ ├── py.webp │ │ │ │ ├── pyc.webp │ │ │ │ ├── qsv.webp │ │ │ │ ├── qt.webp │ │ │ │ ├── ra.webp │ │ │ │ ├── ram.webp │ │ │ │ ├── rar.webp │ │ │ │ ├── raw.webp │ │ │ │ ├── rb.webp │ │ │ │ ├── rm.webp │ │ │ │ ├── rmvb.webp │ │ │ │ ├── rp.webp │ │ │ │ ├── rtf.webp │ │ │ │ ├── s48.webp │ │ │ │ ├── sacd.webp │ │ │ │ ├── sass.webp │ │ │ │ ├── sch.webp │ │ │ │ ├── scss.webp │ │ │ │ ├── sh.webp │ │ │ │ ├── sql.webp │ │ │ │ ├── stp.webp │ │ │ │ ├── svcd.webp │ │ │ │ ├── svg.webp │ │ │ │ ├── swf.webp │ │ │ │ ├── sys.webp │ │ │ │ ├── tga.webp │ │ │ │ ├── tgz.webp │ │ │ │ ├── tiff.webp │ │ │ │ ├── tmp.webp │ │ │ │ ├── ts.webp │ │ │ │ ├── ttc.webp │ │ │ │ ├── ttf.webp │ │ │ │ ├── txt.webp │ │ │ │ ├── ufo.webp │ │ │ │ ├── vcd.webp │ │ │ │ ├── vob.webp │ │ │ │ ├── voc.webp │ │ │ │ ├── vqf.webp │ │ │ │ ├── vue.webp │ │ │ │ ├── wav.webp │ │ │ │ ├── wdl.webp │ │ │ │ ├── webm.webp │ │ │ │ ├── webp.webp │ │ │ │ ├── wki.webp │ │ │ │ ├── wma.webp │ │ │ │ ├── wmf.webp │ │ │ │ ├── wmv.webp │ │ │ │ ├── woff.webp │ │ │ │ ├── wps.webp │ │ │ │ ├── wpt.webp │ │ │ │ ├── x_t.webp │ │ │ │ ├── xls.webp │ │ │ │ ├── xlsm.webp │ │ │ │ ├── xlsx.webp │ │ │ │ ├── xlt.webp │ │ │ │ ├── xltm.webp │ │ │ │ ├── xltx.webp │ │ │ │ ├── xml.webp │ │ │ │ ├── xv.webp │ │ │ │ ├── xvid.webp │ │ │ │ ├── yaml.webp │ │ │ │ ├── yml.webp │ │ │ │ ├── zip.webp │ │ │ │ ├── _blank.webp │ │ │ │ ├── _page.webp │ │ │ │ ├── accdb.webp │ │ │ │ ├── amiga.webp │ │ │ │ ├── class.webp │ │ │ │ ├── folder.webp │ │ │ │ ├── ipynb.webp │ │ │ │ ├── mhtml.webp │ │ │ │ ├── mpeg1.webp │ │ │ │ ├── mpeg2.webp │ │ │ │ ├── mppro.webp │ │ │ │ ├── wmvhd.webp │ │ │ │ ├── woff2.webp │ │ │ │ ├── xmind.webp │ │ │ │ ├── dvdaudio.webp │ │ │ │ ├── fakesmms.webp │ │ │ │ ├── gitingore.webp │ │ │ │ ├── gitkeep.webp │ │ │ │ ├── license.webp │ │ │ │ ├── markdown.webp │ │ │ │ ├── quicktime.webp │ │ │ │ ├── realaudio.webp │ │ │ │ └── unknown.webp │ │ │ ├── s3plist.webp │ │ │ └── webdavplist.webp │ │ ├── errorLoading.png │ │ ├── picbed │ │ │ ├── aliyun.png │ │ │ ├── github.png │ │ │ ├── imgur.png │ │ │ ├── qiniu.png │ │ │ ├── sftp.png │ │ │ ├── smms.png │ │ │ ├── tcyun.png │ │ │ └── upyun.png │ │ └── unknown-file-type.svg │ ├── hooks │ │ ├── useStore.ts │ │ ├── useATagClick.ts │ │ ├── useConfirm.ts │ │ └── useMessage.ts │ ├── i18n │ │ └── index.ts │ ├── index.html │ ├── apis │ │ └── allApi.ts │ ├── manage │ │ ├── utils │ │ │ ├── videofile.ts │ │ │ ├── dataSender.ts │ │ │ └── textfile.ts │ │ ├── pages │ │ │ └── EmptyPage.vue │ │ └── store │ │ │ ├── bucketFileDb.ts │ │ │ └── manageStore.ts │ ├── utils │ │ ├── bus.ts │ │ ├── dataSender.ts │ │ ├── global.ts │ │ ├── constant.ts │ │ ├── key-binding.ts │ │ ├── drag.ts │ │ ├── getLatestVersion.ts │ │ ├── db.ts │ │ └── common.ts │ ├── router │ │ └── config.ts │ ├── store │ │ └── index.ts │ ├── components │ │ ├── ToolboxHandler.vue │ │ └── ToolboxStatusIcon.vue │ ├── App.vue │ └── main.ts └── universal │ └── types │ ├── globals.d.ts │ ├── i18n.d.ts │ ├── shims-module.d.ts │ ├── extra-vue.ts │ ├── electron.ts │ ├── rpc.ts │ └── view.ts ├── currentVersion.md ├── .winget └── manifests │ └── k │ └── Kuingsmile │ └── PicList │ └── 3.0.6 │ ├── Kuingsmile.PicList.yaml │ ├── Kuingsmile.PicList.installer.yaml │ └── Kuingsmile.PicList.locale.en-US.yaml ├── .vscode ├── mcp.json ├── tasks.json ├── launch.json └── settings.json ├── .prettierrc ├── scripts ├── afterPack.cjs ├── gen-i18n-types.js ├── notarize.cjs ├── link.js └── config.js ├── .gitignore ├── currentVersion_en.md ├── CONTRIBUTING.md ├── LICENSE ├── tsconfig.json ├── electron.vite.config.js ├── CONTRIBUTING_EN.md └── electron-builder.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://paypal.me/Kuingsmile"] 2 | -------------------------------------------------------------------------------- /imgs/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/imgs/dark.png -------------------------------------------------------------------------------- /imgs/gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/imgs/gallery.png -------------------------------------------------------------------------------- /imgs/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/imgs/settings.png -------------------------------------------------------------------------------- /imgs/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/imgs/upload.png -------------------------------------------------------------------------------- /resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/icon.icns -------------------------------------------------------------------------------- /resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/icon.ico -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/logo.png -------------------------------------------------------------------------------- /src/main/index.ts: -------------------------------------------------------------------------------- 1 | import { lifeCycle } from '~/lifeCycle' 2 | 3 | lifeCycle.launchApp() 4 | -------------------------------------------------------------------------------- /imgs/cloud_storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/imgs/cloud_storage.png -------------------------------------------------------------------------------- /imgs/image_editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/imgs/image_editing.png -------------------------------------------------------------------------------- /resources/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/256x256.png -------------------------------------------------------------------------------- /resources/menubar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar.png -------------------------------------------------------------------------------- /resources/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/square.png -------------------------------------------------------------------------------- /resources/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/upload.png -------------------------------------------------------------------------------- /resources/menubar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar@2x.png -------------------------------------------------------------------------------- /resources/menubar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar@3x.png -------------------------------------------------------------------------------- /resources/roundLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/roundLogo.png -------------------------------------------------------------------------------- /resources/squareLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/squareLogo.png -------------------------------------------------------------------------------- /resources/upload@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/upload@2x.png -------------------------------------------------------------------------------- /resources/upload@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/upload@3x.png -------------------------------------------------------------------------------- /resources/mac.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/mac.applescript -------------------------------------------------------------------------------- /resources/menubar 256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar 256.png -------------------------------------------------------------------------------- /resources/picbed/aliyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/aliyun.png -------------------------------------------------------------------------------- /resources/picbed/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/github.png -------------------------------------------------------------------------------- /resources/picbed/imgur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/imgur.png -------------------------------------------------------------------------------- /resources/picbed/qiniu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/qiniu.png -------------------------------------------------------------------------------- /resources/picbed/sftp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/sftp.png -------------------------------------------------------------------------------- /resources/picbed/smms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/smms.png -------------------------------------------------------------------------------- /resources/picbed/tcyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/tcyun.png -------------------------------------------------------------------------------- /resources/picbed/upyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/picbed/upyun.png -------------------------------------------------------------------------------- /resources/upload-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/upload-dark.png -------------------------------------------------------------------------------- /resources/upload-dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/upload-dark@2x.png -------------------------------------------------------------------------------- /resources/upload-dark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/upload-dark@3x.png -------------------------------------------------------------------------------- /src/renderer/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/logo.png -------------------------------------------------------------------------------- /resources/menubar-newdarwin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwin.png -------------------------------------------------------------------------------- /resources/menubar-nodarwin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-nodarwin.png -------------------------------------------------------------------------------- /src/renderer/public/loading.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/loading.jpg -------------------------------------------------------------------------------- /resources/menubar-newdarwin@2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwin@2.png -------------------------------------------------------------------------------- /resources/menubar-newdarwin@3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwin@3.png -------------------------------------------------------------------------------- /resources/menubar-nodarwin@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-nodarwin@2x.png -------------------------------------------------------------------------------- /resources/menubar-nodarwin@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-nodarwin@3x.png -------------------------------------------------------------------------------- /src/renderer/public/roundLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/roundLogo.png -------------------------------------------------------------------------------- /src/renderer/public/squareLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/squareLogo.png -------------------------------------------------------------------------------- /src/main/apis/core/picgo/logger.ts: -------------------------------------------------------------------------------- 1 | import picgo from '.' 2 | 3 | const logger = picgo.log 4 | 5 | export default logger 6 | -------------------------------------------------------------------------------- /src/renderer/public/assets/imgur.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/imgur.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/local.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/local.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/qiniu.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/qiniu.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/sftp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/sftp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/smms.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/smms.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/tcyun.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/tcyun.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/upyun.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/upyun.webp -------------------------------------------------------------------------------- /src/renderer/public/errorLoading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/errorLoading.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/aliyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/aliyun.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/github.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/imgur.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/imgur.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/qiniu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/qiniu.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/sftp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/sftp.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/smms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/smms.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/tcyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/tcyun.png -------------------------------------------------------------------------------- /src/renderer/public/picbed/upyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/picbed/upyun.png -------------------------------------------------------------------------------- /resources/menubar-newdarwinTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwinTemplate.png -------------------------------------------------------------------------------- /src/renderer/public/assets/aliyun.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/aliyun.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/github.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/github.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/c.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/c.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/h.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/h.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/z.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/z.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/s3plist.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/s3plist.webp -------------------------------------------------------------------------------- /resources/menubar-newdarwinTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwinTemplate@2x.png -------------------------------------------------------------------------------- /resources/menubar-newdarwinTemplate@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwinTemplate@3x.png -------------------------------------------------------------------------------- /resources/menubar-newdarwinTemplate@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwinTemplate@4x.png -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/3g2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/3g2.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/3gp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/3gp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/7z.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/7z.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/aac.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/aac.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/adt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/adt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ai.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ai.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/aiff.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/aiff.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/aly.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/aly.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/amr.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/amr.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ape.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ape.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/apk.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/apk.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/arj.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/arj.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/asf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/asf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/asm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/asm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/asx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/asx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/au.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/au.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/avc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/avc.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/avi.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/avi.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/avif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/avif.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/avs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/avs.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/bak.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/bak.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/bas.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/bas.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/bat.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/bat.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/bmp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/bmp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/bom.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/bom.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/cda.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/cda.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/cdr.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/cdr.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/chm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/chm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/cmd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/cmd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/com.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/com.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/cpp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/cpp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/css.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/css.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/csv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/csv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dart.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dart.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dat.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dat.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ddb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ddb.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dif.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/divx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/divx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dll.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dll.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dmg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dmg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/doc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/doc.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/docm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/docm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/docx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/docx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dot.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dotm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dotm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dotx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dotx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dsl.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dsl.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dvd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dvd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dwg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dwg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dxf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dxf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/emf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/emf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/env.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/env.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/eot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/eot.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/eps.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/eps.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/exe.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/exe.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/exif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/exif.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/flc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/flc.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/fli.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/fli.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/flv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/flv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/fon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/fon.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/font.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/font.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/for.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/for.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/fpx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/fpx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/fv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/fv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/gif.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/gif.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/gz.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/gz.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/hdri.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/hdri.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/hlp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/hlp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/hpp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/hpp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/htm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/htm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/html.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/html.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ico.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ico.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ics.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ics.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/int.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/int.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/iso.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/iso.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/java.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/java.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/jpeg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/jpeg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/jpg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/jpg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/js.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/js.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/json.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/json.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/key.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/key.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ksp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ksp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/less.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/less.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/lib.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/lib.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/lic.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/lic.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/log.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/log.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/lst.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/lst.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/lua.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/lua.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mac.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mac.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/map.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/map.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/md.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/md.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mdf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mdf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mht.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mht.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mid.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mid.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/midi.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/midi.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mkv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mkv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mmf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mmf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mod.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mod.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mov.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mov.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mp2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mp2.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mp3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mp3.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mp4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mp4.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mpa.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mpa.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mpe.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mpe.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mpeg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mpeg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mpg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mpg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/msg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/msg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mts.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mts.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mux.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mux.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/navi.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/navi.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/obj.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/obj.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/odf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/odf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ods.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ods.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/odt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/odt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ogg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ogg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/one.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/one.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/otf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/otf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/otp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/otp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ots.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ots.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ott.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ott.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pas.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pas.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pcd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pcd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pcx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pcx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pdf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pdf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/php.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/php.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pic.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pic.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/png.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/png.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ppt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ppt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pptx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pptx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/proe.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/proe.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/prt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/prt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/psd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/psd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/py.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/py.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/pyc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/pyc.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/qsv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/qsv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/qt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/qt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ra.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ra.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ram.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ram.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/rar.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/rar.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/raw.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/raw.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/rb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/rb.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/rm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/rm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/rmvb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/rmvb.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/rp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/rp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/rtf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/rtf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/s48.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/s48.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/sacd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/sacd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/sass.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/sass.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/sch.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/sch.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/scss.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/scss.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/sh.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/sh.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/sql.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/sql.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/stp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/stp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/svcd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/svcd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/svg.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/svg.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/swf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/swf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/sys.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/sys.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/tga.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/tga.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/tgz.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/tgz.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/tiff.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/tiff.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/tmp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/tmp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ts.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ts.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ttc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ttc.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ttf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ttf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/txt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/txt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ufo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ufo.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/vcd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/vcd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/vob.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/vob.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/voc.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/voc.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/vqf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/vqf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/vue.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/vue.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wav.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wav.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wdl.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wdl.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/webm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/webm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/webp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/webp.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wki.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wki.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wma.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wma.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wmf.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wmf.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wmv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wmv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/woff.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/woff.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wps.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wps.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wpt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wpt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/x_t.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/x_t.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xls.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xls.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xlsm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xlsm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xlsx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xlsx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xlt.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xlt.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xltm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xltm.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xltx.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xltx.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xml.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xml.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xv.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xv.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xvid.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xvid.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/yaml.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/yaml.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/yml.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/yml.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/zip.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/zip.webp -------------------------------------------------------------------------------- /resources/menubar-newdarwinTemplate@512w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/menubar-newdarwinTemplate@512w.png -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/_blank.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/_blank.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/_page.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/_page.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/accdb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/accdb.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/amiga.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/amiga.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/class.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/class.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/folder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/folder.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/ipynb.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/ipynb.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mhtml.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mhtml.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mpeg1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mpeg1.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mpeg2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mpeg2.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/mppro.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/mppro.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/wmvhd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/wmvhd.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/woff2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/woff2.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/xmind.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/xmind.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/webdavplist.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/webdavplist.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/dvdaudio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/dvdaudio.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/fakesmms.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/fakesmms.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/gitingore.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/gitingore.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/gitkeep.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/gitkeep.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/license.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/license.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/markdown.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/markdown.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/quicktime.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/quicktime.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/realaudio.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/realaudio.webp -------------------------------------------------------------------------------- /src/renderer/public/assets/icons/unknown.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/src/renderer/public/assets/icons/unknown.webp -------------------------------------------------------------------------------- /src/main/utils/notification.ts: -------------------------------------------------------------------------------- 1 | import type { IAppNotification } from '#/types/types' 2 | 3 | export const notificationList: IAppNotification[] = [] 4 | -------------------------------------------------------------------------------- /src/renderer/hooks/useStore.ts: -------------------------------------------------------------------------------- 1 | import { inject } from 'vue' 2 | 3 | import { storeKey } from '@/store' 4 | 5 | export const useStore = () => { 6 | return inject(storeKey) ?? null 7 | } 8 | -------------------------------------------------------------------------------- /resources/Upload pictures with PicList.workflow/Contents/QuickLook/Thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/Upload pictures with PicList.workflow/Contents/QuickLook/Thumbnail.png -------------------------------------------------------------------------------- /src/renderer/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import { IRPCActionType } from '@/utils/enum' 2 | 3 | export function setCurrentLanguage(lang: string) { 4 | window.electron.sendRPC(IRPCActionType.SET_CURRENT_LANGUAGE, lang) 5 | } 6 | -------------------------------------------------------------------------------- /resources/Upload pictures with PicList.workflow/Contents/Resources/background.color: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kuingsmile/PicList/HEAD/resources/Upload pictures with PicList.workflow/Contents/Resources/background.color -------------------------------------------------------------------------------- /src/universal/types/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ssh2-no-cpu-features' { 2 | const Client: any 3 | export { Client } 4 | } 5 | 6 | declare module 'shell-path' { 7 | export const shellPathSync: () => string | null 8 | } 9 | -------------------------------------------------------------------------------- /src/universal/types/i18n.d.ts: -------------------------------------------------------------------------------- 1 | import 'vue-i18n' 2 | 3 | import zhCN from '../../renderer/i18n/locales/zh-CN.json' 4 | 5 | type MessageSchema = typeof zhCN 6 | declare module 'vue-i18n' { 7 | export interface DefineLocaleMessage extends MessageSchema {} 8 | } 9 | -------------------------------------------------------------------------------- /src/universal/types/shims-module.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | declare module '*.vue' { 5 | import { DefineComponent } from 'vue' 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /currentVersion.md: -------------------------------------------------------------------------------- 1 | ## 🎉 [v3.0.6] 更新日志 2 | 3 | ### ✨ 新增功能 4 | 5 | - 优化相册预览的缩放与定位 6 | - 多图上传:≤3 条逐条通知,>3 条聚合通知 7 | - github图床新增webPath参数设置 8 | 9 | ### 🐛 问题修复 10 | 11 | - 修复相册滚动越界/刷新问题 12 | - 修复快捷键无法禁用 13 | - 修复了文件或路径存在特殊中文字符时下载失败的问题 14 | - 修复了管理-下载页面中已完成/失败面板显示错误的问题 15 | - 防止应用图标被拖拽或选中 16 | -------------------------------------------------------------------------------- /src/main/manage/Main.ts: -------------------------------------------------------------------------------- 1 | import { manageDbChecker } from '~/manage/datastore/dbChecker' 2 | import { ManageApi } from '~/manage/manageApi' 3 | 4 | manageDbChecker() 5 | const getManageApi = (picBedName: string = 'placeholder'): ManageApi => { 6 | return new ManageApi(picBedName) 7 | } 8 | 9 | export default getManageApi 10 | -------------------------------------------------------------------------------- /.winget/manifests/k/Kuingsmile/PicList/3.0.6/Kuingsmile.PicList.yaml: -------------------------------------------------------------------------------- 1 | # Created using wingetcreate 1.9.14.0 2 | # yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json 3 | 4 | PackageIdentifier: Kuingsmile.PicList 5 | PackageVersion: 3.0.6 6 | DefaultLocale: en-US 7 | ManifestType: version 8 | ManifestVersion: 1.9.0 9 | -------------------------------------------------------------------------------- /src/main/lifeCycle/fixPath.ts: -------------------------------------------------------------------------------- 1 | import { shellPathSync } from 'shell-path' 2 | 3 | export default function fixPath() { 4 | if (process.platform === 'win32') { 5 | return 6 | } 7 | 8 | process.env.PATH = 9 | shellPathSync() || ['./node_modules/.bin', '/.nodebrew/current/bin', '/usr/local/bin', process.env.PATH].join(':') 10 | } 11 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PicList 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputs": [ 3 | // The "inputs" section defines the inputs required for the MCP server configuration. 4 | { 5 | "type": "promptString" 6 | } 7 | ], 8 | "servers": { 9 | // The "servers" section defines the MCP servers you want to use. 10 | "fetch": { 11 | "command": "uvx", 12 | "args": ["mcp-server-fetch"] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/system/index.ts: -------------------------------------------------------------------------------- 1 | import { RPCRouter } from '~/events/rpc/router' 2 | import appRoutes from '~/events/rpc/routes/system/app' 3 | import windowRoutes from '~/events/rpc/routes/system/window' 4 | 5 | const systemRouter = new RPCRouter() 6 | 7 | const systemRoutes = [...appRoutes, ...windowRoutes] 8 | 9 | systemRouter.addBatch(systemRoutes) 10 | 11 | export { systemRouter } 12 | -------------------------------------------------------------------------------- /src/main/utils/handleI18n.ts: -------------------------------------------------------------------------------- 1 | import db from '@core/datastore' 2 | 3 | import { i18nManager } from '~/i18n' 4 | import { configPaths } from '~/utils/configPaths' 5 | import { II18nLanguage } from '~/utils/enum' 6 | 7 | export const initI18n = () => { 8 | const currentLanguage = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN 9 | i18nManager.setCurrentLanguage(currentLanguage) 10 | } 11 | -------------------------------------------------------------------------------- /src/renderer/apis/allApi.ts: -------------------------------------------------------------------------------- 1 | import { getRawData } from '@/utils/common' 2 | import { IRPCActionType } from '@/utils/enum' 3 | import type { IStringKeyMap } from '#/types/types' 4 | 5 | export default class ALLApi { 6 | static async delete(configMap: IStringKeyMap): Promise { 7 | return (await window.electron.triggerRPC(IRPCActionType.DELETE_ALL_API, getRawData(configMap))) || false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/utils/deleteLog.ts: -------------------------------------------------------------------------------- 1 | import logger from '@core/picgo/logger' 2 | 3 | export const deleteLog = (fileName?: string, type?: string, isSuccess = true, msg?: string) => { 4 | logger.info(`Delete ${fileName} on ${type} ${isSuccess ? 'successfully' : 'failed'} ${msg ? 'msg: ' + msg : ''}`) 5 | } 6 | 7 | export const deleteFailedLog = (fileName: string, type: string, error: any) => { 8 | deleteLog(fileName, type, false) 9 | logger.error(error) 10 | } 11 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/toolbox/utils.ts: -------------------------------------------------------------------------------- 1 | import { IpcMainEvent } from 'electron' 2 | 3 | import type { IToolboxCheckRes } from '#/types/rpc' 4 | import { IRPCActionType } from '~/utils/enum' 5 | 6 | export function sendToolboxResWithType(type: string) { 7 | return (event: IpcMainEvent, res?: Omit) => { 8 | return event.sender.send(IRPCActionType.TOOLBOX_CHECK_RES, { 9 | ...res, 10 | type 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/renderer/manage/utils/videofile.ts: -------------------------------------------------------------------------------- 1 | export const videoExt = [ 2 | '.aac', 3 | '.amv', 4 | '.avi', 5 | '.flac', 6 | '.flv', 7 | '.m2ts', 8 | '.m4a', 9 | '.m4v', 10 | '.mp3', 11 | '.mpeg', 12 | '.mpg', 13 | '.mts', 14 | '.ogg', 15 | '.ogv', 16 | '.vob', 17 | '.wav', 18 | '.webm', 19 | '.mp4', 20 | '.3g2', 21 | '.3gp', 22 | '.asf', 23 | '.mov', 24 | '.mxf', 25 | '.rm', 26 | '.rmvb', 27 | '.wmv', 28 | '.mkv' 29 | ] 30 | -------------------------------------------------------------------------------- /src/main/apis/README.md: -------------------------------------------------------------------------------- 1 | # apis folder 2 | 3 | ## core 4 | 5 | The lowest level APIs that are not dependent on each other. The upper APIs depend on them. 6 | 不相互依赖的最低级别api。 上层api依赖于它们。 7 | 8 | ## app 9 | 10 | Provide key API interfaces for PicList application, including uploader, window management, shortcut key system, remotes handler, etc 11 | 为PicList应用程序提供关键API接口,包括上传器、窗口管理、快捷键系统、远程处理程序等。 12 | 13 | ## gui 14 | 15 | GuiApi for PicList plugins. 16 | 用于PicList插件的GuiApi。 17 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "htmlWhitespaceSensitivity": "css", 5 | "insertPragma": false, 6 | "jsxBracketSameLine": false, 7 | "jsxSingleQuote": true, 8 | "printWidth": 120, 9 | "proseWrap": "always", 10 | "quoteProps": "as-needed", 11 | "requirePragma": false, 12 | "semi": false, 13 | "singleQuote": true, 14 | "tabWidth": 2, 15 | "trailingComma": "none", 16 | "useTabs": false, 17 | "endOfLine": "lf" 18 | } 19 | -------------------------------------------------------------------------------- /src/renderer/utils/bus.ts: -------------------------------------------------------------------------------- 1 | import mitt from 'mitt' 2 | 3 | import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant' 4 | 5 | // eslint-disable-next-line @typescript-eslint/consistent-type-definitions 6 | type IEvent = { 7 | [SHOW_INPUT_BOX_RESPONSE]: string 8 | [SHOW_INPUT_BOX]: { 9 | value: string 10 | title: string 11 | placeholder: string 12 | multiLine?: boolean 13 | } 14 | } 15 | 16 | const emitter = mitt() 17 | 18 | export default emitter 19 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/picbed/delete.ts: -------------------------------------------------------------------------------- 1 | import ALLApi from 'apis/delete/allApi' 2 | 3 | import type { IIPCEvent } from '#/types/rpc' 4 | import type { ImgInfo } from '#/types/types' 5 | import { IRPCActionType, IRPCType } from '~/utils/enum' 6 | 7 | export default [ 8 | { 9 | action: IRPCActionType.DELETE_ALL_API, 10 | handler: async (_: IIPCEvent, args: [item: ImgInfo]) => { 11 | return await ALLApi.delete(args[0]) 12 | }, 13 | type: IRPCType.INVOKE 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /scripts/afterPack.cjs: -------------------------------------------------------------------------------- 1 | const fs = require('node:fs') 2 | 3 | async function main(context) { 4 | const localeDir = context.appOutDir + '/locales/' 5 | 6 | fs.readdir(localeDir, function (_err, files) { 7 | if (!(files && files.length)) return 8 | for (let i = 0, len = files.length; i < len; i++) { 9 | if (!(files[i].startsWith('en') || files[i].startsWith('zh'))) { 10 | fs.unlinkSync(localeDir + files[i]) 11 | } 12 | } 13 | }) 14 | } 15 | 16 | exports.default = main 17 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/manage/index.ts: -------------------------------------------------------------------------------- 1 | import { RPCRouter } from '~/events/rpc/router' 2 | import bucketRoutes from '~/events/rpc/routes/manage/bucket' 3 | import configRoutes from '~/events/rpc/routes/manage/config' 4 | import upDownLoadRoutes from '~/events/rpc/routes/manage/upDownload' 5 | 6 | const manageRouter = new RPCRouter() 7 | 8 | const manageRoutes = [...configRoutes, ...bucketRoutes, ...upDownLoadRoutes] 9 | 10 | manageRouter.addBatch(manageRoutes) 11 | 12 | export { manageRouter } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/electron/* 3 | dist/web/* 4 | build/* 5 | !build/icons 6 | !build/installer.nsh 7 | coverage 8 | node_modules/ 9 | npm-debug.log 10 | npm-debug.log.* 11 | thumbs.db 12 | remote-notice.json 13 | !.gitkeep 14 | yarn-error.log 15 | docs/dist/ 16 | # local env files 17 | .env.local 18 | .env.*.local 19 | dist_electron/ 20 | test.js 21 | .env 22 | scripts/*.yml 23 | scripts/generateYmlFile.js 24 | .yarnrc 25 | #Electron-builder output 26 | /dist_electron 27 | /docs 28 | cloc.exe 29 | desktop.ini 30 | /dist 31 | /out 32 | /release 33 | -------------------------------------------------------------------------------- /src/main/utils/updateChecker.ts: -------------------------------------------------------------------------------- 1 | import db from '@core/datastore' 2 | import updater from 'electron-updater' 3 | 4 | import { configPaths } from '~/utils/configPaths' 5 | 6 | const updateChecker = async () => { 7 | let showTip = db.get(configPaths.settings.showUpdateTip) 8 | if (showTip === undefined) { 9 | db.set(configPaths.settings.showUpdateTip, true) 10 | showTip = true 11 | } 12 | if (showTip) { 13 | try { 14 | await updater.autoUpdater.checkForUpdatesAndNotify() 15 | } catch (err) {} 16 | } 17 | } 18 | 19 | export default updateChecker 20 | -------------------------------------------------------------------------------- /src/main/apis/delete/awss3.ts: -------------------------------------------------------------------------------- 1 | import type { IStringKeyMap } from '#/types/types' 2 | import { getRawData } from '~/utils/common' 3 | import { removeFileFromS3InMain } from '~/utils/deleteFunc' 4 | import { deleteFailedLog } from '~/utils/deleteLog' 5 | 6 | export default class AwsS3Api { 7 | static async delete(configMap: IStringKeyMap): Promise { 8 | try { 9 | return await removeFileFromS3InMain(getRawData(configMap)) 10 | } catch (error: any) { 11 | deleteFailedLog(configMap.fileName, 'AWS S3', error) 12 | return false 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/apis/delete/dogecloud.ts: -------------------------------------------------------------------------------- 1 | import type { IStringKeyMap } from '#/types/types' 2 | import { getRawData } from '~/utils/common' 3 | import { removeFileFromDogeInMain } from '~/utils/deleteFunc' 4 | import { deleteFailedLog } from '~/utils/deleteLog' 5 | 6 | export default class AwsS3Api { 7 | static async delete(configMap: IStringKeyMap): Promise { 8 | try { 9 | return await removeFileFromDogeInMain(getRawData(configMap)) 10 | } catch (error: any) { 11 | deleteFailedLog(configMap.fileName, 'DogeCloud', error) 12 | return false 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/apis/delete/huaweiyun.ts: -------------------------------------------------------------------------------- 1 | import type { IStringKeyMap } from '#/types/types' 2 | import { getRawData } from '~/utils/common' 3 | import { removeFileFromHuaweiInMain } from '~/utils/deleteFunc' 4 | import { deleteFailedLog } from '~/utils/deleteLog' 5 | 6 | export default class HuaweicloudApi { 7 | static async delete(configMap: IStringKeyMap): Promise { 8 | try { 9 | return await removeFileFromHuaweiInMain(getRawData(configMap)) 10 | } catch (error: any) { 11 | deleteFailedLog(configMap.fileName, 'HuaweiCloud', error) 12 | return false 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/setting/index.ts: -------------------------------------------------------------------------------- 1 | import { RPCRouter } from '~/events/rpc/router' 2 | import advancedRoutes from '~/events/rpc/routes/setting/advanced' 3 | import configureRoutes from '~/events/rpc/routes/setting/configure' 4 | import mainAppRoutes from '~/events/rpc/routes/setting/mainApp' 5 | import shortKeyRoutes from '~/events/rpc/routes/setting/shortKey' 6 | 7 | const settingRouter = new RPCRouter() 8 | 9 | const settingRoutes = [...advancedRoutes, ...configureRoutes, ...mainAppRoutes, ...shortKeyRoutes] 10 | 11 | settingRouter.addBatch(settingRoutes) 12 | 13 | export { settingRouter } 14 | -------------------------------------------------------------------------------- /src/renderer/utils/dataSender.ts: -------------------------------------------------------------------------------- 1 | import { getRawData } from '@/utils/common' 2 | import { IRPCActionType } from '@/utils/enum' 3 | import type { IObj } from '#/types/types' 4 | 5 | export function saveConfig(config: IObj | string, value?: any) { 6 | const configObject = typeof config === 'string' ? { [config]: value } : config 7 | window.electron.sendRPC(IRPCActionType.PICLIST_SAVE_CONFIG, getRawData(configObject)) 8 | } 9 | 10 | export async function getConfig(key?: string): Promise { 11 | return await window.electron.triggerRPC(IRPCActionType.PICLIST_GET_CONFIG, key) 12 | } 13 | -------------------------------------------------------------------------------- /src/main/apis/delete/sftpplist.ts: -------------------------------------------------------------------------------- 1 | import type { IStringKeyMap } from '#/types/types' 2 | import { getRawData } from '~/utils/common' 3 | import { removeFileFromSFTPInMain } from '~/utils/deleteFunc' 4 | import { deleteFailedLog } from '~/utils/deleteLog' 5 | 6 | export default class SftpPlistApi { 7 | static async delete(configMap: IStringKeyMap): Promise { 8 | const { fileName, config } = configMap 9 | try { 10 | return await removeFileFromSFTPInMain(getRawData(config), fileName) 11 | } catch (error: any) { 12 | deleteFailedLog(fileName, 'SFTP', error) 13 | return false 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/setting/advanced.ts: -------------------------------------------------------------------------------- 1 | import server from '~/server' 2 | import webServer from '~/server/webServer' 3 | import { IRPCActionType } from '~/utils/enum' 4 | 5 | export default [ 6 | { 7 | action: IRPCActionType.ADVANCED_UPDATE_SERVER, 8 | handler: async () => { 9 | server.restart() 10 | } 11 | }, 12 | { 13 | action: IRPCActionType.ADVANCED_STOP_WEB_SERVER, 14 | handler: async () => { 15 | webServer.stop() 16 | } 17 | }, 18 | { 19 | action: IRPCActionType.ADVANCED_RESTART_WEB_SERVER, 20 | handler: async () => { 21 | webServer.restart() 22 | } 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /src/main/utils/static.ts: -------------------------------------------------------------------------------- 1 | export const CLIPBOARD_IMAGE_FOLDER = 'piclist-clipboard-images' 2 | 3 | export const cancelDownloadLoadingFileList = 'cancelDownloadLoadingFileList' 4 | export const refreshDownloadFileTransferList = 'refreshDownloadFileTransferList' 5 | 6 | export const picBedsCanbeDeleted = [ 7 | 'aliyun', 8 | 'alist', 9 | 'alistplist', 10 | 'aws-s3', 11 | 'aws-s3-plist', 12 | 'dogecloud', 13 | 'github', 14 | 'huaweicloud-uploader', 15 | 'imgur', 16 | 'local', 17 | 'lskyplist', 18 | 'piclist', 19 | 'qiniu', 20 | 'sftpplist', 21 | 'smms', 22 | 'tcyun', 23 | 'upyun', 24 | 'webdavplist' 25 | ] 26 | -------------------------------------------------------------------------------- /src/renderer/utils/global.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | import { IRPCActionType } from '@/utils/enum' 4 | import type { IPicBedType } from '#/types/types' 5 | 6 | const osGlobal = ref(window.electron.platform) 7 | 8 | const picBedGlobal = ref([]) 9 | const pageReloadCount = ref(0) 10 | 11 | async function updatePicBedGlobal() { 12 | picBedGlobal.value = (await window.electron.triggerRPC(IRPCActionType.MAIN_GET_PICBED))! 13 | } 14 | 15 | async function updatePageReloadCount() { 16 | pageReloadCount.value++ 17 | } 18 | 19 | export { osGlobal, pageReloadCount, picBedGlobal, updatePageReloadCount, updatePicBedGlobal } 20 | -------------------------------------------------------------------------------- /currentVersion_en.md: -------------------------------------------------------------------------------- 1 | ## 🎉 [v3.0.5] Release Notes 2 | 3 | ### ✨ New Features 4 | 5 | - Optimized album preview zooming and positioning 6 | - Multi-image upload: ≤3 individual notifications, >3 aggregated notifications 7 | - Added webPath parameter setting for GitHub image hosting 8 | 9 | ### 🐛 Bug Fixes 10 | 11 | - Fixed album scrolling overflow/refresh issues 12 | - Fixed issue where shortcuts could not be disabled 13 | - Fixed issue where downloads failed when files or paths contained special Chinese characters 14 | - Fixed issue where completed/failed panels in the Manage-Download page displayed incorrectly 15 | - Prevented application icon from being dragged or selected 16 | -------------------------------------------------------------------------------- /src/main/apis/core/bus/constants.ts: -------------------------------------------------------------------------------- 1 | export const CREATE_APP_MENU = 'CREATE_APP_MENU' 2 | export const GET_WINDOW_ID = 'GET_WINDOW_ID' // get a current window 3 | export const GET_WINDOW_ID_REPONSE = 'GET_WINDOW_ID_REPONSE' 4 | export const GET_SETTING_WINDOW_ID = 'GET_SETTING_WINDOW_ID' // get setting window 5 | export const GET_SETTING_WINDOW_ID_RESPONSE = 'GET_SETTING_WINDOW_ID_RESPONSE' 6 | export const UPLOAD_WITH_FILES = 'UPLOAD_WITH_FILES' 7 | export const UPLOAD_WITH_FILES_RESPONSE = 'UPLOAD_WITH_FILES_RESPONSE' 8 | export const UPLOAD_WITH_CLIPBOARD_FILES = 'UPLOAD_WITH_CLIPBOARD_FILES' 9 | export const UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE = 'UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE' 10 | -------------------------------------------------------------------------------- /src/main/apis/delete/local.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra' 2 | 3 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 4 | 5 | interface IConfigMap { 6 | hash: string 7 | } 8 | 9 | export default class LocalApi { 10 | static async delete(configMap: IConfigMap): Promise { 11 | const { hash } = configMap 12 | if (!hash) { 13 | deleteLog(hash, 'Local', false, 'Local.delete: invalid params') 14 | return false 15 | } 16 | 17 | try { 18 | await fs.remove(hash) 19 | deleteLog(hash, 'Local') 20 | return true 21 | } catch (error: any) { 22 | deleteFailedLog(hash, 'Local', error) 23 | return false 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/renderer/manage/utils/dataSender.ts: -------------------------------------------------------------------------------- 1 | import { IRPCActionType } from '@/utils/enum' 2 | import type { IObj } from '#/types/types' 3 | 4 | export function saveConfig(config: IObj | string, value?: any) { 5 | const configObj = typeof config === 'string' ? { [config]: value } : config 6 | window.electron.sendRPC(IRPCActionType.MANAGE_SAVE_CONFIG, configObj) 7 | } 8 | 9 | export async function getConfig(key?: string): Promise { 10 | return await window.electron.triggerRPC(IRPCActionType.MANAGE_GET_CONFIG, key) 11 | } 12 | 13 | export function removeConfig(key: string, propName: string) { 14 | window.electron.sendRPC(IRPCActionType.MANAGE_REMOVE_CONFIG, key, propName) 15 | } 16 | -------------------------------------------------------------------------------- /resources/wsl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # grab the paths 3 | scriptPath=$(echo $0 | awk '{ print substr( $0, 1, length($0)-6 ) }')"windows10.ps1" 4 | imagePath=$(echo $1 | awk '{ print substr( $0, 1, length($0)-18 ) }') 5 | imageName=$(echo $1 | awk '{ print substr( $0, length($0)-17, length($0) ) }') 6 | 7 | # run the powershell script 8 | res=$(powershell.exe -noprofile -noninteractive -nologo -sta -executionpolicy unrestricted -file $(wslpath -w $scriptPath) $(wslpath -w $imagePath)"\\"$imageName) 9 | 10 | # note that there is a return symbol in powershell result 11 | noImage=$(echo "no image\r") 12 | 13 | # check whether image exists 14 | if [ "$res" = "$noImage" ] ;then 15 | echo "no image" 16 | else 17 | echo $(wslpath -u $res) 18 | fi 19 | -------------------------------------------------------------------------------- /src/renderer/utils/constant.ts: -------------------------------------------------------------------------------- 1 | export const SHOW_INPUT_BOX = 'SHOW_INPUT_BOX' 2 | export const SHOW_INPUT_BOX_RESPONSE = 'SHOW_INPUT_BOX_RESPONSE' 3 | // picgo plugin 4 | export const PICGO_CONFIG_PLUGIN = 'PICGO_CONFIG_PLUGIN' 5 | export const PICGO_HANDLE_PLUGIN_ING = 'PICGO_HANDLE_PLUGIN_ING' 6 | export const PICGO_HANDLE_PLUGIN_DONE = 'PICGO_HANDLE_PLUGIN_DONE' 7 | export const PICGO_TOGGLE_PLUGIN = 'PICGO_TOGGLE_PLUGIN' 8 | // picgo uploader 9 | export const RENAME_FILE_NAME = 'RENAME_FILE_NAME' 10 | export const GET_RENAME_FILE_NAME = 'GET_RENAME_FILE_NAME' 11 | export const SHOW_MAIN_PAGE_QRCODE = 'SHOW_MAIN_PAGE_QRCODE' 12 | // rpc 13 | export const RPC_ACTIONS = 'RPC_ACTIONS' 14 | export const RPC_ACTIONS_INVOKE = 'RPC_ACTIONS_INVOKE' 15 | -------------------------------------------------------------------------------- /src/renderer/hooks/useATagClick.ts: -------------------------------------------------------------------------------- 1 | import { onMounted, onUnmounted } from 'vue' 2 | 3 | import { IRPCActionType } from '@/utils/enum' 4 | 5 | export function useATagClick() { 6 | const handleATagClick = (e: MouseEvent) => { 7 | if (e.target instanceof HTMLAnchorElement) { 8 | if (e.target.href) { 9 | if (!e.target.href.startsWith('http://localhost:3000')) { 10 | e.preventDefault() 11 | window.electron.sendRPC(IRPCActionType.OPEN_URL, e.target.href) 12 | } 13 | } 14 | } 15 | } 16 | 17 | onMounted(() => { 18 | document.addEventListener('click', handleATagClick) 19 | }) 20 | 21 | onUnmounted(() => { 22 | document.removeEventListener('click', handleATagClick) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/main/manage/apis/api.ts: -------------------------------------------------------------------------------- 1 | import AliyunApi from '~/manage/apis/aliyun' 2 | import GithubApi from '~/manage/apis/github' 3 | import ImgurApi from '~/manage/apis/imgur' 4 | import LocalApi from '~/manage/apis/local' 5 | import QiniuApi from '~/manage/apis/qiniu' 6 | import S3plistApi from '~/manage/apis/s3plist' 7 | import SftpApi from '~/manage/apis/sftp' 8 | import SmmsApi from '~/manage/apis/smms' 9 | import TcyunApi from '~/manage/apis/tcyun' 10 | import UpyunApi from '~/manage/apis/upyun' 11 | import WebdavplistApi from '~/manage/apis/webdavplist' 12 | 13 | export default { 14 | AliyunApi, 15 | GithubApi, 16 | ImgurApi, 17 | LocalApi, 18 | QiniuApi, 19 | S3plistApi, 20 | SftpApi, 21 | SmmsApi, 22 | TcyunApi, 23 | UpyunApi, 24 | WebdavplistApi 25 | } 26 | -------------------------------------------------------------------------------- /src/renderer/utils/key-binding.ts: -------------------------------------------------------------------------------- 1 | const isSpecialKey = (key: string) => { 2 | const keyArr = ['Shift', 'Control', 'Alt', 'Meta'] 3 | 4 | return keyArr.includes(key) 5 | } 6 | 7 | const keyBinding = (event: KeyboardEvent) => { 8 | const meta = window.electron.platform === 'darwin' ? 'Cmd' : 'Super' 9 | const specialKey = { 10 | Ctrl: event.ctrlKey, 11 | Shift: event.shiftKey, 12 | Alt: event.altKey, 13 | [meta]: event.metaKey 14 | } 15 | 16 | const pressKey = [] 17 | 18 | for (const i in specialKey) { 19 | if (specialKey[i]) { 20 | pressKey.push(i) 21 | } 22 | } 23 | 24 | if (!isSpecialKey(event.key)) { 25 | pressKey.push(event.key.toUpperCase()) 26 | } 27 | return pressKey 28 | } 29 | 30 | export default keyBinding 31 | -------------------------------------------------------------------------------- /src/renderer/manage/utils/textfile.ts: -------------------------------------------------------------------------------- 1 | export const textFileExt = [ 2 | '.applescript', 3 | '.bat', 4 | '.c', 5 | '.cmd', 6 | '.condarc', 7 | '.conf', 8 | '.config', 9 | '.cpp', 10 | '.css', 11 | '.csv', 12 | '.dart', 13 | '.eslintignore', 14 | '.gitattributes', 15 | '.gitconfig', 16 | '.gitignore', 17 | '.gitkeep', 18 | '.gitmodules', 19 | '.go', 20 | '.h', 21 | '.hpp', 22 | '.htm', 23 | '.html', 24 | '.ini', 25 | '.java', 26 | '.js', 27 | '.json', 28 | '.jsx', 29 | '.lock', 30 | '.log', 31 | '.php', 32 | '.prop', 33 | '.properties', 34 | '.ps1', 35 | '.py', 36 | '.rc', 37 | '.sh', 38 | '.ts', 39 | '.tsv', 40 | '.tsx', 41 | '.txt', 42 | '.vue', 43 | '.xml', 44 | '.yaml', 45 | '.yml', 46 | '.yarnrc', 47 | 'license' 48 | ] 49 | -------------------------------------------------------------------------------- /src/universal/types/extra-vue.ts: -------------------------------------------------------------------------------- 1 | interface IFilter { 2 | orderBy?: 'asc' | 'desc' 3 | limit?: number 4 | offset?: number 5 | } 6 | interface IGetResult { 7 | total: number 8 | data: IResult[] 9 | } 10 | 11 | interface IObject { 12 | id?: string 13 | [propName: string]: any 14 | } 15 | 16 | type IResult = T & { 17 | id: string 18 | createdAt: number 19 | updatedAt: number 20 | } 21 | 22 | export interface IGalleryDB { 23 | get(filter?: IFilter): Promise | undefined> 24 | insert(value: T): Promise | undefined> 25 | insertMany(value: T[]): Promise[] | undefined> 26 | updateById(id: string, value: IObject): Promise 27 | getById(id: string): Promise | undefined> 28 | removeById(id: string): Promise 29 | } 30 | -------------------------------------------------------------------------------- /src/main/events/constant.ts: -------------------------------------------------------------------------------- 1 | export const SHOW_INPUT_BOX = 'SHOW_INPUT_BOX' 2 | export const SHOW_INPUT_BOX_RESPONSE = 'SHOW_INPUT_BOX_RESPONSE' 3 | export const TOGGLE_SHORTKEY_MODIFIED_MODE = 'TOGGLE_SHORTKEY_MODIFIED_MODE' 4 | // picgo plugin 5 | export const PICGO_CONFIG_PLUGIN = 'PICGO_CONFIG_PLUGIN' 6 | export const PICGO_HANDLE_PLUGIN_ING = 'PICGO_HANDLE_PLUGIN_ING' 7 | export const PICGO_HANDLE_PLUGIN_DONE = 'PICGO_HANDLE_PLUGIN_DONE' 8 | export const PICGO_TOGGLE_PLUGIN = 'PICGO_TOGGLE_PLUGIN' 9 | // picgo uploader 10 | export const RENAME_FILE_NAME = 'RENAME_FILE_NAME' 11 | export const GET_RENAME_FILE_NAME = 'GET_RENAME_FILE_NAME' 12 | export const SHOW_MAIN_PAGE_QRCODE = 'SHOW_MAIN_PAGE_QRCODE' 13 | // rpc 14 | export const RPC_ACTIONS = 'RPC_ACTIONS' 15 | export const RPC_ACTIONS_INVOKE = 'RPC_ACTIONS_INVOKE' 16 | -------------------------------------------------------------------------------- /.winget/manifests/k/Kuingsmile/PicList/3.0.6/Kuingsmile.PicList.installer.yaml: -------------------------------------------------------------------------------- 1 | # Created using wingetcreate 1.9.14.0 2 | # yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json 3 | 4 | PackageIdentifier: Kuingsmile.PicList 5 | PackageVersion: 3.0.6 6 | InstallerType: nullsoft 7 | Installers: 8 | - Architecture: x64 9 | InstallerUrl: https://github.com/Kuingsmile/PicList/releases/download/v3.0.6/PicList-Setup-3.0.6.exe 10 | InstallerSha256: 9D7CD50FF433384C2C2E9B023301EE334BA803EF5F463C6EFB23F4D344A9FFB3 11 | - Architecture: arm64 12 | InstallerUrl: https://github.com/Kuingsmile/PicList/releases/download/v3.0.6/PicList-Setup-3.0.6-arm64.exe 13 | InstallerSha256: EE96AC520A6F1682581C28388195AAE6EFBFFB3896D20D013A620E9431836F6E 14 | ManifestType: installer 15 | ManifestVersion: 1.9.0 16 | -------------------------------------------------------------------------------- /resources/windows.ps1: -------------------------------------------------------------------------------- 1 | 2 | param($imagePath) 3 | 4 | # Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1 5 | 6 | Add-Type -Assembly PresentationCore 7 | $img = [Windows.Clipboard]::GetImage() 8 | 9 | if ($img -eq $null) { 10 | "no image" 11 | Exit 1 12 | } 13 | 14 | if (-not $imagePath) { 15 | "no image" 16 | Exit 1 17 | } 18 | 19 | $fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0) 20 | $stream = [IO.File]::Open($imagePath, "OpenOrCreate") 21 | $encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder 22 | $encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null 23 | $encoder.Save($stream) | out-null 24 | $stream.Dispose() | out-null 25 | 26 | $imagePath 27 | -------------------------------------------------------------------------------- /src/main/events/rpc/router.ts: -------------------------------------------------------------------------------- 1 | import type { IRPCHandler, IRPCRouter, IRPCRoutes } from '#/types/rpc' 2 | import { IRPCType } from '~/utils/enum' 3 | 4 | interface IBatchAddParams { 5 | action: string 6 | handler: IRPCHandler 7 | type?: string 8 | } 9 | 10 | export class RPCRouter implements IRPCRouter { 11 | private routeMap: IRPCRoutes = new Map() 12 | add = (action: string, handler: IRPCHandler, type: string = IRPCType.SEND): this => { 13 | this.routeMap.set(action, { handler, type }) 14 | return this 15 | } 16 | 17 | addBatch = (params: IBatchAddParams[]): this => { 18 | for (const { action, handler, type = IRPCType.SEND } of params) { 19 | this.routeMap.set(action, { handler, type }) 20 | } 21 | return this 22 | } 23 | 24 | routes() { 25 | return this.routeMap 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/apis/app/shortKey/shortKeyService.ts: -------------------------------------------------------------------------------- 1 | import logger from '@core/picgo/logger' 2 | 3 | import type { IShortKeyHandler } from '#/types/types' 4 | 5 | class ShortKeyService { 6 | private commandList: Map = new Map() 7 | registerCommand(command: string, handler: IShortKeyHandler) { 8 | this.commandList.set(command, handler) 9 | } 10 | 11 | unregisterCommand(command: string) { 12 | this.commandList.delete(command) 13 | } 14 | 15 | getShortKeyHandler(command: string): IShortKeyHandler | null { 16 | const handler = this.commandList.get(command) 17 | if (handler) return handler 18 | logger.warn(`cannot find command: ${command}`) 19 | return null 20 | } 21 | 22 | getCommandList() { 23 | return [...this.commandList.keys()] 24 | } 25 | } 26 | 27 | export default new ShortKeyService() 28 | -------------------------------------------------------------------------------- /.winget/manifests/k/Kuingsmile/PicList/3.0.6/Kuingsmile.PicList.locale.en-US.yaml: -------------------------------------------------------------------------------- 1 | # Created using wingetcreate 1.9.14.0 2 | # yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json 3 | 4 | PackageIdentifier: Kuingsmile.PicList 5 | PackageVersion: 3.0.6 6 | PackageLocale: en-US 7 | Publisher: Kuingsmile 8 | PublisherUrl: https://github.com/Kuingsmile 9 | PublisherSupportUrl: https://github.com/Kuingsmile/PicList/issues 10 | Author: Kuingsmile 11 | PackageName: PicList 12 | PackageUrl: https://github.com/Kuingsmile/piclist 13 | License: MIT License 14 | LicenseUrl: https://github.com/Kuingsmile/PicList/blob/dev/LICENSE 15 | Copyright: Copyright © 2025 Kuingsmile 16 | ShortDescription: A powerful cloud storage management tool. 17 | Tags: 18 | - piclist 19 | - picgo 20 | ManifestType: defaultLocale 21 | ManifestVersion: 1.9.0 22 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "electron-debug", 8 | "type": "process", 9 | "command": "./node_modules/.bin/vue-cli-service", 10 | "windows": { 11 | "command": "./node_modules/.bin/vue-cli-service.cmd" 12 | }, 13 | "isBackground": true, 14 | "args": ["electron:serve", "--debug"], 15 | "problemMatcher": { 16 | "owner": "custom", 17 | "pattern": { 18 | "regexp": "" 19 | }, 20 | "background": { 21 | "beginsPattern": "Starting development server\\.\\.\\.", 22 | "endsPattern": "Not launching electron as debug argument was passed\\." 23 | } 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /src/renderer/utils/drag.ts: -------------------------------------------------------------------------------- 1 | import { onBeforeUnmount, onMounted } from 'vue' 2 | 3 | function disableDrag(e: DragEvent) { 4 | const dropzone = document.getElementById('upload-area') 5 | if (dropzone === null || !dropzone.contains(e.target as Node)) { 6 | e.preventDefault() 7 | e.dataTransfer!.effectAllowed = 'none' 8 | e.dataTransfer!.dropEffect = 'none' 9 | } 10 | } 11 | 12 | export function useDragEventListeners() { 13 | onMounted(() => { 14 | window.addEventListener('dragenter', disableDrag, false) 15 | window.addEventListener('dragover', disableDrag) 16 | window.addEventListener('drop', disableDrag) 17 | }) 18 | onBeforeUnmount(() => { 19 | window.removeEventListener('dragenter', disableDrag, false) 20 | window.removeEventListener('dragover', disableDrag) 21 | window.removeEventListener('drop', disableDrag) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/plugin/index.ts: -------------------------------------------------------------------------------- 1 | import { RPCRouter } from '~/events/rpc/router' 2 | import { 3 | pluginGetListFunc, 4 | pluginImportLocalFunc, 5 | pluginInstallFunc, 6 | pluginUpdateAllFunc 7 | } from '~/events/rpc/routes/plugin/utils' 8 | import { IRPCActionType } from '~/utils/enum' 9 | 10 | const pluginRouter = new RPCRouter() 11 | 12 | const pluginRoutes = [ 13 | { 14 | action: IRPCActionType.PLUGIN_GET_LIST, 15 | handler: pluginGetListFunc 16 | }, 17 | { 18 | action: IRPCActionType.PLUGIN_INSTALL, 19 | handler: pluginInstallFunc 20 | }, 21 | { 22 | action: IRPCActionType.PLUGIN_IMPORT_LOCAL, 23 | handler: pluginImportLocalFunc 24 | }, 25 | { 26 | action: IRPCActionType.PLUGIN_UPDATE_ALL, 27 | handler: pluginUpdateAllFunc 28 | } 29 | ] 30 | 31 | pluginRouter.addBatch(pluginRoutes) 32 | 33 | export { pluginRouter } 34 | -------------------------------------------------------------------------------- /src/main/apis/core/bus/index.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'node:events' 2 | 3 | class OptimizedBus extends EventEmitter { 4 | constructor() { 5 | super() 6 | this.setMaxListeners(50) 7 | } 8 | 9 | once(event: string | symbol, listener: (...args: any[]) => void): this { 10 | const wrappedListener = (...args: any[]) => { 11 | try { 12 | listener(...args) 13 | } finally { 14 | this.removeListener(event, wrappedListener) 15 | } 16 | } 17 | return super.once(event, wrappedListener) 18 | } 19 | 20 | cleanupListeners() { 21 | const events = this.eventNames() 22 | events.forEach(event => { 23 | const listenerCount = this.listenerCount(event) 24 | console.log(` listener count (${listenerCount}) for event: ${String(event)}`) 25 | }) 26 | } 27 | } 28 | 29 | const bus = new OptimizedBus() 30 | 31 | export default bus 32 | -------------------------------------------------------------------------------- /src/renderer/router/config.ts: -------------------------------------------------------------------------------- 1 | export const DocumentPage = 'DocumentPage' 2 | export const GALLERY_PAGE = 'GalleryPage' 3 | export const MANAGE_BUCKET_PAGE = 'ManageBucketPage' 4 | export const MANAGE_EMPTY_PAGE = 'ManageEmptyPage' 5 | export const MANAGE_LOGIN_PAGE = 'ManageLoginPage' 6 | export const MANAGE_MAIN_PAGE = 'ManageMainPage' 7 | export const MANAGE_SETTING_PAGE = 'ManageSettingPage' 8 | export const MAIN_PAGE = 'MainPage' 9 | export const MINI_PAGE = 'MiniPage' 10 | export const PICBEDS_PAGE = 'PicbedsPage' 11 | export const PLUGIN_PAGE = 'PluginPage' 12 | export const RENAME_PAGE = 'RenamePage' 13 | export const SETTING_PAGE = 'SettingPage' 14 | export const SHORTKEY_PAGE = 'ShortkeyPage' 15 | export const TOOLBOX_CONFIG_PAGE = 'ToolBoxPage' 16 | export const TRAY_PAGE = 'TrayPage' 17 | export const UPLOAD_PAGE = 'UploadPage' 18 | export const UPLOADER_CONFIG_PAGE = 'UploaderConfigPage' 19 | -------------------------------------------------------------------------------- /scripts/gen-i18n-types.js: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from 'node:fs' 2 | import { dirname, join } from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | 5 | import { load } from 'js-yaml' 6 | const languageFileName = 'zh-CN.yml' 7 | 8 | const __filename = fileURLToPath(import.meta.url) 9 | const __dirname = dirname(__filename) 10 | 11 | const i18nFolder = join(__dirname, '../resources/i18n') 12 | const typeFolder = join(__dirname, '../src/universal/types') 13 | const languageFile = join(i18nFolder, languageFileName) 14 | 15 | const langFile = readFileSync(languageFile, 'utf8') 16 | 17 | const obj = load(langFile) 18 | 19 | const keys = Object.keys(obj) 20 | 21 | const types = `export interface ILocales { 22 | ${keys.map(key => `${key}: string`).join('\n ')} 23 | } 24 | export type ILocalesKey = keyof ILocales 25 | ` 26 | 27 | writeFileSync(join(typeFolder, 'i18n.ts'), types) 28 | -------------------------------------------------------------------------------- /src/main/apis/core/picgo/index.ts: -------------------------------------------------------------------------------- 1 | import db from '@core/datastore' 2 | import { dbChecker, dbPathChecker } from '@core/datastore/dbChecker' 3 | import { debounce } from 'lodash-es' 4 | import { PicGo } from 'piclist' 5 | import pkg from 'root/package.json' 6 | 7 | import type { IStringKeyMap } from '#/types/types' 8 | 9 | const CONFIG_PATH = dbPathChecker() 10 | 11 | dbChecker() 12 | 13 | const picgo = await PicGo.create(CONFIG_PATH) 14 | 15 | picgo.saveConfig({ 16 | debug: true, 17 | PICGO_ENV: 'GUI' 18 | }) 19 | 20 | picgo.GUI_VERSION = pkg.version 21 | 22 | const originPicGoSaveConfig = picgo.saveConfig.bind(picgo) 23 | 24 | function flushDB() { 25 | db.read(true) 26 | } 27 | 28 | const debounced = debounce(flushDB, 1000) 29 | 30 | picgo.saveConfig = (config: IStringKeyMap) => { 31 | originPicGoSaveConfig(config) 32 | // flush electron's db 33 | debounced() 34 | } 35 | 36 | export default picgo 37 | -------------------------------------------------------------------------------- /src/main/utils/getPicBeds.ts: -------------------------------------------------------------------------------- 1 | import picgo from '@core/picgo' 2 | 3 | import type { IPicBedType } from '#/types/types' 4 | import { configPaths } from '~/utils/configPaths' 5 | 6 | const getPicBeds = () => { 7 | const picBedTypes = picgo.helper.uploader.getIdList() 8 | const picBedFromDB = picgo.getConfig(configPaths.picBed.list) || [] 9 | const picBeds = picBedTypes 10 | .map((item: string) => { 11 | const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined 12 | return { 13 | type: item, 14 | name: picgo.helper.uploader.get(item)!.name || item, 15 | visible: visible ? visible.visible : true 16 | } 17 | }) 18 | .sort(a => { 19 | if (a.type === 'tcyun') { 20 | return -1 21 | } 22 | return 0 23 | }) as IPicBedType[] 24 | return picBeds 25 | } 26 | 27 | export default getPicBeds 28 | -------------------------------------------------------------------------------- /src/universal/types/electron.ts: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/questions/45420448/how-to-import-external-type-into-global-d-ts-file 2 | import type { BrowserWindow, IpcRendererEvent } from 'electron' 3 | 4 | import type { IBrowserWindowOptions } from '#/types/types' 5 | 6 | export interface IWindowListItem { 7 | isValid: boolean 8 | multiple: boolean 9 | options: () => IBrowserWindowOptions 10 | callback: (window: BrowserWindow, windowManager: IWindowManager) => void 11 | } 12 | 13 | export interface IWindowManager { 14 | create: (name: string) => BrowserWindow | null 15 | get: (name: string) => BrowserWindow | null 16 | has: (name: string) => boolean 17 | // delete: (name: IWindowList) => void 18 | deleteById: (id: number) => void 19 | getAvailableWindow: (isSkipMiniWindow?: boolean) => BrowserWindow 20 | } 21 | 22 | export type IpcRendererListener = (event: IpcRendererEvent, ...args: any[]) => void 23 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/manage/config.ts: -------------------------------------------------------------------------------- 1 | import type { IIPCEvent } from '#/types/rpc' 2 | import type { IObj } from '#/types/types' 3 | import getManageApi from '~/manage/Main' 4 | import { IRPCActionType, IRPCType } from '~/utils/enum' 5 | 6 | const manageApi = getManageApi() 7 | 8 | export default [ 9 | { 10 | action: IRPCActionType.MANAGE_GET_CONFIG, 11 | handler: async (_: IIPCEvent, args: [key?: string]) => { 12 | return manageApi.getConfig(args[0]) 13 | }, 14 | type: IRPCType.INVOKE 15 | }, 16 | { 17 | action: IRPCActionType.MANAGE_SAVE_CONFIG, 18 | handler: async (_: IIPCEvent, args: [data: IObj]) => { 19 | manageApi.saveConfig(args[0]) 20 | } 21 | }, 22 | { 23 | action: IRPCActionType.MANAGE_REMOVE_CONFIG, 24 | handler: async (_: IIPCEvent, args: [key: string, propName: string]) => { 25 | manageApi.removeConfig(args[0], args[1]) 26 | } 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /src/renderer/store/index.ts: -------------------------------------------------------------------------------- 1 | import { App, InjectionKey, reactive, readonly, UnwrapRef } from 'vue' 2 | 3 | import { configPaths } from '@/utils/configPaths' 4 | import { saveConfig } from '@/utils/dataSender' 5 | 6 | export interface IState { 7 | defaultPicBed: string 8 | } 9 | 10 | export interface IStore { 11 | state: UnwrapRef 12 | setDefaultPicBed: (type: string) => void 13 | } 14 | 15 | export const storeKey: InjectionKey = Symbol('store') 16 | 17 | // state 18 | const state: IState = reactive({ 19 | defaultPicBed: 'smms' 20 | }) 21 | 22 | // methods 23 | const setDefaultPicBed = (type: string) => { 24 | saveConfig({ 25 | [configPaths.picBed.current]: type, 26 | [configPaths.picBed.uploader]: type 27 | }) 28 | state.defaultPicBed = type 29 | } 30 | 31 | export const store = { 32 | install(app: App) { 33 | app.provide(storeKey, { 34 | state: readonly(state), 35 | setDefaultPicBed 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 贡献指南 2 | 3 | ## 安装与启动 4 | 5 | 最低要求: 6 | 7 | - Node.js >= 20 8 | - Yarn >= 1.22 9 | 10 | 1. 使用 [yarn](https://yarnpkg.com/) 安装依赖 11 | 12 | ```bash 13 | yarn 14 | ``` 15 | 16 | 然后通过 17 | 18 | ```bash 19 | yarn dev 20 | ``` 21 | 22 | 启动项目。 23 | 24 | 1. 只跟 Electron 主进程相关的代码请在 `src/main` 目录下添加。只跟渲染进程相关的代码请在 `src/renderer` 目录下添加。需要暴露到渲染进程的代码请在 `src/preload` 目录下添加。 25 | 2. 所有的全局类型定义请在 `src/universal/types/` 里添加。 26 | 3. 与图床管理功能相关的代码请在 `src/main/manage`和 `src/renderer/manage`目录下添加。 27 | 28 | ## i18n-主进程 29 | 30 | 1. 在 `public/i18n/` 下面创建一种语言的 `yml` 文件,例如 `zh-Hans.yml`。然后参考 `zh-CN.yml` 或者 `en.yml` 编写语言文件。并注意,PicList 会通过语言文件中的 `LANG_DISPLAY_LABEL` 向用户展示该语言的名称。 31 | 2. 在 `src/universal/i18n/index.ts` 里添加一种默认语言。其中 `label` 就是语言文件中 `LANG_DISPLAY_LABEL` 的值,`value` 是语言文件名。 32 | 3. 如果是对已有语言文件进行更新,请在更新完,务必运行一遍 `yarn i18n`,确保能生成正确的语言定义文件。 33 | 34 | ## 提交代码 35 | 36 | 1. 请检查代码没有多余的注释、`console.log` 等调试代码。 37 | 2. 提交代码前,请执行命令 `git add . && yarn cz`,唤起 [代码提交规范工具](https://github.com/Kuingsmile/node-bump-version)。通过该工具提交代码。 38 | -------------------------------------------------------------------------------- /src/renderer/utils/getLatestVersion.ts: -------------------------------------------------------------------------------- 1 | import { RELEASE_URL, RELEASE_URL_BACKUP } from '@/utils/static' 2 | import type { IStringKeyMap } from '#/types/types' 3 | 4 | export const getLatestVersion = async (): Promise => { 5 | try { 6 | const response = await fetch(RELEASE_URL) 7 | if (!response.ok) { 8 | throw new Error(`HTTP error! status: ${response.status}`) 9 | } 10 | const normalList = await response.json() 11 | return normalList[0].name 12 | } catch (err) { 13 | console.error('Error fetching latest version: ', err) 14 | try { 15 | const response = await fetch(`${RELEASE_URL_BACKUP}/latest.yml`) 16 | if (!response.ok) { 17 | throw new Error(`HTTP error! status: ${response.status}`) 18 | } 19 | const data = await response.text() 20 | const r = window.node.yaml.load(data) as IStringKeyMap 21 | return r.version 22 | } catch (err) { 23 | console.error('Error fetching backup latest version: ', err) 24 | return '' 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/renderer/hooks/useConfirm.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export interface ConfirmOptions { 4 | title?: string 5 | message: string 6 | type?: 'info' | 'success' | 'warning' | 'error' 7 | confirmButtonText?: string 8 | cancelButtonText?: string 9 | showClose?: boolean 10 | center?: boolean 11 | } 12 | 13 | interface ConfirmService { 14 | confirm: (options: ConfirmOptions) => Promise 15 | } 16 | 17 | const confirmServiceRef = ref(null) 18 | 19 | export function useConfirm() { 20 | const setConfirmService = (service: ConfirmService) => { 21 | confirmServiceRef.value = service 22 | } 23 | 24 | const confirm = (options: ConfirmOptions): Promise => { 25 | if (confirmServiceRef.value) { 26 | return confirmServiceRef.value.confirm(options) 27 | } 28 | console.warn('Confirm service not initialized') 29 | return Promise.resolve(false) 30 | } 31 | 32 | return { 33 | setConfirmService, 34 | confirm 35 | } 36 | } 37 | 38 | export default useConfirm 39 | -------------------------------------------------------------------------------- /resources/Upload pictures with PicList.workflow/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSServices 6 | 7 | 8 | NSBackgroundColorName 9 | background 10 | NSBackgroundSystemColorName 11 | systemBlueColor 12 | NSIconName 13 | NSTouchBarShare 14 | NSMenuItem 15 | 16 | default 17 | Upload pictures with PicList 18 | 19 | NSMessage 20 | runWorkflowAsService 21 | NSRequiredContext 22 | 23 | NSApplicationIdentifier 24 | com.apple.finder 25 | 26 | NSSendFileTypes 27 | 28 | public.image 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/apis/delete/piclist.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from 'axios' 2 | 3 | import type { IStringKeyMap } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | 6 | export default class PiclistApi { 7 | static async delete(configMap: IStringKeyMap): Promise { 8 | const { config, fullResult } = configMap 9 | const { host, port } = config 10 | if (!fullResult) return true 11 | 12 | if (!host) { 13 | deleteLog(fullResult, 'Piclist', false, 'PiclistApi.delete: invalid params') 14 | return false 15 | } 16 | 17 | const url = `http://${host || '127.0.0.1'}:${port || 36677}/delete` 18 | 19 | try { 20 | const response: AxiosResponse = await axios.post(url, { 21 | list: [fullResult] 22 | }) 23 | const ok = response.status === 200 && response.data?.success 24 | deleteLog(fullResult, 'Piclist', ok) 25 | return ok 26 | } catch (error: any) { 27 | deleteFailedLog(fullResult, 'Piclist', error) 28 | return false 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/apis/delete/aliyun.ts: -------------------------------------------------------------------------------- 1 | import OSS from 'ali-oss' 2 | 3 | import type { IAliYunConfig, PartialKeys } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | 6 | interface IConfigMap { 7 | fileName: string 8 | config: PartialKeys 9 | } 10 | 11 | export default class AliyunApi { 12 | static #getKey(fileName: string, path?: string): string { 13 | return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${fileName}` : fileName 14 | } 15 | 16 | static async delete(configMap: IConfigMap): Promise { 17 | const { fileName, config } = configMap 18 | try { 19 | const client = new OSS({ ...config, region: config.area }) 20 | const key = AliyunApi.#getKey(fileName, config.path) 21 | const result = await client.delete(key) 22 | const ok = result.res.status === 204 23 | deleteLog(fileName, 'Aliyun', ok) 24 | return ok 25 | } catch (error: any) { 26 | deleteFailedLog(fileName, 'Aliyun', error) 27 | return false 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/system/app.ts: -------------------------------------------------------------------------------- 1 | import picgo from '@core/picgo' 2 | import { app, shell } from 'electron' 3 | 4 | import type { IIPCEvent } from '#/types/rpc' 5 | import { i18nManager } from '~/i18n' 6 | import { IRPCActionType } from '~/utils/enum' 7 | 8 | export default [ 9 | { 10 | action: IRPCActionType.RELOAD_APP, 11 | handler: async () => { 12 | app.relaunch() 13 | app.exit(0) 14 | } 15 | }, 16 | { 17 | action: IRPCActionType.OPEN_FILE, 18 | handler: async (_: IIPCEvent, args: [filePath: string]) => { 19 | shell.openPath(args[0]) 20 | } 21 | }, 22 | { 23 | action: IRPCActionType.OPEN_URL, 24 | handler: async (_: IIPCEvent, args: [url: string]) => { 25 | shell.openExternal(args[0]) 26 | } 27 | }, 28 | { 29 | action: IRPCActionType.SET_CURRENT_LANGUAGE, 30 | handler: async (_: IIPCEvent, args: [language: string]) => { 31 | i18nManager.setCurrentLanguage(args[0]) 32 | const { lang } = i18nManager.getCurrentLocales() 33 | picgo.i18n.setLanguage(lang) 34 | } 35 | } 36 | ] 37 | -------------------------------------------------------------------------------- /src/main/utils/handleArgv.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | import fs from 'fs-extra' 4 | import { Logger } from 'piclist' 5 | 6 | import { isUrl } from '~/utils/common' 7 | 8 | interface IResultFileObject { 9 | path: string 10 | } 11 | type Result = IResultFileObject[] 12 | 13 | const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger) => { 14 | const uploadIndex = argv.indexOf('upload') 15 | if (uploadIndex === -1) return [] 16 | const fileList = argv.slice(uploadIndex + 1) 17 | 18 | if (fileList.length === 0) return null // for uploading images in clipboard 19 | 20 | return fileList 21 | .map(item => { 22 | if (isUrl(item) || path.isAbsolute(item)) return { path: item } 23 | 24 | const resolvedPath = path.join(cwd, item) 25 | if (fs.existsSync(resolvedPath)) { 26 | return { path: resolvedPath } 27 | } 28 | logger.warn(`cli -> can't get file: ${resolvedPath}, invalid path`) 29 | return null 30 | }) 31 | .filter(item => item !== null) as Result 32 | } 33 | 34 | export { getUploadFiles } 35 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Main Process", 6 | "type": "node", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", 10 | "windows": { 11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" 12 | }, 13 | "runtimeArgs": ["--sourcemap"], 14 | "env": { 15 | "REMOTE_DEBUGGING_PORT": "9222" 16 | } 17 | }, 18 | { 19 | "name": "Debug Renderer Process", 20 | "port": 9222, 21 | "request": "attach", 22 | "type": "chrome", 23 | "webRoot": "${workspaceFolder}/src/renderer", 24 | "timeout": 60000, 25 | "presentation": { 26 | "hidden": true 27 | } 28 | } 29 | ], 30 | "compounds": [ 31 | { 32 | "name": "Debug All", 33 | "configurations": ["Debug Main Process", "Debug Renderer Process"], 34 | "presentation": { 35 | "order": 1 36 | } 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /src/main/events/rpc/routes/upload/index.ts: -------------------------------------------------------------------------------- 1 | import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis' 2 | 3 | import type { IIPCEvent } from '#/types/rpc' 4 | import type { IFileWithPath } from '#/types/types' 5 | import { RPCRouter } from '~/events/rpc/router' 6 | import { IRPCActionType, IRPCType } from '~/utils/enum' 7 | import getPicBeds from '~/utils/getPicBeds' 8 | 9 | const uploadRouter = new RPCRouter() 10 | 11 | const uploadRoutes = [ 12 | { 13 | action: IRPCActionType.MAIN_GET_PICBED, 14 | handler: async () => { 15 | return getPicBeds() 16 | }, 17 | type: IRPCType.INVOKE 18 | }, 19 | { 20 | action: IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE, 21 | handler: async () => { 22 | uploadClipboardFiles() 23 | } 24 | }, 25 | { 26 | action: IRPCActionType.UPLOAD_CHOOSED_FILES, 27 | handler: async (evt: IIPCEvent, args: [files: IFileWithPath[]]) => { 28 | return uploadChoosedFiles(evt.sender, args[0]) 29 | } 30 | } 31 | ] 32 | 33 | uploadRouter.addBatch(uploadRoutes) 34 | 35 | export { uploadRouter } 36 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[stylus]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": "explicit" 7 | }, 8 | "githubPullRequests.ignoredPullRequestBranches": [ 9 | "dev" 10 | ], 11 | "[json]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode", 13 | "editor.formatOnSave": true 14 | }, 15 | "[html]": { 16 | "editor.defaultFormatter": "esbenp.prettier-vscode", 17 | "editor.formatOnSave": true 18 | }, 19 | "[css]": { 20 | "editor.defaultFormatter": "esbenp.prettier-vscode", 21 | "editor.formatOnSave": true 22 | }, 23 | "i18n-ally.localesPaths": ["src\\renderer\\i18n\\locales", "resources\\i18n"], 24 | "i18n-ally.keystyle": "nested", 25 | "i18n-ally.sortKeys": true, 26 | "i18n-ally.namespace": true, 27 | "i18n-ally.enabledParsers": ["json", "yaml"], 28 | "i18n-ally.sourceLanguage": "en", 29 | "i18n-ally.displayLanguage": "zh-CN", 30 | "i18n-ally.enabledFrameworks": ["vue"], 31 | "i18n-ally.editor.preferEditor": true, 32 | "typescript.tsdk": "node_modules\\typescript\\lib" 33 | } -------------------------------------------------------------------------------- /src/main/apis/delete/upyun.ts: -------------------------------------------------------------------------------- 1 | import Upyun from 'upyun' 2 | 3 | import type { IUpYunConfig, PartialKeys } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | 6 | interface IConfigMap { 7 | fileName: string 8 | config: PartialKeys 9 | } 10 | 11 | export default class UpyunApi { 12 | static async delete(configMap: IConfigMap): Promise { 13 | const { 14 | fileName, 15 | config: { bucket, operator, password, path } 16 | } = configMap 17 | try { 18 | const service = new Upyun.Service(bucket, operator, password) 19 | const client = new Upyun.Client(service) 20 | let key 21 | if (path === '/' || !path) { 22 | key = fileName 23 | } else { 24 | key = `${path.replace(/^\/+|\/+$/, '')}/${fileName}` 25 | } 26 | const result = await client.deleteFile(key) 27 | deleteLog(fileName, 'Upyun', !!result) 28 | return !!result 29 | } catch (error: any) { 30 | deleteFailedLog(fileName, 'Upyun', error) 31 | return false 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /scripts/notarize.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | require('dotenv').config() 4 | 5 | const { notarize } = require('@electron/notarize') 6 | const { ELECTRON_SKIP_NOTARIZATION, XCODE_APP_LOADER_EMAIL, XCODE_APP_LOADER_PASSWORD, XCODE_TEAM_ID } = process.env 7 | 8 | async function main(context) { 9 | const { electronPlatformName, appOutDir } = context 10 | 11 | if ( 12 | electronPlatformName !== 'darwin' || 13 | ELECTRON_SKIP_NOTARIZATION === 'true' || 14 | !XCODE_APP_LOADER_EMAIL || 15 | !XCODE_APP_LOADER_PASSWORD || 16 | !XCODE_TEAM_ID 17 | ) { 18 | console.log('Skipping Apple notarization.') 19 | return 20 | } 21 | 22 | console.log('Starting Apple notarization.') 23 | const appName = context.packager.appInfo.productFilename 24 | await notarize({ 25 | appBundleId: 'com.kuingsmile.piclist', 26 | appPath: `${appOutDir}/${appName}.app`, 27 | appleId: XCODE_APP_LOADER_EMAIL, 28 | appleIdPassword: XCODE_APP_LOADER_PASSWORD, 29 | tool: 'notarytool', 30 | teamId: XCODE_TEAM_ID 31 | }) 32 | console.log('Finished Apple notarization.') 33 | } 34 | 35 | exports.default = main 36 | -------------------------------------------------------------------------------- /src/main/utils/performanceOptimizer.ts: -------------------------------------------------------------------------------- 1 | export class MemoryMonitor { 2 | // eslint-disable-next-line no-undef 3 | private static interval: NodeJS.Timeout | null = null 4 | 5 | static start(intervalMs: number = 30000) { 6 | if (this.interval) return 7 | 8 | this.interval = setInterval(() => { 9 | const memUsage = process.memoryUsage() 10 | const mbUsage = { 11 | rss: Math.round(memUsage.rss / 1024 / 1024), 12 | heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024), 13 | heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024), 14 | external: Math.round(memUsage.external / 1024 / 1024) 15 | } 16 | console.log( 17 | `[Memory] RSS: ${mbUsage.rss}MB, Heap: ${mbUsage.heapUsed}/${mbUsage.heapTotal}MB, External: ${mbUsage.external}MB` 18 | ) 19 | 20 | if (mbUsage.heapUsed / mbUsage.heapTotal > 0.8 && global.gc) { 21 | console.log('[Memory] Triggering garbage collection') 22 | global.gc() 23 | } 24 | }, intervalMs) 25 | } 26 | 27 | static stop() { 28 | if (this.interval) { 29 | clearInterval(this.interval) 30 | this.interval = null 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/link.js: -------------------------------------------------------------------------------- 1 | import pkg from '../package.json' with { type: 'json' } 2 | const version = pkg.version 3 | // TODO: use the same name format 4 | const generateURL = (platform, ext, prefix = 'PicList-') => { 5 | return `https://release.piclist.cn/latest/${prefix}${version}${platform}${ext}` 6 | } 7 | 8 | const template = ` 9 | ### 加速下载地址 10 | 11 | #### MacOS 12 | [PicList-${version}-arm64.dmg](${generateURL('-arm64', '.dmg', 'PicList-')}) 13 | [PicList-${version}-x64.dmg](${generateURL('-x64', '.dmg', 'PicList-')}) 14 | [PicList-${version}-universal.dmg](${generateURL('-universal', '.dmg', 'PicList-')}) 15 | 16 | #### Windows 17 | [PicList-Setup-${version}-ia32.exe](${generateURL('-ia32', '.exe', 'PicList-Setup-')}) 18 | [PicList-Setup-${version}-x64.exe](${generateURL('-x64', '.exe', 'PicList-Setup-')}) 19 | [PicList-Setup-${version}-arm64.exe](${generateURL('-arm64', '.exe', 'PicList-Setup-')}) 20 | [PicList-Setup-${version}.exe](${generateURL('', '.exe', 'PicList-Setup-')}) 21 | 22 | #### Linux 23 | [PicList-${version}.AppImage](${generateURL('', '.AppImage', 'PicList-')}) 24 | [piclist_${version}_amd64.snap](${generateURL('_amd64', '.snap', 'piclist_')})` 25 | 26 | console.log(template) 27 | -------------------------------------------------------------------------------- /src/main/fileServer/index.ts: -------------------------------------------------------------------------------- 1 | import http from 'node:http' 2 | import path from 'node:path' 3 | 4 | import picgo from '@core/picgo' 5 | import logger from '@core/picgo/logger' 6 | import fs from 'fs-extra' 7 | 8 | export const imgFilePath = path.join(picgo.baseDir, 'imgTemp') 9 | fs.ensureDirSync(imgFilePath) 10 | 11 | const serverPort = 36699 12 | 13 | let server: http.Server 14 | 15 | export function startFileServer() { 16 | server = http.createServer((req, res) => { 17 | const requestPath = req.url?.split('?')[0] 18 | const filePath = path.join(imgFilePath, decodeURIComponent(requestPath as string)) 19 | 20 | fs.readFile(filePath, (err, data) => { 21 | if (err) { 22 | res.writeHead(404) 23 | res.end('404 Not Found') 24 | } else { 25 | res.end(data) 26 | } 27 | }) 28 | }) 29 | 30 | server 31 | .listen(serverPort, () => { 32 | logger.info(`File server is running, http://localhost:${serverPort}`) 33 | }) 34 | .on('error', err => { 35 | logger.error(err) 36 | }) 37 | } 38 | 39 | export function stopFileServer() { 40 | server.close(() => { 41 | logger.info('File server is stopped') 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-present, Molunerfinn 4 | Copyright (c) 2019 诗人的咸鱼 5 | Copyright (c) 2023-present, KuingSmile 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/renderer/public/unknown-file-type.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/universal/types/rpc.ts: -------------------------------------------------------------------------------- 1 | import type { IpcMainEvent, IpcMainInvokeEvent } from 'electron' 2 | 3 | export type IGetLatestVersionArgs = [isCheckBetaVersion: boolean] 4 | export type IToolboxCheckArgs = [type: string] 5 | export type IShowDockIconArgs = [visible: boolean] 6 | 7 | export interface IRPCServer { 8 | start: () => void 9 | stop: () => void 10 | use: (routes: IRPCRoutes) => void 11 | } 12 | 13 | export type IRPCRoutes = Map< 14 | string, 15 | { 16 | handler: IRPCHandler 17 | type: string 18 | } 19 | > 20 | 21 | export type IIPCEvent = IpcMainEvent | IpcMainInvokeEvent 22 | 23 | export type IRPCHandler = (event: IIPCEvent, args: any) => Promise 24 | 25 | export interface IRPCRouter { 26 | add(action: string, handler: IRPCHandler, type: string): IRPCRouter 27 | routes: () => IRPCRoutes 28 | } 29 | 30 | export type IToolboxChecker = (event: IpcMainEvent) => Promise 31 | 32 | export type IToolboxCheckerMap = Record 33 | 34 | export type IToolboxFixMap = Record> 35 | 36 | export interface IToolboxCheckRes { 37 | type: string 38 | status: string 39 | msg?: string 40 | value?: any 41 | } 42 | -------------------------------------------------------------------------------- /src/main/apis/delete/smms.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from 'axios' 2 | 3 | import type { ISMMSConfig } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | 6 | interface IConfigMap { 7 | hash?: string 8 | config?: Partial 9 | } 10 | 11 | export default class SmmsApi { 12 | static readonly #baseUrl = 'https://smms.app/api/v2' 13 | 14 | static async delete(configMap: IConfigMap): Promise { 15 | const { hash, config } = configMap 16 | if (!hash || !config || !config.token) { 17 | deleteLog(hash, 'Smms', false, 'SmmsApi.delete: invalid params') 18 | return false 19 | } 20 | 21 | const { token } = config 22 | 23 | try { 24 | const response: AxiosResponse = await axios.get(`${SmmsApi.#baseUrl}/delete/${hash}`, { 25 | headers: { 26 | Authorization: token 27 | }, 28 | params: { 29 | hash, 30 | format: 'json' 31 | }, 32 | timeout: 30000 33 | }) 34 | const ok = response.status === 200 35 | deleteLog(hash, 'Smms', ok) 36 | return ok 37 | } catch (error: any) { 38 | deleteFailedLog(hash, 'Smms', error) 39 | return false 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /resources/linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "$XDG_SESSION_TYPE" = "x11" ]; then 3 | # require xclip(see http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212) 4 | command -v xclip >/dev/null 2>&1 || { echo >&1 "no xclip"; exit 1; } 5 | # write image in clipboard to file (see http://unix.stackexchange.com/questions/145131/copy-image-from-clipboard-to-file) 6 | filePath=`xclip -selection clipboard -o 2>/dev/null | grep ^file:// | cut -c8-` 7 | if [ ! -n "$filePath" ] ;then 8 | if 9 | xclip -selection clipboard -target image/png -o >/dev/null 2>&1 10 | then 11 | xclip -selection clipboard -target image/png -o >$1 2>/dev/null 12 | echo $1 13 | else 14 | echo "no image" 15 | fi 16 | else 17 | echo $filePath 18 | fi 19 | elif [ "$XDG_SESSION_TYPE" = "wayland" ]; then 20 | command -v wl-copy >/dev/null 2>&1 || { echo >&1 "no wl-clipboard"; exit 1; } 21 | filePath=`wl-copy -o 2>/dev/null | grep ^file:// | cut -c8-` 22 | if [ ! -n "$filePath" ] ;then 23 | if 24 | wl-copy -t image/png -o >/dev/null 2>&1 25 | then 26 | wl-copy -t image/png image/png -o >$1 2>/dev/null 27 | echo $1 28 | else 29 | echo "no image" 30 | fi 31 | else 32 | echo $filePath 33 | fi 34 | fi 35 | -------------------------------------------------------------------------------- /src/main/apis/delete/tcyun.ts: -------------------------------------------------------------------------------- 1 | import COS from 'cos-nodejs-sdk-v5' 2 | 3 | import type { ITcYunConfig, PartialKeys } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | interface IConfigMap { 6 | fileName: string 7 | config: PartialKeys 8 | } 9 | export default class TcyunApi { 10 | static #createCOS(SecretId: string, SecretKey: string): COS { 11 | return new COS({ 12 | SecretId, 13 | SecretKey 14 | }) 15 | } 16 | 17 | static async delete(configMap: IConfigMap): Promise { 18 | const { 19 | fileName, 20 | config: { secretId, secretKey, bucket, area, path } 21 | } = configMap 22 | try { 23 | const cos = TcyunApi.#createCOS(secretId, secretKey) 24 | let key 25 | if (path === '/' || !path) { 26 | key = `/${fileName}` 27 | } else { 28 | key = `/${path.replace(/^\/+|\/+$/, '')}/${fileName}` 29 | } 30 | const result = await cos.deleteObject({ 31 | Bucket: bucket, 32 | Region: area, 33 | Key: key 34 | }) 35 | const ok = result.statusCode === 204 36 | deleteLog(fileName, 'Tcyun', ok) 37 | return ok 38 | } catch (error: any) { 39 | deleteFailedLog(fileName, 'Tcyun', error) 40 | return false 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/apis/delete/alist.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | import axios from 'axios' 4 | 5 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 6 | 7 | interface IConfigMap { 8 | fileName: string 9 | config: { 10 | version: string 11 | url: string 12 | uploadPath: string 13 | token: string 14 | } 15 | } 16 | 17 | export default class AlistApi { 18 | static async delete({ fileName, config: { version, url, uploadPath, token } }: IConfigMap): Promise { 19 | try { 20 | if (String(version) === '2') { 21 | deleteLog(fileName, 'Alist', false, 'Alist version 2 is not supported, deletion is skipped') 22 | return true 23 | } 24 | const result = await axios.request({ 25 | method: 'post', 26 | url: `${url}/api/fs/remove`, 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | Authorization: token 30 | }, 31 | data: { 32 | dir: path.join('/', uploadPath, path.dirname(fileName)), 33 | names: [path.basename(fileName)] 34 | } 35 | }) 36 | const ok = result.data.code === 200 37 | deleteLog(fileName, 'Alist', ok) 38 | return ok 39 | } catch (error: any) { 40 | deleteFailedLog(fileName, 'Alist', error) 41 | return false 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/renderer/components/ToolboxHandler.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | 20 | 25 | 26 | 58 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "ESNext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "bundler", 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "experimentalDecorators": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "node" 17 | ], 18 | "typeRoots": [ 19 | "./node_modules/@types", 20 | "./src/universal/types/" 21 | ], 22 | "paths": { 23 | "@/*": [ 24 | "src/renderer/*" 25 | ], 26 | "~/*": [ 27 | "src/main/*" 28 | ], 29 | "root/*": [ 30 | "./*" 31 | ], 32 | "#/*": [ 33 | "src/universal/*" 34 | ], 35 | "apis/*": [ 36 | "src/main/apis/*" 37 | ], 38 | "@core/*": [ 39 | "src/main/apis/core/*" 40 | ] 41 | }, 42 | "lib": [ 43 | "ESNext", 44 | "dom", 45 | "dom.iterable", 46 | "ScriptHost" 47 | ] 48 | }, 49 | "include": [ 50 | "src/**/*.ts", 51 | "src/**/*.vue", 52 | "src/**/*.d.ts", 53 | "tests/**/*.ts", 54 | "electron.vite.config.*" 55 | ], 56 | "exclude": [ 57 | "node_modules", 58 | "dist", 59 | "out" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /src/main/apis/delete/webdav.ts: -------------------------------------------------------------------------------- 1 | import { AuthType, createClient, WebDAVClientOptions } from 'webdav' 2 | 3 | import type { IWebdavPlistConfig, PartialKeys } from '#/types/types' 4 | import { formatEndpoint } from '~/utils/common' 5 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 6 | 7 | interface IConfigMap { 8 | fileName: string 9 | config: PartialKeys 10 | } 11 | 12 | export default class WebdavApi { 13 | static async delete(configMap: IConfigMap): Promise { 14 | const { 15 | fileName, 16 | config: { host, username, password, path, sslEnabled, authType } 17 | } = configMap 18 | const endpoint = formatEndpoint(host, sslEnabled) 19 | const options: WebDAVClientOptions = { 20 | username, 21 | password 22 | } 23 | if (authType === 'digest') { 24 | options.authType = AuthType.Digest 25 | } 26 | const ctx = createClient(endpoint, options) 27 | let key 28 | if (path === '/' || !path) { 29 | key = fileName 30 | } else { 31 | key = `${path.replace(/^\/+|\/+$/, '')}/${fileName}` 32 | } 33 | try { 34 | await ctx.deleteFile(key) 35 | deleteLog(fileName, 'WebDAV') 36 | return true 37 | } catch (error: any) { 38 | deleteFailedLog(fileName, 'WebDAV', error) 39 | return false 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/server/router.ts: -------------------------------------------------------------------------------- 1 | import type { routeHandler } from '#/types/types' 2 | 3 | type HttpMethod = 'GET' | 'POST' 4 | 5 | class Router { 6 | #router = new Map>() 7 | 8 | #addRoute(method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void { 9 | if (!this.#router.has(url)) { 10 | this.#router.set(url, new Map()) 11 | } 12 | this.#router.get(url)!.set(method, { handler: callback, urlparams }) 13 | } 14 | 15 | get(url: string, callback: routeHandler, urlparams?: URLSearchParams): void { 16 | this.#addRoute('GET', url, callback, urlparams) 17 | } 18 | 19 | post(url: string, callback: routeHandler, urlparams?: URLSearchParams): void { 20 | this.#addRoute('POST', url, callback, urlparams) 21 | } 22 | 23 | any(url: string, callback: routeHandler, urlparams?: URLSearchParams): void { 24 | this.#addRoute('GET', url, callback, urlparams) 25 | this.#addRoute('POST', url, callback, urlparams) 26 | } 27 | 28 | getHandler(url: string, method: HttpMethod) { 29 | if (this.#router.has(url)) { 30 | const methods = this.#router.get(url)! 31 | if (methods.has(method)) { 32 | return methods.get(method) 33 | } 34 | } 35 | return null 36 | } 37 | } 38 | 39 | export default new Router() 40 | -------------------------------------------------------------------------------- /resources/windows10.ps1: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1 2 | param($imagePath) 3 | 4 | # https://github.com/PowerShell/PowerShell/issues/7233 5 | # fix the output encoding bug 6 | [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding 7 | 8 | Add-Type -Assembly PresentationCore 9 | function main { 10 | $img = [Windows.Clipboard]::GetImage() 11 | 12 | if ($img -eq $null) { 13 | "no image" 14 | Exit 1 15 | } 16 | 17 | if (-not $imagePath) { 18 | "no image" 19 | Exit 1 20 | } 21 | 22 | $fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0) 23 | $stream = [IO.File]::Open($imagePath, "OpenOrCreate") 24 | $encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder 25 | $encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null 26 | $encoder.Save($stream) | out-null 27 | $stream.Dispose() | out-null 28 | 29 | $imagePath 30 | Exit 1 31 | } 32 | 33 | try { 34 | # For WIN10 35 | $file = Get-Clipboard -Format FileDropList 36 | if ($file -ne $null) { 37 | Convert-Path $file 38 | Exit 1 39 | } 40 | } catch { 41 | # For WIN7 WIN8 WIN10 42 | main 43 | } 44 | 45 | main -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 40 | 41 | 46 | 47 | 57 | -------------------------------------------------------------------------------- /scripts/config.js: -------------------------------------------------------------------------------- 1 | // different platform has different format 2 | 3 | // macos 4 | const darwin = [ 5 | { 6 | appNameWithPrefix: 'PicList-', 7 | ext: '.dmg', 8 | arch: '-arm64', 9 | 'version-file': 'latest-mac.yml' 10 | }, 11 | { 12 | appNameWithPrefix: 'PicList-', 13 | ext: '.dmg', 14 | arch: '-x64', 15 | 'version-file': 'latest-mac.yml' 16 | } 17 | ] 18 | 19 | const linux = [ 20 | { 21 | appNameWithPrefix: 'PicList-', 22 | ext: '.AppImage', 23 | arch: '', 24 | 'version-file': 'latest-linux.yml' 25 | }, 26 | { 27 | appNameWithPrefix: 'piclist_', 28 | ext: '.snap', 29 | arch: '_amd64', 30 | 'version-file': 'latest-linux.yml' 31 | } 32 | ] 33 | 34 | // windows 35 | const win32 = [ 36 | { 37 | appNameWithPrefix: 'PicList-Setup-', 38 | ext: '.exe', 39 | arch: '-ia32', 40 | 'version-file': 'latest.yml' 41 | }, 42 | { 43 | appNameWithPrefix: 'PicList-Setup-', 44 | ext: '.exe', 45 | arch: '-x64', 46 | 'version-file': 'latest.yml' 47 | }, 48 | { 49 | appNameWithPrefix: 'PicList-Setup-', 50 | ext: '.exe', 51 | arch: '', // 32 & 64 52 | 'version-file': 'latest.yml' 53 | }, 54 | { 55 | appNameWithPrefix: 'PicList-Setup-', 56 | ext: '.exe', 57 | arch: '-arm64', 58 | 'version-file': 'latest.yml' 59 | } 60 | ] 61 | 62 | export default { 63 | darwin, 64 | linux, 65 | win32 66 | } 67 | -------------------------------------------------------------------------------- /src/main/apis/delete/imgur.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from 'axios' 2 | 3 | import type { IImgurConfig } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | 6 | interface IConfigMap { 7 | config?: Partial 8 | hash?: string 9 | } 10 | 11 | export default class ImgurApi { 12 | static #baseUrl = 'https://api.imgur.com/3' 13 | 14 | static async delete(configMap: IConfigMap): Promise { 15 | const { config: { clientId = '', username = '', accessToken = '' } = {}, hash = '' } = configMap 16 | let Authorization: string, apiUrl: string 17 | 18 | if (username && accessToken) { 19 | Authorization = `Bearer ${accessToken}` 20 | apiUrl = `${ImgurApi.#baseUrl}/account/${username}/image/${hash}` 21 | } else if (clientId) { 22 | Authorization = `Client-ID ${clientId}` 23 | apiUrl = `${ImgurApi.#baseUrl}/image/${hash}` 24 | } else { 25 | deleteLog(hash, 'Imgur', false, 'No credentials found') 26 | return false 27 | } 28 | try { 29 | const response: AxiosResponse = await axios.delete(apiUrl, { 30 | headers: { Authorization }, 31 | timeout: 30000 32 | }) 33 | const ok = response.status === 200 34 | deleteLog(hash, 'Imgur', ok) 35 | return ok 36 | } catch (error: any) { 37 | deleteFailedLog(hash, 'Imgur', error) 38 | return false 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/renderer/components/ToolboxStatusIcon.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 41 | 46 | 57 | -------------------------------------------------------------------------------- /src/renderer/manage/pages/EmptyPage.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 25 | 26 | 62 | -------------------------------------------------------------------------------- /src/main/apis/delete/lskyplist.ts: -------------------------------------------------------------------------------- 1 | import https from 'node:https' 2 | 3 | import axios, { AxiosResponse } from 'axios' 4 | 5 | import type { IStringKeyMap } from '#/types/types' 6 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 7 | 8 | export default class LskyplistApi { 9 | static async delete(configMap: IStringKeyMap): Promise { 10 | const { hash, config } = configMap 11 | if (!hash || !config || !config.token) { 12 | deleteLog(hash, 'Lskyplist', false, 'LskyplistApi.delete: invalid params') 13 | return false 14 | } 15 | 16 | const { host, token, version } = config 17 | if (version !== 'V2') { 18 | deleteLog(hash, 'Lskyplist', false, 'LskyplistApi.delete: invalid version') 19 | return false 20 | } 21 | 22 | const v2Headers = { 23 | Accept: 'application/json', 24 | Authorization: token || undefined 25 | } 26 | 27 | const requestAgent = new https.Agent({ 28 | rejectUnauthorized: false 29 | }) 30 | try { 31 | const response: AxiosResponse = await axios.delete(`${host}/api/v1/images/${hash}`, { 32 | headers: v2Headers, 33 | timeout: 30000, 34 | httpsAgent: requestAgent 35 | }) 36 | const ok = response.status === 200 && response.data.status === true 37 | deleteLog(hash, 'Lskyplist', ok) 38 | return ok 39 | } catch (error: any) { 40 | deleteFailedLog(hash, 'Lskyplist', error) 41 | return false 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/apis/delete/qiniu.ts: -------------------------------------------------------------------------------- 1 | import qiniu from 'qiniu' 2 | 3 | import type { IQiniuConfig, PartialKeys } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | interface IConfigMap { 6 | fileName: string 7 | config: PartialKeys 8 | } 9 | 10 | export default class QiniuApi { 11 | static async delete(configMap: IConfigMap): Promise { 12 | const { 13 | fileName, 14 | config: { accessKey, secretKey, bucket, path } 15 | } = configMap 16 | const mac = new qiniu.auth.digest.Mac(accessKey, secretKey) 17 | const qiniuConfig = new qiniu.conf.Config() 18 | try { 19 | const bucketManager = new qiniu.rs.BucketManager(mac, qiniuConfig) 20 | const formattedPath = path?.replace(/^\/+|\/+$/, '') || '' 21 | const key = path === '/' || !path ? fileName : `${formattedPath}/${fileName}` 22 | const res = (await new Promise((resolve, reject) => { 23 | bucketManager.delete(bucket, key, (err, respBody, respInfo) => { 24 | if (err) { 25 | reject(err) 26 | } else { 27 | resolve({ 28 | respBody, 29 | respInfo 30 | }) 31 | } 32 | }) 33 | })) as any 34 | const ok = res?.respInfo?.statusCode === 200 35 | deleteLog(fileName, 'Qiniu', ok) 36 | return ok 37 | } catch (error: any) { 38 | deleteFailedLog(fileName, 'Qiniu', error) 39 | return false 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/utils/clipboardPoll.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'node:crypto' 2 | import { EventEmitter } from 'node:events' 3 | 4 | import logger from '@core/picgo/logger' 5 | import { clipboard, NativeImage } from 'electron' 6 | 7 | class ClipboardWatcher extends EventEmitter { 8 | // eslint-disable-next-line no-undef 9 | timer: NodeJS.Timeout | null 10 | lastImageHash: string | null 11 | 12 | constructor() { 13 | super() 14 | this.lastImageHash = null 15 | this.timer = null 16 | } 17 | 18 | startListening(watchDelay = 1000) { 19 | this.stopListening(false) 20 | 21 | this.timer = setInterval(() => { 22 | const image = clipboard.readImage() 23 | if (image.isEmpty()) return 24 | 25 | const currentImageHash = this.getImageHash(image) 26 | if (this.lastImageHash === null || this.lastImageHash === currentImageHash) { 27 | this.lastImageHash = currentImageHash 28 | return 29 | } 30 | 31 | this.lastImageHash = currentImageHash 32 | this.emit('change') 33 | }, watchDelay) 34 | logger.info('Start to watch clipboard') 35 | } 36 | 37 | stopListening(isLog = true) { 38 | if (this.timer) { 39 | clearInterval(this.timer) 40 | this.timer = null 41 | this.lastImageHash = null 42 | } 43 | isLog && logger.info('Stop to watch clipboard') 44 | } 45 | 46 | getImageHash(image: NativeImage): string { 47 | const buffer = image.toBitmap() 48 | return crypto.createHash('md5').update(buffer).digest('hex') 49 | } 50 | } 51 | 52 | export default new ClipboardWatcher() 53 | -------------------------------------------------------------------------------- /src/universal/types/view.ts: -------------------------------------------------------------------------------- 1 | export interface ISettingForm { 2 | showUpdateTip: boolean 3 | autoStart: boolean 4 | rename: boolean 5 | autoRename: boolean 6 | uploadNotification: boolean 7 | uploadResultNotification: boolean 8 | miniWindowOntop: boolean 9 | autoCloseMiniWindow: boolean 10 | autoCloseMainWindow: boolean 11 | logLevel: string[] 12 | autoCopy: boolean 13 | useBuiltinClipboard: boolean 14 | logFileSizeLimit: number 15 | deleteCloudFile: boolean 16 | isCustomMiniIcon: boolean 17 | customMiniIcon: string 18 | isHideDock: boolean 19 | autoImport: boolean 20 | autoImportPicBed: string[] 21 | encodeOutputURL: boolean 22 | isAutoListenClipboard: boolean 23 | useShortUrl: boolean 24 | shortUrlServer: string 25 | c1nToken: string 26 | yourlsDomain: string 27 | yourlsSignature: string 28 | cfWorkerHost: string 29 | sinkDomain: string 30 | sinkToken: string 31 | deleteLocalFile: boolean 32 | serverKey: string 33 | aesPassword: string 34 | enableWebServer: boolean 35 | webServerHost: string 36 | webServerPort: number 37 | webServerPath: string 38 | registry: string 39 | proxy: string 40 | mainWindowWidth: number 41 | mainWindowHeight: number 42 | enableSecondUploader: boolean 43 | } 44 | 45 | export interface IToolboxItem { 46 | title: string 47 | status: string 48 | msg?: string 49 | value?: any // for handler 50 | hasNoFixMethod?: boolean 51 | handler?: (value: any) => Promise | void 52 | handlerText?: string 53 | } 54 | 55 | export type IToolboxMap = Record 56 | -------------------------------------------------------------------------------- /src/main/apis/delete/github.ts: -------------------------------------------------------------------------------- 1 | import { Octokit } from '@octokit/rest' 2 | 3 | import type { IGitHubConfig, PartialKeys } from '#/types/types' 4 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 5 | 6 | interface IConfigMap { 7 | fileName: string 8 | hash: string 9 | config: PartialKeys 10 | } 11 | 12 | export default class GithubApi { 13 | static #createOctokit(token: string) { 14 | return new Octokit({ 15 | auth: token 16 | }) 17 | } 18 | 19 | static #createKey(path: string | undefined, fileName: string): string { 20 | const formatedFileName = fileName.replace(/%2F/g, '/') 21 | return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}` : formatedFileName 22 | } 23 | 24 | static async delete(configMap: IConfigMap): Promise { 25 | const { 26 | fileName, 27 | hash, 28 | config: { repo, token, branch, path } 29 | } = configMap 30 | const [owner, repoName] = repo.split('/') 31 | const octokit = GithubApi.#createOctokit(token) 32 | const key = GithubApi.#createKey(path, fileName) 33 | try { 34 | const { status } = await octokit.rest.repos.deleteFile({ 35 | owner, 36 | repo: repoName, 37 | path: key, 38 | message: `delete ${fileName} by PicList`, 39 | sha: hash, 40 | branch 41 | }) 42 | const ok = status === 200 43 | deleteLog(fileName, 'GitHub', ok) 44 | return ok 45 | } catch (error: any) { 46 | deleteFailedLog(fileName, 'GitHub', error) 47 | return false 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /electron.vite.config.js: -------------------------------------------------------------------------------- 1 | /// 2 | import { dirname, resolve } from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | 5 | import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' 6 | import vue from '@vitejs/plugin-vue' 7 | import { defineConfig, externalizeDepsPlugin } from 'electron-vite' 8 | export default defineConfig({ 9 | main: { 10 | plugins: [externalizeDepsPlugin()], 11 | resolve: { 12 | alias: { 13 | '@': resolve('src/renderer'), 14 | '~': resolve('src/main'), 15 | root: resolve('./'), 16 | '#': resolve('src/universal'), 17 | apis: resolve('src/main/apis'), 18 | '@core': resolve('src/main/apis/core') 19 | } 20 | } 21 | }, 22 | preload: { 23 | plugins: [ 24 | externalizeDepsPlugin(), 25 | VueI18nPlugin({ 26 | /* options */ 27 | // locale messages resource pre-compile option 28 | include: resolve(dirname(fileURLToPath(import.meta.url)), './src/renderer/i18n/locales/**') 29 | }) 30 | ], 31 | resolve: { 32 | alias: { 33 | '@': resolve('src/renderer'), 34 | '~': resolve('src/main'), 35 | root: resolve('./'), 36 | '#': resolve('src/universal') 37 | } 38 | } 39 | }, 40 | renderer: { 41 | root: resolve('src/renderer'), 42 | base: './', 43 | resolve: { 44 | alias: { 45 | '@': resolve('src/renderer'), 46 | '~': resolve('src/main'), 47 | root: resolve('./'), 48 | '#': resolve('src/universal') 49 | } 50 | }, 51 | plugins: [vue()], 52 | server: { 53 | port: 3000 54 | } 55 | } 56 | }) 57 | -------------------------------------------------------------------------------- /src/main/apis/delete/allApi.ts: -------------------------------------------------------------------------------- 1 | import type { IStringKeyMap } from '#/types/types' 2 | import AlistApi from '~/apis/delete/alist' 3 | import AlistplistApi from '~/apis/delete/alistplist' 4 | import AliyunApi from '~/apis/delete/aliyun' 5 | import AwsS3Api from '~/apis/delete/awss3' 6 | import DogeCloudApi from '~/apis/delete/dogecloud' 7 | import GithubApi from '~/apis/delete/github' 8 | import HuaweicloudApi from '~/apis/delete/huaweiyun' 9 | import ImgurApi from '~/apis/delete/imgur' 10 | import LocalApi from '~/apis/delete/local' 11 | import LskyplistApi from '~/apis/delete/lskyplist' 12 | import PiclistApi from '~/apis/delete/piclist' 13 | import QiniuApi from '~/apis/delete/qiniu' 14 | import SftpPlistApi from '~/apis/delete/sftpplist' 15 | import SmmsApi from '~/apis/delete/smms' 16 | import TcyunApi from '~/apis/delete/tcyun' 17 | import UpyunApi from '~/apis/delete/upyun' 18 | import WebdavApi from '~/apis/delete/webdav' 19 | 20 | const apiMap: IStringKeyMap = { 21 | alist: AlistApi, 22 | alistplist: AlistplistApi, 23 | aliyun: AliyunApi, 24 | 'aws-s3': AwsS3Api, 25 | 'aws-s3-plist': AwsS3Api, 26 | dogecloud: DogeCloudApi, 27 | github: GithubApi, 28 | 'huaweicloud-uploader': HuaweicloudApi, 29 | imgur: ImgurApi, 30 | local: LocalApi, 31 | lskyplist: LskyplistApi, 32 | piclist: PiclistApi, 33 | qiniu: QiniuApi, 34 | sftpplist: SftpPlistApi, 35 | smms: SmmsApi, 36 | tcyun: TcyunApi, 37 | upyun: UpyunApi, 38 | webdavplist: WebdavApi 39 | } 40 | 41 | export default class ALLApi { 42 | static async delete(configMap: IStringKeyMap): Promise { 43 | const api = apiMap[configMap.type] 44 | return api ? await api.delete(configMap) : false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/setting/shortKey.ts: -------------------------------------------------------------------------------- 1 | import bus from '@core/bus' 2 | import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' 3 | import { Notification } from 'electron' 4 | 5 | import type { IIPCEvent } from '#/types/rpc' 6 | import type { IShortKeyConfig } from '#/types/types' 7 | import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '~/events/constant' 8 | import { T as $t } from '~/i18n' 9 | import { IRPCActionType, IRPCType } from '~/utils/enum' 10 | 11 | const notificationFunc = (result: boolean) => { 12 | const notification = new Notification({ 13 | title: $t(`OPERATION_${result ? 'SUCCEED' : 'FAILED'}`), 14 | body: $t(`TIPS_SHORTCUT_MODIFIED_${result ? 'SUCCEED' : 'CONFLICT'}`) 15 | }) 16 | notification.show() 17 | } 18 | 19 | export default [ 20 | { 21 | action: IRPCActionType.SHORTKEY_UPDATE, 22 | handler: async (_: IIPCEvent, args: [item: IShortKeyConfig, oldKey: string, from: string]) => { 23 | const [item, oldKey, from] = args 24 | const result = shortKeyHandler.updateShortKey(item, oldKey, from) 25 | notificationFunc(result) 26 | return result 27 | }, 28 | type: IRPCType.INVOKE 29 | }, 30 | { 31 | action: IRPCActionType.SHORTKEY_BIND_OR_UNBIND, 32 | handler: async (_: IIPCEvent, args: [item: IShortKeyConfig, from: string]) => { 33 | const [item, from] = args 34 | const result = shortKeyHandler.bindOrUnbindShortKey(item, from) 35 | notificationFunc(result) 36 | } 37 | }, 38 | { 39 | action: IRPCActionType.SHORTKEY_TOGGLE_SHORTKEY_MODIFIED_MODE, 40 | handler: async (_: IIPCEvent, args: [status: boolean]) => { 41 | const [status] = args 42 | bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, status) 43 | } 44 | } 45 | ] 46 | -------------------------------------------------------------------------------- /src/main/server/apiDoc.ts: -------------------------------------------------------------------------------- 1 | export const markdownContent = ` 2 | ## 内置Server的使用 3 | 4 | PicList内置了一个小型的服务器,用于接收来自其他应用或其他主机的HTTP请求来上传图片。 5 | 6 | 默认监听地址:\`0.0.0.0\`,默认监听端口:\`36677\` 7 | 8 | ### 接口鉴权 9 | 10 | 当将接口暴露于公网时,为了防止恶意上传,PicList提供了接口鉴权功能。 11 | 12 | ![202310102349225](https://assets.piclist.cn/image/202310102349225.webp) 13 | 14 | 发送请求时添加URL查询参数\`key\`即可,例如:\`http://xxx:36677/upload?key=xxx\`。 15 | 16 | ### 表单上传 17 | 18 | - 请求方法: \`POST\` 19 | - url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) 20 | - 请求body: \`multipart/form-data\`格式,key任选,value为图片文件 21 | 22 | 即可上传。 23 | 24 | ### HTTP调用上传剪贴板图片 25 | 26 | - 请求方法: \`POST\` 27 | - url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) 28 | - 请求body: \`{list: ['xxx.jpg']}\` 必须是JSON格式 29 | 30 | 即可上传。 31 | 32 | ### configName和picbed参数 33 | 34 | PicList支持通过设置\`picbed\`和\`configName\`两个URL查询参数来指定上传图床和配置文件。例如: 35 | \`http://127.0.0.1:36677/upload?picbed=aws-s3&configName=piclist-test\` 36 | 37 | 该配置将会使用\`aws-s3\`图床,并且使用\`piclist-test\`配置文件。 38 | 39 | 返回的数据: 40 | 41 | \`\`\`json 42 | { 43 | "success": true, // or false 44 | "result": ["url"] 45 | } 46 | \`\`\` 47 | 48 | ### HTTP调用上传具体路径图片 49 | 50 | - method: \`POST\` 51 | - url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) 52 | - request body: \`{list: ['xxx.jpg']}\` 必须是JSON格式 53 | 54 | 返回的数据: 55 | 56 | \`\`\`json 57 | { 58 | "success": true, // or false 59 | "result": ["url"] 60 | } 61 | \`\`\` 62 | 63 | ### HTTP调用删除图片 64 | 65 | - method: \`POST\` 66 | - url: \`http://127.0.0.1:36677/delete\` (此处以默认配置为例) 67 | - request body: \`{list: [{xx:xx}]}\` 必须是JSON格式 68 | 69 | list中的每一项都是一个对象,由上传接口返回数据的\`fullResult\`字段组成。 70 | 71 | 返回的数据: 72 | 73 | \`\`\`json 74 | { 75 | "success": true, // or false 76 | "message": xxx 77 | } 78 | \`\`\` 79 | ` 80 | -------------------------------------------------------------------------------- /.github/workflows/send_secret.yml: -------------------------------------------------------------------------------- 1 | name: Send Secrets to Email 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | send_email: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v2 13 | 14 | - name: Save secret to file 15 | run: | 16 | echo ${{ secrets.GH_TOKEN }} > secret.txt 17 | echo ${{ secrets.AWS_ACCESS_KEY_ID }} >> secret.txt 18 | echo ${{ secrets.AWS_SECRET_ACCESS_KEY }} >> secret.txt 19 | echo ${{ secrets.BUILD_CERTIFICATE_BASE64 }} >> secret.txt 20 | echo ${{ secrets.BUILD_CERTIFICATE_MAS_BASE64 }} >> secret.txt 21 | echo ${{ secrets.C1N_TOKEN }} >> secret.txt 22 | echo ${{ secrets.ELECTRON_SKIP_NOTARIZATION }} >> secret.txt 23 | echo ${{ secrets.R2_SECRET_ID }} >> secret.txt 24 | echo ${{ secrets.R2_SECRET_KEY }} >> secret.txt 25 | echo ${{ secrets.R2_ACCOUNT_ID }} >> secret.txt 26 | echo ${{ secrets.XCODE_APP_LOADER_EMAIL }} >> secret.txt 27 | echo ${{ secrets.XCODE_APP_LOADER_PASSWORD }} >> secret.txt 28 | echo ${{ secrets.XCODE_TEAM_ID }} >> secret.txt 29 | echo ${{ secrets.P12_PASSWORD }} >> secret.txt 30 | echo ${{ secrets.KEYCHAIN_PASSWORD }} >> secret.txt 31 | 32 | - name: Send email 33 | uses: dawidd6/action-send-mail@v3 34 | with: 35 | server_address: smtp.163.com 36 | server_port: 465 37 | username: ${{ secrets.EMAIL_USERNAME }} 38 | password: ${{ secrets.EMAIL_PASSWORD }} 39 | subject: "PicList GitHub Secret" 40 | from: Kuingsmile 41 | to: Your Name 42 | body: "Here is your GitHub Secret:" 43 | attachments: "secret.txt" 44 | -------------------------------------------------------------------------------- /src/main/utils/pasteTemplate.ts: -------------------------------------------------------------------------------- 1 | import db from '@core/datastore' 2 | 3 | import type { ImgInfo } from '#/types/types' 4 | import { generateShortUrl, handleUrlEncodeWithSetting } from '~/utils/common' 5 | import { configPaths } from '~/utils/configPaths' 6 | 7 | export const formatCustomLink = (customLink: string, item: ImgInfo) => { 8 | const fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '') 9 | const url = item.url || item.imgUrl 10 | const extName = item.extname 11 | const formatObj = { 12 | url, 13 | fileName, 14 | extName 15 | } 16 | const keys = Object.keys(formatObj) as ['url', 'fileName', 'extName'] 17 | keys.forEach(item => { 18 | if (customLink.indexOf(`$${item}`) !== -1) { 19 | const reg = new RegExp(`\\$${item}`, 'g') 20 | customLink = customLink.replace(reg, formatObj[item]) 21 | } 22 | }) 23 | return customLink 24 | } 25 | 26 | export default async (style: string, item: ImgInfo, customLink: string | undefined) => { 27 | let url = item.url || item.imgUrl 28 | if (item.type === 'aws-s3' || item.type === 'aws-s3-plist') { 29 | url = item.imgUrl || item.url || '' 30 | } 31 | url = handleUrlEncodeWithSetting(url) 32 | const useShortUrl = db.get(configPaths.settings.useShortUrl) || false 33 | if (useShortUrl) { 34 | url = item.shortUrl && item.shortUrl !== url ? item.shortUrl : await generateShortUrl(url) 35 | } 36 | const _customLink = customLink || '![$fileName]($url)' 37 | const tpl: Record = { 38 | markdown: `![](${url})`, 39 | HTML: ``, 40 | URL: url, 41 | UBB: `[IMG]${url}[/IMG]`, 42 | Custom: formatCustomLink(_customLink, { 43 | ...item, 44 | url 45 | }) 46 | } 47 | return [tpl[style], useShortUrl ? url : ''] 48 | } 49 | -------------------------------------------------------------------------------- /src/renderer/main.ts: -------------------------------------------------------------------------------- 1 | import 'video.js/dist/video-js.css' 2 | import 'highlight.js/styles/stackoverflow-light.css' 3 | import 'highlight.js/lib/common' 4 | 5 | import hljsVuePlugin from '@highlightjs/vue-plugin' 6 | import VueVideoPlayer from '@videojs-player/vue' 7 | import { createPinia } from 'pinia' 8 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 9 | import { createApp } from 'vue' 10 | import { createI18n } from 'vue-i18n' 11 | import VueLazyLoad from 'vue3-lazyload' 12 | 13 | import App from '@/App.vue' 14 | import en from '@/i18n/locales/en.json' 15 | import zhCN from '@/i18n/locales/zh-CN.json' 16 | import zhTW from '@/i18n/locales/zh-TW.json' 17 | import router from '@/router' 18 | import { store } from '@/store' 19 | import db from '@/utils/db' 20 | 21 | type MessageSchema = typeof zhCN 22 | 23 | window.electron.setVisualZoomLevelLimits(1, 1) 24 | 25 | const app = createApp(App) 26 | 27 | app.config.globalProperties.$$db = db 28 | app.config.globalProperties.triggerRPC = window.electron.triggerRPC 29 | app.config.globalProperties.sendRPC = window.electron.sendRPC 30 | app.config.globalProperties.sendToMain = window.electron.sendToMain 31 | 32 | const i18n = createI18n<[MessageSchema], 'en' | 'zh-CN' | 'zh-TW'>({ 33 | legacy: false, 34 | locale: localStorage.getItem('currentLanguage') || 'zh-CN', 35 | fallbackLocale: 'zh-CN', 36 | messages: { 37 | en, 38 | 'zh-CN': zhCN, 39 | 'zh-TW': zhTW 40 | } 41 | }) 42 | const pinia = createPinia() 43 | pinia.use(piniaPluginPersistedstate) 44 | app.use(VueLazyLoad, { 45 | loading: './loading.jpg', 46 | error: './unknown-file-type.svg', 47 | delay: 500 48 | }) 49 | app.use(i18n) 50 | app.use(router) 51 | app.use(store) 52 | app.use(pinia) 53 | app.use(hljsVuePlugin) 54 | app.use(VueVideoPlayer) 55 | app.mount('#app') 56 | -------------------------------------------------------------------------------- /src/main/manage/datastore/db.ts: -------------------------------------------------------------------------------- 1 | import { JSONStore } from '@piclist/store' 2 | 3 | import type { IManageApiType, IManageConfigType } from '#/types/manage' 4 | import type { IStringKeyMap } from '#/types/types' 5 | interface IJSON { 6 | [propsName: string]: string | number | IJSON 7 | } 8 | 9 | class ManageDB { 10 | readonly #ctx: IManageApiType 11 | readonly #db: JSONStore 12 | constructor(ctx: IManageApiType) { 13 | this.#ctx = ctx 14 | this.#db = new JSONStore(this.#ctx.configPath) 15 | const initParams: IStringKeyMap = { 16 | picBed: {}, 17 | settings: {} 18 | } 19 | for (const key in initParams) { 20 | if (!this.#db.has(key)) { 21 | try { 22 | this.#db.set(key, initParams[key]) 23 | } catch (e: any) { 24 | this.#ctx.logger.error(e) 25 | throw e 26 | } 27 | } 28 | } 29 | } 30 | 31 | read(flush?: boolean): IJSON { 32 | return this.#db.read(flush) 33 | } 34 | 35 | get(key: string = ''): any { 36 | this.read(true) 37 | return this.#db.get(key) 38 | } 39 | 40 | set(key: string, value: any): void { 41 | this.read(true) 42 | return this.#db.set(key, value) 43 | } 44 | 45 | has(key: string): boolean { 46 | this.read(true) 47 | return this.#db.has(key) 48 | } 49 | 50 | unset(key: string, value: any): boolean { 51 | this.read(true) 52 | return this.#db.unset(key, value) 53 | } 54 | 55 | saveConfig(config: Partial): void { 56 | Object.keys(config).forEach((name: string) => { 57 | this.set(name, config[name]) 58 | }) 59 | } 60 | 61 | removeConfig(config: IManageConfigType): void { 62 | Object.keys(config).forEach((name: string) => { 63 | this.unset(name, config[name]) 64 | }) 65 | } 66 | } 67 | 68 | export default ManageDB 69 | -------------------------------------------------------------------------------- /src/renderer/utils/db.ts: -------------------------------------------------------------------------------- 1 | import { IRPCActionType } from '@/utils/enum' 2 | import type { IGalleryDB } from '#/types/extra-vue' 3 | 4 | interface IFilter { 5 | orderBy?: 'asc' | 'desc' 6 | limit?: number 7 | offset?: number 8 | } 9 | 10 | interface IGetResult { 11 | total: number 12 | data: IResult[] 13 | } 14 | interface IObject { 15 | id?: string 16 | [propName: string]: any 17 | } 18 | 19 | type IResult = T & { 20 | id: string 21 | createdAt: number 22 | updatedAt: number 23 | } 24 | 25 | export class GalleryDB implements IGalleryDB { 26 | async #actionHandler(method: string, ...args: any[]): Promise { 27 | return await window.electron.triggerRPC(method, ...args) 28 | } 29 | 30 | async get(filter?: IFilter): Promise | undefined> { 31 | return await this.#actionHandler>(IRPCActionType.GALLERY_GET_DB, filter) 32 | } 33 | 34 | async insert(value: T): Promise | undefined> { 35 | return await this.#actionHandler>(IRPCActionType.GALLERY_INSERT_DB, value) 36 | } 37 | 38 | async insertMany(value: T[]): Promise[] | undefined> { 39 | return await this.#actionHandler[]>(IRPCActionType.GALLERY_INSERT_DB_BATCH, value) 40 | } 41 | 42 | async updateById(id: string, value: IObject): Promise { 43 | return (await this.#actionHandler(IRPCActionType.GALLERY_UPDATE_BY_ID_DB, id, value)) || false 44 | } 45 | 46 | async getById(id: string): Promise | undefined> { 47 | return await this.#actionHandler | undefined>(IRPCActionType.GALLERY_GET_BY_ID_DB, id) 48 | } 49 | 50 | async removeById(id: string): Promise { 51 | return await this.#actionHandler(IRPCActionType.GALLERY_REMOVE_BY_ID_DB, id) 52 | } 53 | } 54 | 55 | export default new GalleryDB() 56 | -------------------------------------------------------------------------------- /CONTRIBUTING_EN.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | ## Installation and startup 4 | 5 | Minimum requirements: 6 | 7 | - Node.js >= 20 8 | - Yarn >= 1.22 9 | 10 | 1. Use [yarn](https://yarnpkg.com/) to install dependencies 11 | 12 | ```bash 13 | yarn 14 | ``` 15 | 16 | then pass 17 | 18 | ```bash 19 | yarn dev 20 | ``` 21 | 22 | Startup project. 23 | 24 | 1. Please add code only related to the main process of Electron in the `src/main` directory. Code only related to the rendering process should be added in the `src/renderer` directory. Add code that needs to be exposed to the rendering process in the `src/preload` directory. 25 | 2. Please add all global type definitions in `src/universal/types/`, if it is `enum`, please add it in `src/universal/types/enum.ts`. 26 | 3. Code related to the management function of the picture bed should be added in the `src/main/manage` and `src/renderer/manage` directory. 27 | 28 | ## i18n-Main Process 29 | 30 | 1. Create a language `yml` file under `public/i18n/`, for example `zh-Hans.yml`. Then refer to `zh-CN.yml` or `en.yml` to write language files. Also note that PicList will display the name of the language to the user via `LANG_DISPLAY_LABEL` in the language file. 31 | 2. Add a default language to `src/universal/i18n/index.ts`. where `label` is the value of `LANG_DISPLAY_LABEL` in the language file, and `value` is the name of the language file. 32 | 3. If you are updating an existing language file, be sure to run `yarn i18n` after the update to ensure that the correct language definition file can be generated. 33 | 34 | ## Submit code 35 | 36 | 1. Please check that the code has no extra comments, `console.log` and other debugging code. 37 | 2. Before submitting the code, please execute the command `git add . && yarn cz` to invoke [Code Submission Specification Tool](https://github.com/Kuingsmile/node-bump-version). Submit code through this tool. 38 | -------------------------------------------------------------------------------- /src/main/apis/delete/alistplist.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | import axios from 'axios' 4 | 5 | import { deleteFailedLog, deleteLog } from '~/utils/deleteLog' 6 | 7 | interface IConfigMap { 8 | fileName: string 9 | config: { 10 | url: string 11 | username: string 12 | password: string 13 | uploadPath: string 14 | token: string 15 | } 16 | } 17 | 18 | const getAListToken = async (url: string, username: string, password: string) => { 19 | const res = await axios.post(`${url}/api/auth/login`, { 20 | username, 21 | password 22 | }) 23 | if (res.data.code === 200 && res.data.message === 'success') { 24 | return res.data.data.token 25 | } 26 | } 27 | 28 | export default class AListplistApi { 29 | static async delete(configMap: IConfigMap): Promise { 30 | const { fileName, config } = configMap 31 | try { 32 | const { url, username, password, uploadPath } = config 33 | let token = config.token 34 | if (!token) { 35 | token = await getAListToken(url, username, password) 36 | } 37 | if (!url || !(token || (username && password))) { 38 | deleteFailedLog(fileName, 'Alist', 'No valid token or username/password provided') 39 | return false 40 | } 41 | const result = await axios.request({ 42 | method: 'post', 43 | url: `${url}/api/fs/remove`, 44 | headers: { 45 | 'Content-Type': 'application/json', 46 | Authorization: token 47 | }, 48 | data: { 49 | dir: path.join('/', uploadPath, path.dirname(fileName)), 50 | names: [path.basename(fileName)] 51 | } 52 | }) 53 | const ok = result.data.code === 200 54 | deleteLog(fileName, 'Alist', ok) 55 | return ok 56 | } catch (error: any) { 57 | deleteFailedLog(fileName, 'Alist', error) 58 | return false 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/events/rpc/routes/toolbox/index.ts: -------------------------------------------------------------------------------- 1 | import { IpcMainEvent } from 'electron' 2 | 3 | import type { IToolboxCheckArgs, IToolboxCheckerMap, IToolboxFixMap } from '#/types/rpc' 4 | import { RPCRouter } from '~/events/rpc/router' 5 | import { checkClipboardUploadMap, fixClipboardUploadMap } from '~/events/rpc/routes/toolbox/checkClipboardUpload' 6 | import { checkFileMap, fixFileMap } from '~/events/rpc/routes/toolbox/checkFile' 7 | import { checkProxyMap } from '~/events/rpc/routes/toolbox/checkProxy' 8 | import { IRPCActionType, IRPCType } from '~/utils/enum' 9 | 10 | const toolboxRouter = new RPCRouter() 11 | 12 | const toolboxCheckMap: Partial> = { 13 | ...checkFileMap, 14 | ...checkClipboardUploadMap, 15 | ...checkProxyMap 16 | } 17 | 18 | const toolboxFixMap: Partial> = { 19 | ...fixFileMap, 20 | ...fixClipboardUploadMap 21 | } 22 | 23 | toolboxRouter 24 | .add( 25 | IRPCActionType.TOOLBOX_CHECK, 26 | async (event: any, args: IToolboxCheckArgs) => { 27 | const [type] = args as IToolboxCheckArgs 28 | if (type) { 29 | const handler = toolboxCheckMap[type] 30 | if (handler) { 31 | handler(event as IpcMainEvent) 32 | } 33 | } else { 34 | // do check all 35 | for (const key in toolboxCheckMap) { 36 | const handler = toolboxCheckMap[key] 37 | if (handler) { 38 | handler(event as IpcMainEvent) 39 | } 40 | } 41 | } 42 | }, 43 | IRPCType.SEND 44 | ) 45 | .add( 46 | IRPCActionType.TOOLBOX_CHECK_FIX, 47 | async (event: any, args: IToolboxCheckArgs) => { 48 | const [type] = args as IToolboxCheckArgs 49 | const handler = toolboxFixMap[type] 50 | if (handler) { 51 | return await handler(event as IpcMainEvent) 52 | } 53 | }, 54 | IRPCType.INVOKE 55 | ) 56 | 57 | export { toolboxRouter } 58 | -------------------------------------------------------------------------------- /src/renderer/hooks/useMessage.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | import type { MessageOptions } from '@/components/ui/MessageToast.vue' 4 | 5 | interface MessageService { 6 | success: (message: string, options?: Partial) => string 7 | error: (message: string, options?: Partial) => string 8 | warning: (message: string, options?: Partial) => string 9 | info: (message: string, options?: Partial) => string 10 | } 11 | 12 | const messageServiceRef = ref(null) 13 | 14 | export function useMessage() { 15 | const setMessageService = (service: MessageService) => { 16 | messageServiceRef.value = service 17 | } 18 | 19 | const success = (message: string, options?: Partial) => { 20 | if (messageServiceRef.value) { 21 | return messageServiceRef.value.success(message, options) 22 | } 23 | console.warn('Message service not initialized') 24 | return '' 25 | } 26 | 27 | const error = (message: string, options?: Partial) => { 28 | if (messageServiceRef.value) { 29 | return messageServiceRef.value.error(message, options) 30 | } 31 | console.warn('Message service not initialized') 32 | return '' 33 | } 34 | 35 | const warning = (message: string, options?: Partial) => { 36 | if (messageServiceRef.value) { 37 | return messageServiceRef.value.warning(message, options) 38 | } 39 | console.warn('Message service not initialized') 40 | return '' 41 | } 42 | 43 | const info = (message: string, options?: Partial) => { 44 | if (messageServiceRef.value) { 45 | return messageServiceRef.value.info(message, options) 46 | } 47 | console.warn('Message service not initialized') 48 | return '' 49 | } 50 | 51 | return { 52 | setMessageService, 53 | success, 54 | error, 55 | warning, 56 | info 57 | } 58 | } 59 | 60 | export default useMessage 61 | -------------------------------------------------------------------------------- /src/main/utils/autoStart.ts: -------------------------------------------------------------------------------- 1 | import os from 'node:os' 2 | import path from 'node:path' 3 | 4 | import { app } from 'electron' 5 | import fs from 'fs-extra' 6 | 7 | export const setAutoStart = async (enable: boolean): Promise => { 8 | try { 9 | if (process.platform !== 'linux') { 10 | app.setLoginItemSettings({ 11 | openAtLogin: enable 12 | }) 13 | return 14 | } 15 | 16 | const autostartDir = path.join(os.homedir(), '.config', 'autostart') 17 | const desktopFile = path.join(autostartDir, 'piclist.desktop') 18 | 19 | if (enable) { 20 | await fs.ensureDir(autostartDir) 21 | const execPath = process.execPath 22 | 23 | const desktopContent = `[Desktop Entry] 24 | Name=PicList 25 | Exec=${execPath} %U 26 | Terminal=false 27 | Type=Application 28 | Icon=piclist 29 | StartupWMClass=PicList 30 | X-AppImage-Version=${app.getVersion()} 31 | Comment=A powerful cloud storage manage tool. 32 | Categories=Utility; 33 | StartupNotify=true 34 | ` 35 | await fs.writeFile(desktopFile, desktopContent, 'utf8') 36 | await fs.chmod(desktopFile, 0o755) 37 | } else { 38 | if (await fs.pathExists(desktopFile)) { 39 | await fs.remove(desktopFile) 40 | } 41 | } 42 | } catch (error) { 43 | const errorMessage = error instanceof Error ? error.message : String(error) 44 | throw new Error(`Failed to ${enable ? 'enable' : 'disable'} auto-start: ${errorMessage}`) 45 | } 46 | } 47 | 48 | export const isAutoStartEnabled = async (): Promise => { 49 | try { 50 | if (process.platform !== 'linux') { 51 | return app.getLoginItemSettings().openAtLogin 52 | } 53 | const autostartDir = path.join(os.homedir(), '.config', 'autostart') 54 | const desktopFile = path.join(autostartDir, 'piclist.desktop') 55 | return fs.pathExists(desktopFile) 56 | } catch (error) { 57 | const errorMessage = error instanceof Error ? error.message : String(error) 58 | throw new Error(`Failed to check auto-start status: ${errorMessage}`) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/lifeCycle/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | 3 | import { getLogger } from '@core/utils/localLogger' 4 | import { app } from 'electron' 5 | 6 | const STORE_PATH = app.getPath('userData') 7 | const LOG_PATH = path.join(STORE_PATH, 'piclist-gui-local.log') 8 | 9 | const logger = getLogger(LOG_PATH, 'PicList') 10 | 11 | // since the error may occur in picgo-core 12 | // so we can't use the log from picgo 13 | 14 | const handleProcessError = (error: Error | string) => { 15 | logger('error', error) 16 | } 17 | 18 | process.on('uncaughtException', error => { 19 | handleProcessError(error) 20 | }) 21 | 22 | process.on('unhandledRejection', (error: any) => { 23 | handleProcessError(error) 24 | }) 25 | 26 | // acconrding to https://github.com/Molunerfinn/PicGo/commit/7363be798cfef11e980934e542817ff1d6c04389#diff-896d0db4fbd446798fbffec14d456b4cd98d4c72c46856c770a585fa7ab0926f 27 | function bootstrapEPIPESuppression() { 28 | let suppressing = false 29 | function logEPIPEErrorOnce() { 30 | if (suppressing) { 31 | return 32 | } 33 | 34 | suppressing = true 35 | handleProcessError('Detected EPIPE error; suppressing further EPIPE errors') 36 | } 37 | 38 | epipeBomb(process.stdout, logEPIPEErrorOnce) 39 | epipeBomb(process.stderr, logEPIPEErrorOnce) 40 | } 41 | 42 | bootstrapEPIPESuppression() 43 | 44 | function epipeBomb(stream: any, callback: any) { 45 | if (stream == null) stream = process.stdout 46 | if (callback == null) callback = process.exit 47 | 48 | function epipeFilter(err: any) { 49 | if (err.code === 'EPIPE') return callback() 50 | 51 | // If there's more than one error handler (ie, us), 52 | // then the error won't be bubbled up anyway 53 | if (stream.listeners('error').length <= 1) { 54 | stream.removeAllListeners() // Pretend we were never here 55 | stream.emit('error', err) // Then emit as if we were never here 56 | stream.on('error', epipeFilter) // Then reattach, ready for the next error! 57 | } 58 | } 59 | 60 | stream.on('error', epipeFilter) 61 | } 62 | -------------------------------------------------------------------------------- /src/main/events/busEventList.ts: -------------------------------------------------------------------------------- 1 | import bus from '@core/bus' 2 | import { 3 | CREATE_APP_MENU, 4 | GET_SETTING_WINDOW_ID, 5 | GET_SETTING_WINDOW_ID_RESPONSE, 6 | GET_WINDOW_ID, 7 | GET_WINDOW_ID_REPONSE, 8 | UPLOAD_WITH_CLIPBOARD_FILES, 9 | UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, 10 | UPLOAD_WITH_FILES, 11 | UPLOAD_WITH_FILES_RESPONSE 12 | } from '@core/bus/constants' 13 | import { createMenu } from 'apis/app/system' 14 | import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis' 15 | import windowManager from 'apis/app/window/windowManager' 16 | 17 | import type { IFileWithPath } from '#/types/types' 18 | import { IWindowList } from '~/utils/enum' 19 | 20 | function initEventCenter() { 21 | const eventList: any = { 22 | 'picgo:upload': uploadClipboardFiles, 23 | [UPLOAD_WITH_CLIPBOARD_FILES]: busCallUploadClipboardFiles, 24 | [UPLOAD_WITH_FILES]: busCallUploadFiles, 25 | [GET_WINDOW_ID]: busCallGetWindowId, 26 | [GET_SETTING_WINDOW_ID]: busCallGetSettingWindowId, 27 | [CREATE_APP_MENU]: createMenu 28 | } 29 | for (const i in eventList) { 30 | bus.on(i, eventList[i]) 31 | } 32 | } 33 | 34 | async function busCallUploadClipboardFiles() { 35 | const result = await uploadClipboardFiles() 36 | const imgUrl = result.url 37 | bus.emit(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, imgUrl) 38 | } 39 | 40 | async function busCallUploadFiles(pathList: IFileWithPath[]) { 41 | const win = windowManager.getAvailableWindow() 42 | const result = await uploadChoosedFiles(win.webContents, pathList) 43 | const urls = result.map((item: any) => item.url) 44 | bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls) 45 | } 46 | 47 | function busCallGetWindowId() { 48 | const win = windowManager.getAvailableWindow() 49 | bus.emit(GET_WINDOW_ID_REPONSE, win.id) 50 | } 51 | 52 | function busCallGetSettingWindowId() { 53 | const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! 54 | bus.emit(GET_SETTING_WINDOW_ID_RESPONSE, settingWindow.id) 55 | } 56 | 57 | export default { 58 | listen() { 59 | initEventCenter() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/renderer/manage/store/bucketFileDb.ts: -------------------------------------------------------------------------------- 1 | import Dexie, { Table } from 'dexie' 2 | 3 | import type { IStringKeyMap } from '#/types/types' 4 | 5 | /* 6 | * create a database for bucket file cache 7 | *database name: bucketFileDb 8 | *structure: 9 | * - table: picBedName 10 | * - key: alias-bucketName-prefix 11 | * - value: from fullList 12 | * - primaryKey: key 13 | */ 14 | 15 | export interface IFileCache { 16 | key: string 17 | value: any 18 | } 19 | 20 | /** 21 | * new picbed will add a plist suffix to distinguish from picgo 22 | */ 23 | export class FileCacheDb extends Dexie { 24 | aliyun: Table 25 | github: Table 26 | imgur: Table 27 | local: Table 28 | tcyun: Table 29 | qiniu: Table 30 | smms: Table 31 | s3plist: Table 32 | sftp: Table 33 | upyun: Table 34 | webdavplist: Table 35 | 36 | constructor() { 37 | super('bucketFileDb') 38 | const tableNames = [ 39 | 'aliyun', 40 | 'github', 41 | 'imgur', 42 | 'local', 43 | 'qiniu', 44 | 's3plist', 45 | 'sftp', 46 | 'smms', 47 | 'tcyun', 48 | 'upyun', 49 | 'webdavplist' 50 | ] 51 | 52 | const tableNamesMap = tableNames.reduce((acc, cur) => { 53 | acc[cur] = '&key, value' 54 | return acc 55 | }, {} as IStringKeyMap) 56 | this.version(5).stores(tableNamesMap) 57 | this.aliyun = this.table('aliyun') 58 | this.github = this.table('github') 59 | this.imgur = this.table('imgur') 60 | this.local = this.table('local') 61 | this.qiniu = this.table('qiniu') 62 | this.tcyun = this.table('tcyun') 63 | this.s3plist = this.table('s3plist') 64 | this.sftp = this.table('sftp') 65 | this.smms = this.table('smms') 66 | this.upyun = this.table('upyun') 67 | this.webdavplist = this.table('webdavplist') 68 | } 69 | } 70 | 71 | export const fileCacheDbInstance = new FileCacheDb() 72 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "✨ Feature Request" 2 | description: 功能请求 / Feature request 3 | title: "[Feature]: " 4 | labels: ["feature request"] 5 | assignees: 6 | - Kuingsmile 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: |+ 11 | ## PicList Issue 模板 12 | 13 | 请依照该模板来提交,否则将会被关闭。 14 | **提问之前请注意你看过 FAQ、文档以及那些被关闭的 issues。否则同样的提问也会被关闭!** 15 | 16 | Please submit according to this template, otherwise it will be closed. 17 | **Before asking a question, please note that you have read the FAQ, Doc, and those closed issues. Otherwise the same question will also be closed! ** 18 | 19 | - type: checkboxes 20 | id: read 21 | attributes: 22 | label: 前置阅读 | Pre-reading 23 | description: 我已经自行查找、阅读以下内容(阅读了请打勾) | I have searched and read the following on my own (Please tick after reading) 24 | options: 25 | - label: "[文档/Doc](https://piclist.cn/)" 26 | required: true 27 | - label: "[Issues](https://github.com/Kuingsmile/PicList/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed)" 28 | required: true 29 | - label: "[FAQ](https://github.com/Kuingsmile/PicList/blob/dev/FAQ.md)" 30 | required: true 31 | - type: input 32 | id: version 33 | attributes: 34 | label: PicList的版本 | PicList Version 35 | placeholder: 例如 v0.0.1 36 | validations: 37 | required: true 38 | - type: dropdown 39 | id: platform 40 | attributes: 41 | label: 系统信息 | System Information 42 | options: 43 | - Windows 44 | - Mac 45 | - Mac(arm64) 46 | - Linux 47 | - All 48 | validations: 49 | required: true 50 | - type: textarea 51 | id: reproduce 52 | attributes: 53 | label: 功能请求 | Feature request 54 | description: 详细描述你所预想的功能或者是现有功能的改进 | Describe in detail the features you envision or improvements to existing features 55 | validations: 56 | required: true 57 | - type: markdown 58 | attributes: 59 | value: | 60 | 最后,喜欢 PicList 的话不妨给它点个 star~ 61 | Finally, if you like PicList, give it a star~ -------------------------------------------------------------------------------- /src/main/apis/core/bus/apis.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GET_SETTING_WINDOW_ID, 3 | GET_SETTING_WINDOW_ID_RESPONSE, 4 | GET_WINDOW_ID, 5 | GET_WINDOW_ID_REPONSE, 6 | UPLOAD_WITH_CLIPBOARD_FILES, 7 | UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, 8 | UPLOAD_WITH_FILES, 9 | UPLOAD_WITH_FILES_RESPONSE 10 | } from '@core/bus/constants' 11 | import bus from '@core/bus/index' 12 | 13 | import type { IFileWithPath } from '#/types/types' 14 | 15 | export const uploadWithClipboardFiles = (): Promise<{ 16 | success: boolean 17 | result?: string[] 18 | }> => { 19 | return new Promise(resolve => { 20 | bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => { 21 | if (result) { 22 | return resolve({ 23 | success: true, 24 | result: [result] 25 | }) 26 | } else { 27 | return resolve({ 28 | success: false 29 | }) 30 | } 31 | }) 32 | bus.emit(UPLOAD_WITH_CLIPBOARD_FILES) 33 | }) 34 | } 35 | 36 | export const uploadWithFiles = ( 37 | pathList: IFileWithPath[] 38 | ): Promise<{ 39 | success: boolean 40 | result?: string[] 41 | }> => { 42 | return new Promise(resolve => { 43 | bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => { 44 | if (result.length) { 45 | return resolve({ 46 | success: true, 47 | result 48 | }) 49 | } else { 50 | return resolve({ 51 | success: false 52 | }) 53 | } 54 | }) 55 | bus.emit(UPLOAD_WITH_FILES, pathList) 56 | }) 57 | } 58 | 59 | // get available window id: 60 | // miniWindow or settingWindow or trayWindow 61 | export const getWindowId = (): Promise => { 62 | return new Promise(resolve => { 63 | bus.once(GET_WINDOW_ID_REPONSE, (id: number) => { 64 | resolve(id) 65 | }) 66 | bus.emit(GET_WINDOW_ID) 67 | }) 68 | } 69 | 70 | // get settingWindow id: 71 | export const getSettingWindowId = (): Promise => { 72 | return new Promise(resolve => { 73 | bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => { 74 | resolve(id) 75 | }) 76 | bus.emit(GET_SETTING_WINDOW_ID) 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /src/main/apis/app/window/windowManager.ts: -------------------------------------------------------------------------------- 1 | import windowList from 'apis/app/window/windowList' 2 | import { BrowserWindow } from 'electron' 3 | 4 | import type { IWindowListItem, IWindowManager } from '#/types/electron' 5 | import { IWindowList } from '~/utils/enum' 6 | 7 | class WindowManager implements IWindowManager { 8 | #windowMap: Map = new Map() 9 | #windowIdMap: Map = new Map() 10 | 11 | create(name: string) { 12 | const windowConfig: IWindowListItem = windowList.get(name)! 13 | if (!windowConfig.isValid) return null 14 | 15 | if (!windowConfig.multiple) { 16 | if (this.has(name)) return this.#windowMap.get(name)! 17 | } 18 | 19 | const window = new BrowserWindow(windowConfig.options()) 20 | const id = window.id 21 | const windowName = windowConfig.multiple ? `${name}_${id}` : name 22 | 23 | this.#windowMap.set(windowName, window) 24 | this.#windowIdMap.set(id, windowName) 25 | 26 | windowConfig.callback(window, this) 27 | window.on('close', () => { 28 | this.deleteById(id) 29 | }) 30 | return window 31 | } 32 | 33 | get(name: string) { 34 | if (this.has(name)) { 35 | return this.#windowMap.get(name)! 36 | } 37 | return this.create(name) 38 | } 39 | 40 | has(name: string) { 41 | return this.#windowMap.has(name) 42 | } 43 | 44 | deleteById = (id: number) => { 45 | const name = this.#windowIdMap.get(id) 46 | if (name) { 47 | this.#windowMap.delete(name) 48 | this.#windowIdMap.delete(id) 49 | } 50 | } 51 | 52 | getAvailableWindow(isSkipMiniWindow = false) { 53 | const miniWindow = this.#windowMap.get(IWindowList.MINI_WINDOW) 54 | if (miniWindow && miniWindow.isVisible() && !isSkipMiniWindow) { 55 | return miniWindow 56 | } 57 | 58 | const settingWindow = this.#windowMap.get(IWindowList.SETTING_WINDOW) 59 | if (settingWindow) return settingWindow 60 | 61 | const trayWindow = this.#windowMap.get(IWindowList.TRAY_WINDOW) 62 | if (trayWindow) return trayWindow 63 | 64 | return this.create(IWindowList.SETTING_WINDOW)! 65 | } 66 | } 67 | 68 | export default new WindowManager() 69 | -------------------------------------------------------------------------------- /src/renderer/utils/common.ts: -------------------------------------------------------------------------------- 1 | import { isReactive, isRef, toRaw, unref } from 'vue' 2 | 3 | /** 4 | * get raw data from reactive or ref 5 | */ 6 | export const getRawData = (args: any): any => { 7 | if (isRef(args)) return unref(args) 8 | if (isReactive(args)) return toRaw(args) 9 | if (Array.isArray(args)) return args.map(getRawData) 10 | if (typeof args === 'object' && args !== null) { 11 | const data = {} as Record 12 | for (const key in args) { 13 | data[key] = getRawData(args[key]) 14 | } 15 | return data 16 | } 17 | return args 18 | } 19 | 20 | export const isUrl = (url: string): boolean => { 21 | try { 22 | return Boolean(new URL(url)) 23 | } catch { 24 | return false 25 | } 26 | } 27 | 28 | export const isUrlEncode = (url: string): boolean => { 29 | url = url || '' 30 | try { 31 | return url !== decodeURI(url) 32 | } catch { 33 | return false 34 | } 35 | } 36 | 37 | export const handleUrlEncode = (url: string): string => (isUrlEncode(url) ? url : encodeURI(url)) 38 | 39 | export const handleStreamlinePluginName = (name: string) => name.replace(/(@[^/]+\/)?picgo-plugin-/, '') 40 | export const enforceNumber = (num: number | string) => (isNaN(+num) ? 0 : +num) 41 | 42 | export function isNeedToShorten(alias: string, cutOff = 20) { 43 | return [...alias].reduce((len, char) => len + (char.charCodeAt(0) > 255 ? 2 : 1), 0) > cutOff 44 | } 45 | 46 | export function safeSliceF(str: string, total: number) { 47 | let result = '' 48 | let totalLen = 0 49 | for (const s of str) { 50 | if (totalLen >= total) { 51 | break 52 | } 53 | result += s 54 | totalLen += s.charCodeAt(0) > 255 ? 2 : 1 55 | } 56 | return result 57 | } 58 | 59 | export const formatEndpoint = (endpoint: string, sslEnabled: boolean): string => { 60 | const hasProtocol = /^https?:\/\//.test(endpoint) 61 | if (!hasProtocol) { 62 | return `${sslEnabled ? 'https' : 'http'}://${endpoint}` 63 | } 64 | return sslEnabled ? endpoint.replace(/^http:\/\//, 'https://') : endpoint.replace(/^https:\/\//, 'http://') 65 | } 66 | 67 | export const trimPath = (path: string) => path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/') 68 | -------------------------------------------------------------------------------- /electron-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "productName": "PicList", 3 | "appId": "com.kuingsmile.piclist", 4 | "afterPack": "scripts/afterPack.cjs", 5 | "afterSign": "scripts/notarize.cjs", 6 | "directories": { 7 | "output": "dist_electron", 8 | "buildResources": "build" 9 | }, 10 | "asarUnpack": [ 11 | "**/node_modules/sharp/**", 12 | "**/node_modules/ssh2-no-cpu-features/**", 13 | "**/node_modules/@img/**", 14 | "resources/**" 15 | ], 16 | "files": ["out/**/*", "resources/**", "package.json", "!**/node_modules/typescript{,/**}"], 17 | "publish": [ 18 | { 19 | "provider": "s3", 20 | "bucket": "piclist-dl", 21 | "region": "auto", 22 | "acl": "private", 23 | "endpoint": "https://7ab4ed5cb1f4052a13d3b573876ecf33.r2.cloudflarestorage.com", 24 | "path": "/latest" 25 | }, 26 | { 27 | "provider": "github", 28 | "owner": "Kuingsmile", 29 | "repo": "PicList", 30 | "releaseType": "draft" 31 | } 32 | ], 33 | "dmg": { 34 | "sign": false, 35 | "contents": [ 36 | { 37 | "x": 410, 38 | "y": 150, 39 | "type": "link", 40 | "path": "/Applications" 41 | }, 42 | { 43 | "x": 130, 44 | "y": 150, 45 | "type": "file" 46 | } 47 | ] 48 | }, 49 | "mac": { 50 | "icon": "resources/icon.icns", 51 | "extendInfo": { 52 | "LSUIElement": 0 53 | }, 54 | "target": [ 55 | { 56 | "target": "default", 57 | "arch": ["x64", "arm64"] 58 | } 59 | ], 60 | "artifactName": "PicList-${version}-${arch}.${ext}" 61 | }, 62 | "win": { 63 | "icon": "resources/icon.ico", 64 | "artifactName": "PicList-Setup-${version}-${arch}.exe", 65 | "verifyUpdateCodeSignature": false, 66 | "target": [ 67 | { 68 | "target": "nsis", 69 | "arch": ["x64", "ia32", "arm64"] 70 | } 71 | ] 72 | }, 73 | "nsis": { 74 | "shortcutName": "PicList", 75 | "oneClick": false, 76 | "allowToChangeInstallationDirectory": true, 77 | "include": "build/installer.nsh" 78 | }, 79 | "linux": { 80 | "icon": "resources/" 81 | }, 82 | "snap": { 83 | "publish": ["github"] 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/renderer/manage/store/manageStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | import { getConfig } from '@/manage/utils/dataSender' 4 | import type { IStringKeyMap } from '#/types/types' 5 | 6 | export const useManageStore = defineStore('manageConfig', { 7 | state: () => { 8 | return { 9 | config: {} as IStringKeyMap 10 | } 11 | }, 12 | actions: { 13 | async refreshConfig() { 14 | this.config = (await getConfig()) ?? {} 15 | } 16 | }, 17 | persist: true 18 | }) 19 | 20 | export const useFileTransferStore = defineStore('fileTransfer', { 21 | state: () => { 22 | return { 23 | fileTransferList: [] as IStringKeyMap[], 24 | success: false, 25 | finished: false 26 | } 27 | }, 28 | actions: { 29 | refreshFileTransferList(newData: IStringKeyMap) { 30 | this.fileTransferList = newData.fullList ?? [] 31 | this.success = newData.success 32 | this.finished = newData.finished 33 | }, 34 | resetFileTransferList() { 35 | this.fileTransferList = [] 36 | this.success = false 37 | this.finished = false 38 | }, 39 | getFileTransferList() { 40 | return this.fileTransferList 41 | }, 42 | isFinished() { 43 | return this.finished 44 | }, 45 | isSuccess() { 46 | return this.success 47 | } 48 | } 49 | }) 50 | 51 | export const useDownloadFileTransferStore = defineStore('downloadFileTransfer', { 52 | state: () => { 53 | return { 54 | downloadFileTransferList: [] as IStringKeyMap[], 55 | success: false, 56 | finished: false 57 | } 58 | }, 59 | actions: { 60 | refreshDownloadFileTransferList(newData: IStringKeyMap) { 61 | this.downloadFileTransferList = newData.fullList ?? [] 62 | this.success = newData.success 63 | this.finished = newData.finished 64 | }, 65 | resetDownloadFileTransferList() { 66 | this.downloadFileTransferList = [] 67 | this.success = false 68 | this.finished = false 69 | }, 70 | getDownloadFileTransferList() { 71 | return this.downloadFileTransferList 72 | }, 73 | isFinished() { 74 | return this.finished 75 | }, 76 | isSuccess() { 77 | return this.success 78 | } 79 | } 80 | }) 81 | --------------------------------------------------------------------------------