You've already forked iFlow-Settings-Editor-GUI
新增 完整的国际化(i18n)支持,支持中英日三种语言
This commit is contained in:
BIN
lang-zh-CN.png
Normal file
BIN
lang-zh-CN.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
108
main.js
108
main.js
@@ -9,6 +9,68 @@ let mainWindow
|
|||||||
let tray
|
let tray
|
||||||
const isDev = process.argv.includes('--dev')
|
const isDev = process.argv.includes('--dev')
|
||||||
|
|
||||||
|
// 主进程翻译
|
||||||
|
const trayTranslations = {
|
||||||
|
'zh-CN': {
|
||||||
|
showWindow: '显示主窗口',
|
||||||
|
switchApiConfig: '切换 API 配置',
|
||||||
|
exit: '退出',
|
||||||
|
tooltip: 'iFlow 设置编辑器'
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
showWindow: 'Show Window',
|
||||||
|
switchApiConfig: 'Switch API Config',
|
||||||
|
exit: 'Exit',
|
||||||
|
tooltip: 'iFlow Settings Editor'
|
||||||
|
},
|
||||||
|
'ja-JP': {
|
||||||
|
showWindow: 'メインウィンドウを表示',
|
||||||
|
switchApiConfig: 'API 設定切替',
|
||||||
|
exit: '終了',
|
||||||
|
tooltip: 'iFlow 設定エディタ'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTrayTranslation() {
|
||||||
|
const settings = readSettings()
|
||||||
|
const lang = settings?.language || 'zh-CN'
|
||||||
|
return trayTranslations[lang] || trayTranslations['zh-CN']
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误消息翻译
|
||||||
|
const errorTranslations = {
|
||||||
|
'zh-CN': {
|
||||||
|
configNotFound: '配置文件不存在',
|
||||||
|
configNotExist: '配置 "{name}" 不存在',
|
||||||
|
configAlreadyExists: '配置 "{name}" 已存在',
|
||||||
|
cannotDeleteDefault: '不能删除默认配置',
|
||||||
|
cannotRenameDefault: '不能重命名默认配置',
|
||||||
|
switchFailed: '切换API配置失败'
|
||||||
|
},
|
||||||
|
'en-US': {
|
||||||
|
configNotFound: 'Configuration file not found',
|
||||||
|
configNotExist: 'Configuration "{name}" does not exist',
|
||||||
|
configAlreadyExists: 'Configuration "{name}" already exists',
|
||||||
|
cannotDeleteDefault: 'Cannot delete default configuration',
|
||||||
|
cannotRenameDefault: 'Cannot rename default configuration',
|
||||||
|
switchFailed: 'Failed to switch API configuration'
|
||||||
|
},
|
||||||
|
'ja-JP': {
|
||||||
|
configNotFound: '設定ファイルが存在しません',
|
||||||
|
configNotExist: 'プロファイル "{name}" が存在しません',
|
||||||
|
configAlreadyExists: 'プロファイル "{name}" が既に存在します',
|
||||||
|
cannotDeleteDefault: 'デフォルトプロファイルは削除できません',
|
||||||
|
cannotRenameDefault: 'デフォルトプロファイルは名前変更できません',
|
||||||
|
switchFailed: 'API 設定の切替に失敗しました'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getErrorTranslation() {
|
||||||
|
const settings = readSettings()
|
||||||
|
const lang = settings?.language || 'zh-CN'
|
||||||
|
return errorTranslations[lang] || errorTranslations['zh-CN']
|
||||||
|
}
|
||||||
|
|
||||||
// 创建系统托盘
|
// 创建系统托盘
|
||||||
function createTray() {
|
function createTray() {
|
||||||
// 获取图标路径 - 打包后需要从 extraResources 获取
|
// 获取图标路径 - 打包后需要从 extraResources 获取
|
||||||
@@ -30,7 +92,7 @@ function createTray() {
|
|||||||
trayIcon = trayIcon.resize({ width: 16, height: 16 })
|
trayIcon = trayIcon.resize({ width: 16, height: 16 })
|
||||||
|
|
||||||
tray = new Tray(trayIcon)
|
tray = new Tray(trayIcon)
|
||||||
tray.setToolTip('iFlow 设置编辑器')
|
tray.setToolTip(getTrayTranslation().tooltip)
|
||||||
|
|
||||||
updateTrayMenu()
|
updateTrayMenu()
|
||||||
|
|
||||||
@@ -59,9 +121,10 @@ function updateTrayMenu() {
|
|||||||
click: () => switchApiProfileFromTray(name)
|
click: () => switchApiProfileFromTray(name)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const t = getTrayTranslation()
|
||||||
const contextMenu = Menu.buildFromTemplate([
|
const contextMenu = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: '显示主窗口',
|
label: t.showWindow,
|
||||||
click: () => {
|
click: () => {
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
mainWindow.show()
|
mainWindow.show()
|
||||||
@@ -71,12 +134,12 @@ function updateTrayMenu() {
|
|||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
label: '切换 API 配置',
|
label: t.switchApiConfig,
|
||||||
submenu: profileMenuItems
|
submenu: profileMenuItems
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
label: '退出',
|
label: t.exit,
|
||||||
click: () => {
|
click: () => {
|
||||||
app.isQuitting = true
|
app.isQuitting = true
|
||||||
app.quit()
|
app.quit()
|
||||||
@@ -198,6 +261,10 @@ ipcMain.on('window-close', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
ipcMain.handle('is-maximized', () => mainWindow.isMaximized())
|
ipcMain.handle('is-maximized', () => mainWindow.isMaximized())
|
||||||
|
// 监听语言切换以更新托盘菜单
|
||||||
|
ipcMain.on('language-changed', () => {
|
||||||
|
updateTrayMenu()
|
||||||
|
})
|
||||||
// API 配置相关的字段
|
// API 配置相关的字段
|
||||||
const API_FIELDS = ['selectedAuthType', 'apiKey', 'baseUrl', 'modelName', 'searchApiKey', 'cna']
|
const API_FIELDS = ['selectedAuthType', 'apiKey', 'baseUrl', 'modelName', 'searchApiKey', 'cna']
|
||||||
// 读取设置文件
|
// 读取设置文件
|
||||||
@@ -245,12 +312,13 @@ ipcMain.handle('list-api-profiles', async () => {
|
|||||||
ipcMain.handle('switch-api-profile', async (event, profileName) => {
|
ipcMain.handle('switch-api-profile', async (event, profileName) => {
|
||||||
try {
|
try {
|
||||||
const settings = readSettings()
|
const settings = readSettings()
|
||||||
|
const t = getErrorTranslation()
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return { success: false, error: '配置文件不存在' }
|
return { success: false, error: t.configNotFound }
|
||||||
}
|
}
|
||||||
const profiles = settings.apiProfiles || {}
|
const profiles = settings.apiProfiles || {}
|
||||||
if (!profiles[profileName]) {
|
if (!profiles[profileName]) {
|
||||||
return { success: false, error: `配置 "${profileName}" 不存在` }
|
return { success: false, error: t.configNotExist.replace('{name}', profileName) }
|
||||||
}
|
}
|
||||||
// 保存当前配置到 apiProfiles(如果当前配置存在)
|
// 保存当前配置到 apiProfiles(如果当前配置存在)
|
||||||
const currentProfile = settings.currentApiProfile || 'default'
|
const currentProfile = settings.currentApiProfile || 'default'
|
||||||
@@ -282,8 +350,9 @@ ipcMain.handle('switch-api-profile', async (event, profileName) => {
|
|||||||
ipcMain.handle('create-api-profile', async (event, name) => {
|
ipcMain.handle('create-api-profile', async (event, name) => {
|
||||||
try {
|
try {
|
||||||
const settings = readSettings()
|
const settings = readSettings()
|
||||||
|
const t = getErrorTranslation()
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return { success: false, error: '配置文件不存在' }
|
return { success: false, error: t.configNotFound }
|
||||||
}
|
}
|
||||||
if (!settings.apiProfiles) {
|
if (!settings.apiProfiles) {
|
||||||
settings.apiProfiles = { default: {} }
|
settings.apiProfiles = { default: {} }
|
||||||
@@ -295,7 +364,7 @@ ipcMain.handle('create-api-profile', async (event, name) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (settings.apiProfiles[name]) {
|
if (settings.apiProfiles[name]) {
|
||||||
return { success: false, error: `配置 "${name}" 已存在` }
|
return { success: false, error: t.configAlreadyExists.replace('{name}', name) }
|
||||||
}
|
}
|
||||||
// 复制当前配置到新配置
|
// 复制当前配置到新配置
|
||||||
const newConfig = {}
|
const newConfig = {}
|
||||||
@@ -315,15 +384,16 @@ ipcMain.handle('create-api-profile', async (event, name) => {
|
|||||||
ipcMain.handle('delete-api-profile', async (event, name) => {
|
ipcMain.handle('delete-api-profile', async (event, name) => {
|
||||||
try {
|
try {
|
||||||
const settings = readSettings()
|
const settings = readSettings()
|
||||||
|
const t = getErrorTranslation()
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return { success: false, error: '配置文件不存在' }
|
return { success: false, error: t.configNotFound }
|
||||||
}
|
}
|
||||||
if (name === 'default') {
|
if (name === 'default') {
|
||||||
return { success: false, error: '不能删除默认配置' }
|
return { success: false, error: t.cannotDeleteDefault }
|
||||||
}
|
}
|
||||||
const profiles = settings.apiProfiles || {}
|
const profiles = settings.apiProfiles || {}
|
||||||
if (!profiles[name]) {
|
if (!profiles[name]) {
|
||||||
return { success: false, error: `配置 "${name}" 不存在` }
|
return { success: false, error: t.configNotExist.replace('{name}', name) }
|
||||||
}
|
}
|
||||||
delete profiles[name]
|
delete profiles[name]
|
||||||
settings.apiProfiles = profiles
|
settings.apiProfiles = profiles
|
||||||
@@ -348,18 +418,19 @@ ipcMain.handle('delete-api-profile', async (event, name) => {
|
|||||||
ipcMain.handle('rename-api-profile', async (event, oldName, newName) => {
|
ipcMain.handle('rename-api-profile', async (event, oldName, newName) => {
|
||||||
try {
|
try {
|
||||||
const settings = readSettings()
|
const settings = readSettings()
|
||||||
|
const t = getErrorTranslation()
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return { success: false, error: '配置文件不存在' }
|
return { success: false, error: t.configNotFound }
|
||||||
}
|
}
|
||||||
if (oldName === 'default') {
|
if (oldName === 'default') {
|
||||||
return { success: false, error: '不能重命名默认配置' }
|
return { success: false, error: t.cannotRenameDefault }
|
||||||
}
|
}
|
||||||
const profiles = settings.apiProfiles || {}
|
const profiles = settings.apiProfiles || {}
|
||||||
if (!profiles[oldName]) {
|
if (!profiles[oldName]) {
|
||||||
return { success: false, error: `配置 "${oldName}" 不存在` }
|
return { success: false, error: t.configNotExist.replace('{name}', oldName) }
|
||||||
}
|
}
|
||||||
if (profiles[newName]) {
|
if (profiles[newName]) {
|
||||||
return { success: false, error: `配置 "${newName}" 已存在` }
|
return { success: false, error: t.configAlreadyExists.replace('{name}', newName) }
|
||||||
}
|
}
|
||||||
profiles[newName] = profiles[oldName]
|
profiles[newName] = profiles[oldName]
|
||||||
delete profiles[oldName]
|
delete profiles[oldName]
|
||||||
@@ -377,15 +448,16 @@ ipcMain.handle('rename-api-profile', async (event, oldName, newName) => {
|
|||||||
ipcMain.handle('duplicate-api-profile', async (event, sourceName, newName) => {
|
ipcMain.handle('duplicate-api-profile', async (event, sourceName, newName) => {
|
||||||
try {
|
try {
|
||||||
const settings = readSettings()
|
const settings = readSettings()
|
||||||
|
const t = getErrorTranslation()
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return { success: false, error: '配置文件不存在' }
|
return { success: false, error: t.configNotFound }
|
||||||
}
|
}
|
||||||
const profiles = settings.apiProfiles || {}
|
const profiles = settings.apiProfiles || {}
|
||||||
if (!profiles[sourceName]) {
|
if (!profiles[sourceName]) {
|
||||||
return { success: false, error: `配置 "${sourceName}" 不存在` }
|
return { success: false, error: t.configNotExist.replace('{name}', sourceName) }
|
||||||
}
|
}
|
||||||
if (profiles[newName]) {
|
if (profiles[newName]) {
|
||||||
return { success: false, error: `配置 "${newName}" 已存在` }
|
return { success: false, error: t.configAlreadyExists.replace('{name}', newName) }
|
||||||
}
|
}
|
||||||
// 深拷贝配置
|
// 深拷贝配置
|
||||||
profiles[newName] = JSON.parse(JSON.stringify(profiles[sourceName]))
|
profiles[newName] = JSON.parse(JSON.stringify(profiles[sourceName]))
|
||||||
|
|||||||
103
package-lock.json
generated
103
package-lock.json
generated
@@ -1,13 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "iflow-settings-editor",
|
"name": "iflow-settings-editor",
|
||||||
"version": "1.0.0",
|
"version": "1.5.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "iflow-settings-editor",
|
"name": "iflow-settings-editor",
|
||||||
"version": "1.0.0",
|
"version": "1.5.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"vue-i18n": "^9.14.5"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@icon-park/vue-next": "^1.4.2",
|
"@icon-park/vue-next": "^1.4.2",
|
||||||
"@vitejs/plugin-vue": "^6.0.6",
|
"@vitejs/plugin-vue": "^6.0.6",
|
||||||
@@ -22,7 +25,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -32,7 +34,6 @@
|
|||||||
"version": "7.28.5",
|
"version": "7.28.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -42,7 +43,6 @@
|
|||||||
"version": "7.29.2",
|
"version": "7.29.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
|
||||||
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
|
"integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.29.0"
|
"@babel/types": "^7.29.0"
|
||||||
@@ -68,7 +68,6 @@
|
|||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
||||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.27.1",
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
@@ -417,6 +416,50 @@
|
|||||||
"vue": "3.x"
|
"vue": "3.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@intlify/core-base": {
|
||||||
|
"version": "9.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
|
||||||
|
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@intlify/message-compiler": "9.14.5",
|
||||||
|
"@intlify/shared": "9.14.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@intlify/message-compiler": {
|
||||||
|
"version": "9.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
|
||||||
|
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@intlify/shared": "9.14.5",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@intlify/shared": {
|
||||||
|
"version": "9.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
|
||||||
|
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
@@ -524,7 +567,6 @@
|
|||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@malept/cross-spawn-promise": {
|
"node_modules/@malept/cross-spawn-promise": {
|
||||||
@@ -1085,7 +1127,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
|
||||||
"integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==",
|
"integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.2",
|
"@babel/parser": "^7.29.2",
|
||||||
@@ -1099,7 +1140,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz",
|
||||||
"integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==",
|
"integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.5.32",
|
"@vue/compiler-core": "3.5.32",
|
||||||
@@ -1110,7 +1150,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz",
|
||||||
"integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==",
|
"integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.2",
|
"@babel/parser": "^7.29.2",
|
||||||
@@ -1128,18 +1167,22 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz",
|
||||||
"integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==",
|
"integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.32",
|
"@vue/compiler-dom": "3.5.32",
|
||||||
"@vue/shared": "3.5.32"
|
"@vue/shared": "3.5.32"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@vue/reactivity": {
|
"node_modules/@vue/reactivity": {
|
||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.32.tgz",
|
||||||
"integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==",
|
"integrity": "sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.5.32"
|
"@vue/shared": "3.5.32"
|
||||||
@@ -1149,7 +1192,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.32.tgz",
|
||||||
"integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==",
|
"integrity": "sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.32",
|
"@vue/reactivity": "3.5.32",
|
||||||
@@ -1160,7 +1202,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.32.tgz",
|
||||||
"integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==",
|
"integrity": "sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.32",
|
"@vue/reactivity": "3.5.32",
|
||||||
@@ -1173,7 +1214,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.32.tgz",
|
||||||
"integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==",
|
"integrity": "sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.5.32",
|
"@vue/compiler-ssr": "3.5.32",
|
||||||
@@ -1187,7 +1227,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz",
|
||||||
"integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
|
"integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
@@ -2107,7 +2146,6 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/date-fns": {
|
"node_modules/date-fns": {
|
||||||
@@ -2654,7 +2692,6 @@
|
|||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||||
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
@@ -2765,7 +2802,6 @@
|
|||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/extract-zip": {
|
"node_modules/extract-zip": {
|
||||||
@@ -3920,7 +3956,6 @@
|
|||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
@@ -4080,7 +4115,6 @@
|
|||||||
"version": "3.3.11",
|
"version": "3.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -4220,7 +4254,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
@@ -4255,7 +4288,6 @@
|
|||||||
"version": "8.5.9",
|
"version": "8.5.9",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz",
|
||||||
"integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==",
|
"integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -4702,7 +4734,6 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -5009,7 +5040,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -5159,7 +5190,6 @@
|
|||||||
"version": "3.5.32",
|
"version": "3.5.32",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz",
|
||||||
"integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==",
|
"integrity": "sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.32",
|
"@vue/compiler-dom": "3.5.32",
|
||||||
@@ -5177,6 +5207,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-i18n": {
|
||||||
|
"version": "9.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
|
||||||
|
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
|
||||||
|
"deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@intlify/core-base": "9.14.5",
|
||||||
|
"@intlify/shared": "9.14.5",
|
||||||
|
"@vue/devtools-api": "^6.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -91,5 +91,8 @@
|
|||||||
"electron-builder": "^24.13.3",
|
"electron-builder": "^24.13.3",
|
||||||
"vite": "^8.0.8",
|
"vite": "^8.0.8",
|
||||||
"vue": "^3.4.0"
|
"vue": "^3.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue-i18n": "^9.14.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,5 +23,10 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
// 托盘事件监听
|
// 托盘事件监听
|
||||||
onApiProfileSwitched: (callback) => {
|
onApiProfileSwitched: (callback) => {
|
||||||
ipcRenderer.on('api-profile-switched', (event, profileName) => callback(profileName))
|
ipcRenderer.on('api-profile-switched', (event, profileName) => callback(profileName))
|
||||||
|
},
|
||||||
|
|
||||||
|
// 语言切换通知
|
||||||
|
notifyLanguageChanged: () => {
|
||||||
|
ipcRenderer.send('language-changed')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
323
src/App.vue
323
src/App.vue
@@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app">
|
<div class="app" :class="themeClass">
|
||||||
<div class="titlebar">
|
<div class="titlebar">
|
||||||
<div class="titlebar-left">
|
<div class="titlebar-left">
|
||||||
<span class="titlebar-title">iFlow 设置编辑器</span>
|
<span class="titlebar-title">{{ $t('app.title') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="titlebar-controls">
|
<div class="titlebar-controls">
|
||||||
<button class="titlebar-btn" @click="minimize" title="最小化">
|
<button class="titlebar-btn" @click="minimize" :title="$t('window.minimize')">
|
||||||
<svg viewBox="0 0 10 1"><line x1="0" y1="0.5" x2="10" y2="0.5" /></svg>
|
<svg viewBox="0 0 10 1"><line x1="0" y1="0.5" x2="10" y2="0.5" /></svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="titlebar-btn" @click="maximize" title="最大化">
|
<button class="titlebar-btn" @click="maximize" :title="$t('window.maximize')">
|
||||||
<svg viewBox="0 0 10 10"><rect x="0.5" y="0.5" width="9" height="9" stroke-width="1" stroke="currentColor" fill="none" /></svg>
|
<svg viewBox="0 0 10 10"><rect x="0.5" y="0.5" width="9" height="9" stroke-width="1" stroke="currentColor" fill="none" /></svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="titlebar-btn close" @click="close" title="关闭">
|
<button class="titlebar-btn close" @click="close" :title="$t('window.close')">
|
||||||
<svg viewBox="0 0 10 10">
|
<svg viewBox="0 0 10 10">
|
||||||
<line x1="0" y1="0" x2="10" y2="10" />
|
<line x1="0" y1="0" x2="10" y2="10" />
|
||||||
<line x1="10" y1="0" x2="0" y2="10" />
|
<line x1="10" y1="0" x2="0" y2="10" />
|
||||||
@@ -22,21 +22,21 @@
|
|||||||
<main class="main">
|
<main class="main">
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
<div class="sidebar-title">常规</div>
|
<div class="sidebar-title">{{ $t('sidebar.general') }}</div>
|
||||||
<div class="nav-item" :class="{ active: currentSection === 'general' }" @click="showSection('general')">
|
<div class="nav-item" :class="{ active: currentSection === 'general' }" @click="showSection('general')">
|
||||||
<Config size="16" />
|
<Config size="16" />
|
||||||
<span class="nav-item-text">基本设置</span>
|
<span class="nav-item-text">{{ $t('sidebar.basicSettings') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item" :class="{ active: currentSection === 'api' }" @click="showSection('api')">
|
<div class="nav-item" :class="{ active: currentSection === 'api' }" @click="showSection('api')">
|
||||||
<Key size="16" />
|
<Key size="16" />
|
||||||
<span class="nav-item-text">API 配置</span>
|
<span class="nav-item-text">{{ $t('sidebar.apiConfig') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
<div class="sidebar-title">高级</div>
|
<div class="sidebar-title">{{ $t('sidebar.advanced') }}</div>
|
||||||
<div class="nav-item" :class="{ active: currentSection === 'mcp' }" @click="showSection('mcp')">
|
<div class="nav-item" :class="{ active: currentSection === 'mcp' }" @click="showSection('mcp')">
|
||||||
<Server size="16" />
|
<Server size="16" />
|
||||||
<span class="nav-item-text">MCP 服务器</span>
|
<span class="nav-item-text">{{ $t('sidebar.mcpServers') }}</span>
|
||||||
<span class="nav-item-badge">{{ serverCount }}</span>
|
<span class="nav-item-badge">{{ serverCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,30 +44,30 @@
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<section v-if="currentSection === 'general'">
|
<section v-if="currentSection === 'general'">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<h1 class="content-title">基本设置</h1>
|
<h1 class="content-title">{{ $t('general.title') }}</h1>
|
||||||
<p class="content-desc">配置应用程序的常规选项</p>
|
<p class="content-desc">{{ $t('general.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<Globe size="16" />
|
<Globe size="16" />
|
||||||
语言与界面
|
{{ $t('general.languageInterface') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">语言</label>
|
<label class="form-label">{{ $t('general.language') }}</label>
|
||||||
<select class="form-select" v-model="settings.language">
|
<select class="form-select" v-model="settings.language">
|
||||||
<option value="zh-CN">简体中文</option>
|
<option value="zh-CN">{{ $t('languages.zh-CN') }}</option>
|
||||||
<option value="en-US">English</option>
|
<option value="en-US">{{ $t('languages.en-US') }}</option>
|
||||||
<option value="ja-JP">日本語</option>
|
<option value="ja-JP">{{ $t('languages.ja-JP') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">主题</label>
|
<label class="form-label">{{ $t('general.theme') }}</label>
|
||||||
<select class="form-select" v-model="settings.theme">
|
<select class="form-select" v-model="settings.theme">
|
||||||
<option value="Xcode">Xcode</option>
|
<option value="Xcode">{{ $t('theme.xcode') }}</option>
|
||||||
<option value="Dark">深色</option>
|
<option value="Dark">{{ $t('theme.dark') }}</option>
|
||||||
<option value="Light">浅色</option>
|
<option value="Light">{{ $t('theme.light') }}</option>
|
||||||
<option value="Solarized Dark">Solarized Dark</option>
|
<option value="Solarized Dark">{{ $t('theme.solarizedDark') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -75,21 +75,21 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<Setting size="16" />
|
<Setting size="16" />
|
||||||
其他设置
|
{{ $t('general.otherSettings') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">启动动画</label>
|
<label class="form-label">{{ $t('general.bootAnimation') }}</label>
|
||||||
<select class="form-select" v-model="settings.bootAnimationShown">
|
<select class="form-select" v-model="settings.bootAnimationShown">
|
||||||
<option :value="true">已显示</option>
|
<option :value="true">{{ $t('general.bootAnimationShown') }}</option>
|
||||||
<option :value="false">未显示</option>
|
<option :value="false">{{ $t('general.bootAnimationNotShown') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">检查点保存</label>
|
<label class="form-label">{{ $t('general.checkpointing') }}</label>
|
||||||
<select class="form-select" v-model="settings.checkpointing.enabled">
|
<select class="form-select" v-model="settings.checkpointing.enabled">
|
||||||
<option :value="true">已启用</option>
|
<option :value="true">{{ $t('general.enabled') }}</option>
|
||||||
<option :value="false">已禁用</option>
|
<option :value="false">{{ $t('general.disabled') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,16 +97,16 @@
|
|||||||
</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">{{ $t('api.title') }}</h1>
|
||||||
<p class="content-desc">配置 AI 服务和搜索 API</p>
|
<p class="content-desc">{{ $t('api.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<Exchange size="16" />
|
<Exchange size="16" />
|
||||||
配置文件管理
|
{{ $t('api.profileManagement') }}
|
||||||
<button class="btn btn-primary btn-sm" @click="createNewApiProfile" style="margin-left: auto">
|
<button class="btn btn-primary btn-sm" @click="createNewApiProfile" style="margin-left: auto">
|
||||||
<Add size="14" />
|
<Add size="14" />
|
||||||
新建配置
|
{{ $t('api.newProfile') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-list">
|
<div class="profile-list">
|
||||||
@@ -123,17 +123,17 @@
|
|||||||
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<polyline points="3,8 6,11 13,4"></polyline>
|
<polyline points="3,8 6,11 13,4"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
使用中
|
{{ $t('api.inUse') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-actions">
|
<div class="profile-actions">
|
||||||
<button class="action-btn" @click.stop="openApiEditDialog(profile.name)" title="编辑">
|
<button class="action-btn" @click.stop="openApiEditDialog(profile.name)" :title="$t('api.edit')">
|
||||||
<Edit size="14" />
|
<Edit size="14" />
|
||||||
</button>
|
</button>
|
||||||
<button class="action-btn" @click.stop="duplicateApiProfile(profile.name)" title="复制">
|
<button class="action-btn" @click.stop="duplicateApiProfile(profile.name)" :title="$t('api.duplicate')">
|
||||||
<Copy size="14" />
|
<Copy size="14" />
|
||||||
</button>
|
</button>
|
||||||
<button class="action-btn action-btn-danger" v-if="profile.name !== 'default'" @click.stop="deleteApiProfile(profile.name)" title="删除">
|
<button class="action-btn action-btn-danger" v-if="profile.name !== 'default'" @click.stop="deleteApiProfile(profile.name)" :title="$t('api.delete')">
|
||||||
<Delete size="14" />
|
<Delete size="14" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -143,15 +143,15 @@
|
|||||||
</section>
|
</section>
|
||||||
<section v-if="currentSection === 'mcp'">
|
<section v-if="currentSection === 'mcp'">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<h1 class="content-title">MCP 服务器</h1>
|
<h1 class="content-title">{{ $t('mcp.title') }}</h1>
|
||||||
<p class="content-desc">管理 Model Context Protocol 服务器配置</p>
|
<p class="content-desc">{{ $t('mcp.description') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px">
|
||||||
<label class="form-label" style="margin: 0">服务器列表</label>
|
<label class="form-label" style="margin: 0">{{ $t('mcp.serverList') }}</label>
|
||||||
<button class="btn btn-primary" @click="addServer" style="padding: 6px 12px; font-size: 12px">
|
<button class="btn btn-primary" @click="addServer" style="padding: 6px 12px; font-size: 12px">
|
||||||
<Add size="12" />
|
<Add size="12" />
|
||||||
添加服务器
|
{{ $t('mcp.addServerBtn') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="server-list">
|
<div class="server-list">
|
||||||
@@ -159,15 +159,15 @@
|
|||||||
<div v-for="(config, name) in settings.mcpServers" :key="name" class="server-item" :class="{ selected: currentServerName === name }" @click="selectServer(name)">
|
<div v-for="(config, name) in settings.mcpServers" :key="name" class="server-item" :class="{ selected: currentServerName === name }" @click="selectServer(name)">
|
||||||
<div class="server-info">
|
<div class="server-info">
|
||||||
<div class="server-name">{{ name }}</div>
|
<div class="server-name">{{ name }}</div>
|
||||||
<div class="server-desc">{{ config.description || '无描述' }}</div>
|
<div class="server-desc">{{ config.description || $t('mcp.noDescription') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="server-status"></div>
|
<div class="server-status"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-else class="empty-state">
|
<div v-else class="empty-state">
|
||||||
<Server size="48" class="empty-state-icon" />
|
<Server size="48" class="empty-state-icon" />
|
||||||
<div class="empty-state-title">暂无 MCP 服务器</div>
|
<div class="empty-state-title">{{ $t('mcp.noServers') }}</div>
|
||||||
<div class="empty-state-desc">点击上方按钮添加第一个服务器</div>
|
<div class="empty-state-desc">{{ $t('mcp.addFirstServer') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
<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>{{ $t('api.currentConfig') }}: {{ currentApiProfile || 'default' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<!-- Input Dialog -->
|
<!-- Input Dialog -->
|
||||||
@@ -187,8 +187,8 @@
|
|||||||
<div v-if="showInputDialog.isConfirm" class="dialog-confirm-text">{{ showInputDialog.placeholder }}</div>
|
<div v-if="showInputDialog.isConfirm" class="dialog-confirm-text">{{ showInputDialog.placeholder }}</div>
|
||||||
<input v-else type="text" class="form-input" v-model="inputDialogValue" :placeholder="showInputDialog.placeholder" @keyup.enter="closeInputDialog(true)" autofocus />
|
<input v-else type="text" class="form-input" v-model="inputDialogValue" :placeholder="showInputDialog.placeholder" @keyup.enter="closeInputDialog(true)" autofocus />
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button class="btn btn-secondary" @click="closeInputDialog(false)">取消</button>
|
<button class="btn btn-secondary" @click="closeInputDialog(false)">{{ $t('dialog.cancel') }}</button>
|
||||||
<button class="btn btn-primary" @click="closeInputDialog(true)">确定</button>
|
<button class="btn btn-primary" @click="closeInputDialog(true)">{{ $t('dialog.confirm') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -197,8 +197,8 @@
|
|||||||
<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">
|
||||||
<Add size="18" />
|
<Key size="18" />
|
||||||
新建 API 配置
|
{{ $t('api.createTitle') }}
|
||||||
</div>
|
</div>
|
||||||
<button class="side-panel-close" @click="closeApiCreateDialog">
|
<button class="side-panel-close" @click="closeApiCreateDialog">
|
||||||
<svg viewBox="0 0 10 10">
|
<svg viewBox="0 0 10 10">
|
||||||
@@ -209,43 +209,43 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">配置名称 <span class="form-required">*</span></label>
|
<label class="form-label">{{ $t('api.configName') }} <span class="form-required">*</span></label>
|
||||||
<input type="text" class="form-input" v-model="creatingApiData.name" placeholder="请输入配置名称" />
|
<input type="text" class="form-input" v-model="creatingApiData.name" :placeholder="$t('api.configNamePlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">认证方式</label>
|
<label class="form-label">{{ $t('api.authType') }}</label>
|
||||||
<select class="form-select" v-model="creatingApiData.selectedAuthType">
|
<select class="form-select" v-model="creatingApiData.selectedAuthType">
|
||||||
<option value="iflow">iFlow</option>
|
<option value="iflow">{{ $t('api.auth.iflow') }}</option>
|
||||||
<option value="api">API Key</option>
|
<option value="api">{{ $t('api.auth.api') }}</option>
|
||||||
<option value="openai-compatible">OpenAI 兼容</option>
|
<option value="openai-compatible">{{ $t('api.auth.openaiCompatible') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">API Key</label>
|
<label class="form-label">{{ $t('api.apiKey') }}</label>
|
||||||
<input type="password" class="form-input" v-model="creatingApiData.apiKey" placeholder="sk-cp-XXXXX..." />
|
<input type="password" class="form-input" v-model="creatingApiData.apiKey" :placeholder="$t('api.apiKeyPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Base URL</label>
|
<label class="form-label">{{ $t('api.baseUrl') }}</label>
|
||||||
<input type="text" class="form-input" v-model="creatingApiData.baseUrl" placeholder="https://api.minimaxi.com/v1" />
|
<input type="text" class="form-input" v-model="creatingApiData.baseUrl" :placeholder="$t('api.baseUrlPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">模型名称</label>
|
<label class="form-label">{{ $t('api.modelName') }}</label>
|
||||||
<input type="text" class="form-input" v-model="creatingApiData.modelName" placeholder="MiniMax-M2.7" />
|
<input type="text" class="form-input" v-model="creatingApiData.modelName" :placeholder="$t('api.modelNamePlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">搜索 API Key</label>
|
<label class="form-label">{{ $t('api.searchApiKey') }}</label>
|
||||||
<input type="password" class="form-input" v-model="creatingApiData.searchApiKey" placeholder="sk-XXXXX..." />
|
<input type="password" class="form-input" v-model="creatingApiData.searchApiKey" :placeholder="$t('api.searchApiKeyPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">CNA</label>
|
<label class="form-label">{{ $t('api.cna') }}</label>
|
||||||
<input type="text" class="form-input" v-model="creatingApiData.cna" placeholder="CNA 标识" />
|
<input type="text" class="form-input" v-model="creatingApiData.cna" :placeholder="$t('api.cnaPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button class="btn btn-secondary" @click="closeApiCreateDialog">取消</button>
|
<button class="btn btn-secondary" @click="closeApiCreateDialog">{{ $t('dialog.cancel') }}</button>
|
||||||
<button class="btn btn-primary" @click="saveApiCreate"> <Save size="14" /> 创建 </button>
|
<button class="btn btn-primary" @click="saveApiCreate"> <Save size="14" /> {{ $t('api.create') }} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
<div class="dialog-title">
|
<div class="dialog-title">
|
||||||
<Key size="18" />
|
<Key size="18" />
|
||||||
编辑 API 配置
|
{{ $t('api.editTitle') }}
|
||||||
</div>
|
</div>
|
||||||
<button class="side-panel-close" @click="closeApiEditDialog">
|
<button class="side-panel-close" @click="closeApiEditDialog">
|
||||||
<svg viewBox="0 0 10 10">
|
<svg viewBox="0 0 10 10">
|
||||||
@@ -266,39 +266,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">认证方式</label>
|
<label class="form-label">{{ $t('api.authType') }}</label>
|
||||||
<select class="form-select" v-model="editingApiData.selectedAuthType">
|
<select class="form-select" v-model="editingApiData.selectedAuthType">
|
||||||
<option value="iflow">iFlow</option>
|
<option value="iflow">{{ $t('api.auth.iflow') }}</option>
|
||||||
<option value="api">API Key</option>
|
<option value="api">{{ $t('api.auth.api') }}</option>
|
||||||
<option value="openai-compatible">OpenAI 兼容</option>
|
<option value="openai-compatible">{{ $t('api.auth.openaiCompatible') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">API Key</label>
|
<label class="form-label">{{ $t('api.apiKey') }}</label>
|
||||||
<input type="password" class="form-input" v-model="editingApiData.apiKey" placeholder="sk-cp-XXXXX..." />
|
<input type="password" class="form-input" v-model="editingApiData.apiKey" :placeholder="$t('api.apiKeyPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Base URL</label>
|
<label class="form-label">{{ $t('api.baseUrl') }}</label>
|
||||||
<input type="text" class="form-input" v-model="editingApiData.baseUrl" placeholder="https://api.minimaxi.com/v1" />
|
<input type="text" class="form-input" v-model="editingApiData.baseUrl" :placeholder="$t('api.baseUrlPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">模型名称</label>
|
<label class="form-label">{{ $t('api.modelName') }}</label>
|
||||||
<input type="text" class="form-input" v-model="editingApiData.modelName" placeholder="MiniMax-M2.7" />
|
<input type="text" class="form-input" v-model="editingApiData.modelName" :placeholder="$t('api.modelNamePlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">搜索 API Key</label>
|
<label class="form-label">{{ $t('api.searchApiKey') }}</label>
|
||||||
<input type="password" class="form-input" v-model="editingApiData.searchApiKey" placeholder="sk-XXXXX..." />
|
<input type="password" class="form-input" v-model="editingApiData.searchApiKey" :placeholder="$t('api.searchApiKeyPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">CNA</label>
|
<label class="form-label">{{ $t('api.cna') }}</label>
|
||||||
<input type="text" class="form-input" v-model="editingApiData.cna" placeholder="CNA 标识" />
|
<input type="text" class="form-input" v-model="editingApiData.cna" :placeholder="$t('api.cnaPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button class="btn btn-secondary" @click="closeApiEditDialog">取消</button>
|
<button class="btn btn-secondary" @click="closeApiEditDialog">{{ $t('dialog.cancel') }}</button>
|
||||||
<button class="btn btn-primary" @click="saveApiEdit"> <Save size="14" /> 保存 </button>
|
<button class="btn btn-primary" @click="saveApiEdit"> <Save size="14" /> {{ $t('api.save') }} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -308,7 +308,7 @@
|
|||||||
<div class="side-panel-header">
|
<div class="side-panel-header">
|
||||||
<div class="side-panel-title">
|
<div class="side-panel-title">
|
||||||
<Server size="18" />
|
<Server size="18" />
|
||||||
{{ isEditingServer ? '编辑服务器' : '添加服务器' }}
|
{{ isEditingServer ? $t('mcp.editServer') : $t('mcp.addServer') }}
|
||||||
</div>
|
</div>
|
||||||
<button class="side-panel-close" @click="closeServerPanel">
|
<button class="side-panel-close" @click="closeServerPanel">
|
||||||
<svg viewBox="0 0 10 10">
|
<svg viewBox="0 0 10 10">
|
||||||
@@ -319,40 +319,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="side-panel-body">
|
<div class="side-panel-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">服务器名称 <span class="form-required">*</span></label>
|
<label class="form-label">{{ $t('mcp.serverName') }} <span class="form-required">*</span></label>
|
||||||
<input type="text" class="form-input" v-model="editingServerData.name" placeholder="my-mcp-server" />
|
<input type="text" class="form-input" v-model="editingServerData.name" :placeholder="$t('mcp.serverNamePlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">描述</label>
|
<label class="form-label">{{ $t('mcp.descriptionLabel') }}</label>
|
||||||
<input type="text" class="form-input" v-model="editingServerData.description" placeholder="服务器描述信息" />
|
<input type="text" class="form-input" v-model="editingServerData.description" :placeholder="$t('mcp.descriptionPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">命令 <span class="form-required">*</span></label>
|
<label class="form-label">{{ $t('mcp.command') }} <span class="form-required">*</span></label>
|
||||||
<input type="text" class="form-input" v-model="editingServerData.command" placeholder="npx" />
|
<input type="text" class="form-input" v-model="editingServerData.command" :placeholder="$t('mcp.commandPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">工作目录</label>
|
<label class="form-label">{{ $t('mcp.workingDir') }}</label>
|
||||||
<input type="text" class="form-input" v-model="editingServerData.cwd" placeholder="." />
|
<input type="text" class="form-input" v-model="editingServerData.cwd" :placeholder="$t('mcp.cwdPlaceholder')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">参数 (每行一个)</label>
|
<label class="form-label">{{ $t('mcp.args') }}</label>
|
||||||
<textarea class="form-textarea" v-model="editingServerData.args" rows="4" placeholder="-y package-name"></textarea>
|
<textarea class="form-textarea" v-model="editingServerData.args" rows="4" :placeholder="$t('mcp.argsPlaceholder')"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">环境变量 (JSON 格式)</label>
|
<label class="form-label">{{ $t('mcp.envVars') }}</label>
|
||||||
<textarea class="form-textarea" v-model="editingServerData.env" rows="3" placeholder='{"API_KEY": "xxx"}'></textarea>
|
<textarea class="form-textarea" v-model="editingServerData.env" rows="3" :placeholder="$t('mcp.envVarsPlaceholder')"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="side-panel-footer">
|
<div class="side-panel-footer">
|
||||||
<button v-if="isEditingServer" class="btn btn-danger" @click="deleteServer">
|
<button v-if="isEditingServer" class="btn btn-danger" @click="deleteServer">
|
||||||
<Delete size="14" />
|
<Delete size="14" />
|
||||||
删除
|
{{ $t('mcp.delete') }}
|
||||||
</button>
|
</button>
|
||||||
<div class="side-panel-footer-right">
|
<div class="side-panel-footer-right">
|
||||||
<button class="btn btn-secondary" @click="closeServerPanel">取消</button>
|
<button class="btn btn-secondary" @click="closeServerPanel">{{ $t('dialog.cancel') }}</button>
|
||||||
<button class="btn btn-primary" @click="saveServerFromPanel">
|
<button class="btn btn-primary" @click="saveServerFromPanel">
|
||||||
<Save size="14" />
|
<Save size="14" />
|
||||||
{{ isEditingServer ? '保存更改' : '添加服务器' }}
|
{{ isEditingServer ? $t('mcp.saveChanges') : $t('mcp.addServer') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -384,7 +384,7 @@
|
|||||||
<div class="message-dialog-title">{{ showMessageDialog.title }}</div>
|
<div class="message-dialog-title">{{ showMessageDialog.title }}</div>
|
||||||
<div class="message-dialog-message">{{ showMessageDialog.message }}</div>
|
<div class="message-dialog-message">{{ showMessageDialog.message }}</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button class="btn btn-primary" @click="closeMessageDialog">确定</button>
|
<button class="btn btn-primary" @click="closeMessageDialog">{{ $t('dialog.confirm') }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -392,7 +392,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { Save, Config, Key, Server, Globe, Setting, 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 { locale, t } = useI18n()
|
||||||
const settings = ref({
|
const settings = ref({
|
||||||
language: 'zh-CN',
|
language: 'zh-CN',
|
||||||
theme: 'Xcode',
|
theme: 'Xcode',
|
||||||
@@ -472,7 +474,7 @@ const switchApiProfile = async () => {
|
|||||||
originalSettings.value = JSON.parse(JSON.stringify(data))
|
originalSettings.value = JSON.parse(JSON.stringify(data))
|
||||||
modified.value = false
|
modified.value = false
|
||||||
} else {
|
} else {
|
||||||
await showMessage({ type: 'error', title: '切换失败', message: result.error })
|
await showMessage({ type: 'error', title: t('api.switchFailed'), message: result.error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create new API profile
|
// Create new API profile
|
||||||
@@ -496,7 +498,7 @@ const closeApiCreateDialog = () => {
|
|||||||
const saveApiCreate = async () => {
|
const saveApiCreate = async () => {
|
||||||
const name = creatingApiData.value.name.trim()
|
const name = creatingApiData.value.name.trim()
|
||||||
if (!name) {
|
if (!name) {
|
||||||
await showMessage({ type: 'warning', title: '错误', message: '请输入配置名称' })
|
await showMessage({ type: 'warning', title: t('messages.error'), message: t('messages.inputConfigName') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const result = await window.electronAPI.createApiProfile(name)
|
const result = await window.electronAPI.createApiProfile(name)
|
||||||
@@ -519,24 +521,24 @@ const saveApiCreate = async () => {
|
|||||||
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: t('messages.success'), message: t('api.configCreated', { name }) })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await showMessage({ type: 'error', title: '创建失败', message: result.error })
|
await showMessage({ type: 'error', title: t('messages.error'), 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
|
||||||
if (profileName === 'default') {
|
if (profileName === 'default') {
|
||||||
await showMessage({ type: 'warning', title: '无法删除', message: '不能删除默认配置' })
|
await showMessage({ type: 'warning', title: t('messages.warning'), message: t('messages.cannotDeleteDefault') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const confirmed = await new Promise(resolve => {
|
const confirmed = await new Promise(resolve => {
|
||||||
showInputDialog.value = {
|
showInputDialog.value = {
|
||||||
show: true,
|
show: true,
|
||||||
title: '删除配置',
|
title: t('api.delete'),
|
||||||
placeholder: `确定要删除配置 "${profileName}" 吗?`,
|
placeholder: t('messages.confirmDeleteConfig', { name: profileName }),
|
||||||
callback: resolve,
|
callback: resolve,
|
||||||
isConfirm: true,
|
isConfirm: true,
|
||||||
}
|
}
|
||||||
@@ -551,9 +553,9 @@ const deleteApiProfile = async name => {
|
|||||||
originalSettings.value = JSON.parse(JSON.stringify(data))
|
originalSettings.value = JSON.parse(JSON.stringify(data))
|
||||||
modified.value = false
|
modified.value = false
|
||||||
await loadApiProfiles()
|
await loadApiProfiles()
|
||||||
await showMessage({ type: 'info', title: '删除成功', message: `配置已删除` })
|
await showMessage({ type: 'info', title: t('messages.success'), message: t('api.configDeleted') })
|
||||||
} else {
|
} else {
|
||||||
await showMessage({ type: 'error', title: '删除失败', message: result.error })
|
await showMessage({ type: 'error', title: t('messages.error'), message: result.error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,10 +575,10 @@ const getProfileInitial = name => {
|
|||||||
// 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]) {
|
||||||
return '未配置'
|
return t('api.unconfigured')
|
||||||
}
|
}
|
||||||
const profile = settings.value.apiProfiles[name]
|
const profile = settings.value.apiProfiles[name]
|
||||||
return profile.baseUrl || '未配置 Base URL'
|
return profile.baseUrl || t('api.noBaseUrl')
|
||||||
}
|
}
|
||||||
// Get profile icon style (gradient colors)
|
// Get profile icon style (gradient colors)
|
||||||
const profileColors = [
|
const profileColors = [
|
||||||
@@ -603,8 +605,8 @@ 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: t('api.duplicate'),
|
||||||
placeholder: '请输入新配置的名称',
|
placeholder: t('api.newConfigNamePlaceholder'),
|
||||||
callback: resolve,
|
callback: resolve,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -612,9 +614,9 @@ const duplicateApiProfile = async name => {
|
|||||||
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: t('messages.success'), message: t('api.configCopied', { name: newName }) })
|
||||||
} else {
|
} else {
|
||||||
await showMessage({ type: 'error', title: '复制失败', message: result.error })
|
await showMessage({ type: 'error', title: t('messages.error'), message: result.error })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Open API edit dialog
|
// Open API edit dialog
|
||||||
@@ -690,17 +692,33 @@ const loadSettings = async () => {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
settings,
|
settings,
|
||||||
() => {
|
async () => {
|
||||||
if (!isLoading.value) {
|
if (!isLoading.value) {
|
||||||
modified.value = true
|
modified.value = true
|
||||||
|
// 自动保存基础设置到文件
|
||||||
|
const dataToSave = JSON.parse(JSON.stringify(settings.value))
|
||||||
|
await window.electronAPI.saveSettings(dataToSave)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
)
|
)
|
||||||
|
watch(
|
||||||
|
() => settings.value.language,
|
||||||
|
newLang => {
|
||||||
|
locale.value = newLang
|
||||||
|
window.electronAPI.notifyLanguageChanged()
|
||||||
|
},
|
||||||
|
)
|
||||||
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 themeClass = computed(() => {
|
||||||
|
const theme = settings.value.theme
|
||||||
|
if (theme === 'Dark') return 'dark'
|
||||||
|
if (theme === 'Solarized Dark') return 'solarized-dark'
|
||||||
|
return ''
|
||||||
|
})
|
||||||
const selectServer = name => {
|
const selectServer = name => {
|
||||||
currentServerName.value = name
|
currentServerName.value = name
|
||||||
openEditServerPanel(name)
|
openEditServerPanel(name)
|
||||||
@@ -744,11 +762,11 @@ const closeServerPanel = () => {
|
|||||||
const saveServerFromPanel = async () => {
|
const saveServerFromPanel = async () => {
|
||||||
const name = editingServerData.value.name.trim()
|
const name = editingServerData.value.name.trim()
|
||||||
if (!name) {
|
if (!name) {
|
||||||
await showMessage({ type: 'warning', title: '错误', message: '请输入服务器名称' })
|
await showMessage({ type: 'warning', title: t('messages.error'), message: t('mcp.inputServerName') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!isEditingServer.value && settings.value.mcpServers[name]) {
|
if (!isEditingServer.value && settings.value.mcpServers[name]) {
|
||||||
await showMessage({ type: 'warning', title: '错误', message: '服务器名称已存在' })
|
await showMessage({ type: 'warning', title: t('messages.error'), message: t('mcp.serverNameExists') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 如果是编辑模式且名称改变了,需要删除旧的服务器
|
// 如果是编辑模式且名称改变了,需要删除旧的服务器
|
||||||
@@ -769,7 +787,7 @@ const saveServerFromPanel = async () => {
|
|||||||
try {
|
try {
|
||||||
serverConfig.env = JSON.parse(envText)
|
serverConfig.env = JSON.parse(envText)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showMessage({ type: 'error', title: '错误', message: '环境变量 JSON 格式错误' })
|
await showMessage({ type: 'error', title: t('messages.error'), message: t('mcp.invalidEnvJson') })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -791,7 +809,7 @@ 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
|
||||||
const confirmed = await new Promise(resolve => {
|
const confirmed = await new Promise(resolve => {
|
||||||
showInputDialog.value = { show: true, title: '删除服务器', placeholder: `确定要删除服务器 "${serverName}" 吗?`, callback: resolve, isConfirm: true }
|
showInputDialog.value = { show: true, title: t('mcp.delete'), placeholder: t('messages.confirmDeleteServer', { name: serverName }), callback: resolve, isConfirm: true }
|
||||||
})
|
})
|
||||||
if (!confirmed) return
|
if (!confirmed) return
|
||||||
delete settings.value.mcpServers[serverName]
|
delete settings.value.mcpServers[serverName]
|
||||||
@@ -848,11 +866,32 @@ watch(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
// Apply theme class to body when theme changes
|
||||||
|
watch(
|
||||||
|
() => settings.value.theme,
|
||||||
|
theme => {
|
||||||
|
const cls = themeClass.value
|
||||||
|
if (cls) {
|
||||||
|
document.body.classList.add(cls)
|
||||||
|
if (cls === 'dark') document.body.classList.remove('solarized-dark')
|
||||||
|
else if (cls === 'solarized-dark') document.body.classList.remove('dark')
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove('dark', 'solarized-dark')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadApiProfiles()
|
await loadApiProfiles()
|
||||||
await loadSettings()
|
await loadSettings()
|
||||||
|
// 同步初始语言到 i18n
|
||||||
|
locale.value = settings.value.language
|
||||||
|
// 应用初始主题
|
||||||
|
const cls = themeClass.value
|
||||||
|
if (cls) {
|
||||||
|
document.body.classList.add(cls)
|
||||||
|
}
|
||||||
// 监听托盘切换 API 配置事件
|
// 监听托盘切换 API 配置事件
|
||||||
window.electronAPI.onApiProfileSwitched(async (profileName) => {
|
window.electronAPI.onApiProfileSwitched(async profileName => {
|
||||||
currentApiProfile.value = profileName
|
currentApiProfile.value = profileName
|
||||||
await loadSettings()
|
await loadSettings()
|
||||||
})
|
})
|
||||||
@@ -937,6 +976,46 @@ body {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
/* Dark Theme Variables */
|
||||||
|
.dark,
|
||||||
|
.solarized-dark {
|
||||||
|
--bg-primary: #1a1a2e;
|
||||||
|
--bg-secondary: #16213e;
|
||||||
|
--bg-tertiary: #0f3460;
|
||||||
|
--bg-hover: #1f4068;
|
||||||
|
--text-primary: #e4e4e7;
|
||||||
|
--text-secondary: #a1a1aa;
|
||||||
|
--text-tertiary: #71717a;
|
||||||
|
--accent: #60a5fa;
|
||||||
|
--accent-hover: #3b82f6;
|
||||||
|
--accent-light: rgba(96, 165, 250, 0.15);
|
||||||
|
--border: #2d2d44;
|
||||||
|
--border-light: #232338;
|
||||||
|
--success: #34d399;
|
||||||
|
--danger: #f87171;
|
||||||
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Solarized Dark specific overrides */
|
||||||
|
.solarized-dark {
|
||||||
|
--bg-primary: #002b36;
|
||||||
|
--bg-secondary: #073642;
|
||||||
|
--bg-tertiary: #094856;
|
||||||
|
--bg-hover: #0a5a6f;
|
||||||
|
--text-primary: #839496;
|
||||||
|
--text-secondary: #93a1a1;
|
||||||
|
--text-tertiary: #586e75;
|
||||||
|
--accent: #268bd2;
|
||||||
|
--accent-hover: #1a73c0;
|
||||||
|
--accent-light: rgba(38, 139, 210, 0.15);
|
||||||
|
--border: #1d3a47;
|
||||||
|
--border-light: #0d3a47;
|
||||||
|
--success: #2aa198;
|
||||||
|
--danger: #dc322f;
|
||||||
|
}
|
||||||
|
|
||||||
/* Scrollbar Styles */
|
/* Scrollbar Styles */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
|
|||||||
130
src/locales/en-US.js
Normal file
130
src/locales/en-US.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
export default {
|
||||||
|
app: {
|
||||||
|
title: 'iFlow Settings Editor'
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
minimize: 'Minimize',
|
||||||
|
maximize: 'Maximize',
|
||||||
|
close: 'Close'
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
general: 'General',
|
||||||
|
basicSettings: 'Basic Settings',
|
||||||
|
apiConfig: 'API Config',
|
||||||
|
advanced: 'Advanced',
|
||||||
|
mcpServers: 'MCP Servers'
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
title: 'Basic Settings',
|
||||||
|
description: 'Configure general application options',
|
||||||
|
language: 'Language',
|
||||||
|
theme: 'Theme',
|
||||||
|
languageInterface: 'Language & Interface',
|
||||||
|
otherSettings: 'Other Settings',
|
||||||
|
bootAnimation: 'Boot Animation',
|
||||||
|
bootAnimationShown: 'Shown',
|
||||||
|
bootAnimationNotShown: 'Not Shown',
|
||||||
|
checkpointing: 'Checkpointing',
|
||||||
|
enabled: 'Enabled',
|
||||||
|
disabled: 'Disabled'
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
xcode: 'Xcode',
|
||||||
|
dark: 'Dark',
|
||||||
|
light: 'Light',
|
||||||
|
solarizedDark: 'Solarized Dark'
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
title: 'API Configuration',
|
||||||
|
description: 'Configure AI services and search API',
|
||||||
|
currentConfig: 'Current Config',
|
||||||
|
createTitle: 'Create API Configuration',
|
||||||
|
editTitle: 'Edit API Configuration',
|
||||||
|
profileManagement: 'Profile Management',
|
||||||
|
newProfile: 'New Profile',
|
||||||
|
profileName: 'Profile Name',
|
||||||
|
configName: 'Profile Name',
|
||||||
|
configNamePlaceholder: 'Enter configuration name',
|
||||||
|
newConfigNamePlaceholder: 'Enter new configuration name',
|
||||||
|
authType: 'Auth Type',
|
||||||
|
apiKey: 'API Key',
|
||||||
|
apiKeyPlaceholder: 'sk-cp-XXXXX...',
|
||||||
|
baseUrl: 'Base URL',
|
||||||
|
baseUrlPlaceholder: 'https://api.minimaxi.com/v1',
|
||||||
|
modelName: 'Model Name',
|
||||||
|
modelNamePlaceholder: 'MiniMax-M2.7',
|
||||||
|
searchApiKey: 'Search API Key',
|
||||||
|
searchApiKeyPlaceholder: 'sk-XXXXX...',
|
||||||
|
cna: 'CNA',
|
||||||
|
cnaPlaceholder: 'CNA identifier',
|
||||||
|
inUse: 'In Use',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
create: 'Create',
|
||||||
|
save: 'Save',
|
||||||
|
edit: 'Edit',
|
||||||
|
duplicate: 'Duplicate',
|
||||||
|
delete: 'Delete',
|
||||||
|
unconfigured: 'Not configured',
|
||||||
|
noBaseUrl: 'Base URL not configured',
|
||||||
|
configCreated: 'Configuration "{name}" created',
|
||||||
|
configDeleted: 'Configuration deleted',
|
||||||
|
configCopied: 'Configuration copied as "{name}"',
|
||||||
|
switchFailed: 'Switch failed',
|
||||||
|
auth: {
|
||||||
|
iflow: 'iFlow',
|
||||||
|
api: 'API Key',
|
||||||
|
openaiCompatible: 'OpenAI Compatible'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mcp: {
|
||||||
|
title: 'MCP Servers',
|
||||||
|
description: 'Manage Model Context Protocol server configurations',
|
||||||
|
serverList: 'Server List',
|
||||||
|
addServer: 'Add Server',
|
||||||
|
editServer: 'Edit Server',
|
||||||
|
serverName: 'Server Name',
|
||||||
|
serverNamePlaceholder: 'my-mcp-server',
|
||||||
|
descriptionLabel: 'Description',
|
||||||
|
descriptionPlaceholder: 'Server description',
|
||||||
|
command: 'Command',
|
||||||
|
commandPlaceholder: 'npx',
|
||||||
|
workingDir: 'Working Directory',
|
||||||
|
cwdPlaceholder: '.',
|
||||||
|
args: 'Arguments (one per line)',
|
||||||
|
argsPlaceholder: '-y\\npackage-name',
|
||||||
|
envVars: 'Environment Variables (JSON)',
|
||||||
|
envVarsPlaceholder: "e.g. API_KEY=xxx",
|
||||||
|
invalidEnvJson: 'Invalid environment variables JSON format',
|
||||||
|
noServers: 'No MCP Servers',
|
||||||
|
addFirstServer: 'Click the button above to add your first server',
|
||||||
|
noDescription: 'No description',
|
||||||
|
delete: 'Delete',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
saveChanges: 'Save Changes',
|
||||||
|
addServerBtn: 'Add Server',
|
||||||
|
inputServerName: 'Please enter server name',
|
||||||
|
serverNameExists: 'Server name already exists'
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
error: 'Error',
|
||||||
|
warning: 'Warning',
|
||||||
|
success: 'Success',
|
||||||
|
info: 'Info',
|
||||||
|
cannotDeleteDefault: 'Cannot delete default configuration',
|
||||||
|
inputConfigName: 'Please enter configuration name',
|
||||||
|
confirmDeleteConfig: 'Are you sure you want to delete configuration "{name}"?',
|
||||||
|
confirmDeleteServer: 'Are you sure you want to delete server "{name}"?'
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
confirm: 'Confirm',
|
||||||
|
cancel: 'Cancel'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
config: 'Config'
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
|
'zh-CN': '简体中文',
|
||||||
|
'en-US': 'English',
|
||||||
|
'ja-JP': '日本語'
|
||||||
|
}
|
||||||
|
}
|
||||||
130
src/locales/index.js
Normal file
130
src/locales/index.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
export default {
|
||||||
|
app: {
|
||||||
|
title: 'iFlow 设置编辑器'
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
minimize: '最小化',
|
||||||
|
maximize: '最大化',
|
||||||
|
close: '关闭'
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
general: '常规',
|
||||||
|
basicSettings: '基本设置',
|
||||||
|
apiConfig: 'API 配置',
|
||||||
|
advanced: '高级',
|
||||||
|
mcpServers: 'MCP 服务器'
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
title: '基本设置',
|
||||||
|
description: '配置应用程序的常规选项',
|
||||||
|
language: '语言',
|
||||||
|
theme: '主题',
|
||||||
|
languageInterface: '语言与界面',
|
||||||
|
otherSettings: '其他设置',
|
||||||
|
bootAnimation: '启动动画',
|
||||||
|
bootAnimationShown: '已显示',
|
||||||
|
bootAnimationNotShown: '未显示',
|
||||||
|
checkpointing: '检查点保存',
|
||||||
|
enabled: '已启用',
|
||||||
|
disabled: '已禁用'
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
xcode: 'Xcode',
|
||||||
|
dark: '深色',
|
||||||
|
light: '浅色',
|
||||||
|
solarizedDark: 'Solarized Dark'
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
title: 'API 配置',
|
||||||
|
description: '配置 AI 服务和搜索 API',
|
||||||
|
currentConfig: '当前配置',
|
||||||
|
createTitle: '新建 API 配置',
|
||||||
|
editTitle: '编辑 API 配置',
|
||||||
|
profileManagement: '配置文件管理',
|
||||||
|
newProfile: '新建配置',
|
||||||
|
profileName: '配置名称',
|
||||||
|
configName: '配置名称',
|
||||||
|
configNamePlaceholder: '请输入配置名称',
|
||||||
|
newConfigNamePlaceholder: '请输入新配置的名称',
|
||||||
|
authType: '认证方式',
|
||||||
|
apiKey: 'API Key',
|
||||||
|
apiKeyPlaceholder: 'sk-cp-XXXXX...',
|
||||||
|
baseUrl: 'Base URL',
|
||||||
|
baseUrlPlaceholder: 'https://api.minimaxi.com/v1',
|
||||||
|
modelName: '模型名称',
|
||||||
|
modelNamePlaceholder: 'MiniMax-M2.7',
|
||||||
|
searchApiKey: '搜索 API Key',
|
||||||
|
searchApiKeyPlaceholder: 'sk-XXXXX...',
|
||||||
|
cna: 'CNA',
|
||||||
|
cnaPlaceholder: 'CNA 标识',
|
||||||
|
inUse: '使用中',
|
||||||
|
cancel: '取消',
|
||||||
|
create: '创建',
|
||||||
|
save: '保存',
|
||||||
|
edit: '编辑',
|
||||||
|
duplicate: '复制',
|
||||||
|
delete: '删除',
|
||||||
|
unconfigured: '未配置',
|
||||||
|
noBaseUrl: '未配置 Base URL',
|
||||||
|
configCreated: '配置 "{name}" 已创建',
|
||||||
|
configDeleted: '配置已删除',
|
||||||
|
configCopied: '配置已复制为 "{name}"',
|
||||||
|
switchFailed: '切换失败',
|
||||||
|
auth: {
|
||||||
|
iflow: 'iFlow',
|
||||||
|
api: 'API Key',
|
||||||
|
openaiCompatible: 'OpenAI 兼容'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mcp: {
|
||||||
|
title: 'MCP 服务器',
|
||||||
|
description: '管理 Model Context Protocol 服务器配置',
|
||||||
|
serverList: '服务器列表',
|
||||||
|
addServer: '添加服务器',
|
||||||
|
editServer: '编辑服务器',
|
||||||
|
serverName: '服务器名称',
|
||||||
|
serverNamePlaceholder: 'my-mcp-server',
|
||||||
|
descriptionLabel: '描述',
|
||||||
|
descriptionPlaceholder: '服务器描述信息',
|
||||||
|
command: '命令',
|
||||||
|
commandPlaceholder: 'npx',
|
||||||
|
workingDir: '工作目录',
|
||||||
|
cwdPlaceholder: '.',
|
||||||
|
args: '参数 (每行一个)',
|
||||||
|
argsPlaceholder: '-y\\npackage-name',
|
||||||
|
envVars: '环境变量 (JSON 格式)',
|
||||||
|
envVarsPlaceholder: "例如: API_KEY=xxx",
|
||||||
|
invalidEnvJson: '环境变量 JSON 格式错误',
|
||||||
|
noServers: '暂无 MCP 服务器',
|
||||||
|
addFirstServer: '点击上方按钮添加第一个服务器',
|
||||||
|
noDescription: '无描述',
|
||||||
|
delete: '删除',
|
||||||
|
cancel: '取消',
|
||||||
|
saveChanges: '保存更改',
|
||||||
|
addServerBtn: '添加服务器',
|
||||||
|
inputServerName: '请输入服务器名称',
|
||||||
|
serverNameExists: '服务器名称已存在'
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
error: '错误',
|
||||||
|
warning: '警告',
|
||||||
|
success: '成功',
|
||||||
|
info: '信息',
|
||||||
|
cannotDeleteDefault: '不能删除默认配置',
|
||||||
|
inputConfigName: '请输入配置名称',
|
||||||
|
confirmDeleteConfig: '确定要删除配置 "{name}" 吗?',
|
||||||
|
confirmDeleteServer: '确定要删除服务器 "{name}" 吗?'
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
confirm: '确定',
|
||||||
|
cancel: '取消'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
config: '配置'
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
|
'zh-CN': '简体中文',
|
||||||
|
'en-US': 'English',
|
||||||
|
'ja-JP': '日本語'
|
||||||
|
}
|
||||||
|
}
|
||||||
130
src/locales/ja-JP.js
Normal file
130
src/locales/ja-JP.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
export default {
|
||||||
|
app: {
|
||||||
|
title: 'iFlow 設定エディタ'
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
minimize: '最小化',
|
||||||
|
maximize: '最大化',
|
||||||
|
close: '閉じる'
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
general: '一般',
|
||||||
|
basicSettings: '基本設定',
|
||||||
|
apiConfig: 'API 設定',
|
||||||
|
advanced: '詳細',
|
||||||
|
mcpServers: 'MCP サーバー'
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
title: '基本設定',
|
||||||
|
description: 'アプリケーションの一般設定を構成',
|
||||||
|
language: '言語',
|
||||||
|
theme: 'テーマ',
|
||||||
|
languageInterface: '言語とインターフェース',
|
||||||
|
otherSettings: 'その他の設定',
|
||||||
|
bootAnimation: '起動アニメーション',
|
||||||
|
bootAnimationShown: '表示済み',
|
||||||
|
bootAnimationNotShown: '未表示',
|
||||||
|
checkpointing: 'チェックポイント保存',
|
||||||
|
enabled: '有効',
|
||||||
|
disabled: '無効'
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
xcode: 'Xcode',
|
||||||
|
dark: 'ダーク',
|
||||||
|
light: 'ライト',
|
||||||
|
solarizedDark: 'Solarized Dark'
|
||||||
|
},
|
||||||
|
api: {
|
||||||
|
title: 'API 設定',
|
||||||
|
description: 'AI サービスと検索 API を構成',
|
||||||
|
currentConfig: '現在設定',
|
||||||
|
createTitle: 'API 設定を作成',
|
||||||
|
editTitle: 'API 設定を編集',
|
||||||
|
profileManagement: 'プロファイル管理',
|
||||||
|
newProfile: '新規プロファイル',
|
||||||
|
profileName: 'プロファイル名',
|
||||||
|
configName: 'プロファイル名',
|
||||||
|
configNamePlaceholder: 'プロファイル名を入力',
|
||||||
|
newConfigNamePlaceholder: '新しいプロファイル名を入力',
|
||||||
|
authType: '認証方式',
|
||||||
|
apiKey: 'API Key',
|
||||||
|
apiKeyPlaceholder: 'sk-cp-XXXXX...',
|
||||||
|
baseUrl: 'Base URL',
|
||||||
|
baseUrlPlaceholder: 'https://api.minimaxi.com/v1',
|
||||||
|
modelName: 'モデル名',
|
||||||
|
modelNamePlaceholder: 'MiniMax-M2.7',
|
||||||
|
searchApiKey: '検索 API Key',
|
||||||
|
searchApiKeyPlaceholder: 'sk-XXXXX...',
|
||||||
|
cna: 'CNA',
|
||||||
|
cnaPlaceholder: 'CNA 識別子',
|
||||||
|
inUse: '使用中',
|
||||||
|
cancel: 'キャンセル',
|
||||||
|
create: '作成',
|
||||||
|
save: '保存',
|
||||||
|
edit: '編集',
|
||||||
|
duplicate: '複製',
|
||||||
|
delete: '削除',
|
||||||
|
unconfigured: '未設定',
|
||||||
|
noBaseUrl: 'Base URL 未設定',
|
||||||
|
configCreated: 'プロファイル "{name}" を作成しました',
|
||||||
|
configDeleted: 'プロファイルを削除しました',
|
||||||
|
configCopied: 'プロファイルを "{name}" に複製しました',
|
||||||
|
switchFailed: '切り替えに失敗しました',
|
||||||
|
auth: {
|
||||||
|
iflow: 'iFlow',
|
||||||
|
api: 'API Key',
|
||||||
|
openaiCompatible: 'OpenAI 互換'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mcp: {
|
||||||
|
title: 'MCP サーバー',
|
||||||
|
description: 'Model Context Protocol サーバー設定を管理',
|
||||||
|
serverList: 'サーバー一覧',
|
||||||
|
addServer: 'サーバーを追加',
|
||||||
|
editServer: 'サーバーを編集',
|
||||||
|
serverName: 'サーバー名',
|
||||||
|
serverNamePlaceholder: 'my-mcp-server',
|
||||||
|
descriptionLabel: '説明',
|
||||||
|
descriptionPlaceholder: 'サーバーの説明',
|
||||||
|
command: 'コマンド',
|
||||||
|
commandPlaceholder: 'npx',
|
||||||
|
workingDir: '作業ディレクトリ',
|
||||||
|
cwdPlaceholder: '.',
|
||||||
|
args: '引数(1行に1つ)',
|
||||||
|
argsPlaceholder: '-y\\npackage-name',
|
||||||
|
envVars: '環境変数(JSON形式)',
|
||||||
|
envVarsPlaceholder: "例: API_KEY=xxx",
|
||||||
|
invalidEnvJson: '環境変数の JSON 形式が無効です',
|
||||||
|
noServers: 'MCP サーバーがありません',
|
||||||
|
addFirstServer: '上のボタンをクリックして最初のサーバーを追加',
|
||||||
|
noDescription: '説明なし',
|
||||||
|
delete: '削除',
|
||||||
|
cancel: 'キャンセル',
|
||||||
|
saveChanges: '変更を保存',
|
||||||
|
addServerBtn: 'サーバーを追加',
|
||||||
|
inputServerName: 'サーバー名を入力してください',
|
||||||
|
serverNameExists: 'サーバー名は既に存在します'
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
error: 'エラー',
|
||||||
|
warning: '警告',
|
||||||
|
success: '成功',
|
||||||
|
info: '情報',
|
||||||
|
cannotDeleteDefault: 'デフォルトプロファイルは削除できません',
|
||||||
|
inputConfigName: 'プロファイル名を入力してください',
|
||||||
|
confirmDeleteConfig: 'プロファイル "{name}" を削除してもよろしいですか?',
|
||||||
|
confirmDeleteServer: 'サーバー "{name}" を削除してもよろしいですか?'
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
confirm: '確認',
|
||||||
|
cancel: 'キャンセル'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
config: '設定'
|
||||||
|
},
|
||||||
|
languages: {
|
||||||
|
'zh-CN': '简体中文',
|
||||||
|
'en-US': 'English',
|
||||||
|
'ja-JP': '日本語'
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main.js
23
src/main.js
@@ -1,4 +1,21 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue';
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import App from './App.vue'
|
||||||
|
import zhCN from './locales/index.js'
|
||||||
|
import enUS from './locales/en-US.js'
|
||||||
|
import jaJP from './locales/ja-JP.js'
|
||||||
|
|
||||||
createApp(App).mount('#app');
|
const i18n = createI18n({
|
||||||
|
legacy: false,
|
||||||
|
locale: 'zh-CN',
|
||||||
|
fallbackLocale: 'zh-CN',
|
||||||
|
messages: {
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
'en-US': enUS,
|
||||||
|
'ja-JP': jaJP
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(i18n)
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
BIN
theme-dark.png
Normal file
BIN
theme-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
theme-solarized-dark.png
Normal file
BIN
theme-solarized-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
theme-xcode.png
Normal file
BIN
theme-xcode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Reference in New Issue
Block a user