From 317b6cd725a0005b1e7296ae139253ec8808ae3a Mon Sep 17 00:00:00 2001 From: Ryan Westfall Date: Tue, 4 Aug 2026 08:13:52 -0500 Subject: [PATCH] Add settings toggle for previous-conversation context (#66). Wire Account Preferences to use_conversation_context via preferences API, with tooltip copy plus loading and error handling on update. --- .../ThemeSettingsCard/ThemeSettingsCard.tsx | 104 +++++++++++++++++- llm-fe/src/llm-fe/data.ts | 1 + 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/llm-fe/src/llm-fe/components/ThemeSettingsCard/ThemeSettingsCard.tsx b/llm-fe/src/llm-fe/components/ThemeSettingsCard/ThemeSettingsCard.tsx index c147f5f..aafbe48 100644 --- a/llm-fe/src/llm-fe/components/ThemeSettingsCard/ThemeSettingsCard.tsx +++ b/llm-fe/src/llm-fe/components/ThemeSettingsCard/ThemeSettingsCard.tsx @@ -1,5 +1,7 @@ import React, { useContext, useEffect, useState } from 'react'; import styled from 'styled-components'; +import Tooltip from '@mui/material/Tooltip'; +import InfoOutlined from '@mui/icons-material/InfoOutlined'; import { useMaterialUIController, setDarkMode, @@ -48,6 +50,16 @@ const SettingLabel = styled.span` font-size: 1.1rem; color: ${({ theme }) => theme.colors.text}; font-weight: 500; + display: inline-flex; + align-items: center; + gap: 0.4rem; +`; + +const InfoIcon = styled(InfoOutlined)` + font-size: 1rem !important; + opacity: 0.7; + cursor: help; + vertical-align: middle; `; const ToggleSwitch = styled.label` @@ -93,6 +105,11 @@ const Checkbox = styled.input` &:checked + ${Slider}:before { transform: translateX(26px); } + + &:disabled + ${Slider} { + opacity: 0.5; + cursor: not-allowed; + } `; const ColorPickerContainer = styled.div` @@ -115,6 +132,23 @@ const ColorButton = styled.button<{ color: string; isSelected: boolean }>` } `; +const ErrorText = styled.p` + color: #ff6b6b; + margin: 0.75rem 0 0 0; + font-size: 0.95rem; +`; + +const CONTEXT_TOOLTIP = + 'This will use previous conversations to better customize the experience.'; + +function prefsErrorMessage(error: unknown, fallback: string): string { + const axiosError = error as { + response?: { data?: { detail?: string } }; + message?: string; + }; + return axiosError.response?.data?.detail || axiosError.message || fallback; +} + type PreferencesValues = { order: boolean; } @@ -128,6 +162,10 @@ const ThemeSettingsCard = (): JSX.Element => { const themeColors = Object.keys(palettes); const [order, setOrder] = useState(true); + const [useConversationContext, setUseConversationContext] = useState(false); + const [prefsLoading, setPrefsLoading] = useState(true); + const [contextSaving, setContextSaving] = useState(false); + const [prefsError, setPrefsError] = useState(''); const { updatePreferences } = useContext(PreferenceContext); const handleConversationOrder = async ({ order }: PreferencesValues): Promise => { @@ -141,17 +179,50 @@ const ThemeSettingsCard = (): JSX.Element => { } } - async function getConversationOrder() { + async function getConversationPreferences() { + setPrefsLoading(true); + setPrefsError(''); try { - const { data, }: AxiosResponse = await axiosInstance.get(`/conversation_preferences`); + const { data }: AxiosResponse = await axiosInstance.get( + `/conversation_preferences` + ); setOrder(data.order); + setUseConversationContext(Boolean(data.use_conversation_context)); } catch (error) { - console.log(error) + setPrefsError( + prefsErrorMessage(error, 'Could not load account preferences.') + ); + } finally { + setPrefsLoading(false); } } + const handleConversationContext = async (enabled: boolean): Promise => { + const previous = useConversationContext; + setUseConversationContext(enabled); + setContextSaving(true); + setPrefsError(''); + try { + const { data }: AxiosResponse = await axiosInstance.post( + '/conversation_preferences', + { use_conversation_context: enabled } + ); + setUseConversationContext(Boolean(data.use_conversation_context)); + } catch (error) { + setUseConversationContext(previous); + setPrefsError( + prefsErrorMessage( + error, + 'Could not update conversation context setting. Try again.' + ) + ); + } finally { + setContextSaving(false); + } + }; + useEffect(() => { - getConversationOrder(); + getConversationPreferences(); }, []) const handleDarkMode = () => setDarkMode(dispatch, !darkMode); @@ -193,6 +264,7 @@ const ThemeSettingsCard = (): JSX.Element => { { setOrder(!order); handleConversationOrder({ order: !order }) @@ -202,6 +274,30 @@ const ThemeSettingsCard = (): JSX.Element => { + + + Use previous conversations + + + + + + + + { + handleConversationContext(!useConversationContext); + }} + /> + + + + + {prefsError ? {prefsError} : null} + ); }; diff --git a/llm-fe/src/llm-fe/data.ts b/llm-fe/src/llm-fe/data.ts index ed7ad0c..49a96fc 100644 --- a/llm-fe/src/llm-fe/data.ts +++ b/llm-fe/src/llm-fe/data.ts @@ -269,6 +269,7 @@ export interface AccountType { export interface PreferencesType { order: boolean; + use_conversation_context?: boolean; } export class Account { -- 2.54.0