Perf(custom): reduce idle memory usage by 60 percent

This commit is contained in:
Kuingsmile
2026-01-19 21:56:36 +08:00
parent ddb89496aa
commit f129018362
44 changed files with 451 additions and 597 deletions

View File

@@ -109,8 +109,7 @@ export function createMenu() {
export function createContextMenu() { export function createContextMenu() {
const ClipboardWatcher = clipboardPoll const ClipboardWatcher = clipboardPoll
const isListeningClipboard = picgo.getConfig<boolean | undefined>(configPaths.settings.isListeningClipboard) || false const isListeningClipboard = picgo.getConfig<boolean | undefined>(configPaths.settings.isListeningClipboard) || false
const isMiniWindowVisible = const isMiniWindowVisible = windowManager.get(IWindowList.MINI_WINDOW)?.isVisible() || false
windowManager.has(IWindowList.MINI_WINDOW) && windowManager.get(IWindowList.MINI_WINDOW)!.isVisible()
const startWatchClipboard = () => { const startWatchClipboard = () => {
picgo.saveConfig({ [configPaths.settings.isListeningClipboard]: true }) picgo.saveConfig({ [configPaths.settings.isListeningClipboard]: true })
@@ -236,11 +235,9 @@ export function createTray(tooltip: string) {
// click事件在Mac和Windows上可以触发在Ubuntu上无法触发Unity不支持 // click事件在Mac和Windows上可以触发在Ubuntu上无法触发Unity不支持
if (process.platform === 'darwin' || process.platform === 'win32') { if (process.platform === 'darwin' || process.platform === 'win32') {
tray.on('right-click', () => { tray.on('right-click', () => {
if (windowManager.has(IWindowList.TRAY_WINDOW)) { windowManager.get(IWindowList.TRAY_WINDOW)?.hide()
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
}
createContextMenu() createContextMenu()
tray!.popUpContextMenu(contextMenu!) tray?.popUpContextMenu(contextMenu!)
}) })
tray.on('click', (_, bounds) => { tray.on('click', (_, bounds) => {
@@ -277,33 +274,29 @@ export function createTray(tooltip: string) {
}) })
} }
} }
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('clipboardFiles', obj) windowManager.get(IWindowList.TRAY_WINDOW)?.webContents.send('clipboardFiles', obj)
}, 0) }, 0)
} else { } else {
if (windowManager.has(IWindowList.TRAY_WINDOW)) { windowManager.get(IWindowList.TRAY_WINDOW)?.hide()
windowManager.get(IWindowList.TRAY_WINDOW)!.hide()
}
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
const autoCloseMiniWindow = const autoCloseMiniWindow =
picgo.getConfig<boolean | undefined>(configPaths.settings.autoCloseMiniWindow) || false picgo.getConfig<boolean | undefined>(configPaths.settings.autoCloseMiniWindow) || false
settingWindow!.show() if (autoCloseMiniWindow) {
settingWindow!.focus() windowManager.get(IWindowList.MINI_WINDOW)?.close()
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) {
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
} }
windowManager.create(IWindowList.SETTING_WINDOW)
} }
}) })
tray.on('drag-enter', () => { tray.on('drag-enter', () => {
if (nativeTheme.shouldUseDarkColors) { if (nativeTheme.shouldUseDarkColors) {
tray!.setImage(uploadDarkPng) tray?.setImage(uploadDarkPng)
} else { } else {
tray!.setImage(uploadPng) tray?.setImage(uploadPng)
} }
}) })
tray.on('drag-end', () => { tray.on('drag-end', () => {
tray!.setImage(getTrayIcon()) tray?.setImage(getTrayIcon())
}) })
// drop-files only be supported in macOS // drop-files only be supported in macOS
@@ -313,8 +306,8 @@ export function createTray(tooltip: string) {
const allConfig = picgo.getConfig<any>() || {} const allConfig = picgo.getConfig<any>() || {}
const pasteStyle = allConfig.settings?.pasteStyle || IPasteStyle.MARKDOWN const pasteStyle = allConfig.settings?.pasteStyle || IPasteStyle.MARKDOWN
const rawInput = cloneDeep(files) const rawInput = cloneDeep(files)
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
const res = await uploader.setWebContents(trayWindow.webContents).uploadReturnCtx(files) const res = await uploader.setWebContents(trayWindow?.webContents).uploadReturnCtx(files)
const imgs = res[0] ? res[0] : false const imgs = res[0] ? res[0] : false
const backImgs = res[1] ? res[1] : false const backImgs = res[1] ? res[1] : false
const deleteLocalFile = allConfig.settings?.deleteLocalFile || false const deleteLocalFile = allConfig.settings?.deleteLocalFile || false
@@ -344,33 +337,35 @@ export function createTray(tooltip: string) {
await GalleryDB.getInstance().insert(imgs[i]) await GalleryDB.getInstance().insert(imgs[i])
} }
handleCopyUrl(pasteText.join('\n')) handleCopyUrl(pasteText.join('\n'))
trayWindow.webContents.send('dragFiles', imgs) trayWindow?.webContents.send('dragFiles', imgs)
} }
if (backImgs !== false) { if (backImgs !== false) {
for (const backImg of backImgs) { for (const backImg of backImgs) {
await GalleryDB.getInstance().insert(backImg) await GalleryDB.getInstance().insert(backImg)
} }
trayWindow.webContents.send('dragFiles', backImgs) trayWindow?.webContents.send('dragFiles', backImgs)
} }
}) })
} }
// toggleWindow()
} else if (process.platform === 'linux') { } else if (process.platform === 'linux') {
// click事件在Ubuntu上无法触发Unity不支持在Mac和Windows上可以触发 // click事件在Ubuntu上无法触发Unity不支持在Mac和Windows上可以触发
// 需要使用 setContextMenu 设置菜单 // 需要使用 setContextMenu 设置菜单
createContextMenu() createContextMenu()
tray!.setContextMenu(contextMenu) tray?.setContextMenu(contextMenu)
} }
} }
const toggleWindow = (bounds: IBounds) => { const toggleWindow = (bounds: IBounds) => {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! let trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
if (trayWindow.isVisible()) { if (!trayWindow) {
trayWindow = windowManager.create(IWindowList.TRAY_WINDOW)
}
if (trayWindow?.isVisible()) {
trayWindow.hide() trayWindow.hide()
} else { } else {
trayWindow.setPosition(bounds.x - 98 + 11, bounds.y, false) trayWindow?.setPosition(bounds.x - 98 + 11, bounds.y, false)
trayWindow.webContents.send('updateFiles') trayWindow?.webContents.send('updateFiles')
trayWindow.show() trayWindow?.show()
trayWindow.focus() trayWindow?.focus()
} }
} }

View File

