mirror of
https://github.com/ILoveBingLu/CipherTalk.git
synced 2026-05-20 19:40:32 +08:00
feat: 新增 CipherTalk MCP 服务
This commit is contained in:
@@ -16,6 +16,7 @@ import GroupAnalyticsPage from './pages/GroupAnalyticsPage'
|
||||
import DataManagementPage from './pages/DataManagementPage'
|
||||
import SettingsPage from './pages/SettingsPage'
|
||||
import OpenApiPage from './pages/OpenApiPage'
|
||||
import McpPage from './pages/McpPage'
|
||||
import ExportPage from './pages/ExportPage'
|
||||
import ActivationPage from './pages/ActivationPage'
|
||||
import ImageWindow from './pages/ImageWindow'
|
||||
@@ -513,6 +514,7 @@ function App() {
|
||||
<Route path="/data-management" element={<DataManagementPage />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
<Route path="/open-api" element={<OpenApiPage />} />
|
||||
<Route path="/mcp" element={<McpPage />} />
|
||||
<Route path="/export" element={<ExportPage />} />
|
||||
<Route path="/chat-history/:sessionId/:messageId" element={<ChatHistoryPage />} />
|
||||
</Routes>
|
||||
|
||||
@@ -10,7 +10,7 @@ import ListItemButton from '@mui/material/ListItemButton'
|
||||
import ListItemIcon from '@mui/material/ListItemIcon'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import { Home, MessageSquare, BarChart3, Users, FileText, Database, Settings, SquareChevronLeft, SquareChevronRight, Download, Aperture, Network } from 'lucide-react'
|
||||
import { Home, MessageSquare, BarChart3, Users, FileText, Database, Settings, SquareChevronLeft, SquareChevronRight, Download, Aperture, Network, Boxes } from 'lucide-react'
|
||||
import { useAppStore } from '../stores/appStore'
|
||||
|
||||
const DRAWER_WIDTH = 220
|
||||
@@ -82,6 +82,7 @@ function Sidebar() {
|
||||
{ key: 'export', label: '导出数据', icon: <Download size={20} />, type: 'route', path: '/export' },
|
||||
{ key: 'data-management', label: '数据管理', icon: <Database size={20} />, type: 'route', path: '/data-management' },
|
||||
{ key: 'open-api', label: '开放接口', icon: <Network size={20} />, type: 'route', path: '/open-api' },
|
||||
{ key: 'mcp', label: 'MCP 服务', icon: <Boxes size={20} />, type: 'route', path: '/mcp' },
|
||||
]
|
||||
|
||||
const navItemSx = {
|
||||
|
||||
@@ -111,10 +111,6 @@ interface AISummarySettingsProps {
|
||||
setEnableThinking: (val: boolean) => void
|
||||
messageLimit: number
|
||||
setMessageLimit: (val: number) => void
|
||||
mcpEnabled: boolean
|
||||
setMcpEnabled: (val: boolean) => void
|
||||
mcpExposeMediaPaths: boolean
|
||||
setMcpExposeMediaPaths: (val: boolean) => void
|
||||
showMessage: (text: string, success: boolean) => void
|
||||
}
|
||||
|
||||
@@ -137,10 +133,6 @@ function AISummarySettings({
|
||||
setEnableThinking,
|
||||
messageLimit,
|
||||
setMessageLimit,
|
||||
mcpEnabled,
|
||||
setMcpEnabled,
|
||||
mcpExposeMediaPaths,
|
||||
setMcpExposeMediaPaths,
|
||||
showMessage
|
||||
}: AISummarySettingsProps) {
|
||||
const [showApiKey, setShowApiKey] = useState(false)
|
||||
@@ -758,55 +750,6 @@ function AISummarySettings({
|
||||
</>
|
||||
)}
|
||||
|
||||
<h3 className="section-title">MCP Server</h3>
|
||||
<div className="settings-form" style={{ marginTop: '8px' }}>
|
||||
<div className="form-group">
|
||||
<label className="toggle-label">
|
||||
<div className="toggle-header">
|
||||
<span className="toggle-title">启用内嵌 MCP Server</span>
|
||||
<span className="toggle-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={mcpEnabled}
|
||||
onChange={(e) => setMcpEnabled(e.target.checked)}
|
||||
/>
|
||||
<span className="toggle-slider"></span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<div className="toggle-description">
|
||||
<p>为 Claude Desktop、Codex、Cherry Studio 等 MCP 宿主暴露 CipherTalk 数据读取能力。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="toggle-label">
|
||||
<div className="toggle-header">
|
||||
<span className="toggle-title">默认暴露媒体本地路径</span>
|
||||
<span className="toggle-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={mcpExposeMediaPaths}
|
||||
onChange={(e) => setMcpExposeMediaPaths(e.target.checked)}
|
||||
/>
|
||||
<span className="toggle-slider"></span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<div className="toggle-description">
|
||||
<p>控制 MCP `get_messages` 默认是否解析并返回图片、视频、文件等本地路径。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>启动命令</label>
|
||||
<input className="api-key-input" type="text" value="npm run build:mcp && node scripts/mcp-runner.js" readOnly />
|
||||
<div className="form-hint">
|
||||
首批工具:`health_check`、`get_status`、`list_sessions`、`get_messages`、`list_contacts`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="info-box-simple">
|
||||
<p>💡 提示:API 密钥存储在本地,不会上传到任何服务器。摘要内容仅用于本地展示。</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
Container,
|
||||
Snackbar,
|
||||
Stack,
|
||||
Switch,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { Check, Copy, Save } from 'lucide-react'
|
||||
import * as configService from '../services/config'
|
||||
|
||||
type ToastState = {
|
||||
text: string
|
||||
success: boolean
|
||||
}
|
||||
|
||||
type McpLaunchConfig = {
|
||||
command: string
|
||||
args: string[]
|
||||
cwd: string
|
||||
mode: 'dev' | 'packaged'
|
||||
}
|
||||
|
||||
function formatCommandPart(value: string) {
|
||||
if (!value) return value
|
||||
return /[\s"]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value
|
||||
}
|
||||
|
||||
const textFieldSx = {
|
||||
'& .MuiInputLabel-root': {
|
||||
color: 'var(--text-secondary)',
|
||||
},
|
||||
'& .MuiInputLabel-root.Mui-focused': {
|
||||
color: 'var(--primary)',
|
||||
},
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: '14px',
|
||||
color: 'var(--text-primary)',
|
||||
backgroundColor: 'var(--bg-secondary)',
|
||||
'& fieldset': {
|
||||
borderColor: 'var(--border-color)',
|
||||
},
|
||||
'&:hover fieldset': {
|
||||
borderColor: 'var(--primary)',
|
||||
},
|
||||
'&.Mui-focused fieldset': {
|
||||
borderColor: 'var(--primary)',
|
||||
},
|
||||
},
|
||||
'& .MuiInputBase-input': {
|
||||
color: 'var(--text-primary)',
|
||||
},
|
||||
}
|
||||
|
||||
const switchSx = {
|
||||
'& .MuiSwitch-switchBase.Mui-checked': {
|
||||
color: 'var(--primary)',
|
||||
},
|
||||
'& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {
|
||||
backgroundColor: 'var(--primary)',
|
||||
},
|
||||
'& .MuiSwitch-track': {
|
||||
backgroundColor: 'var(--text-tertiary)',
|
||||
},
|
||||
}
|
||||
|
||||
const secondaryButtonSx = {
|
||||
borderRadius: '999px',
|
||||
minWidth: 120,
|
||||
textTransform: 'none',
|
||||
fontWeight: 600,
|
||||
color: 'var(--text-primary)',
|
||||
borderColor: 'var(--border-color)',
|
||||
backgroundColor: 'var(--bg-secondary)',
|
||||
'&:hover': {
|
||||
borderColor: 'var(--primary)',
|
||||
backgroundColor: 'var(--primary-light)',
|
||||
},
|
||||
}
|
||||
|
||||
function McpPage() {
|
||||
const [mcpEnabled, setMcpEnabled] = useState(false)
|
||||
const [mcpExposeMediaPaths, setMcpExposeMediaPaths] = useState(true)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [toast, setToast] = useState<ToastState | null>(null)
|
||||
const [launchConfig, setLaunchConfig] = useState<McpLaunchConfig>({
|
||||
command: 'npm',
|
||||
args: ['run', 'mcp'],
|
||||
cwd: 'D:/CipherTalk',
|
||||
mode: 'dev',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
try {
|
||||
const [enabled, exposeMediaPaths] = await Promise.all([
|
||||
configService.getMcpEnabled(),
|
||||
configService.getMcpExposeMediaPaths(),
|
||||
])
|
||||
setMcpEnabled(enabled)
|
||||
setMcpExposeMediaPaths(exposeMediaPaths)
|
||||
|
||||
try {
|
||||
const mcpLaunchConfig = await window.electronAPI.app.getMcpLaunchConfig()
|
||||
if (mcpLaunchConfig?.command && Array.isArray(mcpLaunchConfig.args) && mcpLaunchConfig.cwd) {
|
||||
setLaunchConfig(mcpLaunchConfig)
|
||||
}
|
||||
} catch (innerError) {
|
||||
const message = String(innerError || '')
|
||||
if (!message.includes("No handler registered for 'app:getMcpLaunchConfig'")) {
|
||||
console.error('获取 MCP 启动配置失败:', innerError)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载 MCP 配置失败:', e)
|
||||
setToast({ text: '加载 MCP 配置失败', success: false })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
void load()
|
||||
}, [])
|
||||
|
||||
const mcpRunCommand = useMemo(() => {
|
||||
const parts = [launchConfig.command, ...launchConfig.args].map(formatCommandPart)
|
||||
return parts.join(' ')
|
||||
}, [launchConfig])
|
||||
|
||||
const mcpServerJsonTemplate = useMemo(() => JSON.stringify({
|
||||
mcpServers: {
|
||||
ciphertalk: {
|
||||
command: launchConfig.command,
|
||||
args: launchConfig.args,
|
||||
cwd: launchConfig.cwd
|
||||
}
|
||||
}
|
||||
}, null, 2), [launchConfig])
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true)
|
||||
try {
|
||||
await Promise.all([
|
||||
configService.setMcpEnabled(mcpEnabled),
|
||||
configService.setMcpExposeMediaPaths(mcpExposeMediaPaths),
|
||||
])
|
||||
setToast({ text: 'MCP 配置已保存', success: true })
|
||||
} catch (e) {
|
||||
console.error('保存 MCP 配置失败:', e)
|
||||
setToast({ text: '保存 MCP 配置失败', success: false })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const copyText = async (text: string, successText: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
setToast({ text: successText, success: true })
|
||||
} catch (e) {
|
||||
console.error('复制失败:', e)
|
||||
setToast({ text: '复制失败,请手动复制', success: false })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ height: '100%', mx: -3, mt: -3, overflowY: 'auto', pb: 3 }}>
|
||||
<Container maxWidth="lg" sx={{ px: { xs: 2, md: 4 }, py: { xs: 3, md: 4 } }}>
|
||||
<Stack spacing={2.2}>
|
||||
<Box sx={{ px: { xs: 0.5, md: 1 }, pt: 0.5 }}>
|
||||
<Typography variant="h4" sx={{ fontSize: 30, fontWeight: 700, color: 'var(--text-primary)' }}>
|
||||
MCP Server
|
||||
</Typography>
|
||||
<Typography sx={{ mt: 1, color: 'var(--text-secondary)' }}>
|
||||
使用标准 MCP `stdio` 工具接口为 Claude Desktop、Codex、Cherry Studio 等宿主提供本地聊天数据读取能力。
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Card
|
||||
sx={{
|
||||
borderRadius: '26px',
|
||||
border: '1px solid var(--border-color)',
|
||||
bgcolor: 'var(--bg-secondary)',
|
||||
boxShadow: 'none',
|
||||
}}
|
||||
>
|
||||
<CardHeader
|
||||
title="服务配置"
|
||||
titleTypographyProps={{ fontWeight: 700, fontSize: 18, color: 'var(--text-primary)' }}
|
||||
sx={{ px: { xs: 2, md: 3 }, pb: 0.8 }}
|
||||
/>
|
||||
<CardContent sx={{ px: { xs: 2, md: 3 }, pt: 0.6 }}>
|
||||
<Stack spacing={2.4}>
|
||||
<Alert
|
||||
severity="info"
|
||||
variant="outlined"
|
||||
sx={{
|
||||
borderRadius: '18px',
|
||||
bgcolor: 'var(--bg-primary)',
|
||||
borderColor: 'var(--border-color)',
|
||||
color: 'var(--text-primary)',
|
||||
'& .MuiAlert-message': {
|
||||
color: 'var(--text-primary)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
`mcpEnabled` 现在只作为状态标记和 warning 来源,不阻止宿主拉起 MCP。
|
||||
{launchConfig.mode === 'packaged'
|
||||
? ' 当前展示的是打包版伴随启动器 `ciphertalk-mcp.cmd`。'
|
||||
: ' 当前展示的是开发态入口 `npm run mcp`。'}
|
||||
</Alert>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: '18px',
|
||||
border: '1px solid var(--border-color)',
|
||||
bgcolor: 'var(--bg-primary)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography sx={{ fontWeight: 600, color: 'var(--text-primary)' }}>MCP 状态标记</Typography>
|
||||
<Typography sx={{ mt: 0.5, fontSize: 13, color: 'var(--text-secondary)' }}>
|
||||
仅用于在 `health_check` / `get_status` 中暴露当前配置状态,不会阻止宿主调用工具。
|
||||
</Typography>
|
||||
</Box>
|
||||
<Switch
|
||||
checked={mcpEnabled}
|
||||
onChange={(e) => setMcpEnabled(e.target.checked)}
|
||||
disabled={loading || saving}
|
||||
sx={switchSx}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: '18px',
|
||||
border: '1px solid var(--border-color)',
|
||||
bgcolor: 'var(--bg-primary)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Typography sx={{ fontWeight: 600, color: 'var(--text-primary)' }}>默认解析媒体本地路径</Typography>
|
||||
<Typography sx={{ mt: 0.5, fontSize: 13, color: 'var(--text-secondary)' }}>
|
||||
控制 `get_messages` 默认是否解析并返回图片、视频、语音、文件等本地路径。
|
||||
</Typography>
|
||||
</Box>
|
||||
<Switch
|
||||
checked={mcpExposeMediaPaths}
|
||||
onChange={(e) => setMcpExposeMediaPaths(e.target.checked)}
|
||||
disabled={loading || saving}
|
||||
sx={switchSx}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography sx={{ mb: 1, fontWeight: 600, color: 'var(--text-primary)' }}>启动命令</Typography>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.2}>
|
||||
<TextField
|
||||
fullWidth
|
||||
value={mcpRunCommand}
|
||||
InputProps={{ readOnly: true }}
|
||||
sx={textFieldSx}
|
||||
/>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Copy size={16} />}
|
||||
onClick={() => copyText(mcpRunCommand, 'MCP 启动命令已复制')}
|
||||
sx={secondaryButtonSx}
|
||||
>
|
||||
复制
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography sx={{ mb: 1, fontWeight: 600, color: 'var(--text-primary)' }}>
|
||||
标准 mcpServers 配置(可直接粘贴)
|
||||
</Typography>
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={9}
|
||||
value={mcpServerJsonTemplate}
|
||||
InputProps={{ readOnly: true }}
|
||||
sx={{
|
||||
...textFieldSx,
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: '14px',
|
||||
color: 'var(--text-primary)',
|
||||
backgroundColor: 'var(--bg-secondary)',
|
||||
fontFamily: 'var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace)',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.2} sx={{ mt: 1.2 }}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Copy size={16} />}
|
||||
onClick={() => copyText(mcpServerJsonTemplate, 'mcpServers 配置已复制')}
|
||||
sx={secondaryButtonSx}
|
||||
>
|
||||
复制配置
|
||||
</Button>
|
||||
<Typography sx={{ alignSelf: 'center', fontSize: 13, color: 'var(--text-secondary)' }}>
|
||||
{launchConfig.mode === 'packaged'
|
||||
? '`cwd` 已指向安装目录,宿主通常无需额外包一层 shell。'
|
||||
: '`cwd` 已自动使用当前仓库目录,通常无需修改。'}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.2} justifyContent="space-between" alignItems={{ xs: 'stretch', sm: 'center' }}>
|
||||
<Typography sx={{ fontSize: 13, color: 'var(--text-secondary)' }}>
|
||||
v1 工具:`health_check`、`get_status`、`list_sessions`、`get_messages`
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<Save size={16} />}
|
||||
onClick={handleSave}
|
||||
disabled={loading || saving}
|
||||
sx={{
|
||||
borderRadius: '999px',
|
||||
px: 2.6,
|
||||
textTransform: 'none',
|
||||
fontWeight: 700,
|
||||
background: 'var(--primary-gradient)',
|
||||
'&:hover': {
|
||||
background: 'var(--primary-gradient)',
|
||||
filter: 'brightness(0.98)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{saving ? '保存中...' : '保存配置'}
|
||||
</Button>
|
||||
</Stack>
|
||||
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Container>
|
||||
|
||||
<Snackbar
|
||||
open={!!toast}
|
||||
autoHideDuration={2400}
|
||||
onClose={() => setToast(null)}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
>
|
||||
<Alert
|
||||
icon={toast?.success ? <Check size={16} /> : undefined}
|
||||
severity={toast?.success ? 'success' : 'error'}
|
||||
variant="filled"
|
||||
onClose={() => setToast(null)}
|
||||
sx={{
|
||||
borderRadius: '12px',
|
||||
color: '#fff',
|
||||
bgcolor: toast?.success ? 'var(--primary)' : 'var(--danger)',
|
||||
}}
|
||||
>
|
||||
{toast?.text}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default McpPage
|
||||
@@ -140,8 +140,6 @@ function SettingsPage() {
|
||||
const [aiCustomSystemPrompt, setAiCustomSystemPromptState] = useState<string>('')
|
||||
const [aiEnableThinking, setAiEnableThinkingState] = useState<boolean>(true)
|
||||
const [aiMessageLimit, setAiMessageLimitState] = useState<number>(3000)
|
||||
const [mcpEnabled, setMcpEnabledState] = useState<boolean>(false)
|
||||
const [mcpExposeMediaPaths, setMcpExposeMediaPathsState] = useState<boolean>(true)
|
||||
|
||||
// 日志相关状态
|
||||
const [logFiles, setLogFiles] = useState<Array<{ name: string; size: number; mtime: Date }>>([])
|
||||
@@ -221,8 +219,6 @@ function SettingsPage() {
|
||||
const savedAiCustomSystemPrompt = await configService.getAiCustomSystemPrompt()
|
||||
const savedAiEnableThinking = await configService.getAiEnableThinking()
|
||||
const savedAiMessageLimit = await configService.getAiMessageLimit()
|
||||
const savedMcpEnabled = await configService.getMcpEnabled()
|
||||
const savedMcpExposeMediaPaths = await configService.getMcpExposeMediaPaths()
|
||||
|
||||
setAiProviderState(savedAiProvider)
|
||||
setAiApiKeyState(savedAiApiKey)
|
||||
@@ -233,8 +229,6 @@ function SettingsPage() {
|
||||
setAiCustomSystemPromptState(savedAiCustomSystemPrompt)
|
||||
setAiEnableThinkingState(savedAiEnableThinking)
|
||||
setAiMessageLimitState(savedAiMessageLimit)
|
||||
setMcpEnabledState(savedMcpEnabled)
|
||||
setMcpExposeMediaPathsState(savedMcpExposeMediaPaths)
|
||||
|
||||
// 加载关闭行为配置
|
||||
const savedCloseToTray = await configService.getCloseToTray()
|
||||
@@ -268,8 +262,6 @@ function SettingsPage() {
|
||||
aiCustomSystemPrompt: savedAiCustomSystemPrompt,
|
||||
aiEnableThinking: savedAiEnableThinking,
|
||||
aiMessageLimit: savedAiMessageLimit,
|
||||
mcpEnabled: savedMcpEnabled,
|
||||
mcpExposeMediaPaths: savedMcpExposeMediaPaths,
|
||||
closeToTray: savedCloseToTray
|
||||
})
|
||||
|
||||
@@ -318,8 +310,6 @@ function SettingsPage() {
|
||||
aiCustomSystemPrompt,
|
||||
aiEnableThinking,
|
||||
aiMessageLimit,
|
||||
mcpEnabled,
|
||||
mcpExposeMediaPaths,
|
||||
closeToTray
|
||||
}
|
||||
|
||||
@@ -333,7 +323,6 @@ function SettingsPage() {
|
||||
quoteStyle, exportDefaultDateRange, exportDefaultAvatars,
|
||||
aiProvider, aiApiKey, aiModel, aiDefaultTimeRange, aiSummaryDetail,
|
||||
aiSystemPromptPreset, aiCustomSystemPrompt, aiEnableThinking, aiMessageLimit,
|
||||
mcpEnabled, mcpExposeMediaPaths,
|
||||
closeToTray, initialConfig
|
||||
])
|
||||
|
||||
@@ -859,8 +848,6 @@ function SettingsPage() {
|
||||
await configService.setAiCustomSystemPrompt(aiCustomSystemPrompt)
|
||||
await configService.setAiEnableThinking(aiEnableThinking)
|
||||
await configService.setAiMessageLimit(aiMessageLimit)
|
||||
await configService.setMcpEnabled(mcpEnabled)
|
||||
await configService.setMcpExposeMediaPaths(mcpExposeMediaPaths)
|
||||
|
||||
// 保存关闭行为配置
|
||||
await configService.setCloseToTray(closeToTray)
|
||||
@@ -900,8 +887,6 @@ function SettingsPage() {
|
||||
aiCustomSystemPrompt,
|
||||
aiEnableThinking,
|
||||
aiMessageLimit,
|
||||
mcpEnabled,
|
||||
mcpExposeMediaPaths,
|
||||
closeToTray
|
||||
})
|
||||
setHasUnsavedChanges(false)
|
||||
@@ -2802,10 +2787,6 @@ function SettingsPage() {
|
||||
setEnableThinking={setAiEnableThinkingState}
|
||||
messageLimit={aiMessageLimit}
|
||||
setMessageLimit={setAiMessageLimitState}
|
||||
mcpEnabled={mcpEnabled}
|
||||
setMcpEnabled={setMcpEnabledState}
|
||||
mcpExposeMediaPaths={mcpExposeMediaPaths}
|
||||
setMcpExposeMediaPaths={setMcpExposeMediaPathsState}
|
||||
showMessage={showMessage}
|
||||
/>
|
||||
)}
|
||||
|
||||
Vendored
+6
@@ -75,6 +75,12 @@ export interface ElectronAPI {
|
||||
app: {
|
||||
getDownloadsPath: () => Promise<string>
|
||||
getVersion: () => Promise<string>
|
||||
getMcpLaunchConfig: () => Promise<{
|
||||
command: string
|
||||
args: string[]
|
||||
cwd: string
|
||||
mode: 'dev' | 'packaged'
|
||||
} | null>
|
||||
checkForUpdates: () => Promise<{ hasUpdate: boolean; version?: string; releaseNotes?: string }>
|
||||
downloadAndInstall: () => Promise<void>
|
||||
getStartupDbConnected?: () => Promise<boolean>
|
||||
|
||||
Reference in New Issue
Block a user