"use client"; import { SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionHeader, SettingsSectionTitle } from "@app/components/Settings"; import { Button } from "@app/components/ui/button"; import { cn } from "@app/lib/cn"; import { launcherQueries } from "@app/lib/queries"; import { useQuery } from "@tanstack/react-query"; import { Loader2 } from "lucide-react"; import { useTranslations } from "next-intl"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; const COLLAPSED_ROWS = 5; const GRID_COLUMNS = 2; type LauncherInferenceModelsSectionProps = { orgId: string; params: | { resourceType: "public"; resourceId: number; } | { resourceType: "site"; siteResourceId: number; }; }; export function LauncherInferenceModelsSection({ orgId, params }: LauncherInferenceModelsSectionProps) { const t = useTranslations(); const { data, isPending, isError } = useQuery( launcherQueries.aiModels(orgId, params) ); const models = data?.models ?? []; const [listExpanded, setListExpanded] = useState(false); const [clipHeight, setClipHeight] = useState(null); const gridRef = useRef(null); const collapsedLimit = GRID_COLUMNS * COLLAPSED_ROWS; const hasOverflow = models.length > collapsedLimit; const isCollapsed = hasOverflow && !listExpanded; useEffect(() => { if (!hasOverflow) { setListExpanded(false); } }, [hasOverflow]); useLayoutEffect(() => { if (!isCollapsed || !gridRef.current) { setClipHeight(null); return; } const children = Array.from(gridRef.current.children) as HTMLElement[]; const lastVisible = children[collapsedLimit - 1]; if (!lastVisible) { setClipHeight(null); return; } const gridTop = gridRef.current.getBoundingClientRect().top; const cardBottom = lastVisible.getBoundingClientRect().bottom; // Peek slightly into the next row so the fade has content to soften. setClipHeight(cardBottom - gridTop + 12); }, [isCollapsed, collapsedLimit, models]); return ( {t("resourceLauncherAvailableModels")} {t("resourceLauncherAvailableModelsDescription")} {isPending ? (
) : null} {isError ? (

{t("resourceLauncherAvailableModelsError")}

) : null} {!isPending && !isError && models.length === 0 ? (

{t("resourceLauncherAvailableModelsEmpty")}

) : null} {!isPending && !isError && models.length > 0 ? (
{models.map((model) => (
{model.modelKey} {model.providerName ? ( {model.providerName} ) : null}
))}
{isCollapsed ? (
) : null}
{isCollapsed ? (
) : null} {hasOverflow && listExpanded ? (
) : null}
) : null} ); }