'use client' import { useEffect, useState, useCallback, ChangeEvent } from 'react' import { openExternalUrl, fs, AppConfiguration } from '@janhq/core' import { ScrollArea, Button, Switch, Input, Tooltip, Checkbox, useClickOutside, } from '@janhq/joi' import { useAtom, useAtomValue } from 'jotai' import { ChevronDownIcon } from 'lucide-react' import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react' import { twMerge } from 'tailwind-merge' import { snackbar, toaster } from '@/containers/Toast' import { useActiveModel } from '@/hooks/useActiveModel' import { useSettings } from '@/hooks/useSettings' import DataFolder from './DataFolder' import FactoryReset from './FactoryReset' import { experimentalFeatureEnabledAtom, ignoreSslAtom, proxyAtom, proxyEnabledAtom, vulkanEnabledAtom, quickAskEnabledAtom, } from '@/helpers/atoms/AppConfig.atom' type GPU = { id: string vram: number | null name: string } /** * Advanced Settings Screen * @returns */ const Advanced = () => { const [experimentalEnabled, setExperimentalEnabled] = useAtom( experimentalFeatureEnabledAtom ) const [vulkanEnabled, setVulkanEnabled] = useAtom(vulkanEnabledAtom) const [proxyEnabled, setProxyEnabled] = useAtom(proxyEnabledAtom) const quickAskEnabled = useAtomValue(quickAskEnabledAtom) const [proxy, setProxy] = useAtom(proxyAtom) const [ignoreSSL, setIgnoreSSL] = useAtom(ignoreSslAtom) const [partialProxy, setPartialProxy] = useState(proxy) const [gpuEnabled, setGpuEnabled] = useState(false) const [gpuList, setGpuList] = useState([]) const [gpusInUse, setGpusInUse] = useState([]) const [dropdownOptions, setDropdownOptions] = useState( null ) const [toggle, setToggle] = useState(null) const { readSettings, saveSettings } = useSettings() const { stopModel } = useActiveModel() const [open, setOpen] = useState(false) const selectedGpu = gpuList .filter((x) => gpusInUse.includes(x.id)) .map((y) => { return y['name'] }) /** * Handle proxy change */ const onProxyChange = useCallback( (event: ChangeEvent) => { const value = event.target.value || '' setPartialProxy(value) if (value.trim().startsWith('http')) { setProxy(value.trim()) } else { setProxy('') } }, [setPartialProxy, setProxy] ) /** * Update Quick Ask Enabled * @param e * @param relaunch * @returns void */ const updateQuickAskEnabled = async ( e: boolean, relaunch: boolean = true ) => { const appConfiguration: AppConfiguration = await window.core?.api?.getAppConfigurations() appConfiguration.quick_ask = e await window.core?.api?.updateAppConfiguration(appConfiguration) if (relaunch) window.core?.api?.relaunch() } /** * Update Vulkan Enabled * @param e * @param relaunch * @returns void */ const updateVulkanEnabled = async (e: boolean, relaunch: boolean = true) => { toaster({ title: 'Reload', description: 'Vulkan settings updated. Reload now to apply the changes.', }) stopModel() setVulkanEnabled(e) await saveSettings({ vulkan: e, gpusInUse: [] }) // Relaunch to apply settings if (relaunch) window.location.reload() } /** * Update Experimental Enabled * @param e * @returns */ const updateExperimentalEnabled = async ( e: ChangeEvent ) => { setExperimentalEnabled(e.target.checked) // If it checked, we don't need to do anything else // Otherwise have to reset other settings if (e.target.checked) return // It affects other settings, so we need to reset them const isRelaunch = quickAskEnabled || vulkanEnabled if (quickAskEnabled) await updateQuickAskEnabled(false, false) if (vulkanEnabled) await updateVulkanEnabled(false, false) if (isRelaunch) window.core?.api?.relaunch() } /** * useEffect to set GPU enabled if possible */ useEffect(() => { const setUseGpuIfPossible = async () => { const settings = await readSettings() setGpuEnabled(settings.run_mode === 'gpu' && settings.gpus?.length > 0) setGpusInUse(settings.gpus_in_use || []) setVulkanEnabled(settings.vulkan || false) if (settings.gpus) { setGpuList(settings.gpus) } } setUseGpuIfPossible() }, [readSettings, setGpuList, setGpuEnabled, setGpusInUse, setVulkanEnabled]) /** * Clear logs * @returns */ const clearLogs = async () => { try { await fs.rm(`file://logs`) } catch (err) { console.error('Error clearing logs: ', err) } toaster({ title: 'Logs cleared', description: 'All logs have been cleared.', type: 'success', }) } /** * Handle GPU Change * @param gpuId * @returns */ const handleGPUChange = (gpuId: string) => { let updatedGpusInUse = [...gpusInUse] if (updatedGpusInUse.includes(gpuId)) { updatedGpusInUse = updatedGpusInUse.filter((id) => id !== gpuId) if (gpuEnabled && updatedGpusInUse.length === 0) { // Vulkan support only allow 1 active device at a time if (vulkanEnabled) { updatedGpusInUse = [] } updatedGpusInUse.push(gpuId) } } else { // Vulkan support only allow 1 active device at a time if (vulkanEnabled) { updatedGpusInUse = [] } updatedGpusInUse.push(gpuId) } setGpusInUse(updatedGpusInUse) saveSettings({ gpusInUse: updatedGpusInUse }) window.core?.api?.relaunch() } const gpuSelectionPlaceHolder = gpuList.length > 0 ? 'Select GPU' : "You don't have any compatible GPU" /** * Handle click outside */ useClickOutside(() => setOpen(false), null, [dropdownOptions, toggle]) return (
{/* Experimental */}
Experimental Mode

