性能优化
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* app Provider:扫描开始菜单应用。
|
||||
* 1 分钟缓存减少重复 IPC;图标按需加载(前端 Map 缓存,避免重复请求)。
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { bestScore } from '../engine'
|
||||
import type { QPItem, QPProvider } from './types'
|
||||
import { buildItemForms, makeAppLaunch, makeAppSubActions } from './utils'
|
||||
|
||||
interface AppRecord {
|
||||
name: string
|
||||
path: string
|
||||
}
|
||||
|
||||
let appCache: AppRecord[] | null = null
|
||||
let appCacheTime = 0
|
||||
const APP_CACHE_TTL = 60_000 // 1 分钟缓存
|
||||
|
||||
async function loadApps(): Promise<AppRecord[]> {
|
||||
if (appCache && Date.now() - appCacheTime < APP_CACHE_TTL) {
|
||||
return appCache
|
||||
}
|
||||
try {
|
||||
const apps = await invoke<AppRecord[]>('quickpanel_scan_apps')
|
||||
appCache = apps
|
||||
appCacheTime = Date.now()
|
||||
return apps
|
||||
} catch (e) {
|
||||
console.error('[quickpanel] 扫描应用失败:', e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export class AppProvider implements QPProvider {
|
||||
id = 'app'
|
||||
label = '应用'
|
||||
priority = 95
|
||||
|
||||
async search(query: string): Promise<QPItem[]> {
|
||||
const apps = await loadApps()
|
||||
if (!query.trim()) {
|
||||
// 空查询:不显示应用(避免列表过长),由命令入口承担
|
||||
return []
|
||||
}
|
||||
const results: Array<{ item: QPItem; score: number }> = []
|
||||
let idx = 0
|
||||
for (const app of apps) {
|
||||
const forms = buildItemForms(app.name)
|
||||
const score = bestScore(query, forms)
|
||||
if (score >= 0) {
|
||||
results.push({
|
||||
item: {
|
||||
id: `app-${idx}`,
|
||||
title: app.name,
|
||||
subtitle: app.path,
|
||||
group: '应用',
|
||||
iconPath: app.path,
|
||||
action: makeAppLaunch(app.path),
|
||||
subActions: makeAppSubActions(app.path),
|
||||
appRank: 0, // 开始菜单:最可靠来源
|
||||
},
|
||||
score,
|
||||
})
|
||||
}
|
||||
idx++
|
||||
}
|
||||
results.sort((a, b) => b.score - a.score)
|
||||
return results.slice(0, 15).map(r => ({ ...r.item, score: r.score }))
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 应用图标按需加载 =====
|
||||
// 前端缓存(path -> dataUrl)。Rust 侧另有内存 + 磁盘缓存,此处仅避免重复 IPC。
|
||||
|
||||
const appIconCache = new Map<string, string>() // path -> dataUrl('' = 无图标)
|
||||
|
||||
/** 为搜索结果中带 iconPath 的项(应用、历史中的应用)按需加载图标(data URL),
|
||||
* 并写入 item.iconUrl 触发响应式更新。
|
||||
* 命中前端缓存时同步返回;否则异步调用 Rust 命令(命中 Rust 缓存则零开销)。 */
|
||||
export async function loadAppIconsForResults(items: QPItem[]): Promise<void> {
|
||||
const toLoad: QPItem[] = []
|
||||
for (const item of items) {
|
||||
if (!item.iconPath) continue
|
||||
if (item.iconUrl !== undefined) continue // 已设置(含加载中)
|
||||
const cached = appIconCache.get(item.iconPath)
|
||||
if (cached !== undefined) {
|
||||
item.iconUrl = cached
|
||||
} else {
|
||||
item.iconUrl = '' // 标记加载中,避免重复请求
|
||||
toLoad.push(item)
|
||||
}
|
||||
}
|
||||
if (!toLoad.length) return
|
||||
await Promise.all(
|
||||
toLoad.map(async item => {
|
||||
const path = item.iconPath!
|
||||
try {
|
||||
const url = await invoke<string | null>('quickpanel_get_app_icon', { path })
|
||||
const u = url ?? ''
|
||||
appIconCache.set(path, u)
|
||||
item.iconUrl = u
|
||||
} catch {
|
||||
appIconCache.set(path, '')
|
||||
item.iconUrl = ''
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/** 清空前端图标缓存(Rust 端清理命令 quickpanel_clear_app_icon_cache 调用后可一并清空) */
|
||||
export function invalidateAppIconCache() {
|
||||
appIconCache.clear()
|
||||
}
|
||||
Reference in New Issue
Block a user