fix(studio): UI fixes for chat and studio routes (#4419)

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
Wasim Yousef Said 2026-03-18 11:47:13 +01:00 committed by GitHub
commit 7b07ad0fa3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 72 additions and 21 deletions

View file

@ -151,7 +151,7 @@ function ModelSelectorContent({
<button
type="button"
onClick={onEject}
className="flex w-full items-center justify-center gap-1.5 rounded-md px-2 py-1.5 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
className="flex w-full items-center justify-center gap-1.5 rounded-md px-2 py-1.5 text-xs text-destructive transition-colors hover:bg-destructive/10"
title="Eject model"
>
<HugeiconsIcon icon={Logout01Icon} className="size-3.5" />

View file

@ -310,8 +310,8 @@ export function DatasetSection() {
if (datasetSource !== "upload") return;
if (!uploadedFile) return;
if (selectedLocalDataset) return;
// Don't clear if this is a direct file upload (not a recipe directory)
if (isLikelyLocalDatasetRef(uploadedFile)) return;
// Don't clear if this is a direct file upload (e.g. user uploaded a .jsonl/.csv)
if (/\.(jsonl|json|csv|parquet|arrow)$/i.test(uploadedFile)) return;
selectLocalDataset(null);
}, [
datasetSource,

View file

@ -372,7 +372,7 @@ export function ModelSection() {
<div data-tour="studio-base-model" className="flex flex-col gap-2">
<span className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground">
Base Model
Hugging Face Model
<Tooltip>
<TooltipTrigger asChild={true}>
<button

View file

@ -9,6 +9,14 @@ import {
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Input } from "@/components/ui/input";
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox";
import {
Select,
SelectContent,
@ -37,7 +45,7 @@ import {
Settings04Icon,
} from "@hugeicons/core-free-icons";
import { HugeiconsIcon } from "@hugeicons/react";
import { type ReactElement, type ReactNode, useState } from "react";
import { type ReactElement, type ReactNode, useRef, useState } from "react";
function Row({
label,
@ -120,6 +128,9 @@ export function ParamsSection(): ReactElement {
const showVisionLora = store.isVisionModel && store.isDatasetImage === true;
const [loraOpen, setLoraOpen] = useState(false);
const [hyperOpen, setHyperOpen] = useState(false);
const [ctxInput, setCtxInput] = useState(String(store.contextLength));
const ctxAnchorRef = useRef<HTMLDivElement>(null);
const ctxItems = CONTEXT_LENGTHS.map(String);
const { useEpochs, toggleUseEpochs } = useMaxStepsEpochsToggle({
maxSteps: store.maxSteps,
epochs: store.epochs,
@ -259,21 +270,61 @@ export function ParamsSection(): ReactElement {
</TooltipContent>
</Tooltip>
</span>
<Select
value={String(store.contextLength)}
onValueChange={(v) => store.setContextLength(Number(v))}
>
<SelectTrigger className="w-full font-mono">
<SelectValue />
</SelectTrigger>
<SelectContent>
{CONTEXT_LENGTHS.map((len) => (
<SelectItem key={len} value={String(len)} className="font-mono">
{len.toLocaleString()}
</SelectItem>
))}
</SelectContent>
</Select>
<div ref={ctxAnchorRef}>
<Combobox
items={ctxItems}
filteredItems={ctxItems}
filter={null}
value={String(store.contextLength)}
onValueChange={(v) => {
if (v) {
const n = Number(v);
if (Number.isInteger(n) && n > 0) {
store.setContextLength(n);
setCtxInput(v);
}
}
}}
onInputValueChange={setCtxInput}
itemToStringValue={(id) => Number(id).toLocaleString()}
autoHighlight={false}
>
<ComboboxInput
placeholder={String(store.contextLength)}
className="w-full font-mono"
onBlur={() => {
const n = Number(ctxInput);
if (Number.isInteger(n) && n > 0) {
store.setContextLength(n);
}
setCtxInput(String(store.contextLength));
}}
onKeyDown={(e) => {
if (e.key !== "Enter") { return; }
const n = Number(ctxInput);
if (!Number.isInteger(n) || n <= 0) { return; }
// If the typed value isn't an exact preset, prevent the
// combobox from selecting a highlighted dropdown item.
if (!ctxItems.includes(ctxInput.trim())) {
e.stopPropagation();
e.preventDefault();
}
store.setContextLength(n);
setCtxInput(String(n));
}}
/>
<ComboboxContent anchor={ctxAnchorRef}>
<ComboboxEmpty>Enter a custom value</ComboboxEmpty>
<ComboboxList className="p-1">
{(id: string) => (
<ComboboxItem key={id} value={id} className="font-mono">
{Number(id).toLocaleString()}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
</div>
<p className="text-[10px] text-muted-foreground">
Max sequence length for training samples
</p>

View file

@ -6,7 +6,7 @@ import { ReadMore, type TourStep } from "@/features/tour";
export const studioBaseModelStep: TourStep = {
id: "base-model",
target: "studio-base-model",
title: "Base model from Hugging Face",
title: "Hugging Face Model",
body: (
<>
Paste <span className="font-mono">org/model</span> or search. Pick a base