mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-27 13:48:51 +01:00
🎨 feat: UI Refresh for Enhanced UX (#6346)
* ✨ feat: Add Expand Chat functionality and improve UI components * ✨ feat: Introduce Chat Badges feature with editing capabilities and UI enhancements * ✨ feat: re-implement file attachment functionality with new components and improved UI * ✨ feat: Enhance BadgeRow component with drag-and-drop functionality and add animations for better user experience * ✨ feat: Add useChatBadges hook and enhance Badge component with animations and toggle functionality * feat: Improve Add/Delete Badges + style and bug fixes * ✨ feat: Refactor EditBadges component and optimize useChatBadges hook for improved performance and readability * ✨ feat: Add type definition for LucideIcon in EditBadges component * refactor: Clean up BadgeRow component by removing outdated comment and improving code readability * refactor: Rename app-icon class to badge-icon for consistency and improve badge styling * feat: Add Center Chat Input toggle and update related components for improved UI/UX * refactor: Simplify ChatView and MessagesView components for improved readability and performance * refactor: Improve layout and positioning of scroll button in MessagesView component * refactor: Adjust scroll button position in MessagesView component for better visibility * refactor: Remove redundant background class from Badge component for cleaner styling * feat: disable chat badges * refactor: adjust positioning of scroll button and popover for improved layout * refactor: simplify class names in ChatForm and RemoveFile components for cleaner code * refactor: move Switcher to HeaderOptions from SidePanel * fix(Landing): duplicate description * feat: add SplitText component for animated text display and update Landing component to use it * feat(Chat): add ConversationStarters component and integrate it into ChatView; remove ConvoStarter component * feat(Chat): enhance Message component layout and styling for improved readability * feat(ControlCombobox, Select): enhance styling and add animation for improved UI experience * feat(Chat): update Header and HeaderNewChat components for improved layout and styling * feat(Chat): add ModelDropdown (now includes both endpoint and model) and refactor Menu components for improved UI * feat(ModelDropdown): add Agent Select; removed old AgentSwitcher components * feat(ModelDropdown): add settings button for user key configuration * fix(ModelDropdown): the model dropdown wasn't opening automatically when opening the endpoint one * refactor(Chat): remove unused EndpointsMenu and related components to streamline codebase * feat: enhance greeting message and improve accessibility fro ModelDropdown * refactor(Endpoints): add new hooks and components for endpoint management * feat(Endpoint): add support for modelSpecs * feat(Endpoints): add mobile support * fix: type issues * fix(modelSpec): type issue * fix(EndpointMenuDropdown): double overflow scroller in mobile model list * fix: search model on mobile * refactor: Endpoint/Model/modelSpec dropdown * refactor: reorganize imports in Endpoint components * refactor: remove unused translation keys from English locale * BREAKING: moving to ariakit with new CustomMenu * refactor: remove unnecessary comments * refactor: remove EndpointItem, ModelDropdownButton, SpecIcon, and SpecItem components * 🔧 fix: AI Icon bump when regenerating message * wip: chat UI refactoring, fix issues * chore: add recent update to useAutoSave * feat: add access control for agent permissions in useMentions hook * refactor: streamline ModelSelector by removing unused endpoints logic * refactor: enhance ModelSelector and context by integrating endpointsConfig and improving type usage * feat: update ModelSelectorContext to utilize conversation data for initial state * feat: add selector effects for synced endpoint handling * feat: add guard clause for conversation endpoint in useSelectorEffects hook * fix: safely call onSelectMention and add autofocus to mention input * chore: typing * refactor: ModelSelector to streamline key dialog handling and improve endpoint rendering * refactor: extract SettingsButton component for cleaner endpoint item rendering * wip: first pass, expand set api key * wip: first pass, expanding set key * refactor: update EndpointItem styles for improved layout and hover effects * refactor: adjust padding in EndpointItem for improved layout consistency * refactor: update preset structure in useSelectMention to include spec as null * refactor: rename setKeyDialogOpen to onOpenChange for clarity and consistency, bring focus back to button that opened dialog * feat: add SpecIcon component for dynamic model spec icons in menu, adjust icon styling * refactor: update getSelectedIcon to accept additional parameters and improve icon rendering logic * fix: adjust padding in MessageRender for improved layout * refactor: remove inline style for menu width in CustomMenu component * refactor: enhance layout and styling in ModelSpecItem component for better responsiveness * refactor: update getDefaultModelSpec to accept startupConfig and improve model spec retrieval logic * refactor: improve key management and default values in ModelSelector and related components * refactor: adjust menu width and improve responsiveness in CustomMenu and EndpointItem components * refactor: enhance focus styles and responsiveness in EndpointItem component * refactor: improve layout and spacing in Header and ModelSelector components for better responsiveness * refactor: adjust button styles for consistency and improved layout in AddMultiConvo and PresetsMenu components * fix: initial fix of assistant names * fix: assistants handling * chore: update version of librechat-data-provider to 0.7.75 and add 'spec' to excludedKeys * fix: improve endpoint filtering logic based on interface configuration and access rights * fix: remove unused HeaderOptions import and set spec to null in presets and mentions * fix: ensure currentExample is always an object when updating examples * fix: update interfaceConfig checks to ensure modelSelect is considered for rendering components * fix: update model selection logic to consider interface configuration when prioritizing model specs * fix: add missing localizations * fix: remove unused agent and assistant selection translations * fix: implement debounced state updates for selected values in useSelectorEffects * style: minor style changes related to the ModelSelector * fix: adjust maximum height for popover and set fixed height for model item * fix: update placeholders for model and endpoint search inputs * fix: refactor MessageRender and ContentRender components to better match each other * fix: remove convo fallback for iconURL in MessageRender and ContentRender components * fix: update handling of spec, iconURL, and modelLabel in conversation presets, to allow better interchangeability * fix: replace chatGptLabel with modelLabel in OpenAI settings configuration (fully deprecate chatGptLabel) * fix: remove console log for assistantNames in useEndpoints hook * refactor: add cleanInput and cleanOutput options to default conversation handling * chore: update bun.lockb * fix: set default value for showIconInHeader in getSelectedIcon function * refactor: enhance error handling in message processing when latest message has existing content blocks * chore: allow import/no-cycle for messages * fix: adjust flex properties in BookmarkMenu for better layout * feat: support both 'prompt' and 'q' as query parameters in useQueryParams hook * feat: re-enable Badges components * refactor: disable edit badge component * chore: rename assistantMap to assistantsMap for consistency * chore: rename assistantMap to assistantsMap for consistency in Mention component * feat: set staleTime for various queries to improve data freshness * feat: add spec field to tQueryParamsSchema for model specification * feat: enhance useQueryParams to handle model specs --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
c4fea9cd79
commit
7f29f2f676
127 changed files with 4507 additions and 2163 deletions
|
|
@ -1,88 +0,0 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import { EModelEndpoint, isAgentsEndpoint, LocalStorageKeys } from 'librechat-data-provider';
|
||||
import type { Agent } from 'librechat-data-provider';
|
||||
import type { SwitcherProps, OptionWithIcon } from '~/common';
|
||||
import { useSetIndexOptions, useSelectAgent, useLocalize } from '~/hooks';
|
||||
import { useChatContext, useAgentsMapContext } from '~/Providers';
|
||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||
import Icon from '~/components/Endpoints/Icon';
|
||||
|
||||
export default function AgentSwitcher({ isCollapsed }: SwitcherProps) {
|
||||
const localize = useLocalize();
|
||||
const { setOption } = useSetIndexOptions();
|
||||
const { index, conversation } = useChatContext();
|
||||
const { agent_id: selectedAgentId = null, endpoint } = conversation ?? {};
|
||||
|
||||
const agentsMapResult = useAgentsMapContext();
|
||||
|
||||
const agentsMap = useMemo(() => {
|
||||
return agentsMapResult ?? {};
|
||||
}, [agentsMapResult]);
|
||||
|
||||
const { onSelect } = useSelectAgent();
|
||||
|
||||
const agents: Agent[] = useMemo(() => {
|
||||
return Object.values(agentsMap) as Agent[];
|
||||
}, [agentsMap]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAgentId == null && agents.length > 0) {
|
||||
let agent_id = localStorage.getItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`);
|
||||
if (agent_id == null) {
|
||||
agent_id = agents[0].id;
|
||||
}
|
||||
const agent = agentsMap[agent_id];
|
||||
|
||||
if (agent !== undefined && isAgentsEndpoint(endpoint as string) === true) {
|
||||
setOption('model')('');
|
||||
setOption('agent_id')(agent_id);
|
||||
}
|
||||
}
|
||||
}, [index, agents, selectedAgentId, agentsMap, endpoint, setOption]);
|
||||
|
||||
const currentAgent = agentsMap[selectedAgentId ?? ''];
|
||||
|
||||
const agentOptions: OptionWithIcon[] = useMemo(
|
||||
() =>
|
||||
agents.map((agent: Agent) => {
|
||||
return {
|
||||
label: agent.name ?? '',
|
||||
value: agent.id,
|
||||
icon: (
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
endpoint={EModelEndpoint.agents}
|
||||
agentName={agent.name ?? ''}
|
||||
iconURL={agent.avatar?.filepath}
|
||||
/>
|
||||
),
|
||||
};
|
||||
}),
|
||||
[agents],
|
||||
);
|
||||
|
||||
return (
|
||||
<ControlCombobox
|
||||
selectedValue={currentAgent?.id ?? ''}
|
||||
displayValue={
|
||||
agents.find((agent: Agent) => agent.id === selectedAgentId)?.name ??
|
||||
localize('com_sidepanel_select_agent')
|
||||
}
|
||||
selectPlaceholder={localize('com_sidepanel_select_agent')}
|
||||
searchPlaceholder={localize('com_agents_search_name')}
|
||||
isCollapsed={isCollapsed}
|
||||
ariaLabel={'agent'}
|
||||
setValue={onSelect}
|
||||
items={agentOptions}
|
||||
iconClassName="assistant-item"
|
||||
SelectIcon={
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
endpoint={endpoint}
|
||||
agentName={currentAgent?.name ?? ''}
|
||||
iconURL={currentAgent?.avatar?.filepath ?? ''}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ import type { TPlugin } from 'librechat-data-provider';
|
|||
import type { AgentForm, AgentPanelProps, IconComponentTypes } from '~/common';
|
||||
import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils';
|
||||
import { useToastContext, useFileMapContext } from '~/Providers';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
import Action from '~/components/SidePanel/Builder/Action';
|
||||
import { ToolSelectDialog } from '~/components/Tools';
|
||||
import { icons } from '~/hooks/Endpoint/Icons';
|
||||
import { processAgentOption } from '~/utils';
|
||||
import AgentAvatar from './AgentAvatar';
|
||||
import FileContext from './FileContext';
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import { isAssistantsEndpoint, LocalStorageKeys } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { SwitcherProps, AssistantListItem } from '~/common';
|
||||
import { useSetIndexOptions, useSelectAssistant, useLocalize, useAssistantListMap } from '~/hooks';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||
import Icon from '~/components/Endpoints/Icon';
|
||||
|
||||
export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
||||
const localize = useLocalize();
|
||||
const { setOption } = useSetIndexOptions();
|
||||
const { index, conversation } = useChatContext();
|
||||
|
||||
/* `selectedAssistant` must be defined with `null` to cause re-render on update */
|
||||
const { assistant_id: selectedAssistant = null, endpoint } = conversation ?? {};
|
||||
|
||||
const assistantListMap = useAssistantListMap((res) =>
|
||||
res.data.map(({ id, name, metadata }) => ({ id, name, metadata })),
|
||||
);
|
||||
const assistants: Omit<AssistantListItem, 'model'>[] = useMemo(
|
||||
() => assistantListMap[endpoint ?? ''] ?? [],
|
||||
[endpoint, assistantListMap],
|
||||
);
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
const { onSelect } = useSelectAssistant(endpoint as AssistantsEndpoint);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedAssistant && assistants && assistants.length && assistantMap) {
|
||||
const assistant_id =
|
||||
localStorage.getItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}${endpoint}`) ??
|
||||
assistants[0]?.id ??
|
||||
'';
|
||||
const assistant = assistantMap[endpoint ?? ''][assistant_id];
|
||||
|
||||
if (!assistant) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAssistantsEndpoint(endpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setOption('model')(assistant.model);
|
||||
setOption('assistant_id')(assistant_id);
|
||||
}
|
||||
}, [index, assistants, selectedAssistant, assistantMap, endpoint, setOption]);
|
||||
|
||||
const currentAssistant = assistantMap?.[endpoint ?? '']?.[selectedAssistant ?? ''];
|
||||
|
||||
const assistantOptions = useMemo(() => {
|
||||
return assistants.map((assistant) => {
|
||||
return {
|
||||
label: (assistant.name as string | null) ?? '',
|
||||
value: assistant.id,
|
||||
icon: (
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
endpoint={endpoint}
|
||||
assistantName={(assistant.name as string | null) ?? ''}
|
||||
iconURL={assistant.metadata?.avatar ?? ''}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
}, [assistants, endpoint]);
|
||||
|
||||
return (
|
||||
<ControlCombobox
|
||||
selectedValue={currentAssistant?.id ?? ''}
|
||||
displayValue={
|
||||
assistants.find((assistant) => assistant.id === selectedAssistant)?.name ??
|
||||
localize('com_sidepanel_select_assistant')
|
||||
}
|
||||
selectPlaceholder={localize('com_sidepanel_select_assistant')}
|
||||
searchPlaceholder={localize('com_assistants_search_name')}
|
||||
isCollapsed={isCollapsed}
|
||||
ariaLabel={'assistant'}
|
||||
setValue={onSelect}
|
||||
items={assistantOptions}
|
||||
iconClassName="assistant-item"
|
||||
SelectIcon={
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
endpoint={endpoint}
|
||||
assistantName={currentAssistant?.name ?? ''}
|
||||
iconURL={currentAssistant?.metadata?.avatar ?? ''}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { useMemo, useRef, useCallback } from 'react';
|
||||
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { SwitcherProps } from '~/common';
|
||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||
import MinimalIcon from '~/components/Endpoints/MinimalIcon';
|
||||
import { useSetIndexOptions, useLocalize } from '~/hooks';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import { mainTextareaId } from '~/common';
|
||||
|
||||
export default function ModelSwitcher({ isCollapsed }: SwitcherProps) {
|
||||
const localize = useLocalize();
|
||||
const modelsQuery = useGetModelsQuery();
|
||||
const { conversation } = useChatContext();
|
||||
const { setOption } = useSetIndexOptions();
|
||||
const timeoutIdRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
const { endpoint, model = null } = conversation ?? {};
|
||||
const models = useMemo(() => {
|
||||
return (modelsQuery.data?.[endpoint ?? ''] ?? []).map((model) => ({
|
||||
label: model,
|
||||
value: model,
|
||||
}));
|
||||
}, [modelsQuery, endpoint]);
|
||||
|
||||
const setModel = useCallback(
|
||||
(model: string) => {
|
||||
setOption('model')(model);
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
timeoutIdRef.current = setTimeout(() => {
|
||||
const textarea = document.getElementById(mainTextareaId);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
}
|
||||
}, 150);
|
||||
},
|
||||
[setOption],
|
||||
);
|
||||
|
||||
return (
|
||||
<ControlCombobox
|
||||
displayValue={model ?? ''}
|
||||
selectPlaceholder={localize('com_ui_select_model')}
|
||||
searchPlaceholder={localize('com_ui_select_search_model')}
|
||||
isCollapsed={isCollapsed}
|
||||
ariaLabel={'model'}
|
||||
selectedValue={model ?? ''}
|
||||
setValue={setModel}
|
||||
items={models}
|
||||
SelectIcon={
|
||||
<MinimalIcon
|
||||
isCreatedByUser={false}
|
||||
endpoint={endpoint}
|
||||
// iconURL={} // for future preset icons
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ const googleCol2: SettingsConfiguration = [
|
|||
];
|
||||
|
||||
const openAI: SettingsConfiguration = [
|
||||
openAIParams.chatGptLabel,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
librechat.maxContextTokens,
|
||||
openAIParams.max_tokens,
|
||||
|
|
@ -495,7 +495,7 @@ const openAI: SettingsConfiguration = [
|
|||
|
||||
const openAICol1: SettingsConfiguration = [
|
||||
baseDefinitions.model as SettingDefinition,
|
||||
openAIParams.chatGptLabel,
|
||||
librechat.modelLabel,
|
||||
librechat.promptPrefix,
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useGetEndpointsQuery } from '~/data-provider';
|
|||
import NavToggle from '~/components/Nav/NavToggle';
|
||||
import { cn, getEndpointField } from '~/utils';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import Switcher from './Switcher';
|
||||
|
||||
import Nav from './Nav';
|
||||
|
||||
const defaultMinSize = 20;
|
||||
|
|
@ -163,27 +163,13 @@ const SidePanel = ({
|
|||
localStorage.setItem('react-resizable-panels:collapsed', 'true');
|
||||
}}
|
||||
className={cn(
|
||||
'sidenav hide-scrollbar border-l border-border-light bg-background transition-opacity',
|
||||
'sidenav hide-scrollbar border-l border-border-light bg-background py-1 transition-opacity',
|
||||
isCollapsed ? 'min-w-[50px]' : 'min-w-[340px] sm:min-w-[352px]',
|
||||
(isSmallScreen && isCollapsed && (minSize === 0 || collapsedSize === 0)) || fullCollapse
|
||||
? 'hidden min-w-0'
|
||||
: 'opacity-100',
|
||||
)}
|
||||
>
|
||||
{interfaceConfig.modelSelect === true && (
|
||||
<div
|
||||
className={cn(
|
||||
'sticky left-0 right-0 top-0 z-[100] flex h-[52px] flex-wrap items-center justify-center bg-background',
|
||||
isCollapsed ? 'h-[52px]' : 'px-2',
|
||||
)}
|
||||
>
|
||||
<Switcher
|
||||
isCollapsed={isCollapsed}
|
||||
endpointKeyProvided={keyProvided}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Nav
|
||||
resize={panelRef.current?.resize}
|
||||
isCollapsed={isCollapsed}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { isAssistantsEndpoint, isAgentsEndpoint } from 'librechat-data-provider';
|
||||
import type { SwitcherProps } from '~/common';
|
||||
import AssistantSwitcher from './AssistantSwitcher';
|
||||
import AgentSwitcher from './AgentSwitcher';
|
||||
import ModelSwitcher from './ModelSwitcher';
|
||||
|
||||
export default function Switcher(props: SwitcherProps) {
|
||||
if (isAssistantsEndpoint(props.endpoint) && props.endpointKeyProvided) {
|
||||
return <AssistantSwitcher {...props} />;
|
||||
} else if (isAgentsEndpoint(props.endpoint) && props.endpointKeyProvided) {
|
||||
return <AgentSwitcher {...props} />;
|
||||
} else if (isAssistantsEndpoint(props.endpoint)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ModelSwitcher {...props} />;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue