From 085f9529b612f214a4e6057543b2bc2bdeddcc75 Mon Sep 17 00:00:00 2001 From: Wasim Yousef Said Date: Wed, 25 Mar 2026 03:39:27 +0100 Subject: [PATCH] Regroup chat settings sidebar into focused sections (#4551) * feat(chat): regroup settings sidebar into Model, Sampling, Tools, and Preferences sections Split the monolithic Settings collapsible into focused sections with icons. Model section shows context length and KV cache dtype for GGUF models, trust remote code for non GGUF. Tools section groups auto heal, max tool calls, and tool call timeout. Preferences section holds auto title toggle. * feat(chat): persist collapsible section open/closed state in localStorage Remember which sections the user expanded or collapsed across sidebar toggles, mobile sheet reopens, and browser sessions. * fix(chat): harden collapsible state persistence and restore defaultOpen - Validate localStorage values are booleans before using them, preventing corrupted entries like string "false" from being treated as truthy - Use Object.hasOwn() instead of `in` operator to avoid prototype chain matches on keys like "constructor" or "toString" - Restore defaultOpen={true} on Model and Preferences sections so they are expanded on first visit, matching the old Settings section behavior - Fix misleading Context Length description to reflect it is read-only - Downgrade console.error to console.warn for non-critical localStorage parse failures * fix(chat): remove redundant disabled styles on Context Length input The Input component already applies opacity-50 and cursor-not-allowed via its disabled: variants. Specifying them unconditionally in the className is redundant. --------- Co-authored-by: Daniel Han --- .../src/features/chat/chat-settings-sheet.tsx | 162 +++++++++++++----- 1 file changed, 116 insertions(+), 46 deletions(-) diff --git a/studio/frontend/src/features/chat/chat-settings-sheet.tsx b/studio/frontend/src/features/chat/chat-settings-sheet.tsx index 45c9d17888..081e3efc26 100644 --- a/studio/frontend/src/features/chat/chat-settings-sheet.tsx +++ b/studio/frontend/src/features/chat/chat-settings-sheet.tsx @@ -28,6 +28,8 @@ import { PencilEdit01Icon, Settings02Icon, SlidersHorizontalIcon, + UserSettings01Icon, + Wrench01Icon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { AnimatePresence, motion } from "motion/react"; @@ -164,6 +166,39 @@ function ParamSlider({ ); } +const COLLAPSIBLE_STATE_KEY = "unsloth_chat_collapsible_state"; + +function loadCollapsibleState(): Record { + if (!canUseStorage()) return {}; + try { + const raw = localStorage.getItem(COLLAPSIBLE_STATE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { + return {}; + } + return Object.fromEntries( + Object.entries(parsed).filter( + (entry): entry is [string, boolean] => typeof entry[1] === "boolean", + ), + ); + } catch (error) { + console.warn("Failed to load collapsible state from localStorage:", error); + return {}; + } +} + +function saveCollapsibleOpen(label: string, open: boolean) { + if (!canUseStorage()) return; + try { + const state = loadCollapsibleState(); + state[label] = open; + localStorage.setItem(COLLAPSIBLE_STATE_KEY, JSON.stringify(state)); + } catch { + // ignore + } +} + function CollapsibleSection({ icon, label, @@ -175,13 +210,20 @@ function CollapsibleSection({ children?: ReactNode; defaultOpen?: boolean; }) { - const [open, setOpen] = useState(defaultOpen); + const [open, setOpen] = useState(() => { + const saved = loadCollapsibleState(); + return Object.hasOwn(saved, label) ? saved[label] : defaultOpen; + }); return (
+ +
+ {isGguf && ( + <> +
+
+
Context Length
+
+ Reported by the loaded GGUF model. +
+
+ +
+
+
+
KV Cache Dtype
+
+ Quantize KV cache to reduce VRAM. Reload to apply. +
+
+ +
+ + )} + {!isGguf && ( +
+
+
Trust remote code
+
+ Allow models with custom code (e.g. Nemotron). Only enable for repos you trust. +
+
+ +
+ )} +
+
+ - + +
+ + + +
+
+ +
@@ -519,49 +632,6 @@ export function ChatSettingsPanel({ onCheckedChange={onAutoTitleChange} />
-
-
-
Trust remote code
-
- Allow models with custom code (e.g. Nemotron). Only enable for repos you trust. -
-
- -
- {isGguf && ( -
-
-
KV Cache Dtype
-
- Quantize KV cache to reduce VRAM. Reload to apply. -
-
- -
- )} - - -