import { a as setSessionUser, i as logout, n as isAuthenticated, o as backendClient, r as login, t as IS_FRONTEND_AUTH_DISABLED } from "./authentication.server-DQtqLuXr.js";
import { Form, Link, Links, Meta, Outlet, Scripts, ScrollRestoration, ServerRouter, UNSAFE_withComponentProps, redirect, useBlocker, useLocation, useNavigate, useNavigation } from "react-router";
import { PassThrough } from "node:stream";
import { createReadableStreamFromReadable } from "@react-router/node";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import React, { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
import { Accordion, Alert, Badge, Button, Card, Form as Form$1, InputGroup, Modal, OverlayTrigger, Spinner, Tab, Table, Tabs, Tooltip } from "react-bootstrap";
import { lookup } from "mime-types";
import { createHash } from "node:crypto";
import { useDropzone } from "react-dropzone";
//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
	let target = {};
	for (var name in all) __defProp(target, name, {
		get: all[name],
		enumerable: true
	});
	if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
	return target;
};
//#endregion
//#region node_modules/@react-router/dev/dist/config/defaults/entry.server.node.tsx
var entry_server_node_exports = /* @__PURE__ */ __exportAll({
	default: () => handleRequest,
	streamTimeout: () => streamTimeout
});
var streamTimeout = 5e3;
function handleRequest(request, responseStatusCode, responseHeaders, routerContext, loadContext) {
	if (request.method.toUpperCase() === "HEAD") return new Response(null, {
		status: responseStatusCode,
		headers: responseHeaders
	});
	return new Promise((resolve, reject) => {
		let shellRendered = false;
		let userAgent = request.headers.get("user-agent");
		let readyOption = userAgent && isbot(userAgent) || routerContext.isSpaMode ? "onAllReady" : "onShellReady";
		let timeoutId = setTimeout(() => abort(), streamTimeout + 1e3);
		const { pipe, abort } = renderToPipeableStream(/* @__PURE__ */ jsx(ServerRouter, {
			context: routerContext,
			url: request.url
		}), {
			[readyOption]() {
				shellRendered = true;
				const body = new PassThrough({ final(callback) {
					clearTimeout(timeoutId);
					timeoutId = void 0;
					callback();
				} });
				const stream = createReadableStreamFromReadable(body);
				responseHeaders.set("Content-Type", "text/html");
				pipe(body);
				resolve(new Response(stream, {
					headers: responseHeaders,
					status: responseStatusCode
				}));
			},
			onShellError(error) {
				reject(error);
			},
			onError(error) {
				responseStatusCode = 500;
				if (shellRendered) console.error(error);
			}
		});
	});
}
var top_navigation_module_default = {
	container: "_container_tmf9c_1",
	"title-container": "_title-container_tmf9c_11",
	title: "_title_tmf9c_11",
	logo: "_logo_tmf9c_28"
};
var hamburger_menu_module_default = {
	container: "_container_89ilb_1",
	"hamburger-icon": "_hamburger-icon_89ilb_22",
	"close-icon": "_close-icon_89ilb_28"
};
//#endregion
//#region app/routes/_index/components/hamburger-menu/hamburger-menu.tsx
function HamburgerMenu(props) {
	const icon = /* @__PURE__ */ jsx("div", {
		className: hamburger_menu_module_default[props.isOpen ? "close-icon" : "hamburger-icon"],
		onClick: props.onClick
	});
	return /* @__PURE__ */ jsx("div", {
		className: hamburger_menu_module_default.container,
		children: icon
	});
}
//#endregion
//#region app/routes/_index/components/top-navigation/top-navigation.tsx
var TopNavigation = memo(function TopNavigation(props) {
	const { isHamburgerMenuOpen, onHamburgerMenuClick } = props;
	const navigate = useNavigate();
	return /* @__PURE__ */ jsxs("div", {
		className: top_navigation_module_default["container"],
		children: [/* @__PURE__ */ jsx(HamburgerMenu, {
			isOpen: isHamburgerMenuOpen,
			onClick: onHamburgerMenuClick
		}), /* @__PURE__ */ jsxs("div", {
			className: top_navigation_module_default["title-container"],
			onClick: () => navigate("/"),
			children: [/* @__PURE__ */ jsx("img", {
				className: top_navigation_module_default["logo"],
				src: "/logo.svg"
			}), /* @__PURE__ */ jsx("div", {
				className: top_navigation_module_default["title"],
				children: "Nzb DAV"
			})]
		})]
	});
});
var left_navigation_module_default = {
	container: "_container_mz8gf_1",
	unstyled: "_unstyled_mz8gf_10",
	item: "_item_mz8gf_15",
	selected: "_selected_mz8gf_29",
	"queue-icon": "_queue-icon_mz8gf_44",
	"explore-icon": "_explore-icon_mz8gf_53",
	"stream-icon": "_stream-icon_mz8gf_62",
	"settings-icon": "_settings-icon_mz8gf_71",
	"health-icon": "_health-icon_mz8gf_80",
	title: "_title_mz8gf_89",
	footer: "_footer_mz8gf_93",
	"footer-item": "_footer-item_mz8gf_101",
	"github-link": "_github-link_mz8gf_109",
	"github-icon": "_github-icon_mz8gf_125",
	"logout-icon": "_logout-icon_mz8gf_134"
};
//#endregion
//#region app/utils/styling.ts
function className(classNames) {
	return { className: classNames.filter(Boolean).join(" ") };
}
function classNames(classNames) {
	return classNames.filter(Boolean).join(" ");
}
var live_usenet_connections_module_default = {
	container: "_container_rct7x_1",
	title: "_title_rct7x_6",
	bar: "_bar_rct7x_11",
	max: "_max_rct7x_17",
	live: "_live_rct7x_25",
	active: "_active_rct7x_33",
	caption: "_caption_rct7x_41"
};
//#endregion
//#region app/utils/websocket-util.ts
function receiveMessage(onMessage) {
	return (event) => {
		var parsed = JSON.parse(event.data);
		onMessage(parsed.Topic, parsed.Message);
	};
}
//#endregion
//#region app/routes/_index/components/live-usenet-connections/live-usenet-connections.tsx
var usenetConnectionsTopic$1 = { "cxs": "state" };
function LiveUsenetConnections() {
	const navigate = useNavigate();
	const [connections, setConnections] = useState(null);
	const [_0, _1, _2, live, max, idle] = (connections || "0|0|0|0|1|0").split("|").map((x) => Number(x));
	const active = live - idle;
	const activePercent = 100 * (active / max);
	const livePercent = 100 * (live / max);
	useEffect(() => {
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage((_, message) => setConnections(message));
			ws.onopen = () => ws.send(JSON.stringify(usenetConnectionsTopic$1));
			ws.onerror = () => {
				ws.close();
			};
			ws.onclose = onClose;
			return () => {
				disposed = true;
				ws.close();
			};
		}
		function onClose(e) {
			if (e.code == 1008) navigate("/login");
			!disposed && setTimeout(() => connect(), 1e3);
			setConnections(null);
		}
		return connect();
	}, [setConnections]);
	return /* @__PURE__ */ jsxs("div", {
		className: live_usenet_connections_module_default.container,
		children: [
			/* @__PURE__ */ jsx("div", {
				className: live_usenet_connections_module_default.title,
				children: "Usenet Connections"
			}),
			/* @__PURE__ */ jsxs("div", {
				className: live_usenet_connections_module_default.bar,
				children: [
					/* @__PURE__ */ jsx("div", { className: live_usenet_connections_module_default.max }),
					/* @__PURE__ */ jsx("div", {
						className: live_usenet_connections_module_default.live,
						style: { width: `${livePercent}%` }
					}),
					/* @__PURE__ */ jsx("div", {
						className: live_usenet_connections_module_default.active,
						style: { width: `${activePercent}%` }
					})
				]
			}),
			/* @__PURE__ */ jsxs("div", {
				className: live_usenet_connections_module_default.caption,
				children: [connections && `${live} connected / ${max} max`, !connections && `Loading...`]
			}),
			connections && /* @__PURE__ */ jsxs("div", {
				className: live_usenet_connections_module_default.caption,
				children: [
					"( ",
					active,
					" active )"
				]
			})
		]
	});
}
//#endregion
//#region app/routes/_index/components/left-navigation/left-navigation.tsx
function LeftNavigation({ version, isFrontendAuthDisabled }) {
	return /* @__PURE__ */ jsxs("div", {
		className: left_navigation_module_default.container,
		children: [
			/* @__PURE__ */ jsxs(Item, {
				target: "/queue",
				children: [/* @__PURE__ */ jsx("div", { className: left_navigation_module_default["queue-icon"] }), /* @__PURE__ */ jsx("div", {
					className: left_navigation_module_default.title,
					children: "Queue & History"
				})]
			}),
			/* @__PURE__ */ jsxs(Item, {
				target: "/explore",
				children: [/* @__PURE__ */ jsx("div", { className: left_navigation_module_default["explore-icon"] }), /* @__PURE__ */ jsx("div", {
					className: left_navigation_module_default.title,
					children: "Dav Explore"
				})]
			}),
			/* @__PURE__ */ jsxs(Item, {
				target: "/health",
				children: [/* @__PURE__ */ jsx("div", { className: left_navigation_module_default["health-icon"] }), /* @__PURE__ */ jsx("div", {
					className: left_navigation_module_default.title,
					children: "Health"
				})]
			}),
			/* @__PURE__ */ jsxs(Item, {
				target: "/settings",
				children: [/* @__PURE__ */ jsx("div", { className: left_navigation_module_default["settings-icon"] }), /* @__PURE__ */ jsx("div", {
					className: left_navigation_module_default.title,
					children: "Settings"
				})]
			}),
			/* @__PURE__ */ jsx(LiveUsenetConnections, {}),
			/* @__PURE__ */ jsxs("div", {
				className: left_navigation_module_default.footer,
				children: [
					/* @__PURE__ */ jsxs("div", {
						className: left_navigation_module_default["footer-item"],
						children: [/* @__PURE__ */ jsx(Link, {
							to: "https://github.com/nzbdav-dev/nzbdav",
							className: left_navigation_module_default["github-link"],
							children: "github"
						}), /* @__PURE__ */ jsx("div", { className: left_navigation_module_default["github-icon"] })]
					}),
					/* @__PURE__ */ jsx("div", {
						className: left_navigation_module_default["footer-item"],
						children: /* @__PURE__ */ jsx(Link, {
							to: "https://github.com/nzbdav-dev/nzbdav/releases",
							className: left_navigation_module_default["github-link"],
							children: "changelog"
						})
					}),
					/* @__PURE__ */ jsxs("div", {
						className: left_navigation_module_default["footer-item"],
						children: ["version: ", version || "unknown"]
					}),
					!isFrontendAuthDisabled && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("hr", {}), /* @__PURE__ */ jsxs(Form, {
						method: "post",
						action: "/logout",
						children: [/* @__PURE__ */ jsx("input", {
							name: "confirm",
							value: "true",
							type: "hidden"
						}), /* @__PURE__ */ jsxs("button", {
							className: left_navigation_module_default.unstyled + " " + left_navigation_module_default.item,
							type: "submit",
							children: [/* @__PURE__ */ jsx("div", { className: left_navigation_module_default["logout-icon"] }), /* @__PURE__ */ jsx("div", {
								className: left_navigation_module_default.title,
								children: "Logout"
							})]
						})]
					})] })
				]
			})
		]
	});
}
function Item({ target, children }) {
	const location = useLocation();
	const isSelected = (useNavigation().location?.pathname ?? location.pathname).startsWith(target);
	return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Link, {
		...className([left_navigation_module_default.item, isSelected ? left_navigation_module_default.selected : null]),
		to: target,
		children
	}) });
}
var page_layout_module_default = {
	container: "_container_1v1fq_1",
	"top-navigation": "_top-navigation_1v1fq_10",
	page: "_page_1v1fq_19",
	"left-navigation": "_left-navigation_1v1fq_26",
	body: "_body_1v1fq_37",
	"hamburger-open": "_hamburger-open_1v1fq_62"
};
//#endregion
//#region app/routes/_index/components/page-layout/page-layout.tsx
function PageLayout(props) {
	const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false);
	const isNavigating = Boolean(useNavigation().location);
	useEffect(() => {
		!isNavigating && setIsHamburgerMenuOpen(false);
	}, [isNavigating, setIsHamburgerMenuOpen]);
	const onHamburgerMenuClick = useCallback(function() {
		setIsHamburgerMenuOpen(!isHamburgerMenuOpen);
	}, [setIsHamburgerMenuOpen, isHamburgerMenuOpen]);
	const onBodyClick = useCallback(function() {
		setIsHamburgerMenuOpen(false);
	}, [setIsHamburgerMenuOpen]);
	let containerClassName = page_layout_module_default["container"];
	if (isHamburgerMenuOpen) containerClassName += " " + page_layout_module_default["hamburger-open"];
	return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
		className: containerClassName,
		children: [/* @__PURE__ */ jsx("div", {
			className: page_layout_module_default["top-navigation"],
			children: /* @__PURE__ */ jsx(props.topNavComponent, {
				isHamburgerMenuOpen,
				onHamburgerMenuClick
			})
		}), /* @__PURE__ */ jsxs("div", {
			className: page_layout_module_default["page"],
			children: [/* @__PURE__ */ jsx("div", {
				className: page_layout_module_default["left-navigation"],
				children: props.leftNavChild
			}), /* @__PURE__ */ jsx("div", {
				className: page_layout_module_default["body"],
				onClick: onBodyClick,
				children: props.bodyChild
			})]
		})]
	}) });
}
var loading_module_default = {
	container: "_container_1knun_1",
	"loader-ring": "_loader-ring_1knun_10",
	spin: "_spin_1knun_1",
	"loading-text": "_loading-text_1knun_34"
};
//#endregion
//#region app/routes/_index/components/loading/loading.tsx
function Loading({ className }) {
	return /* @__PURE__ */ jsxs("div", {
		className: `${loading_module_default.container} ${className ? className : ""}`,
		children: [/* @__PURE__ */ jsx("div", { className: loading_module_default["loader-ring"] }), /* @__PURE__ */ jsx("div", {
			className: loading_module_default["loading-text"],
			children: "Loading..."
		})]
	});
}
//#endregion
//#region app/root.tsx
var root_exports = /* @__PURE__ */ __exportAll({
	Layout: () => Layout,
	default: () => root_default,
	loader: () => loader$7
});
async function loader$7({ request }) {
	let path = new URL(request.url).pathname;
	if (path === "/login") return { useLayout: false };
	if (path === "/onboarding") return { useLayout: false };
	return {
		useLayout: true,
		version: process.env.NZBDAV_VERSION,
		isFrontendAuthDisabled: IS_FRONTEND_AUTH_DISABLED
	};
}
function Layout({ children }) {
	return /* @__PURE__ */ jsxs("html", {
		lang: "en",
		"data-bs-theme": "dark",
		children: [/* @__PURE__ */ jsxs("head", { children: [
			/* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
			/* @__PURE__ */ jsx("meta", {
				name: "viewport",
				content: "width=device-width, initial-scale=1"
			}),
			/* @__PURE__ */ jsx("link", {
				rel: "icon",
				href: "/logo.svg"
			}),
			/* @__PURE__ */ jsx(Meta, {}),
			/* @__PURE__ */ jsx(Links, {})
		] }), /* @__PURE__ */ jsxs("body", { children: [
			children,
			/* @__PURE__ */ jsx(ScrollRestoration, {}),
			/* @__PURE__ */ jsx(Scripts, {})
		] })]
	});
}
var root_default = UNSAFE_withComponentProps(function App({ loaderData }) {
	const { useLayout, version, isFrontendAuthDisabled } = loaderData;
	const location = useLocation();
	const navigation = useNavigation();
	const isNavigating = Boolean(navigation.location);
	const isCurrentExplorePage = location.pathname.startsWith("/explore");
	const isNextExplorePage = navigation.location?.pathname?.startsWith("/explore");
	const showLoading = isNavigating && !(isCurrentExplorePage && isNextExplorePage);
	if (useLayout) return /* @__PURE__ */ jsx(PageLayout, {
		topNavComponent: TopNavigation,
		bodyChild: showLoading ? /* @__PURE__ */ jsx(Loading, {}) : /* @__PURE__ */ jsx(Outlet, {}),
		leftNavChild: /* @__PURE__ */ jsx(LeftNavigation, {
			version,
			isFrontendAuthDisabled
		})
	});
	return /* @__PURE__ */ jsx(Outlet, {});
});
var route_module_default$5 = {
	container: "_container_nv67c_1",
	logo: "_logo_nv67c_13",
	title: "_title_nv67c_17",
	alert: "_alert_nv67c_25"
};
//#endregion
//#region app/routes/onboarding/route.tsx
var route_exports$8 = /* @__PURE__ */ __exportAll({
	action: () => action$3,
	default: () => route_default$5,
	loader: () => loader$6
});
async function loader$6({ request }) {
	if (await isAuthenticated(request)) return redirect("/");
	if (!await backendClient.isOnboarding()) return redirect("/login");
	return { error: null };
}
var route_default$5 = UNSAFE_withComponentProps(function Index({ loaderData, actionData }) {
	var pageData = actionData || loaderData;
	const [username, setUsername] = useState("");
	const [password, setPassword] = useState("");
	const [confirmPassword, setConfirmPassword] = useState("");
	const isLoading = useNavigation().state == "submitting";
	var submitButtonDisabled = false;
	var submitButtonText = "Register";
	if (isLoading) {
		submitButtonDisabled = true;
		submitButtonText = "Registering...";
	} else if (username == "") {
		submitButtonDisabled = true;
		submitButtonText = "Username is required";
	} else if (password === "") {
		submitButtonDisabled = true;
		submitButtonText = "Password is required";
	} else if (password != confirmPassword) {
		submitButtonDisabled = true;
		submitButtonText = "Passwords must match";
	}
	return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Form, {
		className: route_module_default$5["container"],
		method: "POST",
		children: [
			/* @__PURE__ */ jsx("img", {
				className: route_module_default$5["logo"],
				src: "/logo.svg"
			}),
			/* @__PURE__ */ jsx("div", {
				className: route_module_default$5["title"],
				children: "Nzb DAV"
			}),
			pageData.error && /* @__PURE__ */ jsx(Alert, {
				className: route_module_default$5["alert"],
				variant: "danger",
				children: pageData.error
			}),
			!pageData.error && /* @__PURE__ */ jsxs(Alert, {
				className: route_module_default$5["alert"],
				variant: "warning",
				children: [/* @__PURE__ */ jsx("p", {
					style: { marginBottom: "5px" },
					children: "Welcome!"
				}), "Register your admin account."]
			}),
			/* @__PURE__ */ jsx(Form$1.Control, {
				autoFocus: true,
				name: "username",
				type: "text",
				placeholder: "Username",
				value: username,
				onChange: (e) => setUsername(e.currentTarget.value)
			}),
			/* @__PURE__ */ jsx(Form$1.Control, {
				name: "password",
				type: "password",
				placeholder: "Password",
				value: password,
				onChange: (e) => setPassword(e.currentTarget.value)
			}),
			/* @__PURE__ */ jsx(Form$1.Control, {
				type: "password",
				placeholder: "Confirm Password",
				value: confirmPassword,
				onChange: (e) => setConfirmPassword(e.currentTarget.value)
			}),
			/* @__PURE__ */ jsx(Button, {
				type: "submit",
				variant: "primary",
				disabled: submitButtonDisabled,
				children: submitButtonText
			})
		]
	}) });
});
async function action$3({ request }) {
	try {
		if (await isAuthenticated(request)) return redirect("/");
		if (!await backendClient.isOnboarding()) return redirect("/login");
		const formData = await request.formData();
		const username = formData.get("username")?.toString();
		const password = formData.get("password")?.toString();
		if (!username || !password) throw new Error("username and password required");
		if (!await backendClient.createAccount(username, password)) throw new Error("Unknown error creating account");
		return redirect("/", await setSessionUser(request, username));
	} catch (error) {
		if (error instanceof Error) return { error: error.message };
		throw error;
	}
}
var route_module_default$4 = {
	container: "_container_yygrw_1",
	tabs: "_tabs_yygrw_9",
	button: "_button_yygrw_25"
};
var usenet_module_default = {
	container: "_container_1a60k_1",
	section: "_section_1a60k_6",
	sectionHeader: "_sectionHeader_1a60k_10",
	alertMessage: "_alertMessage_1a60k_21",
	"providers-grid": "_providers-grid_1a60k_30",
	"provider-card": "_provider-card_1a60k_37",
	"provider-card-inner": "_provider-card-inner_1a60k_45",
	"provider-header": "_provider-header_1a60k_49",
	"provider-header-actions": "_provider-header-actions_1a60k_57",
	"header-action-button": "_header-action-button_1a60k_62",
	"delete": "_delete_1a60k_83",
	"provider-icon": "_provider-icon_1a60k_88",
	"provider-header-content": "_provider-header-content_1a60k_93",
	"provider-host": "_provider-host_1a60k_98",
	"provider-port": "_provider-port_1a60k_107",
	"provider-divider": "_provider-divider_1a60k_115",
	"provider-details": "_provider-details_1a60k_121",
	"provider-detail-row": "_provider-detail-row_1a60k_127",
	"provider-detail-item": "_provider-detail-item_1a60k_133",
	"provider-detail-icon": "_provider-detail-icon_1a60k_144",
	"provider-detail-content": "_provider-detail-content_1a60k_161",
	"provider-detail-label": "_provider-detail-label_1a60k_169",
	"provider-detail-value": "_provider-detail-value_1a60k_178",
	"connection-bar": "_connection-bar_1a60k_186",
	"connection-bar-live": "_connection-bar-live_1a60k_197",
	"connection-bar-active": "_connection-bar-active_1a60k_205",
	"modal-overlay": "_modal-overlay_1a60k_213",
	fadeIn: "_fadeIn_1a60k_1",
	"modal-container": "_modal-container_1a60k_237",
	slideUp: "_slideUp_1a60k_1",
	"modal-header": "_modal-header_1a60k_261",
	"modal-title": "_modal-title_1a60k_269",
	"modal-close": "_modal-close_1a60k_276",
	"modal-body": "_modal-body_1a60k_297",
	"modal-footer": "_modal-footer_1a60k_303",
	"modal-footer-left": "_modal-footer-left_1a60k_312",
	"modal-footer-right": "_modal-footer-right_1a60k_317",
	"form-grid": "_form-grid_1a60k_322",
	"form-group": "_form-group_1a60k_329",
	"full-width": "_full-width_1a60k_333",
	"form-label": "_form-label_1a60k_337",
	"form-input": "_form-input_1a60k_345",
	error: "_error_1a60k_370",
	"form-select": "_form-select_1a60k_379",
	"form-checkbox-wrapper": "_form-checkbox-wrapper_1a60k_406",
	"form-checkbox": "_form-checkbox_1a60k_406",
	"form-checkbox-label": "_form-checkbox-label_1a60k_420",
	alert: "_alert_1a60k_21",
	"alert-success": "_alert-success_1a60k_434",
	"alert-danger": "_alert-danger_1a60k_440",
	input: "_input_1a60k_446"
};
//#endregion
//#region app/routes/settings/usenet/usenet.tsx
var usenetConnectionsTopic = { "cxs": "state" };
var ProviderType = /* @__PURE__ */ function(ProviderType) {
	ProviderType[ProviderType["Disabled"] = 0] = "Disabled";
	ProviderType[ProviderType["Pooled"] = 1] = "Pooled";
	ProviderType[ProviderType["BackupAndStats"] = 2] = "BackupAndStats";
	ProviderType[ProviderType["BackupOnly"] = 3] = "BackupOnly";
	return ProviderType;
}(ProviderType || {});
var PROVIDER_TYPE_LABELS = {
	[ProviderType.Disabled]: "Disabled",
	[ProviderType.Pooled]: "Pool Connections",
	[ProviderType.BackupAndStats]: "Backup & Health Checks",
	[ProviderType.BackupOnly]: "Backup Only"
};
function parseProviderConfig(jsonString) {
	try {
		if (!jsonString || jsonString.trim() === "") return { Providers: [] };
		return JSON.parse(jsonString);
	} catch {
		return { Providers: [] };
	}
}
function serializeProviderConfig(config) {
	return JSON.stringify(config);
}
function UsenetSettings({ config, setNewConfig }) {
	const [showModal, setShowModal] = useState(false);
	const [editingIndex, setEditingIndex] = useState(null);
	const [connections, setConnections] = useState({});
	const providerConfig = useMemo(() => parseProviderConfig(config["usenet.providers"]), [config]);
	const handleAddProvider = useCallback(() => {
		setEditingIndex(null);
		setShowModal(true);
	}, []);
	const handleEditProvider = useCallback((index) => {
		setEditingIndex(index);
		setShowModal(true);
	}, []);
	const handleDeleteProvider = useCallback((index) => {
		const newProviderConfig = { ...providerConfig };
		newProviderConfig.Providers = providerConfig.Providers.filter((_, i) => i !== index);
		setNewConfig({
			...config,
			"usenet.providers": serializeProviderConfig(newProviderConfig)
		});
	}, [
		config,
		providerConfig,
		setNewConfig
	]);
	const handleCloseModal = useCallback(() => {
		setShowModal(false);
		setEditingIndex(null);
	}, []);
	const handleSaveProvider = useCallback((provider) => {
		const newProviderConfig = { ...providerConfig };
		if (editingIndex !== null) newProviderConfig.Providers[editingIndex] = provider;
		else newProviderConfig.Providers.push(provider);
		setNewConfig({
			...config,
			"usenet.providers": serializeProviderConfig(newProviderConfig)
		});
		handleCloseModal();
	}, [
		config,
		providerConfig,
		editingIndex,
		setNewConfig,
		handleCloseModal
	]);
	const handleConnectionsMessage = useCallback((message) => {
		const [index, live, idle, _0, _1, _2] = (message || "0|0|0|0|1|0").split("|").map((x) => Number(x));
		if (showModal) return;
		if (index >= providerConfig.Providers.length) return;
		setConnections((prev) => ({
			...prev,
			[index]: {
				active: live - idle,
				live,
				max: providerConfig.Providers[index]?.MaxConnections || 1
			}
		}));
	}, [setConnections]);
	useEffect(() => {
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage((_, message) => handleConnectionsMessage(message));
			ws.onopen = () => ws.send(JSON.stringify(usenetConnectionsTopic));
			ws.onerror = () => {
				ws.close();
			};
			ws.onclose = onClose;
			return () => {
				disposed = true;
				ws.close();
			};
		}
		function onClose(e) {
			!disposed && setTimeout(() => connect(), 1e3);
			setConnections({});
		}
		return connect();
	}, [setConnections, handleConnectionsMessage]);
	return /* @__PURE__ */ jsxs("div", {
		className: usenet_module_default.container,
		children: [/* @__PURE__ */ jsxs("div", {
			className: usenet_module_default.section,
			children: [/* @__PURE__ */ jsxs("div", {
				className: usenet_module_default.sectionHeader,
				children: [/* @__PURE__ */ jsx("div", { children: "Usenet Providers" }), /* @__PURE__ */ jsx(Button, {
					variant: "primary",
					size: "sm",
					onClick: handleAddProvider,
					children: "Add"
				})]
			}), providerConfig.Providers.length === 0 ? /* @__PURE__ */ jsx("p", {
				className: usenet_module_default.alertMessage,
				children: "No Usenet providers configured. Click on the \"Add\" button to get started."
			}) : /* @__PURE__ */ jsx("div", {
				className: usenet_module_default["providers-grid"],
				children: providerConfig.Providers.map((provider, index) => /* @__PURE__ */ jsx("div", {
					className: usenet_module_default["provider-card"],
					children: /* @__PURE__ */ jsxs("div", {
						className: usenet_module_default["provider-card-inner"],
						children: [/* @__PURE__ */ jsxs("div", {
							className: usenet_module_default["provider-header"],
							children: [/* @__PURE__ */ jsxs("div", {
								className: usenet_module_default["provider-header-content"],
								children: [/* @__PURE__ */ jsx("div", {
									className: usenet_module_default["provider-host"],
									children: provider.Host
								}), /* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["provider-port"],
									children: ["Port ", provider.Port]
								})]
							}), /* @__PURE__ */ jsxs("div", {
								className: usenet_module_default["provider-header-actions"],
								children: [/* @__PURE__ */ jsx("button", {
									className: usenet_module_default["header-action-button"],
									onClick: () => handleEditProvider(index),
									title: "Edit Provider",
									children: /* @__PURE__ */ jsxs("svg", {
										width: "14",
										height: "14",
										viewBox: "0 0 24 24",
										fill: "none",
										stroke: "currentColor",
										strokeWidth: "2",
										children: [/* @__PURE__ */ jsx("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }), /* @__PURE__ */ jsx("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })]
									})
								}), /* @__PURE__ */ jsx("button", {
									className: `${usenet_module_default["header-action-button"]} ${usenet_module_default["delete"]}`,
									onClick: () => handleDeleteProvider(index),
									title: "Delete Provider",
									children: /* @__PURE__ */ jsxs("svg", {
										width: "14",
										height: "14",
										viewBox: "0 0 24 24",
										fill: "none",
										stroke: "currentColor",
										strokeWidth: "2",
										children: [/* @__PURE__ */ jsx("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })]
									})
								})]
							})]
						}), /* @__PURE__ */ jsx("div", {
							className: usenet_module_default["provider-details"],
							children: /* @__PURE__ */ jsxs("div", {
								className: usenet_module_default["provider-detail-row"],
								children: [
									/* @__PURE__ */ jsxs("div", {
										className: usenet_module_default["provider-detail-item"],
										children: [/* @__PURE__ */ jsx("div", {
											className: usenet_module_default["provider-detail-icon"],
											children: /* @__PURE__ */ jsxs("svg", {
												width: "13",
												height: "13",
												viewBox: "0 0 24 24",
												fill: "none",
												stroke: "currentColor",
												strokeWidth: "2",
												children: [/* @__PURE__ */ jsx("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }), /* @__PURE__ */ jsx("circle", {
													cx: "12",
													cy: "7",
													r: "4"
												})]
											})
										}), /* @__PURE__ */ jsxs("div", {
											className: usenet_module_default["provider-detail-content"],
											children: [/* @__PURE__ */ jsx("span", {
												className: usenet_module_default["provider-detail-label"],
												children: "Username"
											}), /* @__PURE__ */ jsx("span", {
												className: usenet_module_default["provider-detail-value"],
												children: provider.User
											})]
										})]
									}),
									/* @__PURE__ */ jsxs("div", {
										className: usenet_module_default["provider-detail-item"],
										children: [
											connections[index] && /* @__PURE__ */ jsxs("div", {
												className: usenet_module_default["connection-bar"],
												children: [/* @__PURE__ */ jsx("div", {
													className: usenet_module_default["connection-bar-live"],
													style: { width: `${100 * (connections[index].live / connections[index].max)}%` }
												}), /* @__PURE__ */ jsx("div", {
													className: usenet_module_default["connection-bar-active"],
													style: { width: `${100 * (connections[index].active / connections[index].max)}%` }
												})]
											}),
											/* @__PURE__ */ jsx("div", {
												className: usenet_module_default["provider-detail-icon"],
												children: /* @__PURE__ */ jsx("svg", {
													width: "13",
													height: "13",
													viewBox: "0 0 24 24",
													fill: "none",
													stroke: "currentColor",
													strokeWidth: "2",
													children: /* @__PURE__ */ jsx("path", { d: "M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" })
												})
											}),
											/* @__PURE__ */ jsxs("div", {
												className: usenet_module_default["provider-detail-content"],
												children: [/* @__PURE__ */ jsx("span", {
													className: usenet_module_default["provider-detail-label"],
													children: "Max Connections"
												}), /* @__PURE__ */ jsx("span", {
													className: usenet_module_default["provider-detail-value"],
													children: provider.MaxConnections
												})]
											})
										]
									}),
									/* @__PURE__ */ jsxs("div", {
										className: usenet_module_default["provider-detail-item"],
										children: [/* @__PURE__ */ jsx("div", {
											className: usenet_module_default["provider-detail-icon"],
											children: provider.UseSsl ? /* @__PURE__ */ jsxs("svg", {
												width: "13",
												height: "13",
												viewBox: "0 0 24 24",
												fill: "none",
												stroke: "currentColor",
												strokeWidth: "2",
												children: [
													/* @__PURE__ */ jsx("rect", {
														x: "5",
														y: "11",
														width: "14",
														height: "11",
														rx: "2",
														ry: "2"
													}),
													/* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" }),
													/* @__PURE__ */ jsx("circle", {
														cx: "12",
														cy: "16",
														r: "1",
														fill: "currentColor"
													})
												]
											}) : /* @__PURE__ */ jsxs("svg", {
												width: "13",
												height: "13",
												viewBox: "0 -2 24 26",
												fill: "none",
												stroke: "currentColor",
												strokeWidth: "2",
												children: [
													/* @__PURE__ */ jsx("rect", {
														x: "5",
														y: "11",
														width: "14",
														height: "11",
														rx: "2",
														ry: "2"
													}),
													/* @__PURE__ */ jsx("path", { d: "M7 11V4a5 5 0 0 1 9.9 1" }),
													/* @__PURE__ */ jsx("circle", {
														cx: "12",
														cy: "16",
														r: "1",
														fill: "currentColor"
													})
												]
											})
										}), /* @__PURE__ */ jsxs("div", {
											className: usenet_module_default["provider-detail-content"],
											children: [/* @__PURE__ */ jsx("span", {
												className: usenet_module_default["provider-detail-label"],
												children: "Security"
											}), /* @__PURE__ */ jsx("span", {
												className: usenet_module_default["provider-detail-value"],
												children: provider.UseSsl ? "SSL Enabled" : "No SSL"
											})]
										})]
									}),
									/* @__PURE__ */ jsxs("div", {
										className: usenet_module_default["provider-detail-item"],
										children: [/* @__PURE__ */ jsx("div", {
											className: usenet_module_default["provider-detail-icon"],
											children: /* @__PURE__ */ jsxs("svg", {
												width: "16",
												height: "16",
												viewBox: "0 0 24 24",
												fill: "none",
												stroke: "currentColor",
												strokeWidth: "1.3",
												children: [
													/* @__PURE__ */ jsx("text", {
														x: "12",
														y: "9",
														fontSize: "10",
														fill: "currentColor",
														textAnchor: "middle",
														fontWeight: "600",
														children: "1"
													}),
													/* @__PURE__ */ jsx("text", {
														x: "6",
														y: "21",
														fontSize: "10",
														fill: "currentColor",
														textAnchor: "middle",
														fontWeight: "600",
														children: "2"
													}),
													/* @__PURE__ */ jsx("text", {
														x: "18",
														y: "21",
														fontSize: "10",
														fill: "currentColor",
														textAnchor: "middle",
														fontWeight: "600",
														children: "3"
													})
												]
											})
										}), /* @__PURE__ */ jsxs("div", {
											className: usenet_module_default["provider-detail-content"],
											children: [/* @__PURE__ */ jsx("span", {
												className: usenet_module_default["provider-detail-label"],
												children: "Behavior"
											}), /* @__PURE__ */ jsx("span", {
												className: usenet_module_default["provider-detail-value"],
												children: PROVIDER_TYPE_LABELS[provider.Type]
											})]
										})]
									})
								]
							})
						})]
					})
				}, index))
			})]
		}), /* @__PURE__ */ jsx(ProviderModal, {
			show: showModal,
			provider: editingIndex !== null ? providerConfig.Providers[editingIndex] : null,
			onClose: handleCloseModal,
			onSave: handleSaveProvider
		})]
	});
}
function ProviderModal({ show, provider, onClose, onSave }) {
	const [host, setHost] = useState(provider?.Host || "");
	const [port, setPort] = useState(provider?.Port?.toString() || "");
	const [useSsl, setUseSsl] = useState(provider?.UseSsl ?? true);
	const [user, setUser] = useState(provider?.User || "");
	const [pass, setPass] = useState(provider?.Pass || "");
	const [maxConnections, setMaxConnections] = useState(provider?.MaxConnections?.toString() || "");
	const [type, setType] = useState(provider?.Type ?? ProviderType.Pooled);
	const [isTestingConnection, setIsTestingConnection] = useState(false);
	const [connectionTested, setConnectionTested] = useState(false);
	const [testError, setTestError] = useState(null);
	useEffect(() => {
		if (show) {
			setHost(provider?.Host || "");
			setPort(provider?.Port?.toString() || "");
			setUseSsl(provider?.UseSsl ?? true);
			setUser(provider?.User || "");
			setPass(provider?.Pass || "");
			setMaxConnections(provider?.MaxConnections?.toString() || "");
			setType(provider?.Type ?? ProviderType.Pooled);
			setConnectionTested(false);
			setTestError(null);
		}
	}, [show, provider]);
	useEffect(() => {
		const handleEscape = (e) => {
			if (e.key === "Escape" && show) onClose();
		};
		if (show) {
			document.addEventListener("keydown", handleEscape);
			return () => document.removeEventListener("keydown", handleEscape);
		}
	}, [show, onClose]);
	const handleTestConnection = useCallback(async () => {
		setIsTestingConnection(true);
		setTestError(null);
		try {
			const formData = new FormData();
			formData.append("host", host);
			formData.append("port", port);
			formData.append("use-ssl", useSsl.toString());
			formData.append("user", user);
			formData.append("pass", pass);
			const response = await fetch("/api/test-usenet-connection", {
				method: "POST",
				body: formData
			});
			if (response.ok) if ((await response.json()).connected) {
				setConnectionTested(true);
				setTestError(null);
			} else setTestError("Connection test failed");
			else setTestError("Failed to test connection");
		} catch (error) {
			setTestError("Network error: " + (error instanceof Error ? error.message : "Unknown error"));
		} finally {
			setIsTestingConnection(false);
		}
	}, [
		host,
		port,
		useSsl,
		user,
		pass
	]);
	const handleSave = useCallback(() => {
		onSave({
			Type: type,
			Host: host,
			Port: parseInt(port, 10),
			UseSsl: useSsl,
			User: user,
			Pass: pass,
			MaxConnections: parseInt(maxConnections, 10)
		});
	}, [
		type,
		host,
		port,
		useSsl,
		user,
		pass,
		maxConnections,
		onSave
	]);
	const handleOverlayClick = useCallback((e) => {
		if (e.target === e.currentTarget) onClose();
	}, [onClose]);
	const isFormValid = host.trim() !== "" && isPositiveInteger(port) && user.trim() !== "" && pass.trim() !== "" && isPositiveInteger(maxConnections);
	const canSave = isFormValid && (connectionTested || type == ProviderType.Disabled);
	if (!show) return null;
	return /* @__PURE__ */ jsx("div", {
		className: usenet_module_default["modal-overlay"],
		onClick: handleOverlayClick,
		children: /* @__PURE__ */ jsxs("div", {
			className: usenet_module_default["modal-container"],
			children: [
				/* @__PURE__ */ jsxs("div", {
					className: usenet_module_default["modal-header"],
					children: [/* @__PURE__ */ jsx("h2", {
						className: usenet_module_default["modal-title"],
						children: provider ? "Edit Provider" : "Add Provider"
					}), /* @__PURE__ */ jsx("button", {
						className: usenet_module_default["modal-close"],
						onClick: onClose,
						"aria-label": "Close",
						children: "×"
					})]
				}),
				/* @__PURE__ */ jsxs("div", {
					className: usenet_module_default["modal-body"],
					children: [
						/* @__PURE__ */ jsxs("div", {
							className: usenet_module_default["form-grid"],
							children: [
								/* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["form-group"],
									children: [/* @__PURE__ */ jsx("label", {
										htmlFor: "provider-host",
										className: usenet_module_default["form-label"],
										children: "Host"
									}), /* @__PURE__ */ jsx("input", {
										type: "text",
										id: "provider-host",
										className: usenet_module_default["form-input"],
										placeholder: "news.provider.com",
										value: host,
										onChange: (e) => {
											setHost(e.target.value);
											setConnectionTested(false);
										}
									})]
								}),
								/* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["form-group"],
									children: [/* @__PURE__ */ jsx("label", {
										htmlFor: "provider-port",
										className: usenet_module_default["form-label"],
										children: "Port"
									}), /* @__PURE__ */ jsx("input", {
										type: "text",
										id: "provider-port",
										className: `${usenet_module_default["form-input"]} ${!isPositiveInteger(port) && port !== "" ? usenet_module_default.error : ""}`,
										placeholder: "563",
										value: port,
										onChange: (e) => {
											setPort(e.target.value);
											setConnectionTested(false);
										}
									})]
								}),
								/* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["form-group"],
									children: [/* @__PURE__ */ jsx("label", {
										htmlFor: "provider-user",
										className: usenet_module_default["form-label"],
										children: "Username"
									}), /* @__PURE__ */ jsx("input", {
										type: "text",
										id: "provider-user",
										className: usenet_module_default["form-input"],
										placeholder: "username",
										value: user,
										onChange: (e) => {
											setUser(e.target.value);
											setConnectionTested(false);
										}
									})]
								}),
								/* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["form-group"],
									children: [/* @__PURE__ */ jsx("label", {
										htmlFor: "provider-pass",
										className: usenet_module_default["form-label"],
										children: "Password"
									}), /* @__PURE__ */ jsx("input", {
										type: "password",
										id: "provider-pass",
										className: usenet_module_default["form-input"],
										placeholder: "password",
										value: pass,
										onChange: (e) => {
											setPass(e.target.value);
											setConnectionTested(false);
										}
									})]
								}),
								/* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["form-group"],
									children: [/* @__PURE__ */ jsx("label", {
										htmlFor: "provider-max-connections",
										className: usenet_module_default["form-label"],
										children: "Max Connections"
									}), /* @__PURE__ */ jsx("input", {
										type: "text",
										id: "provider-max-connections",
										className: `${usenet_module_default["form-input"]} ${!isPositiveInteger(maxConnections) && maxConnections !== "" ? usenet_module_default.error : ""}`,
										placeholder: "20",
										value: maxConnections,
										onChange: (e) => setMaxConnections(e.target.value)
									})]
								}),
								/* @__PURE__ */ jsxs("div", {
									className: usenet_module_default["form-group"],
									children: [/* @__PURE__ */ jsx("label", {
										htmlFor: "provider-type",
										className: usenet_module_default["form-label"],
										children: "Type"
									}), /* @__PURE__ */ jsxs("select", {
										id: "provider-type",
										className: usenet_module_default["form-select"],
										value: type,
										onChange: (e) => setType(parseInt(e.target.value, 10)),
										children: [
											/* @__PURE__ */ jsx("option", {
												value: ProviderType.Disabled,
												children: "Disabled"
											}),
											/* @__PURE__ */ jsx("option", {
												value: ProviderType.Pooled,
												children: "Pool Connections"
											}),
											/* @__PURE__ */ jsx("option", {
												value: ProviderType.BackupOnly,
												children: "Backup Only"
											})
										]
									})]
								}),
								/* @__PURE__ */ jsx("div", {
									className: `${usenet_module_default["form-group"]} ${usenet_module_default["full-width"]}`,
									children: /* @__PURE__ */ jsxs("div", {
										className: usenet_module_default["form-checkbox-wrapper"],
										children: [/* @__PURE__ */ jsx("input", {
											type: "checkbox",
											id: "provider-ssl",
											className: usenet_module_default["form-checkbox"],
											checked: useSsl,
											onChange: (e) => {
												setUseSsl(e.target.checked);
												setConnectionTested(false);
											}
										}), /* @__PURE__ */ jsx("label", {
											htmlFor: "provider-ssl",
											className: usenet_module_default["form-checkbox-label"],
											children: "Use SSL"
										})]
									})
								})
							]
						}),
						testError && /* @__PURE__ */ jsx("div", {
							className: `${usenet_module_default.alert} ${usenet_module_default["alert-danger"]}`,
							style: { marginTop: "16px" },
							children: testError
						}),
						connectionTested && /* @__PURE__ */ jsx("div", {
							className: `${usenet_module_default.alert} ${usenet_module_default["alert-success"]}`,
							style: { marginTop: "16px" },
							children: "Connection test successful!"
						})
					]
				}),
				/* @__PURE__ */ jsxs("div", {
					className: usenet_module_default["modal-footer"],
					children: [/* @__PURE__ */ jsx("div", { className: usenet_module_default["modal-footer-left"] }), /* @__PURE__ */ jsxs("div", {
						className: usenet_module_default["modal-footer-right"],
						children: [/* @__PURE__ */ jsx(Button, {
							variant: "secondary",
							onClick: onClose,
							children: "Cancel"
						}), !canSave ? /* @__PURE__ */ jsx(Button, {
							variant: "primary",
							onClick: handleTestConnection,
							disabled: !isFormValid || isTestingConnection,
							children: isTestingConnection ? "Testing..." : "Test Connection"
						}) : /* @__PURE__ */ jsx(Button, {
							variant: "primary",
							onClick: handleSave,
							disabled: !canSave,
							children: "Save Provider"
						})]
					})]
				})
			]
		})
	});
}
function isUsenetSettingsUpdated(config, newConfig) {
	return config["usenet.providers"] !== newConfig["usenet.providers"];
}
function isPositiveInteger(value) {
	const num = Number(value);
	return Number.isInteger(num) && num > 0 && value.trim() === num.toString();
}
var tag_input_module_default = {
	container: "_container_18bfh_1",
	tag: "_tag_18bfh_20",
	removeButton: "_removeButton_18bfh_33",
	input: "_input_18bfh_54",
	measure: "_measure_18bfh_70"
};
//#endregion
//#region app/components/tag-input/tag-input.tsx
var delimiter = ",";
function TagInput({ className, value, onChange, placeholder, id, "aria-describedby": ariaDescribedBy }) {
	const [inputValue, setInputValue] = useState("");
	const [inputWidth, setInputWidth] = useState(void 0);
	const inputRef = useRef(null);
	const measureRef = useRef(null);
	const tags = value.split(delimiter).map((t) => t.trim()).filter((t) => t.length > 0);
	const displayText = inputValue || (tags.length === 0 ? placeholder : "") || "";
	useEffect(() => {
		if (measureRef.current) setInputWidth(measureRef.current.offsetWidth + 4);
	}, [displayText]);
	const updateTags = useCallback((newTags) => {
		onChange(newTags.join(`${delimiter} `));
	}, [onChange, delimiter]);
	const addTag = useCallback((tag) => {
		const trimmed = tag.trim();
		if (trimmed && !tags.includes(trimmed)) updateTags([...tags, trimmed]);
		setInputValue("");
	}, [tags, updateTags]);
	const removeTag = useCallback((index) => {
		updateTags(tags.filter((_, i) => i !== index));
	}, [tags, updateTags]);
	const handleInputChange = useCallback((e) => {
		const newValue = e.target.value;
		const commaIndex = newValue.indexOf(",");
		const spaceIndex = newValue.indexOf(" ");
		const delimiterIndex = commaIndex === -1 ? spaceIndex : spaceIndex === -1 ? commaIndex : Math.min(commaIndex, spaceIndex);
		if (delimiterIndex !== -1) {
			const beforeDelimiter = newValue.slice(0, delimiterIndex).trim();
			const afterDelimiter = newValue.slice(delimiterIndex + 1);
			if (beforeDelimiter) updateTags(tags.includes(beforeDelimiter) ? tags : [...tags, beforeDelimiter]);
			setInputValue(afterDelimiter);
		} else setInputValue(newValue);
	}, [tags, updateTags]);
	const handleKeyDown = useCallback((e) => {
		if (e.key === "Enter") {
			e.preventDefault();
			if (inputValue.trim()) addTag(inputValue);
		} else if (e.key === "Backspace" && inputValue === "" && tags.length > 0) removeTag(tags.length - 1);
	}, [
		inputValue,
		tags,
		addTag,
		removeTag
	]);
	const handleContainerClick = useCallback(() => {
		inputRef.current?.focus();
	}, []);
	return /* @__PURE__ */ jsxs("div", {
		className: classNames([tag_input_module_default.container, className]),
		onClick: handleContainerClick,
		children: [
			tags.map((tag, index) => /* @__PURE__ */ jsxs("span", {
				className: tag_input_module_default.tag,
				children: [tag, /* @__PURE__ */ jsx("button", {
					type: "button",
					className: tag_input_module_default.removeButton,
					onClick: (e) => {
						e.stopPropagation();
						removeTag(index);
					},
					"aria-label": `Remove ${tag}`,
					children: "×"
				})]
			}, index)),
			/* @__PURE__ */ jsx("span", {
				ref: measureRef,
				className: tag_input_module_default.measure,
				children: displayText
			}),
			/* @__PURE__ */ jsx("input", {
				ref: inputRef,
				type: "text",
				className: tag_input_module_default.input,
				style: { width: inputWidth },
				value: inputValue,
				onChange: handleInputChange,
				onKeyDown: handleKeyDown,
				onBlur: () => {
					if (inputValue.trim()) addTag(inputValue);
				},
				placeholder: tags.length === 0 ? placeholder : void 0,
				id,
				"aria-describedby": ariaDescribedBy
			})
		]
	});
}
var multi_checkbox_input_module_default = { container: "_container_1maro_1" };
//#endregion
//#region app/components/multi-checkbox-input/multi-checkbox-input.tsx
function MultiCheckboxInput({ options, value, onChange }) {
	const selectedOptions = useMemo(() => {
		if (!value || value.trim() === "") return [];
		return value.split(",").map((c) => c.trim()).filter((c) => c.length > 0);
	}, [value]);
	const onOptionCheckboxChange = useCallback((option, checked) => {
		let newSelected;
		if (checked) newSelected = [...selectedOptions, option];
		else newSelected = selectedOptions.filter((o) => o !== option);
		onChange(newSelected.join(", "));
	}, [onChange, selectedOptions]);
	if (options.length === 0) return null;
	return /* @__PURE__ */ jsx("div", {
		className: multi_checkbox_input_module_default.container,
		children: options.map((option) => /* @__PURE__ */ jsx(Form$1.Check, {
			type: "checkbox",
			id: `multi-checkbox-${option}`,
			label: option,
			checked: selectedOptions.includes(option),
			onChange: (e) => onOptionCheckboxChange(option, e.target.checked)
		}, option))
	});
}
var expanding_text_input_module_default = { textarea: "_textarea_l4psx_1" };
//#endregion
//#region app/components/expanding-text-input/expanding-text-input.tsx
function ExpandingTextInput({ className, value, onChange, placeholder, id, "aria-describedby": ariaDescribedBy, readOnly }) {
	const textareaRef = useRef(null);
	const adjustHeight = useCallback(() => {
		const textarea = textareaRef.current;
		if (textarea) {
			textarea.style.height = "0";
			textarea.style.height = `${textarea.scrollHeight}px`;
		}
	}, []);
	useEffect(() => {
		const textarea = textareaRef.current;
		if (!textarea) return;
		adjustHeight();
		const intersectionObserver = new IntersectionObserver((entries) => {
			if (entries[0].isIntersecting) adjustHeight();
		});
		intersectionObserver.observe(textarea);
		const resizeObserver = new ResizeObserver(() => {
			adjustHeight();
		});
		resizeObserver.observe(textarea);
		return () => {
			intersectionObserver.disconnect();
			resizeObserver.disconnect();
		};
	}, [value, adjustHeight]);
	const handleChange = useCallback((e) => {
		onChange(e.target.value);
	}, [onChange]);
	return /* @__PURE__ */ jsx(Form$1.Control, {
		as: "textarea",
		ref: textareaRef,
		className: classNames([expanding_text_input_module_default.textarea, className]),
		value,
		onChange: handleChange,
		onInput: adjustHeight,
		placeholder,
		id,
		"aria-describedby": ariaDescribedBy,
		readOnly,
		spellCheck: false
	});
}
var sabnzbd_module_default = {
	container: "_container_cx4n8_1",
	input: "_input_cx4n8_5",
	error: "_error_cx4n8_13",
	subGroup: "_subGroup_cx4n8_17"
};
//#endregion
//#region app/routes/settings/sabnzbd/sabnzbd.tsx
function SabnzbdSettings({ config, setNewConfig, appVersion }) {
	const onRefreshApiKey = useCallback(() => {
		setNewConfig({
			...config,
			"api.key": generateNewApiKey()
		});
	}, [setNewConfig, config]);
	const ensureArticleExistanceSetting = useEnsureArticleExistanceSetting(config, setNewConfig);
	return /* @__PURE__ */ jsxs("div", {
		className: sabnzbd_module_default.container,
		children: [
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "api-key-input",
					children: "API Key"
				}),
				/* @__PURE__ */ jsxs(InputGroup, {
					className: sabnzbd_module_default.input,
					children: [/* @__PURE__ */ jsx(Form$1.Control, {
						type: "text",
						id: "api-key-input",
						"aria-describedby": "api-key-help",
						value: config["api.key"],
						readOnly: true
					}), /* @__PURE__ */ jsx(Button, {
						variant: "primary",
						onClick: onRefreshApiKey,
						children: "Refresh"
					})]
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "api-key-help",
					muted: true,
					children: "Use this API key when configuring your download client in Radarr or Sonarr."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "categories-input",
					children: "Categories"
				}),
				/* @__PURE__ */ jsx(TagInput, {
					className: !isValidCategories(config["api.categories"]) ? `${sabnzbd_module_default.error} ${sabnzbd_module_default.input}` : sabnzbd_module_default.input,
					id: "categories-input",
					"aria-describedby": "categories-help",
					value: config["api.categories"],
					placeholder: "tv, movies, audio, software",
					onChange: (value) => setNewConfig({
						...config,
						"api.categories": value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "categories-help",
					muted: true,
					children: "The complete list of categories for organizing imported nzbs. Only letters, numbers, and dashes are allowed."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "manual-category-input",
					children: "Manual Upload Category"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					className: sabnzbd_module_default.input,
					type: "text",
					id: "manual-category-input",
					"aria-describedby": "manual-category-help",
					value: config["api.manual-category"],
					placeholder: "uncategorized",
					onChange: (e) => setNewConfig({
						...config,
						"api.manual-category": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "manual-category-help",
					muted: true,
					children: "The category to use for manual uploads through the Queue page on the UI."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "import-strategy-input",
					children: "Import Strategy"
				}),
				/* @__PURE__ */ jsxs(Form$1.Select, {
					className: sabnzbd_module_default.input,
					value: config["api.import-strategy"],
					onChange: (e) => setNewConfig({
						...config,
						"api.import-strategy": e.target.value
					}),
					children: [/* @__PURE__ */ jsx("option", {
						value: "symlinks",
						children: "Symlinks — Plex"
					}), /* @__PURE__ */ jsx("option", {
						value: "strm",
						children: "STRM Files — Emby/Jellyfin"
					})]
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "import-strategy-help",
					muted: true,
					children: "If you need to be able to stream from Plex, you will need to configure rclone and should select the `Symlinks` option here. If you only need to stream through Emby or Jellyfin, then you can skip rclone altogether and select the `STRM Files` option."
				})
			] }),
			config["api.import-strategy"] === "symlinks" && /* @__PURE__ */ jsxs(Form$1.Group, {
				className: sabnzbd_module_default.subGroup,
				children: [
					/* @__PURE__ */ jsx(Form$1.Label, {
						htmlFor: "mount-dir-input",
						children: "Rclone Mount Directory"
					}),
					/* @__PURE__ */ jsx(Form$1.Control, {
						className: sabnzbd_module_default.input,
						type: "text",
						id: "mount-dir-input",
						"aria-describedby": "mount-dir-help",
						placeholder: "/mnt/nzbdav",
						value: config["rclone.mount-dir"],
						onChange: (e) => setNewConfig({
							...config,
							"rclone.mount-dir": e.target.value
						})
					}),
					/* @__PURE__ */ jsx(Form$1.Text, {
						id: "mount-dir-help",
						muted: true,
						children: "The location at which you've mounted (or will mount) the webdav root, through Rclone. This is used to tell Radarr / Sonarr where to look for completed \"downloads.\""
					})
				]
			}),
			config["api.import-strategy"] === "strm" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Form$1.Group, {
				className: sabnzbd_module_default.subGroup,
				children: [
					/* @__PURE__ */ jsx(Form$1.Label, {
						htmlFor: "completed-downloads-dir-input",
						children: "Completed Downloads Dir"
					}),
					/* @__PURE__ */ jsx(Form$1.Control, {
						className: sabnzbd_module_default.input,
						type: "text",
						id: "completed-downloads-dir-input",
						"aria-describedby": "completed-downloads-dir-help",
						placeholder: "/data/completed-downloads",
						value: config["api.completed-downloads-dir"],
						onChange: (e) => setNewConfig({
							...config,
							"api.completed-downloads-dir": e.target.value
						})
					}),
					/* @__PURE__ */ jsx(Form$1.Text, {
						id: "completed-downloads-dir-help",
						muted: true,
						children: "This is used to tell Radarr / Sonarr where to look for completed \"downloads.\" Make sure this path is also visible to your Radarr / Sonarr containers. The \"downloads\" placed in this folder will all be *.strm files that point to nzbdav for streaming."
					})
				]
			}), /* @__PURE__ */ jsxs(Form$1.Group, {
				className: sabnzbd_module_default.subGroup,
				children: [
					/* @__PURE__ */ jsx(Form$1.Label, {
						htmlFor: "base-url-input",
						children: "Base URL"
					}),
					/* @__PURE__ */ jsx(Form$1.Control, {
						className: sabnzbd_module_default.input,
						type: "text",
						id: "base-url-input",
						"aria-describedby": "base-url-help",
						placeholder: "http://localhost:3000",
						value: config["general.base-url"],
						onChange: (e) => setNewConfig({
							...config,
							"general.base-url": e.target.value
						})
					}),
					/* @__PURE__ */ jsx(Form$1.Text, {
						id: "base-url-help",
						muted: true,
						children: "What is the base URL at which you access nzbdav? Make sure that Emby/Jellyfin can access this url. This is the URL they will connect to for streaming. All *.strm files will point to this URL."
					})
				]
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "ignored-files-input",
					children: "Ignored Files"
				}),
				/* @__PURE__ */ jsx(TagInput, {
					className: sabnzbd_module_default.input,
					id: "ignored-files-input",
					"aria-describedby": "ignored-files-help",
					placeholder: "*.nfo, *.par2, *.sfv, *sample.mkv",
					value: config["api.download-file-blocklist"],
					onChange: (value) => setNewConfig({
						...config,
						"api.download-file-blocklist": value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "ignored-files-help",
					muted: true,
					children: "Files that match these patterns will be ignored and not mounted onto the webdav when processing an nzb. Wildcards (*) are supported."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "duplicate-nzb-behavior-input",
					children: "Behavior for Duplicate NZBs"
				}),
				/* @__PURE__ */ jsxs(Form$1.Select, {
					className: sabnzbd_module_default.input,
					"aria-describedby": "duplicate-nzb-behavior-help",
					value: config["api.duplicate-nzb-behavior"],
					onChange: (e) => setNewConfig({
						...config,
						"api.duplicate-nzb-behavior": e.target.value
					}),
					children: [/* @__PURE__ */ jsx("option", {
						value: "increment",
						children: "Download again with suffix (2)"
					}), /* @__PURE__ */ jsx("option", {
						value: "mark-failed",
						children: "Mark the download as failed"
					})]
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "duplicate-nzb-behavior-help",
					muted: true,
					children: "When an NZB is added, a new folder is created on the webdav. What should be done when the download folder for an NZB already exists?"
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "user-agent-input",
					children: "User Agent"
				}),
				/* @__PURE__ */ jsx(ExpandingTextInput, {
					className: sabnzbd_module_default.input,
					id: "user-agent-input",
					"aria-describedby": "user-agent-help",
					value: config["api.user-agent"],
					placeholder: `nzbdav/${appVersion}`,
					onChange: (value) => setNewConfig({
						...config,
						"api.user-agent": value
					})
				}),
				/* @__PURE__ */ jsxs(Form$1.Text, {
					id: "user-agent-help",
					muted: true,
					children: [
						"The user-agent used by the\xA0",
						/* @__PURE__ */ jsx("a", {
							href: "https://sabnzbd.org/wiki/configuration/4.5/api#addurl",
							children: "addurl"
						}),
						" api for fetching nzb files."
					]
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: sabnzbd_module_default.input,
				type: "checkbox",
				id: "ensure-importable-video-checkbox",
				"aria-describedby": "ensure-importable-video-help",
				label: `Fail downloads for nzbs without video content`,
				checked: config["api.ensure-importable-video"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"api.ensure-importable-video": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "ensure-importable-video-help",
				muted: true,
				children: "Whether to mark downloads as `failed` when no single video file is found inside the nzb. This will force Radarr / Sonarr to automatically look for a new nzb."
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Check, {
					className: sabnzbd_module_default.input,
					type: "checkbox",
					id: "ensure-article-existence-checkbox",
					"aria-describedby": "ensure-article-existence-help",
					label: `Perform article health check during downloads`,
					ref: ensureArticleExistanceSetting.masterCheckboxRef,
					checked: !ensureArticleExistanceSetting.areNoneSelected,
					onChange: (e) => ensureArticleExistanceSetting.onMasterCheckboxChange(e.target.checked)
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "ensure-article-existence-help",
					muted: true,
					children: "Whether to check for the existence of all articles within an NZB during queue processing. This process may be slow."
				}),
				/* @__PURE__ */ jsx(MultiCheckboxInput, {
					options: ensureArticleExistanceSetting.categories,
					value: config["api.ensure-article-existence-categories"] ?? "",
					onChange: (value) => setNewConfig({
						...config,
						"api.ensure-article-existence-categories": value
					})
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: sabnzbd_module_default.input,
				type: "checkbox",
				id: "ignore-history-limit-checkbox",
				"aria-describedby": "ignore-history-limit-help",
				label: `Always send full History to Radarr/Sonarr`,
				checked: config["api.ignore-history-limit"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"api.ignore-history-limit": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsxs(Form$1.Text, {
				id: "ignore-history-limit-help",
				muted: true,
				children: [
					"When enabled, this will ignore the History limit sent by radarr/sonarr and always reply with all History items.\xA0",
					/* @__PURE__ */ jsx("a", {
						href: "https://github.com/Sonarr/Sonarr/issues/5452",
						children: "See here"
					}),
					"."
				]
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Check, {
					className: sabnzbd_module_default.input,
					type: "checkbox",
					id: "nzb-backup-enabled-checkbox",
					"aria-describedby": "nzb-backup-location-help",
					label: `Save backup copies of incoming NZBs`,
					checked: config["api.nzb-backup-enabled"] === "true",
					onChange: (e) => setNewConfig({
						...config,
						"api.nzb-backup-enabled": "" + e.target.checked
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					className: sabnzbd_module_default.input,
					style: { marginTop: "15px" },
					type: "text",
					id: "nzb-backup-location-input",
					"aria-describedby": "nzb-backup-location-help",
					placeholder: "/data/nzb-backups",
					value: config["api.nzb-backup-location"],
					disabled: config["api.nzb-backup-enabled"] !== "true",
					isInvalid: !isValidNzbBackupLocation(config),
					onChange: (e) => setNewConfig({
						...config,
						"api.nzb-backup-location": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "nzb-backup-location-help",
					muted: true,
					children: "When enabled, a copy of each incoming NZB will be saved to this directory, organized by category. The directory will be created if it doesn't already exist."
				})
			] })
		]
	});
}
function useEnsureArticleExistanceSetting(config, setNewConfig) {
	const manualCategoryValue = config["api.manual-category"];
	const categoriesValue = config["api.categories"];
	const healthCheckCategoriesValue = config["api.ensure-article-existence-categories"];
	const manualCategory = useMemo(() => {
		return !!manualCategoryValue?.trim() ? manualCategoryValue.trim() : "uncategorized";
	}, [manualCategoryValue]);
	const categories = useMemo(() => {
		return [manualCategory, ...!!categoriesValue?.trim() ? categoriesValue.split(",").map((c) => c.trim()).filter((c) => c.length > 0) : [
			"audio",
			"software",
			"tv",
			"movies"
		]];
	}, [categoriesValue]);
	const healthCheckCategories = useMemo(() => {
		const cats = healthCheckCategoriesValue;
		if (!cats || cats.trim() === "") return [];
		return cats.split(",").map((c) => c.trim()).filter((c) => c.length > 0);
	}, [healthCheckCategoriesValue]);
	const masterCheckboxRef = useRef(null);
	const areAllSelected = categories.length > 0 && categories.every((c) => healthCheckCategories.includes(c));
	const areNoneSelected = healthCheckCategories.length === 0 || categories.every((c) => !healthCheckCategories.includes(c));
	const areSomeSelected = !areAllSelected && !areNoneSelected;
	useEffect(() => {
		if (masterCheckboxRef.current) masterCheckboxRef.current.indeterminate = areSomeSelected;
	}, [areSomeSelected]);
	return {
		categories,
		masterCheckboxRef,
		areAllSelected,
		areNoneSelected,
		areSomeSelected,
		onMasterCheckboxChange: useCallback((checked) => {
			if (checked) setNewConfig((prev) => ({
				...prev,
				"api.ensure-article-existence-categories": categories.join(", ")
			}));
			else setNewConfig((prev) => ({
				...prev,
				"api.ensure-article-existence-categories": ""
			}));
		}, [setNewConfig, categories])
	};
}
function isSabnzbdSettingsUpdated(config, newConfig) {
	return config["api.key"] !== newConfig["api.key"] || config["api.categories"] !== newConfig["api.categories"] || config["api.manual-category"] !== newConfig["api.manual-category"] || config["rclone.mount-dir"] !== newConfig["rclone.mount-dir"] || config["api.ensure-importable-video"] !== newConfig["api.ensure-importable-video"] || config["api.ensure-article-existence-categories"] !== newConfig["api.ensure-article-existence-categories"] || config["api.ignore-history-limit"] !== newConfig["api.ignore-history-limit"] || config["api.duplicate-nzb-behavior"] !== newConfig["api.duplicate-nzb-behavior"] || config["api.download-file-blocklist"] !== newConfig["api.download-file-blocklist"] || config["api.import-strategy"] !== newConfig["api.import-strategy"] || config["api.completed-downloads-dir"] !== newConfig["api.completed-downloads-dir"] || config["general.base-url"] !== newConfig["general.base-url"] || config["api.user-agent"] !== newConfig["api.user-agent"] || config["api.nzb-backup-enabled"] !== newConfig["api.nzb-backup-enabled"] || config["api.nzb-backup-location"] !== newConfig["api.nzb-backup-location"];
}
function isSabnzbdSettingsValid(newConfig) {
	return isValidCategories(newConfig["api.categories"]) && isValidNzbBackupLocation(newConfig);
}
function generateNewApiKey() {
	return crypto.randomUUID().toString().replaceAll("-", "");
}
function isValidCategories(categories) {
	if (categories === "") return true;
	return categories.split(",").map((x) => x.trim()).every((x) => isAlphaNumericWithDashes(x));
}
function isValidNzbBackupLocation(config) {
	return config["api.nzb-backup-enabled"] !== "true" || !!config["api.nzb-backup-location"]?.trim();
}
function isAlphaNumericWithDashes(input) {
	return /^[A-Za-z0-9-]+$/.test(input);
}
var webdav_module_default = {
	container: "_container_wc504_1",
	input: "_input_wc504_5",
	error: "_error_wc504_13"
};
//#endregion
//#region app/routes/settings/webdav/webdav.tsx
function WebdavSettings({ config, setNewConfig }) {
	return /* @__PURE__ */ jsxs("div", {
		className: webdav_module_default.container,
		children: [
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "webdav-user-input",
					children: "WebDAV User"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					...className([webdav_module_default.input, !isValidUser(config["webdav.user"]) && webdav_module_default.error]),
					type: "text",
					id: "webdav-user-input",
					"aria-describedby": "webdav-user-help",
					placeholder: "admin",
					value: config["webdav.user"],
					onChange: (e) => setNewConfig({
						...config,
						"webdav.user": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "webdav-user-help",
					muted: true,
					children: "Use this user to connect to the webdav. Only letters, numbers, dashes, and underscores allowed."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "webdav-pass-input",
					children: "WebDAV Password"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					className: webdav_module_default.input,
					type: "password",
					id: "webdav-pass-input",
					"aria-describedby": "webdav-pass-help",
					value: config["webdav.pass"],
					onChange: (e) => setNewConfig({
						...config,
						"webdav.pass": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "webdav-pass-help",
					muted: true,
					children: "Use this password to connect to the webdav."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "max-download-connections-input",
					children: "Max Download Connections"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					...className([webdav_module_default.input, !isValidMaxDownloadConnections(config["usenet.max-download-connections"]) && webdav_module_default.error]),
					type: "text",
					id: "max-download-connections-input",
					"aria-describedby": "max-download-connections-help",
					placeholder: "15",
					value: config["usenet.max-download-connections"],
					onChange: (e) => setNewConfig({
						...config,
						"usenet.max-download-connections": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "max-download-connections-help",
					muted: true,
					children: "The maximum number of connections that will be used for downloading articles from your usenet provider(s). Configure this to the minimum number of connections that will fully saturate your server's bandwidth."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "streaming-priority-input",
					children: "Streaming Priority (vs Queue)"
				}),
				/* @__PURE__ */ jsxs(InputGroup, {
					className: webdav_module_default.input,
					children: [/* @__PURE__ */ jsx(Form$1.Control, {
						className: !isValidStreamingPriority(config["usenet.streaming-priority"]) ? webdav_module_default.error : void 0,
						type: "text",
						id: "streaming-priority-input",
						"aria-describedby": "streaming-priority-help",
						placeholder: "80",
						value: config["usenet.streaming-priority"],
						onChange: (e) => setNewConfig({
							...config,
							"usenet.streaming-priority": e.target.value
						})
					}), /* @__PURE__ */ jsx(InputGroup.Text, { children: "%" })]
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "streaming-priority-help",
					muted: true,
					children: "When streaming from the webdav while the queue is also active, how much bandwidth should be dedicated to streaming?"
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "article-buffer-size-input",
					children: "Article Buffer Size"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					...className([webdav_module_default.input, !isValidArticleBufferSize(config["usenet.article-buffer-size"]) && webdav_module_default.error]),
					type: "text",
					id: "article-buffer-size-input",
					"aria-describedby": "article-buffer-size-help",
					placeholder: "40",
					value: config["usenet.article-buffer-size"],
					onChange: (e) => setNewConfig({
						...config,
						"usenet.article-buffer-size": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "article-buffer-size-help",
					muted: true,
					children: "The number of articles to buffer ahead, per stream, when reading from the webdav."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: webdav_module_default.input,
				type: "checkbox",
				id: "readonly-checkbox",
				"aria-describedby": "readonly-help",
				label: `Enforce Read-Only`,
				checked: config["webdav.enforce-readonly"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"webdav.enforce-readonly": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "readonly-help",
				muted: true,
				children: "The WebDAV `/content` folder will be readonly when checked. WebDAV clients will not be able to delete files within this directory."
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: webdav_module_default.input,
				type: "checkbox",
				id: "show-hidden-files-checkbox",
				"aria-describedby": "show-hidden-files-help",
				label: `Show hidden files on Dav Explorer`,
				checked: config["webdav.show-hidden-files"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"webdav.show-hidden-files": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "show-hidden-files-help",
				muted: true,
				children: "Hidden files or directories are those whose names are prefixed by a period."
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: webdav_module_default.input,
				type: "checkbox",
				id: "preview-par2-files-checkbox",
				"aria-describedby": "preview-par2-files-help",
				label: `Preview par2 files on Dav Explorer`,
				checked: config["webdav.preview-par2-files"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"webdav.preview-par2-files": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "preview-par2-files-help",
				muted: true,
				children: "When enabled, par2 files will be rendered as text files on the Dav Explorer page, displaying all File-Descriptor entries."
			})] })
		]
	});
}
function isWebdavSettingsUpdated(config, newConfig) {
	return config["webdav.user"] !== newConfig["webdav.user"] || config["webdav.pass"] !== newConfig["webdav.pass"] || config["usenet.max-download-connections"] !== newConfig["usenet.max-download-connections"] || config["usenet.streaming-priority"] !== newConfig["usenet.streaming-priority"] || config["usenet.article-buffer-size"] !== newConfig["usenet.article-buffer-size"] || config["webdav.show-hidden-files"] !== newConfig["webdav.show-hidden-files"] || config["webdav.enforce-readonly"] !== newConfig["webdav.enforce-readonly"] || config["webdav.preview-par2-files"] !== newConfig["webdav.preview-par2-files"];
}
function isWebdavSettingsValid(newConfig) {
	return isValidUser(newConfig["webdav.user"]) && isValidMaxDownloadConnections(newConfig["usenet.max-download-connections"]) && isValidStreamingPriority(newConfig["usenet.streaming-priority"]) && isValidArticleBufferSize(newConfig["usenet.article-buffer-size"]);
}
function isValidUser(user) {
	return /^[A-Za-z0-9_-]+$/.test(user);
}
function isValidMaxDownloadConnections(value) {
	return isPositiveInteger(value);
}
function isValidStreamingPriority(value) {
	if (value.trim() === "") return false;
	const num = Number(value);
	return Number.isInteger(num) && num >= 0 && num <= 100;
}
function isValidArticleBufferSize(value) {
	return isPositiveInteger(value);
}
var arrs_module_default = {
	container: "_container_uicqh_1",
	section: "_section_uicqh_5",
	sectionHeader: "_sectionHeader_uicqh_9",
	sectionDivider: "_sectionDivider_uicqh_20",
	instanceCard: "_instanceCard_uicqh_25",
	"card-body": "_card-body_uicqh_33",
	closeButton: "_closeButton_uicqh_37",
	input: "_input_uicqh_58",
	error: "_error_uicqh_66",
	removeButton: "_removeButton_uicqh_70",
	alertMessage: "_alertMessage_uicqh_74",
	listItem: "_listItem_uicqh_83",
	statusMessage: "_statusMessage_uicqh_87",
	testButton: "_testButton_uicqh_91"
};
//#endregion
//#region app/routes/settings/arrs/arrs.tsx
var queueStatusMessages = [
	{
		display: "Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible.",
		searchTerm: "Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible."
	},
	{
		display: "Found matching movie via grab history, but release was matched to movie by ID. Manual Import required.",
		searchTerm: "Found matching movie via grab history, but release was matched to movie by ID. Manual Import required."
	},
	{
		display: "Episode was not found in the grabbed release",
		searchTerm: "was not found in the grabbed release"
	},
	{
		display: "Episode(s) was/were unexpected considering the folder name",
		searchTerm: "unexpected considering the"
	},
	{
		display: "Not an upgrade for existing episode file(s)",
		searchTerm: "Not an upgrade for existing episode file(s)"
	},
	{
		display: "Not an upgrade for existing movie file",
		searchTerm: "Not an upgrade for existing movie file"
	},
	{
		display: "Not a Custom Format upgrade",
		searchTerm: "Not a Custom Format upgrade"
	},
	{
		display: "No files found are eligible for import",
		searchTerm: "No files found are eligible for import"
	},
	{
		display: "Episode file already imported",
		searchTerm: "Episode file already imported"
	},
	{
		display: "No audio tracks detected",
		searchTerm: "No audio tracks detected"
	},
	{
		display: "Invalid season or episode",
		searchTerm: "Invalid season or episode"
	},
	{
		display: "Single episode file contains all episodes in seasons",
		searchTerm: "Single episode file contains all episodes in seasons"
	},
	{
		display: "Unable to determine if file is a sample",
		searchTerm: "Unable to determine if file is a sample"
	},
	{
		display: "Sample",
		searchTerm: "Sample"
	},
	{
		display: "Found archive file, might need to be extracted",
		searchTerm: "Found archive file, might need to be extracted"
	}
];
function ArrsSettings({ config, setNewConfig }) {
	const arrConfig = JSON.parse(config["arr.instances"]);
	const updateConfig = useCallback((newArrConfig) => {
		setNewConfig({
			...config,
			"arr.instances": JSON.stringify(newArrConfig)
		});
	}, [config, setNewConfig]);
	const addRadarrInstance = useCallback(() => {
		updateConfig({
			...arrConfig,
			RadarrInstances: [...arrConfig.RadarrInstances, {
				Host: "",
				ApiKey: ""
			}]
		});
	}, [arrConfig, updateConfig]);
	const removeRadarrInstance = useCallback((index) => {
		updateConfig({
			...arrConfig,
			RadarrInstances: arrConfig.RadarrInstances.filter((_, i) => i !== index)
		});
	}, [arrConfig, updateConfig]);
	const updateRadarrInstance = useCallback((index, field, value) => {
		updateConfig({
			...arrConfig,
			RadarrInstances: arrConfig.RadarrInstances.map((instance, i) => i === index ? {
				...instance,
				[field]: value
			} : instance)
		});
	}, [arrConfig, updateConfig]);
	const addSonarrInstance = useCallback(() => {
		updateConfig({
			...arrConfig,
			SonarrInstances: [...arrConfig.SonarrInstances, {
				Host: "",
				ApiKey: ""
			}]
		});
	}, [arrConfig, updateConfig]);
	const removeSonarrInstance = useCallback((index) => {
		updateConfig({
			...arrConfig,
			SonarrInstances: arrConfig.SonarrInstances.filter((_, i) => i !== index)
		});
	}, [arrConfig, updateConfig]);
	const updateSonarrInstance = useCallback((index, field, value) => {
		updateConfig({
			...arrConfig,
			SonarrInstances: arrConfig.SonarrInstances.map((instance, i) => i === index ? {
				...instance,
				[field]: value
			} : instance)
		});
	}, [arrConfig, updateConfig]);
	const updateQueueAction = useCallback((searchTerm, action) => {
		var newQueueRules = (arrConfig.QueueRules || []).filter((queueRule) => queueStatusMessages.map((x) => x.searchTerm).includes(queueRule.Message)).map((queueRule) => queueRule.Message == searchTerm ? {
			Message: searchTerm,
			Action: action
		} : queueRule);
		if (!newQueueRules.find((queueRule) => queueRule.Message == searchTerm)) newQueueRules.push({
			Message: searchTerm,
			Action: action
		});
		updateConfig({
			...arrConfig,
			QueueRules: newQueueRules
		});
	}, [arrConfig, updateConfig]);
	return /* @__PURE__ */ jsxs("div", {
		className: arrs_module_default.container,
		children: [
			/* @__PURE__ */ jsxs("div", {
				className: arrs_module_default.section,
				children: [/* @__PURE__ */ jsxs("div", {
					className: arrs_module_default.sectionHeader,
					children: [/* @__PURE__ */ jsx("div", { children: "Radarr Instances" }), /* @__PURE__ */ jsx(Button, {
						variant: "primary",
						size: "sm",
						onClick: addRadarrInstance,
						children: "Add"
					})]
				}), arrConfig.RadarrInstances.length === 0 ? /* @__PURE__ */ jsx("p", {
					className: arrs_module_default.alertMessage,
					children: "No Radarr instances configured. Click on the \"Add\" button to get started."
				}) : arrConfig.RadarrInstances.map((instance, index) => /* @__PURE__ */ jsx(InstanceForm, {
					instance,
					index,
					type: "radarr",
					onUpdate: updateRadarrInstance,
					onRemove: removeRadarrInstance
				}, index))]
			}),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs("div", {
				className: arrs_module_default.section,
				children: [/* @__PURE__ */ jsxs("div", {
					className: arrs_module_default.sectionHeader,
					children: [/* @__PURE__ */ jsx("div", { children: "Sonarr Instances" }), /* @__PURE__ */ jsx(Button, {
						variant: "primary",
						size: "sm",
						onClick: addSonarrInstance,
						children: "Add"
					})]
				}), arrConfig.SonarrInstances.length === 0 ? /* @__PURE__ */ jsx("p", {
					className: arrs_module_default.alertMessage,
					children: "No Sonarr instances configured. Click on the \"Add\" button to get started."
				}) : arrConfig.SonarrInstances.map((instance, index) => /* @__PURE__ */ jsx(InstanceForm, {
					instance,
					index,
					type: "sonarr",
					onUpdate: updateSonarrInstance,
					onRemove: removeSonarrInstance
				}, index))]
			}),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs("div", {
				className: arrs_module_default.section,
				children: [
					/* @__PURE__ */ jsx("div", {
						className: arrs_module_default.sectionHeader,
						children: /* @__PURE__ */ jsx("div", { children: "Automatic Queue Management" })
					}),
					/* @__PURE__ */ jsx("p", {
						className: arrs_module_default.alertMessage,
						children: "Configure what to do for items stuck in Radarr / Sonarr queues. Different actions can be configured for different status messages. Only `usenet` queue items will be acted upon."
					}),
					/* @__PURE__ */ jsx("ul", { children: queueStatusMessages.map((queueStatusMessage, index) => /* @__PURE__ */ jsxs("li", {
						className: arrs_module_default.listItem,
						children: [/* @__PURE__ */ jsx("div", {
							className: arrs_module_default.statusMessage,
							children: queueStatusMessage.display
						}), /* @__PURE__ */ jsxs(Form$1.Select, {
							className: arrs_module_default.input,
							value: arrConfig.QueueRules.find((x) => x.Message == queueStatusMessage.searchTerm)?.Action ?? "0",
							onChange: (e) => updateQueueAction(queueStatusMessage.searchTerm, Number(e.target.value)),
							children: [
								/* @__PURE__ */ jsx("option", {
									value: "0",
									children: "Do Nothing"
								}),
								/* @__PURE__ */ jsx("option", {
									value: "1",
									children: "Remove"
								}),
								/* @__PURE__ */ jsx("option", {
									value: "2",
									children: "Remove and Blocklist"
								}),
								/* @__PURE__ */ jsx("option", {
									value: "3",
									children: "Remove, Blocklist, and Search"
								})
							]
						})]
					}, index)) })
				]
			})
		]
	});
}
function InstanceForm({ instance, index, type, onUpdate, onRemove }) {
	const [connectionState, setConnectionState] = useState("idle");
	useEffect(() => {
		setConnectionState("idle");
	}, [instance.Host, instance.ApiKey]);
	const testConnection = useCallback(async (host, apiKey) => {
		if (!host.trim() || !apiKey.trim()) return;
		setConnectionState("testing");
		try {
			const formData = new FormData();
			formData.append("host", host);
			formData.append("apiKey", apiKey);
			const result = await (await fetch("/api/test-arr-connection", {
				method: "POST",
				body: formData
			})).json();
			if (result.status && result.connected) setConnectionState("success");
			else setConnectionState("error");
		} catch (error) {
			setConnectionState("error");
		}
	}, []);
	return /* @__PURE__ */ jsxs(Card, {
		className: arrs_module_default.instanceCard,
		children: [/* @__PURE__ */ jsx("button", {
			className: arrs_module_default.closeButton,
			onClick: () => onRemove(index),
			"aria-label": "Remove instance",
			children: "×"
		}), /* @__PURE__ */ jsxs(Card.Body, { children: [/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Label, { children: "Host" }), /* @__PURE__ */ jsxs(InputGroup, {
			className: arrs_module_default.input,
			children: [/* @__PURE__ */ jsx(Form$1.Control, {
				type: "text",
				placeholder: type === "radarr" ? "http://localhost:7878" : "http://localhost:8989",
				value: instance.Host,
				onChange: (e) => onUpdate(index, "Host", e.target.value)
			}), instance.Host.trim() && instance.ApiKey.trim() && /* @__PURE__ */ jsx(Button, {
				variant: connectionState === "success" ? "success" : connectionState === "error" ? "danger" : "secondary",
				onClick: () => testConnection(instance.Host, instance.ApiKey),
				disabled: connectionState === "testing",
				className: arrs_module_default.testButton,
				children: connectionState === "testing" ? /* @__PURE__ */ jsx(Spinner, {
					animation: "border",
					size: "sm"
				}) : connectionState === "success" ? "✓" : connectionState === "error" ? "✗" : "Test Conn"
			})]
		})] }), /* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Label, { children: "API Key" }), /* @__PURE__ */ jsx(Form$1.Control, {
			type: "password",
			className: arrs_module_default.input,
			value: instance.ApiKey,
			onChange: (e) => onUpdate(index, "ApiKey", e.target.value)
		})] })] })]
	});
}
function isArrsSettingsUpdated(config, newConfig) {
	return config["arr.instances"] !== newConfig["arr.instances"];
}
function isArrsSettingsValid(newConfig) {
	try {
		const arrConfig = JSON.parse(newConfig["arr.instances"] || "{}");
		for (const instance of arrConfig.RadarrInstances || []) if (!isValidHost(instance.Host) || !isValidApiKey(instance.ApiKey)) return false;
		for (const instance of arrConfig.SonarrInstances || []) if (!isValidHost(instance.Host) || !isValidApiKey(instance.ApiKey)) return false;
		return true;
	} catch {
		return false;
	}
}
function isValidHost(host) {
	if (host.trim().length === 0) return false;
	try {
		new URL(host);
		return true;
	} catch {
		return false;
	}
}
function isValidApiKey(apiKey) {
	return apiKey.trim().length > 0;
}
var maintenance_module_default = {
	tasksContainer: "_tasksContainer_1cvs7_1",
	settingsContainer: "_settingsContainer_1cvs7_5",
	input: "_input_1cvs7_9",
	accordionItem: "_accordionItem_1cvs7_13",
	accordionHeader: "_accordionHeader_1cvs7_18",
	accordionBody: "_accordionBody_1cvs7_29"
};
var remove_unlinked_files_module_default = {
	container: "_container_i7h0x_1",
	task: "_task_i7h0x_5",
	alert: "_alert_i7h0x_11",
	run: "_run_i7h0x_15",
	"run-button": "_run-button_i7h0x_23",
	"dryrun-button": "_dryrun-button_i7h0x_27",
	"task-progress": "_task-progress_i7h0x_31",
	list: "_list_i7h0x_36",
	"list-item": "_list-item_i7h0x_40"
};
//#endregion
//#region app/routes/settings/maintenance/remove-unlinked-files/remove-unlinked-files.tsx
var cleanupTaskTopic$1 = { "ctp": "state" };
function RemoveUnlinkedFiles({ savedConfig }) {
	const [connected, setConnected] = useState(false);
	const [progress, setProgress] = useState(null);
	const [isFetching, setIsFetching] = useState(false);
	const progressMessage = progress?.replace("Dry Run - ", "");
	const libraryDir = savedConfig["media.library-dir"];
	const isDone = progressMessage?.startsWith("Done");
	const isRunning = !(progressMessage?.startsWith("Done") || progressMessage?.startsWith("Failed") || progressMessage?.startsWith("Aborted")) && (isFetching || progress !== null);
	const isRunButtonEnabled = !!libraryDir && connected && !isRunning;
	const runButtonVariant = isRunButtonEnabled ? "success" : "secondary";
	const runButtonLabel = isRunning ? "⌛ Running.." : "▶ Run Task";
	useEffect(() => {
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage((_, message) => setProgress(message));
			ws.onopen = () => {
				setConnected(true);
				ws.send(JSON.stringify(cleanupTaskTopic$1));
			};
			ws.onclose = () => {
				!disposed && setTimeout(() => connect(), 1e3);
				setProgress(null);
			};
			ws.onerror = () => {
				ws.close();
			};
			return () => {
				disposed = true;
				ws.close();
			};
		}
		return connect();
	}, [setProgress, setConnected]);
	const onRun = useCallback(async () => {
		setIsFetching(true);
		await fetch("/api/remove-unlinked-files");
		setIsFetching(false);
	}, [setIsFetching]);
	const onDryRun = useCallback(async (event) => {
		setIsFetching(true);
		await fetch("/api/remove-unlinked-files/dry-run");
		setIsFetching(false);
	}, [setIsFetching]);
	const dryRunButton = /* @__PURE__ */ jsx(Button, {
		className: remove_unlinked_files_module_default["dryrun-button"],
		disabled: !isRunButtonEnabled,
		onClick: onDryRun,
		variant: "secondary",
		size: "sm",
		children: "perform a dry-run"
	});
	return /* @__PURE__ */ jsxs(Fragment, { children: [
		!libraryDir && /* @__PURE__ */ jsxs(Alert, {
			className: remove_unlinked_files_module_default.alert,
			variant: "warning",
			children: ["Warning", /* @__PURE__ */ jsx("ul", {
				className: remove_unlinked_files_module_default.list,
				children: /* @__PURE__ */ jsx("li", {
					className: remove_unlinked_files_module_default["list-item"],
					children: "You must first configure the Library Directory setting before running this task. Head over to the Repairs tab."
				})
			})]
		}),
		libraryDir && /* @__PURE__ */ jsxs(Alert, {
			className: remove_unlinked_files_module_default.alert,
			variant: "danger",
			children: [/* @__PURE__ */ jsx("span", {
				style: { fontWeight: "bold" },
				children: "Danger"
			}), /* @__PURE__ */ jsxs("ul", {
				className: remove_unlinked_files_module_default.list,
				children: [/* @__PURE__ */ jsx("li", {
					className: remove_unlinked_files_module_default["list-item"],
					children: "Make a backup of your NzbDAV database prior to running this task"
				}), /* @__PURE__ */ jsx("li", {
					className: remove_unlinked_files_module_default["list-item"],
					children: "Files will be removed from the webdav and will not be recoverable without a backup"
				})]
			})]
		}),
		/* @__PURE__ */ jsx("div", {
			className: remove_unlinked_files_module_default.task,
			children: /* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsxs("div", {
				className: remove_unlinked_files_module_default.run,
				children: [/* @__PURE__ */ jsx(Button, {
					className: remove_unlinked_files_module_default["run-button"],
					variant: runButtonVariant,
					onClick: onRun,
					disabled: !isRunButtonEnabled,
					children: runButtonLabel
				}), /* @__PURE__ */ jsxs("div", {
					className: remove_unlinked_files_module_default["task-progress"],
					children: [progress, isDone && /* @__PURE__ */ jsxs(Fragment, { children: ["\xA0", /* @__PURE__ */ jsx("a", {
						href: "/api/remove-unlinked-files/audit",
						children: "Audit."
					})] })]
				})]
			}), /* @__PURE__ */ jsxs(Form$1.Text, {
				id: "cleanup-task-progress-help",
				muted: true,
				children: [
					/* @__PURE__ */ jsx("br", {}),
					"This task will scan your organized media library for all symlinked or *.strm linked files. Any file on the webdav that is not pointed to by your library will be deleted. If you would like to see what would be deleted without running the task, you can ",
					dryRunButton,
					". The dry-run will not delete anything.",
					/* @__PURE__ */ jsx("br", {}),
					/* @__PURE__ */ jsx("br", {}),
					"Note: Files still present in the History table will not be removed when running this task. It is assumed that files still present in the History table have not yet been imported by Arrs and they are expected to not yet have a corresponding symlink/strm in the Library folder. These files will remain intact until Arrs have a chance to process them and remove them from the History table."
				]
			})] })
		})
	] });
}
var strm_to_symlinks_module_default = {
	container: "_container_i7h0x_1",
	task: "_task_i7h0x_5",
	alert: "_alert_i7h0x_11",
	run: "_run_i7h0x_15",
	"run-button": "_run-button_i7h0x_23",
	"dryrun-button": "_dryrun-button_i7h0x_27",
	"task-progress": "_task-progress_i7h0x_31",
	list: "_list_i7h0x_36",
	"list-item": "_list-item_i7h0x_40"
};
//#endregion
//#region app/routes/settings/maintenance/strm-to-symlinks/strm-to-symlinks.tsx
var cleanupTaskTopic = { "st2sy": "state" };
function ConvertStrmToSymlinks({ savedConfig }) {
	const [connected, setConnected] = useState(false);
	const [progress, setProgress] = useState(null);
	const [isFetching, setIsFetching] = useState(false);
	const libraryDir = savedConfig["media.library-dir"];
	progress?.startsWith("Done");
	const isRunning = !(progress?.startsWith("Done") || progress?.startsWith("Failed")) && (isFetching || progress !== null);
	const isRunButtonEnabled = !!libraryDir && connected && !isRunning;
	const runButtonVariant = isRunButtonEnabled ? "success" : "secondary";
	const runButtonLabel = isRunning ? "⌛ Running.." : "▶ Run Task";
	useEffect(() => {
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage((_, message) => setProgress(message));
			ws.onopen = () => {
				setConnected(true);
				ws.send(JSON.stringify(cleanupTaskTopic));
			};
			ws.onclose = () => {
				!disposed && setTimeout(() => connect(), 1e3);
				setProgress(null);
			};
			ws.onerror = () => {
				ws.close();
			};
			return () => {
				disposed = true;
				ws.close();
			};
		}
		return connect();
	}, [setProgress, setConnected]);
	const onRun = useCallback(async () => {
		setIsFetching(true);
		await fetch("/api/convert-strm-to-symlinks");
		setIsFetching(false);
	}, [setIsFetching]);
	return /* @__PURE__ */ jsxs(Fragment, { children: [
		!libraryDir && /* @__PURE__ */ jsxs(Alert, {
			className: strm_to_symlinks_module_default.alert,
			variant: "warning",
			children: ["Warning", /* @__PURE__ */ jsx("ul", {
				className: strm_to_symlinks_module_default.list,
				children: /* @__PURE__ */ jsx("li", {
					className: strm_to_symlinks_module_default["list-item"],
					children: "You must first configure the Library Directory setting before running this task. Head over to the Repairs tab."
				})
			})]
		}),
		libraryDir && /* @__PURE__ */ jsxs(Alert, {
			className: strm_to_symlinks_module_default.alert,
			variant: "danger",
			children: [/* @__PURE__ */ jsx("span", {
				style: { fontWeight: "bold" },
				children: "Danger"
			}), /* @__PURE__ */ jsxs("ul", {
				className: strm_to_symlinks_module_default.list,
				children: [/* @__PURE__ */ jsx("li", {
					className: strm_to_symlinks_module_default["list-item"],
					children: "Make a backup of your entire Library Dir prior to running this task"
				}), /* @__PURE__ */ jsxs("li", {
					className: strm_to_symlinks_module_default["list-item"],
					children: [
						"Strm files will be deleted from `",
						libraryDir,
						"` and will not be recoverable without a backup."
					]
				})]
			})]
		}),
		/* @__PURE__ */ jsx("div", {
			className: strm_to_symlinks_module_default.task,
			children: /* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsxs("div", {
				className: strm_to_symlinks_module_default.run,
				children: [/* @__PURE__ */ jsx(Button, {
					className: strm_to_symlinks_module_default["run-button"],
					variant: runButtonVariant,
					onClick: onRun,
					disabled: !isRunButtonEnabled,
					children: runButtonLabel
				}), /* @__PURE__ */ jsx("div", {
					className: strm_to_symlinks_module_default["task-progress"],
					children: progress
				})]
			}), /* @__PURE__ */ jsxs(Form$1.Text, {
				id: "cleanup-task-progress-help",
				muted: true,
				children: [/* @__PURE__ */ jsx("br", {}), "This task will scan your organized media library for all *.strm files. Every *.strm file that links to nzbdav media will be deleted and be replaced by a symlink. The newly created symlinks will all point to the corresponding file within your rclone mount."]
			})] })
		})
	] });
}
var migrate_database_files_to_blobstore_module_default = { task: "_task_3gt3o_1" };
//#endregion
//#region app/routes/settings/maintenance/migrate-database-files-to-blobstore/migrate-database-files-to-blobstore.tsx
var TaskTopic = { "uftbmp": "state" };
function MigrateDatabaseFilesToBlobstore({ savedConfig }) {
	const [connected, setConnected] = useState(false);
	const [progress, setProgress] = useState(null);
	useEffect(() => {
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage((_, message) => setProgress(message));
			ws.onopen = () => {
				setConnected(true);
				ws.send(JSON.stringify(TaskTopic));
			};
			ws.onclose = () => {
				!disposed && setTimeout(() => connect(), 1e3);
				setProgress(null);
			};
			ws.onerror = () => {
				ws.close();
			};
			return () => {
				disposed = true;
				ws.close();
			};
		}
		return connect();
	}, [setProgress, setConnected]);
	return /* @__PURE__ */ jsx("div", {
		className: migrate_database_files_to_blobstore_module_default.task,
		children: /* @__PURE__ */ jsx(Form$1.Group, { children: /* @__PURE__ */ jsxs(Form$1.Text, {
			id: "blob-task-progress-help",
			muted: true,
			children: [
				/* @__PURE__ */ jsx("br", {}),
				"This task runs automatically in the background to optimize the database. No action is required on your part. You can simply track the progress here. For context, the sqlite database used by the backend is slow at reading and writing large data blobs. It is better to store those externally in the filesystem directly, as documented ",
				/* @__PURE__ */ jsx("a", {
					href: "https://sqlite.org/intern-v-extern-blob.html",
					children: "here"
				}),
				". However, as of now, all blobs have been stored in the database directly. This task migrates those blobs to the filesystem, for better performance.",
				/* @__PURE__ */ jsx("br", {}),
				/* @__PURE__ */ jsx("br", {}),
				/* @__PURE__ */ jsx("code", { children: progress || "The task has not started." })
			]
		}) })
	});
}
//#endregion
//#region app/routes/settings/maintenance/maintenance.tsx
function Maintenance({ savedConfig, config, setNewConfig }) {
	return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
		className: maintenance_module_default.settingsContainer,
		children: [
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: maintenance_module_default.input,
				type: "checkbox",
				id: "db-startup-vacuum-enabled-checkbox",
				"aria-describedby": "db-startup-vacuum-enabled-help",
				label: "Perform Database Vacuum on Start",
				checked: config["db.is-startup-vacuum-enabled"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"db.is-startup-vacuum-enabled": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "db-startup-vacuum-enabled-help",
				muted: true,
				children: "When enabled, nzbdav will run a SQLite VACUUM on the database at every startup. This reclaims unused disk space and can improve query performance over time, but may increase startup time for large databases."
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Check, {
					className: maintenance_module_default.input,
					type: "checkbox",
					id: "remove-orphaned-schedule-enabled-checkbox",
					"aria-describedby": "remove-orphaned-schedule-help",
					label: "Schedule \"Remove Orphaned Files\" Task Daily",
					checked: isScheduledOrphanTaskEnabled(config),
					onChange: (e) => setNewConfig({
						...config,
						"maintenance.remove-orphaned-schedule-enabled": "" + e.target.checked
					})
				}),
				/* @__PURE__ */ jsxs(InputGroup, {
					className: maintenance_module_default.input,
					style: { marginTop: "15px" },
					children: [
						/* @__PURE__ */ jsx(Form$1.Select, {
							disabled: !isScheduledOrphanTaskEnabled(config),
							value: getScheduledTime(config).hour,
							onChange: (e) => setNewConfig({
								...config,
								"maintenance.remove-orphaned-schedule-time": buildScheduledTime(parseInt(e.target.value), getScheduledTime(config).minute, getScheduledTime(config).period)
							}),
							children: Array.from({ length: 12 }, (_, i) => i + 1).map((h) => /* @__PURE__ */ jsx("option", {
								value: h,
								children: h
							}, h))
						}),
						/* @__PURE__ */ jsxs(Form$1.Select, {
							disabled: !isScheduledOrphanTaskEnabled(config),
							value: getScheduledTime(config).minute,
							onChange: (e) => setNewConfig({
								...config,
								"maintenance.remove-orphaned-schedule-time": buildScheduledTime(getScheduledTime(config).hour, parseInt(e.target.value), getScheduledTime(config).period)
							}),
							children: [
								/* @__PURE__ */ jsx("option", {
									value: 0,
									children: "00"
								}),
								/* @__PURE__ */ jsx("option", {
									value: 15,
									children: "15"
								}),
								/* @__PURE__ */ jsx("option", {
									value: 30,
									children: "30"
								}),
								/* @__PURE__ */ jsx("option", {
									value: 45,
									children: "45"
								})
							]
						}),
						/* @__PURE__ */ jsxs(Form$1.Select, {
							disabled: !isScheduledOrphanTaskEnabled(config),
							value: getScheduledTime(config).period,
							onChange: (e) => setNewConfig({
								...config,
								"maintenance.remove-orphaned-schedule-time": buildScheduledTime(getScheduledTime(config).hour, getScheduledTime(config).minute, e.target.value)
							}),
							children: [/* @__PURE__ */ jsx("option", {
								value: "am",
								children: "am"
							}), /* @__PURE__ */ jsx("option", {
								value: "pm",
								children: "pm"
							})]
						})
					]
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "remove-orphaned-schedule-help",
					muted: true,
					children: "When enabled, the \"Remove Orphaned Files\" task will run every day at the specified time. You may need to set the TZ env variable to ensure the correct timezone."
				})
			] })
		]
	}), /* @__PURE__ */ jsxs("div", {
		className: maintenance_module_default.tasksContainer,
		children: [/* @__PURE__ */ jsx("hr", {}), /* @__PURE__ */ jsxs(Accordion, { children: [
			/* @__PURE__ */ jsxs(Accordion.Item, {
				className: maintenance_module_default.accordionItem,
				eventKey: "remove-unlinked-files",
				children: [/* @__PURE__ */ jsx(Accordion.Header, {
					className: maintenance_module_default.accordionHeader,
					children: "Remove Orphaned Files"
				}), /* @__PURE__ */ jsx(Accordion.Body, {
					className: maintenance_module_default.accordionBody,
					children: /* @__PURE__ */ jsx(RemoveUnlinkedFiles, { savedConfig })
				})]
			}),
			/* @__PURE__ */ jsxs(Accordion.Item, {
				className: maintenance_module_default.accordionItem,
				eventKey: "strm-to-symlinks",
				children: [/* @__PURE__ */ jsx(Accordion.Header, {
					className: maintenance_module_default.accordionHeader,
					children: "Convert Strm Files to Symlnks"
				}), /* @__PURE__ */ jsx(Accordion.Body, {
					className: maintenance_module_default.accordionBody,
					children: /* @__PURE__ */ jsx(ConvertStrmToSymlinks, { savedConfig })
				})]
			}),
			/* @__PURE__ */ jsxs(Accordion.Item, {
				className: maintenance_module_default.accordionItem,
				eventKey: "migrate-database-files-to-blobstore",
				children: [/* @__PURE__ */ jsx(Accordion.Header, {
					className: maintenance_module_default.accordionHeader,
					children: "Migrate Large Database Blobs to Blobstore"
				}), /* @__PURE__ */ jsx(Accordion.Body, {
					className: maintenance_module_default.accordionBody,
					children: /* @__PURE__ */ jsx(MigrateDatabaseFilesToBlobstore, { savedConfig })
				})]
			})
		] })]
	})] });
}
function isMaintenanceSettingsUpdated(config, newConfig) {
	return config["db.is-startup-vacuum-enabled"] !== newConfig["db.is-startup-vacuum-enabled"] || config["maintenance.remove-orphaned-schedule-enabled"] !== newConfig["maintenance.remove-orphaned-schedule-enabled"] || config["maintenance.remove-orphaned-schedule-time"] !== newConfig["maintenance.remove-orphaned-schedule-time"];
}
function isScheduledOrphanTaskEnabled(config) {
	return config["maintenance.remove-orphaned-schedule-enabled"] === "true";
}
function getScheduledTime(config) {
	const totalMinutes = parseInt(config["maintenance.remove-orphaned-schedule-time"] || "0");
	return {
		hour: Math.floor(totalMinutes / 60) % 12 || 12,
		minute: totalMinutes % 60,
		period: Math.floor(totalMinutes / 60) >= 12 ? "pm" : "am"
	};
}
function buildScheduledTime(hour, minute, period) {
	return "" + ((hour % 12 + (period === "pm" ? 12 : 0)) * 60 + minute);
}
var repairs_module_default = {
	container: "_container_1xywg_1",
	input: "_input_1xywg_5",
	error: "_error_1xywg_13"
};
//#endregion
//#region app/routes/settings/repairs/repairs.tsx
function RepairsSettings({ config, setNewConfig }) {
	const libraryDirConfig = config["media.library-dir"];
	const arrConfig = JSON.parse(config["arr.instances"]);
	const areArrInstancesConfigured = arrConfig.RadarrInstances.length > 0 || arrConfig.SonarrInstances.length > 0;
	const canEnableRepairs = !!libraryDirConfig && areArrInstancesConfigured;
	var helpText = canEnableRepairs ? "When enabled, usenet items will be continuously monitored for health. Unhealthy items will be removed. If an unhealthy item is part of your Radarr/Sonarr library, a new search will be triggered to find a replacement." : "When enabled, usenet items will be continuously monitored for health. Unhealthy items will be removed and replaced. This setting can only be enabled once your Library-Directory and Radarr/Sonarr instances are configured.";
	return /* @__PURE__ */ jsxs("div", {
		className: repairs_module_default.container,
		children: [
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: repairs_module_default.input,
				type: "checkbox",
				id: "enable-repairs-checkbox",
				"aria-describedby": "enable-repairs-help",
				label: `Enable Background Repairs`,
				checked: canEnableRepairs && config["repair.enable"] === "true",
				disabled: !canEnableRepairs,
				onChange: (e) => setNewConfig({
					...config,
					"repair.enable": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "enable-repairs-help",
				muted: true,
				children: helpText
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "library-dir-input",
					children: "Library Directory"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					className: repairs_module_default.input,
					type: "text",
					id: "library-dir-input",
					"aria-describedby": "library-dir-help",
					value: config["media.library-dir"],
					onChange: (e) => setNewConfig({
						...config,
						"media.library-dir": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "library-dir-help",
					muted: true,
					children: "The path to your organized media library that contains all your imported symlinks or *.strm files. Make sure this path is visible to your NzbDAV container."
				})
			] })
		]
	});
}
function isRepairsSettingsUpdated(config, newConfig) {
	return config["repair.enable"] !== newConfig["repair.enable"] || config["media.library-dir"] !== newConfig["media.library-dir"];
}
var rclone_module_default = {
	container: "_container_yu8p3_1",
	input: "_input_yu8p3_5",
	testButton: "_testButton_yu8p3_13"
};
//#endregion
//#region app/routes/settings/rclone/rclone.tsx
function RcloneSettings({ config, setNewConfig }) {
	const [connectionState, setConnectionState] = useState("idle");
	useEffect(() => {
		setConnectionState("idle");
	}, [
		config["rclone.host"],
		config["rclone.user"],
		config["rclone.pass"]
	]);
	const testConnection = useCallback(async () => {
		const host = config["rclone.host"];
		if (!host?.trim()) return;
		setConnectionState("testing");
		try {
			const formData = new FormData();
			formData.append("host", host);
			formData.append("user", config["rclone.user"] ?? "");
			formData.append("pass", config["rclone.pass"] ?? "");
			const result = await (await fetch("/api/test-rclone-connection", {
				method: "POST",
				body: formData
			})).json();
			if (result.status && result.connected) setConnectionState("success");
			else setConnectionState("error");
		} catch (error) {
			setConnectionState("error");
		}
	}, [config]);
	return /* @__PURE__ */ jsxs("div", {
		className: rclone_module_default.container,
		children: [
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [/* @__PURE__ */ jsx(Form$1.Check, {
				className: rclone_module_default.input,
				type: "checkbox",
				id: "rclone-rc-enabled-checkbox",
				"aria-describedby": "rclone-rc-enabled-help",
				label: `Enable Rclone RC Server Notifications`,
				checked: config["rclone.rc-enabled"] === "true",
				onChange: (e) => setNewConfig({
					...config,
					"rclone.rc-enabled": "" + e.target.checked
				})
			}), /* @__PURE__ */ jsx(Form$1.Text, {
				id: "rclone-rc-enabled-help",
				muted: true,
				children: "When enabled, nzbdav will automatically notify your rclone mount via the RC API whenever files are added or removed on the webdav. This allows setting a high dir-cache-time setting on Rclone."
			})] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "rclone-host-input",
					children: "Rclone Server Host"
				}),
				/* @__PURE__ */ jsxs(InputGroup, {
					className: rclone_module_default.input,
					children: [/* @__PURE__ */ jsx(Form$1.Control, {
						type: "text",
						id: "rclone-host-input",
						"aria-describedby": "rclone-host-help",
						placeholder: "http://localhost:5572",
						value: config["rclone.host"],
						onChange: (e) => setNewConfig({
							...config,
							"rclone.host": e.target.value
						})
					}), config["rclone.host"]?.trim() && /* @__PURE__ */ jsx(Button, {
						variant: connectionState === "success" ? "success" : connectionState === "error" ? "danger" : "secondary",
						onClick: testConnection,
						disabled: connectionState === "testing",
						className: rclone_module_default.testButton,
						children: connectionState === "testing" ? /* @__PURE__ */ jsx(Spinner, {
							animation: "border",
							size: "sm"
						}) : connectionState === "success" ? "✓" : connectionState === "error" ? "✗" : "Test Conn"
					})]
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "rclone-host-help",
					muted: true,
					children: "The host address of the rclone RC API."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "rclone-user-input",
					children: "Rclone Server User"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					className: rclone_module_default.input,
					type: "text",
					id: "rclone-user-input",
					"aria-describedby": "rclone-user-help",
					value: config["rclone.user"],
					onChange: (e) => setNewConfig({
						...config,
						"rclone.user": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "rclone-user-help",
					muted: true,
					children: "The username for authenticating to the rclone RC API. This field is optional."
				})
			] }),
			/* @__PURE__ */ jsx("hr", {}),
			/* @__PURE__ */ jsxs(Form$1.Group, { children: [
				/* @__PURE__ */ jsx(Form$1.Label, {
					htmlFor: "rclone-pass-input",
					children: "Rclone Server Password"
				}),
				/* @__PURE__ */ jsx(Form$1.Control, {
					className: rclone_module_default.input,
					type: "password",
					id: "rclone-pass-input",
					"aria-describedby": "rclone-pass-help",
					value: config["rclone.pass"],
					onChange: (e) => setNewConfig({
						...config,
						"rclone.pass": e.target.value
					})
				}),
				/* @__PURE__ */ jsx(Form$1.Text, {
					id: "rclone-pass-help",
					muted: true,
					children: "The password for authenticating to the rclone RC API. This field is optional."
				})
			] })
		]
	});
}
function isRcloneSettingsUpdated(config, newConfig) {
	return config["rclone.rc-enabled"] !== newConfig["rclone.rc-enabled"] || config["rclone.host"] !== newConfig["rclone.host"] || config["rclone.user"] !== newConfig["rclone.user"] || config["rclone.pass"] !== newConfig["rclone.pass"];
}
var word_wrap_module_default = { wordWrap: "_wordWrap_vlc7m_1" };
//#endregion
//#region app/components/word-wrap/word-wrap.tsx
function WordWrap({ children }) {
	return /* @__PURE__ */ jsx("div", {
		className: word_wrap_module_default.wordWrap,
		children
	});
}
//#endregion
//#region app/components/confirm-modal/confirm-modal.tsx
function ConfirmModal(props) {
	const [isCheckboxChecked, setIsCheckboxChecked] = useState(false);
	const onConfirm = useCallback((isChecked) => {
		props.onConfirm(isChecked);
		setIsCheckboxChecked(false);
	}, [props.onConfirm, setIsCheckboxChecked]);
	const onCancel = useCallback(() => {
		props.onCancel();
		setIsCheckboxChecked(false);
	}, [props.onCancel, setIsCheckboxChecked]);
	return /* @__PURE__ */ jsxs(Modal, {
		show: props.show,
		onHide: onCancel,
		centered: true,
		scrollable: true,
		children: [
			/* @__PURE__ */ jsx(Modal.Header, {
				closeButton: true,
				children: /* @__PURE__ */ jsx(Modal.Title, { children: props.title })
			}),
			/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs("div", { children: [
				/* @__PURE__ */ jsx("div", {
					style: { fontSize: "12px" },
					children: /* @__PURE__ */ jsx(WordWrap, { children: props.message })
				}),
				props.checkboxMessage && /* @__PURE__ */ jsx(Form$1.Check, {
					type: "checkbox",
					id: "modal-checkbox",
					style: { marginTop: "12px" },
					label: props.checkboxMessage,
					checked: isCheckboxChecked,
					onChange: (e) => setIsCheckboxChecked(Boolean(e.target.checked))
				}),
				props.errorMessage && /* @__PURE__ */ jsx(Alert, {
					variant: "warning",
					style: {
						marginTop: "20px",
						fontSize: "14px"
					},
					children: props.errorMessage
				})
			] }) }),
			/* @__PURE__ */ jsxs(Modal.Footer, { children: [/* @__PURE__ */ jsx(Button, {
				variant: "secondary",
				onClick: onCancel,
				children: props.cancelText || "Close"
			}), /* @__PURE__ */ jsx(Button, {
				variant: "danger",
				onClick: () => onConfirm(isCheckboxChecked),
				children: props.confirmText || "Confirm Removal"
			})] })
		]
	});
}
//#endregion
//#region app/routes/settings/route.tsx
var route_exports$7 = /* @__PURE__ */ __exportAll({
	default: () => route_default$4,
	loader: () => loader$5
});
var defaultConfig = {
	"general.base-url": "",
	"api.key": "",
	"api.categories": "",
	"api.manual-category": "uncategorized",
	"api.ensure-importable-video": "true",
	"api.ensure-article-existence-categories": "",
	"api.ignore-history-limit": "true",
	"api.download-file-blocklist": "*.nfo, *.par2, *.sfv, *sample.mkv",
	"api.duplicate-nzb-behavior": "increment",
	"api.import-strategy": "symlinks",
	"api.completed-downloads-dir": "",
	"api.user-agent": "",
	"usenet.providers": "",
	"usenet.max-download-connections": "15",
	"usenet.streaming-priority": "80",
	"usenet.article-buffer-size": "40",
	"webdav.user": "admin",
	"webdav.pass": "",
	"webdav.show-hidden-files": "false",
	"webdav.enforce-readonly": "true",
	"webdav.preview-par2-files": "false",
	"rclone.rc-enabled": "false",
	"rclone.host": "",
	"rclone.user": "",
	"rclone.pass": "",
	"rclone.mount-dir": "",
	"media.library-dir": "",
	"arr.instances": "{\"RadarrInstances\":[],\"SonarrInstances\":[],\"QueueRules\":[]}",
	"repair.enable": "false",
	"db.is-startup-vacuum-enabled": "false",
	"maintenance.remove-orphaned-schedule-enabled": "false",
	"maintenance.remove-orphaned-schedule-time": "0",
	"api.nzb-backup-enabled": "false",
	"api.nzb-backup-location": ""
};
async function loader$5({ request }) {
	var configItems = await backendClient.getConfig(Object.keys(defaultConfig));
	const config = defaultConfig;
	for (const item of configItems) config[item.configName] = item.configValue;
	return {
		config,
		appVersion: process.env.NZBDAV_VERSION ?? "unknown"
	};
}
var route_default$4 = UNSAFE_withComponentProps(function Settings(props) {
	return /* @__PURE__ */ jsx(Body$1, { ...props.loaderData });
});
function Body$1(props) {
	const [config, setConfig] = useState(props.config);
	const [newConfig, setNewConfig] = useState(config);
	const [isSaving, setIsSaving] = useState(false);
	const [isSaved, setIsSaved] = useState(false);
	const [activeTab, setActiveTab] = useState("usenet");
	const iseUsenetUpdated = isUsenetSettingsUpdated(config, newConfig);
	const isSabnzbdUpdated = isSabnzbdSettingsUpdated(config, newConfig);
	const isWebdavUpdated = isWebdavSettingsUpdated(config, newConfig);
	const isArrsUpdated = isArrsSettingsUpdated(config, newConfig);
	const isRepairsUpdated = isRepairsSettingsUpdated(config, newConfig);
	const isRcloneUpdated = isRcloneSettingsUpdated(config, newConfig);
	const isMaintenanceUpdated = isMaintenanceSettingsUpdated(config, newConfig);
	const isUpdated = iseUsenetUpdated || isSabnzbdUpdated || isWebdavUpdated || isArrsUpdated || isRepairsUpdated || isRcloneUpdated || isMaintenanceUpdated;
	const navigationBlocker = useNavigationBlocker(isUpdated);
	const usenetTitle = iseUsenetUpdated ? "✏️ Usenet" : "Usenet";
	const sabnzbdTitle = isSabnzbdUpdated ? "✏️ SABnzbd " : "SABnzbd";
	const webdavTitle = isWebdavUpdated ? "✏️ WebDAV" : "WebDAV";
	const arrsTitle = isArrsUpdated ? "✏️ Radarr/Sonarr" : "Radarr/Sonarr";
	const repairsTitle = isRepairsUpdated ? "✏️ Repairs" : "Repairs";
	const rcloneTitle = isRcloneUpdated ? "✏️ Rclone Server" : "Rclone Server";
	const maintenanceTitle = isMaintenanceUpdated ? "✏️ Maintenance" : "Maintenance";
	const saveButtonLabel = isSaving ? "Saving..." : !isUpdated && isSaved ? "Saved ✅" : !isUpdated && !isSaved ? "There are no changes to save" : isSabnzbdUpdated && !isSabnzbdSettingsValid(newConfig) ? "Invalid SABnzbd settings" : isWebdavUpdated && !isWebdavSettingsValid(newConfig) ? "Invalid WebDAV settings" : isArrsUpdated && !isArrsSettingsValid(newConfig) ? "Invalid Arrs settings" : "Save";
	const saveButtonVariant = saveButtonLabel === "Save" ? "primary" : saveButtonLabel === "Saved ✅" ? "success" : "secondary";
	const isSaveButtonDisabled = saveButtonLabel !== "Save";
	const onClear = useCallback(() => {
		setNewConfig(config);
		setIsSaved(false);
	}, [config, setNewConfig]);
	const onSave = useCallback(async () => {
		setIsSaving(true);
		setIsSaved(false);
		if ((await fetch("/settings/update", {
			method: "POST",
			body: (() => {
				const form = new FormData();
				const changedConfig = getChangedConfig(config, newConfig);
				form.append("config", JSON.stringify(changedConfig));
				return form;
			})()
		})).ok) setConfig(newConfig);
		setIsSaving(false);
		setIsSaved(true);
	}, [
		config,
		newConfig,
		setIsSaving,
		setIsSaved,
		setConfig
	]);
	return /* @__PURE__ */ jsxs("div", {
		className: route_module_default$4.container,
		children: [
			/* @__PURE__ */ jsxs(Tabs, {
				activeKey: activeTab,
				onSelect: (x) => setActiveTab(x),
				className: route_module_default$4.tabs,
				children: [
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "usenet",
						title: usenetTitle,
						children: /* @__PURE__ */ jsx(UsenetSettings, {
							config: newConfig,
							setNewConfig
						})
					}),
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "sabnzbd",
						title: sabnzbdTitle,
						children: /* @__PURE__ */ jsx(SabnzbdSettings, {
							config: newConfig,
							setNewConfig,
							appVersion: props.appVersion
						})
					}),
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "webdav",
						title: webdavTitle,
						children: /* @__PURE__ */ jsx(WebdavSettings, {
							config: newConfig,
							setNewConfig
						})
					}),
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "arrs",
						title: arrsTitle,
						children: /* @__PURE__ */ jsx(ArrsSettings, {
							config: newConfig,
							setNewConfig
						})
					}),
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "repairs",
						title: repairsTitle,
						children: /* @__PURE__ */ jsx(RepairsSettings, {
							config: newConfig,
							setNewConfig
						})
					}),
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "rclone",
						title: rcloneTitle,
						children: /* @__PURE__ */ jsx(RcloneSettings, {
							config: newConfig,
							setNewConfig
						})
					}),
					/* @__PURE__ */ jsx(Tab, {
						eventKey: "maintenance",
						title: maintenanceTitle,
						children: /* @__PURE__ */ jsx(Maintenance, {
							savedConfig: config,
							config: newConfig,
							setNewConfig
						})
					})
				]
			}),
			/* @__PURE__ */ jsx("hr", {}),
			isUpdated && /* @__PURE__ */ jsx(Button, {
				className: route_module_default$4.button,
				variant: "secondary",
				disabled: !isUpdated,
				onClick: () => onClear(),
				children: "Clear"
			}),
			/* @__PURE__ */ jsx(Button, {
				className: route_module_default$4.button,
				variant: saveButtonVariant,
				disabled: isSaveButtonDisabled,
				onClick: onSave,
				children: saveButtonLabel
			}),
			/* @__PURE__ */ jsx(ConfirmModal, {
				show: navigationBlocker.showConfirmation,
				title: "Unsaved Changes",
				message: /* @__PURE__ */ jsxs(Fragment, { children: [
					"You have unsaved changes.",
					/* @__PURE__ */ jsx("br", {}),
					"Are you sure you want to leave this page?"
				] }),
				cancelText: "Stay",
				confirmText: "Leave",
				onCancel: navigationBlocker.onCancelNavigation,
				onConfirm: navigationBlocker.onConfirmNavigation
			})
		]
	});
}
function getChangedConfig(config, newConfig) {
	let changedConfig = {};
	let configKeys = Object.keys(defaultConfig);
	for (const configKey of configKeys) if (config[configKey] !== newConfig[configKey]) changedConfig[configKey] = newConfig[configKey];
	return changedConfig;
}
function useNavigationBlocker(isConfigUpdated) {
	const blocker = useBlocker(isConfigUpdated);
	const onConfirmNavigation = useCallback(() => {
		if (blocker.state === "blocked") blocker.proceed();
	}, [blocker]);
	const onCancelNavigation = useCallback(() => {
		if (blocker.state === "blocked") blocker.reset();
	}, [blocker]);
	return {
		showConfirmation: blocker.state === "blocked",
		onConfirmNavigation,
		onCancelNavigation
	};
}
//#endregion
//#region app/routes/settings.update/route.tsx
var route_exports$6 = /* @__PURE__ */ __exportAll({ action: () => action$2 });
async function action$2({ request }) {
	const configJson = (await request.formData()).get("config").toString();
	const config = JSON.parse(configJson);
	const configItems = [];
	for (const [key, value] of Object.entries(config)) configItems.push({
		configName: key,
		configValue: value
	});
	await backendClient.updateConfig(configItems);
	return { config };
}
var breadcrumbs_module_default = {
	container: "_container_137fq_1",
	home: "_home_137fq_11",
	"home-icon": "_home-icon_137fq_17",
	separator: "_separator_137fq_28",
	directory: "_directory_137fq_38"
};
//#endregion
//#region app/routes/explore/breadcrumbs/breadcrumbs.tsx
function Breadcrumbs({ parentDirectories }) {
	const navigate = useNavigate();
	const onClick = useCallback((index) => {
		if (index === -1) return navigate("/explore");
		navigate(`/explore/${parentDirectories.slice(0, index + 1).join("/")}`);
	}, [parentDirectories, navigate]);
	return /* @__PURE__ */ jsxs("div", {
		className: breadcrumbs_module_default.container,
		children: [/* @__PURE__ */ jsxs("div", {
			...className([breadcrumbs_module_default.home, breadcrumbs_module_default.directory]),
			onClick: () => onClick(-1),
			children: [/* @__PURE__ */ jsx("div", { className: breadcrumbs_module_default["home-icon"] }), parentDirectories.length == 0 && /* @__PURE__ */ jsx("div", { children: "home" })]
		}), parentDirectories.map((parentDirectory, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsx("div", { className: breadcrumbs_module_default.separator }), /* @__PURE__ */ jsx("div", {
			className: breadcrumbs_module_default.directory,
			onClick: () => onClick(index),
			children: parentDirectory
		})] }, index))]
	});
}
var route_module_default$3 = {
	container: "_container_1ipbz_1",
	item: "_item_1ipbz_12",
	"item-content": "_item-content_1ipbz_44",
	"item-menu": "_item-menu_1ipbz_65",
	"item-info": "_item-info_1ipbz_71",
	"item-name": "_item-name_1ipbz_79",
	"item-size": "_item-size_1ipbz_84",
	loading: "_loading_1ipbz_90",
	hidden: "_hidden_1ipbz_97",
	"open-item-menu": "_open-item-menu_1ipbz_119",
	"directory-icon": "_directory-icon_1ipbz_135",
	"file-icon": "_file-icon_1ipbz_145",
	"video-icon": "_video-icon_1ipbz_155",
	"image-icon": "_image-icon_1ipbz_165"
};
//#endregion
//#region app/auth/downloads.server.ts
function getDownloadKey(path) {
	var input = `${path}_${process.env.FRONTEND_BACKEND_API_KEY}`;
	return createHash("sha256").update(input).digest("hex");
}
//#endregion
//#region app/utils/file-size.ts
function formatFileSize(bytes) {
	var suffix = "B";
	if (bytes === null || bytes === void 0) return "unknown size";
	if (bytes >= 1024) {
		bytes /= 1024;
		suffix = "KB";
	}
	if (bytes >= 1024) {
		bytes /= 1024;
		suffix = "MB";
	}
	if (bytes >= 1024) {
		bytes /= 1024;
		suffix = "GB";
	}
	if (bytes >= 1024) {
		bytes /= 1024;
		suffix = "TB";
	}
	if (bytes >= 1024) {
		bytes /= 1024;
		suffix = "PB";
	}
	return `${parseFloat(bytes.toFixed(2))} ${suffix}`;
}
var dropdown_options_module_default = {
	dropdown: "_dropdown_bxheg_1",
	option: "_option_bxheg_15",
	optionButton: "_optionButton_bxheg_24",
	danger: "_danger_bxheg_45"
};
//#endregion
//#region app/routes/explore/dropdown-options/dropdown-options.tsx
function DropdownOptions({ className, style, isOpen = true, options, onClose }) {
	const ref = useRef(null);
	useEffect(() => {
		if (!isOpen) return;
		function handleClick(e) {
			if (ref.current && !ref.current.contains(e.target)) {
				e.preventDefault();
				onClose?.();
			}
		}
		document.addEventListener("click", handleClick);
		return () => document.removeEventListener("click", handleClick);
	}, [isOpen, onClose]);
	return !isOpen ? null : /* @__PURE__ */ jsx("ul", {
		ref,
		className: classNames([dropdown_options_module_default.dropdown, className]),
		style,
		children: options.filter((x) => !!x).map((option, index) => /* @__PURE__ */ jsxs("li", {
			className: dropdown_options_module_default.option,
			children: [option.linkTo && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("a", {
				href: option.linkTo,
				children: /* @__PURE__ */ jsx("button", {
					type: "button",
					className: classNames([dropdown_options_module_default.optionButton, option.variant && dropdown_options_module_default[option.variant]]),
					onClick: () => option.onSelect?.(),
					children: option.option
				})
			}) }), !option.linkTo && /* @__PURE__ */ jsx("button", {
				type: "button",
				className: classNames([dropdown_options_module_default.optionButton, option.variant && dropdown_options_module_default[option.variant]]),
				onClick: () => option.onSelect?.(),
				children: option.option
			})]
		}, index))
	});
}
//#endregion
//#region app/routes/explore/item-menu/item-menu.tsx
function ItemMenu({ className, openClassName, exploreFile, previewPath }) {
	const [isOpen, setIsOpen] = useState(false);
	const exportNzbUrl = `/api/download-nzb?nzbBlobId=${exploreFile.nzbBlobId}`;
	const downloadUrl = `${previewPath}&download=true`;
	const onClick = useCallback((e) => {
		e.stopPropagation();
		setIsOpen((x) => !x);
	}, []);
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
		className: classNames([className, isOpen && openClassName]),
		onClick,
		children: "⋯"
	}), /* @__PURE__ */ jsx(DropdownOptions, {
		isOpen,
		onClose: () => setIsOpen(false),
		options: [
			{
				option: /* @__PURE__ */ jsx(Preview, {}),
				linkTo: previewPath
			},
			{
				option: /* @__PURE__ */ jsx(Download, {}),
				linkTo: downloadUrl
			},
			!!exploreFile.nzbBlobId ? {
				option: /* @__PURE__ */ jsx(ExportNzb, {}),
				linkTo: exportNzbUrl
			} : void 0
		]
	})] });
}
function Preview() {
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(EyeIcon, {}), " Preview"] });
}
function Download() {
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DownloadIcon, {}), " Download"] });
}
function ExportNzb() {
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ExportIcon, {}), " Export NZB"] });
}
function Remove() {
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TrashIcon$1, {}), " Remove"] });
}
function EyeIcon() {
	return /* @__PURE__ */ jsxs("svg", {
		xmlns: "http://www.w3.org/2000/svg",
		width: "16",
		height: "16",
		viewBox: "0 0 16 16",
		fill: "currentColor",
		style: {
			marginRight: "8px",
			verticalAlign: "-2px"
		},
		children: [/* @__PURE__ */ jsx("path", { d: "M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z" }), /* @__PURE__ */ jsx("path", { d: "M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z" })]
	});
}
function DownloadIcon() {
	return /* @__PURE__ */ jsxs("svg", {
		xmlns: "http://www.w3.org/2000/svg",
		width: "16",
		height: "16",
		viewBox: "0 0 16 16",
		fill: "currentColor",
		style: {
			marginRight: "8px",
			verticalAlign: "-2px"
		},
		children: [/* @__PURE__ */ jsx("path", { d: "M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" }), /* @__PURE__ */ jsx("path", { d: "M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" })]
	});
}
function ExportIcon() {
	return /* @__PURE__ */ jsxs("svg", {
		xmlns: "http://www.w3.org/2000/svg",
		width: "16",
		height: "16",
		viewBox: "0 0 16 16",
		fill: "currentColor",
		style: {
			marginRight: "8px",
			verticalAlign: "-2px"
		},
		children: [/* @__PURE__ */ jsx("path", { d: "M3.5 6a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 1 0-1h2A1.5 1.5 0 0 1 14 6.5v8a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 14.5v-8A1.5 1.5 0 0 1 3.5 5h2a.5.5 0 0 1 0 1h-2z" }), /* @__PURE__ */ jsx("path", { d: "M7.646.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 1.707V10.5a.5.5 0 0 1-1 0V1.707L5.354 3.854a.5.5 0 1 1-.708-.708l3-3z" })]
	});
}
function TrashIcon$1() {
	return /* @__PURE__ */ jsxs("svg", {
		xmlns: "http://www.w3.org/2000/svg",
		width: "16",
		height: "16",
		viewBox: "0 0 16 16",
		fill: "currentColor",
		style: {
			marginRight: "8px",
			verticalAlign: "-2px"
		},
		children: [/* @__PURE__ */ jsx("path", { d: "M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" }), /* @__PURE__ */ jsx("path", { d: "M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" })]
	});
}
//#endregion
//#region app/routes/explore/route.tsx
var route_exports$5 = /* @__PURE__ */ __exportAll({
	default: () => route_default$3,
	loader: () => loader$4
});
async function loader$4({ request }) {
	if (request.url.endsWith("/")) return redirect(request.url.slice(0, -1));
	let path = getWebdavPathDecoded(new URL(request.url).pathname);
	return {
		parentDirectories: getParentDirectories(path),
		items: (await backendClient.listWebdavDirectory(path)).map((x) => {
			if (x.isDirectory) return x;
			return {
				...x,
				mimeType: lookup(x.name),
				downloadKey: getDownloadKey(getRelativePath(path, x.name))
			};
		})
	};
}
var route_default$3 = UNSAFE_withComponentProps(function Explore({ loaderData }) {
	return /* @__PURE__ */ jsx(Body, { ...loaderData });
});
function Body(props) {
	const location = useLocation();
	const navigation = useNavigation();
	const isNavigating = Boolean(navigation.location);
	const items = props.items;
	const parentDirectories = isNavigating ? getParentDirectories(getWebdavPathDecoded(navigation.location.pathname)) : props.parentDirectories;
	const getDirectoryPath = useCallback((directoryName) => {
		return `${location.pathname}/${encodeURIComponent(directoryName)}`;
	}, [location.pathname]);
	const getFilePath = useCallback((file) => {
		const relativePath = getRelativePath(getWebdavPath(location.pathname), encodeURIComponent(file.name));
		const extension = getExtension(file.name);
		const extensionQueryParam = extension ? `&extension=${extension}` : "";
		return `/view/${relativePath}?downloadKey=${file.downloadKey}${extensionQueryParam}`;
	}, [location.pathname]);
	return /* @__PURE__ */ jsxs("div", {
		className: route_module_default$3.container,
		children: [
			/* @__PURE__ */ jsx(Breadcrumbs, { parentDirectories }),
			!isNavigating && /* @__PURE__ */ jsxs("div", { children: [items.filter((x) => x.isDirectory).map((x, index) => /* @__PURE__ */ jsx("div", {
				className: getClassName(x),
				children: /* @__PURE__ */ jsx(Link, {
					to: getDirectoryPath(x.name),
					children: /* @__PURE__ */ jsxs("div", {
						className: route_module_default$3["item-content"],
						children: [/* @__PURE__ */ jsx("div", { className: route_module_default$3["directory-icon"] }), /* @__PURE__ */ jsx("div", {
							className: route_module_default$3["item-name"],
							children: x.name
						})]
					})
				})
			}, `${index}_dir_item`)), items.filter((x) => !x.isDirectory).map((x, index) => /* @__PURE__ */ jsxs("div", {
				className: getClassName(x),
				children: [/* @__PURE__ */ jsxs("a", {
					href: getFilePath(x),
					className: route_module_default$3["item-content"],
					children: [/* @__PURE__ */ jsx("div", { className: getIcon(x) }), /* @__PURE__ */ jsxs("div", {
						className: route_module_default$3["item-info"],
						children: [/* @__PURE__ */ jsx("div", {
							className: route_module_default$3["item-name"],
							children: x.name
						}), /* @__PURE__ */ jsx("div", {
							className: route_module_default$3["item-size"],
							children: formatFileSize(x.size)
						})]
					})]
				}), /* @__PURE__ */ jsx(ItemMenu, {
					className: route_module_default$3["item-menu"],
					openClassName: route_module_default$3["open-item-menu"],
					exploreFile: x,
					previewPath: getFilePath(x)
				})]
			}, `${index}_file_item`))] }),
			isNavigating && /* @__PURE__ */ jsx(Loading, { className: route_module_default$3.loading })
		]
	});
}
function getExtension(filename) {
	const lastDotIndex = filename.lastIndexOf(".");
	if (lastDotIndex === -1 || lastDotIndex === 0) return void 0;
	return filename.slice(lastDotIndex);
}
function getIcon(file) {
	if (file.name.toLowerCase().endsWith(".mkv")) return route_module_default$3["video-icon"];
	if (file.mimeType === "application/mp4") return route_module_default$3["video-icon"];
	if (file.mimeType && file.mimeType.startsWith("video")) return route_module_default$3["video-icon"];
	if (file.mimeType && file.mimeType.startsWith("image")) return route_module_default$3["image-icon"];
	return route_module_default$3["file-icon"];
}
function getWebdavPath(pathname) {
	if (pathname.startsWith("/")) pathname = pathname.slice(1);
	if (pathname.startsWith("explore")) pathname = pathname.slice(7);
	if (pathname.startsWith("/")) pathname = pathname.slice(1);
	return pathname;
}
function getWebdavPathDecoded(pathname) {
	return decodeURIComponent(getWebdavPath(pathname));
}
function getRelativePath(path, filename) {
	if (path === "") return filename;
	return `${path}/${filename}`;
}
function getParentDirectories(webdavPath) {
	return webdavPath == "" ? [] : webdavPath.split("/");
}
function getClassName(item) {
	let className = route_module_default$3.item;
	if (item.name.startsWith(".")) className += " " + route_module_default$3.hidden;
	return className;
}
//#endregion
//#region app/routes/_index/route.tsx
var route_exports$4 = /* @__PURE__ */ __exportAll({ loader: () => loader$3 });
async function loader$3({ request }) {
	return redirect("/queue");
}
var route_module_default$2 = {
	container: "_container_10cc5_1",
	alert: "_alert_10cc5_9",
	list: "_list_10cc5_12",
	listItem: "_listItem_10cc5_16",
	section: "_section_10cc5_21"
};
var health_table_module_default = {
	container: "_container_197ti_1",
	header: "_header_197ti_11",
	title: "_title_197ti_20",
	count: "_count_197ti_24",
	tableContainer: "_tableContainer_197ti_34",
	table: "_table_197ti_34",
	nameContainer: "_nameContainer_197ti_84",
	name: "_name_197ti_84",
	path: "_path_197ti_98",
	dateCell: "_dateCell_197ti_106",
	emptyState: "_emptyState_197ti_112",
	emptyIcon: "_emptyIcon_197ti_118",
	emptyTitle: "_emptyTitle_197ti_124",
	emptyDescription: "_emptyDescription_197ti_131",
	dateDetailsTable: "_dateDetailsTable_197ti_138",
	dateDetailsRow: "_dateDetailsRow_197ti_146",
	dateDetailsLabel: "_dateDetailsLabel_197ti_163",
	dateDetailsValue: "_dateDetailsValue_197ti_172",
	mobile: "_mobile_197ti_181",
	dateBadge: "_dateBadge_197ti_185",
	desktop: "_desktop_197ti_193"
};
var truncate_module_default = { truncate: "_truncate_3f6j8_1" };
//#endregion
//#region app/routes/queue/components/truncate/truncate.tsx
function Truncate({ children }) {
	return /* @__PURE__ */ jsx("div", {
		className: truncate_module_default.truncate,
		children
	});
}
var status_badge_module_default = {
	container: "_container_phj37_1",
	badge: "_badge_phj37_5",
	badgeText: "_badgeText_phj37_21",
	progress: "_progress_phj37_25",
	gray: "_gray_phj37_35",
	healthcheckProgress: "_healthcheckProgress_phj37_40",
	uploadProgress: "_uploadProgress_phj37_44",
	uploadIcon: "_uploadIcon_phj37_54",
	failureBadge: "_failureBadge_phj37_70"
};
//#endregion
//#region app/routes/queue/components/status-badge/status-badge.tsx
function StatusBadge({ className, status, percentage, error }) {
	const statusLower = status?.toLowerCase();
	if (statusLower === "completed") return /* @__PURE__ */ jsx("div", {
		className: status_badge_module_default.container,
		children: /* @__PURE__ */ jsx("div", {
			className: status_badge_module_default.badge,
			style: { backgroundColor: "rgba(var(--bs-success-rgb)" },
			children: /* @__PURE__ */ jsx("div", {
				className: status_badge_module_default.badgeText,
				children: statusLower
			})
		})
	});
	if (statusLower === "failed" || statusLower == "upload failed") {
		const badgeTextClass = statusLower == "upload failed" ? classNames([status_badge_module_default.badgeText, status_badge_module_default.uploadIcon]) : status_badge_module_default.badgeText;
		if (error?.startsWith("Article with message-id")) error = "Missing articles";
		return /* @__PURE__ */ jsx(OverlayTrigger, {
			placement: "top",
			overlay: /* @__PURE__ */ jsx(Tooltip, { children: error }),
			trigger: "click",
			children: /* @__PURE__ */ jsx("div", {
				className: classNames([status_badge_module_default.container, status_badge_module_default.failureBadge]),
				children: /* @__PURE__ */ jsx("div", {
					className: status_badge_module_default.badge,
					style: { backgroundColor: "rgba(var(--bs-danger-rgb)" },
					children: /* @__PURE__ */ jsx("div", {
						className: badgeTextClass,
						children: "failed"
					})
				})
			})
		});
	}
	if (statusLower === "downloading") {
		const percentNum = Number(percentage);
		const badgeText = `${percentNum > 100 ? percentNum - 100 : percentNum}%`;
		const isHealthChecking = percentNum > 100;
		const downloadProgressClass = isHealthChecking ? `${status_badge_module_default.progress} ${status_badge_module_default.gray}` : status_badge_module_default.progress;
		const downloadProgressStyle = percentNum >= 0 ? { width: `${Math.min(percentNum, 100)}%` } : void 0;
		const healthCheckProgressClass = `${status_badge_module_default.progress} ${status_badge_module_default.healthcheckProgress}`;
		const healthCheckProgressStyle = isHealthChecking ? { width: `${Math.min(percentNum - 100, 100)}%` } : void 0;
		return /* @__PURE__ */ jsx("div", {
			className: status_badge_module_default.container,
			children: /* @__PURE__ */ jsxs("div", {
				className: status_badge_module_default.badge,
				style: { backgroundColor: "#333" },
				children: [
					/* @__PURE__ */ jsx("div", {
						className: downloadProgressClass,
						style: downloadProgressStyle
					}),
					/* @__PURE__ */ jsx("div", {
						className: healthCheckProgressClass,
						style: healthCheckProgressStyle
					}),
					/* @__PURE__ */ jsx("div", {
						className: status_badge_module_default.badgeText,
						children: badgeText
					})
				]
			})
		});
	}
	if (statusLower === "uploading") {
		const percentNum = Number(percentage);
		const badgeText = `${percentNum}%`;
		const uploadProgressClass = `${status_badge_module_default.progress} ${status_badge_module_default.uploadProgress}`;
		const uploadProgressStyle = { width: `${Math.min(percentNum, 100)}%` };
		return /* @__PURE__ */ jsx("div", {
			className: status_badge_module_default.container,
			children: /* @__PURE__ */ jsxs("div", {
				className: status_badge_module_default.badge,
				style: { backgroundColor: "#333" },
				children: [/* @__PURE__ */ jsx("div", {
					className: uploadProgressClass,
					style: uploadProgressStyle
				}), /* @__PURE__ */ jsx("div", {
					className: classNames([status_badge_module_default.badgeText, status_badge_module_default.uploadIcon]),
					children: badgeText
				})]
			})
		});
	}
	if (statusLower === "pending") return /* @__PURE__ */ jsx("div", {
		className: status_badge_module_default.container,
		children: /* @__PURE__ */ jsx("div", {
			className: status_badge_module_default.badge,
			style: { backgroundColor: "#333" },
			children: /* @__PURE__ */ jsx("div", {
				className: classNames([status_badge_module_default.badgeText, status_badge_module_default.uploadIcon]),
				children: "pending"
			})
		})
	});
	if (statusLower === "health-checking") {
		const percentNum = Number(percentage);
		const badgeText = `${percentNum}%`;
		const healthCheckProgressClass = `${status_badge_module_default.progress} ${status_badge_module_default.healthcheckProgress}`;
		const healthCheckProgressStyle = { width: `${Math.min(percentNum, 100)}%` };
		return /* @__PURE__ */ jsxs("div", {
			className: classNames([status_badge_module_default.badge, className]),
			style: { backgroundColor: "#333" },
			children: [/* @__PURE__ */ jsx("div", {
				className: healthCheckProgressClass,
				style: healthCheckProgressStyle
			}), /* @__PURE__ */ jsx("div", {
				className: status_badge_module_default.badgeText,
				children: badgeText
			})]
		});
	}
	return /* @__PURE__ */ jsx("div", {
		className: status_badge_module_default.container,
		children: /* @__PURE__ */ jsx("div", {
			className: status_badge_module_default.badge,
			style: { backgroundColor: "grey" },
			children: /* @__PURE__ */ jsx("div", {
				className: status_badge_module_default.badgeText,
				children: statusLower
			})
		})
	});
}
//#endregion
//#region app/routes/health/components/health-table/health-table.tsx
function HealthTable({ isEnabled, healthCheckItems }) {
	return /* @__PURE__ */ jsxs("div", {
		className: health_table_module_default.container,
		children: [/* @__PURE__ */ jsxs("div", {
			className: health_table_module_default.header,
			children: [/* @__PURE__ */ jsx("h3", {
				className: health_table_module_default.title,
				children: "Schedule"
			}), /* @__PURE__ */ jsxs("div", {
				className: health_table_module_default.count,
				children: [
					"Only ",
					healthCheckItems.length,
					" shown"
				]
			})]
		}), !isEnabled ? /* @__PURE__ */ jsxs("div", {
			className: health_table_module_default.emptyState,
			children: [
				/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.emptyIcon,
					children: "🩺💙💪"
				}),
				/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.emptyTitle,
					children: "Enable Repairs In Settings"
				}),
				/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.emptyDescription,
					children: "Once you enable repairs, all mounted usenet files will be queued for continuous health monitoring"
				})
			]
		}) : healthCheckItems.length === 0 ? /* @__PURE__ */ jsxs("div", {
			className: health_table_module_default.emptyState,
			children: [
				/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.emptyIcon,
					children: "🩺💙💪"
				}),
				/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.emptyTitle,
					children: "No Items To Health-Check"
				}),
				/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.emptyDescription,
					children: "Once you begin processing nzbs, the mounted usenet files will be queued for continuous health monitoring"
				})
			]
		}) : /* @__PURE__ */ jsx("div", {
			className: health_table_module_default.tableContainer,
			children: /* @__PURE__ */ jsxs(Table, {
				className: health_table_module_default.table,
				responsive: true,
				children: [/* @__PURE__ */ jsx("thead", {
					className: health_table_module_default.desktop,
					children: /* @__PURE__ */ jsxs("tr", { children: [
						/* @__PURE__ */ jsx("th", { children: "Name" }),
						/* @__PURE__ */ jsx("th", {
							className: health_table_module_default.desktop,
							children: "Created"
						}),
						/* @__PURE__ */ jsx("th", {
							className: health_table_module_default.desktop,
							children: "Last Check"
						}),
						/* @__PURE__ */ jsx("th", {
							className: health_table_module_default.desktop,
							children: "Next Check"
						})
					] })
				}), /* @__PURE__ */ jsx("tbody", { children: healthCheckItems.map((item) => /* @__PURE__ */ jsxs("tr", {
					className: health_table_module_default.tableRow,
					children: [
						/* @__PURE__ */ jsx("td", {
							className: health_table_module_default.nameCell,
							children: /* @__PURE__ */ jsxs("div", {
								className: health_table_module_default.nameContainer,
								children: [
									/* @__PURE__ */ jsx("div", {
										className: health_table_module_default.name,
										children: /* @__PURE__ */ jsx(Truncate, { children: item.name })
									}),
									/* @__PURE__ */ jsx("div", {
										className: health_table_module_default.path,
										children: /* @__PURE__ */ jsx(Truncate, { children: item.path })
									}),
									/* @__PURE__ */ jsx("div", {
										className: health_table_module_default.mobile,
										children: /* @__PURE__ */ jsx(DateDetailsTable, { item })
									})
								]
							})
						}),
						/* @__PURE__ */ jsx("td", {
							className: `${health_table_module_default.dateCell} ${health_table_module_default.desktop}`,
							children: formatDateBadge(item.releaseDate, "Unknown", "info")
						}),
						/* @__PURE__ */ jsx("td", {
							className: `${health_table_module_default.dateCell} ${health_table_module_default.desktop}`,
							children: formatDateBadge(item.lastHealthCheck, "Never", "warning")
						}),
						/* @__PURE__ */ jsx("td", {
							className: `${health_table_module_default.dateCell} ${health_table_module_default.desktop}`,
							children: item.progress > 0 ? /* @__PURE__ */ jsx(StatusBadge, {
								status: "health-checking",
								className: health_table_module_default.dateBadge,
								percentage: item.progress.toString()
							}) : formatDateBadge(item.nextHealthCheck, "ASAP", "success")
						})
					]
				}, item.id)) })]
			})
		})]
	});
}
function DateDetailsTable({ item }) {
	return /* @__PURE__ */ jsxs("div", {
		className: health_table_module_default.dateDetailsTable,
		children: [
			/* @__PURE__ */ jsxs("div", {
				className: health_table_module_default.dateDetailsRow,
				children: [/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.dateDetailsLabel,
					children: "Created"
				}), /* @__PURE__ */ jsx("div", {
					className: health_table_module_default.dateDetailsValue,
					children: formatDateBadge(item.releaseDate, "Unknown", "info")
				})]
			}),
			/* @__PURE__ */ jsxs("div", {
				className: health_table_module_default.dateDetailsRow,
				children: [/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.dateDetailsLabel,
					children: "Last Health Check"
				}), /* @__PURE__ */ jsx("div", {
					className: health_table_module_default.dateDetailsValue,
					children: formatDateBadge(item.lastHealthCheck, "Never", "warning")
				})]
			}),
			/* @__PURE__ */ jsxs("div", {
				className: health_table_module_default.dateDetailsRow,
				children: [/* @__PURE__ */ jsx("div", {
					className: health_table_module_default.dateDetailsLabel,
					children: "Next Health Check"
				}), /* @__PURE__ */ jsx("div", {
					className: health_table_module_default.dateDetailsValue,
					children: item.progress > 0 ? /* @__PURE__ */ jsx(StatusBadge, {
						status: "health-checking",
						className: health_table_module_default.dateBadge,
						percentage: item.progress.toString()
					}) : formatDateBadge(item.nextHealthCheck, "ASAP", "success")
				})]
			})
		]
	});
}
function formatDate(dateString, fallback) {
	try {
		if (!dateString) return fallback;
		const now = /* @__PURE__ */ new Date();
		const datetime = new Date(dateString);
		return isSameDate(datetime, now) ? datetime.toLocaleTimeString([], {
			hour: "numeric",
			minute: "2-digit"
		}) : datetime.toLocaleDateString();
	} catch {
		return "Unknown";
	}
}
function formatDateBadge(dateString, fallback, variant) {
	const dateText = formatDate(dateString, fallback);
	return /* @__PURE__ */ jsx(Badge, {
		bg: variant,
		className: health_table_module_default.dateBadge,
		children: dateText
	});
}
function isSameDate(one, two) {
	return one.getFullYear() === two.getFullYear() && one.getMonth() === two.getMonth() && one.getDate() === two.getDate();
}
var health_stats_module_default = {
	container: "_container_xjp6i_1",
	header: "_header_xjp6i_9",
	title: "_title_xjp6i_18",
	statusIndicator: "_statusIndicator_xjp6i_22",
	statusLabel: "_statusLabel_xjp6i_35",
	statsGrid: "_statsGrid_xjp6i_41",
	statCard: "_statCard_xjp6i_48",
	statNumber: "_statNumber_xjp6i_55",
	statLabel: "_statLabel_xjp6i_63"
};
//#endregion
//#region app/routes/health/components/health-stats/health-stats.tsx
var HealthResult = /* @__PURE__ */ function(HealthResult) {
	HealthResult[HealthResult["Healthy"] = 0] = "Healthy";
	HealthResult[HealthResult["Unhealthy"] = 1] = "Unhealthy";
	return HealthResult;
}(HealthResult || {});
var RepairAction = /* @__PURE__ */ function(RepairAction) {
	RepairAction[RepairAction["None"] = 0] = "None";
	RepairAction[RepairAction["Repaired"] = 1] = "Repaired";
	RepairAction[RepairAction["Deleted"] = 2] = "Deleted";
	RepairAction[RepairAction["ActionNeeded"] = 3] = "ActionNeeded";
	return RepairAction;
}(RepairAction || {});
function HealthStats({ stats }) {
	const totalChecked = stats.reduce((sum, stat) => sum + stat.count, 0);
	const healthy = stats.filter((stat) => stat.result === HealthResult.Healthy).reduce((sum, stat) => sum + stat.count, 0);
	const repaired = stats.filter((stat) => stat.repairStatus === RepairAction.Repaired).reduce((sum, stat) => sum + stat.count, 0);
	const deleted = stats.filter((stat) => stat.repairStatus === RepairAction.Deleted).reduce((sum, stat) => sum + stat.count, 0);
	const getPercentage = (count) => {
		return totalChecked > 0 ? Math.round(count / totalChecked * 100) : 0;
	};
	return /* @__PURE__ */ jsxs("div", {
		className: health_stats_module_default.container,
		children: [/* @__PURE__ */ jsxs("div", {
			className: health_stats_module_default.header,
			children: [/* @__PURE__ */ jsx("h3", {
				className: health_stats_module_default.title,
				children: "Overview"
			}), /* @__PURE__ */ jsx("div", {
				className: health_stats_module_default.statusIndicator,
				children: /* @__PURE__ */ jsx("span", {
					className: health_stats_module_default.statusLabel,
					children: "Last 30 Days"
				})
			})]
		}), /* @__PURE__ */ jsxs("div", {
			className: health_stats_module_default.statsGrid,
			children: [
				/* @__PURE__ */ jsxs("div", {
					className: health_stats_module_default.statCard,
					children: [/* @__PURE__ */ jsx("div", {
						className: health_stats_module_default.statNumber,
						children: totalChecked
					}), /* @__PURE__ */ jsx("div", {
						className: health_stats_module_default.statLabel,
						children: "Total Checked"
					})]
				}),
				/* @__PURE__ */ jsxs("div", {
					className: health_stats_module_default.statCard,
					children: [/* @__PURE__ */ jsx("div", {
						className: health_stats_module_default.statNumber,
						style: { color: "#198754" },
						children: healthy
					}), /* @__PURE__ */ jsxs("div", {
						className: health_stats_module_default.statLabel,
						children: [
							"Healthy (",
							getPercentage(healthy),
							"%)"
						]
					})]
				}),
				/* @__PURE__ */ jsxs("div", {
					className: health_stats_module_default.statCard,
					children: [/* @__PURE__ */ jsx("div", {
						className: health_stats_module_default.statNumber,
						style: { color: "#17a2b8" },
						children: repaired
					}), /* @__PURE__ */ jsxs("div", {
						className: health_stats_module_default.statLabel,
						children: [
							"Repaired (",
							getPercentage(repaired),
							"%)"
						]
					})]
				}),
				/* @__PURE__ */ jsxs("div", {
					className: health_stats_module_default.statCard,
					children: [/* @__PURE__ */ jsx("div", {
						className: health_stats_module_default.statNumber,
						style: { color: "#dc3545" },
						children: deleted
					}), /* @__PURE__ */ jsxs("div", {
						className: health_stats_module_default.statLabel,
						children: [
							"Deleted (",
							getPercentage(deleted),
							"%)"
						]
					})]
				})
			]
		})]
	});
}
//#endregion
//#region app/routes/health/route.tsx
var route_exports$3 = /* @__PURE__ */ __exportAll({
	default: () => route_default$2,
	loader: () => loader$2
});
var topicNames$1 = {
	healthItemStatus: "hs",
	healthItemProgress: "hp"
};
var topicSubscriptions$1 = {
	[topicNames$1.healthItemStatus]: "event",
	[topicNames$1.healthItemProgress]: "event"
};
async function loader$2() {
	const enabledKey = "repair.enable";
	const [queueData, historyData, config] = await Promise.all([
		backendClient.getHealthCheckQueue(30),
		backendClient.getHealthCheckHistory(),
		backendClient.getConfig([enabledKey])
	]);
	return {
		uncheckedCount: queueData.uncheckedCount,
		queueItems: queueData.items,
		historyStats: historyData.stats,
		historyItems: historyData.items,
		isEnabled: config.filter((x) => x.configName === enabledKey).filter((x) => x.configValue.toLowerCase() === "true").length > 0
	};
}
var route_default$2 = UNSAFE_withComponentProps(function Health({ loaderData }) {
	const { isEnabled } = loaderData;
	const [historyStats, setHistoryStats] = useState(loaderData.historyStats);
	const [queueItems, setQueueItems] = useState(loaderData.queueItems);
	const [uncheckedCount, setUncheckedCount] = useState(loaderData.uncheckedCount);
	useEffect(() => {
		if (queueItems.length >= 15) return;
		const refetchData = async () => {
			var response = await fetch("/api/get-health-check-queue?pageSize=30");
			if (response.ok) {
				const healthCheckQueue = await response.json();
				setQueueItems(healthCheckQueue.items);
				setUncheckedCount(healthCheckQueue.uncheckedCount);
			}
		};
		refetchData();
	}, [queueItems, setQueueItems]);
	const onHealthItemStatus = useCallback(async (message) => {
		const [davItemId, healthResult, repairAction] = message.split("|");
		setQueueItems((x) => x.filter((item) => item.id !== davItemId));
		setUncheckedCount((x) => x - 1);
		setHistoryStats((x) => {
			const healthResultNum = Number(healthResult);
			const repairActionNum = Number(repairAction);
			let updated = false;
			const newStats = x.map((stat) => {
				if (stat.result === healthResultNum && stat.repairStatus === repairActionNum) {
					updated = true;
					return {
						...stat,
						count: stat.count + 1
					};
				}
				return stat;
			});
			if (!updated) return [...x, {
				result: healthResultNum,
				repairStatus: repairActionNum,
				count: 1
			}];
			return newStats;
		});
	}, [setQueueItems, setHistoryStats]);
	const onHealthItemProgress = useCallback((message) => {
		const [davItemId, progress] = message.split("|");
		if (progress === "done") return;
		setQueueItems((queueItems) => {
			var index = queueItems.findIndex((x) => x.id === davItemId);
			if (index === -1) return queueItems;
			return queueItems.filter((_, i) => i >= index).map((item) => item.id === davItemId ? {
				...item,
				progress: Number(progress)
			} : item);
		});
	}, [setQueueItems]);
	const onWebsocketMessage = useCallback((topic, message) => {
		if (topic == topicNames$1.healthItemStatus) onHealthItemStatus(message);
		else if (topic == topicNames$1.healthItemProgress) onHealthItemProgress(message);
	}, [onHealthItemStatus, onHealthItemProgress]);
	useEffect(() => {
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage(onWebsocketMessage);
			ws.onopen = () => {
				ws.send(JSON.stringify(topicSubscriptions$1));
			};
			ws.onclose = () => {
				!disposed && setTimeout(() => connect(), 1e3);
			};
			ws.onerror = () => {
				ws.close();
			};
			return () => {
				disposed = true;
				ws.close();
			};
		}
		return connect();
	}, [onWebsocketMessage]);
	return /* @__PURE__ */ jsxs("div", {
		className: route_module_default$2.container,
		children: [
			/* @__PURE__ */ jsx("div", {
				className: route_module_default$2.section,
				children: /* @__PURE__ */ jsx(HealthStats, { stats: historyStats })
			}),
			isEnabled && uncheckedCount > 20 && /* @__PURE__ */ jsxs(Alert, {
				className: route_module_default$2.alert,
				variant: "warning",
				children: [/* @__PURE__ */ jsx("b", { children: "Attention" }), /* @__PURE__ */ jsxs("ul", {
					className: route_module_default$2.list,
					children: [
						/* @__PURE__ */ jsxs("li", {
							className: route_module_default$2.listItem,
							children: [
								"You have ~",
								uncheckedCount,
								" files whose health has never been determined."
							]
						}),
						/* @__PURE__ */ jsx("li", {
							className: route_module_default$2.listItem,
							children: "The queue will run an initial health check of these files."
						}),
						/* @__PURE__ */ jsx("li", {
							className: route_module_default$2.listItem,
							children: "Under normal operation, health checks will occur much less frequently."
						})
					]
				})]
			}),
			/* @__PURE__ */ jsx("div", {
				className: route_module_default$2.section,
				children: /* @__PURE__ */ jsx(HealthTable, {
					isEnabled,
					healthCheckItems: queueItems.filter((_, index) => index < 10)
				})
			})
		]
	});
});
//#endregion
//#region app/routes/logout/route.tsx
var route_exports$2 = /* @__PURE__ */ __exportAll({ action: () => action$1 });
async function action$1({ request }) {
	if ((await request.formData()).get("confirm")?.toString() !== "true") return redirect("/");
	return redirect("/login", await logout(request));
}
var route_module_default$1 = {
	container: "_container_1rge5_1",
	logo: "_logo_1rge5_13",
	title: "_title_1rge5_17",
	error: "_error_1rge5_25"
};
//#endregion
//#region app/routes/login/route.tsx
var route_exports$1 = /* @__PURE__ */ __exportAll({
	action: () => action,
	default: () => route_default$1,
	loader: () => loader$1
});
async function loader$1({ request }) {
	if (await isAuthenticated(request)) return redirect("/");
	if (await backendClient.isOnboarding()) return redirect("/onboarding");
	return { loginError: null };
}
var route_default$1 = UNSAFE_withComponentProps(function Index({ loaderData, actionData }) {
	const isLoading = useNavigation().state == "submitting";
	const pageData = actionData || loaderData;
	const showError = !!pageData.loginError;
	const submitButtonDisabled = isLoading;
	const submitButtonText = isLoading ? "Logging in..." : "Login";
	return /* @__PURE__ */ jsxs(Form, {
		className: route_module_default$1["container"],
		method: "POST",
		children: [
			/* @__PURE__ */ jsx("img", {
				className: route_module_default$1["logo"],
				src: "/logo.svg"
			}),
			/* @__PURE__ */ jsx("div", {
				className: route_module_default$1["title"],
				children: "Nzb DAV"
			}),
			/* @__PURE__ */ jsx(Alert, {
				className: route_module_default$1["error"],
				show: showError,
				variant: "danger",
				children: pageData.loginError
			}),
			/* @__PURE__ */ jsx(Form$1.Control, {
				name: "username",
				type: "text",
				placeholder: "Username",
				autoFocus: true
			}),
			/* @__PURE__ */ jsx(Form$1.Control, {
				name: "password",
				type: "password",
				placeholder: "Password"
			}),
			/* @__PURE__ */ jsx(Button, {
				type: "submit",
				variant: "primary",
				disabled: submitButtonDisabled,
				children: submitButtonText
			})
		]
	});
});
async function action({ request }) {
	try {
		return redirect("/", await login(request));
	} catch (error) {
		if (error instanceof Error) return { loginError: error.message };
		throw error;
	}
}
var route_module_default = {
	container: "_container_3tj65_1",
	queueContainer: "_queueContainer_3tj65_9",
	section: "_section_3tj65_20",
	sectionTitle: "_sectionTitle_3tj65_24",
	alert: "_alert_3tj65_34",
	list: "_list_3tj65_37",
	listItem: "_listItem_3tj65_41",
	dropzone: "_dropzone_3tj65_46",
	activeDropzone: "_activeDropzone_3tj65_50"
};
var action_button_module_default = {
	"action-button": "_action-button_e41mu_1",
	disabled: "_disabled_e41mu_19",
	explore: "_explore_e41mu_28",
	"directory-icon": "_directory-icon_e41mu_31",
	menu: "_menu_e41mu_60",
	selected: "_selected_e41mu_82",
	"delete": "_delete_e41mu_88",
	"trash-icon": "_trash-icon_e41mu_91"
};
//#endregion
//#region app/routes/queue/components/action-button/action-button.tsx
function ActionButton({ type, text, disabled, selected, onClick }) {
	const classes = classNames([
		action_button_module_default["action-button"],
		action_button_module_default[type],
		selected && action_button_module_default.selected
	]);
	return /* @__PURE__ */ jsx("div", {
		className: disabled ? action_button_module_default["disabled"] : void 0,
		children: /* @__PURE__ */ jsxs("div", {
			className: classes,
			onClick,
			children: [
				type === "delete" && /* @__PURE__ */ jsx(TrashIcon, {}),
				type === "explore" && /* @__PURE__ */ jsx(DirectoryIcon, {}),
				type === "menu" && "⋯",
				text && /* @__PURE__ */ jsx("div", {
					className: action_button_module_default.text,
					children: text
				})
			]
		})
	});
}
function DirectoryIcon() {
	return /* @__PURE__ */ jsx("svg", {
		className: action_button_module_default["directory-icon"],
		xmlns: "http://www.w3.org/2000/svg",
		viewBox: "0 0 22 24",
		children: /* @__PURE__ */ jsx("path", { d: "M2 4.75C2 3.784 2.784 3 3.75 3h4.971c.58 0 1.12.286 1.447.765l1.404 2.063a.25.25 0 0 0 .207.11h6.224c.966 0 1.75.783 1.75 1.75v.117H5.408a.848.848 0 0 0 0 1.695h15.484a1 1 0 0 1 .995 1.102L21 19.25c-.106 1.05-.784 1.75-1.75 1.75H3.75A1.75 1.75 0 0 1 2 19.25z" })
	});
}
function TrashIcon() {
	return /* @__PURE__ */ jsx("svg", {
		className: action_button_module_default["trash-icon"],
		xmlns: "http://www.w3.org/2000/svg",
		viewBox: "0 0 22 24",
		children: /* @__PURE__ */ jsx("path", { d: "M16.313 4V2.144C16.313.96 15.353 0 14.169 0H7.831A2.14 2.14 0 0 0 5.69 2.189v-.002V4H0v2h.575c.196.023.372.099.515.214l-.002-.002c.119.157.203.346.239.552l.001.008l1.187 15.106c.094 1.84.094 2.118 2.25 2.118h12.462c2.16 0 2.16-.275 2.25-2.113l1.187-15.1c.036-.217.12-.409.242-.572l-.002.003a1 1 0 0 1 .508-.212h.58v-2h-5.687zM7 2.187c0-.6.487-.938 1.106-.938h5.734c.618 0 1.162.344 1.162.938V4h-8zM6.469 20l-.64-12h1.269l.656 12zm5.225 0H10.32V8h1.375zm3.85 0h-1.275l.656-12h1.269z" })
	});
}
var tri_checkbox_module_default = {
	container: "_container_12mvz_1",
	checkbox: "_checkbox_12mvz_8"
};
//#endregion
//#region app/routes/queue/components/tri-checkbox/tri-checkbox.tsx
function TriCheckbox({ state, onChange, children }) {
	const checkboxRef = useRef(null);
	useEffect(() => {
		if (checkboxRef && checkboxRef.current) checkboxRef.current.indeterminate = state === "some";
	}, [checkboxRef, state]);
	return /* @__PURE__ */ jsxs("div", {
		className: tri_checkbox_module_default.container,
		children: [/* @__PURE__ */ jsx("div", {
			className: tri_checkbox_module_default.checkbox,
			children: /* @__PURE__ */ jsx(Form$1.Check, {
				ref: checkboxRef,
				checked: state === "all" || state === true,
				onChange: (e) => onChange && onChange(e.target.checked)
			})
		}), /* @__PURE__ */ jsx("div", { children })]
	});
}
//#endregion
//#region app/utils/path.ts
function getLeafDirectoryName(fullPath) {
	let normalizedPath = fullPath.replace(/[/\\]$/, "");
	const lastSlash = normalizedPath.lastIndexOf("/");
	const lastBackslash = normalizedPath.lastIndexOf("\\");
	const lastSeparatorIndex = Math.max(lastSlash, lastBackslash);
	const leafName = normalizedPath.substring(lastSeparatorIndex + 1);
	if (leafName.length === 0) return normalizedPath;
	return leafName;
}
var page_table_module_default = {
	container: "_container_i75xf_1",
	header: "_header_i75xf_11",
	footer: "_footer_i75xf_20",
	title: "_title_i75xf_25",
	count: "_count_i75xf_29",
	tableContainer: "_tableContainer_i75xf_39",
	table: "_table_i75xf_39",
	removing: "_removing_i75xf_87",
	uploading: "_uploading_i75xf_91",
	badges: "_badges_i75xf_104",
	actions: "_actions_i75xf_111",
	mobile: "_mobile_i75xf_121",
	skinny: "_skinny_i75xf_125",
	desktop: "_desktop_i75xf_132"
};
//#endregion
//#region app/routes/queue/components/page-table/page-table.tsx
function PageTable({ children, headerCheckboxState, onHeaderCheckboxChange, footer }) {
	return /* @__PURE__ */ jsxs("div", {
		className: page_table_module_default.tableContainer,
		children: [/* @__PURE__ */ jsxs(Table, {
			className: page_table_module_default.table,
			children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
				/* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx(TriCheckbox, {
					state: headerCheckboxState,
					onChange: onHeaderCheckboxChange,
					children: "Name"
				}) }),
				/* @__PURE__ */ jsx("th", {
					className: page_table_module_default.desktop,
					children: "Category"
				}),
				/* @__PURE__ */ jsx("th", {
					className: page_table_module_default.desktop,
					children: "Status"
				}),
				/* @__PURE__ */ jsx("th", {
					className: page_table_module_default.desktop,
					children: "Size"
				}),
				/* @__PURE__ */ jsx("th", { children: "Actions" })
			] }) }), /* @__PURE__ */ jsx("tbody", { children })]
		}), footer && /* @__PURE__ */ jsx("div", {
			className: page_table_module_default.footer,
			children: footer
		})]
	});
}
function PageRow(props) {
	return /* @__PURE__ */ jsxs("tr", {
		className: classNames([props.isRemoving && page_table_module_default.removing, props.isUploading && page_table_module_default.uploading]),
		children: [
			/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs(TriCheckbox, {
				state: props.isSelected,
				onChange: props.onRowSelectionChanged,
				children: [/* @__PURE__ */ jsx(Truncate, { children: props.name }), /* @__PURE__ */ jsxs("div", {
					className: page_table_module_default.mobile,
					children: [/* @__PURE__ */ jsxs("div", {
						className: page_table_module_default.badges,
						children: [/* @__PURE__ */ jsx(StatusBadge, {
							status: props.status,
							percentage: props.percentage,
							error: props.error
						}), /* @__PURE__ */ jsx(CategoryBadge, { category: props.category })]
					}), /* @__PURE__ */ jsx("div", { children: formatFileSize(props.fileSizeBytes) })]
				})]
			}) }),
			/* @__PURE__ */ jsx("td", {
				className: page_table_module_default.desktop,
				children: /* @__PURE__ */ jsx(CategoryBadge, { category: props.category })
			}),
			/* @__PURE__ */ jsx("td", {
				className: page_table_module_default.desktop,
				children: /* @__PURE__ */ jsx(StatusBadge, {
					status: props.status,
					percentage: props.percentage,
					error: props.error
				})
			}),
			/* @__PURE__ */ jsx("td", {
				className: page_table_module_default.desktop,
				children: formatFileSize(props.fileSizeBytes)
			}),
			/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("div", {
				className: page_table_module_default.actions,
				children: props.actions
			}) })
		]
	});
}
function CategoryBadge({ category }) {
	const categoryLower = category?.toLowerCase();
	let variant = "secondary";
	if (categoryLower === "movies") variant = "primary";
	if (categoryLower === "tv") variant = "info";
	return /* @__PURE__ */ jsx(Badge, {
		bg: variant,
		style: { width: "85px" },
		children: categoryLower
	});
}
var page_section_module_default = {
	container: "_container_knhns_1",
	header: "_header_knhns_12",
	titleRow: "_titleRow_knhns_16",
	badgeText: "_badgeText_knhns_25"
};
//#endregion
//#region app/routes/queue/components/page-section/page-section.tsx
function PageSection({ title, subTitle, badgeText, children }) {
	return /* @__PURE__ */ jsxs("div", {
		className: page_section_module_default.container,
		children: [/* @__PURE__ */ jsxs("div", {
			className: page_section_module_default.header,
			children: [/* @__PURE__ */ jsxs("div", {
				className: page_section_module_default.titleRow,
				children: [title, badgeText && /* @__PURE__ */ jsx("div", {
					className: page_section_module_default.badgeText,
					children: badgeText
				})]
			}), subTitle]
		}), children]
	});
}
//#endregion
//#region app/routes/queue/components/history-table/history-table.tsx
function HistoryTable({ historySlots, totalHistoryCount, onIsSelectedChanged, onIsRemovingChanged, onRemoved }) {
	const [isConfirmingRemoval, setIsConfirmingRemoval] = useState(false);
	var selectedCount = historySlots.filter((x) => !!x.isSelected).length;
	var headerCheckboxState = selectedCount === 0 ? "none" : selectedCount === historySlots.length ? "all" : "some";
	const onSelectAll = useCallback((isSelected) => {
		onIsSelectedChanged(new Set(historySlots.map((x) => x.nzo_id)), isSelected);
	}, [historySlots, onIsSelectedChanged]);
	const onRemove = useCallback(() => {
		setIsConfirmingRemoval(true);
	}, [setIsConfirmingRemoval]);
	const onCancelRemoval = useCallback(() => {
		setIsConfirmingRemoval(false);
	}, [setIsConfirmingRemoval]);
	const onConfirmRemoval = useCallback(async (deleteCompletedFiles) => {
		var nzo_ids = new Set(historySlots.filter((x) => !!x.isSelected).map((x) => x.nzo_id));
		setIsConfirmingRemoval(false);
		onIsRemovingChanged(nzo_ids, true);
		try {
			const response = await fetch(`/api?mode=history&name=delete&del_completed_files=${deleteCompletedFiles ? 1 : 0}`, {
				method: "POST",
				headers: { "Content-Type": "application/json;charset=UTF-8" },
				body: JSON.stringify({ nzo_ids: Array.from(nzo_ids) })
			});
			if (response.ok) {
				if ((await response.json()).status === true) {
					onRemoved(nzo_ids);
					return;
				}
			}
		} catch {}
		onIsRemovingChanged(nzo_ids, false);
	}, [
		historySlots,
		setIsConfirmingRemoval,
		onIsRemovingChanged,
		onRemoved
	]);
	return /* @__PURE__ */ jsxs(PageSection, {
		title: /* @__PURE__ */ jsxs("div", {
			className: route_module_default.sectionTitle,
			children: [/* @__PURE__ */ jsx("h3", { children: "History" }), headerCheckboxState !== "none" && /* @__PURE__ */ jsx(ActionButton, {
				type: "delete",
				onClick: onRemove
			})]
		}),
		children: [/* @__PURE__ */ jsx(PageTable, {
			headerCheckboxState,
			onHeaderCheckboxChange: onSelectAll,
			children: historySlots.map((slot) => /* @__PURE__ */ jsx(HistoryRow, {
				slot,
				onIsSelectedChanged: (id, isSelected) => onIsSelectedChanged(new Set([id]), isSelected),
				onIsRemovingChanged: (id, isRemoving) => onIsRemovingChanged(new Set([id]), isRemoving),
				onRemoved: (id) => onRemoved(new Set([id]))
			}, slot.nzo_id))
		}), /* @__PURE__ */ jsx(ConfirmModal, {
			show: isConfirmingRemoval,
			title: "Remove From History?",
			message: `${selectedCount} item(s) will be removed`,
			checkboxMessage: "Delete mounted files",
			onConfirm: onConfirmRemoval,
			onCancel: onCancelRemoval
		})]
	});
}
function HistoryRow({ slot, onIsSelectedChanged, onIsRemovingChanged, onRemoved }) {
	const [isConfirmingRemoval, setIsConfirmingRemoval] = useState(false);
	const onRemove = useCallback(() => {
		setIsConfirmingRemoval(true);
	}, [setIsConfirmingRemoval]);
	const onCancelRemoval = useCallback(() => {
		setIsConfirmingRemoval(false);
	}, [setIsConfirmingRemoval]);
	const onConfirmRemoval = useCallback(async (deleteCompletedFiles) => {
		setIsConfirmingRemoval(false);
		onIsRemovingChanged(slot.nzo_id, true);
		try {
			const url = `/api?mode=history&name=delete&value=${encodeURIComponent(slot.nzo_id)}&del_completed_files=${deleteCompletedFiles ? 1 : 0}`;
			const response = await fetch(url);
			if (response.ok) {
				if ((await response.json()).status === true) {
					onRemoved(slot.nzo_id);
					return;
				}
			}
		} catch {}
		onIsRemovingChanged(slot.nzo_id, false);
	}, [
		slot.nzo_id,
		setIsConfirmingRemoval,
		onIsRemovingChanged,
		onRemoved
	]);
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PageRow, {
		isSelected: !!slot.isSelected,
		isRemoving: !!slot.isRemoving,
		name: slot.name,
		category: slot.category,
		status: slot.status,
		error: slot.fail_message,
		fileSizeBytes: slot.bytes,
		actions: /* @__PURE__ */ jsx(Actions, {
			slot,
			onRemove
		}),
		onRowSelectionChanged: (isSelected) => onIsSelectedChanged(slot.nzo_id, isSelected)
	}), /* @__PURE__ */ jsx(ConfirmModal, {
		show: isConfirmingRemoval,
		title: "Remove From History?",
		message: slot.nzb_name,
		checkboxMessage: !slot.fail_message ? "Delete mounted files" : void 0,
		errorMessage: slot.fail_message,
		onConfirm: onConfirmRemoval,
		onCancel: onCancelRemoval
	})] });
}
function Actions({ slot, onRemove }) {
	const [isMenuOpen, setIsMenuOpen] = useState(false);
	var downloadFolder = slot.storage && getLeafDirectoryName(slot.storage);
	const encodedCategory = downloadFolder && encodeURIComponent(slot.category);
	var folderLink = downloadFolder && `/explore/content/${encodedCategory}/${downloadFolder && encodeURIComponent(downloadFolder)}`;
	var nzbDownloadUrl = slot.nzb_blob_id ? `/api/download-nzb?nzbBlobId=${slot.nzb_blob_id}` : null;
	var isFolderDisabled = !downloadFolder || !!slot.isRemoving || !!slot.fail_message;
	const onMenuClick = useCallback((e) => {
		e.stopPropagation();
		setIsMenuOpen((x) => !x);
	}, []);
	const onRemoveSelected = useCallback(() => {
		setIsMenuOpen(false);
		onRemove?.();
	}, [onRemove]);
	return /* @__PURE__ */ jsxs(Fragment, { children: [
		!isFolderDisabled && /* @__PURE__ */ jsx(Link, {
			to: folderLink,
			children: /* @__PURE__ */ jsx(ActionButton, { type: "explore" })
		}),
		isFolderDisabled && /* @__PURE__ */ jsx(ActionButton, {
			type: "explore",
			disabled: true
		}),
		/* @__PURE__ */ jsxs("div", {
			style: { position: "relative" },
			children: [/* @__PURE__ */ jsx(ActionButton, {
				type: "menu",
				disabled: !!slot.isRemoving,
				selected: isMenuOpen,
				onClick: onMenuClick
			}), /* @__PURE__ */ jsx(DropdownOptions, {
				style: { marginTop: "5px" },
				isOpen: isMenuOpen,
				onClose: () => setIsMenuOpen(false),
				options: [!!nzbDownloadUrl ? {
					option: /* @__PURE__ */ jsx(ExportNzb, {}),
					linkTo: nzbDownloadUrl
				} : void 0, {
					option: /* @__PURE__ */ jsx(Remove, {}),
					onSelect: onRemoveSelected,
					variant: "danger"
				}]
			})]
		})
	] });
}
var empty_queue_module_default = {
	emptyState: "_emptyState_10d6u_1",
	emptyIcon: "_emptyIcon_10d6u_11",
	emptyTitle: "_emptyTitle_10d6u_17",
	emptyDescription: "_emptyDescription_10d6u_24"
};
//#endregion
//#region app/routes/queue/components/empty-queue/empty-queue.tsx
function EmptyQueue(props) {
	const onUploadClicked = useCallback((e) => {
		e.preventDefault();
		props.onUploadClicked?.call(null);
	}, [props.onUploadClicked]);
	return /* @__PURE__ */ jsxs("div", {
		className: empty_queue_module_default.emptyState,
		children: [
			/* @__PURE__ */ jsx("div", {
				className: empty_queue_module_default.emptyIcon,
				children: "🪅🎉🥳"
			}),
			/* @__PURE__ */ jsx("div", {
				className: empty_queue_module_default.emptyTitle,
				children: "Empty Queue!"
			}),
			/* @__PURE__ */ jsxs("div", {
				className: empty_queue_module_default.emptyDescription,
				children: [/* @__PURE__ */ jsx("a", {
					href: "#",
					onClick: onUploadClicked,
					children: "Upload an nzb file"
				}), " to get started"]
			})
		]
	});
}
var simple_dropdown_module_default = {
	container: "_container_1orjg_1",
	nativeSelect: "_nativeSelect_1orjg_6",
	dropdown: "_dropdown_1orjg_20",
	bordered: "_bordered_1orjg_25",
	selected: "_selected_1orjg_31",
	arrow: "_arrow_1orjg_47",
	openAbove: "_openAbove_1orjg_74",
	option: "_option_1orjg_81"
};
//#endregion
//#region app/routes/queue/components/simple-dropdown/simple-dropdown.tsx
var SimpleDropdown = memo(({ type, options, value, onChange, valueRef }) => {
	if (!valueRef && (!value || !onChange)) throw new Error("SimpleDropdown requires either the valueRef prop or both the value and onChange props.");
	const [internalValue, setInternalValue] = useState(options.length > 0 ? options[0] : "");
	const [isOpen, setIsOpen] = useState(false);
	const [openAbove, setOpenAbove] = useState(false);
	const dropdownRef = useRef(null);
	const renderedValue = value || internalValue;
	const containerClassNames = classNames([simple_dropdown_module_default.container, type === "bordered" && simple_dropdown_module_default.bordered]);
	const toggleDropdown = useCallback(() => {
		if (!isOpen && dropdownRef.current) {
			const rect = dropdownRef.current.getBoundingClientRect();
			const viewportHeight = window.innerHeight;
			setOpenAbove(rect.top > viewportHeight / 2);
		}
		setIsOpen((prev) => !prev);
	}, [isOpen]);
	const handleSelectedOptionChange = useCallback((option) => {
		if (!!valueRef) {
			setInternalValue(option);
			valueRef.current = option;
		} else if (!!onChange) onChange(option);
	}, [
		valueRef,
		setInternalValue,
		onChange
	]);
	const handleOptionClick = useCallback((option) => {
		handleSelectedOptionChange(option);
		setIsOpen(false);
	}, [onChange]);
	const handleNativeChange = useCallback((e) => {
		handleSelectedOptionChange(e.target.value);
	}, [handleSelectedOptionChange]);
	useEffect(() => {
		const handleClickOutside = (event) => {
			if (dropdownRef.current && !dropdownRef.current.contains(event.target)) setIsOpen(false);
		};
		if (isOpen) document.addEventListener("mousedown", handleClickOutside);
		return () => {
			document.removeEventListener("mousedown", handleClickOutside);
		};
	}, [isOpen]);
	return /* @__PURE__ */ jsxs("div", {
		className: containerClassNames,
		ref: dropdownRef,
		children: [
			/* @__PURE__ */ jsx("select", {
				className: simple_dropdown_module_default.nativeSelect,
				value: renderedValue,
				onChange: handleNativeChange,
				children: options.map((option) => /* @__PURE__ */ jsx("option", {
					value: option,
					children: option
				}, option))
			}),
			/* @__PURE__ */ jsxs("div", {
				className: simple_dropdown_module_default.selected,
				onClick: toggleDropdown,
				children: [renderedValue, /* @__PURE__ */ jsx("span", { className: simple_dropdown_module_default.arrow })]
			}),
			isOpen && /* @__PURE__ */ jsx("div", {
				className: classNames([simple_dropdown_module_default.dropdown, openAbove && simple_dropdown_module_default.openAbove]),
				children: options.map((option) => /* @__PURE__ */ jsx("div", {
					className: simple_dropdown_module_default.option,
					onClick: () => handleOptionClick(option),
					children: option
				}, option))
			})
		]
	});
});
var wide_viewport_module_default = { container: "_container_9gq24_1" };
//#endregion
//#region app/routes/queue/components/wide-viewport/wide-viewport.tsx
function WideViewport({ width, children }) {
	const uniqueId = `wide-viewport-${useId().replace(/:/g, "-")}`;
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
                    @media not (min-width: ${width}) {
                        .${uniqueId} {
                            display: none !important;
                        }
                    }
                ` } }), /* @__PURE__ */ jsx("div", {
		className: `${wide_viewport_module_default.container} ${uniqueId}`,
		children
	})] });
}
var thin_viewport_module_default = { container: "_container_9gq24_1" };
//#endregion
//#region app/routes/queue/components/thin-viewport/thin-viewport.tsx
var ThinViewport = memo(({ width, children }) => {
	const uniqueId = `thin-viewport-${useId().replace(/:/g, "-")}`;
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: `
                    @media (min-width: ${width}) {
                        .${uniqueId} {
                            display: none !important;
                        }
                    }
                ` } }), /* @__PURE__ */ jsx("div", {
		className: `${thin_viewport_module_default.container} ${uniqueId}`,
		children
	})] });
});
//#endregion
//#region app/routes/queue/components/queue-table/queue-table.tsx
function QueueTable({ queueSlots, totalQueueCount, categories, manualCategoryRef, onIsSelectedChanged, onIsRemovingChanged, onRemoved, onUploadClicked }) {
	const [isConfirmingRemoval, setIsConfirmingRemoval] = useState(false);
	var selectedCount = queueSlots.filter((x) => !!x.isSelected).length;
	var headerCheckboxState = selectedCount === 0 ? "none" : selectedCount === queueSlots.length ? "all" : "some";
	const onRowIsSelectedChanged = useCallback((id, isSelected) => {
		onIsSelectedChanged(new Set([id]), isSelected);
	}, [onIsSelectedChanged]);
	const onRowIsRemovingChanged = useCallback((id, isRemoving) => {
		onIsRemovingChanged(new Set([id]), isRemoving);
	}, [onIsSelectedChanged]);
	const onRowRemoved = useCallback((id) => {
		onRemoved(new Set([id]));
	}, [onRemoved]);
	const onSelectAll = useCallback((isSelected) => {
		onIsSelectedChanged(new Set(queueSlots.map((x) => x.nzo_id)), isSelected);
	}, [queueSlots, onIsSelectedChanged]);
	const onRemove = useCallback(() => {
		setIsConfirmingRemoval(true);
	}, [setIsConfirmingRemoval]);
	const onCancelRemoval = useCallback(() => {
		setIsConfirmingRemoval(false);
	}, [setIsConfirmingRemoval]);
	const onConfirmRemoval = useCallback(async () => {
		onRemoved(new Set(queueSlots.filter((x) => x.isUploading && !!x.isSelected).map((x) => x.nzo_id)));
		const queued_nzo_ids = new Set(queueSlots.filter((x) => !x.isUploading && !!x.isSelected).map((x) => x.nzo_id));
		setIsConfirmingRemoval(false);
		onIsRemovingChanged(queued_nzo_ids, true);
		try {
			const response = await fetch(`/api?mode=queue&name=delete`, {
				method: "POST",
				headers: { "Content-Type": "application/json;charset=UTF-8" },
				body: JSON.stringify({ nzo_ids: Array.from(queued_nzo_ids) })
			});
			if (response.ok) {
				if ((await response.json()).status === true) {
					onRemoved(queued_nzo_ids);
					return;
				}
			}
		} catch {}
		onIsRemovingChanged(queued_nzo_ids, false);
	}, [
		queueSlots,
		setIsConfirmingRemoval,
		onIsRemovingChanged,
		onRemoved
	]);
	const categoryDropdown = useMemo(() => /* @__PURE__ */ jsx("div", {
		title: "Choose the category for manual nzb uploads.",
		children: /* @__PURE__ */ jsx(SimpleDropdown, {
			options: categories,
			valueRef: manualCategoryRef
		})
	}), [categories]);
	return /* @__PURE__ */ jsxs(PageSection, {
		title: /* @__PURE__ */ jsxs("div", {
			className: route_module_default.sectionTitle,
			children: [
				/* @__PURE__ */ jsx("h3", {
					onClick: onUploadClicked,
					style: { cursor: "pointer" },
					children: "Queue"
				}),
				headerCheckboxState !== "none" && /* @__PURE__ */ jsx(ActionButton, {
					type: "delete",
					onClick: onRemove
				}),
				/* @__PURE__ */ jsx(WideViewport, {
					width: "450px",
					children: /* @__PURE__ */ jsx("div", {
						style: { marginLeft: "10px" },
						children: categoryDropdown
					})
				})
			]
		}),
		subTitle: /* @__PURE__ */ jsx(ThinViewport, {
			width: "450px",
			children: categoryDropdown
		}),
		children: [queueSlots?.length == 0 ? /* @__PURE__ */ jsx(EmptyQueue, { onUploadClicked }) : /* @__PURE__ */ jsx(PageTable, {
			headerCheckboxState,
			onHeaderCheckboxChange: onSelectAll,
			children: queueSlots.map((slot) => /* @__PURE__ */ jsx(QueueRow, {
				slot,
				onIsSelectedChanged: onRowIsSelectedChanged,
				onIsRemovingChanged: onRowIsRemovingChanged,
				onRemoved: onRowRemoved
			}, slot.nzo_id))
		}), /* @__PURE__ */ jsx(ConfirmModal, {
			show: isConfirmingRemoval,
			title: "Remove From Queue?",
			message: `${selectedCount} item(s) will be removed`,
			onConfirm: onConfirmRemoval,
			onCancel: onCancelRemoval
		})]
	});
}
var QueueRow = memo(({ slot, onIsSelectedChanged, onIsRemovingChanged, onRemoved }) => {
	const [isConfirmingRemoval, setIsConfirmingRemoval] = useState(false);
	const isActivelyUploading = slot.isUploading && slot.status == "uploading";
	const onRemove = useCallback(() => {
		if (slot.isUploading) {
			onRemoved(slot.nzo_id);
			return;
		}
		setIsConfirmingRemoval(true);
	}, [setIsConfirmingRemoval]);
	const onCancelRemoval = useCallback(() => {
		setIsConfirmingRemoval(false);
	}, [setIsConfirmingRemoval]);
	const onConfirmRemoval = useCallback(async () => {
		if (slot.isUploading) return;
		setIsConfirmingRemoval(false);
		onIsRemovingChanged(slot.nzo_id, true);
		try {
			const url = `/api?mode=queue&name=delete&value=${encodeURIComponent(slot.nzo_id)}`;
			const response = await fetch(url);
			if (response.ok) {
				if ((await response.json()).status === true) {
					onRemoved(slot.nzo_id);
					return;
				}
			}
		} catch {}
		onIsRemovingChanged(slot.nzo_id, false);
	}, [
		slot.nzo_id,
		setIsConfirmingRemoval,
		onIsRemovingChanged,
		onRemoved
	]);
	return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PageRow, {
		isUploading: !!slot.isUploading,
		isSelected: !!slot.isSelected,
		isRemoving: !!slot.isRemoving,
		name: slot.filename,
		category: slot.cat,
		status: slot.status,
		percentage: slot.true_percentage,
		fileSizeBytes: Number(slot.mb) * 1024 * 1024,
		actions: /* @__PURE__ */ jsx(ActionButton, {
			type: "delete",
			disabled: !!slot.isRemoving || isActivelyUploading,
			onClick: onRemove
		}),
		onRowSelectionChanged: (isSelected) => onIsSelectedChanged(slot.nzo_id, isSelected),
		error: slot.error
	}), /* @__PURE__ */ jsx(ConfirmModal, {
		show: isConfirmingRemoval,
		title: "Remove From Queue?",
		message: slot.filename,
		onConfirm: onConfirmRemoval,
		onCancel: onCancelRemoval
	})] });
});
//#endregion
//#region app/routes/queue/controllers/events-controller.ts
function useQueueEvents(setUploadingFiles, setQueueSlots, uploadQueueRef) {
	return memoize({
		onAddQueueSlot: useCallback((queueSlot) => {
			uploadQueueRef.current = uploadQueueRef.current.filter((x) => x.queueSlot.status === "uploading" || x.queueSlot.filename !== queueSlot.filename);
			setUploadingFiles((files) => files.filter((f) => f.queueSlot.filename !== queueSlot.filename));
			setQueueSlots((slots) => [...slots, queueSlot]);
		}, [setQueueSlots]),
		onSelectQueueSlots: useCallback((ids, isSelected) => {
			setUploadingFiles((files) => files.map((x) => ids.has(x.queueSlot.nzo_id) ? {
				...x,
				queueSlot: {
					...x.queueSlot,
					isSelected
				}
			} : x));
			setQueueSlots((slots) => slots.map((x) => ids.has(x.nzo_id) ? {
				...x,
				isSelected
			} : x));
		}, [setQueueSlots]),
		onRemovingQueueSlots: useCallback((ids, isRemoving) => {
			setQueueSlots((slots) => slots.map((x) => ids.has(x.nzo_id) ? {
				...x,
				isRemoving
			} : x));
		}, [setQueueSlots]),
		onRemoveQueueSlots: useCallback((ids) => {
			uploadQueueRef.current = uploadQueueRef.current.filter((x) => x.queueSlot.status === "uploading" || !ids.has(x.queueSlot.nzo_id));
			setUploadingFiles((files) => files.filter((x) => x.queueSlot.status === "uploading" || !ids.has(x.queueSlot.nzo_id)));
			setQueueSlots((slots) => slots.filter((x) => !ids.has(x.nzo_id)));
		}, [setQueueSlots]),
		onChangeQueueSlotStatus: useCallback((message) => {
			const [nzo_id, status] = message.split("|");
			setQueueSlots((slots) => slots.map((x) => x.nzo_id === nzo_id ? {
				...x,
				status
			} : x));
		}, [setQueueSlots]),
		onChangeQueueSlotPercentage: useCallback((message) => {
			const [nzo_id, true_percentage] = message.split("|");
			setQueueSlots((slots) => slots.map((x) => x.nzo_id === nzo_id ? {
				...x,
				true_percentage
			} : x));
		}, [setQueueSlots])
	});
}
function useHistoryEvents(setHistorySlots) {
	return memoize({
		onAddHistorySlot: useCallback((historySlot) => {
			setHistorySlots((slots) => [historySlot, ...slots]);
		}, [setHistorySlots]),
		onSelectHistorySlots: useCallback((ids, isSelected) => {
			setHistorySlots((slots) => slots.map((x) => ids.has(x.nzo_id) ? {
				...x,
				isSelected
			} : x));
		}, [setHistorySlots]),
		onRemovingHistorySlots: useCallback((ids, isRemoving) => {
			setHistorySlots((slots) => slots.map((x) => ids.has(x.nzo_id) ? {
				...x,
				isRemoving
			} : x));
		}, [setHistorySlots]),
		onRemoveHistorySlots: useCallback((ids) => {
			setHistorySlots((slots) => slots.filter((x) => !ids.has(x.nzo_id)));
		}, [setHistorySlots])
	});
}
function memoize(object) {
	return useMemo(() => object, Object.values(object));
}
//#endregion
//#region app/routes/queue/controllers/websocket-controller.ts
var topicNames = {
	queueItemStatus: "qs",
	queueItemPercentage: "qp",
	queueItemAdded: "qa",
	queueItemRemoved: "qr",
	historyItemAdded: "ha",
	historyItemRemoved: "hr"
};
var topicSubscriptions = {
	[topicNames.queueItemStatus]: "state",
	[topicNames.queueItemPercentage]: "state",
	[topicNames.queueItemAdded]: "event",
	[topicNames.queueItemRemoved]: "event",
	[topicNames.historyItemAdded]: "event",
	[topicNames.historyItemRemoved]: "event"
};
function initializeQueueHistoryWebsocket(queueEvents, historyEvents, disableLiveView) {
	const onWebsocketMessage = useCallback((topic, message) => {
		if (disableLiveView) return;
		if (topic == topicNames.queueItemAdded) queueEvents.onAddQueueSlot(JSON.parse(message));
		else if (topic == topicNames.queueItemRemoved) queueEvents.onRemoveQueueSlots(new Set(message.split(",")));
		else if (topic == topicNames.queueItemStatus) queueEvents.onChangeQueueSlotStatus(message);
		else if (topic == topicNames.queueItemPercentage) queueEvents.onChangeQueueSlotPercentage(message);
		else if (topic == topicNames.historyItemAdded) historyEvents.onAddHistorySlot(JSON.parse(message));
		else if (topic == topicNames.historyItemRemoved) historyEvents.onRemoveHistorySlots(new Set(message.split(",")));
	}, [
		queueEvents,
		historyEvents,
		disableLiveView
	]);
	useEffect(() => {
		if (disableLiveView) return;
		let ws;
		let disposed = false;
		function connect() {
			ws = new WebSocket(window.location.origin.replace(/^http/, "ws"));
			ws.onmessage = receiveMessage(onWebsocketMessage);
			ws.onopen = () => {
				ws.send(JSON.stringify(topicSubscriptions));
			};
			ws.onclose = () => {
				!disposed && setTimeout(() => connect(), 1e3);
			};
			ws.onerror = () => {
				ws.close();
			};
			return () => {
				disposed = true;
				ws.close();
			};
		}
		return connect();
	}, [onWebsocketMessage, disableLiveView]);
}
//#endregion
//#region app/routes/queue/controllers/nzb-upload-controller.ts
function initializeUploadController(isUploadingRef, uploadQueueRef, uploadingFiles, setUploadingFiles) {
	useEffect(() => {
		processUploadQueue(isUploadingRef, uploadQueueRef, setUploadingFiles);
	}, [uploadingFiles]);
}
async function processUploadQueue(isUploadingRef, uploadQueueRef, setUploadingFiles) {
	if (isUploadingRef.current || uploadQueueRef.current.length === 0) return;
	isUploadingRef.current = true;
	const fileToUpload = uploadQueueRef.current[0];
	setUploadingFiles((files) => files.map((f) => f.queueSlot.nzo_id === fileToUpload.queueSlot.nzo_id ? {
		...f,
		queueSlot: {
			...f.queueSlot,
			status: "uploading"
		}
	} : f));
	try {
		const xhr = new XMLHttpRequest();
		const formData = new FormData();
		formData.append("nzbFile", fileToUpload.file, fileToUpload.file.name);
		xhr.responseType = "json";
		xhr.upload.addEventListener("progress", (e) => {
			if (e.lengthComputable) {
				const progress = Math.round(e.loaded / e.total * 100);
				setUploadingFiles((files) => files.map((f) => f.queueSlot.nzo_id === fileToUpload.queueSlot.nzo_id ? {
					...f,
					queueSlot: {
						...f.queueSlot,
						percentage: progress.toString(),
						true_percentage: progress.toString()
					}
				} : f));
			}
		});
		var response = await new Promise((resolve, reject) => {
			xhr.addEventListener("load", () => {
				if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
				else {
					const errorMessage = xhr.response.error || `Upload failed with status ${xhr.status}`;
					reject(new Error(errorMessage));
				}
			});
			xhr.addEventListener("error", () => reject(/* @__PURE__ */ new Error("Upload failed")));
			xhr.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("Upload aborted")));
			xhr.open("POST", `/api?mode=addfile&cat=${fileToUpload.queueSlot.cat}&priority=0&pp=0`);
			xhr.send(formData);
		});
		if (response.status == false) throw new Error(response.error);
	} catch (error) {
		setUploadingFiles((files) => files.map((f) => f.queueSlot.nzo_id === fileToUpload.queueSlot.nzo_id ? {
			...f,
			queueSlot: {
				...f.queueSlot,
				status: "upload failed",
				error: error instanceof Error ? error.message : "Upload failed"
			}
		} : f));
	}
	uploadQueueRef.current = uploadQueueRef.current.filter((x) => x !== fileToUpload);
	isUploadingRef.current = false;
	if (uploadQueueRef.current.length > 0) processUploadQueue(isUploadingRef, uploadQueueRef, setUploadingFiles);
}
//#endregion
//#region app/routes/queue/controllers/dropzone-controller.ts
function useQueueDropzone(setUploadingFiles, uploadQueueRef, manualCategoryRef) {
	return useDropzone({
		accept: { "application/x-nzb": [".nzb"] },
		onDrop: useCallback((acceptedFiles) => {
			const newFiles = acceptedFiles.map((file) => ({
				file,
				queueSlot: {
					isUploading: true,
					nzo_id: `upload-${Date.now()}-${Math.random()}`,
					priority: "Normal",
					filename: file.name,
					cat: manualCategoryRef.current,
					percentage: "0",
					true_percentage: "0",
					status: "pending",
					mb: (file.size / (1024 * 1024)).toFixed(2),
					mbleft: (file.size / (1024 * 1024)).toFixed(2)
				}
			}));
			setUploadingFiles((files) => [...files, ...newFiles]);
			uploadQueueRef.current = [...uploadQueueRef.current, ...newFiles];
		}, []),
		noClick: true,
		noKeyboard: true
	});
}
//#endregion
//#region app/routes/queue/route.tsx
var route_exports = /* @__PURE__ */ __exportAll({
	default: () => route_default,
	loader: () => loader
});
var maxItems = 100;
async function loader({ request }) {
	const queuePromise = backendClient.getQueue(maxItems);
	const historyPromise = backendClient.getHistory(maxItems);
	const configPromise = backendClient.getConfig(["api.categories", "api.manual-category"]);
	const queue = await queuePromise;
	const history = await historyPromise;
	const config = await configPromise;
	const categoriesValue = config.find((x) => x.configName === "api.categories")?.configValue ?? "uncategorized,audio,software,tv,movies";
	const manualCategory = config.find((x) => x.configName === "api.manual-category")?.configValue ?? "uncategorized";
	let categories = categoriesValue.split(",").map((x) => x.trim());
	if (!categories.includes(manualCategory)) categories = [manualCategory, ...categories];
	return {
		queueSlots: queue?.slots || [],
		historySlots: history?.slots || [],
		totalQueueCount: queue?.noofslots || 0,
		totalHistoryCount: history?.noofslots || 0,
		categories,
		manualCategory
	};
}
var route_default = UNSAFE_withComponentProps(function Queue(props) {
	const [queueSlots, setQueueSlots] = useState(props.loaderData.queueSlots);
	const [historySlots, setHistorySlots] = useState(props.loaderData.historySlots);
	const [uploadingFiles, setUploadingFiles] = useState([]);
	const uploadQueueRef = useRef([]);
	const manualCategoryRef = useRef(props.loaderData.manualCategory);
	const isUploadingRef = useRef(false);
	const disableLiveView = queueSlots.length == maxItems || historySlots.length == maxItems;
	const combinedQueueSlots = [...uploadingFiles.map((file) => file.queueSlot), ...queueSlots];
	const queueEvents = useQueueEvents(setUploadingFiles, setQueueSlots, uploadQueueRef);
	const historyEvents = useHistoryEvents(setHistorySlots);
	initializeQueueHistoryWebsocket(queueEvents, historyEvents, disableLiveView);
	const dropzone = useQueueDropzone(setUploadingFiles, uploadQueueRef, manualCategoryRef);
	initializeUploadController(isUploadingRef, uploadQueueRef, uploadingFiles, setUploadingFiles);
	return /* @__PURE__ */ jsxs("div", {
		className: route_module_default.container,
		children: [
			disableLiveView && /* @__PURE__ */ jsxs(Alert, {
				className: route_module_default.alert,
				variant: "warning",
				children: [/* @__PURE__ */ jsx("b", { children: "Attention" }), /* @__PURE__ */ jsxs("ul", {
					className: route_module_default.list,
					children: [
						/* @__PURE__ */ jsxs("li", {
							className: route_module_default.listItem,
							children: [
								"Displaying the first ",
								queueSlots.length,
								" of ",
								props.loaderData.totalQueueCount,
								" queue items"
							]
						}),
						/* @__PURE__ */ jsxs("li", {
							className: route_module_default.listItem,
							children: [
								"Displaying the first ",
								historySlots.length,
								" of ",
								props.loaderData.totalHistoryCount,
								" history items"
							]
						}),
						/* @__PURE__ */ jsxs("li", {
							className: route_module_default.listItem,
							children: [
								"Live view is disabled. Manually ",
								/* @__PURE__ */ jsx(Link, {
									to: "/queue",
									children: "refresh"
								}),
								" the page for updates."
							]
						}),
						/* @__PURE__ */ jsx("li", {
							className: route_module_default.listItem,
							children: "(This is a bandaid — Proper pagination will be added soon)"
						})
					]
				})]
			}),
			/* @__PURE__ */ jsx("div", {
				className: route_module_default.queueContainer,
				children: /* @__PURE__ */ jsxs("div", {
					className: route_module_default.dropzone,
					...dropzone.getRootProps(),
					children: [
						dropzone.isDragActive && /* @__PURE__ */ jsx("div", { className: route_module_default.activeDropzone }),
						/* @__PURE__ */ jsx("input", { ...dropzone.getInputProps() }),
						/* @__PURE__ */ jsx(QueueTable, {
							queueSlots: combinedQueueSlots,
							totalQueueCount: props.loaderData.totalQueueCount + uploadingFiles.length,
							categories: props.loaderData.categories,
							manualCategoryRef,
							onIsSelectedChanged: queueEvents.onSelectQueueSlots,
							onIsRemovingChanged: queueEvents.onRemovingQueueSlots,
							onRemoved: queueEvents.onRemoveQueueSlots,
							onUploadClicked: dropzone.open
						})
					]
				})
			}),
			historySlots.length > 0 && /* @__PURE__ */ jsx(HistoryTable, {
				historySlots,
				totalHistoryCount: props.loaderData.totalHistoryCount,
				onIsSelectedChanged: historyEvents.onSelectHistorySlots,
				onIsRemovingChanged: historyEvents.onRemovingHistorySlots,
				onRemoved: historyEvents.onRemoveHistorySlots
			})
		]
	});
});
//#endregion
//#region \0virtual:react-router/server-manifest
var server_manifest_default = {
	"entry": {
		"module": "/assets/entry.client-BEXs_SFx.js",
		"imports": ["/assets/jsx-runtime-BciRPUQx.js", "/assets/react-dom-DCZYeO_j.js"],
		"css": []
	},
	"routes": {
		"root": {
			"id": "root",
			"parentId": void 0,
			"path": "",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/root-DVt0vUqE.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/react-dom-DCZYeO_j.js",
				"/assets/loading-B5ND0RTV.js",
				"/assets/styling-C_P8q9X5.js",
				"/assets/websocket-util-C6p0U1U9.js"
			],
			"css": ["/assets/root-CApBvtzn.css", "/assets/loading-DMS5IieH.css"],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/onboarding": {
			"id": "routes/onboarding",
			"parentId": "root",
			"path": "onboarding",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": true,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-DC7EiLBI.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/warning-CmTaXbvT.js",
				"/assets/Form-DOpuqsbh.js",
				"/assets/react-dom-DCZYeO_j.js"
			],
			"css": ["/assets/route-C6JUXXBo.css"],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/settings": {
			"id": "routes/settings",
			"parentId": "root",
			"path": "settings",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-2jEHnVi5.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/confirm-modal-QYMWgrle.js",
				"/assets/styling-C_P8q9X5.js",
				"/assets/websocket-util-C6p0U1U9.js",
				"/assets/warning-CmTaXbvT.js",
				"/assets/hasClass-7uw8q3Dv.js",
				"/assets/Form-DOpuqsbh.js",
				"/assets/react-dom-DCZYeO_j.js"
			],
			"css": ["/assets/route-CMsWNdO_.css", "/assets/confirm-modal-CqYDgXji.css"],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/settings.update": {
			"id": "routes/settings.update",
			"parentId": "routes/settings",
			"path": "update",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": true,
			"hasLoader": false,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": false,
			"hasErrorBoundary": false,
			"module": "/assets/route-DL6n6iO4.js",
			"imports": [],
			"css": [],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/explore": {
			"id": "routes/explore",
			"parentId": "root",
			"path": "explore",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-CfElXK84.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/loading-B5ND0RTV.js",
				"/assets/item-menu-Cfh9B9In.js",
				"/assets/styling-C_P8q9X5.js"
			],
			"css": [
				"/assets/route--eEQ9df3.css",
				"/assets/loading-DMS5IieH.css",
				"/assets/item-menu-CeWpvaMA.css"
			],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/_index": {
			"id": "routes/_index",
			"parentId": "root",
			"path": void 0,
			"index": true,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": false,
			"hasErrorBoundary": false,
			"module": "/assets/route-D6xVxo3M.js",
			"imports": [],
			"css": [],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/health": {
			"id": "routes/health",
			"parentId": "root",
			"path": "health",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-YhxOjn47.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/status-badge-BVNHzYyM.js",
				"/assets/websocket-util-C6p0U1U9.js",
				"/assets/warning-CmTaXbvT.js",
				"/assets/styling-C_P8q9X5.js",
				"/assets/hasClass-7uw8q3Dv.js",
				"/assets/react-dom-DCZYeO_j.js"
			],
			"css": ["/assets/route-B1W3NP-Z.css", "/assets/status-badge-DWI4dgjY.css"],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/logout": {
			"id": "routes/logout",
			"parentId": "root",
			"path": "logout",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": true,
			"hasLoader": false,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": false,
			"hasErrorBoundary": false,
			"module": "/assets/route-CiL_1sON.js",
			"imports": [],
			"css": [],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/login": {
			"id": "routes/login",
			"parentId": "root",
			"path": "login",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": true,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-ExAS8uM7.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/warning-CmTaXbvT.js",
				"/assets/Form-DOpuqsbh.js",
				"/assets/react-dom-DCZYeO_j.js"
			],
			"css": ["/assets/route-BeNxuwTu.css"],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/queue": {
			"id": "routes/queue",
			"parentId": "root",
			"path": "queue",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-COGAwj6y.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/confirm-modal-QYMWgrle.js",
				"/assets/item-menu-Cfh9B9In.js",
				"/assets/status-badge-BVNHzYyM.js",
				"/assets/styling-C_P8q9X5.js",
				"/assets/websocket-util-C6p0U1U9.js",
				"/assets/warning-CmTaXbvT.js",
				"/assets/Form-DOpuqsbh.js",
				"/assets/hasClass-7uw8q3Dv.js",
				"/assets/react-dom-DCZYeO_j.js"
			],
			"css": [
				"/assets/route-DWOU0Sf5.css",
				"/assets/confirm-modal-CqYDgXji.css",
				"/assets/item-menu-CeWpvaMA.css",
				"/assets/status-badge-DWI4dgjY.css"
			],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		},
		"routes/explore/route": {
			"id": "routes/explore/route",
			"parentId": "root",
			"path": "/explore/*",
			"index": void 0,
			"caseSensitive": void 0,
			"hasAction": false,
			"hasLoader": true,
			"hasClientAction": false,
			"hasClientLoader": false,
			"hasClientMiddleware": false,
			"hasDefaultExport": true,
			"hasErrorBoundary": false,
			"module": "/assets/route-CfElXK84.js",
			"imports": [
				"/assets/jsx-runtime-BciRPUQx.js",
				"/assets/loading-B5ND0RTV.js",
				"/assets/item-menu-Cfh9B9In.js",
				"/assets/styling-C_P8q9X5.js"
			],
			"css": [
				"/assets/route--eEQ9df3.css",
				"/assets/loading-DMS5IieH.css",
				"/assets/item-menu-CeWpvaMA.css"
			],
			"clientActionModule": void 0,
			"clientLoaderModule": void 0,
			"clientMiddlewareModule": void 0,
			"hydrateFallbackModule": void 0
		}
	},
	"url": "/assets/manifest-9963c62f.js",
	"version": "9963c62f",
	"sri": void 0
};
//#endregion
//#region \0virtual:react-router/server-build
var assetsBuildDirectory = "build/client";
var basename = "/";
var future = {
	"unstable_optimizeDeps": false,
	"unstable_passThroughRequests": false,
	"unstable_subResourceIntegrity": false,
	"unstable_trailingSlashAwareDataRequests": false,
	"unstable_previewServerPrerendering": false,
	"v8_middleware": false,
	"v8_splitRouteModules": false,
	"v8_viteEnvironmentApi": false
};
var ssr = true;
var isSpaMode = false;
var prerender = [];
var routeDiscovery = {
	"mode": "lazy",
	"manifestPath": "/__manifest"
};
var publicPath = "/";
var entry = { module: entry_server_node_exports };
var routes = {
	"root": {
		id: "root",
		parentId: void 0,
		path: "",
		index: void 0,
		caseSensitive: void 0,
		module: root_exports
	},
	"routes/onboarding": {
		id: "routes/onboarding",
		parentId: "root",
		path: "onboarding",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$8
	},
	"routes/settings": {
		id: "routes/settings",
		parentId: "root",
		path: "settings",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$7
	},
	"routes/settings.update": {
		id: "routes/settings.update",
		parentId: "routes/settings",
		path: "update",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$6
	},
	"routes/explore": {
		id: "routes/explore",
		parentId: "root",
		path: "explore",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$5
	},
	"routes/_index": {
		id: "routes/_index",
		parentId: "root",
		path: void 0,
		index: true,
		caseSensitive: void 0,
		module: route_exports$4
	},
	"routes/health": {
		id: "routes/health",
		parentId: "root",
		path: "health",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$3
	},
	"routes/logout": {
		id: "routes/logout",
		parentId: "root",
		path: "logout",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$2
	},
	"routes/login": {
		id: "routes/login",
		parentId: "root",
		path: "login",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$1
	},
	"routes/queue": {
		id: "routes/queue",
		parentId: "root",
		path: "queue",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports
	},
	"routes/explore/route": {
		id: "routes/explore/route",
		parentId: "root",
		path: "/explore/*",
		index: void 0,
		caseSensitive: void 0,
		module: route_exports$5
	}
};
var allowedActionOrigins = false;
//#endregion
export { allowedActionOrigins, server_manifest_default as assets, assetsBuildDirectory, basename, entry, future, isSpaMode, prerender, publicPath, routeDiscovery, routes, ssr };
