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

@@ -1,9 +1,6 @@
<template>
<div id="layout" :key="pageReloadCount" class="h-full select-none">
<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 />
</div>
</template>

View File

@@ -21,3 +21,11 @@
@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">
import { useStorage } from '@vueuse/core'
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 { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender'
interface Props {
collapsed?: boolean
}
@@ -11,22 +13,12 @@ interface Props {
defineProps<Props>()
const { t } = useI18n()
const currentTheme = useStorage<'light' | 'dark' | 'system'>('systemTheme', 'system')
const currentTheme = ref<'light' | 'dark' | 'system'>('light')
watch(
currentTheme,
async newTheme => {
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 },
)
async function initializeTheme() {
const savedTheme = (await getConfig<'light' | 'dark' | 'system'>(configPaths.settings.systemTheme)) || 'system'
currentTheme.value = savedTheme
}
const themeOptions = computed(() => [
{
@@ -53,83 +45,38 @@ const currentThemeOption = computed(
() => themeOptions.value.find(option => option.value === currentTheme.value) || themeOptions.value[0],
)
function toggleTheme() {
async function toggleTheme() {
const themes = ['light', 'dark', 'system'] as const
const currentIndex = themes.indexOf(currentTheme.value)
const nextTheme = themes[(currentIndex + 1) % themes.length]
currentTheme.value = nextTheme
}
let listenThemeChange: () => void = () => {}
const themUpdateHandler = (value: 'light' | 'dark') => {
const savedTheme = localStorage.getItem('systemTheme') || 'system'
if (savedTheme === 'system') {
currentTheme.value = value
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 })
}
onBeforeMount(() => {
listenThemeChange = window.electron.ipcRendererOn('theme-update', themUpdateHandler)
})
onBeforeUnmount(() => {
listenThemeChange()
onBeforeMount(() => {
initializeTheme()
})
</script>
<template>
<div class="theme-switcher">
<button class="theme-toggle-btn" :class="{ collapsed }" :title="t('settings.theme.toggle')" @click="toggleTheme">
<div class="relative flex items-center">
<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" />
<span v-if="!collapsed" class="theme-label">{{ currentThemeOption.label }}</span>
<span v-if="!collapsed" class="font-medium max-md:hidden">{{ currentThemeOption.label }}</span>
</button>
</div>
</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>
<div class="title-bar" data-drag-region>
<div class="title-bar-content">
<div v-if="osGlobal !== 'darwin'" class="title-left">
<div class="app-icon">
<img :src="defaultLogo" width="18" height="18" />
<div
class="fixed top-0 right-0 left-0 z-1000 h-[32px] border-b border-b-border bg-bg-secondary drag-region"
data-drag-region
>
<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 class="title-center">
<!-- Progress bar in title bar -->
<div v-if="isShowprogress" class="progress-container">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: `${progress}%` }" />
<div class="flex flex-1 items-center justify-center no-drag-region">
<div v-if="isShowprogress" class="flex w-full max-w-[600px] min-w-[100px] items-center gap-2">
<div class="h-[14px] w-full max-w-[600px] min-w-[100px] flex-1 overflow-hidden rounded-[2px] bg-border">
<div
class="h-full rounded-[2px] bg-success transition-all duration-300 ease-in-out"
:style="{ width: `${progress}%` }"
/>
</div>
<span class="progress-text">{{ progress }}%</span>
<span class="min-w-[35px] text-[11px] text-secondary">{{ progress }}%</span>
</div>
</div>
<div class="title-right">
<div class="window-controls">
<button class="control-button pin-button" :title="$t('titleBar.alwaysOnTop')" @click="setAlwaysOnTop">
<PinIcon :size="14" class="pin-icon" :class="{ active: isAlwaysOnTop }" />
<div class="flex items-center no-drag-region">
<div class="flex items-center gap-[8px]">
<button class="control-button" :title="$t('titleBar.alwaysOnTop')" @click="setAlwaysOnTop">
<PinIcon
:size="14"
class="text-[#6b7280] [.active]:rotate-90 [.active]:text-[#ce6769]"
:class="{ active: isAlwaysOnTop }"
/>
</button>
<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" />
</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" />
</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" />
</button>
</template>
@@ -75,138 +84,11 @@ onBeforeUnmount(() => {
</script>
<style scoped>
.title-bar {
position: fixed;
top: 0;
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;
}
@import 'tailwindcss' reference;
@import '../../assets/css/theme.css' reference;
@import '../../assets/css/utilities.css' reference;
.control-button {
display: flex;
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);
@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;
}
</style>

View File

@@ -53,9 +53,7 @@
}
}
:root,
.light,
[data-theme='light'] {
:root {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: 400;
@@ -63,7 +61,11 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizelegibility;
}
:root,
.light,
[data-theme='light'] {
--background-image: none;
--background-image-opacity: 1;
--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%);
}
:root.dark {
:root.dark,
.dark,
[data-theme='dark'] {
--color-text-primary: #f5f5f7;
--color-text-secondary: #a1a1a6;
--color-text-tertiary: #86868b;

View File

@@ -1,6 +1,9 @@
<template>
<div id="main" class="relative flex h-screen overflow-hidden bg-bg pt-[32px]">
<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 />
<Navigation />
<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
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)

View File

@@ -188,7 +188,7 @@
<span class="text-sm font-medium text-secondary">{{ t('pages.upload.taskUpload') }}</span>
<span
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 }}
</span>

View File

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