import {
ENVIRONMENT_MACHINE_KINDS,
isEnvironmentMachineKind,
resolveEnvironmentMachineKind,
type EnvironmentId,
type ServerConfig,
} from "@t3tools/contracts";
import { useCallback } from "react";
import { isElectron } from "../../env";
import { usePrimarySessionState } from "../../environments/primary";
import { useUpdateEnvironmentSettings } from "../../hooks/useSettings";
import { usePrimaryEnvironmentId } from "../../state/environments";
import { useEnvironmentSessionState } from "../../state/session";
import { ENVIRONMENT_MACHINE_KIND_LABELS, EnvironmentMachineIcon } from "../EnvironmentMachineIcon";
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
import {
resolvePrimaryOperateAccess,
resolveRemoteOperateAccess,
} from "./ProviderSettingsPanel.logic";
const AUTOMATIC_VALUE = "automatic";
/**
* Why the picker is inert, in the order the user can do something about it.
* Null means it can be changed.
*/
export function resolveEnvironmentIconPickerLock(input: {
readonly serverConfig: ServerConfig | null;
readonly operateAccess: "granted" | "denied" | "pending";
}): string | null {
if (input.serverConfig === null) {
return "Connect to this environment to change its icon.";
}
if (input.serverConfig.environment.capabilities.environmentIcon !== true) {
return "This environment's server is too old to keep an icon. Update it to choose one.";
}
if (input.operateAccess === "denied") {
return "Your session on this environment cannot change its settings.";
}
return null;
}
// Same split the provider settings use: the desktop app owns its primary
// server outright, a browser session on the primary checks its cookie
// session's scopes, and a remote checks the scopes its own server reports.
function useEnvironmentOperateAccess(environmentId: EnvironmentId) {
const isPrimary = usePrimaryEnvironmentId() === environmentId;
const primarySession = usePrimarySessionState();
const remoteSession = useEnvironmentSessionState(environmentId);
if (isPrimary) {
return isElectron
? "granted"
: resolvePrimaryOperateAccess({
isPrimary: true,
hasDesktopBridge: false,
session: primarySession.data,
isPending: primarySession.isPending,
hasError: primarySession.error !== null,
});
}
return resolveRemoteOperateAccess({
session: remoteSession.data,
isPending: remoteSession.isPending,
hasError: remoteSession.hasError,
});
}
/**
* Picks the machine glyph an environment wears everywhere it is listed.
* "Automatic" clears the override so the server's own detection shows
* through; the label says what that currently resolves to so the user can
* tell whether detection got it right before overriding. The control stays
* visible while locked so the current icon still reads, the same way
* server-scoped rows go inert instead of disappearing.
*/
export function EnvironmentIconPicker({
environmentId,
serverConfig,
size = "sm",
}: {
readonly environmentId: EnvironmentId;
readonly serverConfig: ServerConfig | null;
readonly size?: "xs" | "sm";
}) {
const updateSettings = useUpdateEnvironmentSettings(environmentId);
const operateAccess = useEnvironmentOperateAccess(environmentId);
const lock = resolveEnvironmentIconPickerLock({ serverConfig, operateAccess });
const override = serverConfig?.settings.environmentIcon ?? null;
const detected = serverConfig?.environment.platform.machine ?? null;
const resolved = resolveEnvironmentMachineKind(serverConfig);
const value = override ?? AUTOMATIC_VALUE;
const automaticLabel =
detected === null ? "Automatic" : `Automatic (${ENVIRONMENT_MACHINE_KIND_LABELS[detected]})`;
const handleValueChange = useCallback(
(next: string | null) => {
if (next === null) return;
if (next === AUTOMATIC_VALUE) {
updateSettings({ environmentIcon: null });
} else if (isEnvironmentMachineKind(next)) {
updateSettings({ environmentIcon: next });
}
},
[updateSettings],
);
const select = (
);
if (lock === null) {
return select;
}
return (
}
>
{select}
{lock}
);
}