import { useCallback, useRef, useState, useEffect } from 'react'; import { type AppFontSize, type FontFamily, type ListRowDensity, type SidebarPosition, type WindowControlStyle, type SettingsState, SettingsTab, runSettingsPersistenceTransaction, useSettingsStore } from '../store/useSettingsStore'; import { Download, Palette, Globe, Folder, Key, Moon, Terminal, Puzzle, Info, Plus, Trash2, Copy, RefreshCw, Code, ShieldAlert, Check, ExternalLink, BadgeCheck, AlertCircle } from 'lucide-react'; import { open } from '@tauri-apps/plugin-dialog'; import { getVersion } from '@tauri-apps/api/app'; import { openUrl } from '@tauri-apps/plugin-opener'; import { invokeCommand as invoke } from '../ipc'; import { useToast, ToastVariant } from '../contexts/ToastContext'; import type { EngineStatusItem } from '../bindings/EngineStatusItem'; import { WindowDragRegion } from './WindowDragRegion'; import appIcon from '../assets/app-icon.png'; import { DEFAULT_CATEGORY_SUBFOLDERS, DOWNLOAD_CATEGORIES, formatDerivedCategoryPath, normalizeCategorySubfolder, subfolderFromDerivedCategoryPath } from '../utils/downloadLocations'; import { usePlatformInfo } from '../utils/platform'; import { isTrustedFirelinkReleaseUrl } from '../utils/releaseUrls'; import { normalizeCustomProxy } from '../store/useDownloadStore'; import { useTranslation } from 'react-i18next'; import { localeDirection, resolveAppLocale } from '../i18n'; const settingsTabs: { type: SettingsTab; icon: typeof Download }[] = [ { type: 'downloads', icon: Download }, { type: 'lookandfeel', icon: Palette }, { type: 'network', icon: Globe }, { type: 'locations', icon: Folder }, { type: 'sitelogins', icon: Key }, { type: 'power', icon: Moon }, { type: 'engine', icon: Terminal }, { type: 'integrations', icon: Puzzle }, { type: 'about', icon: Info }, ]; const engineChecks = [ { kind: 'aria2', name: 'Aria2', command: 'get_aria2_engine_status' }, { kind: 'ytdlp', name: 'yt-dlp', command: 'get_ytdlp_engine_status' }, { kind: 'ffmpeg', name: 'FFmpeg', command: 'get_ffmpeg_engine_status' }, { kind: 'deno', name: 'Deno', command: 'get_deno_engine_status' }, ] as const; const browserCookieSourceOptions = [ { value: 'none', label: 'None' }, { value: 'safari', label: 'Safari' }, { value: 'chrome', label: 'Chrome' }, { value: 'chromium', label: 'Chromium' }, { value: 'firefox', label: 'Firefox' }, { value: 'edge', label: 'Edge' }, { value: 'brave', label: 'Brave' }, { value: 'opera', label: 'Opera' }, { value: 'vivaldi', label: 'Vivaldi' }, { value: 'whale', label: 'Whale' }, ] as const; type EngineCheck = typeof engineChecks[number]; const FIRELINK_SOURCE_URL = 'https://github.com/nimbold/Firelink'; const FIRELINK_LICENSE_URL = 'https://github.com/nimbold/Firelink/blob/main/LICENSE'; const FIRELINK_FIREFOX_ADDON_URL = 'https://addons.mozilla.org/en-US/firefox/addon/firelink-companion/'; const FIRELINK_EXTENSION_RELEASES_URL = 'https://github.com/nimbold/Firelink-Extension/releases'; type ManualUpdateStatus = | { type: 'idle' } | { type: 'checking' } | { type: 'up-to-date'; latestVersion: string; localVersion: string } | { type: 'update-available'; version: string; releaseUrl: string } | { type: 'error'; message: string }; type SystemProxyStatus = 'idle' | 'checking' | 'detected' | 'none' | 'error'; const engineStatusCache = new Map(); const engineStatusInFlight = new Map>(); const upsertEngineStatus = (items: EngineStatusItem[], item: EngineStatusItem) => { const next = items.filter(existing => existing.kind !== item.kind); next.push(item); return next; }; const commitBoundedIntegerInput = ( raw: string, fallback: number, min: number, max: number, setValue: (value: number) => void, setDraft: (value: string) => void ) => { const parsed = Number(raw); const next = Number.isFinite(parsed) ? Math.min(max, Math.max(min, Math.trunc(parsed))) : fallback; setValue(next); setDraft(String(next)); }; const USER_AGENT_SUGGESTIONS = [ { label: 'Chrome (Windows)', detail: 'Windows desktop', value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36' }, { label: 'Chrome (macOS)', detail: 'macOS desktop', value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_7_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36' }, { label: 'Edge (Windows)', detail: 'Windows desktop', value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0' }, { label: 'Firefox (Windows)', detail: 'Windows desktop', value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0) Gecko/20100101 Firefox/152.0' }, { label: 'Firefox (macOS)', detail: 'macOS desktop', value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) Gecko/20100101 Firefox/152.0' }, { label: 'Safari (macOS)', detail: 'macOS desktop', value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_7_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15' } ] as const; const buildEngineStatusError = (check: EngineCheck, error: unknown): EngineStatusItem => ({ name: check.name, kind: check.kind, expected_sidecar: '', resolved_path: null, version: null, ready: false, error: String(error), stderr_tail: null, remediation_hint: null, rpc_port: null, daemon_alive: null, rpc_ready: null, last_stderr_tail: null, expects_internal_dir: null, has_internal_dir: null, has_python_framework: null, }); const runEngineStatusCheck = (check: EngineCheck, force: boolean) => { if (!force) { const cached = engineStatusCache.get(check.kind); if (cached) return Promise.resolve(cached); } if (!force) { const inFlight = engineStatusInFlight.get(check.kind); if (inFlight) return inFlight; } if (force) engineStatusCache.delete(check.kind); const promise = invoke(check.command) .then(item => { if (item.ready) engineStatusCache.set(item.kind, item); return item; }) .catch(error => buildEngineStatusError(check, error)) .finally(() => { if (engineStatusInFlight.get(check.kind) === promise) { engineStatusInFlight.delete(check.kind); } }); engineStatusInFlight.set(check.kind, promise); return promise; }; const CategoryFolderInput = ({ category, categoryLabel, settings, onBrowse, disabled = false }: { category: string; categoryLabel: string; settings: SettingsState; onBrowse: () => void; disabled?: boolean; }) => { const { t } = useTranslation(); const base = settings.baseDownloadFolder || '~/Downloads'; const sub = Object.prototype.hasOwnProperty.call(settings.categorySubfolders, category) ? settings.categorySubfolders[category] : DEFAULT_CATEGORY_SUBFOLDERS[category as keyof typeof DEFAULT_CATEGORY_SUBFOLDERS]; const override = settings.categoryDirectoryOverrides[category]; const displayPath = override ?? formatDerivedCategoryPath(base, sub); const [localValue, setLocalValue] = useState(null); const value = localValue !== null ? localValue : displayPath; return (
setLocalValue(displayPath)} onChange={(e) => setLocalValue(e.target.value)} onBlur={() => { const val = localValue ?? displayPath; const derivedSubfolder = subfolderFromDerivedCategoryPath(val, base); if (!val.trim()) { settings.setCategoryDirectoryOverride(category, undefined); settings.setCategorySubfolder(category, ''); } else if (derivedSubfolder !== null) { settings.setCategoryDirectoryOverride(category, undefined); settings.setCategorySubfolder( category, normalizeCategorySubfolder( derivedSubfolder, DEFAULT_CATEGORY_SUBFOLDERS[category as keyof typeof DEFAULT_CATEGORY_SUBFOLDERS] ) ); } else { settings.setCategoryDirectoryOverride(category, val.trim()); } setLocalValue(null); }} className="app-control flex-1 max-w-[280px] text-[12px] px-3 py-1.5 bg-surface-overlay/50 border-border-color/50 focus:border-accent-color focus:bg-surface-overlay disabled:opacity-60 disabled:cursor-not-allowed" aria-label={t($ => $.settings.locations.categorySubfolder, { category: categoryLabel })} /> {override && ( )}
); }; export default function SettingsView() { const { i18n, t } = useTranslation(); const settings = useSettingsStore(); const activeTab = settings.activeSettingsTab; const isRtl = localeDirection(resolveAppLocale(i18n.language)) === 'rtl'; const isSidebarOnRight = settings.sidebarPosition === 'right' || (settings.sidebarPosition === 'auto' && isRtl); const platform = usePlatformInfo(); const platformName = platform.os === 'macos' ? 'macOS' : platform.os === 'windows' ? 'Windows' : platform.os === 'linux' ? 'Linux' : t($ => $.settings.common.thisOs); const trayIconLabel = platform.os === 'macos' ? t($ => $.settings.lookAndFeel.menuBarIcon) : platform.os === 'linux' ? t($ => $.settings.lookAndFeel.statusIndicatorIcon) : t($ => $.settings.lookAndFeel.systemTrayIcon); const trayIconDescription = platform.os === 'macos' ? t($ => $.settings.lookAndFeel.macosTrayDescription) : platform.os === 'windows' ? t($ => $.settings.lookAndFeel.windowsTrayDescription) : platform.os === 'linux' ? t($ => $.settings.lookAndFeel.linuxTrayDescription) : t($ => $.settings.lookAndFeel.defaultTrayDescription); const userAgentMenuRef = useRef(null); const [isUserAgentMenuOpen, setIsUserAgentMenuOpen] = useState(false); // Local state for engine status const [engineStatus, setEngineStatus] = useState(null); const [expandedEngine, setExpandedEngine] = useState(null); const [isRecheckingEngines, setIsRecheckingEngines] = useState(false); const engineRunId = useRef(0); const [appVersion, setAppVersion] = useState(''); const [extensionServerPort, setExtensionServerPort] = useState(null); const [systemProxyStatus, setSystemProxyStatus] = useState('idle'); const [perServerConnectionsInput, setPerServerConnectionsInput] = useState( () => String(settings.perServerConnections) ); const [maxConcurrentDownloadsInput, setMaxConcurrentDownloadsInput] = useState( () => String(settings.maxConcurrentDownloads) ); const [proxyPortInput, setProxyPortInput] = useState(() => String(settings.proxyPort)); useEffect(() => { setPerServerConnectionsInput(String(settings.perServerConnections)); }, [settings.perServerConnections]); useEffect(() => { setMaxConcurrentDownloadsInput(String(settings.maxConcurrentDownloads)); }, [settings.maxConcurrentDownloads]); useEffect(() => { setProxyPortInput(String(settings.proxyPort)); }, [settings.proxyPort]); // Local state for adding site login const [loginPattern, setLoginPattern] = useState(''); const [loginUser, setLoginUser] = useState(''); const [loginPass, setLoginPass] = useState(''); const [loginError, setLoginError] = useState(''); const [isSavingLogin, setIsSavingLogin] = useState(false); const saveLoginInFlight = useRef(false); const [loginFieldErrors, setLoginFieldErrors] = useState<{ pattern?: string; username?: string; password?: string; }>({}); // Toast notifications const { addToast } = useToast(); const [isCheckingForUpdates, setIsCheckingForUpdates] = useState(false); const [manualUpdateStatus, setManualUpdateStatus] = useState({ type: 'idle' }); useEffect(() => { getVersion().then(setAppVersion).catch(() => undefined); }, []); useEffect(() => { if (settings.activeView !== 'settings' || activeTab !== 'integrations') return; let active = true; const refresh = () => { invoke('get_extension_server_port') .then(port => { if (active) setExtensionServerPort(port); }) .catch(() => { if (active) setExtensionServerPort(null); }); }; refresh(); const timer = window.setInterval(refresh, 3000); return () => { active = false; window.clearInterval(timer); }; }, [settings.activeView, activeTab]); useEffect(() => { if (settings.activeView !== 'settings' || activeTab !== 'network' || settings.proxyMode !== 'system') { setSystemProxyStatus('idle'); return; } let active = true; setSystemProxyStatus('checking'); invoke('get_system_proxy') .then(proxy => { if (active) setSystemProxyStatus(typeof proxy === 'string' && proxy.trim() ? 'detected' : 'none'); }) .catch(() => { if (active) setSystemProxyStatus('error'); }); return () => { active = false; }; }, [settings.activeView, settings.proxyMode, activeTab]); const runEngineChecks = useCallback((force = false) => { const runId = ++engineRunId.current; const cached = engineChecks .map(check => engineStatusCache.get(check.kind)) .filter((item): item is EngineStatusItem => Boolean(item)); setEngineStatus(force ? [] : cached); setExpandedEngine(null); const checksToRun = force ? engineChecks : engineChecks.filter(check => !engineStatusCache.has(check.kind)); if (checksToRun.length === 0) { setIsRecheckingEngines(false); return; } setIsRecheckingEngines(true); let remaining = checksToRun.length; checksToRun.forEach(check => { runEngineStatusCheck(check, force) .then(item => { if (engineRunId.current === runId) { setEngineStatus(current => upsertEngineStatus(current ?? [], item)); } }) .finally(() => { remaining -= 1; if (remaining === 0 && engineRunId.current === runId) { setIsRecheckingEngines(false); } }); }); }, []); // Fetch engine status when Engine tab is opened useEffect(() => { if (settings.activeView === 'settings' && activeTab === 'engine') { runEngineChecks(false); } }, [settings.activeView, activeTab, runEngineChecks]); const showToast = (msg: string, variant: ToastVariant = 'info') => { addToast({ message: msg, variant }); }; const openExternalUrl = async (url: string, label: string) => { try { await openUrl(url); } catch (error) { showToast(t($ => $.settings.common.externalOpenFailed, { label, detail: String(error) }), 'error'); } }; const findEngine = (kind: string) => engineStatus?.find(e => e.kind === kind) ?? null; const renderEngineStatus = (item: EngineStatusItem | null) => { if (!item) return {t($ => $.settings.common.checking)}; if (item.ready) return {t($ => $.settings.common.ready)}; return {t($ => $.settings.common.errorMissing)}; }; const renderEngineVersion = (item: EngineStatusItem | null) => { if (!item) return t($ => $.settings.common.checking); if (item.version) return item.version; if (item.error) return `${t($ => $.settings.common.error)}: ${item.error.length > 80 ? item.error.substring(0, 80) + '…' : item.error}`; return t($ => $.addDownloads.unknown); }; const renderEngineDetails = (item: EngineStatusItem | null) => { if (!item || item.ready || (!item.error && !item.remediation_hint && !item.stderr_tail)) return null; const isExpanded = expandedEngine === item.kind; return ( <> {isExpanded && (
{item.resolved_path &&

{t($ => $.settings.common.binary)}: {item.resolved_path}

} {item.expected_sidecar &&

{t($ => $.settings.common.expected)}: {item.expected_sidecar}

} {item.error &&

{t($ => $.settings.common.error)}: {item.error}

} {item.remediation_hint &&

{t($ => $.settings.common.tip)}: {item.remediation_hint}

} {item.stderr_tail &&
{t($ => $.settings.common.stderr)}
{item.stderr_tail}
} {item.daemon_alive != null &&

{t($ => $.settings.common.daemonProcessAlive)}: {String(item.daemon_alive)}

} {item.rpc_ready != null &&

{t($ => $.settings.common.rpcReady)}: {String(item.rpc_ready)}

} {item.rpc_port != null &&

{t($ => $.settings.common.rpcPort)}: {item.rpc_port}

} {item.last_stderr_tail &&
{t($ => $.settings.common.daemonStderr)}
{item.last_stderr_tail}
} {item.expects_internal_dir === true &&

{t($ => $.settings.common.packaging)}

} {item.has_internal_dir === true &&

{t($ => $.settings.common.internalDirectoryFound)}: true

} {item.has_python_framework != null &&

{t($ => $.settings.common.pythonRuntimeFound)}: {String(item.has_python_framework)}

}
)} ); }; const handleCheckForUpdates = async () => { if (isCheckingForUpdates) return; setIsCheckingForUpdates(true); setManualUpdateStatus({ type: 'checking' }); try { const result = await invoke('check_for_updates'); if (result.type === 'UpToDate') { setManualUpdateStatus({ type: 'up-to-date', latestVersion: result.latest_version, localVersion: result.local_version }); } else if (result.type === 'UpdateAvailable') { if (!isTrustedFirelinkReleaseUrl(result.update.release_url)) { throw new Error(t($ => $.settings.common.updateUntrustedReleaseUrl)); } setManualUpdateStatus({ type: 'update-available', version: result.update.version, releaseUrl: result.update.release_url }); } else { setManualUpdateStatus({ type: 'error', message: t($ => $.settings.common.updateUnexpectedResponse) }); } } catch (error) { setManualUpdateStatus({ type: 'error', message: t($ => $.settings.common.updateFailed, { detail: String(error) }) }); } finally { setIsCheckingForUpdates(false); } }; const handleBrowseCategory = async (category: string) => { const currentPath = settings.categoryDirectoryOverrides[category] || settings.baseDownloadFolder; try { const selected = await open({ directory: true, multiple: false, defaultPath: currentPath.startsWith('~') ? undefined : currentPath }); if (selected && typeof selected === 'string') { const approvedPath = await settings.approveDownloadRoot(selected); settings.setCategoryDirectoryOverride(category, approvedPath); } } catch (e) { console.error(`Failed to select folder for ${category}:`, e); } }; const handleBrowseBase = async () => { try { const base = await open({ directory: true, multiple: false, defaultPath: settings.baseDownloadFolder.startsWith('~') ? undefined : settings.baseDownloadFolder }); if (base && typeof base === 'string') { const approvedBase = await settings.approveDownloadRoot(base); try { if (settings.categorySubfoldersEnabled) { const safeSubfolders = Object.fromEntries( DOWNLOAD_CATEGORIES.map(category => [ category, normalizeCategorySubfolder( Object.prototype.hasOwnProperty.call(settings.categorySubfolders, category) ? settings.categorySubfolders[category] : DEFAULT_CATEGORY_SUBFOLDERS[category], DEFAULT_CATEGORY_SUBFOLDERS[category] ) ]) ); await invoke('create_category_directories', { baseFolder: approvedBase, subfolders: safeSubfolders }); } } catch (e) { console.error("Failed to create directories on disk:", e); showToast(t($ => $.settings.locations.baseFolderCreateFailed, { detail: String(e) }), 'warning'); return; } settings.setBaseDownloadFolder(approvedBase); showToast(t($ => $.settings.locations.baseFolderUpdated), 'success'); } } catch (e) { console.error("Failed to browse base path:", e); } }; const handleAddLogin = async () => { if (saveLoginInFlight.current) return; const fieldErrors: typeof loginFieldErrors = {}; if (!loginPattern.trim()) { fieldErrors.pattern = t($ => $.settings.siteLogins.urlPatternRequired); } else if (/\s/.test(loginPattern.trim())) { fieldErrors.pattern = t($ => $.settings.siteLogins.urlPatternWhitespace); } if (!loginUser.trim()) { fieldErrors.username = t($ => $.settings.siteLogins.usernameRequired); } if (!loginPass) { fieldErrors.password = t($ => $.settings.siteLogins.passwordRequired); } if (Object.keys(fieldErrors).length > 0) { setLoginFieldErrors(fieldErrors); setLoginError(''); return; } setLoginFieldErrors({}); const id = crypto.randomUUID(); if (!settings.keychainAccessReady) { settings.setShowKeychainModal(true); setLoginError(t($ => $.settings.siteLogins.accessBeforeSave)); return; } saveLoginInFlight.current = true; setIsSavingLogin(true); try { await runSettingsPersistenceTransaction(async () => { await invoke('save_site_login', { id, urlPattern: loginPattern.trim(), username: loginUser.trim(), password: loginPass }); settings.addSiteLogin({ id, urlPattern: loginPattern.trim(), username: loginUser.trim() }); }); } catch (e) { console.error("Failed to save site login:", e); setLoginError(t($ => $.settings.siteLogins.saveFailed)); return; } finally { saveLoginInFlight.current = false; setIsSavingLogin(false); } setLoginPattern(''); setLoginUser(''); setLoginPass(''); setLoginError(''); showToast(t($ => $.settings.siteLogins.addedCredential), 'success'); }; const copyToken = async () => { try { await navigator.clipboard.writeText(settings.extensionPairingToken); showToast(t($ => $.settings.integrations.tokenCopied), 'success'); } catch (error) { showToast(t($ => $.settings.integrations.tokenCopyFailed, { detail: String(error) }), 'error'); } }; const tabLabels: Record = { downloads: t($ => $.settings.tabs.downloads), lookandfeel: t($ => $.settings.tabs.lookAndFeel), network: t($ => $.settings.tabs.network), locations: t($ => $.settings.tabs.locations), sitelogins: t($ => $.settings.tabs.siteLogins), power: t($ => $.settings.tabs.power), engine: t($ => $.settings.tabs.engine), integrations: t($ => $.settings.tabs.integrations), about: t($ => $.settings.tabs.about), }; const activeTabLabel = tabLabels[activeTab] ?? t($ => $.settings.tabs.downloads); const categoryLabel = (category: string) => { switch (category) { case 'Musics': return t($ => $.navigation.categories.musics); case 'Movies': return t($ => $.navigation.categories.movies); case 'Compressed': return t($ => $.navigation.categories.compressed); case 'Documents': return t($ => $.navigation.categories.documents); case 'Pictures': return t($ => $.navigation.categories.pictures); case 'Applications': return t($ => $.navigation.categories.applications); default: return t($ => $.navigation.categories.other); } }; const languageOptions = [ { value: 'system', label: t($ => $.settings.lookAndFeel.languageSystem) }, { value: 'en', label: t($ => $.settings.lookAndFeel.languageEnglish) }, { value: 'zh-CN', label: t($ => $.settings.lookAndFeel.languageChinese) }, { value: 'he', label: t($ => $.settings.lookAndFeel.languageHebrew) }, { value: 'fa', label: t($ => $.settings.lookAndFeel.languagePersian) }, { value: 'uk', label: t($ => $.settings.lookAndFeel.languageUkrainian) }, { value: 'ru', label: t($ => $.settings.lookAndFeel.languageRussian) }, ] as const; const calendarOptions = [ { value: 'gregorian', label: t($ => $.settings.lookAndFeel.calendarGregorian) }, { value: 'persian', label: t($ => $.settings.lookAndFeel.calendarPersian) }, { value: 'hebrew', label: t($ => $.settings.lookAndFeel.calendarHebrew) }, ] as const; const windowControlStyleOptions: Array<{ value: WindowControlStyle; label: string }> = [ { value: 'auto', label: t($ => $.settings.lookAndFeel.windowControlStyleAutomatic) }, { value: 'macos', label: t($ => $.settings.lookAndFeel.windowControlStyleMacos) }, { value: 'windows', label: t($ => $.settings.lookAndFeel.windowControlStyleWindows) }, { value: 'gnome', label: t($ => $.settings.lookAndFeel.windowControlStyleGnome) }, { value: 'minimal', label: t($ => $.settings.lookAndFeel.windowControlStyleMinimal) }, ]; const fontFamilyOptions: Array<{ value: FontFamily; label: string }> = [ { value: 'system', label: t($ => $.settings.lookAndFeel.fontFamilySystem) }, { value: 'inter', label: t($ => $.settings.lookAndFeel.fontFamilyInter) }, { value: 'outfit', label: t($ => $.settings.lookAndFeel.fontFamilyOutfit) }, { value: 'vazirmatn', label: t($ => $.settings.lookAndFeel.fontFamilyVazirmatn) }, { value: 'noto-sans-hebrew', label: t($ => $.settings.lookAndFeel.fontFamilyNotoSansHebrew) }, { value: 'noto-sans-sc', label: t($ => $.settings.lookAndFeel.fontFamilyNotoSansSc) }, { value: 'roboto', label: t($ => $.settings.lookAndFeel.fontFamilyRoboto) }, { value: 'serif', label: t($ => $.settings.lookAndFeel.fontFamilySerif) }, { value: 'monospace', label: t($ => $.settings.lookAndFeel.fontFamilyMonospace) }, ]; const TabButton = ({ type, icon: Icon, label }: { type: SettingsTab; icon: typeof Download; label: string }) => { const active = activeTab === type; return ( ); }; return (
{/* SwiftUI SettingsPaneContainer-style horizontal tab strip */}
{settingsTabs.map(tab => ( ))}
{/* Content Area */}

{activeTabLabel}

{/* Downloads Pane */} {activeTab === 'downloads' && (
{t($ => $.settings.downloads.defaultConnections)} {t($ => $.settings.downloads.defaultConnectionsDescription)}
{ const value = e.target.value; setPerServerConnectionsInput(value); if (value !== '' && Number.isFinite(Number(value))) { settings.setPerServerConnections(Number(value)); } }} onBlur={(e) => commitBoundedIntegerInput( e.target.value, settings.perServerConnections, 1, 16, settings.setPerServerConnections, setPerServerConnectionsInput )} className="app-control w-24 text-center" />
{t($ => $.settings.downloads.parallelDownloads)} {t($ => $.settings.downloads.parallelDownloadsDescription)}
{ const value = e.target.value; setMaxConcurrentDownloadsInput(value); if (value !== '' && Number.isFinite(Number(value))) { settings.setMaxConcurrentDownloads(Number(value)); } }} onBlur={(e) => commitBoundedIntegerInput( e.target.value, settings.maxConcurrentDownloads, 1, 12, settings.setMaxConcurrentDownloads, setMaxConcurrentDownloadsInput )} className="app-control w-24 text-center" />
{t($ => $.settings.downloads.automaticRetries)} {t($ => $.settings.downloads.automaticRetriesDescription)}
settings.setMaxAutomaticRetries(Number(e.target.value))} onBlur={(e) => { const val = Number(e.target.value); if (val < 0) settings.setMaxAutomaticRetries(0); if (val > 10) settings.setMaxAutomaticRetries(10); }} className="app-control w-24 text-center" />
)} {/* Look & Feel Pane */} {activeTab === 'lookandfeel' && (

{t($ => $.settings.lookAndFeel.appTheme)}

{t($ => $.settings.lookAndFeel.theme)}
$.settings.lookAndFeel.ariaLabel)}> {[ { value: 'system', label: t($ => $.settings.lookAndFeel.system), colors: ['#f4f4f5', '#252525'] }, { value: 'light', label: t($ => $.settings.lookAndFeel.light), colors: ['#ffffff', '#e9e9ec'] }, { value: 'dark', label: t($ => $.settings.lookAndFeel.dark), colors: ['#1a1a1a', '#292929'] }, { value: 'dracula', label: t($ => $.settings.lookAndFeel.dracula), colors: ['#282a36', '#ff79c6'] }, { value: 'nord', label: t($ => $.settings.lookAndFeel.nord), colors: ['#2e3440', '#88c0d0'] }, ].map(({ value, label, colors }) => ( ))}

