You've already forked iFlow-Settings-Editor-GUI
修复 MCP 服务器保存及消息对话框被遮挡问题
This commit is contained in:
320
src/App.vue
320
src/App.vue
@@ -19,7 +19,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<section v-if="currentSection === 'general'">
|
<section v-if="currentSection === 'general'">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
@@ -97,7 +95,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section v-if="currentSection === 'api'">
|
<section v-if="currentSection === 'api'">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<h1 class="content-title">API 配置</h1>
|
<h1 class="content-title">API 配置</h1>
|
||||||
@@ -177,14 +174,12 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="footer-status">
|
<div class="footer-status">
|
||||||
<div class="footer-status-dot"></div>
|
<div class="footer-status-dot"></div>
|
||||||
<span>配置: {{ currentApiProfile || 'default' }}</span>
|
<span>配置: {{ currentApiProfile || 'default' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Input Dialog -->
|
<!-- Input Dialog -->
|
||||||
<div v-if="showInputDialog.show" class="dialog-overlay dialog-overlay-top">
|
<div v-if="showInputDialog.show" class="dialog-overlay dialog-overlay-top">
|
||||||
<div class="dialog" @click.stop>
|
<div class="dialog" @click.stop>
|
||||||
@@ -197,40 +192,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Message Dialog -->
|
|
||||||
<div v-if="showMessageDialog.show" class="dialog-overlay dialog-overlay-top">
|
|
||||||
<div class="dialog message-dialog" @click.stop>
|
|
||||||
<div class="message-dialog-icon" :class="'message-dialog-icon-' + showMessageDialog.type">
|
|
||||||
<svg v-if="showMessageDialog.type === 'info'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<circle cx="12" cy="12" r="10" />
|
|
||||||
<path d="M12 16v-4M12 8h.01" />
|
|
||||||
</svg>
|
|
||||||
<svg v-else-if="showMessageDialog.type === 'success'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<circle cx="12" cy="12" r="10" />
|
|
||||||
<path d="M9 12l2 2 4-4" />
|
|
||||||
</svg>
|
|
||||||
<svg v-else-if="showMessageDialog.type === 'warning'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
|
|
||||||
<line x1="12" y1="9" x2="12" y2="13" />
|
|
||||||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
||||||
</svg>
|
|
||||||
<svg v-else-if="showMessageDialog.type === 'error'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<circle cx="12" cy="12" r="10" />
|
|
||||||
<line x1="15" y1="9" x2="9" y2="15" />
|
|
||||||
<line x1="9" y1="9" x2="15" y2="15" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="message-dialog-title">{{ showMessageDialog.title }}</div>
|
|
||||||
<div class="message-dialog-message">{{ showMessageDialog.message }}</div>
|
|
||||||
<div class="dialog-actions">
|
|
||||||
<button class="btn btn-primary" @click="closeMessageDialog">确定</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- API Profile Create Dialog -->
|
<!-- API Profile Create Dialog -->
|
||||||
<div v-if="showApiCreateDialog" class="dialog-overlay dialog-overlay-top" @keyup.esc="closeApiCreateDialog" tabindex="-1" ref="apiCreateDialogOverlay" style="z-index: 1200">
|
<div v-if="showApiCreateDialog" class="dialog-overlay dialog-overlay-top" @keyup.esc="closeApiCreateDialog" tabindex="-1" ref="apiCreateDialogOverlay">
|
||||||
<div class="dialog api-edit-dialog" @click.stop>
|
<div class="dialog api-edit-dialog" @click.stop>
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<div class="dialog-title">
|
<div class="dialog-title">
|
||||||
@@ -286,7 +249,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- API Profile Edit Dialog -->
|
<!-- API Profile Edit Dialog -->
|
||||||
<div v-if="showApiEditDialog" class="dialog-overlay dialog-overlay-top" @keyup.esc="closeApiEditDialog" tabindex="-1" ref="apiEditDialogOverlay">
|
<div v-if="showApiEditDialog" class="dialog-overlay dialog-overlay-top" @keyup.esc="closeApiEditDialog" tabindex="-1" ref="apiEditDialogOverlay">
|
||||||
<div class="dialog api-edit-dialog" @click.stop>
|
<div class="dialog api-edit-dialog" @click.stop>
|
||||||
@@ -340,7 +302,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Server Side Panel -->
|
<!-- Server Side Panel -->
|
||||||
<div v-if="showServerPanel" class="side-panel-overlay" @keyup.esc="closeServerPanel" tabindex="-1" ref="serverPanelOverlay">
|
<div v-if="showServerPanel" class="side-panel-overlay" @keyup.esc="closeServerPanel" tabindex="-1" ref="serverPanelOverlay">
|
||||||
<div class="side-panel" @click.stop>
|
<div class="side-panel" @click.stop>
|
||||||
@@ -397,20 +358,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Message Dialog (放在最后确保显示在所有对话框之上) -->
|
||||||
|
<div v-if="showMessageDialog.show" class="dialog-overlay dialog-overlay-top">
|
||||||
|
<div class="dialog message-dialog" @click.stop>
|
||||||
|
<div class="message-dialog-icon" :class="'message-dialog-icon-' + showMessageDialog.type">
|
||||||
|
<svg v-if="showMessageDialog.type === 'info'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M12 16v-4M12 8h.01" />
|
||||||
|
</svg>
|
||||||
|
<svg v-else-if="showMessageDialog.type === 'success'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M9 12l2 2 4-4" />
|
||||||
|
</svg>
|
||||||
|
<svg v-else-if="showMessageDialog.type === 'warning'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
|
||||||
|
<line x1="12" y1="9" x2="12" y2="13" />
|
||||||
|
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||||||
|
</svg>
|
||||||
|
<svg v-else-if="showMessageDialog.type === 'error'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<line x1="15" y1="9" x2="9" y2="15" />
|
||||||
|
<line x1="9" y1="9" x2="15" y2="15" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="message-dialog-title">{{ showMessageDialog.title }}</div>
|
||||||
|
<div class="message-dialog-message">{{ showMessageDialog.message }}</div>
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<button class="btn btn-primary" @click="closeMessageDialog">确定</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue'
|
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
||||||
import { Refresh, Save, Config, Key, Server, Globe, Setting, Robot, Search, Add, Edit, Delete, Exchange, Copy } from '@icon-park/vue-next'
|
import { Save, Config, Key, Server, Globe, Setting, Add, Edit, Delete, Exchange, Copy } from '@icon-park/vue-next'
|
||||||
|
|
||||||
const settings = ref({
|
const settings = ref({
|
||||||
language: 'zh-CN',
|
language: 'zh-CN',
|
||||||
theme: 'Xcode',
|
theme: 'Xcode',
|
||||||
bootAnimationShown: true,
|
bootAnimationShown: true,
|
||||||
checkpointing: { enabled: true },
|
checkpointing: { enabled: true },
|
||||||
mcpServers: {},
|
mcpServers: {},
|
||||||
selectedAuthType: 'iflow',
|
selectedAuthType: 'openai-compatible',
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
modelName: '',
|
modelName: '',
|
||||||
@@ -419,7 +408,6 @@ const settings = ref({
|
|||||||
currentApiProfile: 'default',
|
currentApiProfile: 'default',
|
||||||
apiProfiles: { default: {} },
|
apiProfiles: { default: {} },
|
||||||
})
|
})
|
||||||
|
|
||||||
const originalSettings = ref({})
|
const originalSettings = ref({})
|
||||||
const modified = ref(false)
|
const modified = ref(false)
|
||||||
const currentSection = ref('general')
|
const currentSection = ref('general')
|
||||||
@@ -443,7 +431,7 @@ const editingServerData = ref({
|
|||||||
const showApiEditDialog = ref(false)
|
const showApiEditDialog = ref(false)
|
||||||
const editingApiProfileName = ref('')
|
const editingApiProfileName = ref('')
|
||||||
const editingApiData = ref({
|
const editingApiData = ref({
|
||||||
selectedAuthType: 'iflow',
|
selectedAuthType: 'openai-compatible',
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
modelName: '',
|
modelName: '',
|
||||||
@@ -453,14 +441,13 @@ const editingApiData = ref({
|
|||||||
const showApiCreateDialog = ref(false)
|
const showApiCreateDialog = ref(false)
|
||||||
const creatingApiData = ref({
|
const creatingApiData = ref({
|
||||||
name: '',
|
name: '',
|
||||||
selectedAuthType: 'iflow',
|
selectedAuthType: 'openai-compatible',
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
modelName: '',
|
modelName: '',
|
||||||
searchApiKey: '',
|
searchApiKey: '',
|
||||||
cna: '',
|
cna: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// Load API profiles list
|
// Load API profiles list
|
||||||
const loadApiProfiles = async () => {
|
const loadApiProfiles = async () => {
|
||||||
const result = await window.electronAPI.listApiProfiles()
|
const result = await window.electronAPI.listApiProfiles()
|
||||||
@@ -474,7 +461,6 @@ const loadApiProfiles = async () => {
|
|||||||
currentApiProfile.value = result.currentProfile || 'default'
|
currentApiProfile.value = result.currentProfile || 'default'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch API profile
|
// Switch API profile
|
||||||
const switchApiProfile = async () => {
|
const switchApiProfile = async () => {
|
||||||
const result = await window.electronAPI.switchApiProfile(currentApiProfile.value)
|
const result = await window.electronAPI.switchApiProfile(currentApiProfile.value)
|
||||||
@@ -489,35 +475,23 @@ const switchApiProfile = async () => {
|
|||||||
await showMessage({ type: 'error', title: '切换失败', message: result.error })
|
await showMessage({ type: 'error', title: '切换失败', message: result.error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new API profile
|
// Create new API profile
|
||||||
|
|
||||||
const createNewApiProfile = () => {
|
const createNewApiProfile = () => {
|
||||||
creatingApiData.value = {
|
creatingApiData.value = {
|
||||||
name: '',
|
name: '',
|
||||||
|
selectedAuthType: 'openai-compatible',
|
||||||
selectedAuthType: 'iflow',
|
|
||||||
|
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
|
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
|
|
||||||
modelName: '',
|
modelName: '',
|
||||||
|
|
||||||
searchApiKey: '',
|
searchApiKey: '',
|
||||||
|
|
||||||
cna: '',
|
cna: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
showApiCreateDialog.value = true
|
showApiCreateDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close API create dialog
|
// Close API create dialog
|
||||||
|
|
||||||
const closeApiCreateDialog = () => {
|
const closeApiCreateDialog = () => {
|
||||||
showApiCreateDialog.value = false
|
showApiCreateDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save API create
|
// Save API create
|
||||||
const saveApiCreate = async () => {
|
const saveApiCreate = async () => {
|
||||||
const name = creatingApiData.value.name.trim()
|
const name = creatingApiData.value.name.trim()
|
||||||
@@ -525,7 +499,6 @@ const saveApiCreate = async () => {
|
|||||||
await showMessage({ type: 'warning', title: '错误', message: '请输入配置名称' })
|
await showMessage({ type: 'warning', title: '错误', message: '请输入配置名称' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await window.electronAPI.createApiProfile(name)
|
const result = await window.electronAPI.createApiProfile(name)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// 创建成功后,更新配置数据
|
// 创建成功后,更新配置数据
|
||||||
@@ -537,7 +510,6 @@ const saveApiCreate = async () => {
|
|||||||
searchApiKey: creatingApiData.value.searchApiKey,
|
searchApiKey: creatingApiData.value.searchApiKey,
|
||||||
cna: creatingApiData.value.cna,
|
cna: creatingApiData.value.cna,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存配置数据
|
// 保存配置数据
|
||||||
const loadResult = await window.electronAPI.loadSettings()
|
const loadResult = await window.electronAPI.loadSettings()
|
||||||
if (loadResult.success) {
|
if (loadResult.success) {
|
||||||
@@ -545,7 +517,6 @@ const saveApiCreate = async () => {
|
|||||||
if (!data.apiProfiles) data.apiProfiles = {}
|
if (!data.apiProfiles) data.apiProfiles = {}
|
||||||
data.apiProfiles[name] = profileData
|
data.apiProfiles[name] = profileData
|
||||||
await window.electronAPI.saveSettings(data)
|
await window.electronAPI.saveSettings(data)
|
||||||
|
|
||||||
showApiCreateDialog.value = false
|
showApiCreateDialog.value = false
|
||||||
await loadApiProfiles()
|
await loadApiProfiles()
|
||||||
await showMessage({ type: 'info', title: '创建成功', message: `配置 "${name}" 已创建` })
|
await showMessage({ type: 'info', title: '创建成功', message: `配置 "${name}" 已创建` })
|
||||||
@@ -554,7 +525,6 @@ const saveApiCreate = async () => {
|
|||||||
await showMessage({ type: 'error', title: '创建失败', message: result.error })
|
await showMessage({ type: 'error', title: '创建失败', message: result.error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete API profile
|
// Delete API profile
|
||||||
const deleteApiProfile = async name => {
|
const deleteApiProfile = async name => {
|
||||||
const profileName = name || currentApiProfile.value
|
const profileName = name || currentApiProfile.value
|
||||||
@@ -562,7 +532,6 @@ const deleteApiProfile = async name => {
|
|||||||
await showMessage({ type: 'warning', title: '无法删除', message: '不能删除默认配置' })
|
await showMessage({ type: 'warning', title: '无法删除', message: '不能删除默认配置' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmed = await new Promise(resolve => {
|
const confirmed = await new Promise(resolve => {
|
||||||
showInputDialog.value = {
|
showInputDialog.value = {
|
||||||
show: true,
|
show: true,
|
||||||
@@ -573,7 +542,6 @@ const deleteApiProfile = async name => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
|
|
||||||
const result = await window.electronAPI.deleteApiProfile(profileName)
|
const result = await window.electronAPI.deleteApiProfile(profileName)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const data = JSON.parse(JSON.stringify(result.data))
|
const data = JSON.parse(JSON.stringify(result.data))
|
||||||
@@ -589,26 +557,6 @@ const deleteApiProfile = async name => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename API profile
|
|
||||||
const renameApiProfile = async () => {
|
|
||||||
if (currentApiProfile.value === 'default') {
|
|
||||||
await showMessage({ type: 'warning', title: '无法重命名', message: '不能重命名默认配置' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const newName = await new Promise(resolve => {
|
|
||||||
showInputDialog.value = { show: true, title: '重命名配置', placeholder: '请输入新的配置名称', defaultValue: currentApiProfile.value, callback: resolve }
|
|
||||||
})
|
|
||||||
if (!newName || newName === currentApiProfile.value) return
|
|
||||||
const result = await window.electronAPI.renameApiProfile(currentApiProfile.value, newName)
|
|
||||||
if (result.success) {
|
|
||||||
currentApiProfile.value = newName
|
|
||||||
await loadApiProfiles()
|
|
||||||
await showMessage({ type: 'info', title: '重命名成功', message: `配置已重命名为 "${newName}"` })
|
|
||||||
} else {
|
|
||||||
await showMessage({ type: 'error', title: '重命名失败', message: result.error })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select API profile (click on card)
|
// Select API profile (click on card)
|
||||||
const selectApiProfile = async name => {
|
const selectApiProfile = async name => {
|
||||||
if (name === currentApiProfile.value) return
|
if (name === currentApiProfile.value) return
|
||||||
@@ -617,13 +565,11 @@ const selectApiProfile = async name => {
|
|||||||
await switchApiProfile()
|
await switchApiProfile()
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get profile initial letter for icon
|
// Get profile initial letter for icon
|
||||||
const getProfileInitial = name => {
|
const getProfileInitial = name => {
|
||||||
if (!name) return '?'
|
if (!name) return '?'
|
||||||
return name.charAt(0).toUpperCase()
|
return name.charAt(0).toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get profile URL for display
|
// Get profile URL for display
|
||||||
const getProfileUrl = name => {
|
const getProfileUrl = name => {
|
||||||
if (!settings.value.apiProfiles || !settings.value.apiProfiles[name]) {
|
if (!settings.value.apiProfiles || !settings.value.apiProfiles[name]) {
|
||||||
@@ -632,7 +578,6 @@ const getProfileUrl = name => {
|
|||||||
const profile = settings.value.apiProfiles[name]
|
const profile = settings.value.apiProfiles[name]
|
||||||
return profile.baseUrl || '未配置 Base URL'
|
return profile.baseUrl || '未配置 Base URL'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get profile icon style (gradient colors)
|
// Get profile icon style (gradient colors)
|
||||||
const profileColors = [
|
const profileColors = [
|
||||||
'linear-gradient(135deg, #f97316 0%, #fb923c 100%)', // orange
|
'linear-gradient(135deg, #f97316 0%, #fb923c 100%)', // orange
|
||||||
@@ -642,7 +587,6 @@ const profileColors = [
|
|||||||
'linear-gradient(135deg, #f43f5e 0%, #fb7185 100%)', // rose
|
'linear-gradient(135deg, #f43f5e 0%, #fb7185 100%)', // rose
|
||||||
'linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)', // blue
|
'linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)', // blue
|
||||||
]
|
]
|
||||||
|
|
||||||
const getProfileIconStyle = name => {
|
const getProfileIconStyle = name => {
|
||||||
if (name === 'default') {
|
if (name === 'default') {
|
||||||
return { background: 'linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%)' }
|
return { background: 'linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%)' }
|
||||||
@@ -654,35 +598,25 @@ const getProfileIconStyle = name => {
|
|||||||
const index = Math.abs(hash) % profileColors.length
|
const index = Math.abs(hash) % profileColors.length
|
||||||
return { background: profileColors[index] }
|
return { background: profileColors[index] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicate API profile
|
// Duplicate API profile
|
||||||
|
|
||||||
const duplicateApiProfile = async name => {
|
const duplicateApiProfile = async name => {
|
||||||
const newName = await new Promise(resolve => {
|
const newName = await new Promise(resolve => {
|
||||||
showInputDialog.value = {
|
showInputDialog.value = {
|
||||||
show: true,
|
show: true,
|
||||||
|
|
||||||
title: '复制配置',
|
title: '复制配置',
|
||||||
|
|
||||||
placeholder: '请输入新配置的名称',
|
placeholder: '请输入新配置的名称',
|
||||||
|
|
||||||
callback: resolve,
|
callback: resolve,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!newName) return
|
if (!newName) return
|
||||||
|
|
||||||
const result = await window.electronAPI.duplicateApiProfile(name, newName)
|
const result = await window.electronAPI.duplicateApiProfile(name, newName)
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
await loadApiProfiles()
|
await loadApiProfiles()
|
||||||
|
|
||||||
await showMessage({ type: 'info', title: '复制成功', message: `配置已复制为 "${newName}"` })
|
await showMessage({ type: 'info', title: '复制成功', message: `配置已复制为 "${newName}"` })
|
||||||
} else {
|
} else {
|
||||||
await showMessage({ type: 'error', title: '复制失败', message: result.error })
|
await showMessage({ type: 'error', title: '复制失败', message: result.error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open API edit dialog
|
// Open API edit dialog
|
||||||
const openApiEditDialog = profileName => {
|
const openApiEditDialog = profileName => {
|
||||||
// 保存正在编辑的配置名称
|
// 保存正在编辑的配置名称
|
||||||
@@ -699,24 +633,19 @@ const openApiEditDialog = profileName => {
|
|||||||
}
|
}
|
||||||
showApiEditDialog.value = true
|
showApiEditDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close API edit dialog
|
// Close API edit dialog
|
||||||
|
|
||||||
const closeApiEditDialog = () => {
|
const closeApiEditDialog = () => {
|
||||||
showApiEditDialog.value = false
|
showApiEditDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save API edit
|
// Save API edit
|
||||||
const saveApiEdit = async () => {
|
const saveApiEdit = async () => {
|
||||||
if (!settings.value.apiProfiles) {
|
if (!settings.value.apiProfiles) {
|
||||||
settings.value.apiProfiles = {}
|
settings.value.apiProfiles = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保配置对象存在
|
// 确保配置对象存在
|
||||||
if (!settings.value.apiProfiles[editingApiProfileName.value]) {
|
if (!settings.value.apiProfiles[editingApiProfileName.value]) {
|
||||||
settings.value.apiProfiles[editingApiProfileName.value] = {}
|
settings.value.apiProfiles[editingApiProfileName.value] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存到指定的配置
|
// 保存到指定的配置
|
||||||
settings.value.apiProfiles[editingApiProfileName.value].selectedAuthType = editingApiData.value.selectedAuthType
|
settings.value.apiProfiles[editingApiProfileName.value].selectedAuthType = editingApiData.value.selectedAuthType
|
||||||
settings.value.apiProfiles[editingApiProfileName.value].apiKey = editingApiData.value.apiKey
|
settings.value.apiProfiles[editingApiProfileName.value].apiKey = editingApiData.value.apiKey
|
||||||
@@ -724,17 +653,15 @@ const saveApiEdit = async () => {
|
|||||||
settings.value.apiProfiles[editingApiProfileName.value].modelName = editingApiData.value.modelName
|
settings.value.apiProfiles[editingApiProfileName.value].modelName = editingApiData.value.modelName
|
||||||
settings.value.apiProfiles[editingApiProfileName.value].searchApiKey = editingApiData.value.searchApiKey
|
settings.value.apiProfiles[editingApiProfileName.value].searchApiKey = editingApiData.value.searchApiKey
|
||||||
settings.value.apiProfiles[editingApiProfileName.value].cna = editingApiData.value.cna
|
settings.value.apiProfiles[editingApiProfileName.value].cna = editingApiData.value.cna
|
||||||
|
|
||||||
showApiEditDialog.value = false
|
showApiEditDialog.value = false
|
||||||
|
|
||||||
// 自动保存到文件
|
// 自动保存到文件
|
||||||
const result = await window.electronAPI.saveSettings(settings.value)
|
const dataToSave = JSON.parse(JSON.stringify(settings.value))
|
||||||
|
const result = await window.electronAPI.saveSettings(dataToSave)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
originalSettings.value = JSON.parse(JSON.stringify(settings.value))
|
originalSettings.value = JSON.parse(JSON.stringify(dataToSave))
|
||||||
modified.value = false
|
modified.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
const result = await window.electronAPI.loadSettings()
|
const result = await window.electronAPI.loadSettings()
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -746,7 +673,7 @@ const loadSettings = async () => {
|
|||||||
if (data.theme === undefined) data.theme = 'Xcode'
|
if (data.theme === undefined) data.theme = 'Xcode'
|
||||||
if (data.bootAnimationShown === undefined) data.bootAnimationShown = true
|
if (data.bootAnimationShown === undefined) data.bootAnimationShown = true
|
||||||
// 确保 API 相关字段有默认值
|
// 确保 API 相关字段有默认值
|
||||||
if (data.selectedAuthType === undefined) data.selectedAuthType = 'iflow'
|
if (data.selectedAuthType === undefined) data.selectedAuthType = 'openai-compatible'
|
||||||
if (data.apiKey === undefined) data.apiKey = ''
|
if (data.apiKey === undefined) data.apiKey = ''
|
||||||
if (data.baseUrl === undefined) data.baseUrl = ''
|
if (data.baseUrl === undefined) data.baseUrl = ''
|
||||||
if (data.modelName === undefined) data.modelName = ''
|
if (data.modelName === undefined) data.modelName = ''
|
||||||
@@ -761,29 +688,6 @@ const loadSettings = async () => {
|
|||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveSettings = async () => {
|
|
||||||
const dataToSave = JSON.parse(JSON.stringify(settings.value))
|
|
||||||
const result = await window.electronAPI.saveSettings(dataToSave)
|
|
||||||
if (result.success) {
|
|
||||||
originalSettings.value = JSON.parse(JSON.stringify(settings.value))
|
|
||||||
modified.value = false
|
|
||||||
await showMessage({ type: 'info', title: '保存成功', message: '设置已保存到 settings.json' })
|
|
||||||
} else {
|
|
||||||
await showMessage({ type: 'error', title: '保存失败', message: `无法保存设置: ${result.error}` })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const reloadSettings = async () => {
|
|
||||||
if (modified.value) {
|
|
||||||
const confirmed = await new Promise(resolve => {
|
|
||||||
showInputDialog.value = { show: true, title: '重新加载', placeholder: '当前有未保存的更改,确定要重新加载吗?', callback: resolve, isConfirm: true }
|
|
||||||
})
|
|
||||||
if (!confirmed) return
|
|
||||||
}
|
|
||||||
currentServerName.value = null
|
|
||||||
await loadSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
settings,
|
settings,
|
||||||
() => {
|
() => {
|
||||||
@@ -793,20 +697,15 @@ watch(
|
|||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
const showSection = section => {
|
const showSection = section => {
|
||||||
currentSection.value = section
|
currentSection.value = section
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverCount = computed(() => (settings.value.mcpServers ? Object.keys(settings.value.mcpServers).length : 0))
|
const serverCount = computed(() => (settings.value.mcpServers ? Object.keys(settings.value.mcpServers).length : 0))
|
||||||
|
|
||||||
const selectServer = name => {
|
const selectServer = name => {
|
||||||
currentServerName.value = name
|
currentServerName.value = name
|
||||||
openEditServerPanel(name)
|
openEditServerPanel(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverPanelOverlay = ref(null)
|
const serverPanelOverlay = ref(null)
|
||||||
|
|
||||||
const openAddServerPanel = () => {
|
const openAddServerPanel = () => {
|
||||||
isEditingServer.value = false
|
isEditingServer.value = false
|
||||||
editingServerData.value = {
|
editingServerData.value = {
|
||||||
@@ -822,7 +721,6 @@ const openAddServerPanel = () => {
|
|||||||
serverPanelOverlay.value?.focus()
|
serverPanelOverlay.value?.focus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const openEditServerPanel = name => {
|
const openEditServerPanel = name => {
|
||||||
const server = settings.value.mcpServers[name]
|
const server = settings.value.mcpServers[name]
|
||||||
if (!server) return
|
if (!server) return
|
||||||
@@ -840,11 +738,9 @@ const openEditServerPanel = name => {
|
|||||||
serverPanelOverlay.value?.focus()
|
serverPanelOverlay.value?.focus()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeServerPanel = () => {
|
const closeServerPanel = () => {
|
||||||
showServerPanel.value = false
|
showServerPanel.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveServerFromPanel = async () => {
|
const saveServerFromPanel = async () => {
|
||||||
const name = editingServerData.value.name.trim()
|
const name = editingServerData.value.name.trim()
|
||||||
if (!name) {
|
if (!name) {
|
||||||
@@ -855,12 +751,10 @@ const saveServerFromPanel = async () => {
|
|||||||
await showMessage({ type: 'warning', title: '错误', message: '服务器名称已存在' })
|
await showMessage({ type: 'warning', title: '错误', message: '服务器名称已存在' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是编辑模式且名称改变了,需要删除旧的服务器
|
// 如果是编辑模式且名称改变了,需要删除旧的服务器
|
||||||
if (isEditingServer.value && currentServerName.value && currentServerName.value !== name) {
|
if (isEditingServer.value && currentServerName.value && currentServerName.value !== name) {
|
||||||
delete settings.value.mcpServers[currentServerName.value]
|
delete settings.value.mcpServers[currentServerName.value]
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverConfig = {
|
const serverConfig = {
|
||||||
command: editingServerData.value.command.trim(),
|
command: editingServerData.value.command.trim(),
|
||||||
description: editingServerData.value.description.trim(),
|
description: editingServerData.value.description.trim(),
|
||||||
@@ -870,7 +764,6 @@ const saveServerFromPanel = async () => {
|
|||||||
.map(s => s.trim())
|
.map(s => s.trim())
|
||||||
.filter(s => s),
|
.filter(s => s),
|
||||||
}
|
}
|
||||||
|
|
||||||
const envText = editingServerData.value.env.trim()
|
const envText = editingServerData.value.env.trim()
|
||||||
if (envText) {
|
if (envText) {
|
||||||
try {
|
try {
|
||||||
@@ -880,16 +773,20 @@ const saveServerFromPanel = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.value.mcpServers[name] = serverConfig
|
settings.value.mcpServers[name] = serverConfig
|
||||||
currentServerName.value = name
|
currentServerName.value = name
|
||||||
showServerPanel.value = false
|
showServerPanel.value = false
|
||||||
|
// 自动保存到文件
|
||||||
|
const dataToSave = JSON.parse(JSON.stringify(settings.value))
|
||||||
|
const result = await window.electronAPI.saveSettings(dataToSave)
|
||||||
|
if (result.success) {
|
||||||
|
originalSettings.value = JSON.parse(JSON.stringify(dataToSave))
|
||||||
|
modified.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addServer = async () => {
|
const addServer = async () => {
|
||||||
openAddServerPanel()
|
openAddServerPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteServer = async () => {
|
const deleteServer = async () => {
|
||||||
const serverName = isEditingServer.value ? editingServerData.value.name : currentServerName.value
|
const serverName = isEditingServer.value ? editingServerData.value.name : currentServerName.value
|
||||||
if (!serverName) return
|
if (!serverName) return
|
||||||
@@ -900,17 +797,21 @@ const deleteServer = async () => {
|
|||||||
delete settings.value.mcpServers[serverName]
|
delete settings.value.mcpServers[serverName]
|
||||||
currentServerName.value = null
|
currentServerName.value = null
|
||||||
showServerPanel.value = false
|
showServerPanel.value = false
|
||||||
|
// 自动保存到文件
|
||||||
|
const dataToSave = JSON.parse(JSON.stringify(settings.value))
|
||||||
|
const result = await window.electronAPI.saveSettings(dataToSave)
|
||||||
|
if (result.success) {
|
||||||
|
originalSettings.value = JSON.parse(JSON.stringify(dataToSave))
|
||||||
|
modified.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentServer = computed(() => {
|
const currentServer = computed(() => {
|
||||||
if (!currentServerName.value || !settings.value.mcpServers) return null
|
if (!currentServerName.value || !settings.value.mcpServers) return null
|
||||||
return settings.value.mcpServers[currentServerName.value]
|
return settings.value.mcpServers[currentServerName.value]
|
||||||
})
|
})
|
||||||
|
|
||||||
const minimize = () => window.electronAPI.minimize()
|
const minimize = () => window.electronAPI.minimize()
|
||||||
const maximize = () => window.electronAPI.maximize()
|
const maximize = () => window.electronAPI.maximize()
|
||||||
const close = () => window.electronAPI.close()
|
const close = () => window.electronAPI.close()
|
||||||
|
|
||||||
const closeInputDialog = result => {
|
const closeInputDialog = result => {
|
||||||
if (showInputDialog.value.callback) {
|
if (showInputDialog.value.callback) {
|
||||||
// 如果是确认对话框,传递 result(true/false)
|
// 如果是确认对话框,传递 result(true/false)
|
||||||
@@ -926,21 +827,18 @@ const closeInputDialog = result => {
|
|||||||
showInputDialog.value.defaultValue = ''
|
showInputDialog.value.defaultValue = ''
|
||||||
inputDialogValue.value = ''
|
inputDialogValue.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message Dialog
|
// Message Dialog
|
||||||
const showMessage = ({ type = 'info', title, message }) => {
|
const showMessage = ({ type = 'info', title, message }) => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
showMessageDialog.value = { show: true, type, title, message, callback: resolve }
|
showMessageDialog.value = { show: true, type, title, message, callback: resolve }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeMessageDialog = () => {
|
const closeMessageDialog = () => {
|
||||||
if (showMessageDialog.value.callback) {
|
if (showMessageDialog.value.callback) {
|
||||||
showMessageDialog.value.callback(true)
|
showMessageDialog.value.callback(true)
|
||||||
}
|
}
|
||||||
showMessageDialog.value.show = false
|
showMessageDialog.value.show = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for dialog open to set default value
|
// Watch for dialog open to set default value
|
||||||
watch(
|
watch(
|
||||||
() => showInputDialog.value.show,
|
() => showInputDialog.value.show,
|
||||||
@@ -950,13 +848,11 @@ watch(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadApiProfiles()
|
await loadApiProfiles()
|
||||||
await loadSettings()
|
await loadSettings()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -984,7 +880,6 @@ onMounted(async () => {
|
|||||||
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.06), 0 2px 4px -2px rgba(0, 0, 0, 0.04);
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.06), 0 2px 4px -2px rgba(0, 0, 0, 0.04);
|
||||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.04);
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from {
|
from {
|
||||||
@@ -1033,7 +928,6 @@ body {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollbar Styles */
|
/* Scrollbar Styles */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@@ -1058,13 +952,11 @@ body {
|
|||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: var(--border) transparent;
|
scrollbar-color: var(--border) transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Titlebar */
|
/* Titlebar */
|
||||||
.titlebar {
|
.titlebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1081,13 +973,6 @@ body {
|
|||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.titlebar-icon {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 50%, #ec4899 100%);
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
|
|
||||||
}
|
|
||||||
.titlebar-title {
|
.titlebar-title {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -1129,44 +1014,6 @@ body {
|
|||||||
stroke-width: 1.5;
|
stroke-width: 1.5;
|
||||||
fill: none;
|
fill: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 24px;
|
|
||||||
height: 60px;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.header-left {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
.header-icon {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 50%, #ec4899 100%);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.25);
|
|
||||||
}
|
|
||||||
.header-title {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
.header-subtitle {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
.header-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
.btn {
|
.btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -1216,11 +1063,6 @@ body {
|
|||||||
.btn-primary:active {
|
.btn-primary:active {
|
||||||
transform: translateY(0) scale(0.98);
|
transform: translateY(0) scale(0.98);
|
||||||
}
|
}
|
||||||
.btn-secondary {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.btn-secondary:hover {
|
.btn-secondary:hover {
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
@@ -1246,18 +1088,12 @@ body {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
transform: none !important;
|
transform: none !important;
|
||||||
}
|
}
|
||||||
.btn-group {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main Layout */
|
/* Main Layout */
|
||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sidebar */
|
/* Sidebar */
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 220px;
|
width: 220px;
|
||||||
@@ -1332,7 +1168,6 @@ body {
|
|||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Content */
|
/* Content */
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -1358,7 +1193,6 @@ body {
|
|||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
animation: fadeIn 0.4s ease 0.1s backwards;
|
animation: fadeIn 0.4s ease 0.1s backwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cards */
|
/* Cards */
|
||||||
.card {
|
.card {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
@@ -1396,7 +1230,6 @@ body {
|
|||||||
.card-title .iconpark-icon {
|
.card-title .iconpark-icon {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form Elements */
|
/* Form Elements */
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
@@ -1454,7 +1287,6 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right 12px center;
|
background-position: right 12px center;
|
||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
@@ -1469,7 +1301,6 @@ body {
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px var(--accent-light);
|
box-shadow: 0 0 0 3px var(--accent-light);
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%233b82f6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
|
||||||
}
|
}
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -1490,7 +1321,6 @@ body {
|
|||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px var(--accent-light);
|
box-shadow: 0 0 0 3px var(--accent-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Server List */
|
/* Server List */
|
||||||
.server-list {
|
.server-list {
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
@@ -1568,7 +1398,6 @@ body {
|
|||||||
box-shadow: 0 0 6px rgba(16, 185, 129, 0.5);
|
box-shadow: 0 0 6px rgba(16, 185, 129, 0.5);
|
||||||
animation: pulse 2s ease-in-out infinite;
|
animation: pulse 2s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Empty State */
|
/* Empty State */
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1597,7 +1426,6 @@ body {
|
|||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
.footer {
|
.footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1625,7 +1453,6 @@ body {
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dialog */
|
/* Dialog */
|
||||||
.dialog-overlay {
|
.dialog-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1687,13 +1514,14 @@ body {
|
|||||||
margin-top: 22px;
|
margin-top: 22px;
|
||||||
}
|
}
|
||||||
.dialog-overlay-top {
|
.dialog-overlay-top {
|
||||||
z-index: 1100;
|
z-index: 1300;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Message Dialog */
|
/* Message Dialog */
|
||||||
.message-dialog {
|
.message-dialog {
|
||||||
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 32px 24px;
|
padding: 32px 24px;
|
||||||
|
z-index: 1400;
|
||||||
}
|
}
|
||||||
.message-dialog-icon {
|
.message-dialog-icon {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
@@ -1742,7 +1570,6 @@ body {
|
|||||||
.message-dialog .dialog-actions .btn {
|
.message-dialog .dialog-actions .btn {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconpark-icon {
|
.iconpark-icon {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1753,26 +1580,23 @@ body {
|
|||||||
.iconpark-icon svg {
|
.iconpark-icon svg {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Profile List (API Configuration Cards) */
|
/* Profile List (API Configuration Cards) */
|
||||||
.profile-list {
|
.profile-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-item {
|
.profile-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 14px 16px;
|
padding: 14px 16px;
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border: 2px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
animation: fadeIn 0.3s ease backwards;
|
animation: fadeIn 0.3s ease backwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-item:nth-child(1) {
|
.profile-item:nth-child(1) {
|
||||||
animation-delay: 0.02s;
|
animation-delay: 0.02s;
|
||||||
}
|
}
|
||||||
@@ -1788,21 +1612,17 @@ body {
|
|||||||
.profile-item:nth-child(5) {
|
.profile-item:nth-child(5) {
|
||||||
animation-delay: 0.1s;
|
animation-delay: 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-item:hover {
|
.profile-item:hover {
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
border-color: var(--text-tertiary);
|
border-color: var(--text-tertiary);
|
||||||
transform: translateX(4px);
|
transform: translateX(4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-item.active {
|
.profile-item.active {
|
||||||
background: linear-gradient(135deg, rgba(59, 130, 246, 0.05) 0%, rgba(139, 92, 246, 0.05) 100%);
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.05) 0%, rgba(139, 92, 246, 0.05) 100%);
|
||||||
border-color: var(--accent);
|
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 0 1px var(--accent),
|
0 0 0 1px var(--accent),
|
||||||
0 4px 12px rgba(59, 130, 246, 0.15);
|
0 4px 12px rgba(59, 130, 246, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-icon {
|
.profile-icon {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -1813,27 +1633,23 @@ body {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-icon-text {
|
.profile-icon-text {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: white;
|
color: white;
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-info {
|
.profile-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin-left: 14px;
|
margin-left: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-name {
|
.profile-name {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-url {
|
.profile-url {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
@@ -1842,11 +1658,9 @@ body {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-status {
|
.profile-status {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge {
|
.status-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1858,12 +1672,10 @@ body {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge svg {
|
.status-badge svg {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-actions {
|
.profile-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1872,12 +1684,10 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-item:hover .profile-actions,
|
.profile-item:hover .profile-actions,
|
||||||
.profile-item.active .profile-actions {
|
.profile-item.active .profile-actions {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@@ -1891,22 +1701,18 @@ body {
|
|||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn:hover {
|
.action-btn:hover {
|
||||||
background: var(--bg-hover);
|
background: var(--bg-hover);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn.action-btn-danger:hover {
|
.action-btn.action-btn-danger:hover {
|
||||||
background: rgba(239, 68, 68, 0.1);
|
background: rgba(239, 68, 68, 0.1);
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Side Panel */
|
/* Side Panel */
|
||||||
.side-panel-overlay {
|
.side-panel-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -2013,7 +1819,6 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* API Edit Dialog */
|
/* API Edit Dialog */
|
||||||
.api-edit-dialog {
|
.api-edit-dialog {
|
||||||
min-width: 480px;
|
min-width: 480px;
|
||||||
@@ -2021,7 +1826,6 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-header {
|
.api-edit-dialog .dialog-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -2030,7 +1834,6 @@ body {
|
|||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-title {
|
.api-edit-dialog .dialog-title {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -2040,25 +1843,20 @@ body {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-title .iconpark-icon {
|
.api-edit-dialog .dialog-title .iconpark-icon {
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-body {
|
.api-edit-dialog .dialog-body {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
max-height: 60vh;
|
max-height: 60vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-body .form-group {
|
.api-edit-dialog .dialog-body .form-group {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-body .form-group:last-child {
|
.api-edit-dialog .dialog-body .form-group:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.api-edit-dialog .dialog-actions {
|
.api-edit-dialog .dialog-actions {
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
|
|||||||
Reference in New Issue
Block a user