Skip to content
Closed
2 changes: 2 additions & 0 deletions studio/frontend/src/app/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0

import { Navbar } from "@/components/navbar";
import { VersionFooter } from "@/components/version-footer";
import { usePlatformStore } from "@/config/env";
import {
Outlet,
Expand Down Expand Up @@ -53,6 +54,7 @@ function RootLayout() {
</Suspense>
</motion.div>
</AnimatePresence>
<VersionFooter />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reserve space for the fixed footer on non-chat routes

Rendering VersionFooter at the root adds a fixed overlay to every page, but only ChatPage was updated to make room for it. Other routed screens (for example StudioPage/ExportPage) still use full-height layouts without bottom padding, so their last interactive content can be covered by the footer and become hard to read/click when scrolled to the end. Please add a shared bottom inset for routed content (or scope the footer to pages that already reserve space).

Useful? React with 👍 / 👎.

</AppProvider>
);
}
51 changes: 51 additions & 0 deletions studio/frontend/src/components/version-footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0

import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";

interface HardwareInfo {
gpu: {
gpu_name: string | null;
vram_total_gb: number | null;
vram_free_gb: number | null;
};
versions: {
unsloth: string | null;
torch: string | null;
transformers: string | null;
cuda: string | null;
};
}

export function VersionFooter({ className }: { className?: string }) {
const [version, setVersion] = useState<string | null>(null);

useEffect(() => {
fetch("/api/system/hardware")
.then((response) => {
if (!response.ok) throw new Error("Failed to fetch");
return response.json();
})
.then((data: HardwareInfo) => setVersion(data.versions.unsloth))
.catch(() => {});
}, []);
Comment on lines +4 to +32

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for fetching hardware information and the HardwareInfo interface are already implemented in the useHardwareInfo hook. Re-implementing this fetch logic leads to code duplication and bypasses the caching mechanism provided by the existing hook. Additionally, the existing hook uses authFetch, which handles authentication if required by the API.

import { useHardwareInfo } from "@/hooks/use-hardware-info";
import { cn } from "@/lib/utils";

export function VersionFooter({ className }: { className?: string }) {
  const { unsloth: version } = useHardwareInfo();


if (!version) {
return null;
}

return (
<footer
className={cn(
"fixed bottom-0 left-0 right-0 z-10 flex items-center justify-center gap-1.5 py-2 text-sm text-muted-foreground",
className
)}
>
<span className="font-mono">v{version}</span>
<span className="font-extrabold tracking-[0.12em] text-primary">
BETA
</span>
</footer>
);
}
2 changes: 1 addition & 1 deletion studio/frontend/src/features/chat/chat-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ export function ChatPage(): ReactElement {
}, [modelSelectorLocked, tour.open]);

return (
<div className="h-[calc(100dvh-4rem)] bg-background overflow-hidden">
<div className="h-[calc(100dvh-6rem)] bg-background overflow-hidden">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Reducing the height of the container to 100dvh - 6rem to accommodate a fixed footer is a fragile approach. This creates a tight coupling between the page layout and the footer's height. If the footer is intended to be global, it would be better to include it in the main layout flow (e.g., using a flexbox container in __root.tsx) rather than using fixed positioning and manual height offsets in individual pages.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Avoid shrinking chat viewport when footer is not shown

The chat container now always subtracts an extra 2rem (100dvh-6rem), but the footer is conditional and returns null whenever the version fetch fails or versions.unsloth is missing. In those cases the footer is absent while chat still loses vertical space for the whole session, which unnecessarily reduces message/composer room. The height adjustment should be tied to actual footer visibility.

Useful? React with 👍 / 👎.

<GuidedTour {...tour.tourProps} />
<SidebarProvider
defaultOpen={true}
Expand Down
Loading