{t($ => $.settings.lookAndFeel.systemAppearance, { platform: platformName })}

{t($ => $.settings.lookAndFeel.display)}

{t($ => $.settings.lookAndFeel.fontFamily)} {t($ => $.settings.lookAndFeel.fontFamilyDescription)}
{t($ => $.settings.lookAndFeel.fontSize)} {t($ => $.settings.lookAndFeel.fontSizeDescription)}
{t($ => $.settings.lookAndFeel.listDensity)} {t($ => $.settings.lookAndFeel.listDensityDescription)}

{t($ => $.settings.lookAndFeel.languageAndRegion)}

{t($ => $.settings.lookAndFeel.language)} {t($ => $.settings.lookAndFeel.languageDescription)}
{t($ => $.settings.lookAndFeel.calendar)} {t($ => $.settings.lookAndFeel.calendarDescription)}

{t($ => $.settings.lookAndFeel.windowControls)}

{t($ => $.settings.lookAndFeel.windowControlStyle)} {t($ => $.settings.lookAndFeel.windowControlsDescription)}

{t($ => $.settings.lookAndFeel.layout)}

{t($ => $.settings.lookAndFeel.sidebarPosition)} {t($ => $.settings.lookAndFeel.sidebarPositionDescription)}

{t($ => $.settings.lookAndFeel.osIntegration)}

