实现 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">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<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="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="ignoredPackages">
|
<option name="ignoredPackages">
|
||||||
|
|||||||
@@ -6,11 +6,7 @@ import { loadJsonFile } from 'load-json-file'
|
|||||||
import { XMLParser } from 'fast-xml-parser'
|
import { XMLParser } from 'fast-xml-parser'
|
||||||
import { VSCodeGlobalStorageJson } from '@my-type/vscode-globalstorage-json'
|
import { VSCodeGlobalStorageJson } from '@my-type/vscode-globalstorage-json'
|
||||||
import { JetBrainsIdeOptionsRecentProjects } from '@my-type/jetbrains-ide-options-recentProjects'
|
import { JetBrainsIdeOptionsRecentProjects } from '@my-type/jetbrains-ide-options-recentProjects'
|
||||||
import {
|
import { JetBrainsIDEDisplayNameEnum, JetBrainsProductCode, toProductCode } from '@my-type/jetbrains-state-tools'
|
||||||
JetBrainsIDEDisplayNameEnum,
|
|
||||||
JetBrainsProductCode,
|
|
||||||
toProductCode
|
|
||||||
} from '@my-type/jetbrains-state-tools'
|
|
||||||
import { checkIDEsResultDto, IDECode } from '@my-type/settings'
|
import { checkIDEsResultDto, IDECode } from '@my-type/settings'
|
||||||
import { spawn } from 'node:child_process'
|
import { spawn } from 'node:child_process'
|
||||||
|
|
||||||
@@ -43,7 +39,7 @@ export async function getVscodeProjects(): Promise<IdeProjectsDto> {
|
|||||||
// 还有一种协议名是 vscode-remote:// 将会保留,以作为 VSCode Remote 的标识
|
// 还有一种协议名是 vscode-remote:// 将会保留,以作为 VSCode Remote 的标识
|
||||||
const path = decodeURIComponent(workspace[0]).replace('file:///', '')
|
const path = decodeURIComponent(workspace[0]).replace('file:///', '')
|
||||||
const name = <string>path.split('/').at(-1)
|
const name = <string>path.split('/').at(-1)
|
||||||
result.push({ name, path, ide: ['VSC'] })
|
result.push({ name, path, timestamp: 0, ide: ['VSC'] })
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -89,17 +85,42 @@ export async function getJetBrainsProjects(): Promise<IdeProjectsDto> {
|
|||||||
await fs.readFile(rpp, 'utf-8')
|
await fs.readFile(rpp, 'utf-8')
|
||||||
)
|
)
|
||||||
for (const datum of data.application.component.option) {
|
for (const datum of data.application.component.option) {
|
||||||
|
// 检索正确的 xml 路径
|
||||||
|
if (datum['@_name'] !== 'additionalInfo') continue
|
||||||
for (const entry of datum.map.entry) {
|
for (const entry of datum.map.entry) {
|
||||||
const name = <string>entry.value.RecentProjectMetaInfo['@_frameTitle'].split(' – ').at(0)
|
const name = <string>entry.value.RecentProjectMetaInfo['@_frameTitle'].split(' – ').at(0)
|
||||||
const path = entry['@_key'].replace('$USER_HOME$', os.homedir())
|
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 模式
|
// 认为包含此(安装/设置?)目录的是未保存的编辑,例如 light-edit 模式
|
||||||
// 正常来说不会在这样的目录下保存项目的……吧?而且俺寻思 IDE 用这种变量的话也不像是人为刻意保存到此目录。
|
// 正常来说不会在这样的目录下保存项目的……吧?而且俺寻思 IDE 用这种变量的话也不像是人为刻意保存到此目录。
|
||||||
if (path.includes('$APPLICATION_CONFIG_DIR$/')) continue
|
if (path.includes('$APPLICATION_CONFIG_DIR$/')) continue
|
||||||
result.push({
|
// 去重。
|
||||||
name,
|
let pass = false
|
||||||
path,
|
for (const resultElement of result) {
|
||||||
ide: [ide]
|
// 如果路径已存在,即此项目被用其他 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 {
|
} catch {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import os from 'os'
|
|||||||
import {
|
import {
|
||||||
JetBrainsIDEDisplayNameEnum as JIN,
|
JetBrainsIDEDisplayNameEnum as JIN,
|
||||||
JetBrainsProductCode,
|
JetBrainsProductCode,
|
||||||
JetBrainsStateDto
|
JetBrainsStateDto,
|
||||||
|
toProductDisplayName
|
||||||
} from '@my-type/jetbrains-state-tools'
|
} from '@my-type/jetbrains-state-tools'
|
||||||
import { settingsManager } from '../settings'
|
import { settingsManager } from '../settings'
|
||||||
import { isNodeError } from '@my-type/node-error'
|
import { isNodeError } from '@my-type/node-error'
|
||||||
@@ -137,20 +138,20 @@ async function checkVSCodeVersion(): Promise<checkIDEVersionDto> {
|
|||||||
*/
|
*/
|
||||||
export async function checkJetBrainsIDEsVersion(): Promise<checkIDEsVersionDto> {
|
export async function checkJetBrainsIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||||||
// 构建数据结构的辅助函数
|
// 构建数据结构的辅助函数
|
||||||
const _ = (display: string, code: JetBrainsProductCode): checkIDEVersionDto => {
|
const _ = (code: JetBrainsProductCode): checkIDEVersionDto => {
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
display,
|
display: toProductDisplayName(code) as string,
|
||||||
install: 'unknown',
|
install: 'unknown',
|
||||||
latest: 'unknown'
|
latest: 'unknown'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result: checkIDEsVersionDto = {
|
const result: checkIDEsVersionDto = {
|
||||||
PY: _('pycharm', 'PY'),
|
PY: _('PY'),
|
||||||
CL: _('clion', 'CL'),
|
CL: _('CL'),
|
||||||
WS: _('webstorm', 'WS'),
|
WS: _('WS'),
|
||||||
PS: _('phpstorm', 'PS'),
|
PS: _('PS'),
|
||||||
IU: _('idea', 'IU')
|
IU: _('IU')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试从 JBTState.json 获取已安装的 JetBrains IDEs 的版本
|
// 尝试从 JBTState.json 获取已安装的 JetBrains IDEs 的版本
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import path from 'path'
|
|||||||
import { getJetBrainsProjects, getVscodeProjects, openProject } from './code-launchpad/ide-projects'
|
import { getJetBrainsProjects, getVscodeProjects, openProject } from './code-launchpad/ide-projects'
|
||||||
import { createCodeLaunchpadTray, createCodeLaunchpadWindow } from './code-launchpad/code-launchpad'
|
import { createCodeLaunchpadTray, createCodeLaunchpadWindow } from './code-launchpad/code-launchpad'
|
||||||
import type { IDECode } from '@my-type/settings'
|
import type { IDECode } from '@my-type/settings'
|
||||||
|
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null
|
let mainWindow: BrowserWindow | null = null
|
||||||
// @ts-ignore 保存引用,禁用报错
|
// @ts-ignore 保存引用,禁用报错
|
||||||
@@ -31,6 +32,7 @@ function createWindow(): BrowserWindow {
|
|||||||
show: false,
|
show: false,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
backgroundColor: '#1f1f1f',
|
backgroundColor: '#1f1f1f',
|
||||||
|
frame: false,
|
||||||
icon: fanToolsIcon,
|
icon: fanToolsIcon,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, '../preload/index.mjs'),
|
preload: path.join(__dirname, '../preload/index.mjs'),
|
||||||
@@ -97,6 +99,14 @@ app.whenReady().then(async () => {
|
|||||||
// Set app user model id for windows
|
// Set app user model id for windows
|
||||||
electronApp.setAppUserModelId('com.electron')
|
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
|
// Default open or close DevTools by F12 in development
|
||||||
// and ignore CommandOrControl + R in production.
|
// and ignore CommandOrControl + R in production.
|
||||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||||
@@ -125,6 +135,19 @@ app.whenReady().then(async () => {
|
|||||||
}
|
}
|
||||||
return false
|
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:getIDEs', getIDEs)
|
||||||
ipcMain.handle('codeLaunchpad:checkIDEs', checkIDEs)
|
ipcMain.handle('codeLaunchpad:checkIDEs', checkIDEs)
|
||||||
ipcMain.handle('codeLaunchpad:getIDEsVersion', getIDEsVersion)
|
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 {
|
export interface IdeProjectDto {
|
||||||
name: string
|
name: string
|
||||||
path: string
|
path: string
|
||||||
|
timestamp: number
|
||||||
ide: Ide[]
|
ide: Ide[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
// 这些数据结构是由 XMLParser 解析成的 Javascript 对象,解析设置允许属性。
|
// 这些数据结构是由 XMLParser 解析成的 Javascript 对象,解析设置允许属性。
|
||||||
// 不完整,因为我也看不懂很多东西是干嘛用的,写完整了也用不到,所以把用到的定义一下就酱了
|
// 不完整,因为我也看不懂很多东西是干嘛用的,写完整了也用不到,所以把用到的定义一下就酱了
|
||||||
|
|
||||||
export interface RecentProjectMetaInfo {
|
|
||||||
option: []
|
|
||||||
frame: object
|
|
||||||
'@_frameTitle': string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JetBrainsIdeOptionsRecentProjects {
|
export interface JetBrainsIdeOptionsRecentProjects {
|
||||||
application: {
|
application: {
|
||||||
component: {
|
component: {
|
||||||
option: [
|
option: [
|
||||||
|
{
|
||||||
|
'@_name': 'activationTimestamp'
|
||||||
|
'@_value': string
|
||||||
|
},
|
||||||
{
|
{
|
||||||
map: {
|
map: {
|
||||||
entry: {
|
entry: {
|
||||||
value: { RecentProjectMetaInfo: RecentProjectMetaInfo }
|
value: {
|
||||||
|
RecentProjectMetaInfo: {
|
||||||
|
// option 中还有很多东西,这里只有我们需要的
|
||||||
|
option: [{ '@_name': 'projectOpenTimestamp'; '@_value': string }]
|
||||||
|
frame: object
|
||||||
|
'@_frameTitle': string
|
||||||
|
}
|
||||||
|
}
|
||||||
'@_key': string
|
'@_key': string
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
'@_name': 'additionalInfo'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
'@_name': 'RecentProjectsManager'
|
'@_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>
|
_updateSettings: () => Promise<settingsDto>
|
||||||
_openCodeLaunchpad: () => Promise<boolean>
|
_openCodeLaunchpad: () => Promise<boolean>
|
||||||
_closeCodeLaunchpad: () => Promise<boolean>
|
_closeCodeLaunchpad: () => Promise<boolean>
|
||||||
|
_minimize: () => Promise<void>
|
||||||
|
_maximize: () => Promise<void>
|
||||||
|
_closeWindow: () => Promise<void>
|
||||||
}
|
}
|
||||||
codeLaunchpad: {
|
codeLaunchpad: {
|
||||||
_getIDEs: () => Promise<checkIDEsResultDto>
|
_getIDEs: () => Promise<checkIDEsResultDto>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
|
||||||
import { contextBridge, ipcRenderer } from 'electron'
|
import { contextBridge, ipcRenderer } from 'electron'
|
||||||
import { electronAPI } from '@electron-toolkit/preload'
|
import { electronAPI } from '@electron-toolkit/preload'
|
||||||
import { settingsDto } from '@my-type/settings'
|
import { settingsDto } from '@my-type/settings'
|
||||||
@@ -8,7 +10,10 @@ const api = {
|
|||||||
_saveSettings: (settings: settingsDto) => ipcRenderer.invoke('settings:save', settings),
|
_saveSettings: (settings: settingsDto) => ipcRenderer.invoke('settings:save', settings),
|
||||||
_updateSettings: () => ipcRenderer.invoke('settings:update'),
|
_updateSettings: () => ipcRenderer.invoke('settings:update'),
|
||||||
_openCodeLaunchpad: () => ipcRenderer.invoke('tools:openCodeLaunchpad'),
|
_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 = {
|
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']
|
DetectedIDECard: typeof import('./src/components/DetectedIDECard.vue')['default']
|
||||||
DetectedIDECardList: typeof import('./src/components/DetectedIDECardList.vue')['default']
|
DetectedIDECardList: typeof import('./src/components/DetectedIDECardList.vue')['default']
|
||||||
NAlert: typeof import('naive-ui')['NAlert']
|
NAlert: typeof import('naive-ui')['NAlert']
|
||||||
|
NavigatorBar: typeof import('./src/components/NavigatorBar.vue')['default']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
@@ -37,6 +38,7 @@ declare module 'vue' {
|
|||||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
NP: typeof import('naive-ui')['NP']
|
NP: typeof import('naive-ui')['NP']
|
||||||
NSelect: typeof import('naive-ui')['NSelect']
|
NSelect: typeof import('naive-ui')['NSelect']
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
|
|||||||
@@ -1,39 +1,57 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import SidebarRouter from '@renderer/components/SidebarRouter.vue'
|
import SidebarRouter from '@renderer/components/SidebarRouter.vue'
|
||||||
import SaveSettingsButton from '@renderer/components/SaveSettingsButton.vue'
|
import SaveSettingsButton from '@renderer/components/SaveSettingsButton.vue'
|
||||||
|
import NavigatorBar from '@renderer/components/NavigatorBar.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="everything-container">
|
<div class="main-app-container">
|
||||||
<div class="sidebar-router-container scrollarea">
|
<div class="navigator-bar">
|
||||||
<SidebarRouter />
|
<NavigatorBar />
|
||||||
</div>
|
</div>
|
||||||
<div class="content-container scrollarea">
|
<div class="everything-container">
|
||||||
<n-message-provider placement="bottom">
|
<div class="sidebar-router-container scrollarea">
|
||||||
<RouterView />
|
<SidebarRouter />
|
||||||
<SaveSettingsButton />
|
</div>
|
||||||
</n-message-provider>
|
<div class="content-container scrollarea">
|
||||||
|
<n-message-provider placement="bottom">
|
||||||
|
<RouterView />
|
||||||
|
<SaveSettingsButton />
|
||||||
|
</n-message-provider>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
div.everything-container {
|
div.main-app-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
div.sidebar-router-container {
|
div.navigator-bar {
|
||||||
flex: 0;
|
padding: 8px;
|
||||||
min-width: 200px;
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.content-container {
|
div.everything-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 20px 10px 20px 0;
|
display: flex;
|
||||||
overflow-x: hidden;
|
flex-direction: row;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
div.sidebar-router-container {
|
||||||
|
flex: 0;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.content-container {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px 10px 20px 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,8 +10,24 @@ body {
|
|||||||
overflow: hidden;
|
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 {
|
.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 type { IdeProjectDto } from '@my-type/ide-projects'
|
||||||
import { toProductDisplayName } from '@my-type/jetbrains-state-tools'
|
import { toProductDisplayName } from '@my-type/jetbrains-state-tools'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { formatTimestamp } from '@my-type/dataFormatter'
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
@@ -67,11 +68,11 @@ async function openWithIDE(ide: string): Promise<void> {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-card>
|
<n-card content-class="no-padding" footer-class="no-padding-top small-padding">
|
||||||
<template #default>
|
<template #default>
|
||||||
<div @click="() => (showDetail = true)">
|
<div class="click-area" @click="() => (showDetail = true)">
|
||||||
<n-flex justify="left">
|
<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'">
|
<n-tag v-for="tag in ideTags" :key="tag" round :type="tag === 'WSL' ? 'primary' : 'info'">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</n-tag>
|
</n-tag>
|
||||||
@@ -80,29 +81,48 @@ async function openWithIDE(ide: string): Promise<void> {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="showDetail" #footer>
|
<template v-if="showDetail" #footer>
|
||||||
<n-flex>
|
<n-flex vertical>
|
||||||
<n-button-group>
|
<n-p v-if="project.timestamp !== 0" class="no-margin-bottom" type="default">
|
||||||
<n-button
|
上次打开于 {{ formatTimestamp(project.timestamp) }}
|
||||||
v-for="ide in project.ide"
|
</n-p>
|
||||||
:key="ide"
|
<n-flex>
|
||||||
round
|
<n-button-group>
|
||||||
type="info"
|
<n-button
|
||||||
@click="() => openWithIDE(ide)"
|
v-for="ide in project.ide"
|
||||||
>
|
:key="ide"
|
||||||
用 {{ ide === 'VSC' ? 'VS Code' : toProductDisplayName(ide) }} 打开
|
round
|
||||||
</n-button>
|
type="info"
|
||||||
<n-dropdown trigger="click" :options @select="(key) => openWithIDE(key as string)">
|
size="small"
|
||||||
<n-button type="info" circle>
|
@click="() => openWithIDE(ide)"
|
||||||
<n-icon>
|
>
|
||||||
<EllipsisIcon />
|
用 {{ ide === 'VSC' ? 'VS Code' : toProductDisplayName(ide) }} 打开
|
||||||
</n-icon>
|
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-dropdown>
|
<n-dropdown trigger="click" :options @select="(key) => openWithIDE(key as string)">
|
||||||
</n-button-group>
|
<n-button type="info" circle size="small">
|
||||||
<n-button round secondary type="primary" @click="() => (showDetail = false)">收起</n-button>
|
<n-icon>
|
||||||
|
<EllipsisIcon />
|
||||||
|
</n-icon>
|
||||||
|
</n-button>
|
||||||
|
</n-dropdown>
|
||||||
|
</n-button-group>
|
||||||
|
<n-button round secondary type="primary" size="small" @click="() => (showDetail = false)">
|
||||||
|
收起
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</template>
|
</template>
|
||||||
</n-card>
|
</n-card>
|
||||||
</template>
|
</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 ProjectCard from '@renderer/components-code-launchpad/ProjectCard.vue'
|
||||||
import { useProjects } from '@renderer/stores'
|
import { useProjects } from '@renderer/stores'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
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 projects = useProjects()
|
||||||
|
|
||||||
const ide = ref<'VSCode' | 'JetBrains'>('VSCode')
|
const ide = ref<'VSCode' | 'JetBrains'>('VSCode')
|
||||||
|
const showModal = ref(false)
|
||||||
|
|
||||||
// 此 switch-case 结构已经触及所有情况
|
// 此 switch-case 结构已经触及所有情况
|
||||||
// eslint-disable-next-line vue/return-in-computed-property
|
// eslint-disable-next-line vue/return-in-computed-property
|
||||||
@@ -15,7 +16,7 @@ const reverseProjects = computed(() => {
|
|||||||
case 'VSCode':
|
case 'VSCode':
|
||||||
return projects.vscodeProjects.toReversed()
|
return projects.vscodeProjects.toReversed()
|
||||||
case 'JetBrains':
|
case 'JetBrains':
|
||||||
return projects.jetBrainsProjects.toReversed()
|
return projects.jetBrainsProjects.toSorted((a, b) => (a.timestamp > b.timestamp ? -1 : 1))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -32,10 +33,12 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<n-flex>
|
<n-flex>
|
||||||
<n-button-group class="ide-button-group">
|
<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 = 'VSCode')">
|
||||||
<n-button type="primary" secondary round @click="() => (ide = 'JetBrains')"
|
<n-text :strong="ide === 'VSCode'" type="success">VS Code</n-text>
|
||||||
>JetBrains</n-button
|
</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-group>
|
||||||
<n-button
|
<n-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -52,12 +55,38 @@ onMounted(() => {
|
|||||||
<RefreshIcon />
|
<RefreshIcon />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</n-button>
|
</n-button>
|
||||||
|
<n-button type="default" secondary circle @click="() => (showModal = true)">
|
||||||
|
<n-icon>
|
||||||
|
<AlertIcon />
|
||||||
|
</n-icon>
|
||||||
|
</n-button>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
<n-flex size="small" vertical>
|
<n-flex size="small" vertical>
|
||||||
<div v-for="project of reverseProjects" :key="project.path" class="project-card">
|
<div v-for="project of reverseProjects" :key="project.path" class="project-card">
|
||||||
<ProjectCard :project />
|
<ProjectCard :project />
|
||||||
</div>
|
</div>
|
||||||
</n-flex>
|
</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>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user