feat: enhance theme management and image caching functionality

This commit is contained in:
DEV_DSW
2026-04-16 15:43:35 +08:00
parent 411f4f3421
commit b1f589a674
13 changed files with 74 additions and 183 deletions

View File

@@ -9,6 +9,7 @@ import { extractText, isToolOnlyMessage, isToolResultRole, isInternalMessage } f
import { hostApiFetch } from '@lib/host-api'
import { gatewayRpc, onGatewayEvent } from '@lib/gateway-client'
import { useProviderStore } from '@stores/providers'
import { IPC_EVENTS, CONFIG_KEYS } from '@lib/constants'
// ── Constants ───────────────────────────────────────────────────
const DEFAULT_SESSION_KEY = 'agent:main:main'
@@ -32,7 +33,6 @@ let _lastLoadSessionsAt = 0
const _historyLoadInFlight = new Map<string, Promise<void>>()
const _lastHistoryLoadAtBySession = new Map<string, number>()
const _chatEventDedupe = new Map<string, number>()
const IMAGE_CACHE_KEY = 'zn-ai:image-cache'
const IMAGE_CACHE_MAX = 100
// ── Helpers: Timers ─────────────────────────────────────────────
@@ -90,29 +90,35 @@ function isDuplicateChatEvent(eventState: string, event: Record<string, unknown>
}
// ── Helpers: Image Cache ────────────────────────────────────────
function loadImageCache(): Map<string, AttachedFileMeta> {
let _imageCache: Map<string, AttachedFileMeta> = new Map()
let _imageCacheInitialized = false
async function initImageCache(): Promise<void> {
if (_imageCacheInitialized) return
_imageCache = await loadImageCache()
_imageCacheInitialized = true
}
async function loadImageCache(): Promise<Map<string, AttachedFileMeta>> {
try {
const raw = localStorage.getItem(IMAGE_CACHE_KEY)
if (raw) {
const entries = JSON.parse(raw) as Array<[string, AttachedFileMeta]>
return new Map(entries)
const raw = await window.api.invoke(IPC_EVENTS.GET_CONFIG, CONFIG_KEYS.IMAGE_CACHE)
if (Array.isArray(raw)) {
return new Map(raw as Array<[string, AttachedFileMeta]>)
}
} catch { /* ignore */ }
return new Map()
}
function saveImageCache(cache: Map<string, AttachedFileMeta>): void {
async function saveImageCache(cache: Map<string, AttachedFileMeta>): Promise<void> {
try {
const entries = Array.from(cache.entries())
const trimmed = entries.length > IMAGE_CACHE_MAX
? entries.slice(entries.length - IMAGE_CACHE_MAX)
: entries
localStorage.setItem(IMAGE_CACHE_KEY, JSON.stringify(trimmed))
await window.api.invoke(IPC_EVENTS.SET_CONFIG, CONFIG_KEYS.IMAGE_CACHE, trimmed)
} catch { /* ignore */ }
}
const _imageCache = loadImageCache()
// ── Helpers: Timestamp ──────────────────────────────────────────
function toMs(ts: number): number {
return ts < 1e12 ? ts * 1000 : ts
@@ -918,6 +924,7 @@ export const useChatStore = defineStore('chat', {
try {
// Cache image attachments
if (attachments && attachments.length > 0) {
await initImageCache()
for (const a of attachments) {
_imageCache.set(a.stagedPath, {
fileName: a.fileName,
@@ -926,7 +933,7 @@ export const useChatStore = defineStore('chat', {
preview: a.preview,
})
}
saveImageCache(_imageCache)
await saveImageCache(_imageCache)
}
let messageContent = trimmed

View File

@@ -2,9 +2,7 @@ import { defineStore } from 'pinia';
import { i18n, setLanguage, getLanguage, type LanguageCode } from '@src/i18n';
import { SUPPORTED_LANGUAGES, SUPPORTED_LANGUAGE_CODES } from '@src/i18n/constants';
import { resolveSupportedLanguage, detectSystemLanguage } from '@src/i18n/resolver';
// 持久化键
const STORAGE_KEY = 'zn-language';
import { IPC_EVENTS, CONFIG_KEYS } from '@lib/constants';
interface LocaleState {
language: LanguageCode;
@@ -37,8 +35,8 @@ export const useLocaleStore = defineStore('locale', {
if (this.initialized) return;
try {
// 1. 尝试从 localStorage 读取持久化设置
const saved = localStorage.getItem(STORAGE_KEY);
// 1. 尝试从 electron-store 读取持久化设置
const saved = await window.api.invoke(IPC_EVENTS.GET_CONFIG, CONFIG_KEYS.LANGUAGE);
let lang: LanguageCode = 'zh';
if (saved && SUPPORTED_LANGUAGE_CODES.includes(saved as LanguageCode)) {
@@ -63,12 +61,12 @@ export const useLocaleStore = defineStore('locale', {
/**
* 设置语言
* @param language 目标语言代码
* @param persist 是否持久化到 localStorage默认为 true
* @param persist 是否持久化到 electron-store默认为 true
*/
async setLanguage(language: LanguageCode, persist: boolean = true) {
// 验证语言代码有效性
const resolvedLang = resolveSupportedLanguage(language);
if (resolvedLang === this.language) {
return; // 语言未变化
}
@@ -80,14 +78,14 @@ export const useLocaleStore = defineStore('locale', {
// 2. 更新 store 状态
this.language = resolvedLang;
// 3. 持久化到 localStorage
// 3. 持久化到 electron-store
if (persist) {
localStorage.setItem(STORAGE_KEY, resolvedLang);
await window.api.invoke(IPC_EVENTS.SET_CONFIG, CONFIG_KEYS.LANGUAGE, resolvedLang);
}
// 4. 触发语言变化事件(供其他组件监听)
window.dispatchEvent(new CustomEvent('language-changed', {
detail: { language: resolvedLang }
window.dispatchEvent(new CustomEvent('language-changed', {
detail: { language: resolvedLang }
}));
} catch (error) {
console.error('Failed to set language:', error);

View File

@@ -42,18 +42,12 @@ const applyThemeToDom = (theme: 'light' | 'dark') => {
};
export const useThemeStore = defineStore('zn-ai-theme', {
state: (): ThemeState => {
// 从 localStorage 恢复缓存的主题,确保在初始化前 UI 不会闪烁
const cachedTheme = typeof localStorage !== 'undefined' ? localStorage.getItem('zn-ai-theme-cache') as Theme : null;
const initialTheme = (cachedTheme === 'light' || cachedTheme === 'dark' || cachedTheme === 'system') ? cachedTheme : 'system';
return {
theme: initialTheme,
isDark: false,
systemTheme: 'light',
initialized: false,
};
},
state: (): ThemeState => ({
theme: 'system',
isDark: false,
systemTheme: 'light',
initialized: false,
}),
actions: {
async init() {
@@ -83,12 +77,7 @@ export const useThemeStore = defineStore('zn-ai-theme', {
this.isDark = isDark;
this.systemTheme = systemTheme;
this.initialized = true;
// 缓存到 localStorage
if (typeof localStorage !== 'undefined') {
localStorage.setItem('zn-ai-theme-cache', savedTheme);
}
// 6. 监听系统主题变化
if (typeof window !== 'undefined') {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
@@ -159,12 +148,7 @@ export const useThemeStore = defineStore('zn-ai-theme', {
// 4. 更新 store 状态
this.theme = theme;
this.isDark = isDark;
// 5. 缓存到 localStorage
if (typeof localStorage !== 'undefined') {
localStorage.setItem('zn-ai-theme-cache', theme);
}
console.log('Theme changed:', { theme, appliedTheme, isDark });
} catch (error) {
console.error('Failed to set theme:', error);