import { useState } from "react";
import { Link } from "react-router";
import { useSWRConfig } from "swr";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { ChevronDownIcon, PlusIcon } from "@heroicons/react/16/solid";
import { EllipsisVerticalIcon } from "@heroicons/react/20/solid";
import type { Environment } from "@qltysh/fabro-api-client";
import { ApiError, apiData, environmentsApi } from "../lib/api-client";
import { useEnvironments, useServerSettings } from "../lib/queries";
import { queryKeys } from "../lib/query-keys";
import { CREATABLE_PROVIDERS } from "../components/environment-form";
import {
Badge,
Muted,
Panel,
PanelSkeleton,
SettingsPageIntro,
} from "../components/settings-panel";
import { ConfirmDialog } from "../components/ui";
import { useToast } from "../components/toast";
// `local` is a reserved, in-memory environment the server includes only when
// the local sandbox provider is enabled. It has no configurable settings, so it
// gets its own panel instead of a row in the managed environments list.
const RESERVED_ID = "local";
const MENU_ITEM_CLASS =
"flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-fg-3 transition-colors data-focus:bg-overlay data-focus:text-fg data-focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-60";
const MENU_ITEM_DANGER_CLASS =
"flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-coral transition-colors data-focus:bg-coral/10 data-focus:text-coral data-focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-60";
export function meta() {
return [{ title: "Environments — Fabro" }];
}
const DESCRIPTION =
"Environments are server-managed runtime definitions — provider, image, resources, network, and lifecycle — that workflow runs select by id. They are operator policy stored on this Fabro server.";
export default function SettingsEnvironments() {
const query = useEnvironments();
return (
} />
{query.data ? (
) : query.error ? (
Couldn't load environments. Please try again.
) : (
)}
);
}
const NEW_BUTTON_CLASS =
"inline-flex items-center gap-1.5 rounded-md border border-line bg-panel/80 px-2.5 py-1 text-sm font-medium text-fg-3 transition-colors hover:border-line-strong hover:bg-panel hover:text-fg disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:border-line disabled:hover:bg-panel/80 disabled:hover:text-fg-3";
function providerLabel(provider: string): string {
return provider.charAt(0).toUpperCase() + provider.slice(1);
}
// "New environment" is a provider picker: each enabled sandbox provider opens
// the create form pre-set to that provider, which is then fixed for the
// environment's lifetime. `local` is never offered (it's reserved/in-memory).
function NewEnvironmentMenu() {
const { data } = useServerSettings();
const providers = data
? CREATABLE_PROVIDERS.filter((provider) => data.server.sandbox.providers[provider].enabled)
: [];
if (providers.length === 0) {
return (
);
}
return (
);
}
function EnvironmentsContent({ environments }: { environments: Environment[] }) {
const local = environments.find((environment) => environment.id === RESERVED_ID);
const managed = environments.filter((environment) => environment.id !== RESERVED_ID);
return (
<>
{local ? : null}
>
);
}
function LocalEnvironmentPanel({ environment }: { environment: Environment }) {
return (
{environment.id}{environment.provider}
Built-in environment that runs tools directly on the host. Available because the local
sandbox provider is enabled; it has no configurable settings and can't be edited or
deleted.