Enable new features that may be unstable.

{/* CPU / GPU switching */} {!isMac && (
GPU Acceleration

Enable to enhance model performance by utilizing your GPU devices for acceleration. Read{' '} {' '} openExternalUrl( 'https://jan.ai/guides/troubleshooting/gpu-not-used/' ) } > troubleshooting guide {' '} {' '} for further assistance.

{gpuList.length > 0 && !gpuEnabled && ( } content="Disabling NVIDIA GPU Acceleration may result in reduced performance. It is recommended to keep this enabled for optimal user experience." /> )} { if (e.target.checked === true) { saveSettings({ runMode: 'gpu' }) setGpuEnabled(true) snackbar({ description: 'Successfully turned on GPU Acceleration', type: 'success', }) } else { saveSettings({ runMode: 'cpu' }) setGpuEnabled(false) snackbar({ description: 'Successfully turned off GPU Acceleration', type: 'success', }) } // Stop any running model to apply the changes if (e.target.checked !== gpuEnabled) stopModel() }} /> } content="Your current device does not have a compatible GPU for monitoring. To enable GPU monitoring, please ensure your device has a supported Nvidia or AMD GPU with updated drivers." disabled={gpuList.length > 0} />
} onClick={() => setOpen(!open)} /> {gpuList.length > 0 && (

{vulkanEnabled ? 'Vulkan Supported GPUs' : 'Nvidia'}

{gpuList .filter((gpu) => vulkanEnabled ? gpu.name : gpu.name?.toLowerCase().includes('nvidia') ) .map((gpu) => (
handleGPUChange(gpu.id)} label={ {gpu.name} {!vulkanEnabled && ( {gpu.vram}MB VRAM )} } />
))}
{gpuEnabled && gpusInUse.length > 1 && (

If multi-GPU is enabled with different GPU models or without NVLink, it could impact token speed.

)}
)}
)} {/* Vulkan for AMD GPU/ APU and Intel Arc GPU */} {!isMac && experimentalEnabled && (
Vulkan Support

Enable Vulkan with AMD GPU/APU and Intel Arc GPU for better model performance (reload needed).

updateVulkanEnabled(e.target.checked)} />
)} {/* Proxy */}
HTTPS Proxy

Specify the HTTPS proxy or leave blank (proxy auto-configuration and SOCKS not supported).

setProxyEnabled(!proxyEnabled)} />
:@:'} value={partialProxy} onChange={onProxyChange} />
{/* Ignore SSL certificates */}
Ignore SSL certificates

Allow self-signed or unverified certificates - may be required for certain proxies.

setIgnoreSSL(e.target.checked)} />
{experimentalEnabled && (
Jan Quick Ask

Enable Quick Ask to be triggered via the default hotkey{' '} {isMac ? '⌘' : 'Ctrl'} + J {' '} (reload needed).

{ toaster({ title: 'Reload', description: 'Quick Ask settings updated. Reload now to apply the changes.', }) updateQuickAskEnabled(!quickAskEnabled) }} />
)} {/* Clear log */}
Clear logs

Clear all logs from Jan app.

{/* Factory Reset */}
) } export default Advanced