Compare commits
5 Commits
6002ac1418
...
9eb44cac9c
| Author | SHA1 | Date | |
|---|---|---|---|
| 9eb44cac9c | |||
| 4730f7c948 | |||
| 00b5ed9a79 | |||
| aa16a81d8c | |||
| 615dd44129 |
11
.idea/inspectionProfiles/Project_Default.xml
generated
11
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +1,17 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="CssUnknownProperty" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myCustomPropertiesEnabled" value="true" />
|
||||
<option name="myIgnoreVendorSpecificProperties" value="false" />
|
||||
<option name="myCustomPropertiesList">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="app-region" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
|
||||
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
125
package-lock.json
generated
125
package-lock.json
generated
@@ -36,6 +36,7 @@
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"electron": "^39.2.6",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-devtools-installer": "^4.0.0",
|
||||
"electron-vite": "^5.0.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-plugin-vue": "^10.6.2",
|
||||
@@ -4272,8 +4273,7 @@
|
||||
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/crc": {
|
||||
"version": "3.8.0",
|
||||
@@ -4977,6 +4977,16 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-devtools-installer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/electron-devtools-installer/-/electron-devtools-installer-4.0.0.tgz",
|
||||
"integrity": "sha512-9Tntu/jtfSn0n6N/ZI6IdvRqXpDyLQiDuuIbsBI+dL+1Ef7C8J2JwByw58P3TJiNeuqyV3ZkphpNWuZK5iSY2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unzip-crx-3": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-publish": {
|
||||
"version": "26.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-26.8.1.tgz",
|
||||
@@ -6435,6 +6445,13 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.5.tgz",
|
||||
@@ -6577,6 +6594,13 @@
|
||||
"url": "https://github.com/sponsors/mesqueeb"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/isbinaryfile": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
|
||||
@@ -6725,6 +6749,52 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
|
||||
@@ -6754,6 +6824,16 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/load-json-file": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/load-json-file/-/load-json-file-7.0.1.tgz",
|
||||
@@ -7190,7 +7270,6 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -7603,6 +7682,13 @@
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"dev": true,
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -7923,6 +8009,13 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
|
||||
@@ -8696,6 +8789,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -9497,6 +9597,18 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/unzip-crx-3": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz",
|
||||
"integrity": "sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jszip": "^3.1.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"yaku": "^0.16.6"
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
@@ -10449,6 +10561,13 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yaku": {
|
||||
"version": "0.16.7",
|
||||
"resolved": "https://registry.npmmirror.com/yaku/-/yaku-0.16.7.tgz",
|
||||
"integrity": "sha512-Syu3IB3rZvKvYk7yTiyl1bo/jiEFaaStrgv1V2TIJTqYPStSMQVO8EQjg/z+DRzLq/4LIIharNT3iH1hylEIRw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"electron": "^39.2.6",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-devtools-installer": "^4.0.0",
|
||||
"electron-vite": "^5.0.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-plugin-vue": "^10.6.2",
|
||||
|
||||
93
src/main/code-launchpad/code-launchpad.ts
Normal file
93
src/main/code-launchpad/code-launchpad.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { BrowserWindow, screen, shell, Tray } from 'electron'
|
||||
import { settingsManager } from '../settings'
|
||||
import { codeLaunchpadIcon } from '../resources'
|
||||
import path from 'path'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
|
||||
/**
|
||||
* 创建代码启动台窗口。
|
||||
* @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
|
||||
}
|
||||
@@ -6,11 +6,7 @@ import { loadJsonFile } from 'load-json-file'
|
||||
import { XMLParser } from 'fast-xml-parser'
|
||||
import { VSCodeGlobalStorageJson } from '@my-type/vscode-globalstorage-json'
|
||||
import { JetBrainsIdeOptionsRecentProjects } from '@my-type/jetbrains-ide-options-recentProjects'
|
||||
import {
|
||||
JetBrainsIDEDisplayNameEnum,
|
||||
JetBrainsProductCode,
|
||||
toProductCode
|
||||
} from '@my-type/jetbrains-state-tools'
|
||||
import { JetBrainsIDEDisplayNameEnum, JetBrainsProductCode, toProductCode } from '@my-type/jetbrains-state-tools'
|
||||
import { checkIDEsResultDto, IDECode } from '@my-type/settings'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
@@ -43,7 +39,7 @@ export async function getVscodeProjects(): Promise<IdeProjectsDto> {
|
||||
// 还有一种协议名是 vscode-remote:// 将会保留,以作为 VSCode Remote 的标识
|
||||
const path = decodeURIComponent(workspace[0]).replace('file:///', '')
|
||||
const name = <string>path.split('/').at(-1)
|
||||
result.push({ name, path, ide: ['VSC'] })
|
||||
result.push({ name, path, timestamp: 0, ide: ['VSC'] })
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -89,19 +85,44 @@ export async function getJetBrainsProjects(): Promise<IdeProjectsDto> {
|
||||
await fs.readFile(rpp, 'utf-8')
|
||||
)
|
||||
for (const datum of data.application.component.option) {
|
||||
// 检索正确的 xml 路径
|
||||
if (datum['@_name'] !== 'additionalInfo') continue
|
||||
for (const entry of datum.map.entry) {
|
||||
const name = <string>entry.value.RecentProjectMetaInfo['@_frameTitle'].split(' – ').at(0)
|
||||
const path = entry['@_key'].replace('$USER_HOME$', os.homedir())
|
||||
// 项目的上次打开时间 projectOpenTimestamp 是一个时间戳,这里提供默认值 0 表示来自 1970 年的上古项目
|
||||
let timestamp = 0
|
||||
for (const option of entry.value.RecentProjectMetaInfo.option) {
|
||||
if (option['@_name'] === 'projectOpenTimestamp') {
|
||||
timestamp = Number(option['@_value'])
|
||||
}
|
||||
}
|
||||
// 认为包含此(安装/设置?)目录的是未保存的编辑,例如 light-edit 模式
|
||||
// 正常来说不会在这样的目录下保存项目的……吧?而且俺寻思 IDE 用这种变量的话也不像是人为刻意保存到此目录。
|
||||
if (path.includes('$APPLICATION_CONFIG_DIR$/')) continue
|
||||
// 去重。
|
||||
let pass = false
|
||||
for (const resultElement of result) {
|
||||
// 如果路径已存在,即此项目被用其他 JetBrains IDE 打开过
|
||||
if (resultElement.path === path) {
|
||||
// 如果 IDE 与该项目已有的工作 IDE 不同,就把这个 IDE 加进列表里去
|
||||
if (!resultElement.ide.includes(ide)) {
|
||||
resultElement.ide.push(ide)
|
||||
}
|
||||
// 如果 IDE 也重复了就忽略,然后直接
|
||||
pass = true
|
||||
}
|
||||
}
|
||||
if (!pass) {
|
||||
result.push({
|
||||
name,
|
||||
path,
|
||||
timestamp,
|
||||
ide: [ide]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// 忽略
|
||||
}
|
||||
|
||||
@@ -6,18 +6,16 @@ import type {
|
||||
IDECode
|
||||
} 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
|
||||
JetBrainsStateDto,
|
||||
toProductDisplayName
|
||||
} 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'
|
||||
|
||||
@@ -140,20 +138,20 @@ async function checkVSCodeVersion(): Promise<checkIDEVersionDto> {
|
||||
*/
|
||||
export async function checkJetBrainsIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||||
// 构建数据结构的辅助函数
|
||||
const _ = (display: string, code: JetBrainsProductCode): checkIDEVersionDto => {
|
||||
const _ = (code: JetBrainsProductCode): checkIDEVersionDto => {
|
||||
return {
|
||||
code,
|
||||
display,
|
||||
display: toProductDisplayName(code) as string,
|
||||
install: 'unknown',
|
||||
latest: 'unknown'
|
||||
}
|
||||
}
|
||||
const result: checkIDEsVersionDto = {
|
||||
PY: _('pycharm', 'PY'),
|
||||
CL: _('clion', 'CL'),
|
||||
WS: _('webstorm', 'WS'),
|
||||
PS: _('phpstorm', 'PS'),
|
||||
IU: _('idea', 'IU')
|
||||
PY: _('PY'),
|
||||
CL: _('CL'),
|
||||
WS: _('WS'),
|
||||
PS: _('PS'),
|
||||
IU: _('IU')
|
||||
}
|
||||
|
||||
// 尝试从 JBTState.json 获取已安装的 JetBrains IDEs 的版本
|
||||
@@ -233,91 +231,3 @@ export async function checkIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ import { saveSettingsToLocal, updateSettingsFromLocal } from './settings'
|
||||
import {
|
||||
checkIDEs,
|
||||
checkIDEsVersion,
|
||||
createCodeLaunchpadTray,
|
||||
createCodeLaunchpadWindow,
|
||||
getIDEs,
|
||||
getIDEsVersion
|
||||
} from './code-launchpad/ide-versions-check'
|
||||
import { fanToolsIcon } from './resources'
|
||||
import path from 'path'
|
||||
import { getJetBrainsProjects, getVscodeProjects, openProject } from './code-launchpad/ide-projects'
|
||||
import { createCodeLaunchpadTray, createCodeLaunchpadWindow } from './code-launchpad/code-launchpad'
|
||||
import type { IDECode } from '@my-type/settings'
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
// @ts-ignore 保存引用,禁用报错
|
||||
@@ -31,6 +32,7 @@ function createWindow(): BrowserWindow {
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
backgroundColor: '#1f1f1f',
|
||||
frame: false,
|
||||
icon: fanToolsIcon,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.mjs'),
|
||||
@@ -97,6 +99,14 @@ app.whenReady().then(async () => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('com.electron')
|
||||
|
||||
// 安装 DevTools 插件
|
||||
if (is.dev) {
|
||||
// 安装Vue DevTools
|
||||
installExtension(VUEJS_DEVTOOLS)
|
||||
.then((name) => console.log(`已安装扩展: ${name}`))
|
||||
.catch((err) => console.log('安装失败:', err))
|
||||
}
|
||||
|
||||
// Default open or close DevTools by F12 in development
|
||||
// and ignore CommandOrControl + R in production.
|
||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
@@ -125,13 +135,26 @@ app.whenReady().then(async () => {
|
||||
}
|
||||
return false
|
||||
})
|
||||
ipcMain.handle('window:minimize', () => {
|
||||
mainWindow?.minimize()
|
||||
})
|
||||
ipcMain.handle('window:maximize', () => {
|
||||
if (mainWindow?.isMaximized()) {
|
||||
mainWindow?.unmaximize()
|
||||
} else {
|
||||
mainWindow?.maximize()
|
||||
}
|
||||
})
|
||||
ipcMain.handle('window:closeWindow', () => {
|
||||
mainWindow?.hide()
|
||||
})
|
||||
ipcMain.handle('codeLaunchpad:getIDEs', getIDEs)
|
||||
ipcMain.handle('codeLaunchpad:checkIDEs', checkIDEs)
|
||||
ipcMain.handle('codeLaunchpad:getIDEsVersion', getIDEsVersion)
|
||||
ipcMain.handle('codeLaunchpad:checkIDEsVersion', checkIDEsVersion)
|
||||
ipcMain.handle('codeLaunchpad:getVSCodeProjects', getVscodeProjects)
|
||||
ipcMain.handle('codeLaunchpad:getJetBrainsProjects', getJetBrainsProjects)
|
||||
ipcMain.handle('codeLaunchpad:openProject', (_, ide: string, path: string) => {
|
||||
ipcMain.handle('codeLaunchpad:openProject', (_, ide: IDECode, path: string) => {
|
||||
return openProject(ide, path)
|
||||
})
|
||||
|
||||
|
||||
11
src/my-type/dataFormatter.ts
Normal file
11
src/my-type/dataFormatter.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const formatTimestamp = (timestamp: number, locale = 'zh-CN'): string => {
|
||||
return new Date(timestamp).toLocaleString(locale, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
})
|
||||
}
|
||||
1
src/my-type/ide-projects.d.ts
vendored
1
src/my-type/ide-projects.d.ts
vendored
@@ -5,6 +5,7 @@ type Ide = JetBrainsProductCode | 'VSC'
|
||||
export interface IdeProjectDto {
|
||||
name: string
|
||||
path: string
|
||||
timestamp: number
|
||||
ide: Ide[]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
// 这些数据结构是由 XMLParser 解析成的 Javascript 对象,解析设置允许属性。
|
||||
// 不完整,因为我也看不懂很多东西是干嘛用的,写完整了也用不到,所以把用到的定义一下就酱了
|
||||
|
||||
export interface RecentProjectMetaInfo {
|
||||
option: []
|
||||
frame: object
|
||||
'@_frameTitle': string
|
||||
}
|
||||
|
||||
export interface JetBrainsIdeOptionsRecentProjects {
|
||||
application: {
|
||||
component: {
|
||||
option: [
|
||||
{
|
||||
'@_name': 'activationTimestamp'
|
||||
'@_value': string
|
||||
},
|
||||
{
|
||||
map: {
|
||||
entry: {
|
||||
value: { RecentProjectMetaInfo: RecentProjectMetaInfo }
|
||||
value: {
|
||||
RecentProjectMetaInfo: {
|
||||
// option 中还有很多东西,这里只有我们需要的
|
||||
option: [{ '@_name': 'projectOpenTimestamp'; '@_value': string }]
|
||||
frame: object
|
||||
'@_frameTitle': string
|
||||
}
|
||||
}
|
||||
'@_key': string
|
||||
}[]
|
||||
}
|
||||
'@_name': 'additionalInfo'
|
||||
}
|
||||
]
|
||||
'@_name': 'RecentProjectsManager'
|
||||
|
||||
3
src/preload/index.d.ts
vendored
3
src/preload/index.d.ts
vendored
@@ -13,6 +13,9 @@ declare global {
|
||||
_updateSettings: () => Promise<settingsDto>
|
||||
_openCodeLaunchpad: () => Promise<boolean>
|
||||
_closeCodeLaunchpad: () => Promise<boolean>
|
||||
_minimize: () => Promise<void>
|
||||
_maximize: () => Promise<void>
|
||||
_closeWindow: () => Promise<void>
|
||||
}
|
||||
codeLaunchpad: {
|
||||
_getIDEs: () => Promise<checkIDEsResultDto>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { electronAPI } from '@electron-toolkit/preload'
|
||||
import { settingsDto } from '@my-type/settings'
|
||||
@@ -8,7 +10,10 @@ const api = {
|
||||
_saveSettings: (settings: settingsDto) => ipcRenderer.invoke('settings:save', settings),
|
||||
_updateSettings: () => ipcRenderer.invoke('settings:update'),
|
||||
_openCodeLaunchpad: () => ipcRenderer.invoke('tools:openCodeLaunchpad'),
|
||||
_closeCodeLaunchpad: () => ipcRenderer.invoke('tools:closeCodeLaunchpad')
|
||||
_closeCodeLaunchpad: () => ipcRenderer.invoke('tools:closeCodeLaunchpad'),
|
||||
_minimize: () => ipcRenderer.invoke('window:minimize'),
|
||||
_maximize: () => ipcRenderer.invoke('window:maximize'),
|
||||
_closeWindow: () => ipcRenderer.invoke('window:closeWindow')
|
||||
}
|
||||
|
||||
const codeLaunchpadApi = {
|
||||
|
||||
2
src/renderer/components.d.ts
vendored
2
src/renderer/components.d.ts
vendored
@@ -17,6 +17,7 @@ declare module 'vue' {
|
||||
DetectedIDECard: typeof import('./src/components/DetectedIDECard.vue')['default']
|
||||
DetectedIDECardList: typeof import('./src/components/DetectedIDECardList.vue')['default']
|
||||
NAlert: typeof import('naive-ui')['NAlert']
|
||||
NavigatorBar: typeof import('./src/components/NavigatorBar.vue')['default']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
@@ -37,6 +38,7 @@ declare module 'vue' {
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
NMenu: typeof import('naive-ui')['NMenu']
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NP: typeof import('naive-ui')['NP']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import SidebarRouter from '@renderer/components/SidebarRouter.vue'
|
||||
import SaveSettingsButton from '@renderer/components/SaveSettingsButton.vue'
|
||||
import NavigatorBar from '@renderer/components/NavigatorBar.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main-app-container">
|
||||
<div class="navigator-bar">
|
||||
<NavigatorBar />
|
||||
</div>
|
||||
<div class="everything-container">
|
||||
<div class="sidebar-router-container scrollarea">
|
||||
<SidebarRouter />
|
||||
@@ -15,15 +20,27 @@ import SaveSettingsButton from '@renderer/components/SaveSettingsButton.vue'
|
||||
</n-message-provider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
div.main-app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
div.navigator-bar {
|
||||
padding: 8px;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
div.everything-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
div.sidebar-router-container {
|
||||
flex: 0;
|
||||
@@ -36,4 +53,5 @@ div.everything-container {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,8 +10,24 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.small-padding {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.no-padding-top {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.no-margin-top {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.no-margin-bottom {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
// 滚动区域
|
||||
|
||||
@@ -4,6 +4,7 @@ import { EllipsisHorizontalSharp as EllipsisIcon } from '@vicons/ionicons5'
|
||||
import type { IdeProjectDto } from '@my-type/ide-projects'
|
||||
import { toProductDisplayName } from '@my-type/jetbrains-state-tools'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { formatTimestamp } from '@my-type/dataFormatter'
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
@@ -67,11 +68,11 @@ async function openWithIDE(ide: string): Promise<void> {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card>
|
||||
<n-card content-class="no-padding" footer-class="no-padding-top small-padding">
|
||||
<template #default>
|
||||
<div @click="() => (showDetail = true)">
|
||||
<div class="click-area" @click="() => (showDetail = true)">
|
||||
<n-flex justify="left">
|
||||
<n-h4>{{ project.name }}</n-h4>
|
||||
<n-h4 class="no-margin-bottom">{{ project.name }}</n-h4>
|
||||
<n-tag v-for="tag in ideTags" :key="tag" round :type="tag === 'WSL' ? 'primary' : 'info'">
|
||||
{{ tag }}
|
||||
</n-tag>
|
||||
@@ -80,6 +81,10 @@ async function openWithIDE(ide: string): Promise<void> {
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="showDetail" #footer>
|
||||
<n-flex vertical>
|
||||
<n-p v-if="project.timestamp !== 0" class="no-margin-bottom" type="default">
|
||||
上次打开于 {{ formatTimestamp(project.timestamp) }}
|
||||
</n-p>
|
||||
<n-flex>
|
||||
<n-button-group>
|
||||
<n-button
|
||||
@@ -87,22 +92,37 @@ async function openWithIDE(ide: string): Promise<void> {
|
||||
:key="ide"
|
||||
round
|
||||
type="info"
|
||||
size="small"
|
||||
@click="() => openWithIDE(ide)"
|
||||
>
|
||||
用 {{ ide === 'VSC' ? 'VS Code' : toProductDisplayName(ide) }} 打开
|
||||
</n-button>
|
||||
<n-dropdown trigger="click" :options @select="(key) => openWithIDE(key as string)">
|
||||
<n-button type="info" circle>
|
||||
<n-button type="info" circle size="small">
|
||||
<n-icon>
|
||||
<EllipsisIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
</n-button-group>
|
||||
<n-button round secondary type="primary" @click="() => (showDetail = false)">收起</n-button>
|
||||
<n-button round secondary type="primary" size="small" @click="() => (showDetail = false)">
|
||||
收起
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
div.click-area {
|
||||
padding: 10px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
div.click-area:hover {
|
||||
border: 1px solid #74c072;
|
||||
}
|
||||
</style>
|
||||
|
||||
70
src/renderer/src/components/NavigatorBar.vue
Normal file
70
src/renderer/src/components/NavigatorBar.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { CloseOutline as CloseIcon } from '@vicons/ionicons5'
|
||||
import { CropSquareOutlined as FullScreenIcon } from '@vicons/material'
|
||||
import { ArrowMinimize20Filled as HideIcon } from '@vicons/fluent'
|
||||
|
||||
function minimize(): void {
|
||||
window.api._minimize()
|
||||
}
|
||||
|
||||
function maximize(): void {
|
||||
window.api._maximize()
|
||||
}
|
||||
|
||||
function closeWindow(): void {
|
||||
window.api._closeWindow()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="title-bar">
|
||||
<div class="title">
|
||||
<n-text strong type="info">芒果工具箱 FanTools</n-text>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<n-flex justify="right" align="center" class="no-drag">
|
||||
<n-button circle type="default" size="small" @click="minimize()">
|
||||
<n-icon size="large">
|
||||
<HideIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
<n-button circle type="warning" size="small" @click="maximize()">
|
||||
<n-icon size="large">
|
||||
<FullScreenIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
<n-button circle type="error" size="small" @click="closeWindow()">
|
||||
<n-icon size="large">
|
||||
<CloseIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.no-drag {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
div.title-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
div.title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
font-size: 16px;
|
||||
background-color: rgb(57 57 57 / 0.4);
|
||||
border: 1px solid #8cb0bc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
div.button-group {
|
||||
flex: 0;
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,11 +2,12 @@
|
||||
import ProjectCard from '@renderer/components-code-launchpad/ProjectCard.vue'
|
||||
import { useProjects } from '@renderer/stores'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { RefreshOutline as RefreshIcon } from '@vicons/ionicons5'
|
||||
import { AlertOutline as AlertIcon, RefreshOutline as RefreshIcon } from '@vicons/ionicons5'
|
||||
|
||||
const projects = useProjects()
|
||||
|
||||
const ide = ref<'VSCode' | 'JetBrains'>('VSCode')
|
||||
const showModal = ref(false)
|
||||
|
||||
// 此 switch-case 结构已经触及所有情况
|
||||
// eslint-disable-next-line vue/return-in-computed-property
|
||||
@@ -15,7 +16,7 @@ const reverseProjects = computed(() => {
|
||||
case 'VSCode':
|
||||
return projects.vscodeProjects.toReversed()
|
||||
case 'JetBrains':
|
||||
return projects.jetBrainsProjects.toReversed()
|
||||
return projects.jetBrainsProjects.toSorted((a, b) => (a.timestamp > b.timestamp ? -1 : 1))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -32,10 +33,12 @@ onMounted(() => {
|
||||
<template>
|
||||
<n-flex>
|
||||
<n-button-group class="ide-button-group">
|
||||
<n-button type="primary" secondary round @click="() => (ide = 'VSCode')">VS Code</n-button>
|
||||
<n-button type="primary" secondary round @click="() => (ide = 'JetBrains')"
|
||||
>JetBrains</n-button
|
||||
>
|
||||
<n-button type="primary" secondary round @click="() => (ide = 'VSCode')">
|
||||
<n-text :strong="ide === 'VSCode'" type="success">VS Code</n-text>
|
||||
</n-button>
|
||||
<n-button type="primary" secondary round @click="() => (ide = 'JetBrains')">
|
||||
<n-text :strong="ide === 'JetBrains'" type="success">JetBrains</n-text>
|
||||
</n-button>
|
||||
</n-button-group>
|
||||
<n-button
|
||||
type="primary"
|
||||
@@ -52,12 +55,38 @@ onMounted(() => {
|
||||
<RefreshIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
<n-button type="default" secondary circle @click="() => (showModal = true)">
|
||||
<n-icon>
|
||||
<AlertIcon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</n-flex>
|
||||
<n-flex size="small" vertical>
|
||||
<div v-for="project of reverseProjects" :key="project.path" class="project-card">
|
||||
<ProjectCard :project />
|
||||
</div>
|
||||
</n-flex>
|
||||
|
||||
<n-modal v-model:show="showModal" preset="card" title="管理项目?">
|
||||
<n-p>
|
||||
代码启动台仅在此罗列找到的项目。
|
||||
<n-text type="warning" strong>您无法在这里管理、删除某个项目。</n-text>
|
||||
</n-p>
|
||||
<n-p>
|
||||
你可以在这里选择用其他 IDE 打开一个项目,但是并非所有 IDE 都支持某些特殊 URI 的项目。 例如,
|
||||
<n-code inline>vscode-remote://</n-code>
|
||||
协议是
|
||||
<n-text strong type="info">VS Code 远程项目</n-text>
|
||||
的协议,你无法使用 JetBrains IDEs 打开此协议的项目,哪怕它们可能运行在
|
||||
<n-text type="success" strong>WSL</n-text>
|
||||
中。
|
||||
</n-p>
|
||||
<template #footer>
|
||||
<a href="https://gitea.mangofanfan.cn/MangoFanFanw/FanTools/wiki" target="_blank">
|
||||
<n-button secondary type="primary">在 FanTools.wiki 中查看</n-button>
|
||||
</a>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<template>
|
||||
<n-h1 prefix="bar"><n-text type="success">FanTools - 芒果工具箱</n-text></n-h1>
|
||||
<n-h2>主页</n-h2>
|
||||
<n-h3>简介</n-h3>
|
||||
<n-p
|
||||
>FanTools,或者,芒果工具箱,是一个使用 Electron 技术开发的 Windows
|
||||
桌面应用程序,提供一些工具功能,从名称上也能看得出来。</n-p
|
||||
@@ -12,4 +13,12 @@
|
||||
>窗口右下角的浮动按钮是<n-text strong type="success">保存</n-text
|
||||
>!设置需要保存之后才能生效,部分设置还需要重启工具箱来生效。如果你改动了设置,记得保存。</n-p
|
||||
>
|
||||
|
||||
<n-h3>包含工具</n-h3>
|
||||
<n-p>左侧边栏中列出了所有工具。</n-p>
|
||||
<n-p>
|
||||
对于未来的更多工具开发计划,不会考虑添加已经由
|
||||
<n-text strong type="info">PowerToys</n-text>
|
||||
或其他知名同类软件实现的功能。
|
||||
</n-p>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user