@@ -3,13 +3,14 @@ import path from 'node:path'
import { themesDir } from '@core/datastore/dirs' import { themesDir } from '@core/datastore/dirs'
import * as fsWalk from '@nodelib/fs.walk' import * as fsWalk from '@nodelib/fs.walk'
import AdmZip from 'adm-zip' import AdmZip from 'adm-zip'
import windowManager from 'apis/app/window/windowManager'
import axios from 'axios' import axios from 'axios'
import fs from 'fs-extra' import fs from 'fs-extra'
import { randomStringGenerator } from '@/manage/utils/common' import { randomStringGenerator } from '@/manage/utils/common'
import { IWindowList } from '~/utils/enum' import { IWindowList } from '~/utils/enum'
import windowManager from '../window/windowManager'
let insertedCSSKeyMain: string | undefined let insertedCSSKeyMain: string | undefined
export async function resolveThemes(): Promise<{ key: string; label: string }[]> { export async function resolveThemes(): Promise<{ key: string; label: string }[]> {
@@ -75,12 +76,11 @@ export async function readTheme(theme: string): Promise<string> {
export async function applyTheme(theme: string): Promise<void> { export async function applyTheme(theme: string): Promise<void> {
theme = path.basename(theme) theme = path.basename(theme)
const css = await readTheme(theme) const css = await readTheme(theme)
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
try { try {
await windowManager.get(IWindowList.SETTING_WINDOW)?.webContents.removeInsertedCSS(insertedCSSKeyMain || '') const window = windowManager.get(IWindowList.SETTING_WINDOW)
insertedCSSKeyMain = await windowManager.get(IWindowList.SETTING_WINDOW)?.webContents.insertCSS(css) await window?.webContents.removeInsertedCSS(insertedCSSKeyMain || '')
insertedCSSKeyMain = await window?.webContents.insertCSS(css)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
} }
}

View File

@@ -17,9 +17,9 @@ const handleClipboardUploadingReturnCtx = async (img?: IUploadOption): Promise<(
const useBuiltinClipboard = useBuiltinClipboardConfig === undefined ? true : !!useBuiltinClipboardConfig const useBuiltinClipboard = useBuiltinClipboardConfig === undefined ? true : !!useBuiltinClipboardConfig
const win = windowManager.getAvailableWindow() const win = windowManager.getAvailableWindow()
if (useBuiltinClipboard) { if (useBuiltinClipboard) {
return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboardReturnCtx(img) return await uploader.setWebContents(win?.webContents).uploadWithBuildInClipboardReturnCtx(img)
} }
return await uploader.setWebContents(win!.webContents).uploadReturnCtx(img) return await uploader.setWebContents(win?.webContents).uploadReturnCtx(img)
} }
export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => { export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
@@ -31,7 +31,6 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
const allConfig = picgo.getConfig<any>() || {} const allConfig = picgo.getConfig<any>() || {}
if (img !== false) { if (img !== false) {
if (img.length > 0) { if (img.length > 0) {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
const pasteStyle = allConfig.settings?.pasteStyle || IPasteStyle.MARKDOWN const pasteStyle = allConfig.settings?.pasteStyle || IPasteStyle.MARKDOWN
const [pastedText, shortUrl] = await pasteTemplate(pasteStyle, img[0], allConfig.settings?.customLink) const [pastedText, shortUrl] = await pasteTemplate(pasteStyle, img[0], allConfig.settings?.customLink)
img[0].shortUrl = shortUrl img[0].shortUrl = shortUrl
@@ -52,17 +51,15 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
} }
const inserted = await GalleryDB.getInstance().insert(img[0]) const inserted = await GalleryDB.getInstance().insert(img[0])
// trayWindow just be created in mac/windows, not in linux // trayWindow just be created in mac/windows, not in linux
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
trayWindow?.webContents?.send('clipboardFiles', []) trayWindow?.webContents?.send('clipboardFiles', [])
trayWindow?.webContents?.send('uploadFiles') trayWindow?.webContents?.send('uploadFiles')
if (windowManager.has(IWindowList.SETTING_WINDOW)) { const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') settingWindow?.webContents?.send('updateGallery')
}
if (backImg !== false) { if (backImg !== false) {
await GalleryDB.getInstance().insert(backImg[0]) await GalleryDB.getInstance().insert(backImg[0])
trayWindow?.webContents?.send('uploadFiles') trayWindow?.webContents?.send('uploadFiles')
if (windowManager.has(IWindowList.SETTING_WINDOW)) { settingWindow?.webContents?.send('updateGallery')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
}
} }
return { return {
url: handleUrlEncodeWithSetting(inserted.imgUrl as string), url: handleUrlEncodeWithSetting(inserted.imgUrl as string),
@@ -88,7 +85,7 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
} }
export const uploadChoosedFiles = async ( export const uploadChoosedFiles = async (
webContents: WebContents, webContents: WebContents | undefined,
files: IFileWithPath[], files: IFileWithPath[],
): Promise<IStringKeyMap[]> => { ): Promise<IStringKeyMap[]> => {
const input = files.map(item => item.path) const input = files.map(item => item.path)
@@ -150,18 +147,16 @@ export const uploadChoosedFiles = async (
} }
handleCopyUrl(pasteText.join('\n')) handleCopyUrl(pasteText.join('\n'))
// trayWindow just be created in mac/windows, not in linux // trayWindow just be created in mac/windows, not in linux
windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles') const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
if (windowManager.has(IWindowList.SETTING_WINDOW)) { trayWindow?.webContents?.send('uploadFiles')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
} settingWindow?.webContents?.send('updateGallery')
if (backImgs !== false) { if (backImgs !== false) {
for (const backImg of backImgs) { for (const backImg of backImgs) {
await GalleryDB.getInstance().insert(backImg) await GalleryDB.getInstance().insert(backImg)
} }
windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles') trayWindow?.webContents?.send('uploadFiles')
if (windowManager.has(IWindowList.SETTING_WINDOW)) { settingWindow?.webContents?.send('updateGallery')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
}
} }
return result return result
} else { } else {

View File

@@ -17,22 +17,22 @@ import { configPaths } from '~/utils/configPaths'
import { ICOREBuildInEvent, IWindowList } from '~/utils/enum' import { ICOREBuildInEvent, IWindowList } from '~/utils/enum'
import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static' import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static'
const waitForRename = (window: BrowserWindow, id: number): Promise<string | null> => { const waitForRename = (window: BrowserWindow | undefined, id: number | undefined): Promise<string | null> => {
return new Promise(resolve => { return new Promise(resolve => {
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: IpcMainEvent, newName: string) => { ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: IpcMainEvent, newName: string) => {
resolve(newName) resolve(newName)
window.close() window?.close()
}) })
window.on('close', () => { window?.on('close', () => {
resolve(null) resolve(null)
ipcMain.removeAllListeners(`${RENAME_FILE_NAME}${id}`) ipcMain.removeAllListeners(`${RENAME_FILE_NAME}${id}`)
windowManager.deleteById(window.id) windowManager.deleteById(window?.id)
}) })
}) })
} }
class Uploader { class Uploader {
private webContents: WebContents | null = null private webContents: WebContents | undefined = undefined
constructor() { constructor() {
this.init() this.init()
@@ -77,10 +77,10 @@ class Uploader {
? `${dayjs().add(index, 'ms').format('YYYYMMDDHHmmssSSS')}${item.extname}` ? `${dayjs().add(index, 'ms').format('YYYYMMDDHHmmssSSS')}${item.extname}`
: item.fileName : item.fileName
if (rename) { if (rename) {
const window = windowManager.create(IWindowList.RENAME_WINDOW)! const window = windowManager.create(IWindowList.RENAME_WINDOW)
ipcMain.on(GET_RENAME_FILE_NAME, (evt, _) => { ipcMain.on(GET_RENAME_FILE_NAME, (evt, _) => {
try { try {
if (evt.sender.id === window.webContents.id) { if (evt.sender.id === window?.webContents.id) {
logger.info('rename window ready, wait for rename...') logger.info('rename window ready, wait for rename...')
window.webContents.send(RENAME_FILE_NAME, fileName, item.fileName, window.webContents.id) window.webContents.send(RENAME_FILE_NAME, fileName, item.fileName, window.webContents.id)
} }
@@ -88,7 +88,7 @@ class Uploader {
logger.error(e) logger.error(e)
} }
}) })
name = await waitForRename(window, window.webContents.id) name = await waitForRename(window, window?.webContents.id)
} }
item.fileName = name || fileName item.fileName = name || fileName
}), }),
@@ -98,7 +98,7 @@ class Uploader {
}) })
} }
setWebContents(webContents: WebContents) { setWebContents(webContents: WebContents | undefined) {
this.webContents = webContents this.webContents = webContents
return this return this
} }

View File

@@ -12,7 +12,6 @@ import { configPaths } from '~/utils/configPaths'
import { IWindowList } from '~/utils/enum' import { IWindowList } from '~/utils/enum'
import logo from '../../../../../resources/logo.png?asset&asarUnpack' import logo from '../../../../../resources/logo.png?asset&asarUnpack'
import { applyTheme } from '../theme'
const windowList = new Map<string, IWindowListItem>() const windowList = new Map<string, IWindowListItem>()
@@ -182,7 +181,10 @@ windowList.set(IWindowList.TRAY_WINDOW, {
window.loadFile(path.join(dirname, '../renderer/index.html')) window.loadFile(path.join(dirname, '../renderer/index.html'))
} }
window.on('blur', () => { window.on('blur', () => {
window.hide() window.close()
})
window.on('closed', () => {
window = null as unknown as Electron.BrowserWindow
}) })
}, },
}) })
@@ -191,7 +193,7 @@ windowList.set(IWindowList.SETTING_WINDOW, {
isValid: true, isValid: true,
multiple: false, multiple: false,
options: () => settingWindowOptions, options: () => settingWindowOptions,
callback(window, windowManager) { callback(window) {
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) { if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#main-page/upload`) window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#main-page/upload`)
} else { } else {
@@ -201,18 +203,13 @@ windowList.set(IWindowList.SETTING_WINDOW, {
} }
window.on('closed', () => { window.on('closed', () => {
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false) bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
if (process.platform === 'linux') { window = null as unknown as Electron.BrowserWindow
process.nextTick(() => {
app.quit()
}) })
} window.once('ready-to-show', async () => {
}) window.show()
window.on('ready-to-show', () => { window.focus()
const customTheme = picgo.getConfig<string>(configPaths.settings.theme) || 'default.css'
applyTheme(customTheme)
}) })
bus.emit(CREATE_APP_MENU) bus.emit(CREATE_APP_MENU)
windowManager.create(IWindowList.MINI_WINDOW)
}, },
}) })
@@ -220,7 +217,8 @@ windowList.set(IWindowList.MINI_WINDOW, {
isValid: process.platform !== 'darwin', isValid: process.platform !== 'darwin',
multiple: false, multiple: false,
options: () => miniWindowOptions, options: () => miniWindowOptions,
callback(window) { callback(window, windowManager) {
const id = window.id
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) { if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#mini-page`) window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#mini-page`)
} else { } else {
@@ -228,6 +226,10 @@ windowList.set(IWindowList.MINI_WINDOW, {
hash: 'mini-page', hash: 'mini-page',
}) })
} }
window.on('closed', () => {
windowManager.deleteById(id)
window = null as unknown as Electron.BrowserWindow
})
}, },
}) })
@@ -250,6 +252,9 @@ windowList.set(IWindowList.RENAME_WINDOW, {
const positionY = Math.floor(y + height / 2 - (height > 400 ? 88 : 0)) const positionY = Math.floor(y + height / 2 - (height > 400 ? 88 : 0))
window.setPosition(positionX, positionY, false) window.setPosition(positionX, positionY, false)
} }
window.on('closed', () => {
window = null as unknown as Electron.BrowserWindow
})
}, },
}) })
@@ -272,6 +277,13 @@ windowList.set(IWindowList.TOOLBOX_WINDOW, {
const positionY = Math.floor(y + height / 2 - (height > 400 ? 225 : 0)) const positionY = Math.floor(y + height / 2 - (height > 400 ? 225 : 0))
window.setPosition(positionX, positionY, false) window.setPosition(positionX, positionY, false)
} }
window.once('ready-to-show', () => {
window.show()
window.focus()
})
window.on('closed', () => {
window = null as unknown as Electron.BrowserWindow
})
}, },
}) })
@@ -322,6 +334,9 @@ windowList.set(IWindowList.UPDATE_WINDOW, {
const positionY = Math.floor(y + height / 2 - 300) const positionY = Math.floor(y + height / 2 - 300)
window.setPosition(positionX, positionY, false) window.setPosition(positionX, positionY, false)
} }
window.on('closed', () => {
window = null as unknown as Electron.BrowserWindow
})
}, },
}) })

View File