{platform.os === 'macos' && ( )}
)} {/* Network Pane */} {activeTab === 'network' && (

{t($ => $.settings.network.proxy)}

{t($ => $.settings.network.mode)} {t($ => $.settings.network.modeDescription)}
{[ ['none', t($ => $.settings.network.noProxy)], ['system', t($ => $.settings.network.systemProxy)], ['custom', t($ => $.settings.network.customProxy)], ].map(([value, label]) => ( ))}
{settings.proxyMode === 'custom' && ( <>
{t($ => $.settings.network.proxyHost)} {t($ => $.settings.network.proxyHostDescription)}
settings.setProxyHost(e.target.value)} placeholder={t($ => $.settings.network.proxyHostPlaceholder)} className="app-control settings-network-input font-mono" />
{t($ => $.settings.network.proxyPort)} {t($ => $.settings.network.proxyPortDescription)}
{ const value = e.target.value; setProxyPortInput(value); if (value !== '' && Number.isFinite(Number(value))) { settings.setProxyPort(Number(value)); } }} onBlur={(e) => commitBoundedIntegerInput( e.target.value, settings.proxyPort, 1, 65535, settings.setProxyPort, setProxyPortInput )} className="app-control settings-port-input text-center" />
)}

{settings.proxyMode === 'none' && t($ => $.settings.network.noProxyDescription)} {settings.proxyMode === 'system' && t($ => $.settings.network.systemProxyDescription, { platform: platform.os === 'macos' ? 'macOS' : platform.os === 'windows' ? 'Windows' : 'desktop' })} {settings.proxyMode === 'custom' && (normalizeCustomProxy(settings.proxyHost, settings.proxyPort) ? t($ => $.settings.network.customProxyDescription) : settings.proxyHost ? t($ => $.settings.network.invalidCustomProxy) : t($ => $.settings.network.incompleteCustomProxy))}

