tauri.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Tauri API 封装 - 替代原来的 Electron API
  2. import { invoke } from '@tauri-apps/api/core'
  3. import { listen, type UnlistenFn } from '@tauri-apps/api/event'
  4. import { getCurrentWindow, type PhysicalPosition, type PhysicalSize } from '@tauri-apps/api/window'
  5. import { Store } from '@tauri-apps/plugin-store'
  6. // 窗口状态存储
  7. let windowStore: Store | null = null
  8. interface WindowState {
  9. width: number
  10. height: number
  11. x: number
  12. y: number
  13. maximized: boolean
  14. }
  15. async function getWindowStore(): Promise<Store> {
  16. if (!windowStore) {
  17. windowStore = await Store.load('window-state.json')
  18. }
  19. return windowStore
  20. }
  21. import type {
  22. SoftwareType,
  23. SoftwareTypeWithAll,
  24. Platform,
  25. InstallOptions,
  26. InstallStatus,
  27. InstallResult,
  28. InstalledInfo,
  29. AllInstalledInfo,
  30. VersionResult,
  31. PackageManagerResult,
  32. GitMirrorType,
  33. GitMirrorConfig,
  34. NodejsMirrorType,
  35. NodejsMirrorConfig,
  36. InstallHistoryItem,
  37. LogEntry,
  38. UpdateResult,
  39. } from '@shared/types'
  40. // 存储事件监听器的取消函数
  41. const listeners: UnlistenFn[] = []
  42. // 窗口状态保存的防抖定时器
  43. let saveWindowStateTimer: ReturnType<typeof setTimeout> | null = null
  44. const SAVE_DEBOUNCE_MS = 500 // 500ms 防抖延迟
  45. export const tauriAPI = {
  46. // ==================== 安装相关 ====================
  47. async install(software: SoftwareTypeWithAll, options: InstallOptions): Promise<void> {
  48. await invoke('install_software', { software, options })
  49. },
  50. async cancelInstall(): Promise<boolean> {
  51. return await invoke('cancel_install')
  52. },
  53. async checkInstalled(software: SoftwareTypeWithAll): Promise<InstalledInfo | AllInstalledInfo> {
  54. return await invoke('check_installed', { software })
  55. },
  56. async uninstall(software: SoftwareType): Promise<boolean> {
  57. return await invoke('uninstall_software', { software })
  58. },
  59. // ==================== 系统检测 ====================
  60. async checkAdmin(): Promise<boolean> {
  61. return await invoke('check_admin')
  62. },
  63. async checkPackageManager(): Promise<PackageManagerResult> {
  64. return await invoke('check_package_manager')
  65. },
  66. async installPackageManager(manager: string): Promise<{ success: boolean; error?: string }> {
  67. try {
  68. await invoke('install_package_manager', { manager })
  69. return { success: true }
  70. } catch (error) {
  71. return { success: false, error: String(error) }
  72. }
  73. },
  74. async getPlatform(): Promise<Platform> {
  75. return await invoke('get_platform')
  76. },
  77. async checkNetwork(): Promise<boolean> {
  78. return await invoke('check_network')
  79. },
  80. // ==================== 版本 ====================
  81. async getVersions(software: SoftwareType): Promise<VersionResult> {
  82. return await invoke('get_versions', { software })
  83. },
  84. async checkUpdate(software: SoftwareType): Promise<{ hasUpdate: boolean; latestVersion?: string }> {
  85. return await invoke('check_update', { software })
  86. },
  87. // ==================== Git 镜像配置 ====================
  88. async setGitMirror(mirror: GitMirrorType): Promise<void> {
  89. await invoke('set_git_mirror', { mirror })
  90. },
  91. async getGitMirrorConfig(): Promise<GitMirrorConfig> {
  92. return await invoke('get_git_mirror_config')
  93. },
  94. // ==================== Node.js 镜像配置 ====================
  95. async setNodejsMirror(mirror: NodejsMirrorType): Promise<void> {
  96. await invoke('set_nodejs_mirror', { mirror })
  97. },
  98. async getNodejsMirrorConfig(): Promise<NodejsMirrorConfig> {
  99. return await invoke('get_nodejs_mirror_config')
  100. },
  101. // ==================== 历史和日志 ====================
  102. async getInstallHistory(limit?: number): Promise<InstallHistoryItem[]> {
  103. return await invoke('get_install_history', { limit })
  104. },
  105. async getLogs(): Promise<LogEntry[]> {
  106. return await invoke('get_logs')
  107. },
  108. async writeInstallLog(message: string, level?: 'info' | 'warn' | 'error'): Promise<void> {
  109. await invoke('write_install_log', { message, level })
  110. },
  111. async getLogPaths(): Promise<{ appLog: string; installLog: string }> {
  112. return await invoke('get_log_paths')
  113. },
  114. // ==================== 文件夹选择 ====================
  115. async selectDirectory(defaultPath?: string): Promise<{ canceled: boolean; path: string | null }> {
  116. return await invoke('select_directory', { defaultPath })
  117. },
  118. // ==================== 窗口操作 ====================
  119. async setWindowTitle(title: string): Promise<void> {
  120. await invoke('set_window_title', { title })
  121. },
  122. async windowMinimize(): Promise<void> {
  123. await invoke('window_minimize')
  124. },
  125. async windowMaximize(): Promise<boolean> {
  126. return await invoke('window_maximize')
  127. },
  128. async windowClose(): Promise<void> {
  129. await invoke('window_close')
  130. },
  131. async windowIsMaximized(): Promise<boolean> {
  132. return await invoke('window_is_maximized')
  133. },
  134. // 保存窗口状态(内部实现)
  135. async _doSaveWindowState(): Promise<void> {
  136. try {
  137. const window = getCurrentWindow()
  138. const size = await window.innerSize()
  139. const position = await window.outerPosition()
  140. const maximized = await window.isMaximized()
  141. const state: WindowState = {
  142. width: size.width,
  143. height: size.height,
  144. x: position.x,
  145. y: position.y,
  146. maximized,
  147. }
  148. const store = await getWindowStore()
  149. await store.set('windowState', state)
  150. await store.save()
  151. } catch (error) {
  152. console.error('Failed to save window state:', error)
  153. }
  154. },
  155. // 保存窗口状态(带防抖)
  156. saveWindowState(): void {
  157. if (saveWindowStateTimer) {
  158. clearTimeout(saveWindowStateTimer)
  159. }
  160. saveWindowStateTimer = setTimeout(() => {
  161. this._doSaveWindowState()
  162. saveWindowStateTimer = null
  163. }, SAVE_DEBOUNCE_MS)
  164. },
  165. // 立即保存窗口状态(用于关闭前)
  166. async saveWindowStateImmediate(): Promise<void> {
  167. if (saveWindowStateTimer) {
  168. clearTimeout(saveWindowStateTimer)
  169. saveWindowStateTimer = null
  170. }
  171. await this._doSaveWindowState()
  172. },
  173. // 开始监听窗口状态变化(resize 和 move)
  174. startWindowStateListener(): void {
  175. const window = getCurrentWindow()
  176. // 监听窗口大小变化
  177. window.onResized(() => {
  178. this.saveWindowState()
  179. }).then((unlisten) => {
  180. listeners.push(unlisten)
  181. })
  182. // 监听窗口位置变化
  183. window.onMoved(() => {
  184. this.saveWindowState()
  185. }).then((unlisten) => {
  186. listeners.push(unlisten)
  187. })
  188. },
  189. // 恢复窗口状态
  190. async restoreWindowState(): Promise<void> {
  191. try {
  192. const store = await getWindowStore()
  193. const state = await store.get<WindowState>('windowState')
  194. if (state) {
  195. const window = getCurrentWindow()
  196. // 先设置位置和大小
  197. if (!state.maximized) {
  198. await window.setPosition(new PhysicalPosition(state.x, state.y))
  199. await window.setSize(new PhysicalSize(state.width, state.height))
  200. }
  201. // 如果之前是最大化状态,则最大化
  202. if (state.maximized) {
  203. await window.maximize()
  204. }
  205. }
  206. } catch (error) {
  207. console.error('Failed to restore window state:', error)
  208. }
  209. },
  210. // ==================== Claude Code ====================
  211. async checkClaudeCode(): Promise<{ installed: boolean; version: string | null }> {
  212. return await invoke('check_claude_code')
  213. },
  214. async launchClaudeCode(): Promise<{ success: boolean }> {
  215. return await invoke('launch_claude_code')
  216. },
  217. async installClaudeCode(): Promise<{ success: boolean; error?: string }> {
  218. return await invoke('install_claude_code')
  219. },
  220. // ==================== VS Code Extensions ====================
  221. async checkVscodeExtension(extensionId: string): Promise<{ installed: boolean; version?: string }> {
  222. return await invoke('check_vscode_extension', { extensionId })
  223. },
  224. async installVscodeExtension(extensionId: string): Promise<{ success: boolean; error?: string }> {
  225. return await invoke('install_vscode_extension', { extensionId })
  226. },
  227. // ==================== 事件监听 ====================
  228. // 注意:这些方法是同步的,以保持与原 Electron API 的兼容性
  229. // 内部使用 Promise 但不返回它
  230. onInstallStatus(callback: (data: InstallStatus) => void): void {
  231. listen<InstallStatus>('install-status', (event) => {
  232. callback(event.payload)
  233. }).then((unlisten) => {
  234. listeners.push(unlisten)
  235. })
  236. },
  237. onInstallComplete(callback: (data: InstallResult) => void): void {
  238. listen<InstallResult>('install-complete', (event) => {
  239. callback(event.payload)
  240. }).then((unlisten) => {
  241. listeners.push(unlisten)
  242. })
  243. },
  244. onInstallError(callback: (data: InstallResult) => void): void {
  245. listen<InstallResult>('install-error', (event) => {
  246. callback(event.payload)
  247. }).then((unlisten) => {
  248. listeners.push(unlisten)
  249. })
  250. },
  251. onNetworkChange(callback: (online: boolean) => void): void {
  252. listen<boolean>('network-change', (event) => {
  253. callback(event.payload)
  254. }).then((unlisten) => {
  255. listeners.push(unlisten)
  256. })
  257. },
  258. removeAllListeners(): void {
  259. listeners.forEach((unlisten) => unlisten())
  260. listeners.length = 0
  261. },
  262. // ==================== 自动更新 ====================
  263. async updaterCheck(): Promise<UpdateResult> {
  264. return await invoke('updater_check')
  265. },
  266. async updaterDownload(): Promise<UpdateResult> {
  267. return await invoke('updater_download')
  268. },
  269. async updaterInstall(): Promise<void> {
  270. await invoke('updater_install')
  271. },
  272. async updaterVersion(): Promise<string> {
  273. return await invoke('updater_version')
  274. },
  275. async updaterIsPortable(): Promise<boolean> {
  276. return await invoke('updater_is_portable')
  277. },
  278. onUpdaterStatus(callback: (data: UpdateResult) => void): void {
  279. listen<UpdateResult>('updater-status', (event) => {
  280. callback(event.payload)
  281. }).then((unlisten) => {
  282. listeners.push(unlisten)
  283. })
  284. },
  285. }
  286. // 导出类型以便兼容
  287. export type TauriAPI = typeof tauriAPI