Skip to content
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions desktop/src/features/onboarding/ui/RuntimeIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as React from "react";
import { TerminalSquare } from "lucide-react";

import type { AcpRuntimeCatalogEntry } from "@/shared/api/types";
import { cn } from "@/shared/lib/cn";
import { useTheme } from "@/shared/theme/ThemeProvider";
import { BuzzMark } from "@/shared/ui/buzz-logo/BuzzMark";
import chatgptLogoUrl from "../assets/harness-logos/chatgpt.png?inline";
import claudeLogoUrl from "../assets/harness-logos/claude.png?inline";
import gooseLogoUrl from "../assets/harness-logos/goose.png?inline";

const RUNTIME_LOGOS: Record<string, string> = {
claude: claudeLogoUrl,
codex: chatgptLogoUrl,
goose: gooseLogoUrl,
};

function isBuzzRuntime(runtime: AcpRuntimeCatalogEntry): boolean {
return runtime.id.trim().toLowerCase() === "buzz-agent";
}

export function getRuntimeDisplayLabel(
runtime: AcpRuntimeCatalogEntry,
): string {
return isBuzzRuntime(runtime) ? "Buzz" : runtime.label;
}

function getRuntimeLogoUrl(runtime: AcpRuntimeCatalogEntry): string | null {
return RUNTIME_LOGOS[runtime.id.trim().toLowerCase()] ?? null;
}

export function RuntimeIcon({
className = "h-8 w-8",
runtime,
}: {
className?: string;
runtime: AcpRuntimeCatalogEntry;
}) {
const [imageFailed, setImageFailed] = React.useState(false);
const { isDark } = useTheme();
const runtimeLogoUrl = getRuntimeLogoUrl(runtime);
const imageUrl = runtimeLogoUrl ?? runtime.avatarUrl;
const shouldForceForegroundColor = !runtimeLogoUrl && runtime.id === "goose";

if (isBuzzRuntime(runtime)) {
return <BuzzMark className="h-7 w-10 text-foreground" />;
}

if (imageUrl && !imageFailed) {
return (
<img
alt=""
className={cn(
"rounded-md object-contain",
className,
shouldForceForegroundColor &&
(isDark ? "brightness-0 invert" : "brightness-0"),
)}
onError={() => setImageFailed(true)}
src={imageUrl}
/>
);
}

return (
<TerminalSquare
className={cn(className, "text-foreground")}
strokeWidth={1.25}
/>
);
}
43 changes: 4 additions & 39 deletions desktop/src/features/onboarding/ui/SetupStep.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import * as React from "react";
import { openUrl } from "@tauri-apps/plugin-opener";
import {
AlertTriangle,
Check,
ExternalLink,
Info,
Plus,
TerminalSquare,
} from "lucide-react";
import { AlertTriangle, Check, ExternalLink, Info, Plus } from "lucide-react";

import {
useAcpAuthMethodsQuery,
Expand All @@ -20,12 +13,12 @@ import { describeResolvedCommand } from "@/features/agents/ui/agentUi";
import type { AcpRuntimeCatalogEntry } from "@/shared/api/types";
import { getInstallErrorMessage } from "@/shared/lib/installError";
import { cn } from "@/shared/lib/cn";
import { useTheme } from "@/shared/theme/ThemeProvider";
import { Badge } from "@/shared/ui/badge";
import { Button } from "@/shared/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/ui/popover";
import { Spinner } from "@/shared/ui/spinner";
import { runtimeCanBeSelected } from "./onboardingRuntimeSelection";
import { getRuntimeDisplayLabel, RuntimeIcon } from "./RuntimeIcon";
import { ONBOARDING_PRIMARY_CTA_CLASS } from "./OnboardingChrome";
import { OnboardingFooter } from "./OnboardingFooter";
import {
Expand Down Expand Up @@ -74,34 +67,6 @@ function useSetupStepState(): SetupStepState {
};
}

function RuntimeIcon({ runtime }: { runtime: AcpRuntimeCatalogEntry }) {
const [imageFailed, setImageFailed] = React.useState(false);
const { isDark } = useTheme();
const shouldForceForegroundColor = runtime.id === "goose";

if (runtime.avatarUrl && !imageFailed) {
return (
<img
alt=""
className={cn(
"h-12 w-12 rounded-md object-contain",
shouldForceForegroundColor &&
(isDark ? "brightness-0 invert" : "brightness-0"),
)}
onError={() => setImageFailed(true)}
src={runtime.avatarUrl}
/>
);
}

return (
<TerminalSquare
className="h-12 w-12 text-muted-foreground"
strokeWidth={1.25}
/>
);
}

function RuntimeStatus({
installError,
isInstalling,
Expand Down Expand Up @@ -491,11 +456,11 @@ function RuntimeCard({
</Popover>
</div>

<RuntimeIcon runtime={runtime} />
<RuntimeIcon className="h-8 w-8" runtime={runtime} />

<div className="min-w-0">
<h2 className="text-sm font-medium leading-5 text-foreground">
{runtime.label}
{getRuntimeDisplayLabel(runtime)}
</h2>
{!isAvailable && !installError ? (
<p className="mt-1 text-2xs leading-4 text-muted-foreground">
Expand Down
Loading