移动并重命名 code-launchpad.ts
This commit is contained in:
326
src/main/code-launchpad/ide-versions-check.ts
Normal file
326
src/main/code-launchpad/ide-versions-check.ts
Normal file
@@ -0,0 +1,326 @@
|
||||
import type {
|
||||
checkIDEResultDto,
|
||||
checkIDEsResultDto,
|
||||
checkIDEsVersionDto,
|
||||
checkIDEVersionDto
|
||||
} from '@my-type/settings'
|
||||
import { execSync } from 'node:child_process'
|
||||
import { BrowserWindow, screen, shell, Tray } from 'electron'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import path from 'path'
|
||||
import { loadJsonFile } from 'load-json-file'
|
||||
import os from 'os'
|
||||
import {
|
||||
JetBrainsIDEDisplayNameEnum as JIN,
|
||||
JetBrainsProductCode,
|
||||
JetBrainsStateDto
|
||||
} from '@my-type/jetbrains-state-tools'
|
||||
import { settingsManager } from '../settings'
|
||||
import { codeLaunchpadIcon } from '../resources'
|
||||
import { isNodeError } from '@my-type/node-error'
|
||||
import { JetBrainsDataProductDto } from '@my-type/jetbrains-data-products'
|
||||
|
||||
const JETBRAINS_TOOLBOX_STATE_JSON_PATH = path.join(
|
||||
os.homedir(),
|
||||
'AppData/Local/JetBrains/Toolbox/state.json'
|
||||
)
|
||||
|
||||
/**
|
||||
* 通过 where.exe 检查系统中的指定 IDE 是否安装可用。
|
||||
* @param display IDE 的正式显示名称,例如 `Visual Studio Code`。
|
||||
* @param command IDE 的命令行别名,例如 `code`。
|
||||
* @param code (JetBrains IDE)的产品代号;对于其他 IDE 为空字符串
|
||||
*/
|
||||
function checkIDE(
|
||||
display: string,
|
||||
command: string,
|
||||
code: JetBrainsProductCode | '' = ''
|
||||
): checkIDEResultDto | null {
|
||||
try {
|
||||
const paths = execSync(`where.exe ${command}`).toString().split('\n').slice(0, -1)
|
||||
return { code, display: display, command: command, paths: paths }
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 JerBrains IDEs 是否安装可用。
|
||||
*/
|
||||
async function checkJetBrainsIDEs(): Promise<checkIDEsResultDto> {
|
||||
// 构建数据结构的辅助函数
|
||||
const _ = (command: string, code: JetBrainsProductCode): checkIDEResultDto => {
|
||||
return {
|
||||
code,
|
||||
command,
|
||||
display: JIN[command],
|
||||
paths: []
|
||||
}
|
||||
}
|
||||
const result: checkIDEsResultDto = {
|
||||
pycharm: _('pycharm', 'PY'),
|
||||
clion: _('clion', 'CL'),
|
||||
webstorm: _('webstorm', 'WS'),
|
||||
phpstorm: _('phpstorm', 'PS'),
|
||||
idea: _('idea', 'IU')
|
||||
}
|
||||
// 优先从 JBTState.json 查找
|
||||
if (settingsManager._settings?.codeLaunchpadIDESearchPolicy.includes('JBTState.json')) {
|
||||
// 处理文件不存在,即 JetBrains Toolbox 相关问题
|
||||
try {
|
||||
const data: JetBrainsStateDto = await loadJsonFile(JETBRAINS_TOOLBOX_STATE_JSON_PATH)
|
||||
for (const ide of data.tools) {
|
||||
for (const resultElement of Object.values(result)) {
|
||||
if (resultElement?.code === ide.productCode) {
|
||||
resultElement.paths = [ide.launchCommand]
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNodeError(error)) {
|
||||
if (error.code === 'ENOENT') {
|
||||
console.log('由于 JBTState.json 不存在,回退到 where.exe。')
|
||||
}
|
||||
} else {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 从 where.exe 查找
|
||||
// 此分支确保在设置中启用 where.exe 方式,以及 JBTState.json 方式没有结果(未找到任何,或出现错误)时进入
|
||||
if (
|
||||
settingsManager._settings?.codeLaunchpadIDESearchPolicy.includes('where.exe') &&
|
||||
Object.entries(result).length === 0
|
||||
) {
|
||||
result['clion'] = checkIDE(JIN.clion, 'clion', 'CL')
|
||||
result['pycharm'] = checkIDE(JIN.pycharm, 'pycharm', 'PY')
|
||||
result['webstorm'] = checkIDE(JIN.webstorm, 'webstorm', 'WS')
|
||||
result['phpstorm'] = checkIDE(JIN.phpstorm, 'phpstorm', 'PS')
|
||||
result['idea'] = checkIDE(JIN.idea, 'idea', 'IU')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统中的各 IDE 是否安装可用。
|
||||
*/
|
||||
export async function getIDEs(): Promise<checkIDEsResultDto> {
|
||||
return global.installedIDEs === null ? await checkIDEs() : global.installedIDEs
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统中的各 IDE 是否安装可用。
|
||||
* 原理是运行 `where.exe ${command}` 并检查返回值。
|
||||
*/
|
||||
export async function checkIDEs(): Promise<checkIDEsResultDto> {
|
||||
console.log('在系统中查找已安装的 IDE ...')
|
||||
const vscodeIDEs = {
|
||||
vscode: checkIDE('Visual Studio Code', 'code')
|
||||
}
|
||||
global.installedIDEs = { ...vscodeIDEs, ...(await checkJetBrainsIDEs()) }
|
||||
return global.installedIDEs
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 VSCode 的版本信息,通过 code --version。
|
||||
*/
|
||||
async function checkVSCodeVersion(): Promise<checkIDEVersionDto> {
|
||||
const install = execSync('code --version').toString().split('\n')[0]
|
||||
let latest = 'unknown'
|
||||
try {
|
||||
latest = await fetch('https://update.code.visualstudio.com/api/releases/stable')
|
||||
.then((res) => res.json())
|
||||
.then((data) => data[0])
|
||||
} catch (error) {
|
||||
console.error('获取 VSCode 版本列表时出现错误。错误提供如下。')
|
||||
console.error(error)
|
||||
}
|
||||
return { code: '', install, latest, display: 'Visual Studio Code' }
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 JetBrains IDE 的版本信息
|
||||
*/
|
||||
export async function checkJetBrainsIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||||
// 构建数据结构的辅助函数
|
||||
const _ = (display: string, code: JetBrainsProductCode): checkIDEVersionDto => {
|
||||
return {
|
||||
code,
|
||||
display,
|
||||
install: 'unknown',
|
||||
latest: 'unknown'
|
||||
}
|
||||
}
|
||||
const result: checkIDEsVersionDto = {
|
||||
pycharm: _('pycharm', 'PY'),
|
||||
clion: _('clion', 'CL'),
|
||||
webstorm: _('webstorm', 'WS'),
|
||||
phpstorm: _('phpstorm', 'PS'),
|
||||
idea: _('idea', 'IU')
|
||||
}
|
||||
|
||||
// 尝试从 JBTState.json 获取已安装的 JetBrains IDEs 的版本
|
||||
// TODO:添加更多获取 JetBrains IDEs 已安装版本的方案
|
||||
try {
|
||||
const data: JetBrainsStateDto = await loadJsonFile(JETBRAINS_TOOLBOX_STATE_JSON_PATH)
|
||||
for (const tool of data.tools) {
|
||||
for (const resultElement of Object.values(result)) {
|
||||
if (resultElement?.code === tool.productCode) {
|
||||
resultElement.install = tool.displayVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNodeError(error)) {
|
||||
if (error.code === 'ENOENT') {
|
||||
console.log('由于 JBTState.json 不存在,检查 JetBrains IDEs 已安装版本的任务失败。')
|
||||
}
|
||||
} else {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 从命令行获取 JetBrains IDE 的已安装版本的方案仍然在科研中
|
||||
// 所以这个方法就暂时先结束了~
|
||||
|
||||
// 从网络接口获取 JetBrains IDEs 的版本列表
|
||||
const codes: JetBrainsProductCode[] = []
|
||||
for (const resultElement of Object.values(result)) {
|
||||
codes.push(<JetBrainsProductCode>resultElement?.code)
|
||||
}
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
type: 'release',
|
||||
code: codes.join(',')
|
||||
})
|
||||
const data: JetBrainsDataProductDto[] = await fetch(
|
||||
`https://data.services.jetbrains.com/products?${params}`
|
||||
).then((res) => res.json())
|
||||
for (const datum of data) {
|
||||
for (const resultElement of Object.values(result)) {
|
||||
if (resultElement?.code === datum.intellijProductCode) {
|
||||
resultElement.latest = datum.releases[0].version
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('从 JetBrains IDEs 版本列表接口获取数据失败。错误提供如下。')
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取各 IDE 的版本信息。
|
||||
*/
|
||||
export async function getIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||||
return global.installedIDEsVersion === null
|
||||
? await checkIDEsVersion()
|
||||
: global.installedIDEsVersion
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查各 IDE 的版本信息。
|
||||
*/
|
||||
export async function checkIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||||
let result: checkIDEsVersionDto = {}
|
||||
for (const ide in await getIDEs()) {
|
||||
switch (ide) {
|
||||
case 'vscode':
|
||||
result['vscode'] = await checkVSCodeVersion()
|
||||
break
|
||||
}
|
||||
}
|
||||
result = { ...result, ...(await checkJetBrainsIDEsVersion()) }
|
||||
global.installedIDEsVersion = result
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建代码启动台窗口。
|
||||
* @return 布尔值,表明创建是否成功
|
||||
*/
|
||||
export function createCodeLaunchpadWindow(closeOnBlur: boolean): boolean {
|
||||
// 不允许重复创建
|
||||
if (global.codeLaunchpadWindow !== null) {
|
||||
return false
|
||||
}
|
||||
|
||||
const windowWidth = settingsManager._settings?.codeLaunchpadWidth
|
||||
? settingsManager._settings?.codeLaunchpadWidth
|
||||
: 460
|
||||
const windowHeight = settingsManager._settings?.codeLaunchpadHeight
|
||||
? settingsManager._settings?.codeLaunchpadHeight
|
||||
: 760
|
||||
const position = settingsManager._settings?.codeLaunchpadPosition
|
||||
? settingsManager._settings.codeLaunchpadPosition
|
||||
: 'left top'
|
||||
|
||||
const codeLaunchpadWindow = new BrowserWindow({
|
||||
width: windowWidth,
|
||||
height: windowHeight,
|
||||
x:
|
||||
position === 'left top' || position === 'left bottom'
|
||||
? 0
|
||||
: screen.getPrimaryDisplay().workArea.width - windowWidth,
|
||||
y:
|
||||
position === 'left top' || position === 'right top'
|
||||
? 0
|
||||
: screen.getPrimaryDisplay().workArea.height - windowHeight,
|
||||
frame: false,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
// 代码启动台需要置顶
|
||||
alwaysOnTop: true,
|
||||
resizable: false,
|
||||
backgroundColor: '#1f1f1f',
|
||||
icon: codeLaunchpadIcon,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.mjs'),
|
||||
sandbox: false
|
||||
}
|
||||
})
|
||||
|
||||
codeLaunchpadWindow.on('ready-to-show', () => {
|
||||
codeLaunchpadWindow.show()
|
||||
})
|
||||
|
||||
// 如有必要,失去焦点时自动关闭
|
||||
if (closeOnBlur) {
|
||||
codeLaunchpadWindow.on('blur', () => {
|
||||
codeLaunchpadWindow.close()
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭代码启动台时
|
||||
codeLaunchpadWindow.on('close', () => {
|
||||
global.codeLaunchpadWindow = null
|
||||
})
|
||||
|
||||
codeLaunchpadWindow.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
// 开发和生产环境的各自设置
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
codeLaunchpadWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '#/codeLaunchpad/IDEs')
|
||||
} else {
|
||||
codeLaunchpadWindow.loadFile(path.join(__dirname, '../renderer/index.html'), {
|
||||
hash: '/codeLaunchpad/IDEs'
|
||||
})
|
||||
}
|
||||
|
||||
global.codeLaunchpadWindow = codeLaunchpadWindow
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function createCodeLaunchpadTray(): Tray {
|
||||
const tray = new Tray(codeLaunchpadIcon)
|
||||
|
||||
tray.on('click', () => createCodeLaunchpadWindow(true))
|
||||
|
||||
return tray
|
||||
}
|
||||
Reference in New Issue
Block a user