qBitrr2 5.4.5__py3-none-any.whl → 5.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qBitrr/arss.py +457 -127
- qBitrr/bundled_data.py +2 -2
- qBitrr/config_version.py +144 -0
- qBitrr/db_lock.py +189 -0
- qBitrr/db_recovery.py +202 -0
- qBitrr/gen_config.py +285 -3
- qBitrr/main.py +171 -5
- qBitrr/search_activity_store.py +6 -2
- qBitrr/static/assets/ArrView.js +1 -1
- qBitrr/static/assets/ArrView.js.map +1 -1
- qBitrr/static/assets/ConfigView.js +4 -3
- qBitrr/static/assets/ConfigView.js.map +1 -1
- qBitrr/static/assets/LogsView.js +17 -39
- qBitrr/static/assets/LogsView.js.map +1 -1
- qBitrr/static/assets/ProcessesView.js +1 -1
- qBitrr/static/assets/ProcessesView.js.map +1 -1
- qBitrr/static/assets/app.css +1 -1
- qBitrr/static/assets/app.js +1 -9
- qBitrr/static/assets/app.js.map +1 -1
- qBitrr/static/assets/react-select.esm.js +1 -8
- qBitrr/static/assets/react-select.esm.js.map +1 -1
- qBitrr/static/assets/table.js +2 -20
- qBitrr/static/assets/table.js.map +1 -1
- qBitrr/static/assets/vendor.js +1 -25
- qBitrr/static/assets/vendor.js.map +1 -1
- qBitrr/static/sw.js +5 -0
- qBitrr/tables.py +27 -0
- qBitrr/webui.py +523 -23
- {qbitrr2-5.4.5.dist-info → qbitrr2-5.5.0.dist-info}/METADATA +88 -13
- qbitrr2-5.5.0.dist-info/RECORD +63 -0
- qbitrr2-5.4.5.dist-info/RECORD +0 -61
- {qbitrr2-5.4.5.dist-info → qbitrr2-5.5.0.dist-info}/WHEEL +0 -0
- {qbitrr2-5.4.5.dist-info → qbitrr2-5.5.0.dist-info}/entry_points.txt +0 -0
- {qbitrr2-5.4.5.dist-info → qbitrr2-5.5.0.dist-info}/licenses/LICENSE +0 -0
- {qbitrr2-5.4.5.dist-info → qbitrr2-5.5.0.dist-info}/top_level.txt +0 -0
qBitrr/static/assets/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":"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","names":["f","require$$0","k","l","m","n","p","q","c","a","g","b","d","e","h","reactJsxRuntime_production_min","jsxRuntimeModule","client","ToastContext","createContext","toastCounter","ToastProvider","children","toasts","setToasts","useState","dismiss","useCallback","id","prev","t","push","message","kind","value","useMemo","jsx","useToast","ctx","useContext","ToastViewport","toast","SearchContext","SearchProvider","setValueState","handlers","useRef","setValue","term","handler","register","clearHandler","valueObj","useSearch","JSON_HEADERS","TOKEN_STORAGE_KEYS","MAX_AUTH_RETRIES","inflightRequests","createRequestKey","input","init","url","method","body","resolveToken","key","fromQuery","clearStoredToken","buildInit","token","headers","fetchWithAuthRetry","retries","response","fetchJson","existingRequest","promise","handleJson","fetchTextResponse","handleText","res","detail","errorText","getMeta","params","query","getStatus","getProcesses","restartProcess","category","restartAllProcesses","rebuildArrs","getLogs","getLogTail","name","getLogDownloadUrl","getArrList","getRadarrMovies","page","pageSize","getSonarrSeries","options","getLidarrAlbums","restartArr","getConfig","updateConfig","payload","triggerUpdate","WebUIContext","WebUIProvider","settings","setSettings","loading","setLoading","useEffect","webui","storedDensity","storedTheme","backendTheme","theme","error","saveSettings","changes","setLiveArr","setGroupSonarr","setGroupLidarr","setViewDensity","setTheme","useWebUI","context","useNetworkStatus","isOnline","setIsOnline","handleOnline","handleOffline","IconImage","src","alt","className","rest","CloseIcon","ExternalIcon","RefreshIcon","UpdateIcon","DownloadIcon","ProcessesIcon","LogsIcon","RadarrIcon","SonarrIcon","LidarrIcon","ConfigureIcon","ProcessesView","lazy","__vitePreload","module","LogsView","ArrView","ConfigView","formatVersionLabel","trimmed","WelcomeModal","currentVersion","changelog","changelogUrl","repositoryUrl","onClose","jsxs","event","ChangelogModal","latestVersion","updateState","updating","installationType","binaryDownloadUrl","binaryDownloadName","binaryDownloadSize","binaryDownloadError","onUpdate","countdown","setCountdown","updateDisabled","completedLabel","isBinaryInstall","timer","statusClass","statusMessage","Fragment","AppShell","activeTab","setActiveTab","configDirty","setConfigDirty","setSearchValue","viewDensity","meta","setMeta","metaLoading","setMetaLoading","showChangelog","setShowChangelog","updateBusy","setUpdateBusy","backendRestarting","setBackendRestarting","restartPollCount","prevUpdateResult","backendReadyRef","backendWarnedRef","backendTimerRef","reloadKey","setReloadKey","statusData","setStatusData","showWelcomeChangelog","setShowWelcomeChangelog","cacheNames","cacheName","refreshMeta","force","silent","data","lastSeenVersion","handleKeyDown","isMod","tabIndex","tabIds","handleVisibilityChange","refreshStatus","status","state","result","cancelled","attempts","schedule","delay","poll","tabs","baseTabs","arrTabs","arrs","hasRadarr","arr","hasSonarr","hasLidarr","ConfigIcon","displayVersion","updateAvailable","versionTitleParts","versionTitle","tab","handleCheckUpdates","handleOpenChangelog","handleCloseChangelog","handleCloseWelcomeChangelog","handleTriggerUpdate","Suspense","App","createRoot","StrictMode"],"ignoreList":[0,1,2],"sources":["../../../webui/node_modules/react/cjs/react-jsx-runtime.production.min.js","../../../webui/node_modules/react/jsx-runtime.js","../../../webui/node_modules/react-dom/client.js","../../../webui/src/context/ToastContext.tsx","../../../webui/src/context/SearchContext.tsx","../../../webui/src/api/client.ts","../../../webui/src/context/WebUIContext.tsx","../../../webui/src/hooks/useNetworkStatus.ts","../../../webui/src/components/IconImage.tsx","../../../webui/src/icons/close.svg","../../../webui/src/icons/github.svg","../../../webui/src/icons/refresh-arrow.svg","../../../webui/src/icons/up-arrow.svg","../../../webui/src/icons/download.svg","../../../webui/src/icons/process.svg","../../../webui/src/icons/log.svg","../../../webui/src/icons/radarr.svg","../../../webui/src/icons/sonarr.svg","../../../webui/src/icons/lidarr.svg","../../../webui/src/icons/gear.svg","../../../webui/src/App.tsx","../../../webui/src/main.tsx"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"react\"),k=Symbol.for(\"react.element\"),l=Symbol.for(\"react.fragment\"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n","/* eslint-disable react-refresh/only-export-components */\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type PropsWithChildren,\n type JSX,\n} from \"react\";\n\nexport type ToastKind = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface Toast {\n id: number;\n message: string;\n kind: ToastKind;\n}\n\ninterface ToastContextValue {\n toasts: Toast[];\n push: (message: string, kind?: ToastKind) => void;\n dismiss: (id: number) => void;\n}\n\nconst ToastContext = createContext<ToastContextValue | undefined>(undefined);\n\nlet toastCounter = 0;\n\nexport function ToastProvider({ children }: PropsWithChildren): JSX.Element {\n const [toasts, setToasts] = useState<Toast[]>([]);\n\n const dismiss = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n const push = useCallback(\n (message: string, kind: ToastKind = \"info\") => {\n const id = ++toastCounter;\n setToasts((prev) => [...prev, { id, message, kind }]);\n // Auto dismiss\n window.setTimeout(() => dismiss(id), 3500);\n },\n [dismiss]\n );\n\n const value = useMemo(\n () => ({\n toasts,\n push,\n dismiss,\n }),\n [toasts, push, dismiss]\n );\n\n return (\n <ToastContext.Provider value={value}>{children}</ToastContext.Provider>\n );\n}\n\nexport function useToast(): ToastContextValue {\n const ctx = useContext(ToastContext);\n if (!ctx) {\n throw new Error(\"useToast must be used within a ToastProvider\");\n }\n return ctx;\n}\n\nexport function ToastViewport(): JSX.Element | null {\n const { toasts, dismiss } = useToast();\n if (!toasts.length) return null;\n return (\n <div className=\"toasts\">\n {toasts.map((toast) => (\n <div\n key={toast.id}\n className={`toast ${toast.kind !== \"info\" ? toast.kind : \"\"}`}\n role=\"status\"\n onClick={() => dismiss(toast.id)}\n >\n {toast.message}\n </div>\n ))}\n </div>\n );\n}\n","/* eslint-disable react-refresh/only-export-components */\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type PropsWithChildren,\n type JSX,\n} from \"react\";\n\ntype SearchHandler = ((term: string) => void) | null;\n\ninterface SearchContextValue {\n value: string;\n setValue: (term: string) => void;\n register: (handler: SearchHandler) => void;\n clearHandler: (handler: SearchHandler) => void;\n}\n\nconst SearchContext = createContext<SearchContextValue | undefined>(undefined);\n\nexport function SearchProvider({ children }: PropsWithChildren): JSX.Element {\n const [value, setValueState] = useState(\"\");\n const handlers = useRef<Set<SearchHandler>>(new Set());\n\n const setValue = useCallback((term: string) => {\n setValueState(term);\n handlers.current.forEach((handler) => {\n handler?.(term);\n });\n }, []);\n\n const register = useCallback((handler: SearchHandler) => {\n if (!handler) return;\n handlers.current.add(handler);\n }, []);\n\n const clearHandler = useCallback((handler: SearchHandler) => {\n handlers.current.delete(handler);\n }, []);\n\n const valueObj = useMemo(\n () => ({\n value,\n setValue,\n register,\n clearHandler,\n }),\n [value, setValue, register, clearHandler]\n );\n\n return (\n <SearchContext.Provider value={valueObj}>\n {children}\n </SearchContext.Provider>\n );\n}\n\nexport function useSearch(): SearchContextValue {\n const ctx = useContext(SearchContext);\n if (!ctx) {\n throw new Error(\"useSearch must be used within a SearchProvider\");\n }\n return ctx;\n}\n","import type {\n ArrListResponse,\n ConfigDocument,\n ConfigUpdatePayload,\n ConfigUpdateResponse,\n MetaResponse,\n LogsListResponse,\n ProcessesResponse,\n RadarrMoviesResponse,\n RestartResponse,\n SonarrSeriesResponse,\n LidarrAlbumsResponse,\n LidarrAlbum,\n StatusResponse,\n} from \"./types\";\n\nconst JSON_HEADERS = { \"Content-Type\": \"application/json\" } as const;\nconst TOKEN_STORAGE_KEYS = [\"token\", \"webui-token\", \"webui_token\"] as const;\nconst MAX_AUTH_RETRIES = 1;\n\n// Request deduplication cache\nconst inflightRequests = new Map<string, Promise<unknown>>();\n\nfunction createRequestKey(input: RequestInfo | URL, init?: RequestInit): string {\n const url = input instanceof Request ? input.url : String(input);\n const method = init?.method || \"GET\";\n const body = init?.body ? String(init.body) : \"\";\n return `${method}:${url}:${body}`;\n}\n\nfunction resolveToken(): string | null {\n for (const key of TOKEN_STORAGE_KEYS) {\n const value = localStorage.getItem(key) || sessionStorage.getItem(key);\n if (value) {\n if (key !== \"token\") {\n localStorage.setItem(\"token\", value);\n }\n return value;\n }\n }\n try {\n const params = new URLSearchParams(window.location.search);\n const fromQuery = params.get(\"token\");\n if (fromQuery) {\n localStorage.setItem(\"token\", fromQuery);\n return fromQuery;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction clearStoredToken(): void {\n for (const key of TOKEN_STORAGE_KEYS) {\n localStorage.removeItem(key);\n }\n try {\n for (const key of TOKEN_STORAGE_KEYS) {\n sessionStorage.removeItem(key);\n }\n } catch {\n // ignore session storage errors\n }\n}\n\nfunction buildInit(init: RequestInit | undefined, token: string | null): RequestInit {\n const headers = new Headers(init?.headers || {});\n Object.entries(JSON_HEADERS).forEach(([key, value]) => {\n if (!headers.has(key)) headers.set(key, value);\n });\n if (token && !headers.has(\"Authorization\")) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n return {\n ...init,\n headers,\n };\n}\n\nasync function fetchWithAuthRetry<T>(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n handler: (response: Response) => Promise<T>,\n retries = MAX_AUTH_RETRIES\n): Promise<T> {\n const token = resolveToken();\n const response = await fetch(input, buildInit(init, token));\n if (response.status === 401 && retries > 0 && token) {\n clearStoredToken();\n return fetchWithAuthRetry(input, init, handler, retries - 1);\n }\n return handler(response);\n}\n\nasync function fetchJson<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {\n // Only deduplicate GET requests (safe to share)\n const method = init?.method || \"GET\";\n if (method === \"GET\") {\n const key = createRequestKey(input, init);\n const existingRequest = inflightRequests.get(key) as Promise<T> | undefined;\n\n if (existingRequest) {\n return existingRequest;\n }\n\n const promise = fetchWithAuthRetry<T>(input, init, (response) => handleJson<T>(response))\n .finally(() => {\n inflightRequests.delete(key);\n });\n\n inflightRequests.set(key, promise);\n return promise;\n }\n\n return fetchWithAuthRetry<T>(input, init, (response) => handleJson<T>(response));\n}\n\nasync function fetchTextResponse(input: RequestInfo | URL, init?: RequestInit): Promise<string> {\n return fetchWithAuthRetry<string>(input, init, (response) => handleText(response));\n}\n\nasync function handleJson<T>(res: Response): Promise<T> {\n if (!res.ok) {\n let detail: unknown = null;\n try {\n detail = await res.json();\n } catch {\n // ignore\n }\n let message = `${res.status} ${res.statusText}`;\n if (\n detail &&\n typeof detail === \"object\" &&\n \"error\" in detail &&\n typeof (detail as Record<string, unknown>).error === \"string\"\n ) {\n const errorText = (detail as Record<string, unknown>).error as string;\n if (errorText.trim()) {\n message = errorText;\n }\n }\n throw new Error(message);\n }\n return (await res.json()) as T;\n}\n\nasync function handleText(res: Response): Promise<string> {\n if (!res.ok) {\n throw new Error(`${res.status} ${res.statusText}`);\n }\n return res.text();\n}\n\nexport async function getMeta(params?: { force?: boolean }): Promise<MetaResponse> {\n const query = params?.force ? \"?force=1\" : \"\";\n return fetchJson<MetaResponse>(`/web/meta${query}`);\n}\n\nexport async function getStatus(): Promise<StatusResponse> {\n return fetchJson<StatusResponse>(\"/web/status\");\n}\n\nexport async function getProcesses(): Promise<ProcessesResponse> {\n return fetchJson<ProcessesResponse>(\"/web/processes\");\n}\n\nexport async function restartProcess(\n category: string,\n kind: string\n): Promise<RestartResponse> {\n const url = `/web/processes/${encodeURIComponent(\n category\n )}/${encodeURIComponent(kind)}/restart`;\n return fetchJson<RestartResponse>(url, { method: \"POST\" });\n}\n\nexport async function restartAllProcesses(): Promise<RestartResponse> {\n return fetchJson<RestartResponse>(\"/web/processes/restart_all\", {\n method: \"POST\",\n });\n}\n\nexport async function rebuildArrs(): Promise<RestartResponse> {\n return fetchJson<RestartResponse>(\"/web/arr/rebuild\", { method: \"POST\" });\n}\n\nexport async function setLogLevel(level: string): Promise<void> {\n await fetchJson<void>(\"/web/loglevel\", {\n method: \"POST\",\n body: JSON.stringify({ level }),\n });\n}\n\nexport async function getLogs(): Promise<LogsListResponse> {\n return fetchJson<LogsListResponse>(\"/web/logs\");\n}\n\nexport async function getLogTail(name: string): Promise<string> {\n return fetchTextResponse(`/web/logs/${encodeURIComponent(name)}`);\n}\n\nexport function getLogDownloadUrl(name: string): string {\n return `/web/logs/${encodeURIComponent(name)}/download`;\n}\n\nexport async function getArrList(): Promise<ArrListResponse> {\n return fetchJson<ArrListResponse>(\"/web/arr\");\n}\n\nexport async function getRadarrMovies(\n category: string,\n page: number,\n pageSize: number,\n q: string\n): Promise<RadarrMoviesResponse> {\n const params = new URLSearchParams();\n params.set(\"page\", String(page));\n params.set(\"page_size\", String(pageSize));\n if (q) params.set(\"q\", q);\n return fetchJson<RadarrMoviesResponse>(\n `/web/radarr/${encodeURIComponent(category)}/movies?${params}`\n );\n}\n\nexport async function getSonarrSeries(\n category: string,\n page: number,\n pageSize: number,\n q: string,\n options?: { missingOnly?: boolean }\n): Promise<SonarrSeriesResponse> {\n const params = new URLSearchParams();\n params.set(\"page\", String(page));\n params.set(\"page_size\", String(pageSize));\n if (q) params.set(\"q\", q);\n if (options?.missingOnly) {\n params.set(\"missing\", \"1\");\n }\n return fetchJson<SonarrSeriesResponse>(\n `/web/sonarr/${encodeURIComponent(category)}/series?${params}`\n );\n}\n\nexport async function getLidarrAlbums(\n category: string,\n page: number,\n pageSize: number,\n query?: string\n): Promise<LidarrAlbumsResponse> {\n const params = new URLSearchParams();\n params.set(\"page\", page.toString());\n params.set(\"page_size\", pageSize.toString());\n if (query) {\n params.set(\"q\", query);\n }\n // Always include tracks\n params.set(\"include_tracks\", \"true\");\n return fetchJson<LidarrAlbumsResponse>(\n `/web/lidarr/${encodeURIComponent(category)}/albums?${params}`\n );\n}\n\nexport async function restartArr(category: string): Promise<void> {\n await fetchJson<void>(\n `/web/arr/${encodeURIComponent(category)}/restart`,\n { method: \"POST\" }\n );\n}\n\nexport async function getConfig(): Promise<ConfigDocument> {\n return fetchJson<ConfigDocument>(\"/web/config\");\n}\n\nexport async function updateConfig(\n payload: ConfigUpdatePayload\n): Promise<ConfigUpdateResponse> {\n const token = resolveToken();\n const response = await fetch(\"/web/config\", buildInit({\n method: \"POST\",\n body: JSON.stringify(payload),\n }, token));\n\n if (!response.ok) {\n let detail: unknown = null;\n try {\n detail = await response.json();\n } catch {\n // ignore\n }\n let message = `${response.status} ${response.statusText}`;\n if (\n detail &&\n typeof detail === \"object\" &&\n \"error\" in detail &&\n typeof (detail as Record<string, unknown>).error === \"string\"\n ) {\n const errorText = (detail as Record<string, unknown>).error as string;\n if (errorText.trim()) {\n message = errorText;\n }\n }\n throw new Error(message);\n }\n\n // Parse response body with full type information\n const data = await response.json() as ConfigUpdateResponse;\n return data;\n}\n\nexport async function triggerUpdate(): Promise<void> {\n await fetchJson<void>(\"/web/update\", { method: \"POST\" });\n}\n","import { createContext, useCallback, useContext, useEffect, useState, type JSX, type ReactNode } from \"react\";\nimport { getConfig, updateConfig } from \"../api/client\";\n\ntype ViewDensity = \"comfortable\" | \"compact\";\ntype Theme = \"light\" | \"dark\";\n\ninterface WebUISettings {\n liveArr: boolean;\n groupSonarr: boolean;\n groupLidarr: boolean;\n viewDensity: ViewDensity;\n theme: Theme;\n}\n\ninterface WebUIContextValue {\n liveArr: boolean;\n groupSonarr: boolean;\n groupLidarr: boolean;\n viewDensity: ViewDensity;\n theme: Theme;\n setLiveArr: (value: boolean) => void;\n setGroupSonarr: (value: boolean) => void;\n setGroupLidarr: (value: boolean) => void;\n setViewDensity: (value: ViewDensity) => void;\n setTheme: (value: Theme) => void;\n loading: boolean;\n}\n\nconst WebUIContext = createContext<WebUIContextValue | null>(null);\n\nexport function WebUIProvider({ children }: { children: ReactNode }): JSX.Element {\n const [settings, setSettings] = useState<WebUISettings>({\n liveArr: true,\n groupSonarr: true,\n groupLidarr: true,\n viewDensity: \"comfortable\",\n theme: \"dark\",\n });\n const [loading, setLoading] = useState(true);\n\n // Load initial settings\n useEffect(() => {\n const loadSettings = async () => {\n try {\n const config = await getConfig();\n const webui = config?.WebUI as Record<string, unknown> | undefined;\n\n // Load from localStorage as fallback for view density (client-side preference)\n const storedDensity = localStorage.getItem(\"viewDensity\") as ViewDensity | null;\n const storedTheme = localStorage.getItem(\"theme\") as Theme | null;\n\n // Get theme from backend or localStorage\n const backendTheme = webui?.Theme as string | undefined;\n const theme: Theme = storedTheme || (backendTheme?.toLowerCase() as Theme) || \"dark\";\n\n setSettings({\n liveArr: webui?.LiveArr === true,\n groupSonarr: webui?.GroupSonarr === true,\n groupLidarr: webui?.GroupLidarr === true,\n viewDensity: storedDensity || \"comfortable\",\n theme,\n });\n\n // Apply theme immediately\n document.documentElement.setAttribute('data-theme', theme);\n } catch (error) {\n console.error(\"Failed to load WebUI settings:\", error);\n } finally {\n setLoading(false);\n }\n };\n\n void loadSettings();\n }, []);\n\n // Auto-save settings to backend\n const saveSettings = useCallback(async (key: string, value: boolean | string) => {\n try {\n const changes: Record<string, unknown> = {\n WebUI: {\n [key]: value,\n },\n };\n await updateConfig({ changes });\n } catch (error) {\n console.error(`Failed to save ${key}:`, error);\n }\n }, []);\n\n const setLiveArr = useCallback((value: boolean) => {\n setSettings(prev => ({ ...prev, liveArr: value }));\n void saveSettings(\"LiveArr\", value);\n }, [saveSettings]);\n\n const setGroupSonarr = useCallback((value: boolean) => {\n setSettings(prev => ({ ...prev, groupSonarr: value }));\n void saveSettings(\"GroupSonarr\", value);\n }, [saveSettings]);\n\n const setGroupLidarr = useCallback((value: boolean) => {\n setSettings(prev => ({ ...prev, groupLidarr: value }));\n void saveSettings(\"GroupLidarr\", value);\n }, [saveSettings]);\n\n const setViewDensity = useCallback((value: ViewDensity) => {\n setSettings(prev => ({ ...prev, viewDensity: value }));\n // Store in localStorage (client-side preference, not sent to backend)\n localStorage.setItem(\"viewDensity\", value);\n }, []);\n\n const setTheme = useCallback((value: Theme) => {\n setSettings(prev => ({ ...prev, theme: value }));\n // Store in localStorage for instant application\n localStorage.setItem(\"theme\", value);\n // Apply theme immediately to DOM\n document.documentElement.setAttribute('data-theme', value);\n // Save to backend with proper capitalization (Light or Dark)\n const capitalizedTheme = value === \"light\" ? \"Light\" : \"Dark\";\n void saveSettings(\"Theme\", capitalizedTheme);\n }, [saveSettings]);\n\n const value: WebUIContextValue = {\n liveArr: settings.liveArr,\n groupSonarr: settings.groupSonarr,\n groupLidarr: settings.groupLidarr,\n viewDensity: settings.viewDensity,\n theme: settings.theme,\n setLiveArr,\n setGroupSonarr,\n setGroupLidarr,\n setViewDensity,\n setTheme,\n loading,\n };\n\n return <WebUIContext.Provider value={value}>{children}</WebUIContext.Provider>;\n}\n\nexport function useWebUI(): WebUIContextValue {\n const context = useContext(WebUIContext);\n if (!context) {\n throw new Error(\"useWebUI must be used within WebUIProvider\");\n }\n return context;\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useNetworkStatus(): boolean {\n const [isOnline, setIsOnline] = useState(navigator.onLine);\n\n useEffect(() => {\n const handleOnline = () => setIsOnline(true);\n const handleOffline = () => setIsOnline(false);\n\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n return isOnline;\n}\n","import type { ImgHTMLAttributes, JSX } from \"react\";\n\ninterface IconImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\"> {\n src: string;\n alt?: string;\n}\n\nexport function IconImage({ src, alt, className, ...rest }: IconImageProps): JSX.Element {\n return (\n <img\n src={src}\n alt={alt ?? \"\"}\n className={className ? `icon ${className}` : \"icon\"}\n aria-hidden={alt ? undefined : true}\n {...rest}\n />\n );\n}\n","export default \"__VITE_ASSET__BwSX1QeM__\"","export default \"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%20viewBox='0%200%20512%20512'%3e%3cpath%20d='M256%206.3C114.6%206.3%200%20120.9%200%20262.3c0%20113.3%2073.3%20209%20175%20242.9%2012.8%202.2%2017.6-5.4%2017.6-12.2%200-6.1-.3-26.2-.3-47.7-64.3%2011.8-81-15.7-86.1-30.1-2.9-7.4-15.4-30.1-26.2-36.2-9-4.8-21.8-16.6-.3-17%2020.2-.3%2034.6%2018.6%2039.4%2026.2%2023%2038.7%2059.8%2027.8%2074.6%2021.1%202.2-16.6%209-27.8%2016.3-34.2-57-6.4-116.5-28.5-116.5-126.4%200-27.8%209.9-50.9%2026.2-68.8-2.6-6.4-11.5-32.6%202.6-67.8%200%200%2021.4-6.7%2070.4%2026.2%2020.5-5.8%2042.2-8.6%2064-8.6s43.5%202.9%2064%208.6c49-33.3%2070.4-26.2%2070.4-26.2%2014.1%2035.2%205.1%2061.4%202.6%2067.8%2016.3%2017.9%2026.2%2040.6%2026.2%2068.8%200%2098.2-59.8%20120-116.8%20126.4%209.3%208%2017.3%2023.4%2017.3%2047.4%200%2034.2-.3%2061.8-.3%2070.4%200%206.7%204.8%2014.7%2017.6%2012.2C438.7%20471.3%20512%20375.3%20512%20262.3c0-141.4-114.6-256-256-256'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%231b1f23'/%3e%3c/svg%3e\"","export default \"__VITE_ASSET__B5Abvgg6__\"","export default \"__VITE_ASSET__CFg2sUO1__\"","export default \"__VITE_ASSET__Dd3tjKzy__\"","export default \"__VITE_ASSET__DruObj$B__\"","export default \"__VITE_ASSET__DKTRAwQu__\"","export default \"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%20id='Layer_1'%20x='0'%20y='0'%20version='1.1'%20viewBox='0%200%20512%20512'%3e%3cstyle%3e.st0{fill:%2324292e}%3c/style%3e%3cg%20id='Group-Copy'%20transform='translate(70%2021)'%3e%3cpath%20id='Shape'%20d='m10.3%2059.8%203.9%20372.4c-31.4%203.9-54.9-11.8-54.9-43.1l-3.9-309.7c0-98%2090.2-121.5%20145.1-82.3l278.3%20160.7c39.2%2027.4%2047%2078.4%2027.4%20113.7-3.9-27.4-15.7-43.1-39.2-58.8L53.4%2036.2C29.9%2020.6%2010.3%2024.5%2010.3%2059.8'%20class='st0'/%3e%3cpath%20id='Shape_00000114049535938561773820000018271523940913105341_'%20d='M-13.2%20451.8c23.5%207.8%2047%203.9%2066.6-7.8l321.5-188.2c19.6%2027.4%2015.7%2054.9-7.8%2070.6L96.5%20483.2c-39.2%2019.6-90.1%200-109.7-31.4'%20class='st0'/%3e%3cpath%20id='Shape_00000165935924413286433040000003668002807793862576_'%20d='M80.9%20342%20273%20232.3%2084.8%20126.4z'%20style='fill:%23ffc230'/%3e%3c/g%3e%3c/svg%3e\"","export default \"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%20viewBox='0%200%20512%20512'%3e%3cpath%20d='M511.8%20256c0%2070.4-24.9%20130.8-74.6%20181.1-1.7%202-3.5%203.8-5.5%205.4-8.2%208-16.8%2015.3-26%2021.8Q341.05%20512%20256.3%20512c-56.6%200-106.3-15.9-149.2-47.7-11.3-8-22-17.1-31.9-27.3C36.5%20398.7%2012.8%20354%204%20303.2c-1.7-9.9-2.9-20-3.4-30.2-.2-5.7-.4-11.3-.4-17%200-6%20.1-11.7.4-17.1%200-.6.2-1.1.5-1.7%203.7-62.8%2028.4-117%2074.1-162.8C125.5%2024.8%20185.8%200%20256.2%200c70.7%200%20131%2024.8%20180.9%2074.5q74.7%2075.9%2074.7%20181.5'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%23eee'/%3e%3cpath%20d='m459.7%20100.3-52.9%2052.9c-30.9%2030.9-33.6%2057.8-33.6%20105.3%200%2042.3%206.7%2081.1%2038.2%20112.6%2023%2023%2044.9%2044.7%2044.9%2044.7-5.9%207.2-12.3%2014.3-19.1%2021.2-1.7%202-3.5%203.8-5.5%205.4-6%205.9-12.2%2011.4-18.6%2016.4l-41.4-41.4C334.9%20380.6%20305.6%20377%20257%20377c-46.7%200-78.4%204.3-112.6%2038.5-20.4%2020.4-43.8%2043.9-43.8%2043.9-8.9-6.8-17.3-14.2-25.3-22.4-6.6-6.6-12.8-13.4-18.5-20.3%200%200%2023.1-23.2%2045.2-45.3%2032.7-32.7%2038-70.6%2038-113%200-41.3-6.8-79.8-36.8-109.9C82.2%20127.7%2053.3%2099%2053.3%2099c6.7-8.5%2014-16.7%2021.8-24.5%206.9-6.8%2014-13.1%2021.2-19l48%2048c30.7%2030.7%2070%2038.6%20112.4%2038.6%2043.6%200%2082.8-8.4%20114.7-40.4C391%2082.1%20417%2056.3%20417%2056.3c6.8%205.6%2013.5%2011.6%2020.1%2018.2%208.3%208.3%2015.8%2016.9%2022.6%2025.8'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%233a3f51'/%3e%3cpath%20d='M186%20269.1c-.5-2.8-.8-5.5-.9-8.4-.1-1.6-.1-3.1-.1-4.7%200-1.7%200-3.2.1-4.7%200-.2%200-.3.1-.5%201-17.4%207.9-32.4%2020.5-45.1%2013.9-13.8%2030.6-20.7%2050.2-20.7s36.3%206.9%2050.2%2020.7c13.8%2014%2020.7%2030.8%2020.7%2050.3s-6.9%2036.2-20.7%2050.2c-.5.5-1%201.1-1.5%201.5q-3.45%203.3-7.2%206-18%2013.2-41.4%2013.2c-23.4%200-29.4-4.4-41.3-13.2-3.1-2.2-6.1-4.7-8.9-7.6-10.8-10.6-17.3-22.9-19.8-37'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%230cf'/%3e%3cpath%20d='m372.7%20141-35.4%2034.6M72.9%2076.8l96.5%2096.1m199.7%20198.9%2065.6%2067.9m4.4-363.3L372.7%20141M76.6%20438.5l64.6-64.7'%20style='fill:none;stroke:%230cf;stroke-width:2;stroke-miterlimit:1'/%3e%3cpath%20d='m372.7%20141-40%2040.6m-193.3-38.5%2040.6%2040.5M141%20374l39.5-41.1m146.2-3.3%2042.6%2042.4'%20style='fill:none;stroke:%230cf;stroke-width:7;stroke-miterlimit:1'/%3e%3c/svg%3e\"","export default \"__VITE_ASSET__B_sq1wA9__\"","export default \"__VITE_ASSET__CITwrObj__\"","import { useCallback, useEffect, useMemo, useRef, useState, type JSX, lazy, Suspense } from \"react\";\nconst ProcessesView = lazy(() => import(\"./pages/ProcessesView\").then(module => ({ default: module.ProcessesView })));\nconst LogsView = lazy(() => import(\"./pages/LogsView\").then(module => ({ default: module.LogsView })));\nconst ArrView = lazy(() => import(\"./pages/ArrView\").then(module => ({ default: module.ArrView })));\nconst ConfigView = lazy(() => import(\"./pages/ConfigView\").then(module => ({ default: module.ConfigView })));\nimport { ToastProvider, ToastViewport, useToast } from \"./context/ToastContext\";\nimport { SearchProvider, useSearch } from \"./context/SearchContext\";\nimport { WebUIProvider, useWebUI } from \"./context/WebUIContext\";\nimport { useNetworkStatus } from \"./hooks/useNetworkStatus\";\nimport { getMeta, getStatus, triggerUpdate } from \"./api/client\";\nimport type { MetaResponse, StatusResponse } from \"./api/types\";\nimport { IconImage } from \"./components/IconImage\";\nimport CloseIcon from \"./icons/close.svg\";\nimport ExternalIcon from \"./icons/github.svg\";\nimport RefreshIcon from \"./icons/refresh-arrow.svg\";\nimport UpdateIcon from \"./icons/up-arrow.svg\";\nimport DownloadIcon from \"./icons/download.svg\";\nimport ProcessesIcon from \"./icons/process.svg\";\nimport LogsIcon from \"./icons/log.svg\";\nimport RadarrIcon from \"./icons/radarr.svg\";\nimport SonarrIcon from \"./icons/sonarr.svg\";\nimport LidarrIcon from \"./icons/lidarr.svg\";\nimport ConfigIcon from \"./icons/gear.svg\";\n\ntype Tab = \"processes\" | \"logs\" | \"radarr\" | \"sonarr\" | \"lidarr\" | \"config\";\n\ninterface NavTab {\n id: Tab;\n label: string;\n icon: string;\n}\n\nfunction formatVersionLabel(value: string | null | undefined): string {\n if (!value) {\n return \"unknown\";\n }\n const trimmed = value.trim();\n if (!trimmed) {\n return \"unknown\";\n }\n return trimmed[0] === \"v\" || trimmed[0] === \"V\" ? trimmed : `v${trimmed}`;\n}\n\ninterface WelcomeModalProps {\n currentVersion: string;\n changelog: string | null;\n changelogUrl: string | null;\n repositoryUrl: string;\n onClose: () => void;\n}\n\nfunction WelcomeModal({\n currentVersion,\n changelog,\n changelogUrl,\n repositoryUrl,\n onClose,\n}: WelcomeModalProps): JSX.Element {\n return (\n <div className=\"modal-backdrop\" role=\"presentation\" onClick={onClose}>\n <div\n className=\"modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"welcome-title\"\n onClick={(event) => event.stopPropagation()}\n >\n <div className=\"modal-header\">\n <h2 id=\"welcome-title\">\n 🎉 Welcome to qBitrr {formatVersionLabel(currentVersion)}!\n </h2>\n </div>\n <div className=\"modal-body changelog-modal__body\">\n <div className=\"changelog-meta\">\n <p style={{ marginBottom: '1rem', color: 'var(--text-secondary)' }}>\n You've been updated to version <strong>{formatVersionLabel(currentVersion)}</strong>.\n Here's what's new in this release:\n </p>\n </div>\n <div className=\"changelog-section\">\n <h3>Release Notes</h3>\n <pre className=\"changelog-body\">\n {changelog?.trim() ? changelog.trim() : \"No changelog available for this version.\"}\n </pre>\n </div>\n </div>\n <div className=\"modal-footer\">\n <div className=\"changelog-links\">\n {(changelogUrl || repositoryUrl) && (\n <a\n className=\"btn ghost small\"\n href={changelogUrl ?? repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <IconImage src={ExternalIcon} />\n View Full Release on GitHub\n </a>\n )}\n </div>\n <div className=\"changelog-buttons\">\n <button className=\"btn primary\" type=\"button\" onClick={onClose}>\n Got it!\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n\ninterface ChangelogModalProps {\n currentVersion: string;\n latestVersion: string | null;\n changelog: string | null;\n changelogUrl: string | null;\n repositoryUrl: string;\n updateState: MetaResponse[\"update_state\"] | null | undefined;\n updating: boolean;\n installationType: MetaResponse[\"installation_type\"];\n binaryDownloadUrl: string | null;\n binaryDownloadName: string | null;\n binaryDownloadSize: number | null;\n binaryDownloadError: string | null;\n onClose: () => void;\n onUpdate: () => void;\n}\n\nfunction ChangelogModal({\n currentVersion,\n latestVersion,\n changelog,\n changelogUrl,\n repositoryUrl,\n updateState,\n updating,\n installationType,\n binaryDownloadUrl,\n binaryDownloadName,\n binaryDownloadSize,\n binaryDownloadError,\n onClose,\n onUpdate,\n}: ChangelogModalProps): JSX.Element {\n const [countdown, setCountdown] = useState<number | null>(null);\n const updateDisabled = updating || Boolean(updateState?.in_progress);\n const completedLabel = updateState?.completed_at\n ? new Date(updateState.completed_at).toLocaleString()\n : null;\n const isBinaryInstall = installationType === \"binary\";\n\n // Start countdown when update completes successfully\n useEffect(() => {\n if (updateState?.last_result === \"success\" && updateState?.completed_at) {\n let countdown = 10;\n setCountdown(countdown);\n const timer = setInterval(() => {\n countdown -= 1;\n if (countdown <= 0) {\n clearInterval(timer);\n window.location.reload();\n } else {\n setCountdown(countdown);\n }\n }, 1000);\n return () => clearInterval(timer);\n }\n }, [updateState?.last_result, updateState?.completed_at]);\n\n let statusClass = \"\";\n let statusMessage: string | null = null;\n if (updateState?.in_progress) {\n statusClass = \"text-info\";\n statusMessage = \"⏳ Update in progress...\";\n } else if (updateState?.last_result === \"success\") {\n statusClass = \"text-success\";\n if (countdown !== null) {\n statusMessage = `✓ Update completed! Reloading in ${countdown}s...`;\n } else {\n statusMessage = \"✓ Update completed successfully\";\n if (completedLabel) {\n statusMessage = `${statusMessage} (${completedLabel})`;\n }\n }\n } else if (updateState?.last_result === \"error\") {\n statusClass = \"text-danger\";\n const detail = updateState.last_error ? updateState.last_error.trim() : \"\";\n statusMessage = detail ? `✗ Update failed: ${detail}` : \"✗ Update failed\";\n }\n\n return (\n <div className=\"modal-backdrop\" role=\"presentation\" onClick={onClose}>\n <div\n className=\"modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"changelog-title\"\n onClick={(event) => event.stopPropagation()}\n >\n <div className=\"modal-header\">\n <h2 id=\"changelog-title\">\n {updateState?.in_progress ? \"⚙️ Updating...\" : \"🚀 Update Available\"}\n </h2>\n <button className=\"btn ghost\" type=\"button\" onClick={onClose} disabled={updateState?.in_progress}>\n <IconImage src={CloseIcon} />\n Close\n </button>\n </div>\n <div className=\"modal-body changelog-modal__body\">\n <div className=\"changelog-meta\">\n <div className=\"version-comparison\">\n <span className=\"version-item\">\n <strong>Current:</strong>{\" \"}\n <span className=\"version-badge version-current\">{formatVersionLabel(currentVersion)}</span>\n </span>\n <span className=\"version-arrow\">→</span>\n <span className=\"version-item\">\n <strong>Latest:</strong>{\" \"}\n <span className=\"version-badge version-latest\">\n {latestVersion ? formatVersionLabel(latestVersion) : \"Unknown\"}\n </span>\n </span>\n </div>\n {statusMessage ? (\n <div className={`update-status ${statusClass}`}>\n {statusMessage}\n </div>\n ) : null}\n </div>\n <div className=\"changelog-section\">\n <h3>What's New</h3>\n <pre className=\"changelog-body\">\n {changelog?.trim() ? changelog.trim() : \"No changelog provided.\"}\n </pre>\n </div>\n </div>\n <div className=\"modal-footer\">\n <div className=\"changelog-links\">\n {(changelogUrl || repositoryUrl) && (\n <a\n className=\"btn ghost small\"\n href={changelogUrl ?? repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <IconImage src={ExternalIcon} />\n View on GitHub\n </a>\n )}\n </div>\n <div className=\"changelog-buttons\">\n {isBinaryInstall ? (\n binaryDownloadError ? (\n <div className=\"update-status text-danger\" style={{ marginBottom: '0.5rem' }}>\n {binaryDownloadError}\n </div>\n ) : binaryDownloadUrl ? (\n <>\n <a\n className=\"btn primary\"\n href={`/web/download-update`}\n download={binaryDownloadName ?? undefined}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <IconImage src={DownloadIcon} />\n Download Update\n {binaryDownloadSize && binaryDownloadSize > 0 ? (\n <span style={{ marginLeft: '0.5rem', opacity: 0.8, fontSize: '0.875rem' }}>\n ({(binaryDownloadSize / (1024 * 1024)).toFixed(1)} MB)\n </span>\n ) : null}\n </a>\n <div style={{ fontSize: '0.875rem', color: 'var(--text-secondary)', marginTop: '0.5rem' }}>\n Binary installation detected. Download and manually replace the executable.\n </div>\n </>\n ) : (\n <div className=\"update-status text-danger\">\n Unable to fetch binary download URL. Please update manually.\n </div>\n )\n ) : (\n <button\n className=\"btn primary\"\n type=\"button\"\n onClick={onUpdate}\n disabled={updateDisabled}\n >\n <IconImage src={UpdateIcon} />\n {updateDisabled ? \"Updating...\" : \"Update Now\"}\n </button>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction AppShell(): JSX.Element {\n const [activeTab, setActiveTab] = useState<Tab>(\"processes\");\n const [configDirty, setConfigDirty] = useState(false);\n const { push } = useToast();\n const { setValue: setSearchValue } = useSearch();\n const { viewDensity, setViewDensity } = useWebUI();\n const isOnline = useNetworkStatus();\n const [meta, setMeta] = useState<MetaResponse | null>(null);\n const [metaLoading, setMetaLoading] = useState(false);\n const [showChangelog, setShowChangelog] = useState(false);\n const [updateBusy, setUpdateBusy] = useState(false);\n const [backendRestarting, setBackendRestarting] = useState(false);\n const restartPollCount = useRef(0);\n const prevUpdateResult = useRef<string | null>(null);\n const backendReadyRef = useRef(false);\n const backendWarnedRef = useRef(false);\n const backendTimerRef = useRef<number | null>(null);\n const [reloadKey, setReloadKey] = useState(0);\n const [statusData, setStatusData] = useState<StatusResponse | null>(null);\n const [showWelcomeChangelog, setShowWelcomeChangelog] = useState(false);\n\n // Theme is now managed by WebUIContext and applied automatically\n\n // Clear cache on every page load to ensure fresh content\n useEffect(() => {\n const clearCache = async () => {\n if ('caches' in window) {\n try {\n const cacheNames = await caches.keys();\n await Promise.all(\n cacheNames.map(cacheName => caches.delete(cacheName))\n );\n console.log('Cache cleared on page load');\n } catch (error) {\n console.error('Failed to clear cache:', error);\n }\n }\n };\n clearCache();\n }, []);\n\n const refreshMeta = useCallback(\n async (options?: { force?: boolean; silent?: boolean }) => {\n const force = options?.force ?? false;\n const silent = options?.silent ?? !force;\n if (!silent) {\n setMetaLoading(true);\n }\n try {\n const data = await getMeta({ force });\n setMeta(data);\n } catch (error) {\n if (!silent) {\n const message =\n error instanceof Error ? error.message : \"Failed to fetch version information\";\n push(message, \"error\");\n }\n } finally {\n if (!silent) {\n setMetaLoading(false);\n }\n }\n },\n [push]\n );\n\n useEffect(() => {\n void refreshMeta({ force: true });\n }, [refreshMeta]);\n\n // Check for new version on first launch - show welcome popup with changelog\n useEffect(() => {\n if (!meta?.current_version) {\n return;\n }\n\n const lastSeenVersion = localStorage.getItem(\"lastSeenVersion\");\n const currentVersion = meta.current_version;\n\n // Show welcome popup if this is a new version (but not on very first install)\n if (lastSeenVersion && lastSeenVersion !== currentVersion) {\n // Ensure we have changelog data before showing popup\n if (!meta.current_version_changelog && !meta.changelog) {\n void refreshMeta({ force: true, silent: true });\n }\n setShowWelcomeChangelog(true);\n }\n\n // Store current version as last seen when user opens the app (first install)\n if (!lastSeenVersion) {\n localStorage.setItem(\"lastSeenVersion\", currentVersion);\n }\n }, [meta?.current_version, meta?.changelog, refreshMeta]);\n\n // Network status notifications\n useEffect(() => {\n if (!isOnline) {\n push(\"You are offline. Some features may not work.\", \"warning\");\n }\n }, [isOnline, push]);\n\n // Keyboard shortcuts\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // Don't trigger shortcuts when typing in inputs\n if (event.target instanceof HTMLInputElement ||\n event.target instanceof HTMLTextAreaElement ||\n event.target instanceof HTMLSelectElement) {\n return;\n }\n\n const isMod = event.ctrlKey || event.metaKey;\n\n // Ctrl/Cmd + K - Focus search\n if (isMod && event.key === 'k') {\n event.preventDefault();\n const searchInput = document.querySelector('input[type=\"text\"][placeholder*=\"Search\"]') as HTMLInputElement;\n searchInput?.focus();\n return;\n }\n\n // R - Refresh current view\n if (event.key === 'r' || event.key === 'R') {\n event.preventDefault();\n setReloadKey(prev => prev + 1);\n push('Refreshed', 'success');\n return;\n }\n\n // ESC - Clear search\n if (event.key === 'Escape') {\n setSearchValue('');\n return;\n }\n\n // Number keys 1-6 for tab switching\n if (event.key >= '1' && event.key <= '6' && !isMod) {\n event.preventDefault();\n const tabIndex = parseInt(event.key) - 1;\n const tabIds: Tab[] = ['processes', 'logs', 'radarr', 'sonarr', 'lidarr', 'config'];\n if (tabIndex < tabIds.length) {\n setActiveTab(tabIds[tabIndex]);\n }\n return;\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [setSearchValue, push]);\n\n useEffect(() => {\n const id = window.setInterval(() => {\n void refreshMeta();\n }, 5 * 60 * 1000);\n return () => window.clearInterval(id);\n }, [refreshMeta]);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"visible\") {\n // Force reload all data by incrementing the reload key\n setReloadKey((prev) => prev + 1);\n void refreshMeta({ force: true });\n void refreshStatus();\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n return () => {\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [refreshMeta]);\n\n const refreshStatus = useCallback(async () => {\n try {\n const status = await getStatus();\n setStatusData(status);\n } catch {\n // Silently fail - status is not critical\n }\n }, []);\n\n useEffect(() => {\n void refreshStatus();\n const id = window.setInterval(() => {\n void refreshStatus();\n }, 5 * 1000); // Refresh every 5 seconds for more dynamic tab loading\n return () => window.clearInterval(id);\n }, [refreshStatus]);\n\n useEffect(() => {\n if (!meta?.update_state?.in_progress && !backendRestarting) {\n restartPollCount.current = 0;\n return;\n }\n const id = window.setInterval(async () => {\n try {\n const data = await getMeta({ force: true });\n setMeta(data);\n if (backendRestarting) {\n // Backend came back after restart\n window.location.reload();\n }\n restartPollCount.current = 0;\n } catch {\n restartPollCount.current += 1;\n if (restartPollCount.current > 20) { // 60 seconds\n setBackendRestarting(false);\n restartPollCount.current = 0;\n push(\"Update completed but backend restart timed out. Please refresh the page manually.\", \"warning\");\n return;\n }\n if (meta?.update_state?.in_progress) {\n // Failed while update in progress, likely restarting\n setBackendRestarting(true);\n }\n }\n }, 3000);\n return () => window.clearInterval(id);\n }, [meta?.update_state?.in_progress, backendRestarting, meta, push]);\n\n useEffect(() => {\n const state = meta?.update_state;\n if (!state) {\n prevUpdateResult.current = null;\n return;\n }\n const result = state.last_result ?? null;\n if (result && result !== prevUpdateResult.current) {\n if (result === \"success\") {\n push(\"Update completed successfully. Restarting...\", \"success\");\n setBackendRestarting(true);\n restartPollCount.current = 0;\n } else if (result === \"error\") {\n push(state.last_error || \"Update failed.\", \"error\");\n }\n }\n prevUpdateResult.current = result;\n }, [meta?.update_state, push]);\n\n useEffect(() => {\n let cancelled = false;\n let attempts = 0;\n\n const schedule = (delay: number) => {\n if (backendTimerRef.current !== null) {\n window.clearTimeout(backendTimerRef.current);\n }\n backendTimerRef.current = window.setTimeout(() => {\n void poll();\n }, delay);\n };\n\n const poll = async () => {\n if (cancelled || backendReadyRef.current) {\n return;\n }\n attempts += 1;\n try {\n const status = await getStatus();\n if (cancelled) {\n return;\n }\n setStatusData(status);\n const readyHint =\n status.ready ?? (Array.isArray(status.arrs) && status.arrs.length > 0);\n if (readyHint) {\n backendReadyRef.current = true;\n return;\n }\n if (status.ready === false && attempts >= 3 && !backendWarnedRef.current) {\n backendWarnedRef.current = true;\n push(\n \"qBitrr backend is still initialising. Check the logs if this persists.\",\n \"warning\"\n );\n }\n } catch (error) {\n if (!backendWarnedRef.current && attempts >= 3) {\n backendWarnedRef.current = true;\n const detail = error instanceof Error ? error.message : \"Unknown backend error\";\n push(\n `Unable to confirm qBitrr readiness (${detail}). Please inspect the logs.`,\n \"warning\"\n );\n }\n } finally {\n if (!cancelled && !backendReadyRef.current) {\n const delay = attempts < 3 ? 3000 : 10000;\n schedule(delay);\n }\n }\n };\n\n schedule(0);\n\n return () => {\n cancelled = true;\n if (backendTimerRef.current !== null) {\n window.clearTimeout(backendTimerRef.current);\n backendTimerRef.current = null;\n }\n };\n }, [push]);\n\n const tabs = useMemo<NavTab[]>(() => {\n const baseTabs: NavTab[] = [\n { id: \"processes\", label: \"Processes\", icon: ProcessesIcon },\n { id: \"logs\", label: \"Logs\", icon: LogsIcon },\n ];\n\n const arrTabs: NavTab[] = [];\n const arrs = statusData?.arrs ?? [];\n\n const hasRadarr = arrs.some((arr) => arr.type === \"radarr\");\n const hasSonarr = arrs.some((arr) => arr.type === \"sonarr\");\n const hasLidarr = arrs.some((arr) => arr.type === \"lidarr\");\n\n if (hasRadarr) {\n arrTabs.push({ id: \"radarr\", label: \"Radarr\", icon: RadarrIcon });\n }\n if (hasSonarr) {\n arrTabs.push({ id: \"sonarr\", label: \"Sonarr\", icon: SonarrIcon });\n }\n if (hasLidarr) {\n arrTabs.push({ id: \"lidarr\", label: \"Lidarr\", icon: LidarrIcon });\n }\n\n return [\n ...baseTabs,\n ...arrTabs,\n { id: \"config\", label: \"Config\", icon: ConfigIcon },\n ];\n }, [statusData]);\n\n const repositoryUrl = meta?.repository_url ?? \"https://github.com/Feramance/qBitrr\";\n const displayVersion = meta?.current_version\n ? formatVersionLabel(meta.current_version)\n : \"...\";\n const latestVersion = meta?.latest_version ?? null;\n const updateAvailable = Boolean(meta?.update_available);\n const updateState = meta?.update_state;\n const changelogUrl = meta?.changelog_url ?? repositoryUrl;\n\n const versionTitleParts: string[] = [];\n if (meta?.last_checked) {\n versionTitleParts.push(`Last checked ${new Date(meta.last_checked).toLocaleString()}`);\n }\n if (meta?.error) {\n versionTitleParts.push(`Update check failed: ${meta.error}`);\n }\n const versionTitle = versionTitleParts.length ? versionTitleParts.join(\" • \") : undefined;\n\n // Redirect to processes if active tab is no longer available\n useEffect(() => {\n const tabExists = tabs.some((tab) => tab.id === activeTab);\n if (!tabExists && tabs.length > 0) {\n setActiveTab(\"processes\");\n }\n }, [tabs, activeTab]);\n\n const handleCheckUpdates = useCallback(() => {\n void refreshMeta({ force: true });\n }, [refreshMeta]);\n\n const handleOpenChangelog = useCallback(() => {\n setShowChangelog(true);\n if (!meta?.changelog) {\n void refreshMeta({ force: true, silent: true });\n }\n }, [meta?.changelog, refreshMeta]);\n\n const handleCloseChangelog = useCallback(() => {\n setShowChangelog(false);\n }, []);\n\n const handleCloseWelcomeChangelog = useCallback(() => {\n setShowWelcomeChangelog(false);\n // Mark this version as seen\n if (meta?.current_version) {\n localStorage.setItem(\"lastSeenVersion\", meta.current_version);\n }\n }, [meta?.current_version]);\n\n const handleTriggerUpdate = useCallback(async () => {\n setUpdateBusy(true);\n setBackendRestarting(false);\n restartPollCount.current = 0;\n try {\n await triggerUpdate();\n push(\"Update started in the background.\", \"info\");\n await refreshMeta({ force: true, silent: true });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to start update\";\n push(message, \"error\");\n } finally {\n setUpdateBusy(false);\n }\n }, [push, refreshMeta]);\n\n return (\n <div data-density={viewDensity}>\n <header className=\"appbar\">\n <div className=\"appbar__inner\">\n <div className=\"appbar__title\">\n <h1>qBitrr</h1>\n <span className=\"appbar__version\" title={versionTitle}>\n {displayVersion}\n </span>\n {metaLoading ? <span className=\"spinner\" aria-hidden=\"true\" /> : null}\n {updateState?.in_progress ? (\n <span className=\"appbar__status text-info\">Updating...</span>\n ) : null}\n {updateAvailable ? (\n <button\n type=\"button\"\n className=\"btn small primary appbar__update\"\n onClick={handleOpenChangelog}\n disabled={updateBusy || Boolean(updateState?.in_progress)}\n >\n <span className=\"appbar__update-indicator\" aria-hidden=\"true\" />\n <IconImage src={UpdateIcon} />\n Update available\n </button>\n ) : null}\n </div>\n <div className=\"appbar__actions\">\n {!isOnline && (\n <span className=\"badge\" style={{ background: 'rgba(239, 68, 68, 0.15)', borderColor: 'rgba(239, 68, 68, 0.3)', color: 'var(--danger)' }}>\n Offline\n </span>\n )}\n <div className=\"view-density-toggle\">\n <button\n type=\"button\"\n className={viewDensity === \"comfortable\" ? \"active\" : \"\"}\n onClick={() => setViewDensity(\"comfortable\")}\n title=\"Comfortable view\"\n >\n Comfortable\n </button>\n <button\n type=\"button\"\n className={viewDensity === \"compact\" ? \"active\" : \"\"}\n onClick={() => setViewDensity(\"compact\")}\n title=\"Compact view\"\n >\n Compact\n </button>\n </div>\n <button\n type=\"button\"\n className=\"btn small ghost\"\n onClick={handleCheckUpdates}\n disabled={metaLoading}\n >\n <IconImage src={RefreshIcon} />\n {metaLoading ? \"Checking...\" : \"Check Updates\"}\n </button>\n <a\n href={repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"btn small ghost\"\n >\n <IconImage src={ExternalIcon} />\n GitHub\n </a>\n </div>\n </div>\n </header>\n <main className=\"container\">\n <nav className=\"nav\">\n {tabs.map((tab) => (\n <button\n type=\"button\"\n key={tab.id}\n className={activeTab === tab.id ? \"active\" : \"\"}\n onClick={() => {\n if (activeTab === \"config\" && tab.id !== \"config\" && configDirty) {\n const shouldLeave = window.confirm(\n \"You have unsaved configuration changes. Leave without saving?\"\n );\n if (!shouldLeave) {\n return;\n }\n }\n setActiveTab(tab.id);\n setSearchValue(\"\");\n }}\n >\n <IconImage src={tab.icon} />\n <span>{tab.label}</span>\n </button>\n ))}\n </nav>\n <Suspense fallback={<div className=\"loading\">Loading...</div>}>\n {activeTab === \"processes\" && <ProcessesView key={`processes-${reloadKey}`} active />}\n {activeTab === \"logs\" && <LogsView key={`logs-${reloadKey}`} active />}\n {activeTab === \"radarr\" && <ArrView key={`radarr-${reloadKey}`} type=\"radarr\" active />}\n {activeTab === \"sonarr\" && <ArrView key={`sonarr-${reloadKey}`} type=\"sonarr\" active />}\n {activeTab === \"lidarr\" && <ArrView key={`lidarr-${reloadKey}`} type=\"lidarr\" active />}\n {activeTab === \"config\" && <ConfigView key={`config-${reloadKey}`} onDirtyChange={setConfigDirty} />}\n </Suspense>\n </main>\n {showChangelog && meta ? (\n <ChangelogModal\n currentVersion={meta.current_version}\n latestVersion={latestVersion}\n changelog={meta.changelog}\n changelogUrl={changelogUrl}\n repositoryUrl={repositoryUrl}\n updateState={updateState}\n updating={updateBusy}\n installationType={meta.installation_type}\n binaryDownloadUrl={meta.binary_download_url}\n binaryDownloadName={meta.binary_download_name}\n binaryDownloadSize={meta.binary_download_size}\n binaryDownloadError={meta.binary_download_error}\n onClose={handleCloseChangelog}\n onUpdate={handleTriggerUpdate}\n />\n ) : null}\n {showWelcomeChangelog && meta ? (\n <WelcomeModal\n currentVersion={meta.current_version}\n changelog={meta.current_version_changelog || meta.changelog}\n changelogUrl={changelogUrl}\n repositoryUrl={repositoryUrl}\n onClose={handleCloseWelcomeChangelog}\n />\n ) : null}\n </div>\n );\n}\n\nexport default function App(): JSX.Element {\n return (\n <ToastProvider>\n <SearchProvider>\n <WebUIProvider>\n <AppShell />\n <ToastViewport />\n </WebUIProvider>\n </SearchProvider>\n </ToastProvider>\n );\n}\n","import { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"./styles.css\";\nimport App from \"./App\";\n\ncreateRoot(document.getElementById(\"root\")!).render(\n <StrictMode>\n <App />\n </StrictMode>\n);\n"],"file":"app.js"}
|
|
1
|
+
{"version":3,"mappings":"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","names":["f","require$$0","k","l","m","n","p","q","c","a","g","b","d","e","h","reactJsxRuntime_production_min","jsxRuntimeModule","client","ToastContext","createContext","toastCounter","ToastProvider","children","toasts","setToasts","useState","dismiss","useCallback","id","prev","t","push","message","kind","duration","value","useMemo","jsx","useToast","ctx","useContext","ToastViewport","toast","SearchContext","SearchProvider","setValueState","handlers","useRef","setValue","term","handler","register","clearHandler","valueObj","useSearch","JSON_HEADERS","TOKEN_STORAGE_KEYS","MAX_AUTH_RETRIES","inflightRequests","createRequestKey","input","init","url","method","body","resolveToken","key","fromQuery","clearStoredToken","buildInit","token","headers","fetchWithAuthRetry","retries","response","fetchJson","existingRequest","promise","handleJson","res","detail","errorText","getMeta","params","query","getStatus","getProcesses","restartProcess","category","restartAllProcesses","rebuildArrs","getLogs","getLogDownloadUrl","name","getArrList","getRadarrMovies","page","pageSize","getSonarrSeries","options","getLidarrAlbums","restartArr","getConfig","warningResponse","updateConfig","payload","triggerUpdate","testArrConnection","request","WebUIContext","WebUIProvider","settings","setSettings","loading","setLoading","useEffect","webui","warningMessage","storedDensity","storedTheme","backendTheme","theme","error","saveSettings","changes","setLiveArr","setGroupSonarr","setGroupLidarr","setViewDensity","setTheme","useWebUI","context","useNetworkStatus","isOnline","setIsOnline","handleOnline","handleOffline","IconImage","src","alt","className","rest","CloseIcon","ExternalIcon","RefreshIcon","UpdateIcon","DownloadIcon","ProcessesIcon","LogsIcon","RadarrIcon","SonarrIcon","LidarrIcon","ConfigureIcon","ProcessesView","lazy","__vitePreload","module","LogsView","ArrView","ConfigView","formatVersionLabel","trimmed","WelcomeModal","currentVersion","changelog","changelogUrl","repositoryUrl","onClose","jsxs","event","ChangelogModal","latestVersion","updateState","updating","installationType","binaryDownloadUrl","binaryDownloadName","binaryDownloadSize","binaryDownloadError","onUpdate","countdown","setCountdown","updateDisabled","completedLabel","isBinaryInstall","timer","statusClass","statusMessage","Fragment","AppShell","activeTab","setActiveTab","configDirty","setConfigDirty","setSearchValue","viewDensity","meta","setMeta","metaLoading","setMetaLoading","showChangelog","setShowChangelog","updateBusy","setUpdateBusy","backendRestarting","setBackendRestarting","restartPollCount","prevUpdateResult","backendReadyRef","backendWarnedRef","backendTimerRef","reloadKey","setReloadKey","statusData","setStatusData","showWelcomeChangelog","setShowWelcomeChangelog","cacheNames","cacheName","refreshMeta","force","silent","data","lastSeenVersion","handleKeyDown","isMod","tabIndex","tabIds","handleVisibilityChange","refreshStatus","status","state","result","cancelled","attempts","schedule","delay","poll","tabs","baseTabs","arrTabs","arrs","hasRadarr","arr","hasSonarr","hasLidarr","ConfigIcon","displayVersion","updateAvailable","versionTitleParts","versionTitle","tab","handleCheckUpdates","handleOpenChangelog","handleCloseChangelog","handleCloseWelcomeChangelog","handleTriggerUpdate","Suspense","App","createRoot","StrictMode"],"ignoreList":[0,1,2],"sources":["../../../webui/node_modules/react/cjs/react-jsx-runtime.production.min.js","../../../webui/node_modules/react/jsx-runtime.js","../../../webui/node_modules/react-dom/client.js","../../../webui/src/context/ToastContext.tsx","../../../webui/src/context/SearchContext.tsx","../../../webui/src/api/client.ts","../../../webui/src/context/WebUIContext.tsx","../../../webui/src/hooks/useNetworkStatus.ts","../../../webui/src/components/IconImage.tsx","../../../webui/src/icons/close.svg","../../../webui/src/icons/github.svg","../../../webui/src/icons/refresh-arrow.svg","../../../webui/src/icons/up-arrow.svg","../../../webui/src/icons/download.svg","../../../webui/src/icons/process.svg","../../../webui/src/icons/log.svg","../../../webui/src/icons/radarr.svg","../../../webui/src/icons/sonarr.svg","../../../webui/src/icons/lidarr.svg","../../../webui/src/icons/gear.svg","../../../webui/src/App.tsx","../../../webui/src/main.tsx"],"sourcesContent":["/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"react\"),k=Symbol.for(\"react.element\"),l=Symbol.for(\"react.fragment\"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n","/* eslint-disable react-refresh/only-export-components */\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type PropsWithChildren,\n type JSX,\n} from \"react\";\n\nexport type ToastKind = \"info\" | \"success\" | \"warning\" | \"error\";\n\nexport interface Toast {\n id: number;\n message: string;\n kind: ToastKind;\n}\n\ninterface ToastContextValue {\n toasts: Toast[];\n push: (message: string, kind?: ToastKind) => void;\n dismiss: (id: number) => void;\n}\n\nconst ToastContext = createContext<ToastContextValue | undefined>(undefined);\n\nlet toastCounter = 0;\n\nexport function ToastProvider({ children }: PropsWithChildren): JSX.Element {\n const [toasts, setToasts] = useState<Toast[]>([]);\n\n const dismiss = useCallback((id: number) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n const push = useCallback(\n (message: string, kind: ToastKind = \"info\") => {\n const id = ++toastCounter;\n setToasts((prev) => [...prev, { id, message, kind }]);\n // Auto dismiss - longer duration for errors\n const duration = kind === \"error\" ? 8000 : 3500;\n window.setTimeout(() => dismiss(id), duration);\n },\n [dismiss]\n );\n\n const value = useMemo(\n () => ({\n toasts,\n push,\n dismiss,\n }),\n [toasts, push, dismiss]\n );\n\n return (\n <ToastContext.Provider value={value}>{children}</ToastContext.Provider>\n );\n}\n\nexport function useToast(): ToastContextValue {\n const ctx = useContext(ToastContext);\n if (!ctx) {\n throw new Error(\"useToast must be used within a ToastProvider\");\n }\n return ctx;\n}\n\nexport function ToastViewport(): JSX.Element | null {\n const { toasts, dismiss } = useToast();\n if (!toasts.length) return null;\n return (\n <div className=\"toasts\">\n {toasts.map((toast) => (\n <div\n key={toast.id}\n className={`toast ${toast.kind !== \"info\" ? toast.kind : \"\"}`}\n role=\"status\"\n onClick={() => dismiss(toast.id)}\n >\n {toast.message}\n </div>\n ))}\n </div>\n );\n}\n","/* eslint-disable react-refresh/only-export-components */\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type PropsWithChildren,\n type JSX,\n} from \"react\";\n\ntype SearchHandler = ((term: string) => void) | null;\n\ninterface SearchContextValue {\n value: string;\n setValue: (term: string) => void;\n register: (handler: SearchHandler) => void;\n clearHandler: (handler: SearchHandler) => void;\n}\n\nconst SearchContext = createContext<SearchContextValue | undefined>(undefined);\n\nexport function SearchProvider({ children }: PropsWithChildren): JSX.Element {\n const [value, setValueState] = useState(\"\");\n const handlers = useRef<Set<SearchHandler>>(new Set());\n\n const setValue = useCallback((term: string) => {\n setValueState(term);\n handlers.current.forEach((handler) => {\n handler?.(term);\n });\n }, []);\n\n const register = useCallback((handler: SearchHandler) => {\n if (!handler) return;\n handlers.current.add(handler);\n }, []);\n\n const clearHandler = useCallback((handler: SearchHandler) => {\n handlers.current.delete(handler);\n }, []);\n\n const valueObj = useMemo(\n () => ({\n value,\n setValue,\n register,\n clearHandler,\n }),\n [value, setValue, register, clearHandler]\n );\n\n return (\n <SearchContext.Provider value={valueObj}>\n {children}\n </SearchContext.Provider>\n );\n}\n\nexport function useSearch(): SearchContextValue {\n const ctx = useContext(SearchContext);\n if (!ctx) {\n throw new Error(\"useSearch must be used within a SearchProvider\");\n }\n return ctx;\n}\n","import type {\n ArrListResponse,\n ConfigDocument,\n ConfigResponseWithWarning,\n ConfigUpdatePayload,\n ConfigUpdateResponse,\n MetaResponse,\n LogsListResponse,\n ProcessesResponse,\n RadarrMoviesResponse,\n RestartResponse,\n SonarrSeriesResponse,\n LidarrAlbumsResponse,\n LidarrAlbum,\n StatusResponse,\n} from \"./types\";\n\nconst JSON_HEADERS = { \"Content-Type\": \"application/json\" } as const;\nconst TOKEN_STORAGE_KEYS = [\"token\", \"webui-token\", \"webui_token\"] as const;\nconst MAX_AUTH_RETRIES = 1;\n\n// Request deduplication cache\nconst inflightRequests = new Map<string, Promise<unknown>>();\n\nfunction createRequestKey(input: RequestInfo | URL, init?: RequestInit): string {\n const url = input instanceof Request ? input.url : String(input);\n const method = init?.method || \"GET\";\n const body = init?.body ? String(init.body) : \"\";\n return `${method}:${url}:${body}`;\n}\n\nfunction resolveToken(): string | null {\n for (const key of TOKEN_STORAGE_KEYS) {\n const value = localStorage.getItem(key) || sessionStorage.getItem(key);\n if (value) {\n if (key !== \"token\") {\n localStorage.setItem(\"token\", value);\n }\n return value;\n }\n }\n try {\n const params = new URLSearchParams(window.location.search);\n const fromQuery = params.get(\"token\");\n if (fromQuery) {\n localStorage.setItem(\"token\", fromQuery);\n return fromQuery;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nfunction clearStoredToken(): void {\n for (const key of TOKEN_STORAGE_KEYS) {\n localStorage.removeItem(key);\n }\n try {\n for (const key of TOKEN_STORAGE_KEYS) {\n sessionStorage.removeItem(key);\n }\n } catch {\n // ignore session storage errors\n }\n}\n\nfunction buildInit(init: RequestInit | undefined, token: string | null): RequestInit {\n const headers = new Headers(init?.headers || {});\n Object.entries(JSON_HEADERS).forEach(([key, value]) => {\n if (!headers.has(key)) headers.set(key, value);\n });\n if (token && !headers.has(\"Authorization\")) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n return {\n ...init,\n headers,\n };\n}\n\nasync function fetchWithAuthRetry<T>(\n input: RequestInfo | URL,\n init: RequestInit | undefined,\n handler: (response: Response) => Promise<T>,\n retries = MAX_AUTH_RETRIES\n): Promise<T> {\n const token = resolveToken();\n const response = await fetch(input, buildInit(init, token));\n if (response.status === 401 && retries > 0 && token) {\n clearStoredToken();\n return fetchWithAuthRetry(input, init, handler, retries - 1);\n }\n return handler(response);\n}\n\nasync function fetchJson<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {\n // Only deduplicate GET requests (safe to share)\n const method = init?.method || \"GET\";\n if (method === \"GET\") {\n const key = createRequestKey(input, init);\n const existingRequest = inflightRequests.get(key) as Promise<T> | undefined;\n\n if (existingRequest) {\n return existingRequest;\n }\n\n const promise = fetchWithAuthRetry<T>(input, init, (response) => handleJson<T>(response))\n .finally(() => {\n inflightRequests.delete(key);\n });\n\n inflightRequests.set(key, promise);\n return promise;\n }\n\n return fetchWithAuthRetry<T>(input, init, (response) => handleJson<T>(response));\n}\n\nasync function fetchTextResponse(input: RequestInfo | URL, init?: RequestInit): Promise<string> {\n return fetchWithAuthRetry<string>(input, init, (response) => handleText(response));\n}\n\nasync function handleJson<T>(res: Response): Promise<T> {\n if (!res.ok) {\n let detail: unknown = null;\n try {\n detail = await res.json();\n } catch {\n // ignore\n }\n let message = `${res.status} ${res.statusText}`;\n if (\n detail &&\n typeof detail === \"object\" &&\n \"error\" in detail &&\n typeof (detail as Record<string, unknown>).error === \"string\"\n ) {\n const errorText = (detail as Record<string, unknown>).error as string;\n if (errorText.trim()) {\n message = errorText;\n }\n }\n throw new Error(message);\n }\n return (await res.json()) as T;\n}\n\nasync function handleText(res: Response): Promise<string> {\n if (!res.ok) {\n throw new Error(`${res.status} ${res.statusText}`);\n }\n return res.text();\n}\n\nexport async function getMeta(params?: { force?: boolean }): Promise<MetaResponse> {\n const query = params?.force ? \"?force=1\" : \"\";\n return fetchJson<MetaResponse>(`/web/meta${query}`);\n}\n\nexport async function getStatus(): Promise<StatusResponse> {\n return fetchJson<StatusResponse>(\"/web/status\");\n}\n\nexport async function getProcesses(): Promise<ProcessesResponse> {\n return fetchJson<ProcessesResponse>(\"/web/processes\");\n}\n\nexport async function restartProcess(\n category: string,\n kind: string\n): Promise<RestartResponse> {\n const url = `/web/processes/${encodeURIComponent(\n category\n )}/${encodeURIComponent(kind)}/restart`;\n return fetchJson<RestartResponse>(url, { method: \"POST\" });\n}\n\nexport async function restartAllProcesses(): Promise<RestartResponse> {\n return fetchJson<RestartResponse>(\"/web/processes/restart_all\", {\n method: \"POST\",\n });\n}\n\nexport async function rebuildArrs(): Promise<RestartResponse> {\n return fetchJson<RestartResponse>(\"/web/arr/rebuild\", { method: \"POST\" });\n}\n\nexport async function setLogLevel(level: string): Promise<void> {\n await fetchJson<void>(\"/web/loglevel\", {\n method: \"POST\",\n body: JSON.stringify({ level }),\n });\n}\n\nexport async function getLogs(): Promise<LogsListResponse> {\n return fetchJson<LogsListResponse>(\"/web/logs\");\n}\n\nexport async function getLogTail(name: string): Promise<string> {\n return fetchTextResponse(`/web/logs/${encodeURIComponent(name)}`);\n}\n\nexport function getLogDownloadUrl(name: string): string {\n return `/web/logs/${encodeURIComponent(name)}/download`;\n}\n\nexport async function getArrList(): Promise<ArrListResponse> {\n return fetchJson<ArrListResponse>(\"/web/arr\");\n}\n\nexport async function getRadarrMovies(\n category: string,\n page: number,\n pageSize: number,\n q: string\n): Promise<RadarrMoviesResponse> {\n const params = new URLSearchParams();\n params.set(\"page\", String(page));\n params.set(\"page_size\", String(pageSize));\n if (q) params.set(\"q\", q);\n return fetchJson<RadarrMoviesResponse>(\n `/web/radarr/${encodeURIComponent(category)}/movies?${params}`\n );\n}\n\nexport async function getSonarrSeries(\n category: string,\n page: number,\n pageSize: number,\n q: string,\n options?: { missingOnly?: boolean }\n): Promise<SonarrSeriesResponse> {\n const params = new URLSearchParams();\n params.set(\"page\", String(page));\n params.set(\"page_size\", String(pageSize));\n if (q) params.set(\"q\", q);\n if (options?.missingOnly) {\n params.set(\"missing\", \"1\");\n }\n return fetchJson<SonarrSeriesResponse>(\n `/web/sonarr/${encodeURIComponent(category)}/series?${params}`\n );\n}\n\nexport async function getLidarrAlbums(\n category: string,\n page: number,\n pageSize: number,\n query?: string\n): Promise<LidarrAlbumsResponse> {\n const params = new URLSearchParams();\n params.set(\"page\", page.toString());\n params.set(\"page_size\", pageSize.toString());\n if (query) {\n params.set(\"q\", query);\n }\n // Always include tracks\n params.set(\"include_tracks\", \"true\");\n return fetchJson<LidarrAlbumsResponse>(\n `/web/lidarr/${encodeURIComponent(category)}/albums?${params}`\n );\n}\n\nexport async function restartArr(category: string): Promise<void> {\n await fetchJson<void>(\n `/web/arr/${encodeURIComponent(category)}/restart`,\n { method: \"POST\" }\n );\n}\n\nexport async function getConfig(): Promise<ConfigDocument> {\n // Response might be ConfigDocument OR ConfigResponseWithWarning\n const response = await fetchJson<ConfigDocument | ConfigResponseWithWarning>(\"/web/config\");\n\n // Check if response contains a warning structure\n if (response && typeof response === \"object\" && \"warning\" in response && \"config\" in response) {\n // Response has warning structure - store warning for display\n const warningResponse = response as ConfigResponseWithWarning;\n if (warningResponse.warning?.message) {\n sessionStorage.setItem(\"config_version_warning\", warningResponse.warning.message);\n }\n // Return the actual config (always present in warning structure)\n return warningResponse.config;\n }\n\n // Normal response - just a plain config object\n return response as ConfigDocument;\n}\n\nexport async function updateConfig(\n payload: ConfigUpdatePayload\n): Promise<ConfigUpdateResponse> {\n const token = resolveToken();\n const response = await fetch(\"/web/config\", buildInit({\n method: \"POST\",\n body: JSON.stringify(payload),\n }, token));\n\n if (!response.ok) {\n let detail: unknown = null;\n try {\n detail = await response.json();\n } catch {\n // ignore\n }\n let message = `${response.status} ${response.statusText}`;\n if (\n detail &&\n typeof detail === \"object\" &&\n \"error\" in detail &&\n typeof (detail as Record<string, unknown>).error === \"string\"\n ) {\n const errorText = (detail as Record<string, unknown>).error as string;\n if (errorText.trim()) {\n message = errorText;\n }\n }\n throw new Error(message);\n }\n\n // Parse response body with full type information\n const data = await response.json() as ConfigUpdateResponse;\n return data;\n}\n\nexport async function triggerUpdate(): Promise<void> {\n await fetchJson<void>(\"/web/update\", { method: \"POST\" });\n}\n\nexport interface TestConnectionRequest {\n arrType: \"radarr\" | \"sonarr\" | \"lidarr\";\n uri: string;\n apiKey: string;\n}\n\nexport interface TestConnectionResponse {\n success: boolean;\n message: string;\n systemInfo?: {\n version: string;\n branch?: string;\n };\n qualityProfiles?: Array<{ id: number; name: string }>;\n}\n\nexport async function testArrConnection(\n request: TestConnectionRequest\n): Promise<TestConnectionResponse> {\n return fetchJson(\"/web/arr/test-connection\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n}\n","import { createContext, useCallback, useContext, useEffect, useState, type JSX, type ReactNode } from \"react\";\nimport { getConfig, updateConfig } from \"../api/client\";\nimport { useToast } from \"./ToastContext\";\n\ntype ViewDensity = \"comfortable\" | \"compact\";\ntype Theme = \"light\" | \"dark\";\n\ninterface WebUISettings {\n liveArr: boolean;\n groupSonarr: boolean;\n groupLidarr: boolean;\n viewDensity: ViewDensity;\n theme: Theme;\n}\n\ninterface WebUIContextValue {\n liveArr: boolean;\n groupSonarr: boolean;\n groupLidarr: boolean;\n viewDensity: ViewDensity;\n theme: Theme;\n setLiveArr: (value: boolean) => void;\n setGroupSonarr: (value: boolean) => void;\n setGroupLidarr: (value: boolean) => void;\n setViewDensity: (value: ViewDensity) => void;\n setTheme: (value: Theme) => void;\n loading: boolean;\n}\n\nconst WebUIContext = createContext<WebUIContextValue | null>(null);\n\nexport function WebUIProvider({ children }: { children: ReactNode }): JSX.Element {\n const [settings, setSettings] = useState<WebUISettings>({\n liveArr: true,\n groupSonarr: true,\n groupLidarr: true,\n viewDensity: \"comfortable\",\n theme: \"dark\",\n });\n const [loading, setLoading] = useState(true);\n const toast = useToast();\n\n // Load initial settings\n useEffect(() => {\n const loadSettings = async () => {\n try {\n const config = await getConfig();\n const webui = config?.WebUI as Record<string, unknown> | undefined;\n\n // Check for config version warning in sessionStorage\n const warningMessage = sessionStorage.getItem(\"config_version_warning\");\n if (warningMessage) {\n // Show error toast with longer duration for config version mismatch\n toast.push(warningMessage, \"error\");\n // Clear the warning after showing it\n sessionStorage.removeItem(\"config_version_warning\");\n }\n\n // Load from localStorage as fallback for view density (client-side preference)\n const storedDensity = localStorage.getItem(\"viewDensity\") as ViewDensity | null;\n const storedTheme = localStorage.getItem(\"theme\") as Theme | null;\n\n // Get theme from backend or localStorage\n const backendTheme = webui?.Theme as string | undefined;\n const theme: Theme = storedTheme || (backendTheme?.toLowerCase() as Theme) || \"dark\";\n\n setSettings({\n liveArr: webui?.LiveArr === true,\n groupSonarr: webui?.GroupSonarr === true,\n groupLidarr: webui?.GroupLidarr === true,\n viewDensity: storedDensity || \"comfortable\",\n theme,\n });\n\n // Apply theme immediately\n document.documentElement.setAttribute('data-theme', theme);\n } catch (error) {\n console.error(\"Failed to load WebUI settings:\", error);\n } finally {\n setLoading(false);\n }\n };\n\n void loadSettings();\n }, [toast]);\n\n // Auto-save settings to backend\n const saveSettings = useCallback(async (key: string, value: boolean | string) => {\n try {\n const changes: Record<string, unknown> = {\n WebUI: {\n [key]: value,\n },\n };\n await updateConfig({ changes });\n } catch (error) {\n console.error(`Failed to save ${key}:`, error);\n }\n }, []);\n\n const setLiveArr = useCallback((value: boolean) => {\n setSettings(prev => ({ ...prev, liveArr: value }));\n void saveSettings(\"LiveArr\", value);\n }, [saveSettings]);\n\n const setGroupSonarr = useCallback((value: boolean) => {\n setSettings(prev => ({ ...prev, groupSonarr: value }));\n void saveSettings(\"GroupSonarr\", value);\n }, [saveSettings]);\n\n const setGroupLidarr = useCallback((value: boolean) => {\n setSettings(prev => ({ ...prev, groupLidarr: value }));\n void saveSettings(\"GroupLidarr\", value);\n }, [saveSettings]);\n\n const setViewDensity = useCallback((value: ViewDensity) => {\n setSettings(prev => ({ ...prev, viewDensity: value }));\n // Store in localStorage (client-side preference, not sent to backend)\n localStorage.setItem(\"viewDensity\", value);\n }, []);\n\n const setTheme = useCallback((value: Theme) => {\n setSettings(prev => ({ ...prev, theme: value }));\n // Store in localStorage for instant application\n localStorage.setItem(\"theme\", value);\n // Apply theme immediately to DOM\n document.documentElement.setAttribute('data-theme', value);\n // Save to backend with proper capitalization (Light or Dark)\n const capitalizedTheme = value === \"light\" ? \"Light\" : \"Dark\";\n void saveSettings(\"Theme\", capitalizedTheme);\n }, [saveSettings]);\n\n const value: WebUIContextValue = {\n liveArr: settings.liveArr,\n groupSonarr: settings.groupSonarr,\n groupLidarr: settings.groupLidarr,\n viewDensity: settings.viewDensity,\n theme: settings.theme,\n setLiveArr,\n setGroupSonarr,\n setGroupLidarr,\n setViewDensity,\n setTheme,\n loading,\n };\n\n return <WebUIContext.Provider value={value}>{children}</WebUIContext.Provider>;\n}\n\nexport function useWebUI(): WebUIContextValue {\n const context = useContext(WebUIContext);\n if (!context) {\n throw new Error(\"useWebUI must be used within WebUIProvider\");\n }\n return context;\n}\n","import { useEffect, useState } from \"react\";\n\nexport function useNetworkStatus(): boolean {\n const [isOnline, setIsOnline] = useState(navigator.onLine);\n\n useEffect(() => {\n const handleOnline = () => setIsOnline(true);\n const handleOffline = () => setIsOnline(false);\n\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n return isOnline;\n}\n","import type { ImgHTMLAttributes, JSX } from \"react\";\n\ninterface IconImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"src\"> {\n src: string;\n alt?: string;\n}\n\nexport function IconImage({ src, alt, className, ...rest }: IconImageProps): JSX.Element {\n return (\n <img\n src={src}\n alt={alt ?? \"\"}\n className={className ? `icon ${className}` : \"icon\"}\n aria-hidden={alt ? undefined : true}\n {...rest}\n />\n );\n}\n","export default \"__VITE_ASSET__BwSX1QeM__\"","export default \"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%20viewBox='0%200%20512%20512'%3e%3cpath%20d='M256%206.3C114.6%206.3%200%20120.9%200%20262.3c0%20113.3%2073.3%20209%20175%20242.9%2012.8%202.2%2017.6-5.4%2017.6-12.2%200-6.1-.3-26.2-.3-47.7-64.3%2011.8-81-15.7-86.1-30.1-2.9-7.4-15.4-30.1-26.2-36.2-9-4.8-21.8-16.6-.3-17%2020.2-.3%2034.6%2018.6%2039.4%2026.2%2023%2038.7%2059.8%2027.8%2074.6%2021.1%202.2-16.6%209-27.8%2016.3-34.2-57-6.4-116.5-28.5-116.5-126.4%200-27.8%209.9-50.9%2026.2-68.8-2.6-6.4-11.5-32.6%202.6-67.8%200%200%2021.4-6.7%2070.4%2026.2%2020.5-5.8%2042.2-8.6%2064-8.6s43.5%202.9%2064%208.6c49-33.3%2070.4-26.2%2070.4-26.2%2014.1%2035.2%205.1%2061.4%202.6%2067.8%2016.3%2017.9%2026.2%2040.6%2026.2%2068.8%200%2098.2-59.8%20120-116.8%20126.4%209.3%208%2017.3%2023.4%2017.3%2047.4%200%2034.2-.3%2061.8-.3%2070.4%200%206.7%204.8%2014.7%2017.6%2012.2C438.7%20471.3%20512%20375.3%20512%20262.3c0-141.4-114.6-256-256-256'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%231b1f23'/%3e%3c/svg%3e\"","export default \"__VITE_ASSET__B5Abvgg6__\"","export default \"__VITE_ASSET__CFg2sUO1__\"","export default \"__VITE_ASSET__Dd3tjKzy__\"","export default \"__VITE_ASSET__DruObj$B__\"","export default \"__VITE_ASSET__DKTRAwQu__\"","export default \"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%20id='Layer_1'%20x='0'%20y='0'%20version='1.1'%20viewBox='0%200%20512%20512'%3e%3cstyle%3e.st0{fill:%2324292e}%3c/style%3e%3cg%20id='Group-Copy'%20transform='translate(70%2021)'%3e%3cpath%20id='Shape'%20d='m10.3%2059.8%203.9%20372.4c-31.4%203.9-54.9-11.8-54.9-43.1l-3.9-309.7c0-98%2090.2-121.5%20145.1-82.3l278.3%20160.7c39.2%2027.4%2047%2078.4%2027.4%20113.7-3.9-27.4-15.7-43.1-39.2-58.8L53.4%2036.2C29.9%2020.6%2010.3%2024.5%2010.3%2059.8'%20class='st0'/%3e%3cpath%20id='Shape_00000114049535938561773820000018271523940913105341_'%20d='M-13.2%20451.8c23.5%207.8%2047%203.9%2066.6-7.8l321.5-188.2c19.6%2027.4%2015.7%2054.9-7.8%2070.6L96.5%20483.2c-39.2%2019.6-90.1%200-109.7-31.4'%20class='st0'/%3e%3cpath%20id='Shape_00000165935924413286433040000003668002807793862576_'%20d='M80.9%20342%20273%20232.3%2084.8%20126.4z'%20style='fill:%23ffc230'/%3e%3c/g%3e%3c/svg%3e\"","export default \"data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%20viewBox='0%200%20512%20512'%3e%3cpath%20d='M511.8%20256c0%2070.4-24.9%20130.8-74.6%20181.1-1.7%202-3.5%203.8-5.5%205.4-8.2%208-16.8%2015.3-26%2021.8Q341.05%20512%20256.3%20512c-56.6%200-106.3-15.9-149.2-47.7-11.3-8-22-17.1-31.9-27.3C36.5%20398.7%2012.8%20354%204%20303.2c-1.7-9.9-2.9-20-3.4-30.2-.2-5.7-.4-11.3-.4-17%200-6%20.1-11.7.4-17.1%200-.6.2-1.1.5-1.7%203.7-62.8%2028.4-117%2074.1-162.8C125.5%2024.8%20185.8%200%20256.2%200c70.7%200%20131%2024.8%20180.9%2074.5q74.7%2075.9%2074.7%20181.5'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%23eee'/%3e%3cpath%20d='m459.7%20100.3-52.9%2052.9c-30.9%2030.9-33.6%2057.8-33.6%20105.3%200%2042.3%206.7%2081.1%2038.2%20112.6%2023%2023%2044.9%2044.7%2044.9%2044.7-5.9%207.2-12.3%2014.3-19.1%2021.2-1.7%202-3.5%203.8-5.5%205.4-6%205.9-12.2%2011.4-18.6%2016.4l-41.4-41.4C334.9%20380.6%20305.6%20377%20257%20377c-46.7%200-78.4%204.3-112.6%2038.5-20.4%2020.4-43.8%2043.9-43.8%2043.9-8.9-6.8-17.3-14.2-25.3-22.4-6.6-6.6-12.8-13.4-18.5-20.3%200%200%2023.1-23.2%2045.2-45.3%2032.7-32.7%2038-70.6%2038-113%200-41.3-6.8-79.8-36.8-109.9C82.2%20127.7%2053.3%2099%2053.3%2099c6.7-8.5%2014-16.7%2021.8-24.5%206.9-6.8%2014-13.1%2021.2-19l48%2048c30.7%2030.7%2070%2038.6%20112.4%2038.6%2043.6%200%2082.8-8.4%20114.7-40.4C391%2082.1%20417%2056.3%20417%2056.3c6.8%205.6%2013.5%2011.6%2020.1%2018.2%208.3%208.3%2015.8%2016.9%2022.6%2025.8'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%233a3f51'/%3e%3cpath%20d='M186%20269.1c-.5-2.8-.8-5.5-.9-8.4-.1-1.6-.1-3.1-.1-4.7%200-1.7%200-3.2.1-4.7%200-.2%200-.3.1-.5%201-17.4%207.9-32.4%2020.5-45.1%2013.9-13.8%2030.6-20.7%2050.2-20.7s36.3%206.9%2050.2%2020.7c13.8%2014%2020.7%2030.8%2020.7%2050.3s-6.9%2036.2-20.7%2050.2c-.5.5-1%201.1-1.5%201.5q-3.45%203.3-7.2%206-18%2013.2-41.4%2013.2c-23.4%200-29.4-4.4-41.3-13.2-3.1-2.2-6.1-4.7-8.9-7.6-10.8-10.6-17.3-22.9-19.8-37'%20style='fill-rule:evenodd;clip-rule:evenodd;fill:%230cf'/%3e%3cpath%20d='m372.7%20141-35.4%2034.6M72.9%2076.8l96.5%2096.1m199.7%20198.9%2065.6%2067.9m4.4-363.3L372.7%20141M76.6%20438.5l64.6-64.7'%20style='fill:none;stroke:%230cf;stroke-width:2;stroke-miterlimit:1'/%3e%3cpath%20d='m372.7%20141-40%2040.6m-193.3-38.5%2040.6%2040.5M141%20374l39.5-41.1m146.2-3.3%2042.6%2042.4'%20style='fill:none;stroke:%230cf;stroke-width:7;stroke-miterlimit:1'/%3e%3c/svg%3e\"","export default \"__VITE_ASSET__B_sq1wA9__\"","export default \"__VITE_ASSET__CITwrObj__\"","import { useCallback, useEffect, useMemo, useRef, useState, type JSX, lazy, Suspense } from \"react\";\nconst ProcessesView = lazy(() => import(\"./pages/ProcessesView\").then(module => ({ default: module.ProcessesView })));\nconst LogsView = lazy(() => import(\"./pages/LogsView\").then(module => ({ default: module.LogsView })));\nconst ArrView = lazy(() => import(\"./pages/ArrView\").then(module => ({ default: module.ArrView })));\nconst ConfigView = lazy(() => import(\"./pages/ConfigView\").then(module => ({ default: module.ConfigView })));\nimport { ToastProvider, ToastViewport, useToast } from \"./context/ToastContext\";\nimport { SearchProvider, useSearch } from \"./context/SearchContext\";\nimport { WebUIProvider, useWebUI } from \"./context/WebUIContext\";\nimport { useNetworkStatus } from \"./hooks/useNetworkStatus\";\nimport { getMeta, getStatus, triggerUpdate } from \"./api/client\";\nimport type { MetaResponse, StatusResponse } from \"./api/types\";\nimport { IconImage } from \"./components/IconImage\";\nimport CloseIcon from \"./icons/close.svg\";\nimport ExternalIcon from \"./icons/github.svg\";\nimport RefreshIcon from \"./icons/refresh-arrow.svg\";\nimport UpdateIcon from \"./icons/up-arrow.svg\";\nimport DownloadIcon from \"./icons/download.svg\";\nimport ProcessesIcon from \"./icons/process.svg\";\nimport LogsIcon from \"./icons/log.svg\";\nimport RadarrIcon from \"./icons/radarr.svg\";\nimport SonarrIcon from \"./icons/sonarr.svg\";\nimport LidarrIcon from \"./icons/lidarr.svg\";\nimport ConfigIcon from \"./icons/gear.svg\";\n\ntype Tab = \"processes\" | \"logs\" | \"radarr\" | \"sonarr\" | \"lidarr\" | \"config\";\n\ninterface NavTab {\n id: Tab;\n label: string;\n icon: string;\n}\n\nfunction formatVersionLabel(value: string | null | undefined): string {\n if (!value) {\n return \"unknown\";\n }\n const trimmed = value.trim();\n if (!trimmed) {\n return \"unknown\";\n }\n return trimmed[0] === \"v\" || trimmed[0] === \"V\" ? trimmed : `v${trimmed}`;\n}\n\ninterface WelcomeModalProps {\n currentVersion: string;\n changelog: string | null;\n changelogUrl: string | null;\n repositoryUrl: string;\n onClose: () => void;\n}\n\nfunction WelcomeModal({\n currentVersion,\n changelog,\n changelogUrl,\n repositoryUrl,\n onClose,\n}: WelcomeModalProps): JSX.Element {\n return (\n <div className=\"modal-backdrop\" role=\"presentation\" onClick={onClose}>\n <div\n className=\"modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"welcome-title\"\n onClick={(event) => event.stopPropagation()}\n >\n <div className=\"modal-header\">\n <h2 id=\"welcome-title\">\n 🎉 Welcome to qBitrr {formatVersionLabel(currentVersion)}!\n </h2>\n </div>\n <div className=\"modal-body changelog-modal__body\">\n <div className=\"changelog-meta\">\n <p style={{ marginBottom: '1rem', color: 'var(--text-secondary)' }}>\n You've been updated to version <strong>{formatVersionLabel(currentVersion)}</strong>.\n Here's what's new in this release:\n </p>\n </div>\n <div className=\"changelog-section\">\n <h3>Release Notes</h3>\n <pre className=\"changelog-body\">\n {changelog?.trim() ? changelog.trim() : \"No changelog available for this version.\"}\n </pre>\n </div>\n </div>\n <div className=\"modal-footer\">\n <div className=\"changelog-links\">\n {(changelogUrl || repositoryUrl) && (\n <a\n className=\"btn ghost small\"\n href={changelogUrl ?? repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <IconImage src={ExternalIcon} />\n View Full Release on GitHub\n </a>\n )}\n </div>\n <div className=\"changelog-buttons\">\n <button className=\"btn primary\" type=\"button\" onClick={onClose}>\n Got it!\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n\ninterface ChangelogModalProps {\n currentVersion: string;\n latestVersion: string | null;\n changelog: string | null;\n changelogUrl: string | null;\n repositoryUrl: string;\n updateState: MetaResponse[\"update_state\"] | null | undefined;\n updating: boolean;\n installationType: MetaResponse[\"installation_type\"];\n binaryDownloadUrl: string | null;\n binaryDownloadName: string | null;\n binaryDownloadSize: number | null;\n binaryDownloadError: string | null;\n onClose: () => void;\n onUpdate: () => void;\n}\n\nfunction ChangelogModal({\n currentVersion,\n latestVersion,\n changelog,\n changelogUrl,\n repositoryUrl,\n updateState,\n updating,\n installationType,\n binaryDownloadUrl,\n binaryDownloadName,\n binaryDownloadSize,\n binaryDownloadError,\n onClose,\n onUpdate,\n}: ChangelogModalProps): JSX.Element {\n const [countdown, setCountdown] = useState<number | null>(null);\n const updateDisabled = updating || Boolean(updateState?.in_progress);\n const completedLabel = updateState?.completed_at\n ? new Date(updateState.completed_at).toLocaleString()\n : null;\n const isBinaryInstall = installationType === \"binary\";\n\n // Start countdown when update completes successfully\n useEffect(() => {\n if (updateState?.last_result === \"success\" && updateState?.completed_at) {\n let countdown = 10;\n setCountdown(countdown);\n const timer = setInterval(() => {\n countdown -= 1;\n if (countdown <= 0) {\n clearInterval(timer);\n window.location.reload();\n } else {\n setCountdown(countdown);\n }\n }, 1000);\n return () => clearInterval(timer);\n }\n }, [updateState?.last_result, updateState?.completed_at]);\n\n let statusClass = \"\";\n let statusMessage: string | null = null;\n if (updateState?.in_progress) {\n statusClass = \"text-info\";\n statusMessage = \"⏳ Update in progress...\";\n } else if (updateState?.last_result === \"success\") {\n statusClass = \"text-success\";\n if (countdown !== null) {\n statusMessage = `✓ Update completed! Reloading in ${countdown}s...`;\n } else {\n statusMessage = \"✓ Update completed successfully\";\n if (completedLabel) {\n statusMessage = `${statusMessage} (${completedLabel})`;\n }\n }\n } else if (updateState?.last_result === \"error\") {\n statusClass = \"text-danger\";\n const detail = updateState.last_error ? updateState.last_error.trim() : \"\";\n statusMessage = detail ? `✗ Update failed: ${detail}` : \"✗ Update failed\";\n }\n\n return (\n <div className=\"modal-backdrop\" role=\"presentation\" onClick={onClose}>\n <div\n className=\"modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"changelog-title\"\n onClick={(event) => event.stopPropagation()}\n >\n <div className=\"modal-header\">\n <h2 id=\"changelog-title\">\n {updateState?.in_progress ? \"⚙️ Updating...\" : \"🚀 Update Available\"}\n </h2>\n <button className=\"btn ghost\" type=\"button\" onClick={onClose} disabled={updateState?.in_progress}>\n <IconImage src={CloseIcon} />\n Close\n </button>\n </div>\n <div className=\"modal-body changelog-modal__body\">\n <div className=\"changelog-meta\">\n <div className=\"version-comparison\">\n <span className=\"version-item\">\n <strong>Current:</strong>{\" \"}\n <span className=\"version-badge version-current\">{formatVersionLabel(currentVersion)}</span>\n </span>\n <span className=\"version-arrow\">→</span>\n <span className=\"version-item\">\n <strong>Latest:</strong>{\" \"}\n <span className=\"version-badge version-latest\">\n {latestVersion ? formatVersionLabel(latestVersion) : \"Unknown\"}\n </span>\n </span>\n </div>\n {statusMessage ? (\n <div className={`update-status ${statusClass}`}>\n {statusMessage}\n </div>\n ) : null}\n </div>\n <div className=\"changelog-section\">\n <h3>What's New</h3>\n <pre className=\"changelog-body\">\n {changelog?.trim() ? changelog.trim() : \"No changelog provided.\"}\n </pre>\n </div>\n </div>\n <div className=\"modal-footer\">\n <div className=\"changelog-links\">\n {(changelogUrl || repositoryUrl) && (\n <a\n className=\"btn ghost small\"\n href={changelogUrl ?? repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <IconImage src={ExternalIcon} />\n View on GitHub\n </a>\n )}\n </div>\n <div className=\"changelog-buttons\">\n {isBinaryInstall ? (\n binaryDownloadError ? (\n <div className=\"update-status text-danger\" style={{ marginBottom: '0.5rem' }}>\n {binaryDownloadError}\n </div>\n ) : binaryDownloadUrl ? (\n <>\n <a\n className=\"btn primary\"\n href={`/web/download-update`}\n download={binaryDownloadName ?? undefined}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n <IconImage src={DownloadIcon} />\n Download Update\n {binaryDownloadSize && binaryDownloadSize > 0 ? (\n <span style={{ marginLeft: '0.5rem', opacity: 0.8, fontSize: '0.875rem' }}>\n ({(binaryDownloadSize / (1024 * 1024)).toFixed(1)} MB)\n </span>\n ) : null}\n </a>\n <div style={{ fontSize: '0.875rem', color: 'var(--text-secondary)', marginTop: '0.5rem' }}>\n Binary installation detected. Download and manually replace the executable.\n </div>\n </>\n ) : (\n <div className=\"update-status text-danger\">\n Unable to fetch binary download URL. Please update manually.\n </div>\n )\n ) : (\n <button\n className=\"btn primary\"\n type=\"button\"\n onClick={onUpdate}\n disabled={updateDisabled}\n >\n <IconImage src={UpdateIcon} />\n {updateDisabled ? \"Updating...\" : \"Update Now\"}\n </button>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction AppShell(): JSX.Element {\n const [activeTab, setActiveTab] = useState<Tab>(\"processes\");\n const [configDirty, setConfigDirty] = useState(false);\n const { push } = useToast();\n const { setValue: setSearchValue } = useSearch();\n const { viewDensity, setViewDensity } = useWebUI();\n const isOnline = useNetworkStatus();\n const [meta, setMeta] = useState<MetaResponse | null>(null);\n const [metaLoading, setMetaLoading] = useState(false);\n const [showChangelog, setShowChangelog] = useState(false);\n const [updateBusy, setUpdateBusy] = useState(false);\n const [backendRestarting, setBackendRestarting] = useState(false);\n const restartPollCount = useRef(0);\n const prevUpdateResult = useRef<string | null>(null);\n const backendReadyRef = useRef(false);\n const backendWarnedRef = useRef(false);\n const backendTimerRef = useRef<number | null>(null);\n const [reloadKey, setReloadKey] = useState(0);\n const [statusData, setStatusData] = useState<StatusResponse | null>(null);\n const [showWelcomeChangelog, setShowWelcomeChangelog] = useState(false);\n\n // Theme is now managed by WebUIContext and applied automatically\n\n // Clear cache on every page load to ensure fresh content\n useEffect(() => {\n const clearCache = async () => {\n if ('caches' in window) {\n try {\n const cacheNames = await caches.keys();\n await Promise.all(\n cacheNames.map(cacheName => caches.delete(cacheName))\n );\n console.log('Cache cleared on page load');\n } catch (error) {\n console.error('Failed to clear cache:', error);\n }\n }\n };\n clearCache();\n }, []);\n\n const refreshMeta = useCallback(\n async (options?: { force?: boolean; silent?: boolean }) => {\n const force = options?.force ?? false;\n const silent = options?.silent ?? !force;\n if (!silent) {\n setMetaLoading(true);\n }\n try {\n const data = await getMeta({ force });\n setMeta(data);\n } catch (error) {\n if (!silent) {\n const message =\n error instanceof Error ? error.message : \"Failed to fetch version information\";\n push(message, \"error\");\n }\n } finally {\n if (!silent) {\n setMetaLoading(false);\n }\n }\n },\n [push]\n );\n\n useEffect(() => {\n void refreshMeta({ force: true });\n }, [refreshMeta]);\n\n // Check for new version on first launch - show welcome popup with changelog\n useEffect(() => {\n if (!meta?.current_version) {\n return;\n }\n\n const lastSeenVersion = localStorage.getItem(\"lastSeenVersion\");\n const currentVersion = meta.current_version;\n\n // Show welcome popup if this is a new version (but not on very first install)\n if (lastSeenVersion && lastSeenVersion !== currentVersion) {\n // Ensure we have changelog data before showing popup\n if (!meta.current_version_changelog && !meta.changelog) {\n void refreshMeta({ force: true, silent: true });\n }\n setShowWelcomeChangelog(true);\n }\n\n // Store current version as last seen when user opens the app (first install)\n if (!lastSeenVersion) {\n localStorage.setItem(\"lastSeenVersion\", currentVersion);\n }\n }, [meta?.current_version, meta?.changelog, refreshMeta]);\n\n // Network status notifications\n useEffect(() => {\n if (!isOnline) {\n push(\"You are offline. Some features may not work.\", \"warning\");\n }\n }, [isOnline, push]);\n\n // Keyboard shortcuts\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n // Don't trigger shortcuts when typing in inputs\n if (event.target instanceof HTMLInputElement ||\n event.target instanceof HTMLTextAreaElement ||\n event.target instanceof HTMLSelectElement) {\n return;\n }\n\n const isMod = event.ctrlKey || event.metaKey;\n\n // Ctrl/Cmd + K - Focus search\n if (isMod && event.key === 'k') {\n event.preventDefault();\n const searchInput = document.querySelector('input[type=\"text\"][placeholder*=\"Search\"]') as HTMLInputElement;\n searchInput?.focus();\n return;\n }\n\n // ESC - Clear search\n if (event.key === 'Escape') {\n setSearchValue('');\n return;\n }\n\n // Number keys 1-6 for tab switching\n if (event.key >= '1' && event.key <= '6' && !isMod) {\n event.preventDefault();\n const tabIndex = parseInt(event.key) - 1;\n const tabIds: Tab[] = ['processes', 'logs', 'radarr', 'sonarr', 'lidarr', 'config'];\n if (tabIndex < tabIds.length) {\n setActiveTab(tabIds[tabIndex]);\n }\n return;\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [setSearchValue, push]);\n\n useEffect(() => {\n const id = window.setInterval(() => {\n void refreshMeta();\n }, 5 * 60 * 1000);\n return () => window.clearInterval(id);\n }, [refreshMeta]);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (document.visibilityState === \"visible\") {\n // Force reload all data by incrementing the reload key\n setReloadKey((prev) => prev + 1);\n void refreshMeta({ force: true });\n void refreshStatus();\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n return () => {\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [refreshMeta]);\n\n const refreshStatus = useCallback(async () => {\n try {\n const status = await getStatus();\n setStatusData(status);\n } catch {\n // Silently fail - status is not critical\n }\n }, []);\n\n useEffect(() => {\n void refreshStatus();\n const id = window.setInterval(() => {\n void refreshStatus();\n }, 5 * 1000); // Refresh every 5 seconds for more dynamic tab loading\n return () => window.clearInterval(id);\n }, [refreshStatus]);\n\n useEffect(() => {\n if (!meta?.update_state?.in_progress && !backendRestarting) {\n restartPollCount.current = 0;\n return;\n }\n const id = window.setInterval(async () => {\n try {\n const data = await getMeta({ force: true });\n setMeta(data);\n if (backendRestarting) {\n // Backend came back after restart\n window.location.reload();\n }\n restartPollCount.current = 0;\n } catch {\n restartPollCount.current += 1;\n if (restartPollCount.current > 20) { // 60 seconds\n setBackendRestarting(false);\n restartPollCount.current = 0;\n push(\"Update completed but backend restart timed out. Please refresh the page manually.\", \"warning\");\n return;\n }\n if (meta?.update_state?.in_progress) {\n // Failed while update in progress, likely restarting\n setBackendRestarting(true);\n }\n }\n }, 3000);\n return () => window.clearInterval(id);\n }, [meta?.update_state?.in_progress, backendRestarting, meta, push]);\n\n useEffect(() => {\n const state = meta?.update_state;\n if (!state) {\n prevUpdateResult.current = null;\n return;\n }\n const result = state.last_result ?? null;\n if (result && result !== prevUpdateResult.current) {\n if (result === \"success\") {\n push(\"Update completed successfully. Restarting...\", \"success\");\n setBackendRestarting(true);\n restartPollCount.current = 0;\n } else if (result === \"error\") {\n push(state.last_error || \"Update failed.\", \"error\");\n }\n }\n prevUpdateResult.current = result;\n }, [meta?.update_state, push]);\n\n useEffect(() => {\n let cancelled = false;\n let attempts = 0;\n\n const schedule = (delay: number) => {\n if (backendTimerRef.current !== null) {\n window.clearTimeout(backendTimerRef.current);\n }\n backendTimerRef.current = window.setTimeout(() => {\n void poll();\n }, delay);\n };\n\n const poll = async () => {\n if (cancelled || backendReadyRef.current) {\n return;\n }\n attempts += 1;\n try {\n const status = await getStatus();\n if (cancelled) {\n return;\n }\n setStatusData(status);\n const readyHint =\n status.ready ?? (Array.isArray(status.arrs) && status.arrs.length > 0);\n if (readyHint) {\n backendReadyRef.current = true;\n return;\n }\n if (status.ready === false && attempts >= 3 && !backendWarnedRef.current) {\n backendWarnedRef.current = true;\n push(\n \"qBitrr backend is still initialising. Check the logs if this persists.\",\n \"warning\"\n );\n }\n } catch (error) {\n if (!backendWarnedRef.current && attempts >= 3) {\n backendWarnedRef.current = true;\n const detail = error instanceof Error ? error.message : \"Unknown backend error\";\n push(\n `Unable to confirm qBitrr readiness (${detail}). Please inspect the logs.`,\n \"warning\"\n );\n }\n } finally {\n if (!cancelled && !backendReadyRef.current) {\n const delay = attempts < 3 ? 3000 : 10000;\n schedule(delay);\n }\n }\n };\n\n schedule(0);\n\n return () => {\n cancelled = true;\n if (backendTimerRef.current !== null) {\n window.clearTimeout(backendTimerRef.current);\n backendTimerRef.current = null;\n }\n };\n }, [push]);\n\n const tabs = useMemo<NavTab[]>(() => {\n const baseTabs: NavTab[] = [\n { id: \"processes\", label: \"Processes\", icon: ProcessesIcon },\n { id: \"logs\", label: \"Logs\", icon: LogsIcon },\n ];\n\n const arrTabs: NavTab[] = [];\n const arrs = statusData?.arrs ?? [];\n\n const hasRadarr = arrs.some((arr) => arr.type === \"radarr\");\n const hasSonarr = arrs.some((arr) => arr.type === \"sonarr\");\n const hasLidarr = arrs.some((arr) => arr.type === \"lidarr\");\n\n if (hasRadarr) {\n arrTabs.push({ id: \"radarr\", label: \"Radarr\", icon: RadarrIcon });\n }\n if (hasSonarr) {\n arrTabs.push({ id: \"sonarr\", label: \"Sonarr\", icon: SonarrIcon });\n }\n if (hasLidarr) {\n arrTabs.push({ id: \"lidarr\", label: \"Lidarr\", icon: LidarrIcon });\n }\n\n return [\n ...baseTabs,\n ...arrTabs,\n { id: \"config\", label: \"Config\", icon: ConfigIcon },\n ];\n }, [statusData]);\n\n const repositoryUrl = meta?.repository_url ?? \"https://github.com/Feramance/qBitrr\";\n const displayVersion = meta?.current_version\n ? formatVersionLabel(meta.current_version)\n : \"...\";\n const latestVersion = meta?.latest_version ?? null;\n const updateAvailable = Boolean(meta?.update_available);\n const updateState = meta?.update_state;\n const changelogUrl = meta?.changelog_url ?? repositoryUrl;\n\n const versionTitleParts: string[] = [];\n if (meta?.last_checked) {\n versionTitleParts.push(`Last checked ${new Date(meta.last_checked).toLocaleString()}`);\n }\n if (meta?.error) {\n versionTitleParts.push(`Update check failed: ${meta.error}`);\n }\n const versionTitle = versionTitleParts.length ? versionTitleParts.join(\" • \") : undefined;\n\n // Redirect to processes if active tab is no longer available\n useEffect(() => {\n const tabExists = tabs.some((tab) => tab.id === activeTab);\n if (!tabExists && tabs.length > 0) {\n setActiveTab(\"processes\");\n }\n }, [tabs, activeTab]);\n\n const handleCheckUpdates = useCallback(() => {\n void refreshMeta({ force: true });\n }, [refreshMeta]);\n\n const handleOpenChangelog = useCallback(() => {\n setShowChangelog(true);\n if (!meta?.changelog) {\n void refreshMeta({ force: true, silent: true });\n }\n }, [meta?.changelog, refreshMeta]);\n\n const handleCloseChangelog = useCallback(() => {\n setShowChangelog(false);\n }, []);\n\n const handleCloseWelcomeChangelog = useCallback(() => {\n setShowWelcomeChangelog(false);\n // Mark this version as seen\n if (meta?.current_version) {\n localStorage.setItem(\"lastSeenVersion\", meta.current_version);\n }\n }, [meta?.current_version]);\n\n const handleTriggerUpdate = useCallback(async () => {\n setUpdateBusy(true);\n setBackendRestarting(false);\n restartPollCount.current = 0;\n try {\n await triggerUpdate();\n push(\"Update started in the background.\", \"info\");\n await refreshMeta({ force: true, silent: true });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to start update\";\n push(message, \"error\");\n } finally {\n setUpdateBusy(false);\n }\n }, [push, refreshMeta]);\n\n return (\n <div data-density={viewDensity}>\n <header className=\"appbar\">\n <div className=\"appbar__inner\">\n <div className=\"appbar__title\">\n <h1>qBitrr</h1>\n <span className=\"appbar__version\" title={versionTitle}>\n {displayVersion}\n </span>\n {metaLoading ? <span className=\"spinner\" aria-hidden=\"true\" /> : null}\n {updateState?.in_progress ? (\n <span className=\"appbar__status text-info\">Updating...</span>\n ) : null}\n {updateAvailable ? (\n <button\n type=\"button\"\n className=\"btn small primary appbar__update\"\n onClick={handleOpenChangelog}\n disabled={updateBusy || Boolean(updateState?.in_progress)}\n >\n <span className=\"appbar__update-indicator\" aria-hidden=\"true\" />\n <IconImage src={UpdateIcon} />\n Update available\n </button>\n ) : null}\n </div>\n <div className=\"appbar__actions\">\n {!isOnline && (\n <span className=\"badge\" style={{ background: 'rgba(239, 68, 68, 0.15)', borderColor: 'rgba(239, 68, 68, 0.3)', color: 'var(--danger)' }}>\n Offline\n </span>\n )}\n <div className=\"view-density-toggle\">\n <button\n type=\"button\"\n className={viewDensity === \"comfortable\" ? \"active\" : \"\"}\n onClick={() => setViewDensity(\"comfortable\")}\n title=\"Comfortable view\"\n >\n Comfortable\n </button>\n <button\n type=\"button\"\n className={viewDensity === \"compact\" ? \"active\" : \"\"}\n onClick={() => setViewDensity(\"compact\")}\n title=\"Compact view\"\n >\n Compact\n </button>\n </div>\n <button\n type=\"button\"\n className=\"btn small ghost\"\n onClick={handleCheckUpdates}\n disabled={metaLoading}\n >\n <IconImage src={RefreshIcon} />\n {metaLoading ? \"Checking...\" : \"Check Updates\"}\n </button>\n <a\n href={repositoryUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"btn small ghost\"\n >\n <IconImage src={ExternalIcon} />\n GitHub\n </a>\n </div>\n </div>\n </header>\n <main className=\"container\">\n <nav className=\"nav\">\n {tabs.map((tab) => (\n <button\n type=\"button\"\n key={tab.id}\n className={activeTab === tab.id ? \"active\" : \"\"}\n onClick={() => {\n if (activeTab === \"config\" && tab.id !== \"config\" && configDirty) {\n const shouldLeave = window.confirm(\n \"You have unsaved configuration changes. Leave without saving?\"\n );\n if (!shouldLeave) {\n return;\n }\n }\n setActiveTab(tab.id);\n setSearchValue(\"\");\n }}\n >\n <IconImage src={tab.icon} />\n <span>{tab.label}</span>\n </button>\n ))}\n </nav>\n <Suspense fallback={<div className=\"loading\">Loading...</div>}>\n {activeTab === \"processes\" && <ProcessesView key={`processes-${reloadKey}`} active />}\n {activeTab === \"logs\" && <LogsView key={`logs-${reloadKey}`} active />}\n {activeTab === \"radarr\" && <ArrView key={`radarr-${reloadKey}`} type=\"radarr\" active />}\n {activeTab === \"sonarr\" && <ArrView key={`sonarr-${reloadKey}`} type=\"sonarr\" active />}\n {activeTab === \"lidarr\" && <ArrView key={`lidarr-${reloadKey}`} type=\"lidarr\" active />}\n {activeTab === \"config\" && <ConfigView key={`config-${reloadKey}`} onDirtyChange={setConfigDirty} />}\n </Suspense>\n </main>\n {showChangelog && meta ? (\n <ChangelogModal\n currentVersion={meta.current_version}\n latestVersion={latestVersion}\n changelog={meta.changelog}\n changelogUrl={changelogUrl}\n repositoryUrl={repositoryUrl}\n updateState={updateState}\n updating={updateBusy}\n installationType={meta.installation_type}\n binaryDownloadUrl={meta.binary_download_url}\n binaryDownloadName={meta.binary_download_name}\n binaryDownloadSize={meta.binary_download_size}\n binaryDownloadError={meta.binary_download_error}\n onClose={handleCloseChangelog}\n onUpdate={handleTriggerUpdate}\n />\n ) : null}\n {showWelcomeChangelog && meta ? (\n <WelcomeModal\n currentVersion={meta.current_version}\n changelog={meta.current_version_changelog || meta.changelog}\n changelogUrl={changelogUrl}\n repositoryUrl={repositoryUrl}\n onClose={handleCloseWelcomeChangelog}\n />\n ) : null}\n </div>\n );\n}\n\nexport default function App(): JSX.Element {\n return (\n <ToastProvider>\n <SearchProvider>\n <WebUIProvider>\n <AppShell />\n <ToastViewport />\n </WebUIProvider>\n </SearchProvider>\n </ToastProvider>\n );\n}\n","import { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"./styles.css\";\nimport App from \"./App\";\n\ncreateRoot(document.getElementById(\"root\")!).render(\n <StrictMode>\n <App />\n </StrictMode>\n);\n"],"file":"app.js"}
|