2026-07-21 16:13:16 +08:00

68 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { h, render } from 'vue'
// 启动台应用 — 从 js/apps3.js 手动转写
import { el, iconImg } from '../../utils'
import { Apps, stdMenus } from '../../composables/useApps'
import LaunchpadComponent from './Launchpad.vue'
import { ui as UI } from '../../composables/useUI'
let _AppStoreApp: any = null
export function setAppStoreForLaunchpad(a: any) { _AppStoreApp = a }
export const Launchpad = {
overlay: null as HTMLElement | null,
show() {
if (this.overlay) return this.hide()
const ov = el('div', { class: 'launchpad', tabindex: '0' })
const search = el('input', { class: 'lp-search', type: 'search', placeholder: '搜索 App' }) as HTMLInputElement
const grid = el('div', { class: 'lp-grid' })
ov.append(el('div', { class: 'lp-search-wrap' }, search), grid)
const renderGrid = (q: string) => {
grid.innerHTML = ''
const apps = Object.values(Apps.registry)
.filter((a: any) => a.id !== 'launchpad' && (!a.storeApp || (_AppStoreApp && _AppStoreApp.isInstalled(a.id))))
.filter((a: any) => !q || a.name.toLowerCase().includes(q.toLowerCase()))
if (!apps.length) grid.append(el('div', { class: 'lp-empty', text: '没有匹配的 App' }))
;(apps as any[]).sort((a: any, b: any) => a.name.localeCompare(b.name, 'zh-Hans-CN')).forEach((app: any) => {
const c = el('div', { class: 'lp-cell' }, iconImg(app.icon, '', app.name), el('div', { class: 'lp-name', text: app.name }))
c.addEventListener('click', () => { this.hide(); Apps.open(app.id) })
grid.append(c)
})
}
renderGrid('')
search.addEventListener('input', () => renderGrid(search.value.trim()))
search.addEventListener('keydown', (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === 'Escape') this.hide() })
ov.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key === 'Escape') this.hide() })
ov.addEventListener('click', (e: Event) => { if (e.target === ov) this.hide() })
document.body.append(ov)
this.overlay = ov
setTimeout(() => { ov.classList.add('show'); search.focus() }, 10)
},
hide() {
if (!this.overlay) return
const ov = this.overlay; this.overlay = null
ov.classList.remove('show'); setTimeout(() => ov.remove(), 250)
}
};
(window as any).Launchpad = Launchpad
// 注册启动台应用
Apps.register({
id: 'launchpad', name: '启动台', icon: '/assets/icons/launchpad.png',
menus() { return stdMenus(this) },
render() { /* 不走 WM 窗口 */ },
open() { Launchpad.show() }
})
// 扩展 Apps.open拦截 launchpad/stickies/storeApp
const _origOpen = Apps.open.bind(Apps)
Apps.open = function (id: string, args?: any) {
if (id === 'launchpad') { Launchpad.show(); return null }
if (id === 'stickies' && !(args && args.noteId)) { (Apps.get('stickies') as any)?.openAll(); return null }
const app = this.get(id)
if (app && app.storeApp && !(_AppStoreApp && _AppStoreApp.isInstalled(id))) {
UI.alert('尚未安装', `请先在 App Store 中获取"${app.name}"。`, '/assets/icons/appstore.png')
return null
}
return _origOpen(id, args)
}