94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
/**
|
||
* special Provider:Windows 常用快捷位置。
|
||
* 列表由 Rust 提供(quickpanel_get_special_locations),1 分钟缓存。
|
||
*/
|
||
import { bestScore } from '../engine'
|
||
import type { QPItem, QPProvider } from './types'
|
||
import { buildItemForms } from './utils'
|
||
// Rust 端通过 tauri-specta 生成的命令绑定与类型(bindings.ts)
|
||
import { commands, type SpecialLocation } from '@/lib/bindings'
|
||
|
||
let specialCache: SpecialLocation[] | null = null
|
||
let specialCacheTime = 0
|
||
const SPECIAL_CACHE_TTL = 60_000 // 1 分钟缓存
|
||
|
||
async function loadSpecials(): Promise<SpecialLocation[]> {
|
||
if (specialCache && Date.now() - specialCacheTime < SPECIAL_CACHE_TTL) {
|
||
return specialCache
|
||
}
|
||
try {
|
||
const list = await commands.quickpanelGetSpecialLocations()
|
||
specialCache = list
|
||
specialCacheTime = Date.now()
|
||
return list
|
||
} catch (e) {
|
||
console.error('[quickpanel] 获取快捷位置失败:', e)
|
||
return []
|
||
}
|
||
}
|
||
|
||
export class SpecialProvider implements QPProvider {
|
||
id = 'special'
|
||
label = '快捷'
|
||
priority = 60
|
||
|
||
async search(query: string): Promise<QPItem[]> {
|
||
const list = await loadSpecials()
|
||
if (!list.length) return []
|
||
if (!query.trim()) return [] // 空查询不占用列表,由用户主动搜索
|
||
|
||
const open = async (s: SpecialLocation) => {
|
||
try {
|
||
await commands.quickpanelOpenSpecial(s.kind, s.target, s.args)
|
||
} catch (e) {
|
||
console.error('[quickpanel] 打开快捷位置失败:', e)
|
||
}
|
||
}
|
||
|
||
const items: QPItem[] = list.map(s => ({
|
||
id: `sp-${s.id}`,
|
||
title: s.title,
|
||
subtitle: s.subtitle,
|
||
group: '快捷',
|
||
action: () => open(s),
|
||
subActions:
|
||
s.kind === 'file'
|
||
? [
|
||
{ id: 'open', label: '打开', action: () => open(s) },
|
||
{
|
||
id: 'reveal',
|
||
label: '在资源管理器中显示',
|
||
action: async () => {
|
||
try {
|
||
await commands.quickpanelRevealInExplorer(s.target)
|
||
} catch (e) {
|
||
console.error('[quickpanel] 资源管理器显示失败:', e)
|
||
}
|
||
},
|
||
},
|
||
{
|
||
id: 'copy-path',
|
||
label: '复制路径',
|
||
action: async () => {
|
||
try {
|
||
await navigator.clipboard.writeText(s.target)
|
||
} catch {
|
||
/* 忽略 */
|
||
}
|
||
},
|
||
},
|
||
]
|
||
: undefined,
|
||
}))
|
||
|
||
const scored: Array<{ item: QPItem; score: number }> = []
|
||
items.forEach((item, idx) => {
|
||
const forms = buildItemForms(item.title, list[idx].keywords)
|
||
const score = bestScore(query, forms)
|
||
if (score >= 0) scored.push({ item, score })
|
||
})
|
||
scored.sort((a, b) => b.score - a.score)
|
||
return scored.slice(0, 8).map(s => ({ ...s.item, score: s.score }))
|
||
}
|
||
}
|