From d3a20357e9b7e182a3ba348778c4ad96c02e8d62 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sun, 18 Aug 2024 05:52:05 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20feat:=20Prompt=20Dropdown=20Vari?= =?UTF-8?q?able;=20style:=20Add=20Markdown=20Support=20(#3681)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add extended inputs for promts library variables * feat: Add maxRows prop to VariableForm input field * 📩 feat: invite user (#3012) * feat: basic invite-user script * feat: add invite user functionality and registration validation middleware * fix: invite user fixes * refactor: consolidate direct model access to a central place of functions * style(Registration): add spinner to continue button * refactor: import ordrer * feat: improve invite user script and error handling * fix: merge conflict * refactor: remove `console.log` and use `logger` * fix: token operation and checkinvite issues * bring back comment and remove console log * fix: return invalid token when token is not found * fix: getInvite fix * refactor: Update Token.js to use async/await syntax for update and delete operations * feat: Refactor Token.js to use async/await syntax for createToken and findToken functions * refactor(inviteUser): define functions outside of module.exports * Update AuthService.js --------- Co-authored-by: Danny Avila * style: improve OpenAI.tsx input field focus styling * refactor: update import statement in Input.tsx * refactor: remove multi-line * refactor: update placeholder text to use localization * style: new dropdown variable info and markdown styling for info * Add ReactMarkdown * chore: styling, import order * refactor: update ReactMarkdown usage in VariableForm * style: remove markdown class * refactor: update mobile styling and use code renderer * style(InputWithDropDown): update focus trigger style * style(OptionsPopover): update Save As Preset `focus` and `dark:bg` --------- Co-authored-by: Konstantin Meshcheryakov Co-authored-by: Marco Beretta <81851188+berry-13@users.noreply.github.com> Co-authored-by: bsu3338 --- .../components/Chat/Input/OptionsPopover.tsx | 2 +- .../Chat/Messages/Content/Markdown.tsx | 10 +- .../components/Endpoints/Settings/OpenAI.tsx | 2 +- .../components/Messages/Content/CodeBlock.tsx | 14 +- client/src/components/Prompts/Command.tsx | 8 +- client/src/components/Prompts/Description.tsx | 4 +- .../Prompts/Groups/CreatePromptForm.tsx | 26 ++- .../Prompts/Groups/VariableDialog.tsx | 2 +- .../Prompts/Groups/VariableForm.tsx | 154 ++++++++++++------ client/src/components/Prompts/Markdown.tsx | 42 +++++ .../src/components/Prompts/PreviewPrompt.tsx | 2 +- .../src/components/Prompts/PromptDetails.tsx | 43 ++++- .../src/components/Prompts/PromptEditor.tsx | 36 +++- .../components/Prompts/PromptVariables.tsx | 55 +++++-- client/src/components/ui/Input.tsx | 2 +- .../src/components/ui/InputWithDropDown.tsx | 154 ++++++++++++++++++ client/src/components/ui/index.ts | 5 +- client/src/localization/languages/Eng.ts | 11 +- client/src/utils/index.ts | 11 ++ 19 files changed, 473 insertions(+), 110 deletions(-) create mode 100644 client/src/components/Prompts/Markdown.tsx create mode 100644 client/src/components/ui/InputWithDropDown.tsx diff --git a/client/src/components/Chat/Input/OptionsPopover.tsx b/client/src/components/Chat/Input/OptionsPopover.tsx index 02f5150bc5..a49d31f334 100644 --- a/client/src/components/Chat/Input/OptionsPopover.tsx +++ b/client/src/components/Chat/Input/OptionsPopover.tsx @@ -66,7 +66,7 @@ export default function OptionsPopover({ {presetsDisabled ? null : ( diff --git a/client/src/components/Prompts/Command.tsx b/client/src/components/Prompts/Command.tsx index 40c2177734..a80dc1d78c 100644 --- a/client/src/components/Prompts/Command.tsx +++ b/client/src/components/Prompts/Command.tsx @@ -38,7 +38,7 @@ const Command = ({ } }; - if (disabled && !command) { + if (disabled === true && !command) { return null; } @@ -53,10 +53,10 @@ const Command = ({ placeholder={localize('com_ui_command_placeholder')} value={command} onChange={handleInputChange} - className="w-full rounded-lg border-none bg-surface-tertiary p-1 text-text-primary placeholder:text-text-secondary-alt focus:bg-surface-tertiary focus:outline-none focus:ring-0 md:w-96" + className="w-full rounded-lg border-none bg-surface-tertiary p-1 text-text-primary placeholder:text-text-secondary focus:bg-surface-tertiary focus:outline-none focus:ring-1 focus:ring-inset focus:ring-ring-primary md:w-96" /> - {!disabled && ( - {`${charCount}/${Constants.COMMANDS_MAX_LENGTH}`} + {disabled !== true && ( + {`${charCount}/${Constants.COMMANDS_MAX_LENGTH}`} )} diff --git a/client/src/components/Prompts/Description.tsx b/client/src/components/Prompts/Description.tsx index 23c0e85237..452c624ce8 100644 --- a/client/src/components/Prompts/Description.tsx +++ b/client/src/components/Prompts/Description.tsx @@ -50,10 +50,10 @@ const Description = ({ placeholder={localize('com_ui_description_placeholder')} value={description} onChange={handleInputChange} - className="w-full rounded-lg border-none bg-surface-tertiary p-1 text-text-primary placeholder:text-text-secondary-alt focus:bg-surface-tertiary focus:outline-none focus:ring-0 md:w-96" + className="w-full rounded-lg border-none bg-surface-tertiary p-1 text-text-primary placeholder:text-text-secondary focus:bg-surface-tertiary focus:outline-none focus:ring-1 focus:ring-inset focus:ring-ring-primary md:w-96" /> {!disabled && ( - {`${charCount}/${MAX_LENGTH}`} + {`${charCount}/${MAX_LENGTH}`} )} diff --git a/client/src/components/Prompts/Groups/CreatePromptForm.tsx b/client/src/components/Prompts/Groups/CreatePromptForm.tsx index f8c301f656..c409567bcd 100644 --- a/client/src/components/Prompts/Groups/CreatePromptForm.tsx +++ b/client/src/components/Prompts/Groups/CreatePromptForm.tsx @@ -111,10 +111,9 @@ const CreatePromptForm = ({
)} /> - +
-

