jan/web/screens/Settings/MCP/configuration.tsx
2025-05-15 17:10:55 +07:00

125 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect, useCallback, useRef } from 'react'
import { Button, TextArea } from '@janhq/joi'
import { useAtomValue } from 'jotai'
import { janDataFolderPathAtom } from '@/helpers/atoms/AppConfig.atom'
const MCPConfiguration = () => {
const janDataFolderPath = useAtomValue(janDataFolderPathAtom)
const [configContent, setConfigContent] = useState('')
const [isSaving, setIsSaving] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const textAreaRef = useRef<HTMLTextAreaElement>(null)
const readConfigFile = useCallback(async () => {
try {
// Read the file
const content = await window.core?.api.getMcpConfigs()
setConfigContent(content)
setError('')
} catch (err) {
console.error('Error reading config file:', err)
setError('Failed to read config file')
}
}, [janDataFolderPath])
useEffect(() => {
if (janDataFolderPath) {
readConfigFile()
}
}, [janDataFolderPath, readConfigFile])
const saveConfigFile = useCallback(async () => {
try {
setIsSaving(true)
setSuccess('')
setError('')
// Validate JSON
try {
JSON.parse(configContent)
} catch (err) {
setError('Invalid JSON format')
setIsSaving(false)
return
}
await window.core?.api?.saveMcpConfigs({ configs: configContent })
await window.core?.api?.restartMcpServers()
setSuccess('Config saved successfully')
setIsSaving(false)
} catch (err) {
console.error('Error saving config file:', err)
setError('Failed to save config file')
setIsSaving(false)
}
}, [janDataFolderPath, configContent])
return (
<>
{error && (
<div className="mb-4 rounded bg-[hsla(var(--destructive-bg))] px-4 py-3 text-[hsla(var(--destructive-fg))]">
{error}
</div>
)}
{success && (
<div className="mb-4 rounded bg-[hsla(var(--success-bg))] px-4 py-3 text-[hsla(var(--success-fg))]">
{success}
</div>
)}
<div className="mb-4 mt-2">
<label className="mb-2 block text-sm font-medium">
Configuration File (JSON)
</label>
<TextArea
ref={textAreaRef}
className="font-mono text-xs"
value={configContent}
rows={20}
onChange={(e) => {
const input = e.target
const start = input.selectionStart
const end = input.selectionEnd
// Sanitize quotes
const originalValue = input.value
const sanitizedValue = originalValue
.replace(/[“”]/g, '"')
.replace(/[]/g, "'")
// Update content
setConfigContent(sanitizedValue)
setSuccess('')
// Restore cursor after DOM updates
requestAnimationFrame(() => {
if (textAreaRef.current) {
const offset = sanitizedValue.length - originalValue.length
textAreaRef.current.selectionStart = start + offset
textAreaRef.current.selectionEnd = end + offset
}
})
}}
autoComplete="off"
autoCorrect="off"
spellCheck={false}
/>
</div>
<div className="flex justify-end">
<Button onClick={saveConfigFile} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save Config'}
</Button>
</div>
</>
)
}
export default MCPConfiguration