import { useState, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { Button } from '../../../components/ui/Button' import { TrashIcon, WifiIcon, WifiOffIcon, SpinnerIcon, StopIcon } from '../../../components/Icons' import { useServerStore, useIsMobile } from '../../../hooks' import { serviceStore, useServiceStore } from '../../../store/serviceStore' import { isTauri } from '../../../utils/tauri' import { apiErrorHandler } from '../../../utils' import { Toggle, SettingRow, SettingsCard } from './SettingsUI' export function ServiceSettings() { const { t } = useTranslation(['settings', 'common']) const isMobile = useIsMobile() const { autoStart: autoStartService, binaryPath, envVars, running: serviceRunning, startedByUs, starting: serviceStarting, } = useServiceStore() const { activeServer } = useServerStore() const isTauriDesktop = isTauri() && !isMobile // 本地编辑状态(debounce 保存) const [localBinaryPath, setLocalBinaryPath] = useState(binaryPath) const pathDebounceRef = useRef | null>(null) // 启动失败的错误信息 const [serviceError, setServiceError] = useState('') // 同步外部变化 useEffect(() => { setLocalBinaryPath(binaryPath) }, [binaryPath]) // 打开设置页时自动检测一次服务状态 useEffect(() => { if (!isTauriDesktop) return handleCheckService() // eslint-disable-next-line react-hooks/exhaustive-deps }, [isTauriDesktop]) const handleAutoStartToggle = () => { serviceStore.setAutoStart(!autoStartService) } const handleBinaryPathChange = (v: string) => { setLocalBinaryPath(v) if (pathDebounceRef.current) clearTimeout(pathDebounceRef.current) pathDebounceRef.current = setTimeout(() => serviceStore.setBinaryPath(v), 400) } const getServerUrl = () => activeServer?.url || 'http://127.0.0.1:4096' const handleStartService = async () => { setServiceError('') try { const { invoke } = await import('@tauri-apps/api/core') serviceStore.setStarting(true) const weStarted = await invoke('start_pi_service', { url: getServerUrl(), binaryPath: serviceStore.effectiveBinaryPath, envVars: serviceStore.envVarsRecord, }) serviceStore.setStartedByUs(weStarted) serviceStore.setRunning(true) } catch (e) { const msg = String(e) apiErrorHandler('start service', msg) setServiceError(msg) } finally { serviceStore.setStarting(false) } } const handleStopService = async () => { setServiceError('') try { const { invoke } = await import('@tauri-apps/api/core') await invoke('stop_pi_service') serviceStore.setStartedByUs(false) serviceStore.setRunning(false) } catch (e) { apiErrorHandler('stop service', e) } } const handleCheckService = async () => { try { const { invoke } = await import('@tauri-apps/api/core') const running = await invoke('check_pi_service', { url: getServerUrl() }) serviceStore.setRunning(running) if (running) { const byUs = await invoke('get_service_started_by_us') serviceStore.setStartedByUs(byUs) } else { serviceStore.setStartedByUs(false) } } catch (e) { apiErrorHandler('check service', e) } } if (!isTauriDesktop) { return (
{t('service.webModeDesc')}
) } return (
{t('service.binaryPath')}
handleBinaryPathChange(e.target.value)} placeholder={t('service.binaryPathPlaceholder')} className="w-full h-8 px-3 text-[length:var(--fs-md)] font-mono bg-bg-200/50 border border-border-200 rounded-md focus:outline-none focus:border-accent-main-100/50 text-text-100 placeholder:text-text-400" />
{t('service.binaryPathHelp')}
) : serviceRunning ? ( ) : ( ) } className="bg-bg-100/35 border-border-200/45" >
{!serviceStarting && !serviceRunning && ( )} {!serviceStarting && serviceRunning && startedByUs && ( )}
{t('service.envVars')}
{t('service.envVarsDesc')}
{envVars.length > 0 && (
{envVars.map((env, idx) => (
{ const updated = [...envVars] updated[idx] = { ...updated[idx], key: e.target.value } serviceStore.setEnvVars(updated) }} placeholder={t('service.keyPlaceholder')} className="w-[120px] shrink-0 h-7 px-2 text-[length:var(--fs-xs)] font-mono bg-bg-200/50 border border-border-200 rounded focus:outline-none focus:border-accent-main-100/50 text-text-100 placeholder:text-text-500" /> = { const updated = [...envVars] updated[idx] = { ...updated[idx], value: e.target.value } serviceStore.setEnvVars(updated) }} placeholder={t('service.valuePlaceholder')} className="flex-1 min-w-0 h-7 px-2 text-[length:var(--fs-xs)] font-mono bg-bg-200/50 border border-border-200 rounded focus:outline-none focus:border-accent-main-100/50 text-text-100 placeholder:text-text-500" />
))}
)}
{serviceError && (
{serviceError}
)}
) }