Implementation:Microsoft Autogen Studio UI Settings
| Sources | https://github.com/microsoft/autogen/blob/main/python/packages/autogen-studio/frontend/src/components/views/settings/view/ui.tsx |
|---|---|
| Domains | React Component, Settings Management, UI Preferences, Toggle Controls |
| Last Updated | 2026-02-11 |
Overview
The UISettingsPanel component provides a form interface for configuring AutoGen Studio's UI behavior preferences including message display, agent flow visualization, and timeout settings.
Description
The UISettingsPanel is a settings configuration component that manages four key UI preferences for the AutoGen Studio chat interface. It implements a clean, accessible settings form with custom toggle switches and number inputs.
Key architectural features:
- Local state management: Maintains a local copy of UI settings that syncs with the global store
- Dirty state tracking: Visual indicators (red dot on Save button) show unsaved changes
- Reset functionality: One-click reset to default settings with server persistence
- Responsive controls: Custom SettingToggle and SettingNumberInput components with hover effects
- Validation: Number input constrains values to valid ranges (1-30 minutes for timeout)
- Optimistic updates: Immediate UI feedback before server confirmation
The component uses two custom sub-components:
- SettingToggle: A custom switch component with label, description, and accessibility features
- SettingNumberInput: A bounded numeric input with optional suffix display
Settings managed:
- show_llm_call_events: Display detailed LLM call logs in message threads
- expanded_messages_by_default: Auto-expand message threads on load
- show_agent_flow_by_default: Display agent flow diagram automatically
- human_input_timeout_minutes: Timeout duration for waiting on user input (1-30 minutes)
Data flow: Store settings → Local state → User changes → Dirty flag → Save/Reset → API call → Server update → Store refresh → Local state update
Usage
The component is rendered within a settings page, typically in a tab alongside environment variables and other configuration panels. It requires:
- userId: The authenticated user ID for API operations
The component self-manages its connection to the settings store via the useSettingsStore hook, pulling serverSettings and uiSettings from global state.
Code Reference
Source Location: python/packages/autogen-studio/frontend/src/components/views/settings/view/ui.tsx
Signature:
interface UISettingsPanelProps {
userId: string;
}
export const UISettingsPanel: React.FC<UISettingsPanelProps> = ({ userId }) => {
const {
serverSettings,
uiSettings: storeUISettings,
initializeSettings,
} = useSettingsStore();
const [localUISettings, setLocalUISettings] = useState<UISettings>({
show_llm_call_events: false,
expanded_messages_by_default: false,
show_agent_flow_by_default: false,
human_input_timeout_minutes: 3,
});
const [isDirty, setIsDirty] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
// ... handlers
return (
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-medium">UI Settings</h3>
<div className="space-x-2 inline-flex">
<Button icon={<RotateCcw />} onClick={handleReset}>Reset</Button>
<Button type="primary" icon={<Save />} onClick={handleSave}>Save</Button>
</div>
</div>
<div className="space-y-0 rounded border border-secondary">
<SettingToggle ... />
<SettingToggle ... />
<SettingToggle ... />
<SettingNumberInput ... />
</div>
</div>
);
};
interface SettingToggleProps {
checked: boolean;
onChange: (checked: boolean) => void;
label: string;
description?: string;
disabled?: boolean;
}
const SettingToggle: React.FC<SettingToggleProps> = ({ ... }) => (
<div className="flex justify-between items-start p-4 hover:bg-secondary/5">
<div className="flex flex-col gap-1">
<label className="font-medium">{label}</label>
{description && <span className="text-sm text-secondary">{description}</span>}
</div>
<input type="checkbox" ... />
</div>
);
interface SettingNumberInputProps {
value: number;
onChange: (value: number) => void;
label: string;
description?: string;
disabled?: boolean;
min?: number;
max?: number;
suffix?: string;
}
const SettingNumberInput: React.FC<SettingNumberInputProps> = ({ ... }) => (
<div className="flex justify-between items-start p-4">
<div className="flex flex-col gap-1">
<label className="font-medium">{label}</label>
{description && <span className="text-sm text-secondary">{description}</span>}
</div>
<div className="flex items-center gap-2">
<input type="number" min={min} max={max} ... />
{suffix && <span className="text-sm text-secondary">{suffix}</span>}
</div>
</div>
);Import:
import { UISettingsPanel } from "./components/views/settings/view/ui";
// or
import UISettingsPanel from "./components/views/settings/view/ui";I/O Contract
Props
| Name | Type | Required | Description |
|---|---|---|---|
| userId | string | Yes | Authenticated user ID for API operations |
Outputs
| Type | Description |
|---|---|
| JSX.Element | Renders a settings form with toggles and number inputs for UI preferences |
UISettings Type
interface UISettings {
show_llm_call_events: boolean;
expanded_messages_by_default: boolean;
show_agent_flow_by_default: boolean;
human_input_timeout_minutes: number;
}Default Settings
const DEFAULT_UI_SETTINGS: UISettings = {
show_llm_call_events: false,
expanded_messages_by_default: false,
show_agent_flow_by_default: false,
human_input_timeout_minutes: 3,
};State Management
| State Variable | Type | Description |
|---|---|---|
| localUISettings | UISettings | Local working copy of UI settings |
| isDirty | boolean | Tracks whether unsaved changes exist |
| isSaving | boolean | Loading state during save/reset operations |
| messageApi | MessageInstance | Ant Design message API for toast notifications |
Store Dependencies
| Store Method/Property | Description |
|---|---|
| serverSettings | Complete Settings object from server |
| uiSettings | Current UI settings from store (subset of serverSettings.config.ui) |
| initializeSettings(userId) | Fetches latest settings from server and updates store |
API Interactions
| Method | When Called | Description |
|---|---|---|
| settingsAPI.updateSettings(settings, userId) | handleSave() or handleReset() | Persists settings to server |
Usage Examples
Example 1: Basic Integration
import { UISettingsPanel } from "./components/views/settings/view/ui";
import { useContext } from "react";
import { appContext } from "./hooks/provider";
function SettingsPage() {
const { user } = useContext(appContext);
return (
<div className="p-6">
<UISettingsPanel userId={user.id} />
</div>
);
}Example 2: Toggle Change Flow
// User clicks "Show LLM Events" toggle
// SettingToggle calls: onChange(true)
// Triggers: handleSettingChange("show_llm_call_events", true)
const handleSettingChange = (key: keyof UISettings, value: boolean | number) => {
setLocalUISettings(prev => ({
...prev,
[key]: value, // Updates local state
}));
setIsDirty(true); // Shows red dot on Save button
};
// Local state is updated immediately for responsive UI
// Changes are NOT persisted until user clicks SaveExample 3: Number Input with Validation
// SettingNumberInput for human_input_timeout_minutes:
<SettingNumberInput
value={localUISettings.human_input_timeout_minutes ?? 3}
onChange={(value) => handleSettingChange("human_input_timeout_minutes", value)}
label="Human Input Timeout"
description="How long to wait for user input before timing out (1-30 minutes)"
min={1}
max={30}
suffix="minutes"
/>
// onChange handler:
onChange={(e) => {
const newValue = parseInt(e.target.value);
if (!isNaN(newValue) && newValue >= min && newValue <= max) {
onChange(newValue); // Only calls parent if valid
}
}}
// Invalid entries (outside 1-30 range) are silently ignored
// HTML5 min/max attributes provide additional validationExample 4: Saving Settings
// User clicks "Save" button (enabled when isDirty=true)
// Triggers: handleSave()
const handleSave = async () => {
setIsSaving(true); // Disables buttons, shows loading spinner
// Merge local UI settings with server settings:
const updatedSettings: Settings = {
...serverSettings,
config: {
...serverSettings.config,
ui: localUISettings, // Replace UI config
},
created_at: undefined, // Remove timestamps
updated_at: undefined, // API will set these
};
// Persist to server:
await settingsAPI.updateSettings(updatedSettings, userId);
// Refresh from server (ensures sync):
await initializeSettings(userId);
// Reset flags:
setIsDirty(false);
setIsSaving(false);
messageApi.success("UI settings saved successfully");
};Example 5: Reset to Defaults
// User clicks "Reset" button
// Triggers: handleReset()
const handleReset = async () => {
setIsSaving(true);
const DEFAULT_UI_SETTINGS: UISettings = {
show_llm_call_events: false,
expanded_messages_by_default: false,
show_agent_flow_by_default: false,
human_input_timeout_minutes: 3,
};
// Update local state immediately for responsive UI:
setLocalUISettings(DEFAULT_UI_SETTINGS);
// Prepare settings update:
const updatedSettings: Settings = {
...serverSettings,
config: {
...serverSettings.config,
ui: DEFAULT_UI_SETTINGS,
},
created_at: undefined,
updated_at: undefined,
};
// Persist to server:
await settingsAPI.updateSettings(updatedSettings, userId);
await initializeSettings(userId);
setIsDirty(false);
setIsSaving(false);
messageApi.success("UI settings reset successfully");
};Example 6: Store Synchronization
// On mount and whenever store settings change:
useEffect(() => {
setLocalUISettings(storeUISettings);
}, [storeUISettings]);
// This ensures local state stays in sync with:
// - Initial load from server
// - Changes from other components
// - Refresh after save/reset
// Settings flow:
// Server → Store → Local state → User edits → Local state → Server → Store → Local stateExample 7: Custom Toggle Component Accessibility
// SettingToggle implements accessible checkbox pattern:
<input
type="checkbox"
checked={checked}
onChange={(e) => onChange(e.target.checked)}
disabled={disabled}
className="sr-only" // Visually hidden but screen-reader accessible
id={`toggle-${label.replace(/\s+/g, "-").toLowerCase()}`}
/>
<label
htmlFor={`toggle-${label.replace(/\s+/g, "-").toLowerCase()}`}
className={`... cursor-pointer ${checked ? "bg-accent" : "bg-gray-300"}`}
>
<span className={`... ${checked ? "translate-x-6" : "translate-x-1"}`} />
</label>
// Clicking the visible label triggers the hidden checkbox
// Keyboard navigation works (Tab to focus, Space to toggle)
// Screen readers announce "checkbox, [label], [checked/unchecked]"Example 8: Effect on Chat Interface
// Settings are consumed by ChatView and related components:
// show_llm_call_events:
if (uiSettings.show_llm_call_events) {
// Display LLMCallEvent messages in thread
// Show token counts, model info, latency
}
// expanded_messages_by_default:
const [isExpanded, setIsExpanded] = useState(
uiSettings.expanded_messages_by_default ?? false
);
// show_agent_flow_by_default:
const [showAgentFlow, setShowAgentFlow] = useState(
uiSettings.show_agent_flow_by_default ?? false
);
// human_input_timeout_minutes:
const timeoutMs = (uiSettings.human_input_timeout_minutes ?? 3) * 60 * 1000;
setTimeout(() => {
// Cancel waiting for human input
}, timeoutMs);Related Pages
- Studio_Settings_API - API client for persisting settings
- Studio_Settings_Store - Zustand store for settings state management
- Studio_Environment_Settings - Companion panel for environment variables
- Studio_Chat_View - Component that consumes these UI settings
- Types_Datamodel - TypeScript definitions for Settings and UISettings types
- Studio_Settings_View - Parent settings page that renders this panel