{settings.proxyMode === 'system' && (

{systemProxyStatus === 'checking' && t($ => $.settings.network.checkingSystemProxy)} {systemProxyStatus === 'detected' && t($ => $.settings.network.detectedSystemProxy)} {systemProxyStatus === 'none' && t($ => $.settings.network.noSystemProxy)} {systemProxyStatus === 'error' && t($ => $.settings.network.systemProxyReadFailed)}

)}

{t($ => $.settings.network.identity)}

{t($ => $.settings.network.customUserAgent)} {t($ => $.settings.network.userAgentDescription)}
{ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) { setIsUserAgentMenuOpen(false); } }} > settings.setCustomUserAgent(e.target.value)} onFocus={() => setIsUserAgentMenuOpen(true)} placeholder={t($ => $.settings.network.userAgentPlaceholder)} className="app-control settings-network-input font-mono" role="combobox" aria-expanded={isUserAgentMenuOpen} aria-controls="user-agent-suggestions" /> {isUserAgentMenuOpen && (
{USER_AGENT_SUGGESTIONS.map(option => ( ))}
)}

{t($ => $.settings.network.userAgentOverrides)}

)} {/* Locations Pane */} {activeTab === 'locations' && (
{t($ => $.settings.locations.baseDownloadFolder)}

{t($ => $.settings.locations.baseDownloadFolderDescription)}

settings.setBaseDownloadFolder(e.target.value)} onBlur={() => { settings.setBaseDownloadFolder(settings.baseDownloadFolder.trim() || '~/Downloads'); }} className="app-control w-64 text-[11px] px-2" placeholder="~/Downloads" />
{t($ => $.settings.locations.categorySubfolders)} {settings.categorySubfoldersEnabled ? t($ => $.settings.locations.relativeToBase) : t($ => $.settings.locations.disabled)}
{DOWNLOAD_CATEGORIES.map((category) => (
{categoryLabel(category)} handleBrowseCategory(category)} disabled={!settings.categorySubfoldersEnabled} />
))}
)} {/* Site Logins Pane */} {activeTab === 'sitelogins' && (

{t($ => $.settings.siteLogins.title)}

{/* Site Logins List */}
{(settings.siteLogins || []).length === 0 ? (

{t($ => $.settings.siteLogins.noSavedLogins)}

) : ( (settings.siteLogins || []).map((login) => (

{login.urlPattern}

{t($ => $.settings.siteLogins.user, { username: login.username })}

)) )}
{/* Add Site Login Form */}

{t($ => $.settings.siteLogins.addTitle)}

{loginError && (

{loginError}

)}
{ setLoginPattern(e.target.value); setLoginFieldErrors(current => ({ ...current, pattern: undefined })); }} placeholder={t($ => $.settings.siteLogins.urlPatternPlaceholder)} aria-invalid={Boolean(loginFieldErrors.pattern)} className="bg-bg-input border border-border-modal rounded-md px-3 py-1.5 w-full text-text-primary focus:outline-none" /> {loginFieldErrors.pattern &&

{loginFieldErrors.pattern}

}
{ setLoginUser(e.target.value); setLoginFieldErrors(current => ({ ...current, username: undefined })); }} placeholder={t($ => $.settings.siteLogins.usernamePlaceholder)} aria-invalid={Boolean(loginFieldErrors.username)} className="bg-bg-input border border-border-modal rounded-md px-3 py-1.5 w-full text-text-primary focus:outline-none" /> {loginFieldErrors.username &&

{loginFieldErrors.username}

}
{ setLoginPass(e.target.value); setLoginFieldErrors(current => ({ ...current, password: undefined })); }} placeholder={t($ => $.settings.siteLogins.passwordPlaceholder)} aria-invalid={Boolean(loginFieldErrors.password)} className="bg-bg-input border border-border-modal rounded-md px-3 py-1.5 w-full text-text-primary focus:outline-none" /> {loginFieldErrors.password &&

{loginFieldErrors.password}

}
)} {/* Power Pane */} {activeTab === 'power' && (

{t($ => $.settings.power.title)}

)} {/* Engine Pane */} {activeTab === 'engine' && (

{t($ => $.settings.engine.title)}

{t($ => $.settings.engine.description)}

{(() => { const a2 = findEngine('aria2'); const yt = findEngine('ytdlp'); const ff = findEngine('ffmpeg'); const dn = findEngine('deno'); return (
{/* aria2 card */}

{t($ => $.settings.engine.coreDownloader)}

{t($ => $.settings.engine.version)} {renderEngineVersion(a2)}
{t($ => $.settings.engine.status)} {renderEngineStatus(a2)}
{renderEngineDetails(a2)}
{/* yt-dlp / ffmpeg / deno card */}

{t($ => $.settings.engine.mediaExtractors)}

{[ { key: 'ytdlp', item: yt, label: 'yt-dlp' }, { key: 'ffmpeg', item: ff, label: 'FFmpeg' }, { key: 'deno', item: dn, label: 'Deno' }, ].map(({ key, item, label }) => (
{label}: {renderEngineVersion(item)} {renderEngineStatus(item)}
{renderEngineDetails(item)}
))}

{t($ => $.settings.engine.cookieDescription)}

); })()}
)} {/* Integrations Pane */} {activeTab === 'integrations' && (

{t($ => $.settings.integrations.title)}

{t($ => $.settings.integrations.description)}

{settings.isPairingTokenPersistent ? (

{platform.portable ? t($ => $.settings.integrations.portablePairingEnabled) : t($ => $.settings.integrations.pairingTokenPersisted)}

{platform.portable ? t($ => $.settings.integrations.portablePersistedDescription) : t($ => $.settings.integrations.systemPersistedDescription)}

) : (

{platform.portable ? t($ => $.settings.integrations.portablePairingAvailable) : t($ => $.settings.integrations.credentialStorageNeeded)}

{platform.portable ? t($ => $.settings.integrations.portableAvailableDescription) : t($ => $.settings.integrations.credentialStorageDescription)}

)} {/* Step Guide Cards */}
{/* Step 1 */}
1

{t($ => $.settings.integrations.copyToken)}

{t($ => $.settings.integrations.tokenDescription)}

{/* Step 2 */}
2

{t($ => $.settings.integrations.getExtension)}

{t($ => $.settings.integrations.extensionDescription)}

{/* Step 3 */}
3

{t($ => $.settings.integrations.pasteConnect)}

{t($ => $.settings.integrations.pasteConnectDescription)}

{/* Status Info */}
{t($ => $.settings.integrations.extensionServerStatus)} {extensionServerPort ? t($ => $.settings.integrations.listening, { port: extensionServerPort }) : t($ => $.settings.integrations.unavailable)}
)} {/* About Pane */} {activeTab === 'about' && (
{t($ $.settings.about.firelinkIcon)} className="w-[68px] h-[68px] drop-shadow-md rounded-xl shrink-0" />

Firelink

{t($ => $.settings.about.version, { version: appVersion || t($ => $.settings.about.unknown) })}

{t($ => $.settings.about.description)}

{manualUpdateStatus.type !== 'idle' && manualUpdateStatus.type !== 'checking' && (
{manualUpdateStatus.type === 'up-to-date' ? ( ) : manualUpdateStatus.type === 'update-available' ? ( ) : ( )} {manualUpdateStatus.type === 'up-to-date' && t($ => $.settings.about.upToDate, { version: manualUpdateStatus.latestVersion })} {manualUpdateStatus.type === 'update-available' && t($ => $.settings.about.updateAvailable, { version: manualUpdateStatus.version })} {manualUpdateStatus.type === 'error' && manualUpdateStatus.message}
{manualUpdateStatus.type === 'update-available' && ( )}
)}

{t($ => $.settings.about.updates)}

{t($ => $.settings.about.checkForUpdates)}

{t($ => $.settings.about.updateDescription)}

{t($ => $.settings.about.createdBy)}
{t($ => $.settings.about.credits)}
{t($ => $.settings.about.copyright)}
)}
); };