@@ -9,10 +9,15 @@ class WindowManager implements IWindowManager {
create(name: string) { create(name: string) {
const windowConfig: IWindowListItem = windowList.get(name)! const windowConfig: IWindowListItem = windowList.get(name)!
if (!windowConfig.isValid) return null if (!windowConfig.isValid) return undefined
if (!windowConfig.multiple) { if (!windowConfig.multiple) {
if (this.has(name)) return this.#windowMap.get(name)! const existingWin = this.#windowMap.get(name)
if (existingWin) {
if (existingWin.isMinimized()) existingWin.restore()
existingWin.focus()
return existingWin
}
} }
const window = new BrowserWindow(windowConfig.options()) const window = new BrowserWindow(windowConfig.options())
@@ -30,17 +35,15 @@ class WindowManager implements IWindowManager {
} }
get(name: string) { get(name: string) {
if (this.has(name)) { return this.#windowMap.get(name) || undefined
return this.#windowMap.get(name)!
}
return this.create(name)
} }
has(name: string) { has(name: string) {
return this.#windowMap.has(name) return this.#windowMap.has(name)
} }
deleteById = (id: number) => { deleteById = (id: number | undefined) => {
if (id === undefined) return
const name = this.#windowIdMap.get(id) const name = this.#windowIdMap.get(id)
if (name) { if (name) {
this.#windowMap.delete(name) this.#windowMap.delete(name)
@@ -59,8 +62,7 @@ class WindowManager implements IWindowManager {
const trayWindow = this.#windowMap.get(IWindowList.TRAY_WINDOW) const trayWindow = this.#windowMap.get(IWindowList.TRAY_WINDOW)
if (trayWindow) return trayWindow if (trayWindow) return trayWindow
return undefined
return this.create(IWindowList.SETTING_WINDOW)!
} }
} }

View File

@@ -38,19 +38,19 @@ async function busCallUploadClipboardFiles() {
async function busCallUploadFiles(pathList: IFileWithPath[]) { async function busCallUploadFiles(pathList: IFileWithPath[]) {
const win = windowManager.getAvailableWindow() const win = windowManager.getAvailableWindow()
const result = await uploadChoosedFiles(win.webContents, pathList) const result = await uploadChoosedFiles(win?.webContents, pathList)
const urls = result.map((item: any) => item.url) const urls = result.map((item: any) => item.url)
bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls) bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls)
} }
function busCallGetWindowId() { function busCallGetWindowId() {
const win = windowManager.getAvailableWindow() const win = windowManager.getAvailableWindow()
bus.emit(GET_WINDOW_ID_REPONSE, win.id) bus.emit(GET_WINDOW_ID_REPONSE, win?.id)
} }
function busCallGetSettingWindowId() { function busCallGetSettingWindowId() {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
bus.emit(GET_SETTING_WINDOW_ID_RESPONSE, settingWindow.id) bus.emit(GET_SETTING_WINDOW_ID_RESPONSE, settingWindow?.id)
} }
export default { export default {

View File

@@ -52,7 +52,10 @@ const buildMiniPageMenu = () => {
{ {
label: $t('HIDE_MINI_WINDOW'), label: $t('HIDE_MINI_WINDOW'),
click() { click() {
BrowserWindow.getFocusedWindow()!.hide() const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
console.log('hide mini window', miniWindow)
miniWindow?.close()
console.log('mini window closed')
}, },
}, },
{ {
@@ -93,7 +96,7 @@ const buildMiniPageMenu = () => {
return Menu.buildFromTemplate(template) return Menu.buildFromTemplate(template)
} }
const buildMainPageMenu = (win: BrowserWindow) => { const buildMainPageMenu = (win: BrowserWindow | undefined) => {
const template = [ const template = [
{ {
label: $t('ABOUT'), label: $t('ABOUT'),
@@ -121,8 +124,7 @@ const buildMainPageMenu = (win: BrowserWindow) => {
{ {
label: $t('OPEN_TOOLBOX'), label: $t('OPEN_TOOLBOX'),
click() { click() {
const window = windowManager.create(IWindowList.TOOLBOX_WINDOW) windowManager.create(IWindowList.TOOLBOX_WINDOW)
window?.show()
}, },
}, },
{ {
@@ -234,9 +236,7 @@ const buildPicBedListMenu = () => {
checked: config._id === defaultId && item.type === currentPicBed, checked: config._id === defaultId && item.type === currentPicBed,
click() { click() {
changeCurrentUploader(item.type, config, config._id) changeCurrentUploader(item.type, config, config._id)
if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send('syncPicBed')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
setTrayToolTip(`${item.type} ${config._configName || 'Default'}`) setTrayToolTip(`${item.type} ${config._configName || 'Default'}`)
}, },
} }
@@ -248,9 +248,7 @@ const buildPicBedListMenu = () => {
[configPaths.picBed.current]: item.type, [configPaths.picBed.current]: item.type,
[configPaths.picBed.uploader]: item.type, [configPaths.picBed.uploader]: item.type,
}) })
if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send('syncPicBed')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
}
setTrayToolTip(item.type) setTrayToolTip(item.type)
} }
: undefined, : undefined,
@@ -293,8 +291,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
picgo.saveConfig({ picgo.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: true, [`picgoPlugins.${plugin.fullName}`]: true,
}) })
const window = windowManager.get(IWindowList.SETTING_WINDOW)! windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, true)
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, true)
}, },
}, },
{ {
@@ -304,10 +301,10 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
picgo.saveConfig({ picgo.saveConfig({
[`picgoPlugins.${plugin.fullName}`]: false, [`picgoPlugins.${plugin.fullName}`]: false,
}) })
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName) window?.webContents?.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, false) window?.webContents?.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, false)
window.webContents.send(PICGO_HANDLE_PLUGIN_DONE, plugin.fullName) window?.webContents?.send(PICGO_HANDLE_PLUGIN_DONE, plugin.fullName)
if (plugin.config.transformer.name) { if (plugin.config.transformer.name) {
handleRestoreState('transformer', plugin.config.transformer.name) handleRestoreState('transformer', plugin.config.transformer.name)
} }
@@ -319,16 +316,16 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
{ {
label: $t('UNINSTALL_PLUGIN'), label: $t('UNINSTALL_PLUGIN'),
click() { click() {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName) window?.webContents?.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
handlePluginUninstall(plugin.fullName) handlePluginUninstall(plugin.fullName)
}, },
}, },
{ {
label: $t('UPDATE_PLUGIN'), label: $t('UPDATE_PLUGIN'),
click() { click() {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName) window?.webContents?.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
handlePluginUpdate(plugin.fullName) handlePluginUpdate(plugin.fullName)
}, },
}, },
@@ -340,11 +337,11 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
c: `${i} - ${plugin.config[i].fullName || plugin.config[i].name}`, c: `${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
}), }),
click() { click() {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const currentType = i const currentType = i
const configName = plugin.config[i].fullName || plugin.config[i].name const configName = plugin.config[i].fullName || plugin.config[i].name
const config = plugin.config[i].config const config = plugin.config[i].config
window.webContents.send(PICGO_CONFIG_PLUGIN, currentType, configName, config) window?.webContents?.send(PICGO_CONFIG_PLUGIN, currentType, configName, config)
}, },
} }
menu.push(obj) menu.push(obj)

View File

@@ -125,27 +125,27 @@ const handleNPMError = (): IDispose => {
} }
export const handlePluginUpdate = async (fullName: string | string[]) => { export const handlePluginUpdate = async (fullName: string | string[]) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const dispose = handleNPMError() const dispose = handleNPMError()
const res = await picgo.pluginHandler.update(typeof fullName === 'string' ? [fullName] : fullName) const res = await picgo.pluginHandler.update(typeof fullName === 'string' ? [fullName] : fullName)
if (res.success) { if (res.success) {
window.webContents.send('updateSuccess', res.body[0]) window?.webContents?.send('updateSuccess', res.body[0])
} else { } else {
showNotification({ showNotification({
title: $t('PLUGIN_UPDATE_FAILED'), title: $t('PLUGIN_UPDATE_FAILED'),
body: res.body as string, body: res.body as string,
}) })
} }
window.webContents.send('hideLoading') window?.webContents.send('hideLoading')
dispose() dispose()
} }
export const handlePluginUninstall = async (fullName: string) => { export const handlePluginUninstall = async (fullName: string) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const dispose = handleNPMError() const dispose = handleNPMError()
const res = await picgo.pluginHandler.uninstall([fullName]) const res = await picgo.pluginHandler.uninstall([fullName])
if (res.success) { if (res.success) {
window.webContents.send('uninstallSuccess', res.body[0]) window?.webContents?.send('uninstallSuccess', res.body[0])
shortKeyHandler.unregisterPluginShortKey(res.body[0]) shortKeyHandler.unregisterPluginShortKey(res.body[0])
} else { } else {
showNotification({ showNotification({
@@ -153,7 +153,7 @@ export const handlePluginUninstall = async (fullName: string) => {
body: res.body as string, body: res.body as string,
}) })
} }
window.webContents.send('hideLoading') window?.webContents?.send('hideLoading')
dispose() dispose()
} }
@@ -195,7 +195,8 @@ export const pluginInstallFunc = async (event: IIPCEvent, args: [fullName: strin
} }
export const pluginImportLocalFunc = async (event: IIPCEvent) => { export const pluginImportLocalFunc = async (event: IIPCEvent) => {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
if (!settingWindow) return
const res = await dialog.showOpenDialog(settingWindow, { const res = await dialog.showOpenDialog(settingWindow, {
properties: ['openDirectory'], properties: ['openDirectory'],
}) })

View File

@@ -39,7 +39,6 @@ export default [
action: IRPCActionType.CONFIGURE_MIGRATE_FROM_PICLIST_INSTALLATION, action: IRPCActionType.CONFIGURE_MIGRATE_FROM_PICLIST_INSTALLATION,
handler: async () => { handler: async () => {
const configDir = app.getPath('userData') const configDir = app.getPath('userData')
console.log('Migrating from PicList installation at:', configDir)
const files = [ const files = [
'data.json', 'data.json',
'data.bak.json', 'data.bak.json',

View File

@@ -2,8 +2,9 @@ import { isPortable } from '@core/datastore/dirs'
import picgo from '@core/picgo' import picgo from '@core/picgo'
import { app, nativeTheme, shell } from 'electron' import { app, nativeTheme, shell } from 'electron'
import { applyTheme, fetchThemes, importThemes, resolveThemes } from '~/apis/app/theme' import { applyTheme, fetchThemes, importThemes, readTheme, resolveThemes } from '~/apis/app/theme'
import { i18nManager } from '~/i18n' import { i18nManager } from '~/i18n'
import { configPaths } from '~/utils/configPaths'
import { IRPCActionType, IRPCType } from '~/utils/enum' import { IRPCActionType, IRPCType } from '~/utils/enum'
export default [ export default [
@@ -88,4 +89,20 @@ export default [
}, },
type: IRPCType.INVOKE, type: IRPCType.INVOKE,
}, },
{
action: IRPCActionType.THEME_GET_BOOTSTRAP,
handler: async () => {
let savedMode = picgo.getConfig<string>(configPaths.settings.systemTheme) || 'system'
const customTheme = picgo.getConfig<string>(configPaths.settings.theme) || 'default.css'
if (savedMode === 'system') {
savedMode = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
}
const theme = await readTheme(customTheme)
return {
mode: savedMode,
css: theme,
}
},
type: IRPCType.INVOKE,
},
] ]

View File

@@ -22,9 +22,7 @@ export default [
action: IRPCActionType.OPEN_WINDOW, action: IRPCActionType.OPEN_WINDOW,
handler: async (_: IIPCEvent, args: [windowName: string]) => { handler: async (_: IIPCEvent, args: [windowName: string]) => {
const window = windowManager.get(args[0]) const window = windowManager.get(args[0])
if (window) { window?.show()
window.show()
}
}, },
}, },
{ {
@@ -37,11 +35,7 @@ export default [
action: IRPCActionType.CLOSE_WINDOW, action: IRPCActionType.CLOSE_WINDOW,
handler: async () => { handler: async () => {
const window = BrowserWindow.getFocusedWindow() const window = BrowserWindow.getFocusedWindow()
if (process.platform === 'linux') {
window?.hide()
} else {
window?.close() window?.close()
}
}, },
}, },
{ {
@@ -54,7 +48,7 @@ export default [
{ {
action: IRPCActionType.SHOW_MINI_PAGE_MENU, action: IRPCActionType.SHOW_MINI_PAGE_MENU,
handler: async () => { handler: async () => {
const window = windowManager.get(IWindowList.MINI_WINDOW)! const window = windowManager.get(IWindowList.MINI_WINDOW)
const menu = buildMiniPageMenu() const menu = buildMiniPageMenu()
menu.popup({ menu.popup({
window, window,
@@ -64,7 +58,7 @@ export default [
{ {
action: IRPCActionType.SHOW_MAIN_PAGE_MENU, action: IRPCActionType.SHOW_MAIN_PAGE_MENU,
handler: async () => { handler: async () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const menu = buildMainPageMenu(window) const menu = buildMainPageMenu(window)
menu.popup({ menu.popup({
window, window,
@@ -74,7 +68,7 @@ export default [
{ {
action: IRPCActionType.SHOW_UPLOAD_PAGE_MENU, action: IRPCActionType.SHOW_UPLOAD_PAGE_MENU,
handler: async () => { handler: async () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const menu = buildPicBedListMenu() const menu = buildPicBedListMenu()
menu.popup({ menu.popup({
window, window,
@@ -84,7 +78,7 @@ export default [
{ {
action: IRPCActionType.SHOW_SECOND_UPLOADER_MENU, action: IRPCActionType.SHOW_SECOND_UPLOADER_MENU,
handler: async () => { handler: async () => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const menu = buildSecondPicBedMenu() const menu = buildSecondPicBedMenu()
menu.popup({ menu.popup({
window, window,
@@ -94,7 +88,7 @@ export default [
{ {
action: IRPCActionType.SHOW_PLUGIN_PAGE_MENU, action: IRPCActionType.SHOW_PLUGIN_PAGE_MENU,
handler: async (_: IIPCEvent, args: [plugin: IPicGoPlugin]) => { handler: async (_: IIPCEvent, args: [plugin: IPicGoPlugin]) => {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const menu = buildPluginPageMenu(args[0]) const menu = buildPluginPageMenu(args[0])
menu.popup({ menu.popup({
window, window,
@@ -111,30 +105,30 @@ export default [
{ {
action: IRPCActionType.MINI_WINDOW_ON_TOP, action: IRPCActionType.MINI_WINDOW_ON_TOP,
handler: async (_: IIPCEvent, args: [isOnTop: boolean]) => { handler: async (_: IIPCEvent, args: [isOnTop: boolean]) => {
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
miniWindow.setAlwaysOnTop(args[0]) miniWindow?.setAlwaysOnTop(args[0])
}, },
}, },
{ {
action: IRPCActionType.MAIN_WINDOW_ON_TOP, action: IRPCActionType.MAIN_WINDOW_ON_TOP,
handler: async () => { handler: async () => {
const mainWindow = windowManager.get(IWindowList.SETTING_WINDOW)! const mainWindow = windowManager.get(IWindowList.SETTING_WINDOW)
const isAlwaysOnTop = mainWindow.isAlwaysOnTop() const isAlwaysOnTop = mainWindow?.isAlwaysOnTop()
mainWindow.setAlwaysOnTop(!isAlwaysOnTop) mainWindow?.setAlwaysOnTop(!isAlwaysOnTop)
}, },
}, },
{ {
action: IRPCActionType.UPDATE_MINI_WINDOW_ICON, action: IRPCActionType.UPDATE_MINI_WINDOW_ICON,
handler: async (_: IIPCEvent, args: [iconPath: string]) => { handler: async (_: IIPCEvent, args: [iconPath: string]) => {
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
miniWindow.webContents.send('updateMiniIcon', args[0]) miniWindow?.webContents?.send('updateMiniIcon', args[0])
}, },
}, },
{ {
action: IRPCActionType.REFRESH_SETTING_WINDOW, action: IRPCActionType.REFRESH_SETTING_WINDOW,
handler: async () => { handler: async () => {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
settingWindow.webContents.session.clearCache().then(() => { settingWindow?.webContents.session.clearCache().then(() => {
settingWindow.webContents.reloadIgnoringCache() settingWindow.webContents.reloadIgnoringCache()
}) })
}, },

View File

@@ -29,9 +29,9 @@ const trayRoutes = [
{ {
action: IRPCActionType.TRAY_UPLOAD_CLIPBOARD_FILES, action: IRPCActionType.TRAY_UPLOAD_CLIPBOARD_FILES,
handler: async () => { handler: async () => {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
// macOS use builtin clipboard is OK // macOS use builtin clipboard is OK
const res = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboardReturnCtx() const res = await uploader.setWebContents(trayWindow?.webContents).uploadWithBuildInClipboardReturnCtx()
const img = res[0] ? res[0] : false const img = res[0] ? res[0] : false
const backupImgs = res[1] ? res[1] : false const backupImgs = res[1] ? res[1] : false
const allConfig = picgo.getConfig<any>() || {} const allConfig = picgo.getConfig<any>() || {}
@@ -54,19 +54,19 @@ const trayRoutes = [
notification.show() notification.show()
} }
await GalleryDB.getInstance().insert(img[0]) await GalleryDB.getInstance().insert(img[0])
trayWindow.webContents.send('clipboardFiles', []) trayWindow?.webContents.send('clipboardFiles', [])
if (windowManager.has(IWindowList.SETTING_WINDOW)) { if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery') windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
} }
if (backupImgs && backupImgs.length > 0) { if (backupImgs && backupImgs.length > 0) {
await GalleryDB.getInstance().insert(backupImgs[0]) await GalleryDB.getInstance().insert(backupImgs[0])
trayWindow.webContents.send('uploadFiles') trayWindow?.webContents.send('uploadFiles')
if (windowManager.has(IWindowList.SETTING_WINDOW)) { if (windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
} }
} }
} }
trayWindow.webContents.send('uploadFiles') trayWindow?.webContents.send('uploadFiles')
}, },
}, },
] ]

View File

@@ -65,15 +65,8 @@ const progressHandler = (progressObj: updater.ProgressInfo) => {
const percent = { const percent = {
progress: progressObj.percent, progress: progressObj.percent,
} }
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send('updateProgress', percent)
const updateWindow = windowManager.get(IWindowList.UPDATE_WINDOW) windowManager.get(IWindowList.UPDATE_WINDOW)?.webContents?.send('UPDATE_PROGRESS', percent)
if (settingWindow) {
settingWindow.webContents.send('updateProgress', percent)
}
if (updateWindow) {
updateWindow.webContents.send('UPDATE_PROGRESS', percent)
}
} }
const downloadedHandler = () => { const downloadedHandler = () => {
@@ -82,10 +75,10 @@ const downloadedHandler = () => {
if (!windowManager.has(IWindowList.UPDATE_WINDOW)) { if (!windowManager.has(IWindowList.UPDATE_WINDOW)) {
windowManager.create(IWindowList.UPDATE_WINDOW) windowManager.create(IWindowList.UPDATE_WINDOW)
} }
const updateWindow = windowManager.get(IWindowList.UPDATE_WINDOW)! const updateWindow = windowManager.get(IWindowList.UPDATE_WINDOW)
const sendUpdateInfo = () => { const sendUpdateInfo = () => {
updateWindow.webContents.send('SHOW_UPDATE_INFO', { updateWindow?.webContents.send('SHOW_UPDATE_INFO', {
type: 'update-downloaded', type: 'update-downloaded',
title: lang === II18nLanguage.ZH_CN ? '更新已下载' : 'Update Downloaded', title: lang === II18nLanguage.ZH_CN ? '更新已下载' : 'Update Downloaded',
message: message:
@@ -95,14 +88,14 @@ const downloadedHandler = () => {
}) })
} }
if (updateWindow.webContents.isLoading()) { if (updateWindow?.webContents.isLoading()) {
updateWindow.webContents.once('did-finish-load', sendUpdateInfo) updateWindow.webContents.once('did-finish-load', sendUpdateInfo)
} else { } else {
sendUpdateInfo() sendUpdateInfo()
} }
if (!updateWindow.isVisible()) { if (!updateWindow?.isVisible()) {
updateWindow.show() updateWindow?.show()
} }
} }

View File

@@ -1,6 +1,7 @@
import '~/lifeCycle/errorHandler' import '~/lifeCycle/errorHandler'
import path from 'node:path' import path from 'node:path'
import { pathToFileURL } from 'node:url'
import bus from '@core/bus' import bus from '@core/bus'
import picgo from '@core/picgo' import picgo from '@core/picgo'
@@ -10,9 +11,10 @@ import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import { createTray, setDockMenu } from 'apis/app/system' import { createTray, setDockMenu } from 'apis/app/system'
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis' import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
import windowManager from 'apis/app/window/windowManager' import windowManager from 'apis/app/window/windowManager'
import { app, globalShortcut, Notification, protocol, screen } from 'electron' import { app, globalShortcut, net, Notification, protocol, screen } from 'electron'
import fs from 'fs-extra' import fs from 'fs-extra'
import { themesDir } from '~/apis/core/datastore/dirs'
import busEventList from '~/events/busEventList' import busEventList from '~/events/busEventList'
import { rpcServer } from '~/events/rpc' import { rpcServer } from '~/events/rpc'
import { startFileServer, stopFileServer } from '~/fileServer' import { startFileServer, stopFileServer } from '~/fileServer'
@@ -50,7 +52,7 @@ const handleStartUpFiles = (argv: string[], cwd: string) => {
if (files.length > 0) { if (files.length > 0) {
logger.info('cli -> uploading files from cli', ...files.map(file => file.path)) logger.info('cli -> uploading files from cli', ...files.map(file => file.path))
const win = windowManager.getAvailableWindow() const win = windowManager.getAvailableWindow()
uploadChoosedFiles(win.webContents, files) uploadChoosedFiles(win?.webContents, files)
return true return true
} }
@@ -62,6 +64,9 @@ await setupAutoUpdater()
class LifeCycle { class LifeCycle {
async #beforeReady() { async #beforeReady() {
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }]) protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
protocol.registerSchemesAsPrivileged([
{ scheme: 'theme', privileges: { standard: true, secure: true, supportFetchAPI: true } },
])
// fix the $PATH in macOS & linux // fix the $PATH in macOS & linux
fixPath() fixPath()
beforeOpen() beforeOpen()
@@ -78,9 +83,16 @@ class LifeCycle {
#onReady() { #onReady() {
const readyFunction = async () => { const readyFunction = async () => {
protocol.handle('theme', request => {
const requestUrl = request.url
const urlObj = new URL(requestUrl)
const relativePath = urlObj.pathname
const themeBaseDir = path.join(themesDir())
const absolutePath = path.join(themeBaseDir, relativePath)
return net.fetch(pathToFileURL(absolutePath).toString())
})
const allConfig = picgo.getConfig<any>() || {} const allConfig = picgo.getConfig<any>() || {}
windowManager.create(IWindowList.TRAY_WINDOW) // clipboard monitor
windowManager.create(IWindowList.SETTING_WINDOW)
const isAutoListenClipboard = allConfig.settings?.isAutoListenClipboard || false const isAutoListenClipboard = allConfig.settings?.isAutoListenClipboard || false
const ClipboardWatcher = clipboardPoll const ClipboardWatcher = clipboardPoll
if (isAutoListenClipboard) { if (isAutoListenClipboard) {
@@ -127,50 +139,53 @@ class LifeCycle {
notice.show() notice.show()
} }
} }
if (isDevelopment) {
MemoryMonitor.start()
}
await remoteNoticeHandler.init() await remoteNoticeHandler.init()
remoteNoticeHandler.triggerHook(IRemoteNoticeTriggerHook.APP_START) remoteNoticeHandler.triggerHook(IRemoteNoticeTriggerHook.APP_START)
if (startMode === ISartMode.MINI && process.platform !== 'darwin') { if (startMode === ISartMode.MINI && process.platform !== 'darwin') {
windowManager.create(IWindowList.MINI_WINDOW) windowManager.create(IWindowList.MINI_WINDOW)
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
miniWindow.removeAllListeners() miniWindow?.removeAllListeners()
if (allConfig.settings?.miniWindowOntop) { if (allConfig.settings?.miniWindowOntop) {
miniWindow.setAlwaysOnTop(true) miniWindow?.setAlwaysOnTop(true)
} }
const { width, height } = screen.getPrimaryDisplay().workAreaSize const { width, height } = screen.getPrimaryDisplay().workAreaSize
const lastPosition = allConfig.settings?.miniWindowPosition const lastPosition = allConfig.settings?.miniWindowPosition
if (lastPosition) { if (lastPosition) {
if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) { if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) {
miniWindow.setPosition(width - 100, height - 100) miniWindow?.setPosition(width - 100, height - 100)
picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: [width - 100, height - 100] }) picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: [width - 100, height - 100] })
} else if ( } else if (
lastPosition[0] + miniWindow.getSize()[0] > width || lastPosition[0] + miniWindow?.getSize()[0] > width ||
lastPosition[1] + miniWindow.getSize()[1] > height lastPosition[1] + miniWindow?.getSize()[1] > height
) { ) {
miniWindow.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]) miniWindow?.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1])
if (miniWindow) {
picgo.saveConfig({ picgo.saveConfig({
[configPaths.settings.miniWindowPosition]: [ [configPaths.settings.miniWindowPosition]: [
width - miniWindow.getSize()[0], width - miniWindow.getSize()[0],
height - miniWindow.getSize()[1], height - miniWindow.getSize()[1],
], ],
}) })
} else {
miniWindow.setPosition(lastPosition[0], lastPosition[1])
} }
} else { } else {
miniWindow.setPosition(width - 100, height - 100) miniWindow?.setPosition(lastPosition[0], lastPosition[1])
}
} else {
miniWindow?.setPosition(width - 100, height - 100)
} }
const setPositionFunc = () => { const setPositionFunc = () => {
const position = miniWindow.getPosition() const position = miniWindow?.getPosition()
picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: position }) picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: position })
} }
miniWindow.on('close', setPositionFunc) miniWindow?.on('close', setPositionFunc)
miniWindow.on('move', setPositionFunc) miniWindow?.on('move', setPositionFunc)
miniWindow.show() miniWindow?.show()
miniWindow.focus() miniWindow?.focus()
} else if (startMode === ISartMode.MAIN) { } else if (startMode === ISartMode.MAIN) {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! windowManager.create(IWindowList.SETTING_WINDOW)
settingWindow.show()
settingWindow.focus()
} }
const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER) const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER)
fs.emptyDir(clipboardDir) fs.emptyDir(clipboardDir)
@@ -183,19 +198,10 @@ class LifeCycle {
logger.info('detect second instance') logger.info('detect second instance')
const result = handleStartUpFiles(commandLine, workingDirectory) const result = handleStartUpFiles(commandLine, workingDirectory)
if (!result) { if (!result) {
if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.create(IWindowList.SETTING_WINDOW)
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
if (settingWindow.isMinimized()) {
settingWindow.restore()
}
settingWindow.focus()
}
} }
}) })
app.on('activate', () => { app.on('activate', () => {
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
windowManager.create(IWindowList.TRAY_WINDOW)
}
if (!windowManager.has(IWindowList.SETTING_WINDOW)) { if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
windowManager.create(IWindowList.SETTING_WINDOW) windowManager.create(IWindowList.SETTING_WINDOW)
} }
@@ -228,11 +234,7 @@ class LifeCycle {
} }
#onQuit() { #onQuit() {
app.on('window-all-closed', () => { app.on('window-all-closed', () => {})
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('will-quit', () => { app.on('will-quit', () => {
UpDownTaskQueue.getInstance().persist() UpDownTaskQueue.getInstance().persist()

View File

@@ -207,7 +207,7 @@ class AliyunApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketName: bucket, bucketName: bucket,
bucketConfig: { Location: region }, bucketConfig: { Location: region },
@@ -246,10 +246,10 @@ class AliyunApi {
res?.objects?.forEach((item: OSS.ObjectMeta) => { res?.objects?.forEach((item: OSS.ObjectMeta) => {
item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
@@ -257,12 +257,12 @@ class AliyunApi {
} while (res.isTruncated === true && !cancelTask[0]) } while (res.isTruncated === true && !cancelTask[0])
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketName: bucket, bucketName: bucket,
bucketConfig: { Location: region }, bucketConfig: { Location: region },
@@ -305,10 +305,10 @@ class AliyunApi {
res?.objects?.forEach((item: OSS.ObjectMeta) => { res?.objects?.forEach((item: OSS.ObjectMeta) => {
item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -316,7 +316,7 @@ class AliyunApi {
} while (res.isTruncated === true && !cancelTask[0]) } while (res.isTruncated === true && !cancelTask[0])
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -184,7 +184,7 @@ class GithubApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { bucketName: repo, customUrl: branch, prefix, cancelToken, cdnUrl } = configMap const { bucketName: repo, customUrl: branch, prefix, cancelToken, cdnUrl } = configMap
const slicedPrefix = prefix.replace(/(^\/+|\/+$)/g, '') const slicedPrefix = prefix.replace(/(^\/+|\/+$)/g, '')
const cancelTask = [false] const cancelTask = [false]
@@ -220,22 +220,22 @@ class GithubApi {
result.fullList.push(this.formatFile(item, currentPrefix, branch, repo, cdnUrl)) result.fullList.push(this.formatFile(item, currentPrefix, branch, repo, cdnUrl))
} }
}) })
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
} }
result.success = true result.success = true
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { bucketName: repo, customUrl: branch, prefix, cancelToken, cdnUrl } = configMap const { bucketName: repo, customUrl: branch, prefix, cancelToken, cdnUrl } = configMap
const slicedPrefix = prefix.replace(/(^\/+|\/+$)/g, '') const slicedPrefix = prefix.replace(/(^\/+|\/+$)/g, '')
const cancelTask = [false] const cancelTask = [false]
@@ -265,13 +265,13 @@ class GithubApi {
}) })
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
result.success = true result.success = true
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -93,7 +93,7 @@ class ImgurApi {
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketConfig: { Location: albumHash }, bucketConfig: { Location: albumHash },
cancelToken, cancelToken,
@@ -122,7 +122,7 @@ class ImgurApi {
}) })
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -139,7 +139,7 @@ class ImgurApi {
}) })
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -148,7 +148,7 @@ class ImgurApi {
} }
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -74,7 +74,7 @@ class LocalApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { prefix, customUrl = '', cancelToken } = configMap const { prefix, customUrl = '', cancelToken } = configMap
const urlPrefix = customUrl.replace(/\/+$/, '') const urlPrefix = customUrl.replace(/\/+$/, '')
const cancelTask = [false] const cancelTask = [false]
@@ -109,12 +109,12 @@ class LocalApi {
this.logParam(error, 'getBucketListRecursively') this.logParam(error, 'getBucketListRecursively')
} }
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { customUrl = '', cancelToken, baseDir } = configMap const { customUrl = '', cancelToken, baseDir } = configMap
let prefix = configMap.prefix let prefix = configMap.prefix
prefix = this.transBack(prefix) prefix = this.transBack(prefix)
@@ -165,7 +165,7 @@ class LocalApi {
this.logParam(error, 'getBucketListBackstage') this.logParam(error, 'getBucketListBackstage')
} }
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -235,7 +235,7 @@ class QiniuApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { bucketName: bucket, prefix, cancelToken, customUrl: urlPrefix } = configMap const { bucketName: bucket, prefix, cancelToken, customUrl: urlPrefix } = configMap
let marker = undefined as any let marker = undefined as any
const slicedPrefix = prefix.slice(1) const slicedPrefix = prefix.slice(1)
@@ -281,10 +281,10 @@ class QiniuApi {
res.respBody.items.forEach((item: any) => { res.respBody.items.forEach((item: any) => {
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
@@ -292,12 +292,12 @@ class QiniuApi {
} while (res.respBody && res.respBody.marker && !cancelTask[0]) } while (res.respBody && res.respBody.marker && !cancelTask[0])
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { bucketName: bucket, prefix, cancelToken, customUrl: urlPrefix } = configMap const { bucketName: bucket, prefix, cancelToken, customUrl: urlPrefix } = configMap
let marker = undefined as any let marker = undefined as any
const slicedPrefix = prefix.slice(1) const slicedPrefix = prefix.slice(1)
@@ -349,10 +349,10 @@ class QiniuApi {
res.respBody.items.forEach((item: any) => { res.respBody.items.forEach((item: any) => {
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -360,7 +360,7 @@ class QiniuApi {
} while (res.respBody && res.respBody.marker && !cancelTask[0]) } while (res.respBody && res.respBody.marker && !cancelTask[0])
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -305,7 +305,7 @@ class S3plistApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketName: bucket, bucketName: bucket,
bucketConfig: { Location: region }, bucketConfig: { Location: region },
@@ -345,11 +345,11 @@ class S3plistApi {
res.Contents.forEach((item: _Object) => { res.Contents.forEach((item: _Object) => {
result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
} else { } else {
this.logParam(res, 'getBucketListRecursively') this.logParam(res, 'getBucketListRecursively')
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
@@ -358,18 +358,18 @@ class S3plistApi {
} catch (error) { } catch (error) {
this.logParam(error, 'getBucketListRecursively') this.logParam(error, 'getBucketListRecursively')
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketName: bucket, bucketName: bucket,
bucketConfig: { Location: region }, bucketConfig: { Location: region },
@@ -415,11 +415,11 @@ class S3plistApi {
res.Contents.forEach((item: _Object) => { res.Contents.forEach((item: _Object) => {
result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
} else { } else {
this.logParam(res, 'getBucketListBackstage') this.logParam(res, 'getBucketListBackstage')
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -428,13 +428,13 @@ class S3plistApi {
} catch (error) { } catch (error) {
this.logParam(error, 'getBucketListBackstage') this.logParam(error, 'getBucketListBackstage')
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -150,7 +150,7 @@ class SftpApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { prefix, customUrl, cancelToken } = configMap const { prefix, customUrl, cancelToken } = configMap
const urlPrefix = customUrl || `${this.host}:${this.port}` const urlPrefix = customUrl || `${this.host}:${this.port}`
const cancelTask = [false] const cancelTask = [false]
@@ -185,7 +185,7 @@ class SftpApi {
this.logParam(error, 'getBucketListRecursively') this.logParam(error, 'getBucketListRecursively')
} }
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
@@ -216,7 +216,7 @@ class SftpApi {
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { prefix, customUrl, cancelToken, baseDir } = configMap const { prefix, customUrl, cancelToken, baseDir } = configMap
let urlPrefix = customUrl || `${this.host}:${this.port}` let urlPrefix = customUrl || `${this.host}:${this.port}`
urlPrefix = urlPrefix.replace(/\/+$/, '') urlPrefix = urlPrefix.replace(/\/+$/, '')
@@ -257,20 +257,20 @@ class SftpApi {
} }
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
} catch (error) { } catch (error) {
this.logParam(error, 'getBucketListBackstage') this.logParam(error, 'getBucketListBackstage')
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
result.success = true result.success = true
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -54,7 +54,7 @@ class SmmsApi {
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { cancelToken } = configMap const { cancelToken } = configMap
let marker = 1 let marker = 1
const cancelTask = [false] const cancelTask = [false]
@@ -84,18 +84,18 @@ class SmmsApi {
if (res.data.Count === 0) { if (res.data.Count === 0) {
result.success = true result.success = true
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} else { } else {
res.data.data.forEach((item: any) => { res.data.data.forEach((item: any) => {
result.fullList.push(this.formatFile(item)) result.fullList.push(this.formatFile(item))
}) })
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
} }
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -103,7 +103,7 @@ class SmmsApi {
} while (!cancelTask[0] && res?.status === 200 && res?.data?.success && res.data.CurrentPage < res.data.TotalPages) } while (!cancelTask[0] && res?.status === 200 && res?.data?.success && res.data.CurrentPage < res.data.TotalPages)
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -96,7 +96,7 @@ class TcyunApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketName: bucket, bucketName: bucket,
bucketConfig: { Location: region }, bucketConfig: { Location: region },
@@ -134,10 +134,10 @@ class TcyunApi {
this.formatFile(item, slicedPrefix, urlPrefix), this.formatFile(item, slicedPrefix, urlPrefix),
), ),
) )
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
@@ -145,12 +145,12 @@ class TcyunApi {
} while (res.IsTruncated === 'true' && !cancelTask[0]) } while (res.IsTruncated === 'true' && !cancelTask[0])
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { const {
bucketName: bucket, bucketName: bucket,
bucketConfig: { Location: region }, bucketConfig: { Location: region },
@@ -190,10 +190,10 @@ class TcyunApi {
this.formatFile(item, slicedPrefix, urlPrefix), this.formatFile(item, slicedPrefix, urlPrefix),
), ),
) )
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -201,7 +201,7 @@ class TcyunApi {
} while (res.IsTruncated === 'true' && !cancelTask[0]) } while (res.IsTruncated === 'true' && !cancelTask[0])
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -116,7 +116,7 @@ class UpyunApi {
} }
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { bucketName: bucket, prefix, cancelToken } = configMap const { bucketName: bucket, prefix, cancelToken } = configMap
const slicedPrefix = prefix.slice(1) const slicedPrefix = prefix.slice(1)
const urlPrefix = configMap.customUrl || `http://${bucket}.test.upcdn.net` const urlPrefix = configMap.customUrl || `http://${bucket}.test.upcdn.net`
@@ -147,10 +147,10 @@ class UpyunApi {
item.type === 'F' && folderQueue.push(`${slicedPrefix}${item.name}/`) item.type === 'F' && folderQueue.push(`${slicedPrefix}${item.name}/`)
item.type === 'N' && result.fullList.push(this.formatFile(item, folder, urlPrefix)) item.type === 'N' && result.fullList.push(this.formatFile(item, folder, urlPrefix))
}) })
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
return return
} }
@@ -163,12 +163,12 @@ class UpyunApi {
} }
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { bucketName: bucket, prefix, cancelToken } = configMap const { bucketName: bucket, prefix, cancelToken } = configMap
const slicedPrefix = prefix.slice(1) const slicedPrefix = prefix.slice(1)
const urlPrefix = configMap.customUrl || `http://${bucket}.test.upcdn.net` const urlPrefix = configMap.customUrl || `http://${bucket}.test.upcdn.net`
@@ -196,10 +196,10 @@ class UpyunApi {
item.type === 'N' && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.type === 'N' && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
@@ -207,7 +207,7 @@ class UpyunApi {
} while (!cancelTask[0] && res.next !== this.stopMarker) } while (!cancelTask[0] && res.next !== this.stopMarker)
result.success = !cancelTask[0] result.success = !cancelTask[0]
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -98,7 +98,7 @@ class WebdavplistApi {
isRequestSuccess = (code: number) => code >= 200 && code < 300 isRequestSuccess = (code: number) => code >= 200 && code < 300
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { prefix, customUrl, cancelToken } = configMap const { prefix, customUrl, cancelToken } = configMap
const urlPrefix = customUrl || this.endpoint const urlPrefix = customUrl || this.endpoint
const cancelTask = [false] const cancelTask = [false]
@@ -133,12 +133,12 @@ class WebdavplistApi {
this.logParam(error, 'getBucketListRecursively') this.logParam(error, 'getBucketListRecursively')
} }
result.finished = true result.finished = true
window.webContents.send(refreshDownloadFileTransferList, result) window?.webContents.send(refreshDownloadFileTransferList, result)
ipcMain.removeAllListeners(cancelDownloadLoadingFileList) ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
} }
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> { async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
const { prefix, customUrl, cancelToken, baseDir } = configMap const { prefix, customUrl, cancelToken, baseDir } = configMap
let urlPrefix = customUrl || this.endpoint let urlPrefix = customUrl || this.endpoint
urlPrefix = urlPrefix.replace(/\/+$/, '') urlPrefix = urlPrefix.replace(/\/+$/, '')
@@ -179,20 +179,20 @@ class WebdavplistApi {
} }
} else { } else {
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
} catch (error) { } catch (error) {
this.logParam(error, 'getBucketListBackstage') this.logParam(error, 'getBucketListBackstage')
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
return return
} }
result.success = true result.success = true
result.finished = true result.finished = true
window.webContents.send('refreshFileTransferList', result) window?.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList') ipcMain.removeAllListeners('cancelLoadingFileList')
} }

