feat(frontend): add platform icons and improve download button UX

Replace generic download icon with platform-specific icons (Windows,
macOS, Linux) using react-icons. Show detected platform name in the
main download button text and disable auto-download when platform
cannot be detected, requiring manual selection from dropdown instead.
This commit is contained in:
2025-12-11 17:40:04 -06:00
parent 65aa9d66d3
commit fd474767ae
3 changed files with 57 additions and 17 deletions

View File

@@ -22,6 +22,7 @@
"clsx": "^2.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"react-tooltip": "^5.28.0",
"react-use-websocket": "^4.11.1",
"tailwind-merge": "^2.5.5",

View File

@@ -47,6 +47,9 @@ importers:
react-dom:
specifier: ^19.0.0
version: 19.0.0(react@19.0.0)
react-icons:
specifier: ^5.5.0
version: 5.5.0(react@19.0.0)
react-tooltip:
specifier: ^5.28.0
version: 5.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -2086,6 +2089,11 @@ packages:
peerDependencies:
react: ^19.0.0
react-icons@5.5.0:
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
peerDependencies:
react: '*'
react-refresh@0.14.2:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
@@ -5086,6 +5094,10 @@ snapshots:
react: 19.0.0
scheduler: 0.25.0
react-icons@5.5.0(react@19.0.0):
dependencies:
react: 19.0.0
react-refresh@0.14.2: {}
react-tooltip@5.28.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0):

View File

@@ -9,10 +9,10 @@ import {
MenuSeparator,
} from "@headlessui/react";
import {
ArrowDownTrayIcon,
BeakerIcon,
ChevronDownIcon,
} from "@heroicons/react/16/solid";
import { FaWindows, FaApple, FaLinux } from "react-icons/fa";
import { useRef } from "react";
type DownloadButtonProps = {
@@ -36,6 +36,34 @@ function getSystemType(): SystemType | null {
}
}
function getPlatformIcon(id: string, className?: string) {
const platformId = id.toLowerCase();
switch (platformId) {
case "windows":
return <FaWindows className={className} />;
case "macos":
return <FaApple className={className} />;
case "linux":
return <FaLinux className={className} />;
default:
return null;
}
}
function getPlatformDisplayName(id: string): string {
const platformId = id.toLowerCase();
switch (platformId) {
case "windows":
return "Windows";
case "macos":
return "macOS";
case "linux":
return "Linux";
default:
return id;
}
}
export default function DownloadButton({
disabled,
executables,
@@ -47,6 +75,10 @@ export default function DownloadButton({
return executables?.find((e) => e.id.toLowerCase() === id.toLowerCase());
}
const detectedPlatform = getSystemType();
const platformExecutable = detectedPlatform ? getExecutable(detectedPlatform) : null;
const canAutoDownload = platformExecutable != null;
async function handleDownload(id: string) {
const executable = getExecutable(id);
if (executable == null) {
@@ -59,16 +91,8 @@ export default function DownloadButton({
}
function handleDownloadAutomatic() {
const systemType = getSystemType();
// If the system type is unknown/unavailable, open the menu for manual selection
if (systemType == null || getExecutable(systemType) == null) {
menuRef.current?.click();
}
// Otherwise, download the executable automatically
else {
handleDownload(systemType);
if (canAutoDownload && detectedPlatform) {
handleDownload(detectedPlatform);
}
}
@@ -83,14 +107,17 @@ export default function DownloadButton({
)}
>
<Button
onClick={handleDownloadAutomatic}
onClick={canAutoDownload ? handleDownloadAutomatic : undefined}
suppressHydrationWarning
disabled={disabled}
disabled={disabled || !canAutoDownload}
className={cn("pl-3 font-semibold pr-2.5", {
"hover:bg-white/5": !disabled,
"hover:bg-white/5 cursor-pointer": !disabled && canAutoDownload,
"cursor-default": !canAutoDownload,
})}
>
Download
{canAutoDownload && detectedPlatform
? `Download for ${getPlatformDisplayName(detectedPlatform)}`
: "Download"}
</Button>
<Menu>
<MenuButton
@@ -115,8 +142,8 @@ export default function DownloadButton({
onClick={() => handleDownload(executable.id)}
>
<div className="flex items-center gap-1.5">
<ArrowDownTrayIcon className="size-4 fill-white/40" />
{executable.id}
{getPlatformIcon(executable.id, "size-4 fill-white/40")}
{getPlatformDisplayName(executable.id)}
</div>
<div className="text-xs text-zinc-500">
{(executable.size / 1024 / 1024).toFixed(1)} MiB