/** * system/index.ts — 系统启动入口 * * 将 original main.js 中的桌面图标渲染和启动序列集成到模块化架构中。 */ import { $, $$, el, esc, clamp, iconImg } from '../utils' import { bus } from '../composables/useBus' import { store } from '../composables/useStore' import { fs } from '../composables/useFS' import { wm } from '../composables/useWM' import { ui } from '../composables/useUI' import { Sys } from '../composables/useSys' import { Notify } from '../composables/useNotify' import { Spotlight } from '../composables/useSpotlight' import { Apps } from '../composables/useApps' import { WALLPAPERS } from '../composables/useSettings' // 注册所有应用 import '../apps/index' // ==================== 桌面图标渲染 ==================== Sys.renderDesktopIcons = function () { const container = $('#desktop-icons')! const items = fs.list(fs.HOME + '/Desktop') const positions: Record = store.get('desktop-positions', {}) container.innerHTML = '' items.forEach((item: any, i: number) => { const pos = positions[item.name] || { x: 20 + (i % 6) * 110, y: 20 + Math.floor(i / 6) * 110 } const icon = el('div', { class: 'desk-icon', style: { left: pos.x + 'px', top: pos.y + 'px' }, dataset: { path: item.path, name: item.name } }, iconImg(fs.iconFor(item.path), '', item.name), el('span', { class: 'desk-label', text: item.name })) // 单击选中 icon.addEventListener('click', (e: MouseEvent) => { $$('.desk-icon.sel').forEach((n: HTMLElement) => n.classList.remove('sel')) icon.classList.add('sel') }) // 双击打开 icon.addEventListener('dblclick', (e: MouseEvent) => { Apps.openPath(item.path) }) // 拖动 let dragging = false, startX = 0, startY = 0, origX = 0, origY = 0 icon.addEventListener('pointerdown', (e: PointerEvent) => { if (e.button !== 0) return dragging = true; startX = e.clientX; startY = e.clientY origX = parseInt(icon.style.left) || 0; origY = parseInt(icon.style.top) || 0 icon.setPointerCapture(e.pointerId) }) icon.addEventListener('pointermove', (e: PointerEvent) => { if (!dragging) return const dx = e.clientX - startX, dy = e.clientY - startY icon.style.left = (origX + dx) + 'px' icon.style.top = (origY + dy) + 'px' }) icon.addEventListener('pointerup', () => { if (dragging) { dragging = false positions[item.name] = { x: parseInt(icon.style.left) || 0, y: parseInt(icon.style.top) || 0 } store.set('desktop-positions', positions) } }) // 右键菜单 icon.addEventListener('contextmenu', (e: MouseEvent) => { e.preventDefault(); e.stopPropagation() ui.contextMenu([ { label: '打开', action: () => Apps.openPath(item.path) }, { label: '重命名…', action: async () => { const newName = await ui.prompt('重命名', '输入新名称:', item.name) if (newName && newName !== item.name) { try { fs.rename(item.path, newName); Sys.renderDesktopIcons() } catch (e: any) { ui.alert('错误', e.message) } } }}, { sep: true }, { label: '移到废纸篓', action: () => { fs.trash(item.path); Sys.renderDesktopIcons() } }, ], e) }) container.append(icon) }) } // ==================== 桌面空白区域事件 ==================== function initDesktop() { const desktop = $('#desktop')! desktop.addEventListener('click', (e: MouseEvent) => { if ((e.target as Element).closest('.desk-icon')) return $$('.desk-icon.sel').forEach((n: HTMLElement) => n.classList.remove('sel')) }) desktop.addEventListener('contextmenu', (e: MouseEvent) => { if ((e.target as Element).closest('.desk-icon')) return e.preventDefault() ui.contextMenu([ { label: '新建文件夹', action: () => { try { fs.mkdir(fs.HOME + '/Desktop/未命名文件夹'); Sys.renderDesktopIcons() } catch (e: any) { ui.alert('错误', e.message) } }}, { sep: true }, { label: '整理', action: () => { store.remove('desktop-positions'); Sys.renderDesktopIcons() } }, { label: '排序方式', submenu: [ { label: '名称', checked: Sys.settings.finderSort === 'name', action: () => { Sys.settings.finderSort = 'name'; Sys.save(); Sys.renderDesktopIcons() } }, { label: '修改日期', checked: Sys.settings.finderSort === 'date', action: () => { Sys.settings.finderSort = 'date'; Sys.save(); Sys.renderDesktopIcons() } }, ]}, { sep: true }, { label: '更换墙纸…', action: () => Apps.open('settings', { pane: 'wallpaper' }) }, ], e) }) // 刷新桌面图标的文件变更监听 bus.on('fs:changed', (payload: any) => { if (!payload || !payload.paths) return const desktopPath = fs.HOME + '/Desktop' if (payload.paths.some((p: string) => p === desktopPath || p.startsWith(desktopPath + '/'))) Sys.renderDesktopIcons() }) } // ==================== 窗口聚焦 → 菜单栏切换 ==================== bus.on('wm:focus', (win: any) => { Sys.setActiveApp(win ? win.appId : 'finder') }) bus.on('wm:changed', () => { if (!wm.windows.length) Sys.setActiveApp('finder') }) // ==================== 全局 Escape 关闭浮层 ==================== document.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key !== 'Escape' || !Sys.unlocked) return if (!$('#spotlight')!.classList.contains('hidden')) { Spotlight.close(); return } if (!$('#control-center')!.classList.contains('hidden')) { $('#control-center')!.classList.add('hidden'); return } if (!$('#notification-center')!.classList.contains('hidden')) { Notify.hideCenter(); return } ui.closeAllMenus() }) // ==================== 通知中心外部点击关闭 ==================== document.addEventListener('pointerdown', (e: PointerEvent) => { const nc = $('#notification-center')! if (!nc.classList.contains('hidden') && !(e.target as Element).closest('#notification-center') && !(e.target as Element).closest('#mb-clock')) { Notify.hideCenter() } }, true) // ==================== 启动序列 ==================== export function initSystem() { fs.init() Sys.init() Notify.init() Sys.renderDock() initDesktop() bus.emit('apps:ready') Sys.boot() } // Expose globals for compatibility ;(window as any).Sys = Sys ;(window as any).Notify = Notify ;(window as any).Spotlight = Spotlight ;(window as any).WM = wm ;(window as any).UI = ui ;(window as any).FS = fs ;(window as any).Apps = Apps ;(window as any).Bus = bus ;(window as any).Store = store ;(window as any).WALLPAPERS = WALLPAPERS ;(window as any).$ = $ ;(window as any).$$ = $$ ;(window as any).el = el ;(window as any).esc = esc ;(window as any).clamp = clamp ;(window as any).iconImg = iconImg