View File

@@ -166,8 +166,8 @@ export class ManageApi extends EventEmitter implements IManageApiType {
} }
private sendDefaultResult(eventName: string, defaultResult: any) { private sendDefaultResult(eventName: string, defaultResult: any) {
const window = windowManager.get(IWindowList.SETTING_WINDOW)! const window = windowManager.get(IWindowList.SETTING_WINDOW)
window.webContents.send(eventName, defaultResult) window?.webContents.send(eventName, defaultResult)
ipcMain.removeAllListeners( ipcMain.removeAllListeners(
eventName === refreshDownloadFileTransferList ? cancelDownloadLoadingFileList : 'cancelLoadingFileList', eventName === refreshDownloadFileTransferList ? cancelDownloadLoadingFileList : 'cancelLoadingFileList',
) )

View File

@@ -72,8 +72,6 @@ export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> =>
} }
} }
} }
if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send('updateGallery')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
}
return result return result
} }

View File

@@ -3,7 +3,6 @@ import path from 'node:path'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { appConfigPath, themesDir } from '@core/datastore/dirs' import { appConfigPath, themesDir } from '@core/datastore/dirs'
import * as fsWalk from '@nodelib/fs.walk'
import fs from 'fs-extra' import fs from 'fs-extra'
import yaml from 'yaml' import yaml from 'yaml'
@@ -101,37 +100,16 @@ function getClipboardFiles() {
}) })
} }
function getFileInCssDir() {
const cssDir = path.join(dirname, '../../resources/theme').replace('app.asar', 'app.asar.unpacked')
const res = fsWalk.walkSync(cssDir, {
followSymbolicLinks: true,
fs,
stats: true,
throwErrorOnBrokenSymbolicLink: false,
})
const result: string[] = []
res.forEach(item => {
if (item.stats?.isFile()) {
result.push(item.path)
}
})
return result
}
function resolveCss() { function resolveCss() {
try { try {
fs.ensureDirSync(themesDir()) const srcDir = path.join(dirname, '../../resources/theme').replace('app.asar', 'app.asar.unpacked')
const css = getFileInCssDir() const destDir = themesDir()
css.forEach(item => { fs.copySync(srcDir, destDir, {
const dest = path.join(themesDir(), path.basename(item)) overwrite: true,
if (!fs.pathExistsSync(dest)) { errorOnExist: false,
fs.copyFileSync(item, dest)
} else {
diffFilesAndUpdate(item, dest)
}
}) })
} catch (e) { } catch (e) {
console.error(e) console.error('Failed to resolve CSS:', e)
} }
} }

