From 45df407b7879494ac856b7c77b8f752eba116266 Mon Sep 17 00:00:00 2001 From: shine1i Date: Mon, 2 Feb 2026 13:16:08 +0100 Subject: [PATCH] refactor: simplify model and dataset combobox logic, remove curated items, and streamline search handling across components --- studio/frontend/src/components/ui/tooltip.tsx | 2 +- .../components/steps/dataset-step.tsx | 129 +++----------- .../components/steps/model-selection-step.tsx | 139 ++++----------- .../studio/sections/dataset-section.tsx | 158 +++--------------- .../studio/sections/model-section.tsx | 117 +++---------- .../src/hooks/use-hf-dataset-search.ts | 4 +- .../frontend/src/hooks/use-hf-model-search.ts | 29 +++- .../src/hooks/use-hf-paginated-search.ts | 19 +-- .../frontend/src/hooks/use-infinite-scroll.ts | 4 +- 9 files changed, 139 insertions(+), 462 deletions(-) diff --git a/studio/frontend/src/components/ui/tooltip.tsx b/studio/frontend/src/components/ui/tooltip.tsx index 516d2b8d5f..6da22c3b65 100644 --- a/studio/frontend/src/components/ui/tooltip.tsx +++ b/studio/frontend/src/components/ui/tooltip.tsx @@ -4,7 +4,7 @@ import type * as React from "react"; import { cn } from "@/lib/utils"; function TooltipProvider({ - delayDuration = 0, + delayDuration = 400, ...props }: React.ComponentProps) { return ( diff --git a/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx b/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx index 24f6564e66..39664f290f 100644 --- a/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx +++ b/studio/frontend/src/features/onboarding/components/steps/dataset-step.tsx @@ -32,7 +32,6 @@ import { TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; -import { DATASETS } from "@/config/training"; import { useDebouncedValue, useHfDatasetSearch, @@ -86,88 +85,24 @@ export function DatasetStep() { ); const [inputValue, setInputValue] = useState(""); + const selectingRef = useRef(false); const debouncedQuery = useDebouncedValue(inputValue); const { results: hfResults, isLoading, isLoadingMore, - hasMore, fetchMore, } = useHfDatasetSearch(debouncedQuery, { accessToken: hfToken || undefined, }); - const curatedDatasets = useMemo( - () => - [...DATASETS].sort( - (a, b) => (b.recommended ? 1 : 0) - (a.recommended ? 1 : 0), - ), - [], - ); - - const datasetMap = useMemo(() => { - const map = new Map< - string, - { - label: string; - description?: string; - size?: string; - totalExamples?: number; - sizeCategory?: string; - downloads?: number; - recommended?: boolean; - } - >(); - for (const d of curatedDatasets) { - map.set(d.id, { - label: d.name, - description: d.description, - size: d.size, - recommended: d.recommended, - }); - } - for (const r of hfResults) { - if (!map.has(r.id)) { - map.set(r.id, { - label: r.id, - downloads: r.downloads, - totalExamples: r.totalExamples, - sizeCategory: r.sizeCategory, - }); - } - } - return map; - }, [curatedDatasets, hfResults]); - - const displayIds = useMemo(() => { - if (!debouncedQuery.trim()) { - return curatedDatasets.map((d) => d.id); - } - const q = debouncedQuery.toLowerCase(); - const curatedIds = curatedDatasets - .filter( - (d) => - d.name.toLowerCase().includes(q) || d.id.toLowerCase().includes(q), - ) - .map((d) => d.id); - const liveIds = hfResults - .map((r) => r.id) - .filter((id) => !curatedIds.includes(id)); - return [...curatedIds, ...liveIds]; - }, [debouncedQuery, curatedDatasets, hfResults]); - - const allIds = useMemo( - () => [ - ...new Set([ - ...curatedDatasets.map((d) => d.id), - ...hfResults.map((r) => r.id), - ]), - ], - [curatedDatasets, hfResults], - ); + const resultIds = useMemo(() => hfResults.map((r) => r.id), [hfResults]); const comboboxAnchorRef = useRef(null); - const { scrollRef, sentinelRef } = useInfiniteScroll(fetchMore); + const { scrollRef, sentinelRef } = useInfiniteScroll( + fetchMore, + hfResults.length, + ); const handleFileUpload = () => { setUploadedFile("my_dataset.jsonl"); @@ -239,13 +174,13 @@ export function DatasetStep() { Search datasets
setDataset(id)} - onInputValueChange={(val) => setInputValue(val)} - itemToStringValue={(id) => datasetMap.get(id)?.label ?? id} + onValueChange={(id) => { selectingRef.current = true; setDataset(id); }} + onInputValueChange={(val) => { if (selectingRef.current) { selectingRef.current = false; return; } setInputValue(val); }} + itemToStringValue={(id) => id} autoHighlight={true} > {isLoading ? (
- Searching… + Searching...
) : ( No datasets found @@ -270,13 +205,10 @@ export function DatasetStep() { > {(id: string) => { - const meta = datasetMap.get(id); - const label = meta?.label ?? id; - const rowLabel = - meta?.size ?? - (meta?.totalExamples - ? `${formatCompact(meta.totalExamples)} rows` - : null); + const r = hfResults.find((r) => r.id === id); + const detail = r?.totalExamples + ? `${formatCompact(r.totalExamples)} rows` + : (r?.sizeCategory ?? null); return ( - -
- {label} - {meta?.description && ( - - {meta.description} - - )} -
+ + + {id} + - {label} + {id}
- {rowLabel ? ( - - {rowLabel} - - ) : meta?.sizeCategory ? ( + {detail ? ( - {meta.sizeCategory} + {detail} - ) : meta?.downloads != null ? ( + ) : r?.downloads != null ? ( - ↓{formatCompact(meta.downloads)} + ↓{formatCompact(r.downloads)} ) : null}
); }}
- {hasMore &&
} +
{isLoadingMore && (
diff --git a/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx b/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx index dd71ebc219..7f8346f2ff 100644 --- a/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx +++ b/studio/frontend/src/features/onboarding/components/steps/model-selection-step.tsx @@ -1,4 +1,3 @@ -import { Badge } from "@/components/ui/badge"; import { Combobox, ComboboxContent, @@ -31,7 +30,7 @@ import { TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; -import { MODELS, MODEL_TYPE_TO_HF_TASK } from "@/config/training"; +import { MODEL_TYPE_TO_HF_TASK } from "@/config/training"; import { useDebouncedValue, useHfModelSearch, @@ -71,92 +70,26 @@ export function ModelSelectionStep() { ); const [inputValue, setInputValue] = useState(""); + const selectingRef = useRef(false); const debouncedQuery = useDebouncedValue(inputValue); const task = modelType ? MODEL_TYPE_TO_HF_TASK[modelType] : undefined; const { results: hfResults, isLoading, isLoadingMore, - hasMore, fetchMore, } = useHfModelSearch(debouncedQuery, { task, accessToken: hfToken || undefined, }); - const curatedModels = useMemo(() => { - if (!modelType) { - return []; - } - return MODELS.filter((m) => m.type === modelType).sort( - (a, b) => (b.recommended ? 1 : 0) - (a.recommended ? 1 : 0), - ); - }, [modelType]); + const resultIds = useMemo(() => hfResults.map((r) => r.id), [hfResults]); - const modelMap = useMemo(() => { - const map = new Map< - string, - { - label: string; - params?: string; - totalParams?: number; - downloads?: number; - recommended?: boolean; - } - >(); - for (const m of curatedModels) { - map.set(m.hfRepo ?? m.id, { - label: m.name, - params: m.params, - recommended: m.recommended, - }); - } - for (const r of hfResults) { - if (!map.has(r.id)) { - map.set(r.id, { - label: r.id, - downloads: r.downloads, - totalParams: r.totalParams, - }); - } - } - return map; - }, [curatedModels, hfResults]); - - const displayIds = useMemo(() => { - if (!debouncedQuery.trim()) { - return curatedModels.map((m) => m.hfRepo ?? m.id); - } - const q = debouncedQuery.toLowerCase(); - const curatedIds = curatedModels - .filter( - (m) => - m.name.toLowerCase().includes(q) || - m.id.toLowerCase().includes(q) || - m.hfRepo?.toLowerCase().includes(q), - ) - .map((m) => m.hfRepo ?? m.id); - const liveIds = hfResults - .map((r) => r.id) - .filter((id) => !curatedIds.includes(id)); - return [...curatedIds, ...liveIds]; - }, [debouncedQuery, curatedModels, hfResults]); - - const allIds = useMemo( - () => [ - ...new Set([ - ...curatedModels.map((m) => m.hfRepo ?? m.id), - ...hfResults.map((r) => r.id), - ]), - ], - [curatedModels, hfResults], - ); - - const selectedModelData = MODELS.find( - (m) => m.id === selectedModel || m.hfRepo === selectedModel, - ); const comboboxAnchorRef = useRef(null); - const { scrollRef, sentinelRef } = useInfiniteScroll(fetchMore); + const { scrollRef, sentinelRef } = useInfiniteScroll( + fetchMore, + hfResults.length, + ); return ( @@ -219,13 +152,13 @@ export function ModelSelectionStep() {
setSelectedModel(id)} - onInputValueChange={(val) => setInputValue(val)} - itemToStringValue={(id) => modelMap.get(id)?.label ?? id} + onValueChange={(id) => { selectingRef.current = true; setSelectedModel(id); }} + onInputValueChange={(val) => { if (selectingRef.current) { selectingRef.current = false; return; } setInputValue(val); }} + itemToStringValue={(id) => id} autoHighlight={true} > @@ -247,13 +180,10 @@ export function ModelSelectionStep() { > {(id: string) => { - const meta = modelMap.get(id); - const label = meta?.label ?? id; - const sizeLabel = - meta?.params ?? - (meta?.totalParams - ? formatCompact(meta.totalParams) - : null); + const r = hfResults.find((r) => r.id === id); + const sizeLabel = r?.totalParams + ? formatCompact(r.totalParams) + : null; return ( - {label} + {id} - {label} + {id} - - {meta?.recommended && ( - - Recommended - - )} - {sizeLabel ? ( - {sizeLabel} - ) : meta?.downloads != null ? ( - - ↓{formatCompact(meta.downloads)} - - ) : null} - + {sizeLabel ? ( + + {sizeLabel} + + ) : r?.downloads != null ? ( + + ↓{formatCompact(r.downloads)} + + ) : null} ); }} - {hasMore &&
} +
{isLoadingMore && (
@@ -306,7 +228,7 @@ export function ModelSelectionStep() {
- {(selectedModelData || selectedModel) && ( + {selectedModel && (
@@ -340,8 +262,7 @@ export function ModelSelectionStep() { - Choose how to fine-tune{" "} - {selectedModelData?.name ?? selectedModel} + Choose how to fine-tune {selectedModel}