From 55f5f2d11a29a45ba460c46dab49add019e3b13c Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Tue, 29 Apr 2025 03:49:02 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=93=EF=B8=8F=20feat:=20Add=20Special?= =?UTF-8?q?=20Variables=20for=20Prompts=20&=20Agents,=20Prompt=20UI=20Impr?= =?UTF-8?q?ovements=20(#7123)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip: Add Instructions component for agent configuration * ✨ feat: Implement DropdownPopup for variable insertion in instructions * refactor: Enhance variable handling by exporting specialVariables and updating Markdown components * feat: Add special variable support for current date and user in Instructions component * refactor: Update handleAddVariable to include localized label * feat: replace special variables in instructions presets * chore: update parameter type for user in getListAgents function * refactor: integrate dayjs for date handling and move replaceSpecialVars function to data-provider * feat: enhance replaceSpecialVars to include day number in current date format * feat: integrate replaceSpecialVars for processing agent instructions * feat: add support for current date & time in replaceSpecialVars function * feat: add iso_datetime support in replaceSpecialVars function * fix: enforce text parameter to be a required field in replaceSpecialVars function * feat: add ISO datetime support in translation file * fix: disable eslint warning for autoFocus in TextareaAutosize component * feat: add VariablesDropdown component and integrate it into CreatePromptForm and PromptEditor; update translation for special variables * fix: CategorySelector and related localizations * fix: add z-index class to LanguageSTTDropdown for proper stacking context * fix: add max-height and overflow styles to OGDialogContent in VariableDialog and PreviewPrompt components * fix: update variable detection logic to exclude special variables and improve regex matching * fix: improve accessibility text for actions menu in ChatGroupItem component * fix: adjust max-width and height styles for dialog components and improve markdown rendering for light vs. dark, height/widths, etc. * fix: remove commented-out code for better readability in PromptVariableGfm component * fix: handle undefined input parameter in setParams function call * fix: update variable label types to use TSpecialVarLabel for consistency * fix: remove outdated information from special variables description in translation file * fix: enhance unused i18next keys detection for special variable keys * fix: update color classes for consistency/a11y in category and prompt variable components * fix: update PromptVariableGfm component and special variable styles for consistency * fix: improve variable highlighting logic in VariableForm component * fix: update background color classes for consistency in VariableForm component * fix: add missing ref parameter to Dialog component in OriginalDialog * refactor: move navigate call for new conversation to after setConversation update * refactor: move message query hook to client workspace; fix: handle edge case for navigation from finalHandler creating race condition for response message DB save * chore: bump librechat-data-provider to 0.7.793 * ci: add unit tests for replaceSpecialVars function * fix: implement getToolkitKey function for image_gen_oai toolkit filtering/including * ci: enhance dayjs mock for consistent date/time values in tests * fix: MCP stdio server fail to start when passing env property * fix: use optional chaining for clientRef dereferencing in AskController and EditController feat: add context to saveMessage call in streamResponse utility * fix: only save error messages if the userMessageId was initialized * refactor: add isNotAppendable check to disable inputs in ChatForm and useTextarea * feat: enhance error handling in useEventHandlers and update conversation state in useNewConvo * refactor: prepend underscore to conversationId in newConversation template * feat: log aborted conversations with minimal messages and use consistent conversationId generation --------- Co-authored-by: Olivier Schiavo Co-authored-by: aka012 Co-authored-by: jiasheng --- .github/workflows/i18n-unused-keys.yml | 35 ++++- api/models/Agent.js | 2 +- api/server/controllers/AskController.js | 5 +- api/server/controllers/EditController.js | 5 +- api/server/controllers/PluginController.js | 3 +- api/server/controllers/agents/request.js | 1 + api/server/middleware/abortMiddleware.js | 4 +- api/server/services/ActionService.js | 2 +- .../services/Endpoints/agents/initialize.js | 8 ++ api/server/services/ToolService.js | 32 ++++- api/server/utils/streamResponse.js | 8 +- client/src/components/Chat/ChatView.tsx | 4 +- client/src/components/Chat/Input/ChatForm.tsx | 16 ++- .../components/Chat/Input/PromptsCommand.tsx | 2 +- .../Speech/STT/LanguageSTTDropdown.tsx | 2 +- .../Prompts/Groups/CategoryIcon.tsx | 4 +- .../Prompts/Groups/CategorySelector.tsx | 33 ++++- .../Prompts/Groups/ChatGroupItem.tsx | 8 +- .../Prompts/Groups/CreatePromptForm.tsx | 4 +- .../Prompts/Groups/VariableDialog.tsx | 2 +- .../Prompts/Groups/VariableForm.tsx | 25 ++-- client/src/components/Prompts/Markdown.tsx | 7 +- .../src/components/Prompts/PreviewPrompt.tsx | 2 +- .../src/components/Prompts/PromptDetails.tsx | 6 +- .../src/components/Prompts/PromptEditor.tsx | 7 +- .../components/Prompts/PromptVariables.tsx | 20 +-- .../components/Prompts/VariablesDropdown.tsx | 75 +++++++++++ .../SidePanel/Agents/AgentConfig.tsx | 35 +---- .../SidePanel/Agents/Instructions.tsx | 127 ++++++++++++++++++ client/src/components/ui/OriginalDialog.tsx | 3 +- client/src/data-provider/Messages/index.ts | 2 +- client/src/data-provider/Messages/queries.ts | 42 ++++++ client/src/hooks/Chat/useChatFunctions.ts | 27 ++-- client/src/hooks/Chat/useChatHelpers.ts | 4 +- client/src/hooks/Input/useTextarea.ts | 16 ++- client/src/hooks/Messages/useSubmitMessage.ts | 3 +- client/src/hooks/SSE/useEventHandlers.ts | 42 ++++-- client/src/hooks/useNewConvo.ts | 20 ++- client/src/locales/en/translation.json | 14 +- client/src/utils/prompts.ts | 48 +++---- package-lock.json | 9 +- packages/data-provider/package.json | 3 +- packages/data-provider/specs/parsers.spec.ts | 125 +++++++++++++++++ packages/data-provider/src/config.ts | 9 ++ packages/data-provider/src/parsers.ts | 26 ++++ .../src/react-query/react-query-service.ts | 16 --- packages/mcp/src/connection.ts | 9 +- 47 files changed, 707 insertions(+), 195 deletions(-) create mode 100644 client/src/components/Prompts/VariablesDropdown.tsx create mode 100644 client/src/components/SidePanel/Agents/Instructions.tsx create mode 100644 client/src/data-provider/Messages/queries.ts create mode 100644 packages/data-provider/specs/parsers.spec.ts diff --git a/.github/workflows/i18n-unused-keys.yml b/.github/workflows/i18n-unused-keys.yml index 5e29a8a8b..f720a6178 100644 --- a/.github/workflows/i18n-unused-keys.yml +++ b/.github/workflows/i18n-unused-keys.yml @@ -39,12 +39,35 @@ jobs: # Check if each key is used in the source code for KEY in $KEYS; do FOUND=false - for DIR in "${SOURCE_DIRS[@]}"; do - if grep -r --include=\*.{js,jsx,ts,tsx} -q "$KEY" "$DIR"; then - FOUND=true - break + + # Special case for dynamically constructed special variable keys + if [[ "$KEY" == com_ui_special_var_* ]]; then + # Check if TSpecialVarLabel is used in the codebase + for DIR in "${SOURCE_DIRS[@]}"; do + if grep -r --include=\*.{js,jsx,ts,tsx} -q "TSpecialVarLabel" "$DIR"; then + FOUND=true + break + fi + done + + # Also check if the key is directly used somewhere + if [[ "$FOUND" == false ]]; then + for DIR in "${SOURCE_DIRS[@]}"; do + if grep -r --include=\*.{js,jsx,ts,tsx} -q "$KEY" "$DIR"; then + FOUND=true + break + fi + done fi - done + else + # Regular check for other keys + for DIR in "${SOURCE_DIRS[@]}"; do + if grep -r --include=\*.{js,jsx,ts,tsx} -q "$KEY" "$DIR"; then + FOUND=true + break + fi + done + fi if [[ "$FOUND" == false ]]; then UNUSED_KEYS+=("$KEY") @@ -90,4 +113,4 @@ jobs: - name: Fail workflow if unused keys found if: env.unused_keys != '[]' - run: exit 1 \ No newline at end of file + run: exit 1 diff --git a/api/models/Agent.js b/api/models/Agent.js index 0e4321df9..9b34eeae6 100644 --- a/api/models/Agent.js +++ b/api/models/Agent.js @@ -308,7 +308,7 @@ const getListAgents = async (searchParameter) => { * This function also updates the corresponding projects to include or exclude the agent ID. * * @param {Object} params - Parameters for updating the agent's projects. - * @param {import('librechat-data-provider').TUser} params.user - Parameters for updating the agent's projects. + * @param {MongoUser} params.user - Parameters for updating the agent's projects. * @param {string} params.agentId - The ID of the agent to update. * @param {string[]} [params.projectIds] - Array of project IDs to add to the agent. * @param {string[]} [params.removeProjectIds] - Array of project IDs to remove from the agent. diff --git a/api/server/controllers/AskController.js b/api/server/controllers/AskController.js index 8904a5722..7676b7882 100644 --- a/api/server/controllers/AskController.js +++ b/api/server/controllers/AskController.js @@ -128,7 +128,7 @@ const AskController = async (req, res, next, initializeClient, addTitle) => { clientRef = new WeakRef(client); getAbortData = () => { - const currentClient = clientRef.deref(); + const currentClient = clientRef?.deref(); const currentText = currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText(); @@ -255,7 +255,7 @@ const AskController = async (req, res, next, initializeClient, addTitle) => { logger.error('[AskController] Error handling request', error); let partialText = ''; try { - const currentClient = clientRef.deref(); + const currentClient = clientRef?.deref(); partialText = currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText(); } catch (getTextError) { @@ -268,6 +268,7 @@ const AskController = async (req, res, next, initializeClient, addTitle) => { conversationId: reqDataContext.conversationId, messageId: reqDataContext.responseMessageId, parentMessageId: overrideParentMessageId ?? reqDataContext.userMessageId ?? parentMessageId, + userMessageId: reqDataContext.userMessageId, }) .catch((err) => { logger.error('[AskController] Error in `handleAbortError` during catch block', err); diff --git a/api/server/controllers/EditController.js b/api/server/controllers/EditController.js index e7ebc0785..d142d474d 100644 --- a/api/server/controllers/EditController.js +++ b/api/server/controllers/EditController.js @@ -123,7 +123,7 @@ const EditController = async (req, res, next, initializeClient) => { clientRef = new WeakRef(client); getAbortData = () => { - const currentClient = clientRef.deref(); + const currentClient = clientRef?.deref(); const currentText = currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText(); @@ -219,7 +219,7 @@ const EditController = async (req, res, next, initializeClient) => { logger.error('[EditController] Error handling request', error); let partialText = ''; try { - const currentClient = clientRef.deref(); + const currentClient = clientRef?.deref(); partialText = currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText(); } catch (getTextError) { @@ -232,6 +232,7 @@ const EditController = async (req, res, next, initializeClient) => { conversationId, messageId: reqDataContext.responseMessageId, parentMessageId: overrideParentMessageId ?? userMessageId ?? parentMessageId, + userMessageId, }) .catch((err) => { logger.error('[EditController] Error in `handleAbortError` during catch block', err); diff --git a/api/server/controllers/PluginController.js b/api/server/controllers/PluginController.js index 71e7ed348..8bb1df59e 100644 --- a/api/server/controllers/PluginController.js +++ b/api/server/controllers/PluginController.js @@ -1,5 +1,6 @@ const { CacheKeys, AuthType } = require('librechat-data-provider'); const { addOpenAPISpecs } = require('~/app/clients/tools/util/addOpenAPISpecs'); +const { getToolkitKey } = require('~/server/services/ToolService'); const { getCustomConfig } = require('~/server/services/Config'); const { availableTools } = require('~/app/clients/tools'); const { getMCPManager } = require('~/config'); @@ -128,7 +129,7 @@ const getAvailableTools = async (req, res) => { (plugin) => toolDefinitions[plugin.pluginKey] !== undefined || (plugin.toolkit === true && - Object.keys(toolDefinitions).some((key) => key.startsWith(`${plugin.pluginKey}_`))), + Object.keys(toolDefinitions).some((key) => getToolkitKey(key) === plugin.pluginKey)), ); await cache.set(CacheKeys.TOOLS, tools); diff --git a/api/server/controllers/agents/request.js b/api/server/controllers/agents/request.js index 80b70a745..fcee62edc 100644 --- a/api/server/controllers/agents/request.js +++ b/api/server/controllers/agents/request.js @@ -259,6 +259,7 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => { sender, messageId: responseMessageId, parentMessageId: overrideParentMessageId ?? userMessageId ?? parentMessageId, + userMessageId, }) .catch((err) => { logger.error('[api/server/controllers/agents/request] Error in `handleAbortError`', err); diff --git a/api/server/middleware/abortMiddleware.js b/api/server/middleware/abortMiddleware.js index b5a7c4eb9..bfc28f513 100644 --- a/api/server/middleware/abortMiddleware.js +++ b/api/server/middleware/abortMiddleware.js @@ -311,7 +311,7 @@ const handleAbortError = async (res, req, error, data) => { } else { logger.error('[handleAbortError] AI response error; aborting request:', error); } - const { sender, conversationId, messageId, parentMessageId, partialText } = data; + const { sender, conversationId, messageId, parentMessageId, userMessageId, partialText } = data; if (error.stack && error.stack.includes('google')) { logger.warn( @@ -344,10 +344,10 @@ const handleAbortError = async (res, req, error, data) => { parentMessageId, text: errorText, user: req.user.id, - shouldSaveMessage: true, spec: endpointOption?.spec, iconURL: endpointOption?.iconURL, modelLabel: endpointOption?.modelLabel, + shouldSaveMessage: userMessageId != null, model: endpointOption?.modelOptions?.model || req.body?.model, }; diff --git a/api/server/services/ActionService.js b/api/server/services/ActionService.js index 1255b3db4..c8a795542 100644 --- a/api/server/services/ActionService.js +++ b/api/server/services/ActionService.js @@ -146,7 +146,7 @@ async function createActionTool({ /** @type {import('librechat-data-provider').ActionMetadataRuntime} */ const metadata = action.metadata; const executor = requestBuilder.createExecutor(); - const preparedExecutor = executor.setParams(toolInput); + const preparedExecutor = executor.setParams(toolInput ?? {}); if (metadata.auth && metadata.auth.type !== AuthTypeEnum.None) { try { diff --git a/api/server/services/Endpoints/agents/initialize.js b/api/server/services/Endpoints/agents/initialize.js index e26ed0884..d0d29fa75 100644 --- a/api/server/services/Endpoints/agents/initialize.js +++ b/api/server/services/Endpoints/agents/initialize.js @@ -6,6 +6,7 @@ const { EToolResources, getResponseSender, AgentCapabilities, + replaceSpecialVars, providerEndpointMap, } = require('librechat-data-provider'); const { @@ -246,6 +247,13 @@ const initializeAgentOptions = async ({ agent.model_parameters.model = agent.model; } + if (agent.instructions && agent.instructions !== '') { + agent.instructions = replaceSpecialVars({ + text: agent.instructions, + user: req.user, + }); + } + if (typeof agent.artifacts === 'string' && agent.artifacts !== '') { agent.additional_instructions = generateArtifactsPrompt({ endpoint: agent.provider, diff --git a/api/server/services/ToolService.js b/api/server/services/ToolService.js index f3e7df5a7..b71e97f74 100644 --- a/api/server/services/ToolService.js +++ b/api/server/services/ToolService.js @@ -8,6 +8,7 @@ const { ErrorTypes, ContentTypes, imageGenTools, + EToolResources, EModelEndpoint, actionDelimiter, ImageVisionTool, @@ -36,6 +37,30 @@ const { redactMessage } = require('~/config/parsers'); const { sleep } = require('~/server/utils'); const { logger } = require('~/config'); +/** + * @param {string} toolName + * @returns {string | undefined} toolKey + */ +function getToolkitKey(toolName) { + /** @type {string|undefined} */ + let toolkitKey; + for (const toolkit of toolkits) { + if (toolName.startsWith(EToolResources.image_edit)) { + const splitMatches = toolkit.pluginKey.split('_'); + const suffix = splitMatches[splitMatches.length - 1]; + if (toolName.endsWith(suffix)) { + toolkitKey = toolkit.pluginKey; + break; + } + } + if (toolName.startsWith(toolkit.pluginKey)) { + toolkitKey = toolkit.pluginKey; + break; + } + } + return toolkitKey; +} + /** * Loads and formats tools from the specified tool directory. * @@ -108,7 +133,7 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] }) tools.push(formattedTool); } - /** Basic Tools; schema: { input: string } */ + /** Basic Tools & Toolkits; schema: { input: string } */ const basicToolInstances = [ new Calculator(), ...createOpenAIImageTools({ override: true }), @@ -117,9 +142,7 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] }) for (const toolInstance of basicToolInstances) { const formattedTool = formatToOpenAIAssistantTool(toolInstance); let toolName = formattedTool[Tools.function].name; - toolName = toolkits.some((toolkit) => toolName.startsWith(toolkit.pluginKey)) - ? toolName.split('_')[0] - : toolName; + toolName = getToolkitKey(toolName) ?? toolName; if (filter.has(toolName) && included.size === 0) { continue; } @@ -682,6 +705,7 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey }) } module.exports = { + getToolkitKey, loadAgentTools, loadAndFormatTools, processRequiredActions, diff --git a/api/server/utils/streamResponse.js b/api/server/utils/streamResponse.js index 0f042339a..bb8d63b22 100644 --- a/api/server/utils/streamResponse.js +++ b/api/server/utils/streamResponse.js @@ -70,7 +70,13 @@ const sendError = async (req, res, options, callback) => { } if (shouldSaveMessage) { - await saveMessage(req, { ...errorMessage, user }); + await saveMessage( + req, + { ...errorMessage, user }, + { + context: 'api/server/utils/streamResponse.js - sendError', + }, + ); } if (!errorMessage.error) { diff --git a/client/src/components/Chat/ChatView.tsx b/client/src/components/Chat/ChatView.tsx index bce6974c1..a554c5f7d 100644 --- a/client/src/components/Chat/ChatView.tsx +++ b/client/src/components/Chat/ChatView.tsx @@ -1,14 +1,14 @@ -import { memo, useMemo, useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useRecoilValue } from 'recoil'; import { useForm } from 'react-hook-form'; import { useParams } from 'react-router-dom'; import { Constants } from 'librechat-data-provider'; -import { useGetMessagesByConvoId } from 'librechat-data-provider/react-query'; import type { TMessage } from 'librechat-data-provider'; import type { ChatFormValues } from '~/common'; import { ChatContext, AddedChatContext, useFileMapContext, ChatFormProvider } from '~/Providers'; import { useChatHelpers, useAddedResponse, useSSE } from '~/hooks'; import ConversationStarters from './Input/ConversationStarters'; +import { useGetMessagesByConvoId } from '~/data-provider'; import MessagesView from './Messages/MessagesView'; import { Spinner } from '~/components/svg'; import Presentation from './Presentation'; diff --git a/client/src/components/Chat/Input/ChatForm.tsx b/client/src/components/Chat/Input/ChatForm.tsx index 4e5e68c07..2570d213c 100644 --- a/client/src/components/Chat/Input/ChatForm.tsx +++ b/client/src/components/Chat/Input/ChatForm.tsx @@ -132,7 +132,13 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { setShowPlusPopover, setShowMentionPopover, }); - const { handlePaste, handleKeyDown, handleCompositionStart, handleCompositionEnd } = useTextarea({ + const { + isNotAppendable, + handlePaste, + handleKeyDown, + handleCompositionStart, + handleCompositionEnd, + } = useTextarea({ textAreaRef, submitButtonRef, setIsScrollable, @@ -251,7 +257,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { ref(e); (textAreaRef as React.MutableRefObject).current = e; }} - disabled={disableInputs} + disabled={disableInputs || isNotAppendable} onPaste={handlePaste} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} @@ -271,7 +277,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { className={cn( baseClasses, removeFocusRings, - 'transition-[max-height] duration-200', + 'transition-[max-height] duration-200 disabled:cursor-not-allowed', )} />
@@ -306,7 +312,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { methods={methods} ask={submitMessage} textAreaRef={textAreaRef} - disabled={disableInputs} + disabled={disableInputs || isNotAppendable} isSubmitting={isSubmitting} /> )} @@ -318,7 +324,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { ) )} diff --git a/client/src/components/Chat/Input/PromptsCommand.tsx b/client/src/components/Chat/Input/PromptsCommand.tsx index c34ffc070..835c7cd31 100644 --- a/client/src/components/Chat/Input/PromptsCommand.tsx +++ b/client/src/components/Chat/Input/PromptsCommand.tsx @@ -201,7 +201,7 @@ function PromptsCommand({
); diff --git a/client/src/components/Prompts/Groups/CategoryIcon.tsx b/client/src/components/Prompts/Groups/CategoryIcon.tsx index faa8df1c6..b9d37f1af 100644 --- a/client/src/components/Prompts/Groups/CategoryIcon.tsx +++ b/client/src/components/Prompts/Groups/CategoryIcon.tsx @@ -28,9 +28,9 @@ const categoryColorMap: Record = { code: 'text-red-500', misc: 'text-blue-300', shop: 'text-purple-400', - idea: 'text-yellow-300', + idea: 'text-yellow-500/90 dark:text-yellow-300 ', write: 'text-purple-400', - travel: 'text-yellow-300', + travel: 'text-yellow-500/90 dark:text-yellow-300 ', finance: 'text-orange-400', roleplay: 'text-orange-400', teach_or_explain: 'text-blue-300', diff --git a/client/src/components/Prompts/Groups/CategorySelector.tsx b/client/src/components/Prompts/Groups/CategorySelector.tsx index 6e2245e8c..3c5777892 100644 --- a/client/src/components/Prompts/Groups/CategorySelector.tsx +++ b/client/src/components/Prompts/Groups/CategorySelector.tsx @@ -1,4 +1,6 @@ import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { ReactNode } from 'react'; import { useFormContext, Controller } from 'react-hook-form'; import { LocalStorageKeys } from 'librechat-data-provider'; import { Dropdown } from '~/components/ui'; @@ -15,6 +17,7 @@ const CategorySelector: React.FC = ({ onValueChange, className = '', }) => { + const { t } = useTranslation(); const formContext = useFormContext(); const { categories, emptyCategory } = useCategories(); @@ -32,13 +35,25 @@ const CategorySelector: React.FC = ({ [watchedCategory, categories, currentCategory, emptyCategory], ); + const displayCategory = useMemo(() => { + if (!categoryOption.value && !('icon' in categoryOption)) { + return { + ...categoryOption, + icon: () as ReactNode, + label: categoryOption.label || t('com_ui_empty_category'), + }; + } + return categoryOption; + }, [categoryOption, t]); + return formContext ? ( ( { setValue('category', value, { shouldDirty: false }); localStorage.setItem(LocalStorageKeys.LAST_PROMPT_CATEGORY, value); @@ -48,10 +63,12 @@ const CategorySelector: React.FC = ({ ariaLabel="Prompt's category selector" className={className} options={categories || []} - renderValue={(option) => ( + renderValue={() => (
- {option.icon != null && {option.icon as React.ReactNode}} - {option.label} + {'icon' in displayCategory && displayCategory.icon != null && ( + {displayCategory.icon as ReactNode} + )} + {displayCategory.label}
)} /> @@ -68,10 +85,12 @@ const CategorySelector: React.FC = ({ ariaLabel="Prompt's category selector" className={className} options={categories || []} - renderValue={(option) => ( + renderValue={() => (
- {option.icon != null && {option.icon as React.ReactNode}} - {option.label} + {'icon' in displayCategory && displayCategory.icon != null && ( + {displayCategory.icon as ReactNode} + )} + {displayCategory.label}
)} /> diff --git a/client/src/components/Prompts/Groups/ChatGroupItem.tsx b/client/src/components/Prompts/Groups/ChatGroupItem.tsx index f9b2850af..f8818b9f6 100644 --- a/client/src/components/Prompts/Groups/ChatGroupItem.tsx +++ b/client/src/components/Prompts/Groups/ChatGroupItem.tsx @@ -57,7 +57,7 @@ function ChatGroupItem({ snippet={ typeof group.oneliner === 'string' && group.oneliner.length > 0 ? group.oneliner - : group.productionPrompt?.prompt ?? '' + : (group.productionPrompt?.prompt ?? '') } >
@@ -83,7 +83,11 @@ function ChatGroupItem({ className="z-50 inline-flex h-8 w-8 items-center justify-center rounded-lg border border-border-medium bg-transparent p-0 text-sm font-medium transition-all duration-300 ease-in-out hover:border-border-heavy hover:bg-surface-hover focus:border-border-heavy focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" >