feat(i18n): update localization files to include new API server controls and descriptions

- Added "Start" and "Stop" labels in English, Japanese, Russian, Chinese (Simplified and Traditional) locales.
- Introduced "Authorization Header" title and descriptions for API key and port fields across all locales.
- Removed deprecated API documentation unavailable messages for a cleaner user experience.
This commit is contained in:
kangfenmao 2025-07-30 16:01:01 +08:00
parent 0cafaafdf2
commit b629cd236d
7 changed files with 360 additions and 375 deletions

View File

@ -83,25 +83,28 @@
"restart": {
"button": "Restart",
"tooltip": "Restart Server"
}
},
"start": "Start",
"stop": "Stop"
},
"authHeader": {
"title": "Authorization Header"
},
"authHeaderText": "Use in Authorization header:",
"configuration": "Configuration",
"description": "Expose Cherry Studio's AI capabilities through OpenAI-compatible HTTP APIs",
"documentation": {
"title": "API Documentation",
"unavailable": {
"description": "Start the API server to view the interactive documentation",
"title": "API Documentation Unavailable"
}
"title": "API Documentation"
},
"fields": {
"apiKey": {
"copyTooltip": "Copy API Key",
"description": "Secure authentication token for API access",
"label": "API Key",
"placeholder": "API key will be auto-generated"
},
"port": {
"description": "TCP port number for the HTTP server (1000-65535)",
"helpText": "Stop server to change port",
"label": "Port"
},

View File

@ -83,25 +83,28 @@
"restart": {
"button": "再起動",
"tooltip": "サーバーを再起動"
}
},
"start": "開始",
"stop": "停止"
},
"authHeader": {
"title": "認証ヘッダー"
},
"authHeaderText": "認証ヘッダーで使用:",
"configuration": "設定",
"description": "OpenAI 互換の HTTP API を通じて Cherry Studio の AI 機能を公開します",
"documentation": {
"title": "API ドキュメント",
"unavailable": {
"description": "インタラクティブドキュメントを表示するには API サーバーを開始してください",
"title": "API ドキュメントが利用できません"
}
"title": "API ドキュメント"
},
"fields": {
"apiKey": {
"copyTooltip": "API キーをコピー",
"description": "API アクセスのための安全な認証トークン",
"label": "API キー",
"placeholder": "API キーは自動生成されます"
},
"port": {
"description": "HTTP サーバーの TCP ポート番号 (1000-65535)",
"helpText": "ポートを変更するにはサーバーを停止してください",
"label": "ポート"
},
@ -1575,7 +1578,7 @@
"prompt_placeholder": "作成したい画像を説明します。例:夕日の湖畔、遠くに山々",
"prompt_placeholder_edit": "画像の説明を入力します。テキスト描画には '二重引用符' を使用します",
"prompt_placeholder_en": "「英語」の説明を入力します。Imagenは現在、英語のプロンプト語のみをサポートしています",
"proxy_required": "打開代理並開啟TUN模式查看生成圖片或複製到瀏覽器開啟,後續會支持國內直連",
"proxy_required": "打開代理並開啟TUN模式查看生成圖片或複製到瀏覽器開啟後續會支持國內直連",
"quality": "品質",
"quality_options": {
"auto": "自動",

View File

@ -83,25 +83,28 @@
"restart": {
"button": "Перезапустить",
"tooltip": "Перезапустить сервер"
}
},
"start": "Запустить",
"stop": "Остановить"
},
"authHeader": {
"title": "Авторизация"
},
"authHeaderText": "Использовать в заголовке авторизации:",
"configuration": "Конфигурация",
"description": "Предоставляет возможности ИИ Cherry Studio через HTTP API, совместимые с OpenAI",
"documentation": {
"title": "Документация API",
"unavailable": {
"description": "Запустите API сервер для просмотра интерактивной документации",
"title": "Документация API недоступна"
}
"title": "Документация API"
},
"fields": {
"apiKey": {
"copyTooltip": "Копировать API ключ",
"description": "Безопасный токен для доступа к API",
"label": "API Ключ",
"placeholder": "API ключ будет сгенерирован автоматически"
},
"port": {
"description": "TCP порт для HTTP сервера (1000-65535)",
"helpText": "Остановите сервер для изменения порта",
"label": "Порт"
},

View File

@ -83,25 +83,28 @@
"restart": {
"button": "重启",
"tooltip": "重启服务器"
}
},
"start": "启动",
"stop": "停止"
},
"authHeader": {
"title": "授权标头"
},
"authHeaderText": "在授权标头中使用:",
"configuration": "配置",
"description": "通过 OpenAI 兼容的 HTTP API 暴露 Cherry Studio 的 AI 功能",
"documentation": {
"title": "API 文档",
"unavailable": {
"description": "启动 API 服务器以查看交互式文档",
"title": "API 文档不可用"
}
"title": "API 文档"
},
"fields": {
"apiKey": {
"copyTooltip": "复制 API 密钥",
"description": "用于 API 访问的安全认证令牌",
"label": "API 密钥",
"placeholder": "API 密钥将自动生成"
},
"port": {
"description": "HTTP 服务器的 TCP 端口号 (1000-65535)",
"helpText": "停止服务器以更改端口",
"label": "端口"
},

View File

@ -83,25 +83,28 @@
"restart": {
"button": "重新啟動",
"tooltip": "重新啟動伺服器"
}
},
"start": "啟動",
"stop": "停止"
},
"authHeader": {
"title": "授權標頭"
},
"authHeaderText": "在授權標頭中使用:",
"configuration": "配置",
"description": "透過 OpenAI 相容的 HTTP API 公開 Cherry Studio 的 AI 功能",
"documentation": {
"title": "API 文件",
"unavailable": {
"description": "啟動 API 伺服器以檢視互動式文件",
"title": "API 文件無法使用"
}
"title": "API 文件"
},
"fields": {
"apiKey": {
"copyTooltip": "複製 API 金鑰",
"description": "用於 API 訪問的安全認證令牌",
"label": "API 金鑰",
"placeholder": "API 金鑰將自動生成"
},
"port": {
"description": "HTTP 伺服器的 TCP 連接埠 (1000-65535)",
"helpText": "停止伺服器以變更連接埠",
"label": "連接埠"
},
@ -1574,7 +1577,7 @@
"prompt_enhancement_tip": "開啟後將提示重寫為詳細的、適合模型的版本",
"prompt_placeholder": "描述你想建立的圖片,例如:一個寧靜的湖泊,夕陽西下,遠處是群山",
"prompt_placeholder_edit": "輸入你的圖片描述,文本繪製用 ' 雙引號 ' 包裹",
"prompt_placeholder_en": "輸入英文圖片描述,目前 Imagen 僅支持英文提示詞",
"prompt_placeholder_en": "輸入英文圖片描述,目前 Imagen 僅支持英文提示詞",
"proxy_required": "打開代理並開啟”TUN 模式 “查看生成圖片或複製到瀏覽器開啟,後續會支持國內直連",
"quality": "品質",
"quality_options": {

View File

@ -1,10 +1,10 @@
import { CopyOutlined, GlobalOutlined, ReloadOutlined } from '@ant-design/icons'
import { useTheme } from '@renderer/context/ThemeProvider'
import { loggerService } from '@renderer/services/LoggerService'
import { RootState, useAppDispatch } from '@renderer/store'
import { setApiServerApiKey, setApiServerPort } from '@renderer/store/settings'
import { IpcChannel } from '@shared/IpcChannel'
import { Button, Card, Input, Space, Switch, Tooltip, Typography } from 'antd'
import { Button, Input, InputNumber, Tooltip, Typography } from 'antd'
import { Copy, ExternalLink, Play, RotateCcw, Square } from 'lucide-react'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
@ -16,175 +16,13 @@ import { SettingContainer } from '..'
const logger = loggerService.withContext('ApiServerSettings')
const { Text, Title } = Typography
const ConfigCard = styled(Card)`
margin-bottom: 24px;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
border: 1px solid var(--color-border);
.ant-card-head {
border-bottom: 1px solid var(--color-border);
padding: 16px 24px;
}
.ant-card-body {
padding: 16px 20px;
}
`
const SectionHeader = styled.div`
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
h4 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: var(--color-text-1);
}
`
const FieldLabel = styled.div`
font-size: 14px;
font-weight: 500;
color: var(--color-text-1);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
`
const ActionButtonGroup = styled(Space)`
.ant-btn {
border-radius: 6px;
font-weight: 500;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.ant-btn-primary {
background: #1677ff;
border-color: #1677ff;
}
.ant-btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
`
const StyledInput = styled(Input)`
border-radius: 6px;
border: 1.5px solid var(--color-border);
&:focus,
&:focus-within {
border-color: #1677ff;
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1);
}
`
const ServerControlPanel = styled.div<{ status: boolean }>`
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-radius: 8px;
background: ${(props) =>
props.status
? 'linear-gradient(135deg, #f6ffed 0%, #f0f9ff 100%)'
: 'linear-gradient(135deg, #fff2f0 0%, #fafafa 100%)'};
border: 1px solid ${(props) => (props.status ? '#d9f7be' : '#ffd6d6')};
transition: all 0.3s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
`
const StatusSection = styled.div<{ status: boolean }>`
display: flex;
align-items: center;
gap: 10px;
.status-indicator {
position: relative;
width: 10px;
height: 10px;
border-radius: 50%;
background: ${(props) => (props.status ? '#52c41a' : '#ff4d4f')};
&::before {
content: '';
position: absolute;
inset: -3px;
border-radius: 50%;
background: ${(props) => (props.status ? '#52c41a' : '#ff4d4f')};
opacity: 0.2;
animation: ${(props) => (props.status ? 'pulse 2s infinite' : 'none')};
}
}
.status-content {
display: flex;
flex-direction: column;
gap: 2px;
}
.status-text {
font-weight: 600;
font-size: 14px;
color: ${(props) => (props.status ? '#52c41a' : '#ff4d4f')};
margin: 0;
}
.status-subtext {
font-size: 12px;
color: var(--color-text-3);
margin: 0;
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
opacity: 0.2;
}
50% {
transform: scale(1.5);
opacity: 0.1;
}
}
`
const ControlSection = styled.div`
display: flex;
align-items: center;
gap: 12px;
.restart-btn {
opacity: 0;
transform: translateX(10px);
transition: all 0.3s ease;
&.visible {
opacity: 1;
transform: translateX(0);
}
}
`
const ApiServerSettings: FC = () => {
const { theme } = useTheme()
const dispatch = useAppDispatch()
const { t } = useTranslation()
// API Server state with proper defaults
const apiServerConfig = useSelector((state: RootState) => {
return state.settings.apiServer
})
const apiServerConfig = useSelector((state: RootState) => state.settings.apiServer)
const [apiServerRunning, setApiServerRunning] = useState(false)
const [apiServerLoading, setApiServerLoading] = useState(false)
@ -265,179 +103,320 @@ const ApiServerSettings: FC = () => {
}
}
const openApiDocs = () => {
if (apiServerRunning) {
window.open(`http://localhost:${apiServerConfig.port}/api-docs`, '_blank')
}
}
return (
<SettingContainer theme={theme}>
<Container theme={theme}>
{/* Header Section */}
<div style={{ marginBottom: 32 }}>
<Title level={3} style={{ margin: 0, marginBottom: 8 }}>
{t('apiServer.title')}
</Title>
<Text type="secondary">{t('apiServer.description')}</Text>
</div>
{/* Server Status & Configuration Card */}
<ConfigCard
title={
<SectionHeader>
<GlobalOutlined />
<h4>{t('apiServer.configuration')}</h4>
</SectionHeader>
}>
<Space direction="vertical" size={12} style={{ width: '100%' }}>
{/* Server Control Panel */}
<ServerControlPanel status={apiServerRunning}>
<StatusSection status={apiServerRunning}>
<div className="status-indicator" />
<div className="status-content">
<div className="status-text">
{apiServerRunning ? t('apiServer.status.running') : t('apiServer.status.stopped')}
</div>
<div className="status-subtext">
{apiServerRunning ? `http://localhost:${apiServerConfig.port}` : t('apiServer.fields.port.helpText')}
</div>
</div>
</StatusSection>
<ControlSection>
<Switch
checked={apiServerRunning}
loading={apiServerLoading}
onChange={handleApiServerToggle}
size="default"
/>
<Tooltip title={t('apiServer.actions.restart.tooltip')}>
<Button
icon={<ReloadOutlined />}
onClick={handleApiServerRestart}
loading={apiServerLoading}
size="small"
type="text"
className={`restart-btn ${apiServerRunning ? 'visible' : ''}`}>
{t('apiServer.actions.restart.button')}
</Button>
</Tooltip>
</ControlSection>
</ServerControlPanel>
{/* Configuration Fields */}
<div style={{ display: 'grid', gap: '12px' }}>
{/* Port Configuration */}
{!apiServerRunning && (
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
<FieldLabel style={{ minWidth: 50, margin: 0 }}>{t('apiServer.fields.port.label')}</FieldLabel>
<StyledInput
type="number"
value={apiServerConfig.port}
onChange={(e) => handlePortChange(e.target.value)}
style={{ width: 100 }}
min={1000}
max={65535}
disabled={apiServerRunning}
placeholder="23333"
size="small"
/>
{apiServerRunning && (
<Text type="secondary" style={{ fontSize: 12 }}>
{t('apiServer.fields.port.helpText')}
</Text>
)}
</div>
)}
{/* API Key Configuration */}
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
<FieldLabel style={{ minWidth: 50, margin: 0 }}>{t('apiServer.fields.apiKey.label')}</FieldLabel>
<StyledInput
value={apiServerConfig.apiKey}
readOnly
style={{ flex: 1, minWidth: 200, maxWidth: 300 }}
placeholder={t('apiServer.fields.apiKey.placeholder')}
disabled={apiServerRunning}
size="small"
/>
<ActionButtonGroup>
<Tooltip title={t('apiServer.fields.apiKey.copyTooltip')}>
<Button icon={<CopyOutlined />} onClick={copyApiKey} disabled={!apiServerConfig.apiKey} size="small">
{t('apiServer.actions.copy')}
</Button>
</Tooltip>
{!apiServerRunning && (
<Button onClick={regenerateApiKey} disabled={apiServerRunning} size="small">
{t('apiServer.actions.regenerate')}
</Button>
)}
</ActionButtonGroup>
</div>
{/* Authorization header info */}
<Text type="secondary" style={{ fontSize: 11, lineHeight: 1.3 }}>
{t('apiServer.authHeaderText')}{' '}
<Text code style={{ fontSize: 11 }}>
Bearer {apiServerConfig.apiKey || 'your-api-key'}
</Text>
</Text>
</div>
</Space>
</ConfigCard>
{/* API Documentation Card */}
<ConfigCard
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
marginBottom: 0
}}
styles={{
body: {
flex: 1,
display: 'flex',
flexDirection: 'column',
padding: 0
}
}}
title={
<SectionHeader>
<h4>{t('apiServer.documentation.title')}</h4>
</SectionHeader>
}>
{apiServerRunning ? (
<iframe
src={`http://localhost:${apiServerConfig.port}/api-docs`}
style={{
width: '100%',
border: 'none',
height: '100vh'
}}
title="API Documentation"
sandbox="allow-scripts allow-forms"
/>
) : (
<div
style={{
textAlign: 'center',
padding: '60px 20px',
color: 'var(--color-text-2)',
background: 'var(--color-bg-2)',
borderRadius: 8,
border: '1px dashed var(--color-border)',
flex: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
margin: 16,
height: '300px'
}}>
<GlobalOutlined style={{ fontSize: 48, marginBottom: 16, opacity: 0.3 }} />
<div style={{ fontSize: 16, fontWeight: 500, marginBottom: 8 }}>
{t('apiServer.documentation.unavailable.title')}
</div>
<div style={{ fontSize: 14 }}>{t('apiServer.documentation.unavailable.description')}</div>
</div>
<HeaderSection>
<HeaderContent>
<Title level={3} style={{ margin: 0, marginBottom: 8 }}>
{t('apiServer.title')}
</Title>
<Text type="secondary">{t('apiServer.description')}</Text>
</HeaderContent>
{apiServerRunning && (
<Button type="primary" icon={<ExternalLink size={14} />} onClick={openApiDocs}>
{t('apiServer.documentation.title')}
</Button>
)}
</ConfigCard>
</SettingContainer>
</HeaderSection>
{/* Server Control Panel with integrated configuration */}
<ServerControlPanel $status={apiServerRunning}>
<StatusSection>
<StatusIndicator $status={apiServerRunning} />
<StatusContent>
<StatusText $status={apiServerRunning}>
{apiServerRunning ? t('apiServer.status.running') : t('apiServer.status.stopped')}
</StatusText>
<StatusSubtext>
{apiServerRunning ? `http://localhost:${apiServerConfig.port}` : t('apiServer.fields.port.description')}
</StatusSubtext>
</StatusContent>
</StatusSection>
<ControlSection>
{apiServerRunning && (
<Tooltip title={t('apiServer.actions.restart.tooltip')}>
<RestartButton
$loading={apiServerLoading}
onClick={apiServerLoading ? undefined : handleApiServerRestart}>
<RotateCcw size={14} />
<span>{t('apiServer.actions.restart.button')}</span>
</RestartButton>
</Tooltip>
)}
{/* Port input when server is stopped */}
{!apiServerRunning && (
<StyledInputNumber
value={apiServerConfig.port}
onChange={(value) => handlePortChange(String(value || 23333))}
min={1000}
max={65535}
disabled={apiServerRunning}
placeholder="23333"
size="middle"
/>
)}
<Tooltip title={apiServerRunning ? t('apiServer.actions.stop') : t('apiServer.actions.start')}>
{apiServerRunning ? (
<StopButton
$loading={apiServerLoading}
onClick={apiServerLoading ? undefined : () => handleApiServerToggle(false)}>
<Square size={20} style={{ color: 'var(--color-status-error)' }} />
</StopButton>
) : (
<StartButton
$loading={apiServerLoading}
onClick={apiServerLoading ? undefined : () => handleApiServerToggle(true)}>
<Play size={20} style={{ color: 'var(--color-status-success)' }} />
</StartButton>
)}
</Tooltip>
</ControlSection>
</ServerControlPanel>
{/* API Key Configuration */}
<ConfigurationField>
<FieldLabel>{t('apiServer.fields.apiKey.label')}</FieldLabel>
<FieldDescription>{t('apiServer.fields.apiKey.description')}</FieldDescription>
<StyledInput
value={apiServerConfig.apiKey}
readOnly
placeholder={t('apiServer.fields.apiKey.placeholder')}
size="middle"
suffix={
<InputButtonContainer>
{!apiServerRunning && (
<RegenerateButton onClick={regenerateApiKey} disabled={apiServerRunning} type="link">
{t('apiServer.actions.regenerate')}
</RegenerateButton>
)}
<Tooltip title={t('apiServer.fields.apiKey.copyTooltip')}>
<InputButton icon={<Copy size={14} />} onClick={copyApiKey} disabled={!apiServerConfig.apiKey} />
</Tooltip>
</InputButtonContainer>
}
/>
{/* Authorization header info */}
<AuthHeaderSection>
<FieldLabel>{t('apiServer.authHeader.title')}</FieldLabel>
<StyledInput
style={{ height: 38 }}
value={`Authorization: Bearer ${apiServerConfig.apiKey || 'your-api-key'}`}
readOnly
size="middle"
/>
</AuthHeaderSection>
</ConfigurationField>
</Container>
)
}
// Styled Components
const Container = styled(SettingContainer)`
display: flex;
flex-direction: column;
height: calc(100vh - var(--navbar-height));
`
const HeaderSection = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
`
const HeaderContent = styled.div`
flex: 1;
`
const ServerControlPanel = styled.div<{ $status: boolean }>`
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-radius: 8px;
background: var(--color-background);
border: 1px solid ${(props) => (props.$status ? 'var(--color-status-success)' : 'var(--color-border)')};
transition: all 0.3s ease;
margin-bottom: 16px;
`
const StatusSection = styled.div`
display: flex;
align-items: center;
gap: 10px;
`
const StatusIndicator = styled.div<{ $status: boolean }>`
position: relative;
width: 10px;
height: 10px;
border-radius: 50%;
background: ${(props) => (props.$status ? 'var(--color-status-success)' : 'var(--color-status-error)')};
&::before {
content: '';
position: absolute;
inset: -3px;
border-radius: 50%;
background: ${(props) => (props.$status ? 'var(--color-status-success)' : 'var(--color-status-error)')};
opacity: 0.2;
animation: ${(props) => (props.$status ? 'pulse 2s infinite' : 'none')};
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
opacity: 0.2;
}
50% {
transform: scale(1.5);
opacity: 0.1;
}
}
`
const StatusContent = styled.div`
display: flex;
flex-direction: column;
gap: 2px;
`
const StatusText = styled.div<{ $status: boolean }>`
font-weight: 600;
font-size: 14px;
color: ${(props) => (props.$status ? 'var(--color-status-success)' : 'var(--color-text-1)')};
margin: 0;
`
const StatusSubtext = styled.div`
font-size: 12px;
color: var(--color-text-3);
margin: 0;
`
const ControlSection = styled.div`
display: flex;
align-items: center;
gap: 12px;
`
const RestartButton = styled.div<{ $loading: boolean }>`
display: flex;
align-items: center;
gap: 4px;
color: var(--color-text-2);
cursor: ${(props) => (props.$loading ? 'not-allowed' : 'pointer')};
opacity: ${(props) => (props.$loading ? 0.5 : 1)};
font-size: 12px;
transition: all 0.2s ease;
&:hover {
color: ${(props) => (props.$loading ? 'var(--color-text-2)' : 'var(--color-primary)')};
}
`
const StyledInputNumber = styled(InputNumber)`
width: 80px;
border-radius: 6px;
border: 1.5px solid var(--color-border);
margin-right: 5px;
`
const StartButton = styled.div<{ $loading: boolean }>`
display: inline-flex;
align-items: center;
justify-content: center;
cursor: ${(props) => (props.$loading ? 'not-allowed' : 'pointer')};
opacity: ${(props) => (props.$loading ? 0.5 : 1)};
transition: all 0.2s ease;
&:hover {
transform: ${(props) => (props.$loading ? 'scale(1)' : 'scale(1.1)')};
}
`
const StopButton = styled.div<{ $loading: boolean }>`
display: inline-flex;
align-items: center;
justify-content: center;
cursor: ${(props) => (props.$loading ? 'not-allowed' : 'pointer')};
opacity: ${(props) => (props.$loading ? 0.5 : 1)};
transition: all 0.2s ease;
&:hover {
transform: ${(props) => (props.$loading ? 'scale(1)' : 'scale(1.1)')};
}
`
const ConfigurationField = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px;
background: var(--color-background);
border-radius: 8px;
border: 1px solid var(--color-border);
`
const FieldLabel = styled.div`
font-size: 14px;
font-weight: 500;
color: var(--color-text-1);
margin: 0;
`
const FieldDescription = styled.div`
font-size: 12px;
color: var(--color-text-3);
margin: 0;
`
const StyledInput = styled(Input)`
width: 100%;
border-radius: 6px;
border: 1.5px solid var(--color-border);
`
const InputButtonContainer = styled.div`
display: flex;
align-items: center;
gap: 4px;
`
const InputButton = styled(Button)`
border: none;
padding: 0 4px;
background: transparent;
`
const RegenerateButton = styled(Button)`
padding: 0 4px;
font-size: 12px;
height: auto;
line-height: 1;
border: none;
background: transparent;
`
const AuthHeaderSection = styled.div`
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 8px;
`
export default ApiServerSettings

View File

@ -8,7 +8,6 @@ import {
Info,
MonitorCog,
Package,
PencilRuler,
Rocket,
Server,
Settings2,
@ -16,7 +15,6 @@ import {
TextCursorInput,
Zap
} from 'lucide-react'
// 导入useAppSelector
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Link, Route, Routes, useLocation } from 'react-router-dom'
@ -91,12 +89,6 @@ const SettingsPage: FC = () => {
{t('memory.title')}
</MenuItem>
</MenuItemLink>
<MenuItemLink to="/settings/tool">
<MenuItem className={isRoute('/settings/tool')}>
<PencilRuler size={18} />
{t('settings.tool.title')}
</MenuItem>
</MenuItemLink>
<MenuItemLink to="/settings/shortcut">
<MenuItem className={isRoute('/settings/shortcut')}>
<Command size={18} />
@ -219,7 +211,6 @@ const SettingContent = styled.div`
display: flex;
height: 100%;
flex: 1;
border-right: 0.5px solid var(--color-border);
`
export default SettingsPage