mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
👐 refactor: Agents Accessibility and Gemini Error Handling (#5972)
* style: Enhance ControlCombobox with Carat Display, ClassName, and Disabled State * refactor(ModelPanel): replace SelectDropdown with ControlCombobox for improved accessibility * style: Adjust padding and positioning in ModelPanel for improved layout * style(ControlCombobox): add containerClassName and iconSide props for enhanced customization * style(ControlCombobox): add iconClassName prop for customizable icon styling * refactor(AgentPanel): enhance layout with new button for creating agents and adjust structure for better alignment * refactor(AgentSelect): replace SelectDropDown with ControlCombobox for improved accessibility and layout * feat(translation): add new translation key for improved UI clarity * style(AgentSwitcher, AssistantSwitcher): add iconClassName prop for customizable icon styling * refactor(AgentPanelSkeleton): improve layout of skeleton components to match new visual structure * style(AgentPanel, AgentPanelSkeleton): add margin to flex container for improved layout consistency * a11y(AgentSelect, ControlCombobox): add selectId prop for preventing focus going to start to page after agent selection * fix(AgentSelect): update SELECT_ID constant for improved clarity in component identification * fix(GoogleClient): update type annotation, add abort handling for content generation requests, catch "uncaught" abort errors and GoogleGenerativeAI errors from `@google/generative-ai`
This commit is contained in:
parent
1e625f7557
commit
fc733d2b9e
10 changed files with 227 additions and 150 deletions
|
|
@ -641,7 +641,7 @@ class GoogleClient extends BaseClient {
|
|||
let error;
|
||||
try {
|
||||
if (!EXCLUDED_GENAI_MODELS.test(modelName) && !this.project_id) {
|
||||
/** @type {GenAI} */
|
||||
/** @type {GenerativeModel} */
|
||||
const client = this.client;
|
||||
/** @type {GenerateContentRequest} */
|
||||
const requestOptions = {
|
||||
|
|
@ -665,7 +665,17 @@ class GoogleClient extends BaseClient {
|
|||
/** @type {GenAIUsageMetadata} */
|
||||
let usageMetadata;
|
||||
|
||||
const result = await client.generateContentStream(requestOptions);
|
||||
abortController.signal.addEventListener(
|
||||
'abort',
|
||||
() => {
|
||||
logger.warn('[GoogleClient] Request was aborted', abortController.signal.reason);
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
|
||||
const result = await client.generateContentStream(requestOptions, {
|
||||
signal: abortController.signal,
|
||||
});
|
||||
for await (const chunk of result.stream) {
|
||||
usageMetadata = !usageMetadata
|
||||
? chunk?.usageMetadata
|
||||
|
|
|
|||
|
|
@ -146,6 +146,18 @@ process.on('uncaughtException', (err) => {
|
|||
logger.error('There was an uncaught error:', err);
|
||||
}
|
||||
|
||||
if (err.message.includes('abort')) {
|
||||
logger.warn('There was an uncatchable AbortController error.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.message.includes('GoogleGenerativeAI')) {
|
||||
logger.warn(
|
||||
'\n\n`GoogleGenerativeAI` errors cannot be caught due to an upstream issue, see: https://github.com/google-gemini/generative-ai-js/issues/303',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.message.includes('fetch failed')) {
|
||||
if (messageCount === 0) {
|
||||
logger.warn('Meilisearch error, search will be disabled');
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export default function AgentSwitcher({ isCollapsed }: SwitcherProps) {
|
|||
ariaLabel={'agent'}
|
||||
setValue={onSelect}
|
||||
items={agentOptions}
|
||||
iconClassName="assistant-item"
|
||||
SelectIcon={
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { Plus } from 'lucide-react';
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||
import { Controller, useWatch, useForm, FormProvider } from 'react-hook-form';
|
||||
|
|
@ -211,34 +212,54 @@ export default function AgentPanel({
|
|||
className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 overflow-x-hidden"
|
||||
aria-label="Agent configuration form"
|
||||
>
|
||||
<div className="mt-2 flex w-full flex-wrap gap-2">
|
||||
<Controller
|
||||
name="agent"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<AgentSelect
|
||||
reset={reset}
|
||||
value={field.value}
|
||||
agentQuery={agentQuery}
|
||||
setCurrentAgentId={setCurrentAgentId}
|
||||
selectedAgentId={current_agent_id ?? null}
|
||||
createMutation={create}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* Select Button */}
|
||||
<div className="mx-1 mt-2 flex w-full flex-wrap gap-2">
|
||||
<div className="w-full">
|
||||
<Controller
|
||||
name="agent"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<AgentSelect
|
||||
reset={reset}
|
||||
value={field.value}
|
||||
agentQuery={agentQuery}
|
||||
setCurrentAgentId={setCurrentAgentId}
|
||||
selectedAgentId={current_agent_id ?? null}
|
||||
createMutation={create}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Create + Select Button */}
|
||||
{agent_id && (
|
||||
<Button
|
||||
variant="submit"
|
||||
disabled={!agent_id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleSelectAgent();
|
||||
}}
|
||||
aria-label="Select agent"
|
||||
>
|
||||
{localize('com_ui_select')}
|
||||
</Button>
|
||||
<div className="flex w-full gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="w-full justify-center"
|
||||
onClick={() => {
|
||||
reset(defaultAgentFormValues);
|
||||
setCurrentAgentId(undefined);
|
||||
}}
|
||||
>
|
||||
<Plus className="mr-1 h-4 w-4" />
|
||||
{localize('com_ui_create') +
|
||||
' ' +
|
||||
localize('com_ui_new') +
|
||||
' ' +
|
||||
localize('com_ui_agent')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="submit"
|
||||
disabled={!agent_id}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleSelectAgent();
|
||||
}}
|
||||
aria-label={localize('com_ui_select') + ' ' + localize('com_ui_agent')}
|
||||
>
|
||||
{localize('com_ui_select')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!canEditAgent && (
|
||||
|
|
|
|||
|
|
@ -4,10 +4,16 @@ import { Skeleton } from '~/components/ui';
|
|||
export default function AgentPanelSkeleton() {
|
||||
return (
|
||||
<div className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 overflow-x-hidden">
|
||||
{/* Agent Select and Button */}
|
||||
<div className="mt-1 flex w-full gap-2">
|
||||
<Skeleton className="h-[40px] w-4/5 rounded-lg" />
|
||||
<Skeleton className="h-[40px] w-1/5 rounded-lg" />
|
||||
<div className="mx-1 mt-2 flex w-full flex-wrap gap-2">
|
||||
{/* Agent Select Dropdown */}
|
||||
<div className="w-full">
|
||||
<Skeleton className="h-[40px] w-full rounded-md" />
|
||||
</div>
|
||||
{/* Create and Select Buttons */}
|
||||
<div className="flex w-full gap-2">
|
||||
<Skeleton className="h-[40px] w-3/4 rounded-md" /> {/* Create Button */}
|
||||
<Skeleton className="h-[40px] w-1/4 rounded-md" /> {/* Select Button */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-auto bg-white px-4 pb-8 pt-3 dark:bg-transparent">
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { Plus, EarthIcon } from 'lucide-react';
|
||||
import { EarthIcon } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider';
|
||||
import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query';
|
||||
import type { Agent, AgentCreateParams } from 'librechat-data-provider';
|
||||
import type { UseFormReset } from 'react-hook-form';
|
||||
import type { TAgentCapabilities, AgentForm, TAgentOption } from '~/common';
|
||||
import { cn, createDropdownSetter, createProviderOption, processAgentOption } from '~/utils';
|
||||
import { useListAgentsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
import SelectDropDown from '~/components/ui/SelectDropDown';
|
||||
import { cn, createProviderOption, processAgentOption } from '~/utils';
|
||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const keys = new Set(Object.keys(defaultAgentFormValues));
|
||||
const SELECT_ID = 'agent-builder-combobox';
|
||||
|
||||
export default function AgentSelect({
|
||||
reset,
|
||||
|
|
@ -120,6 +121,9 @@ export default function AgentSelect({
|
|||
}
|
||||
|
||||
resetAgentForm(agent);
|
||||
setTimeout(() => {
|
||||
document.getElementById(SELECT_ID)?.focus();
|
||||
}, 5);
|
||||
},
|
||||
[agents, createMutation, setCurrentAgentId, agentQuery.data, resetAgentForm, reset],
|
||||
);
|
||||
|
|
@ -152,51 +156,36 @@ export default function AgentSelect({
|
|||
}, [selectedAgentId, agents, onSelect]);
|
||||
|
||||
const createAgent = localize('com_ui_create') + ' ' + localize('com_ui_agent');
|
||||
const hasAgentValue = !!(typeof currentAgentValue === 'object'
|
||||
? currentAgentValue.value != null && currentAgentValue.value !== ''
|
||||
: typeof currentAgentValue !== 'undefined');
|
||||
|
||||
return (
|
||||
<SelectDropDown
|
||||
value={!hasAgentValue ? createAgent : (currentAgentValue as TAgentOption)}
|
||||
setValue={createDropdownSetter(onSelect)}
|
||||
availableValues={
|
||||
agents ?? [
|
||||
<ControlCombobox
|
||||
selectId={SELECT_ID}
|
||||
containerClassName="px-0"
|
||||
selectedValue={(currentAgentValue?.value ?? '') + ''}
|
||||
displayValue={currentAgentValue?.label ?? ''}
|
||||
selectPlaceholder={createAgent}
|
||||
iconSide="right"
|
||||
searchPlaceholder={localize('com_agents_search_name')}
|
||||
SelectIcon={currentAgentValue?.icon}
|
||||
setValue={onSelect}
|
||||
items={
|
||||
agents?.map((agent) => ({
|
||||
label: agent.name ?? '',
|
||||
value: agent.id ?? '',
|
||||
icon: agent.icon,
|
||||
})) ?? [
|
||||
{
|
||||
label: 'Loading...',
|
||||
value: '',
|
||||
},
|
||||
]
|
||||
}
|
||||
iconSide="left"
|
||||
optionIconSide="right"
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
emptyTitle={true}
|
||||
showOptionIcon={true}
|
||||
containerClassName="flex-grow"
|
||||
searchClassName="dark:from-gray-850"
|
||||
searchPlaceholder={localize('com_agents_search_name')}
|
||||
optionsClass="hover:bg-gray-20/50 dark:border-gray-700"
|
||||
optionsListClass="rounded-lg shadow-lg dark:bg-gray-850 dark:border-gray-700 dark:last:border"
|
||||
currentValueClass={cn(
|
||||
'text-md font-semibold text-gray-900 dark:text-white',
|
||||
hasAgentValue ? 'text-gray-500' : '',
|
||||
)}
|
||||
className={cn(
|
||||
'rounded-md dark:border-gray-700 dark:bg-gray-850',
|
||||
'z-50 flex h-[40px] w-full flex-none items-center justify-center truncate px-4 hover:cursor-pointer hover:border-green-500 focus:border-gray-400',
|
||||
)}
|
||||
renderOption={() => (
|
||||
<span className="flex items-center gap-1.5 truncate">
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-2 text-gray-800 dark:text-gray-100">
|
||||
<Plus className="w-[16px]" />
|
||||
</span>
|
||||
<span className={cn('ml-4 flex h-6 items-center gap-1 text-gray-800 dark:text-gray-100')}>
|
||||
{createAgent}
|
||||
</span>
|
||||
</span>
|
||||
'z-50 flex h-[40px] w-full flex-none items-center justify-center truncate rounded-md bg-transparent font-bold',
|
||||
)}
|
||||
ariaLabel={localize('com_ui_agent')}
|
||||
isCollapsed={false}
|
||||
showCarat={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import React, { useMemo, useEffect } from 'react';
|
||||
import { ChevronLeft, RotateCcw } from 'lucide-react';
|
||||
import { getSettingsKeys } from 'librechat-data-provider';
|
||||
import { useFormContext, useWatch, Controller } from 'react-hook-form';
|
||||
import { getSettingsKeys, alternateName } from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
|
||||
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
||||
import { agentSettings } from '~/components/SidePanel/Parameters/settings';
|
||||
import { getEndpointField, cn, cardStyle } from '~/utils';
|
||||
import ControlCombobox from '~/components/ui/ControlCombobox';
|
||||
import { useGetEndpointsQuery } from '~/data-provider';
|
||||
import { SelectDropDown } from '~/components/ui';
|
||||
import { getEndpointField, cn } from '~/utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { Panel } from '~/common';
|
||||
|
||||
|
|
@ -78,8 +78,8 @@ export default function Parameters({
|
|||
|
||||
return (
|
||||
<div className="scrollbar-gutter-stable h-full min-h-[50vh] overflow-auto pb-12 text-sm">
|
||||
<div className="model-panel relative flex flex-col items-center px-16 py-6 text-center">
|
||||
<div className="absolute left-0 top-6">
|
||||
<div className="model-panel relative flex flex-col items-center px-16 py-4 text-center">
|
||||
<div className="absolute left-0 top-4">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-neutral relative"
|
||||
|
|
@ -109,37 +109,41 @@ export default function Parameters({
|
|||
name="provider"
|
||||
control={control}
|
||||
rules={{ required: true, minLength: 1 }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
id="provider"
|
||||
aria-labelledby="provider-label"
|
||||
aria-label={localize('com_ui_provider')}
|
||||
aria-required="true"
|
||||
emptyTitle={true}
|
||||
value={field.value ?? ''}
|
||||
title={localize('com_ui_provider')}
|
||||
placeholder={localize('com_ui_select_provider')}
|
||||
searchPlaceholder={localize('com_ui_select_search_provider')}
|
||||
setValue={field.onChange}
|
||||
availableValues={providers}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-9 w-full flex-none items-center justify-center border-none px-4 hover:cursor-pointer',
|
||||
(field.value === undefined || field.value === '') &&
|
||||
'border-2 border-yellow-400',
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
const value =
|
||||
typeof field.value === 'string'
|
||||
? field.value
|
||||
: ((field.value as StringOption)?.value ?? '');
|
||||
const display =
|
||||
typeof field.value === 'string'
|
||||
? field.value
|
||||
: ((field.value as StringOption)?.label ?? '');
|
||||
|
||||
return (
|
||||
<>
|
||||
<ControlCombobox
|
||||
selectedValue={value}
|
||||
displayValue={alternateName[display] ?? display}
|
||||
selectPlaceholder={localize('com_ui_select_provider')}
|
||||
searchPlaceholder={localize('com_ui_select_search_provider')}
|
||||
setValue={field.onChange}
|
||||
items={providers.map((provider) => ({
|
||||
label: typeof provider === 'string' ? provider : provider.label,
|
||||
value: typeof provider === 'string' ? provider : provider.value,
|
||||
}))}
|
||||
className={cn(error ? 'border-2 border-red-500' : '')}
|
||||
ariaLabel={localize('com_ui_provider')}
|
||||
isCollapsed={false}
|
||||
showCarat={true}
|
||||
/>
|
||||
{error && (
|
||||
<span className="model-panel-error text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{error && (
|
||||
<span className="model-panel-error text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* Model */}
|
||||
|
|
@ -158,39 +162,36 @@ export default function Parameters({
|
|||
name="model"
|
||||
control={control}
|
||||
rules={{ required: true, minLength: 1 }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
id="model"
|
||||
aria-labelledby="model-label"
|
||||
aria-label={localize('com_ui_model')}
|
||||
aria-required="true"
|
||||
emptyTitle={true}
|
||||
placeholder={
|
||||
provider
|
||||
? localize('com_ui_select_model')
|
||||
: localize('com_ui_select_provider_first')
|
||||
}
|
||||
value={field.value}
|
||||
setValue={field.onChange}
|
||||
availableValues={models}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
disabled={!provider}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4',
|
||||
!provider ? 'cursor-not-allowed bg-gray-200' : 'hover:cursor-pointer',
|
||||
render={({ field, fieldState: { error } }) => {
|
||||
return (
|
||||
<>
|
||||
<ControlCombobox
|
||||
selectedValue={field.value || ''}
|
||||
selectPlaceholder={
|
||||
provider
|
||||
? localize('com_ui_select_model')
|
||||
: localize('com_ui_select_provider_first')
|
||||
}
|
||||
searchPlaceholder={localize('com_ui_select_model')}
|
||||
setValue={field.onChange}
|
||||
items={models.map((model) => ({
|
||||
label: model,
|
||||
value: model,
|
||||
}))}
|
||||
disabled={!provider}
|
||||
className={cn('disabled:opacity-50', error ? 'border-2 border-red-500' : '')}
|
||||
ariaLabel={localize('com_ui_model')}
|
||||
isCollapsed={false}
|
||||
showCarat={true}
|
||||
/>
|
||||
{provider && error && (
|
||||
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{provider && error && (
|
||||
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
|||
ariaLabel={'assistant'}
|
||||
setValue={onSelect}
|
||||
items={assistantOptions}
|
||||
iconClassName="assistant-item"
|
||||
SelectIcon={
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Search } from 'lucide-react';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import { matchSorter } from 'match-sorter';
|
||||
import { Search, ChevronDown } from 'lucide-react';
|
||||
import { useMemo, useState, useRef, memo, useEffect } from 'react';
|
||||
import { SelectRenderer } from '@ariakit/react-core/select/select-renderer';
|
||||
import type { OptionWithIcon } from '~/common';
|
||||
|
|
@ -16,6 +16,13 @@ interface ControlComboboxProps {
|
|||
selectPlaceholder?: string;
|
||||
isCollapsed: boolean;
|
||||
SelectIcon?: React.ReactNode;
|
||||
containerClassName?: string;
|
||||
iconClassName?: string;
|
||||
showCarat?: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
iconSide?: 'left' | 'right';
|
||||
selectId?: string;
|
||||
}
|
||||
|
||||
const ROW_HEIGHT = 36;
|
||||
|
|
@ -28,8 +35,15 @@ function ControlCombobox({
|
|||
ariaLabel,
|
||||
searchPlaceholder,
|
||||
selectPlaceholder,
|
||||
containerClassName,
|
||||
isCollapsed,
|
||||
SelectIcon,
|
||||
showCarat,
|
||||
className,
|
||||
disabled,
|
||||
iconClassName,
|
||||
iconSide = 'left',
|
||||
selectId,
|
||||
}: ControlComboboxProps) {
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
|
@ -70,28 +84,48 @@ function ControlCombobox({
|
|||
}
|
||||
}, [isCollapsed]);
|
||||
|
||||
const selectIconClassName = cn(
|
||||
'flex h-5 w-5 items-center justify-center overflow-hidden rounded-full',
|
||||
iconClassName,
|
||||
);
|
||||
const optionIconClassName = cn(
|
||||
'mr-2 flex h-5 w-5 items-center justify-center overflow-hidden rounded-full',
|
||||
iconClassName,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center justify-center px-1">
|
||||
<div className={cn('flex w-full items-center justify-center px-1', containerClassName)}>
|
||||
<Ariakit.SelectLabel store={select} className="sr-only">
|
||||
{ariaLabel}
|
||||
</Ariakit.SelectLabel>
|
||||
<Ariakit.Select
|
||||
ref={buttonRef}
|
||||
store={select}
|
||||
id={selectId}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'flex items-center justify-center gap-2 rounded-full bg-surface-secondary',
|
||||
'text-text-primary hover:bg-surface-tertiary',
|
||||
'border border-border-light',
|
||||
isCollapsed ? 'h-10 w-10' : 'h-10 w-full rounded-md px-3 py-2 text-sm',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{SelectIcon != null && (
|
||||
<div className="assistant-item flex h-5 w-5 items-center justify-center overflow-hidden rounded-full">
|
||||
{SelectIcon}
|
||||
</div>
|
||||
{SelectIcon != null && iconSide === 'left' && (
|
||||
<div className={selectIconClassName}>{SelectIcon}</div>
|
||||
)}
|
||||
{!isCollapsed && (
|
||||
<span className="flex-grow truncate text-left">{displayValue ?? selectPlaceholder}</span>
|
||||
<>
|
||||
<span className="flex-grow truncate text-left">
|
||||
{displayValue != null
|
||||
? displayValue || selectPlaceholder
|
||||
: selectedValue || selectPlaceholder}
|
||||
</span>
|
||||
{SelectIcon != null && iconSide === 'right' && (
|
||||
<div className={selectIconClassName}>{SelectIcon}</div>
|
||||
)}
|
||||
{showCarat && <ChevronDown className="h-4 w-4 text-text-secondary" />}
|
||||
</>
|
||||
)}
|
||||
</Ariakit.Select>
|
||||
<Ariakit.SelectPopover
|
||||
|
|
@ -126,12 +160,13 @@ function ControlCombobox({
|
|||
)}
|
||||
render={<Ariakit.SelectItem value={value} />}
|
||||
>
|
||||
{icon != null && (
|
||||
<div className="assistant-item mr-2 flex h-5 w-5 items-center justify-center overflow-hidden rounded-full">
|
||||
{icon}
|
||||
</div>
|
||||
{icon != null && iconSide === 'left' && (
|
||||
<div className={optionIconClassName}>{icon}</div>
|
||||
)}
|
||||
<span className="flex-grow truncate text-left">{label}</span>
|
||||
{icon != null && iconSide === 'right' && (
|
||||
<div className={optionIconClassName}>{icon}</div>
|
||||
)}
|
||||
</Ariakit.ComboboxItem>
|
||||
)}
|
||||
</SelectRenderer>
|
||||
|
|
|
|||
|
|
@ -685,6 +685,7 @@
|
|||
"com_ui_more_info": "More info",
|
||||
"com_ui_my_prompts": "My Prompts",
|
||||
"com_ui_name": "Name",
|
||||
"com_ui_new": "New",
|
||||
"com_ui_new_chat": "New chat",
|
||||
"com_ui_next": "Next",
|
||||
"com_ui_no": "No",
|
||||
|
|
@ -828,4 +829,4 @@
|
|||
"com_ui_zoom": "Zoom",
|
||||
"com_user_message": "You",
|
||||
"com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue