hide shutdown button in Docker containers

This commit is contained in:
Roland Tannous 2026-03-27 20:39:17 +00:00
commit 0ee2c3c841
3 changed files with 33 additions and 22 deletions

View file

@ -176,12 +176,15 @@ async def health_check():
platform_map = {"darwin": "mac", "win32": "windows", "linux": "linux"}
device_type = platform_map.get(sys.platform, sys.platform)
is_docker = os.path.exists("/.dockerenv")
return {
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"service": "Unsloth UI Backend",
"device_type": device_type,
"chat_only": _hw_module.CHAT_ONLY,
"is_docker": is_docker,
}

View file

@ -55,6 +55,7 @@ export function Navbar() {
const [shutdownOpen, setShutdownOpen] = useState(false);
const chatOnly = usePlatformStore((s) => s.isChatOnly());
const isDocker = usePlatformStore((s) => s.isDocker);
// Warn before closing the tab only when training is running (data loss risk).
// We store the handler in a ref so removeUnloadHandler() can clean it up
@ -236,15 +237,17 @@ export function Navbar() {
<span className="text-sm font-medium">Tour</span>
</button>
<button
type="button"
onClick={() => setShutdownOpen(true)}
className="-mr-1.5 flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
title="Shut down Unsloth Studio server"
aria-label="Shut down Unsloth Studio server"
>
<HugeiconsIcon icon={Cancel01Icon} className="size-5" />
</button>
{!isDocker && (
<button
type="button"
onClick={() => setShutdownOpen(true)}
className="-mr-1.5 flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
title="Shut down Unsloth Studio server"
aria-label="Shut down Unsloth Studio server"
>
<HugeiconsIcon icon={Cancel01Icon} className="size-5" />
</button>
)}
</div>
{/* Right: mobile */}
@ -331,17 +334,19 @@ export function Navbar() {
Start tour
</button>
) : null}
<button
type="button"
className="flex items-center gap-2 rounded-md border border-border px-3 py-2 text-left text-sm font-medium text-foreground hover:bg-accent"
onClick={() => {
setMobileOpen(false);
setShutdownOpen(true);
}}
>
<HugeiconsIcon icon={Cancel01Icon} className="size-5" />
Quit Unsloth Studio
</button>
{!isDocker && (
<button
type="button"
className="flex items-center gap-2 rounded-md border border-border px-3 py-2 text-left text-sm font-medium text-foreground hover:bg-accent"
onClick={() => {
setMobileOpen(false);
setShutdownOpen(true);
}}
>
<HugeiconsIcon icon={Cancel01Icon} className="size-5" />
Quit Unsloth Studio
</button>
)}
<div className="mt-2 flex items-center justify-between rounded-md border border-border px-3 py-2">
<span className="text-sm font-medium text-foreground">Theme</span>
<AnimatedThemeToggler

View file

@ -17,6 +17,7 @@ export type DeviceType = "mac" | "windows" | "linux" | string;
interface PlatformState {
deviceType: DeviceType;
chatOnly: boolean;
isDocker: boolean;
fetched: boolean;
isChatOnly: () => boolean;
}
@ -24,6 +25,7 @@ interface PlatformState {
export const usePlatformStore = create<PlatformState>()((_, get) => ({
deviceType: "linux",
chatOnly: false,
isDocker: false,
fetched: false,
isChatOnly: () => get().chatOnly,
}));
@ -35,10 +37,11 @@ export async function fetchDeviceType(): Promise<DeviceType> {
try {
const res = await fetch("/api/health");
if (res.ok) {
const data = (await res.json()) as { device_type?: string; chat_only?: boolean };
const data = (await res.json()) as { device_type?: string; chat_only?: boolean; is_docker?: boolean };
const deviceType = data.device_type ?? "linux";
const chatOnly = data.chat_only ?? deviceType === "mac";
usePlatformStore.setState({ deviceType, chatOnly, fetched: true });
const isDocker = data.is_docker ?? false;
usePlatformStore.setState({ deviceType, chatOnly, isDocker, fetched: true });
return deviceType;
}
} catch (err) {