View File

@@ -182,6 +182,7 @@ export const configPaths = {
enableSecondUploader: 'settings.enableSecondUploader', enableSecondUploader: 'settings.enableSecondUploader',
lastSyncTime: 'settings.lastSyncTime', lastSyncTime: 'settings.lastSyncTime',
theme: 'settings.theme', theme: 'settings.theme',
systemTheme: 'settings.systemTheme',
enableAdvancedAnimation: 'settings.enableAdvancedAnimation', enableAdvancedAnimation: 'settings.enableAdvancedAnimation',
isDisableGPU: 'settings.isDisableGPU', isDisableGPU: 'settings.isDisableGPU',
}, },

View File

@@ -79,6 +79,7 @@ export const IRPCActionType = {
THEME_FETCH_THEMES: 'THEME_FETCH_THEMES', THEME_FETCH_THEMES: 'THEME_FETCH_THEMES',
THEME_IMPORT_THEMES: 'THEME_IMPORT_THEMES', THEME_IMPORT_THEMES: 'THEME_IMPORT_THEMES',
THEME_APPLY_THEME: 'THEME_APPLY_THEME', THEME_APPLY_THEME: 'THEME_APPLY_THEME',
THEME_GET_BOOTSTRAP: 'THEME_GET_BOOTSTRAP',
RELOAD_APP: 'RELOAD_APP', RELOAD_APP: 'RELOAD_APP',
OPEN_URL: 'OPEN_URL', OPEN_URL: 'OPEN_URL',
OPEN_FILE: 'OPEN_FILE', OPEN_FILE: 'OPEN_FILE',

View File

@@ -264,17 +264,13 @@ class UploadTaskQueueManager {
const inserted = await GalleryDB.getInstance().insert(img) const inserted = await GalleryDB.getInstance().insert(img)
windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles') windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles')
if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send('updateGallery')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
}
handleCopyUrl(pasteText) handleCopyUrl(pasteText)
if (backupImgs && backupImgs.length > 0) { if (backupImgs && backupImgs.length > 0) {
await GalleryDB.getInstance().insert(backupImgs[0]) await GalleryDB.getInstance().insert(backupImgs[0])
windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles') windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles')
if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)?.webContents?.send('updateGallery')
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery')
}
} }
return { return {

View File

@@ -6,66 +6,69 @@ import { configPaths } from '~/utils/configPaths'
import { IWindowList } from '~/utils/enum' import { IWindowList } from '~/utils/enum'
export function openMiniWindow(hideSettingWindow: boolean = true) { export function openMiniWindow(hideSettingWindow: boolean = true) {
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! const allConfig = picgo.getConfig<any>() || {}
let miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
if (!miniWindow) {
miniWindow = windowManager.create(IWindowList.MINI_WINDOW)
}
miniWindow.removeAllListeners('close') miniWindow?.removeAllListeners('close')
miniWindow.removeAllListeners('move') miniWindow?.removeAllListeners('move')
if (picgo.getConfig<boolean>(configPaths.settings.miniWindowOntop)) { if (allConfig.settings?.miniWindowOntop) {
miniWindow.setAlwaysOnTop(true) miniWindow?.setAlwaysOnTop(true)
} }
const { width, height } = screen.getPrimaryDisplay().workAreaSize const { width, height } = screen.getPrimaryDisplay().workAreaSize
const lastPosition = picgo.getConfig<number[]>(configPaths.settings.miniWindowPosition) const lastPosition = allConfig.settings?.miniWindowPosition
const setPositionFunc = () => { const setPositionFunc = () => {
const position = miniWindow.getPosition() const position = miniWindow?.getPosition()
picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: position }) picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: position })
} }
if (lastPosition) { if (lastPosition) {
if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) { if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) {
miniWindow.setPosition(width - 100, height - 100) miniWindow?.setPosition(width - 100, height - 100)
picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: [width - 100, height - 100] }) picgo.saveConfig({ [configPaths.settings.miniWindowPosition]: [width - 100, height - 100] })
} else if ( } else if (
lastPosition[0] + miniWindow.getSize()[0] > width || lastPosition[0] + miniWindow?.getSize()[0] > width ||
lastPosition[1] + miniWindow.getSize()[1] > height lastPosition[1] + miniWindow?.getSize()[1] > height
) { ) {
miniWindow.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]) miniWindow?.setPosition(width - miniWindow?.getSize()[0], height - miniWindow?.getSize()[1])
if (miniWindow) {
picgo.saveConfig({ picgo.saveConfig({
[configPaths.settings.miniWindowPosition]: [width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]], [configPaths.settings.miniWindowPosition]: [
width - miniWindow.getSize()[0],
height - miniWindow.getSize()[1],
],
}) })
} else {
miniWindow.setPosition(lastPosition[0], lastPosition[1])
} }
} else { } else {
miniWindow.setPosition(width - 100, height - 100) miniWindow?.setPosition(lastPosition[0], lastPosition[1])
} }
} else {
miniWindow.on('close', setPositionFunc) miniWindow?.setPosition(width - 100, height - 100)
miniWindow.on('move', setPositionFunc) }
miniWindow.show() miniWindow?.on('close', setPositionFunc)
miniWindow.focus() miniWindow?.on('move', setPositionFunc)
miniWindow?.show()
miniWindow?.focus()
if (hideSettingWindow) { if (hideSettingWindow) {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! windowManager.get(IWindowList.SETTING_WINDOW)?.close()
settingWindow.hide()
} else { } else {
const autoCloseMainWindow = picgo.getConfig<boolean>(configPaths.settings.autoCloseMainWindow) || false const autoCloseMainWindow = allConfig.settings?.autoCloseMainWindow || false
if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { if (autoCloseMainWindow) {
windowManager.get(IWindowList.SETTING_WINDOW)!.hide() windowManager.get(IWindowList.SETTING_WINDOW)?.close()
} }
} }
} }
export const openMainWindow = () => { export const openMainWindow = () => {
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
const autoCloseMiniWindow = picgo.getConfig<boolean>(configPaths.settings.autoCloseMiniWindow) || false const autoCloseMiniWindow = picgo.getConfig<boolean>(configPaths.settings.autoCloseMiniWindow) || false
settingWindow!.show() windowManager.create(IWindowList.SETTING_WINDOW)
settingWindow!.focus() if (autoCloseMiniWindow) {
if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { windowManager.get(IWindowList.MINI_WINDOW)?.close()
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
} }
} }
export const hideMiniWindow = () => { export const hideMiniWindow = () => {
if (windowManager.has(IWindowList.MINI_WINDOW)) { windowManager.get(IWindowList.MINI_WINDOW)?.close()
windowManager.get(IWindowList.MINI_WINDOW)!.hide()
}
} }

