feat: Enhance form submission for touch screens (#7198)

*  feat: Enhance form submission for touch screens

* chore: add comment

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: add comment

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: linting in AnthropicClient

* chore: Add anthropic model outputs for Claude 3.7

* refactor: Simplify touch-screen detection in message submission

* fix: Correct button rendering order for chat collapse/expand icons

* Revert "refactor: Simplify touch-screen detection in message submission"

This reverts commit 8638442a4c.

* refactor: Improve touchscreen detection for focus handling in ChatForm and useFocusChatEffect

* chore: EditMessage linting

* refactor: Reorder dropdown items in ExportAndShareMenu

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Marco Beretta 2025-05-05 15:23:38 +02:00 committed by GitHub
parent 6e663b2480
commit fc6e14efe2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 35 additions and 24 deletions

View file

@ -396,13 +396,13 @@ class AnthropicClient extends BaseClient {
const formattedMessages = orderedMessages.map((message, i) => { const formattedMessages = orderedMessages.map((message, i) => {
const formattedMessage = this.useMessages const formattedMessage = this.useMessages
? formatMessage({ ? formatMessage({
message, message,
endpoint: EModelEndpoint.anthropic, endpoint: EModelEndpoint.anthropic,
}) })
: { : {
author: message.isCreatedByUser ? this.userLabel : this.assistantLabel, author: message.isCreatedByUser ? this.userLabel : this.assistantLabel,
content: message?.content ?? message.text, content: message?.content ?? message.text,
}; };
const needsTokenCount = this.contextStrategy && !orderedMessages[i].tokenCount; const needsTokenCount = this.contextStrategy && !orderedMessages[i].tokenCount;
/* If tokens were never counted, or, is a Vision request and the message has files, count again */ /* If tokens were never counted, or, is a Vision request and the message has files, count again */
@ -680,7 +680,7 @@ class AnthropicClient extends BaseClient {
} }
getCompletion() { getCompletion() {
logger.debug('AnthropicClient doesn\'t use getCompletion (all handled in sendCompletion)'); logger.debug("AnthropicClient doesn't use getCompletion (all handled in sendCompletion)");
} }
/** /**
@ -888,7 +888,7 @@ class AnthropicClient extends BaseClient {
} }
getBuildMessagesOptions() { getBuildMessagesOptions() {
logger.debug('AnthropicClient doesn\'t use getBuildMessagesOptions'); logger.debug("AnthropicClient doesn't use getBuildMessagesOptions");
} }
getEncoding() { getEncoding() {

View file

@ -239,12 +239,15 @@ const modelMaxOutputs = {
system_default: 1024, system_default: 1024,
}; };
/** Outputs from https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-names */
const anthropicMaxOutputs = { const anthropicMaxOutputs = {
'claude-3-haiku': 4096, 'claude-3-haiku': 4096,
'claude-3-sonnet': 4096, 'claude-3-sonnet': 4096,
'claude-3-opus': 4096, 'claude-3-opus': 4096,
'claude-3.5-sonnet': 8192, 'claude-3.5-sonnet': 8192,
'claude-3-5-sonnet': 8192, 'claude-3-5-sonnet': 8192,
'claude-3.7-sonnet': 128000,
'claude-3-7-sonnet': 128000,
}; };
const maxOutputTokensMap = { const maxOutputTokensMap = {

View file

@ -44,15 +44,6 @@ export default function ExportAndShareMenu({
}; };
const dropdownItems: t.MenuItemProps[] = [ const dropdownItems: t.MenuItemProps[] = [
{
label: localize('com_endpoint_export'),
onClick: exportHandler,
icon: <Upload className="icon-md mr-2 text-text-secondary" />,
/** NOTE: THE FOLLOWING PROPS ARE REQUIRED FOR MENU ITEMS THAT OPEN DIALOGS */
hideOnClick: false,
ref: exportButtonRef,
render: (props) => <button {...props} />,
},
{ {
label: localize('com_ui_share'), label: localize('com_ui_share'),
onClick: shareHandler, onClick: shareHandler,
@ -63,6 +54,15 @@ export default function ExportAndShareMenu({
ref: shareButtonRef, ref: shareButtonRef,
render: (props) => <button {...props} />, render: (props) => <button {...props} />,
}, },
{
label: localize('com_endpoint_export'),
onClick: exportHandler,
icon: <Upload className="icon-md mr-2 text-text-secondary" />,
/** NOTE: THE FOLLOWING PROPS ARE REQUIRED FOR MENU ITEMS THAT OPEN DIALOGS */
hideOnClick: false,
ref: exportButtonRef,
render: (props) => <button {...props} />,
},
]; ];
return ( return (
@ -81,7 +81,7 @@ export default function ExportAndShareMenu({
aria-label="Export options" aria-label="Export options"
className="inline-flex size-10 flex-shrink-0 items-center justify-center rounded-xl border border-border-light bg-transparent text-text-primary transition-all ease-in-out hover:bg-surface-tertiary disabled:pointer-events-none disabled:opacity-50 radix-state-open:bg-surface-tertiary" className="inline-flex size-10 flex-shrink-0 items-center justify-center rounded-xl border border-border-light bg-transparent text-text-primary transition-all ease-in-out hover:bg-surface-tertiary disabled:pointer-events-none disabled:opacity-50 radix-state-open:bg-surface-tertiary"
> >
<Upload <Share2
className="icon-md text-text-secondary" className="icon-md text-text-secondary"
aria-hidden="true" aria-hidden="true"
focusable="false" focusable="false"

View file

@ -108,6 +108,10 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => {
); );
const handleContainerClick = useCallback(() => { const handleContainerClick = useCallback(() => {
/** Check if the device is a touchscreen */
if (window.matchMedia?.('(pointer: coarse)').matches) {
return;
}
textAreaRef.current?.focus(); textAreaRef.current?.focus();
}, []); }, []);
@ -126,6 +130,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => {
}); });
const { submitMessage, submitPrompt } = useSubmitMessage(); const { submitMessage, submitPrompt } = useSubmitMessage();
const handleKeyUp = useHandleKeyUp({ const handleKeyUp = useHandleKeyUp({
index, index,
textAreaRef, textAreaRef,

View file

@ -41,9 +41,9 @@ const CollapseChat = ({
)} )}
> >
{isCollapsed ? ( {isCollapsed ? (
<ChevronDown className="h-full w-full" />
) : (
<ChevronUp className="h-full w-full" /> <ChevronUp className="h-full w-full" />
) : (
<ChevronDown className="h-full w-full" />
)} )}
</button> </button>
} }

View file

@ -113,9 +113,9 @@ const EditMessage = ({
messages.map((msg) => messages.map((msg) =>
msg.messageId === messageId msg.messageId === messageId
? { ? {
...msg, ...msg,
text: data.text, text: data.text,
} }
: msg, : msg,
), ),
); );

View file

@ -11,7 +11,10 @@ export default function useFocusChatEffect(textAreaRef: React.RefObject<HTMLText
'conversation', 'conversation',
`Focusing textarea on location state change: ${location.pathname}`, `Focusing textarea on location state change: ${location.pathname}`,
); );
textAreaRef.current?.focus(); /** Check if the device is not a touchscreen */
if (!window.matchMedia?.('(pointer: coarse)').matches) {
textAreaRef.current?.focus();
}
navigate(`${location.pathname}${location.search ?? ''}`, { replace: true, state: {} }); navigate(`${location.pathname}${location.search ?? ''}`, { replace: true, state: {} });
} }
}, [navigate, textAreaRef, location.pathname, location.state?.focusChat, location.search]); }, [navigate, textAreaRef, location.pathname, location.state?.focusChat, location.search]);