实现 JB 项目时间戳、排序。窗口无边框。
This commit is contained in:
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">
|
||||
|
||||
@@ -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 {
|
||||
// 忽略
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import os from 'os'
|
||||
import {
|
||||
JetBrainsIDEDisplayNameEnum as JIN,
|
||||
JetBrainsProductCode,
|
||||
JetBrainsStateDto
|
||||
JetBrainsStateDto,
|
||||
toProductDisplayName
|
||||
} from '@my-type/jetbrains-state-tools'
|
||||
import { settingsManager } from '../settings'
|
||||
import { isNodeError } from '@my-type/node-error'
|
||||
@@ -137,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 的版本
|
||||
|
||||
@@ -12,6 +12,7 @@ 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,6 +135,19 @@ 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)
|
||||
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user