View File

@@ -7,6 +7,34 @@ import mime from 'mime'
import { isProxy, isRef, toRaw, unref } from 'vue' import { isProxy, isRef, toRaw, unref } from 'vue'
import yaml from 'yaml' import yaml from 'yaml'
function setTheme(mode: string) {
const m = mode === 'dark' ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', m)
document.documentElement.classList.toggle('dark', m === 'dark')
document.documentElement.classList.toggle('light', m === 'light')
}
function injectCSS(css: string) {
const id = '__piclist_theme__'
let el = document.getElementById(id) as HTMLStyleElement | null
if (!el) {
el = document.createElement('style')
el.id = id
;(document.head || document.documentElement).appendChild(el)
}
el.textContent = css
}
;(async () => {
try {
const { mode, css } = await ipcRenderer.invoke('RPC_ACTIONS_INVOKE', 'THEME_GET_BOOTSTRAP')
if (document.documentElement) setTheme(mode)
if (css) injectCSS(css)
} catch (e) {
console.error('[theme] bootstrap failed', e)
}
})()
export const getRawData = (args: any): any => { export const getRawData = (args: any): any => {
if (args === null || typeof args !== 'object') { if (args === null || typeof args !== 'object') {
return args return args

View File

@@ -1,9 +1,6 @@
<template> <template>
<div id="layout" :key="pageReloadCount" class="h-full select-none"> <div id="layout" :key="pageReloadCount" class="h-full select-none">
<router-view /> <router-view />
<div
class="pointer-events-none absolute inset-0 -z-1 bg-custom bg-cover bg-fixed bg-center bg-no-repeat opacity-custom blur-custom"
/>
<UIServiceProvider /> <UIServiceProvider />
</div> </div>
</template> </template>

View File

@@ -21,3 +21,11 @@
@apply outline-2 outline-offset-2 outline-accent outline-solid; @apply outline-2 outline-offset-2 outline-accent outline-solid;
} }
} }
@utility drag-region {
-webkit-app-region: drag;
}
@utility no-drag-region {
-webkit-app-region: none;
}