+

{localize('com_ui_prompt_text')}*

-
+
methods.setValue('oneliner', value)} - tabIndex={3} + tabIndex={0} /> - methods.setValue('command', value)} tabIndex={4} /> + methods.setValue('command', value)} tabIndex={0} />
-
diff --git a/client/src/components/Prompts/Groups/VariableDialog.tsx b/client/src/components/Prompts/Groups/VariableDialog.tsx index c866914974..26fb8c4b1a 100644 --- a/client/src/components/Prompts/Groups/VariableDialog.tsx +++ b/client/src/components/Prompts/Groups/VariableDialog.tsx @@ -31,7 +31,7 @@ const VariableDialog: React.FC = ({ open, onClose, group }) return ( - + {group.name} diff --git a/client/src/components/Prompts/Groups/VariableForm.tsx b/client/src/components/Prompts/Groups/VariableForm.tsx index d77d164f72..711a39bbcd 100644 --- a/client/src/components/Prompts/Groups/VariableForm.tsx +++ b/client/src/components/Prompts/Groups/VariableForm.tsx @@ -1,12 +1,64 @@ import { useMemo } from 'react'; +import remarkGfm from 'remark-gfm'; +import remarkMath from 'remark-math'; +import supersub from 'remark-supersub'; +import rehypeKatex from 'rehype-katex'; +import ReactMarkdown from 'react-markdown'; +import rehypeHighlight from 'rehype-highlight'; import { useForm, useFieldArray, Controller, useWatch } from 'react-hook-form'; import type { TPromptGroup } from 'librechat-data-provider'; -import { extractVariableInfo, wrapVariable, replaceSpecialVars } from '~/utils'; +import { + cn, + wrapVariable, + defaultTextProps, + replaceSpecialVars, + extractVariableInfo, +} from '~/utils'; import { useAuthContext, useLocalize, useSubmitMessage } from '~/hooks'; -import { Textarea } from '~/components/ui'; +import { TextareaAutosize, InputWithDropdown } from '~/components/ui'; +import { code } from '~/components/Chat/Messages/Content/Markdown'; + +type FieldType = 'text' | 'select'; + +type FieldConfig = { + variable: string; + type: FieldType; + options?: string[]; +}; type FormValues = { - fields: { variable: string; value: string }[]; + fields: { variable: string; value: string; config: FieldConfig }[]; +}; + +/** + * Variable Format Guide: + * + * Variables in prompts should be enclosed in double curly braces: {{variable}} + * + * Simple text input: + * {{variable_name}} + * + * Dropdown select with predefined options: + * {{variable_name:option1|option2|option3}} + * + * All dropdown selects allow custom input in addition to predefined options. + * + * Examples: + * {{name}} - Simple text input for a name + * {{tone:formal|casual|business casual}} - Dropdown for tone selection with custom input option + * + * Note: The order of variables in the prompt will be preserved in the input form. + */ + +const parseFieldConfig = (variable: string): FieldConfig => { + const content = variable; + if (content.includes(':')) { + const [name, options] = content.split(':'); + if (options && options.includes('|')) { + return { variable: name, type: 'select', options: options.split('|') }; + } + } + return { variable: content, type: 'text' }; }; export default function VariableForm({ @@ -32,7 +84,11 @@ export default function VariableForm({ const { submitPrompt } = useSubmitMessage(); const { control, handleSubmit } = useForm({ defaultValues: { - fields: uniqueVariables.map((variable) => ({ variable: wrapVariable(variable), value: '' })), + fields: uniqueVariables.map((variable) => ({ + variable: wrapVariable(variable), + value: '', + config: parseFieldConfig(variable), + })), }, }); @@ -50,31 +106,16 @@ export default function VariableForm({ return null; } - const generateHighlightedText = () => { + const generateHighlightedMarkdown = () => { let tempText = mainText; - const parts: JSX.Element[] = []; - - allVariables.forEach((variable, index) => { + allVariables.forEach((variable) => { const placeholder = `{{${variable}}}`; - const partsBeforePlaceholder = tempText.split(placeholder); const fieldIndex = variableIndexMap.get(variable) as string | number; const fieldValue = fieldValues[fieldIndex].value as string; - parts.push( - {partsBeforePlaceholder[0]}, - - {fieldValue !== '' ? fieldValue : placeholder} - , - ); - - tempText = partsBeforePlaceholder.slice(1).join(placeholder); + const highlightText = fieldValue !== '' ? fieldValue : placeholder; + tempText = tempText.replaceAll(placeholder, `**${highlightText}**`); }); - - parts.push({tempText}); - - return parts; + return tempText; }; const onSubmit = (data: FormValues) => { @@ -91,32 +132,53 @@ export default function VariableForm({ }; return ( -
-
-
-

{generateHighlightedText()}

+
+ +
+ + {generateHighlightedMarkdown()} +
-
+
{fields.map((field, index) => ( -
+
( -