diff --git a/ACCESS_DISCLOSURE.md b/ACCESS_DISCLOSURE.md
new file mode 100644
index 00000000..9ac096e1
--- /dev/null
+++ b/ACCESS_DISCLOSURE.md
@@ -0,0 +1,25 @@
+# Termix Access Disclosure
+
+Last updated: July 27, 2026
+
+Termix accesses resources only when required by a configured feature or an action initiated by a user.
+
+## Configured systems
+
+Termix can connect to hosts and services you add, including SSH, Telnet, RDP, VNC, Docker, Proxmox, serial devices, tunnels, monitoring endpoints, and remote Termix servers.
+
+## Credentials and sessions
+
+Termix can use saved usernames, passwords, private keys, certificates, tokens, cookies, and session metadata to authenticate and maintain requested connections. Administrators should restrict instance access and review stored credentials, roles, API keys, active sessions, and shares.
+
+## Files, clipboard, and local data
+
+When requested, Termix can read or write remote files, transfer files, use clipboard data, import or export database backups, and store browser or desktop preferences. Operating systems and browsers may display additional permission prompts.
+
+## Optional integrations
+
+Termix contacts external services only when the related feature is enabled or configured. These can include SSO identity providers, ACME certificate authorities, Tailscale, webhooks, update services, analytics, and remote synchronization servers.
+
+## Analytics
+
+Optional analytics can be disabled in Admin Settings. Analytics events are intended to describe product usage and technical state. They should not intentionally include credentials, terminal input, or file contents.
diff --git a/TERMS_OF_USE.md b/TERMS_OF_USE.md
new file mode 100644
index 00000000..5fecbc3b
--- /dev/null
+++ b/TERMS_OF_USE.md
@@ -0,0 +1,25 @@
+# Termix Terms of Use
+
+Last updated: July 27, 2026
+
+By using Termix, you agree to use it only with systems, accounts, networks, and data that you are authorized to access.
+
+## Authorized use
+
+You are responsible for obtaining permission before connecting to a host, opening a remote file, operating a service, or sharing a session. You must not use Termix to disrupt services, bypass access controls, violate privacy, or perform unlawful activity.
+
+## Account and credential security
+
+You are responsible for protecting access to your Termix instance and for securing passwords, private keys, tokens, API keys, database backups, and exported data. Review user roles, active sessions, shared sessions, and configured integrations regularly.
+
+## Data and backups
+
+Termix stores the data required to provide the features you configure. You are responsible for determining whether that storage is appropriate for your organization and jurisdiction. Maintain tested backups before relying on Termix for critical operations.
+
+## Availability
+
+Termix is provided without a service availability guarantee. Features that depend on remote hosts, browsers, operating systems, or third-party services may be unavailable or behave differently as those systems change.
+
+## Changes
+
+These terms may change as Termix gains new capabilities. Material changes should be reviewed before deploying a new release.
diff --git a/src/ui/AppShell.tsx b/src/ui/AppShell.tsx
index 2729ce48..b3f0fb6b 100644
--- a/src/ui/AppShell.tsx
+++ b/src/ui/AppShell.tsx
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import { Separator } from "@/components/separator";
import { Button } from "@/components/button";
import { Sheet, SheetContent } from "@/components/sheet";
-import { ChevronLeft, ChevronRight, Maximize2 } from "lucide-react";
+import { ChevronLeft, ChevronRight, Maximize2, Minimize2 } from "lucide-react";
import {
useState,
useRef,
@@ -125,6 +125,7 @@ import {
createSSHHost,
getActiveSessions,
getUserPreferences,
+ saveUserPreferences,
dismissDonationModal,
isElectron,
type UserPreferences,
@@ -139,7 +140,7 @@ import { ServerStatusProvider } from "@/lib/ServerStatusContext";
import { TransferMonitor } from "@/features/file-manager/TransferMonitor.tsx";
import { sshHostToHost } from "@/sidebar/HostManagerData";
import { resolveHostTabType } from "@/lib/host-connection-tabs";
-import { changeAppLanguage } from "@/i18n/i18n";
+import { changeAppLanguage, consumeLoginLanguage } from "@/i18n/i18n";
import { quickConnectHostToPayload } from "@/sidebar/quick-connect-host";
function buildHostTree(
@@ -263,6 +264,7 @@ export function AppShell({
});
const [sidebarDragging, setSidebarDragging] = useState(false);
const [sidebarEditing, setSidebarEditing] = useState(false);
+ const [settingsFullscreen, setSettingsFullscreen] = useState(false);
const [isAppFullscreen, setIsAppFullscreen] = useState(
() => !!document.fullscreenElement,
);
@@ -287,6 +289,21 @@ export function AppShell({
}, [paneTabIds, tabs]);
const isMobile = useIsMobile();
+ const isSettingsView =
+ railView === "user-profile" || railView === "admin-settings";
+
+ useEffect(() => {
+ if (!settingsFullscreen) return;
+ const closeOnEscape = (event: KeyboardEvent) => {
+ if (event.key === "Escape") setSettingsFullscreen(false);
+ };
+ window.addEventListener("keydown", closeOnEscape);
+ return () => window.removeEventListener("keydown", closeOnEscape);
+ }, [settingsFullscreen]);
+
+ useEffect(() => {
+ if (!isSettingsView) setSettingsFullscreen(false);
+ }, [isSettingsView]);
const sidebarOpenBeforeMobile = useRef(sidebarOpen);
useEffect(() => {
@@ -691,6 +708,7 @@ export function AppShell({
useEffect(() => {
getUserPreferences()
.then((prefs) => {
+ const loginLanguage = consumeLoginLanguage();
setUserPrefs(prefs);
if (prefs.storageMode === "cloud") {
// Persist the current browser values before overwriting, so any tab can restore them
@@ -724,8 +742,12 @@ export function AppShell({
localStorage.setItem("termix-accent", prefs.accentColor);
applyAccentColor(prefs.accentColor);
}
- if (prefs.language && prefs.language !== i18n.language) {
- void changeAppLanguage(prefs.language);
+ const preferredLanguage = loginLanguage ?? prefs.language;
+ if (preferredLanguage && preferredLanguage !== i18n.language) {
+ void changeAppLanguage(preferredLanguage);
+ }
+ if (loginLanguage && loginLanguage !== prefs.language) {
+ void saveUserPreferences({ language: loginLanguage });
}
if (
prefs.commandAutocomplete !== null &&
@@ -1510,6 +1532,7 @@ export function AppShell({
setSidebarOpen(false);
} else {
if (view !== railView) setSidebarEditing(false);
+ if (view !== railView) setSettingsFullscreen(false);
setRailView(view);
setSidebarOpen(true);
}
@@ -1867,12 +1890,42 @@ export function AppShell({
>
)}
+ {isSettingsView && (
+ <>
+
+
+ >
+ )}
@@ -1904,30 +1957,38 @@ export function AppShell({
)}