View File

@@ -1,9 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { useStorage } from '@vueuse/core'
import { Monitor, Moon, Sun } from 'lucide-vue-next' import { Monitor, Moon, Sun } from 'lucide-vue-next'
import { computed, onBeforeMount, onBeforeUnmount, watch } from 'vue' import { computed, onBeforeMount, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender'
interface Props { interface Props {
collapsed?: boolean collapsed?: boolean
} }
@@ -11,22 +13,12 @@ interface Props {
defineProps<Props>() defineProps<Props>()
const { t } = useI18n() const { t } = useI18n()
const currentTheme = useStorage<'light' | 'dark' | 'system'>('systemTheme', 'system') const currentTheme = ref<'light' | 'dark' | 'system'>('light')
watch( async function initializeTheme() {
currentTheme, const savedTheme = (await getConfig<'light' | 'dark' | 'system'>(configPaths.settings.systemTheme)) || 'system'
async newTheme => { currentTheme.value = savedTheme
document.documentElement.setAttribute('data-theme', newTheme)
document.documentElement.classList.remove('light', 'dark', 'system')
if (newTheme === 'system') {
const systemTheme = (await window.electron.triggerRPC<'light' | 'dark'>('GET_SYSTEM_THEME')) || 'light'
document.documentElement.classList.add(systemTheme)
} else {
document.documentElement.classList.add(newTheme)
} }
},
{ immediate: true },
)
const themeOptions = computed(() => [ const themeOptions = computed(() => [
{ {
@@ -53,83 +45,38 @@ const currentThemeOption = computed(
() => themeOptions.value.find(option => option.value === currentTheme.value) || themeOptions.value[0], () => themeOptions.value.find(option => option.value === currentTheme.value) || themeOptions.value[0],
) )
function toggleTheme() { async function toggleTheme() {
const themes = ['light', 'dark', 'system'] as const const themes = ['light', 'dark', 'system'] as const
const currentIndex = themes.indexOf(currentTheme.value) const currentIndex = themes.indexOf(currentTheme.value)
const nextTheme = themes[(currentIndex + 1) % themes.length] const nextTheme = themes[(currentIndex + 1) % themes.length]
currentTheme.value = nextTheme currentTheme.value = nextTheme
document.documentElement.classList.remove('light', 'dark', 'system')
if (nextTheme === 'system') {
const systemTheme = (await window.electron.triggerRPC<'light' | 'dark'>('GET_SYSTEM_THEME')) || 'light'
document.documentElement.classList.add(systemTheme)
document.documentElement.setAttribute('data-theme', systemTheme)
} else {
document.documentElement.classList.add(nextTheme)
document.documentElement.setAttribute('data-theme', nextTheme)
}
saveConfig({ [configPaths.settings.systemTheme]: nextTheme })
} }
let listenThemeChange: () => void = () => {}
const themUpdateHandler = (value: 'light' | 'dark') => {
const savedTheme = localStorage.getItem('systemTheme') || 'system'
if (savedTheme === 'system') {
currentTheme.value = value
}
}
onBeforeMount(() => { onBeforeMount(() => {
listenThemeChange = window.electron.ipcRendererOn('theme-update', themUpdateHandler) initializeTheme()
})
onBeforeUnmount(() => {
listenThemeChange()
}) })
</script> </script>
<template> <template>
<div class="theme-switcher"> <div class="relative flex items-center">
<button class="theme-toggle-btn" :class="{ collapsed }" :title="t('settings.theme.toggle')" @click="toggleTheme"> <button
class="flex cursor-pointer items-center gap-2 rounded-md border border-border-secondary bg-bg-secondary px-3 py-2 text-sm text-secondary transition-all duration-fast ease-standard hover:text-main max-md:justify-center max-md:gap-0 max-md:p-2 [.collapsed]:justify-center [.collapsed]:gap-0 [.collapsed]:p-2"
:class="{ collapsed }"
:title="t('settings.theme.toggle')"
@click="toggleTheme"
>
<component :is="currentThemeOption.icon" :size="18" /> <component :is="currentThemeOption.icon" :size="18" />
<span v-if="!collapsed" class="theme-label">{{ currentThemeOption.label }}</span> <span v-if="!collapsed" class="font-medium max-md:hidden">{{ currentThemeOption.label }}</span>
</button> </button>
</div> </div>
</template> </template>
<style scoped>
.theme-switcher {
position: relative;
display: flex;
align-items: center;
}
.theme-toggle-btn {
display: flex;
align-items: center;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
color: var(--color-text-secondary);
background: var(--color-background-secondary);
transition: all 0.2s ease;
gap: 0.5rem;
cursor: pointer;
}
.theme-toggle-btn.collapsed {
justify-content: center;
padding: 0.5rem;
gap: 0;
}
.theme-toggle-btn:hover {
color: var(--color-text-primary);
}
.theme-label {
font-weight: 500;
}
/* Mobile responsive */
@media (width <= 768px) {
.theme-label {
display: none;
}
.theme-toggle-btn {
justify-content: center;
padding: 0.5rem;
gap: 0;
}
}
</style>

View File

@@ -1,35 +1,44 @@
<template> <template>
<div class="title-bar" data-drag-region> <div
<div class="title-bar-content"> class="fixed top-0 right-0 left-0 z-1000 h-[32px] border-b border-b-border bg-bg-secondary drag-region"
<div v-if="osGlobal !== 'darwin'" class="title-left"> data-drag-region
<div class="app-icon"> >
<img :src="defaultLogo" width="18" height="18" /> <div class="flex h-full items-center justify-between px-4 py-0">
<div v-if="osGlobal !== 'darwin'" class="flex items-center gap-2 no-drag-region">
<div class="flex items-center text-accent">
<img :src="defaultLogo" width="18" height="18" class="pointer-events-none select-none no-drag-region" />
</div> </div>
</div> </div>
<div class="title-center"> <div class="flex flex-1 items-center justify-center no-drag-region">
<!-- Progress bar in title bar --> <div v-if="isShowprogress" class="flex w-full max-w-[600px] min-w-[100px] items-center gap-2">
<div v-if="isShowprogress" class="progress-container"> <div class="h-[14px] w-full max-w-[600px] min-w-[100px] flex-1 overflow-hidden rounded-[2px] bg-border">
<div class="progress-bar"> <div
<div class="progress-fill" :style="{ width: `${progress}%` }" /> class="h-full rounded-[2px] bg-success transition-all duration-300 ease-in-out"
:style="{ width: `${progress}%` }"
/>
</div> </div>
<span class="progress-text">{{ progress }}%</span> <span class="min-w-[35px] text-[11px] text-secondary">{{ progress }}%</span>
</div> </div>
</div> </div>
<div class="title-right"> <div class="flex items-center no-drag-region">
<div class="window-controls"> <div class="flex items-center gap-[8px]">
<button class="control-button pin-button" :title="$t('titleBar.alwaysOnTop')" @click="setAlwaysOnTop"> <button class="control-button" :title="$t('titleBar.alwaysOnTop')" @click="setAlwaysOnTop">
<PinIcon :size="14" class="pin-icon" :class="{ active: isAlwaysOnTop }" /> <PinIcon
:size="14"
class="text-[#6b7280] [.active]:rotate-90 [.active]:text-[#ce6769]"
:class="{ active: isAlwaysOnTop }"
/>
</button> </button>
<template v-if="osGlobal !== 'darwin'"> <template v-if="osGlobal !== 'darwin'">
<button class="control-button minimize-button" :title="$t('titleBar.minimize')" @click="minimizeWindow"> <button class="control-button minimize" :title="$t('titleBar.minimize')" @click="minimizeWindow">
<MinusIcon :size="14" /> <MinusIcon :size="14" />
</button> </button>
<button class="control-button mini-button" :title="$t('titleBar.miniWindow')" @click="openMiniWindow"> <button class="control-button mini" :title="$t('titleBar.miniWindow')" @click="openMiniWindow">
<ShrinkIcon :size="14" /> <ShrinkIcon :size="14" />
</button> </button>
<button class="control-button close-button" :title="$t('titleBar.close')" @click="closeWindow"> <button class="control-button close" :title="$t('titleBar.close')" @click="closeWindow">
<XIcon :size="14" /> <XIcon :size="14" />
</button> </button>
</template> </template>
@@ -75,138 +84,11 @@ onBeforeUnmount(() => {
</script> </script>
<style scoped> <style scoped>
.title-bar { @import 'tailwindcss' reference;
position: fixed; @import '../../assets/css/theme.css' reference;
top: 0; @import '../../assets/css/utilities.css' reference;
right: 0;
left: 0;
z-index: 1000;
border-bottom: 1px solid var(--color-border);
height: 32px;
background: var(--color-background-secondary);
-webkit-app-region: drag;
}
.title-bar-content {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 16px;
height: 100%;
}
.title-left {
display: flex;
align-items: center;
gap: 8px;
-webkit-app-region: no-drag;
}
.app-icon {
display: flex;
align-items: center;
color: var(--color-accent);
}
.app-icon img {
-webkit-user-drag: none;
user-select: none;
pointer-events: none;
}
.title-center {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
-webkit-app-region: no-drag;
}
.progress-container {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-width: 100px;
max-width: 600px;
}
.progress-bar {
overflow: hidden;
border-radius: 2px;
width: 100%;
min-width: 100px;
max-width: 600px;
height: 14px;
background: var(--color-border);
flex: 1;
}
.progress-fill {
border-radius: 2px;
height: 100%;
background: var(--color-success);
transition: width 0.3s ease;
}
.progress-text {
min-width: 35px;
font-size: 11px;
color: var(--color-text-secondary);
}
.title-right {
display: flex;
align-items: center;
-webkit-app-region: no-drag;
}
.window-controls {
display: flex;
align-items: center;
gap: 8px;
}
.control-button { .control-button {
display: flex; @apply flex h-[20px] w-[28px] cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent text-secondary transition-all duration-fast ease-standard hover:bg-surface-elevated hover:text-main [.close:hover]:bg-danger [.close:hover]:text-white [.mini:hover]:bg-success/85 [.mini:hover]:text-white [.minimize:hover]:bg-accent/85 [.minimize:hover]:text-white;
justify-content: center;
align-items: center;
border: none;
border-radius: 4px;
width: 28px;
height: 20px;
color: var(--color-text-secondary);
background: transparent;
transition: var(--transition);
cursor: pointer;
}
.control-button:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.pin-icon {
color: #6b7280;
}
.pin-icon.active {
rotate: 90deg;
color: #ce6769;
}
.minimize-button:hover {
color: white;
background: color-mix(in srgb, var(--color-warning), transparent 15%);
}
.mini-button:hover {
color: white;
background: color-mix(in srgb, var(--color-success), transparent 15%);
}
.close-button:hover {
color: white;
background: var(--color-danger);
} }
</style> </style>

View File

@@ -53,9 +53,7 @@
} }
} }
:root, :root {
.light,
[data-theme='light'] {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
@@ -63,7 +61,11 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-rendering: optimizelegibility; text-rendering: optimizelegibility;
}
:root,
.light,
[data-theme='light'] {
--background-image: none; --background-image: none;
--background-image-opacity: 1; --background-image-opacity: 1;
--color-text-primary: #1d1d1f; --color-text-primary: #1d1d1f;
@@ -90,7 +92,9 @@
--shadow-xl: 0 20px 25px rgb(0 0 0 / 10%), 0 10px 10px rgb(0 0 0 / 4%); --shadow-xl: 0 20px 25px rgb(0 0 0 / 10%), 0 10px 10px rgb(0 0 0 / 4%);
} }
:root.dark { :root.dark,
.dark,
[data-theme='dark'] {
--color-text-primary: #f5f5f7; --color-text-primary: #f5f5f7;
--color-text-secondary: #a1a1a6; --color-text-secondary: #a1a1a6;
--color-text-tertiary: #86868b; --color-text-tertiary: #86868b;

View File

@@ -1,6 +1,9 @@
<template> <template>
<div id="main" class="relative flex h-screen overflow-hidden bg-bg pt-[32px]"> <div id="main" class="relative flex h-screen overflow-hidden bg-bg pt-[32px]">
<InputBoxDialog /> <InputBoxDialog />
<div
class="pointer-events-none absolute inset-0 -z-1 bg-custom bg-cover bg-fixed bg-center bg-no-repeat opacity-custom blur-custom"
/>
<TitleBar /> <TitleBar />
<Navigation /> <Navigation />
<main class="relative z-1 no-scrollbar h-screen flex-1 overflow-scroll bg-bg-secondary"> <main class="relative z-1 no-scrollbar h-screen flex-1 overflow-scroll bg-bg-secondary">

View File

@@ -22,9 +22,6 @@ import db from '@/utils/db'
type MessageSchema = typeof zhCN type MessageSchema = typeof zhCN
window.electron.setVisualZoomLevelLimits(1, 1) window.electron.setVisualZoomLevelLimits(1, 1)
const savedTheme = localStorage.getItem('systemTheme') || 'system'
document.documentElement.setAttribute('data-theme', savedTheme)
document.documentElement.classList.add(savedTheme)
const app = createApp(App) const app = createApp(App)

View File

@@ -188,7 +188,7 @@
<span class="text-sm font-medium text-secondary">{{ t('pages.upload.taskUpload') }}</span> <span class="text-sm font-medium text-secondary">{{ t('pages.upload.taskUpload') }}</span>
<span <span
v-if="taskQueueStatus.tasks.length > 0" v-if="taskQueueStatus.tasks.length > 0"
class="absolute top-[50%] right-3 flex min-w-6 -translate-y-[50%] animate-[badge-pulse_2s_ease-in-out_infinite] items-center justify-center rounded-full px-1.5 py-0 text-sm font-bold text-accent" class="absolute top-[50%] right-3 flex min-w-6 animate-[badge-pulse_2s_ease-in-out_infinite] items-center justify-center rounded-full px-1.5 py-0 text-sm font-bold text-accent"
> >
{{ taskQueueStatus.tasks.length }} {{ taskQueueStatus.tasks.length }}
</span> </span>

View File

@@ -182,6 +182,7 @@ export const configPaths = {
enableSecondUploader: 'settings.enableSecondUploader', enableSecondUploader: 'settings.enableSecondUploader',
secondPicBedMode: 'settings.secondPicBedMode', secondPicBedMode: 'settings.secondPicBedMode',
theme: 'settings.theme', theme: 'settings.theme',
systemTheme: 'settings.systemTheme',
enableAdvancedAnimation: 'settings.enableAdvancedAnimation', enableAdvancedAnimation: 'settings.enableAdvancedAnimation',
isDisableGPU: 'settings.isDisableGPU', isDisableGPU: 'settings.isDisableGPU',
}, },

View File

@@ -7,12 +7,12 @@ interface IWindowListItem {
} }
interface IWindowManager { interface IWindowManager {
create: (name: string) => import('electron').BrowserWindow | null create: (name: string) => import('electron').BrowserWindow | undefined
get: (name: string) => import('electron').BrowserWindow | null get: (name: string) => import('electron').BrowserWindow | undefined
has: (name: string) => boolean has: (name: string) => boolean
// delete: (name: IWindowList) => void // delete: (name: IWindowList) => void
deleteById: (id: number) => void deleteById: (id: number) => void
getAvailableWindow: (isSkipMiniWindow?: boolean) => import('electron').BrowserWindow getAvailableWindow: (isSkipMiniWindow?: boolean) => import('electron').BrowserWindow | undefined
} }
type IpcRendererListener = (event: import('electron').IpcRendererEvent, ...args: any[]) => void type IpcRendererListener = (event: import('electron').IpcRendererEvent, ...args: any[]) => void