/* Copyright 2026 Marimo. All rights reserved. */
import { atom, useAtomValue, useSetAtom } from "jotai";
import React from "react";
import { useOpenSettingsToTab } from "@/components/app-config/state";
import { GitHubCopilotIcon } from "@/components/icons/github-copilot";
import { Spinner } from "@/components/icons/spinner";
import { Button } from "@/components/ui/button";
import { toast } from "@/components/ui/use-toast";
import { getCopilotClient } from "@/core/codemirror/copilot/client";
import {
copilotSignedInState,
copilotStatusState,
githubCopilotLoadingVersion,
isGitHubCopilotSignedInState,
} from "@/core/codemirror/copilot/state";
import { resolvedMarimoConfigAtom } from "@/core/config/config";
import { useOnMount } from "@/hooks/useLifecycle";
import { cn } from "@/utils/cn";
import { prettyError } from "@/utils/errors";
import { Logger } from "@/utils/Logger";
import { FooterItem } from "../footer-item";
const copilotAtom = atom((get) => {
return get(resolvedMarimoConfigAtom).completion.copilot;
});
export const CopilotStatusIcon: React.FC = () => {
const copilot = useAtomValue(copilotAtom);
// We only show an icon for GitHub Copilot, but not for other copilot providers,
// this can be extended in the future
if (copilot === "github") {
return ;
}
return null;
};
const logger = Logger.get("[copilot-status-bar]");
const GitHubCopilotStatus: React.FC = () => {
const isGitHubCopilotSignedIn = useAtomValue(isGitHubCopilotSignedInState);
const isLoading = useAtomValue(githubCopilotLoadingVersion) !== null;
const status = useAtomValue(copilotStatusState);
const { handleClick } = useOpenSettingsToTab();
const openSettings = () => handleClick("ai");
// Build label from status
let label = isGitHubCopilotSignedIn ? "Ready" : "Not connected";
if (status.message) {
label = status.message;
} else if (status.busy) {
label = "Processing...";
}
const setCopilotSignedIn = useSetAtom(isGitHubCopilotSignedInState);
const setStep = useSetAtom(copilotSignedInState);
// Check connection on mount
useOnMount(() => {
const client = getCopilotClient();
let mounted = true;
const checkConnection = async () => {
try {
// If we fail to initialize, show connection error
await client.initializePromise.catch((error) => {
logger.error("Failed to initialize", error);
client.close();
throw error;
});
if (!mounted) {
return;
}
const signedIn = await client.signedIn();
if (!mounted) {
return;
}
setCopilotSignedIn(signedIn);
setStep(signedIn ? "signedIn" : "signedOut");
} catch (error) {
if (!mounted) {
return;
}
logger.warn("Connection failed", error);
setCopilotSignedIn(false);
setStep("connectionError");
toast({
title: "GitHub Copilot Connection Error",
description: (
<>
{" "}
Failed to connect to GitHub Copilot. Check settings and try
again.
{prettyError(error)}
>
),
variant: "danger",
action: (
),
});
}
};
checkConnection();
return () => {
mounted = false;
};
});
// Determine icon color based on status
const iconColorClass =
status.kind === "Warning" || status.kind === "Error"
? "text-(--yellow-11)"
: isGitHubCopilotSignedIn
? ""
: "opacity-60";
return (
GitHub Copilot: {label}
{status.kind && (
<>
Status: {status.kind}
>
)}
}
selected={false}
onClick={openSettings}
data-testid="footer-copilot-status"
>
{isLoading || status.busy ? (
) : (
)}
);
};