From fe36140586b8b0ce929f5d583a1a78773bdef80d Mon Sep 17 00:00:00 2001 From: shine1i Date: Mon, 13 Apr 2026 12:37:02 +0200 Subject: [PATCH] fix settings sheet save state polish --- .../src/features/chat/chat-settings-sheet.tsx | 172 +++++++++++++++--- 1 file changed, 148 insertions(+), 24 deletions(-) diff --git a/studio/frontend/src/features/chat/chat-settings-sheet.tsx b/studio/frontend/src/features/chat/chat-settings-sheet.tsx index 5bf62d43de..1d125d2651 100644 --- a/studio/frontend/src/features/chat/chat-settings-sheet.tsx +++ b/studio/frontend/src/features/chat/chat-settings-sheet.tsx @@ -41,6 +41,7 @@ import { Slider } from "@/components/ui/slider"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { useIsMobile } from "@/hooks/use-mobile"; +import { cn } from "@/lib/utils"; import { ArrowDown01Icon, CodeIcon, @@ -196,6 +197,79 @@ function saveSystemPrompts(templates: SystemPromptTemplate[]) { } } +type PresetSaveMode = + | "disabled" + | "reserved" + | "overwrite-active" + | "overwrite-other" + | "create"; + +interface PresetSaveState { + mode: PresetSaveMode; + canSubmit: boolean; + isSaveReady: boolean; + buttonLabel: string; + title: string; +} + +function getPresetSaveState({ + rawName, + activePreset, + customPresets, +}: { + rawName: string; + activePreset: string; + customPresets: Preset[]; +}): PresetSaveState { + const trimmedName = rawName.trim(); + if (!trimmedName) { + return { + mode: "disabled", + canSubmit: false, + isSaveReady: false, + buttonLabel: "Save", + title: "Enter a preset name", + }; + } + + if (BUILTIN_PRESETS.some((preset) => preset.name === trimmedName)) { + return { + mode: "reserved", + canSubmit: true, + isSaveReady: false, + buttonLabel: "Reserved", + title: `"${trimmedName}" is reserved. Pick a different name.`, + }; + } + + const matchingCustomPreset = customPresets.find( + (preset) => preset.name === trimmedName, + ); + if (matchingCustomPreset) { + return { + mode: + matchingCustomPreset.name === activePreset + ? "overwrite-active" + : "overwrite-other", + canSubmit: true, + isSaveReady: true, + buttonLabel: "Overwrite", + title: + matchingCustomPreset.name === activePreset + ? "Save current settings to this preset" + : `Overwrite preset "${trimmedName}"`, + }; + } + + return { + mode: "create", + canSubmit: true, + isSaveReady: true, + buttonLabel: "Save as New", + title: `Save current settings as "${trimmedName}"`, + }; +} + function ParamSlider({ label, value, @@ -401,6 +475,24 @@ export function ChatSettingsPanel({ [customPresets], ); const isBuiltinPreset = BUILTIN_PRESETS.some((p) => p.name === activePreset); + const presetSaveState = useMemo( + () => + getPresetSaveState({ + rawName: presetNameInput, + activePreset, + customPresets, + }), + [activePreset, customPresets, presetNameInput], + ); + const selectedSystemPrompt = useMemo( + () => + systemPrompts.find((item) => item.id === selectedSystemPromptId) ?? null, + [selectedSystemPromptId, systemPrompts], + ); + const systemPromptTemplateDirty = + selectedSystemPrompt != null && + (selectedSystemPrompt.name !== systemPromptNameDraft || + selectedSystemPrompt.content !== systemPromptContentDraft); function set(key: K) { return (v: InferenceParams[K]) => onParamsChange({ ...params, [key]: v }); @@ -513,11 +605,12 @@ export function ChatSettingsPanel({ function saveCurrentSystemPromptTemplate() { if (!selectedSystemPromptId) return; const now = Date.now(); + const normalizedName = systemPromptNameDraft.trim() || "Untitled prompt"; const next = systemPrompts.map((item) => item.id === selectedSystemPromptId ? { ...item, - name: systemPromptNameDraft.trim() || "Untitled prompt", + name: normalizedName, content: systemPromptContentDraft, updatedAt: now, } @@ -525,6 +618,7 @@ export function ChatSettingsPanel({ ); setSystemPrompts(next); saveSystemPrompts(next); + setSystemPromptNameDraft(normalizedName); } function createSystemPromptTemplate() { @@ -635,7 +729,7 @@ export function ChatSettingsPanel({ value={presetNameInput} onChange={(e) => setPresetNameInput(e.target.value)} onKeyDown={(e) => { - if (e.key === "Enter") { + if (e.key === "Enter" && presetSaveState.canSubmit) { e.preventDefault(); savePresetWithName(presetNameInput); } @@ -643,7 +737,11 @@ export function ChatSettingsPanel({ placeholder="Preset name" maxLength={80} autoComplete="off" - className="!h-8 min-h-0 min-w-0 self-stretch !pl-2.5 !pr-2 pt-1 pb-1 text-sm leading-10 md:text-sm" + className={cn( + "!h-8 min-h-0 min-w-0 self-stretch !pl-2.5 !pr-2 pt-1 pb-1 text-sm leading-10 md:text-sm", + presetSaveState.isSaveReady && + "text-foreground placeholder:text-primary/45", + )} aria-label="Inference preset name" />
- - +
@@ -735,8 +844,9 @@ export function ChatSettingsPanel({ size="sm" className="h-6 px-2 text-[11px]" onClick={openSystemPromptManager} + title="Open saved prompt templates" > - Manage + Templates