From 21389070fb362a67e76538eca32ac45528ad8658 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Fri, 6 Dec 2024 16:48:01 +0800 Subject: [PATCH] fix: performance issue when pasting long content into the chat input box (#4240) --- web/containers/CenterPanelContainer/index.tsx | 27 ++++++++++++++++++- web/containers/Layout/index.tsx | 2 +- web/containers/LeftPanelContainer/index.tsx | 2 +- web/containers/MainViewContainer/index.tsx | 2 +- web/containers/RightPanelContainer/index.tsx | 14 +++++----- .../ChatInput/RichTextEditor.tsx | 27 ++++++++++++++++--- .../ThreadCenterPanel/ChatInput/index.tsx | 2 +- 7 files changed, 61 insertions(+), 15 deletions(-) diff --git a/web/containers/CenterPanelContainer/index.tsx b/web/containers/CenterPanelContainer/index.tsx index 9ce81f184c..fb2518dc77 100644 --- a/web/containers/CenterPanelContainer/index.tsx +++ b/web/containers/CenterPanelContainer/index.tsx @@ -1,15 +1,40 @@ import { PropsWithChildren } from 'react' +import { useMediaQuery } from '@janhq/joi' import { useAtomValue } from 'jotai' import { twMerge } from 'tailwind-merge' +import { MainViewState } from '@/constants/screens' + +import { LEFT_PANEL_WIDTH } from '../LeftPanelContainer' + +import { RIGHT_PANEL_WIDTH } from '../RightPanelContainer' + +import { + mainViewStateAtom, + showLeftPanelAtom, + showRightPanelAtom, +} from '@/helpers/atoms/App.atom' import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom' const CenterPanelContainer = ({ children }: PropsWithChildren) => { const reduceTransparent = useAtomValue(reduceTransparentAtom) + const matches = useMediaQuery('(max-width: 880px)') + const showLeftPanel = useAtomValue(showLeftPanelAtom) + const showRightPanel = useAtomValue(showRightPanelAtom) + const mainViewState = useAtomValue(mainViewStateAtom) return ( -
+
{ const [leftPanelRef, setLeftPanelRef] = useState(null) diff --git a/web/containers/MainViewContainer/index.tsx b/web/containers/MainViewContainer/index.tsx index ba7f87fd2b..811f19c6e9 100644 --- a/web/containers/MainViewContainer/index.tsx +++ b/web/containers/MainViewContainer/index.tsx @@ -37,7 +37,7 @@ const MainViewContainer = () => { } return ( -
+
{ const [isResizing, setIsResizing] = useState(false) const [threadRightPanelWidth, setRightPanelWidth] = useState( - Number(localStorage.getItem(RIGHT_PANEL_WIDTH)) || DEFAULT_RIGTH_PANEL_WIDTH + Number(localStorage.getItem(RIGHT_PANEL_WIDTH)) || DEFAULT_RIGHT_PANEL_WIDTH ) const [rightPanelRef, setRightPanelRef] = useState( null @@ -55,11 +55,11 @@ const RightPanelContainer = ({ children }: Props) => { mouseMoveEvent.clientX < 200 ) { - setRightPanelWidth(DEFAULT_RIGTH_PANEL_WIDTH) + setRightPanelWidth(DEFAULT_RIGHT_PANEL_WIDTH) setIsResizing(false) localStorage.setItem( RIGHT_PANEL_WIDTH, - String(DEFAULT_RIGTH_PANEL_WIDTH) + String(DEFAULT_RIGHT_PANEL_WIDTH) ) setShowRightPanel(false) } else { @@ -77,8 +77,8 @@ const RightPanelContainer = ({ children }: Props) => { useEffect(() => { if (localStorage.getItem(RIGHT_PANEL_WIDTH) === null) { - setRightPanelWidth(DEFAULT_RIGTH_PANEL_WIDTH) - localStorage.setItem(RIGHT_PANEL_WIDTH, String(DEFAULT_RIGTH_PANEL_WIDTH)) + setRightPanelWidth(DEFAULT_RIGHT_PANEL_WIDTH) + localStorage.setItem(RIGHT_PANEL_WIDTH, String(DEFAULT_RIGHT_PANEL_WIDTH)) } window.addEventListener('mousemove', resize) window.addEventListener('mouseup', stopResizing) diff --git a/web/screens/Thread/ThreadCenterPanel/ChatInput/RichTextEditor.tsx b/web/screens/Thread/ThreadCenterPanel/ChatInput/RichTextEditor.tsx index 0d477d78dc..7166e39753 100644 --- a/web/screens/Thread/ThreadCenterPanel/ChatInput/RichTextEditor.tsx +++ b/web/screens/Thread/ThreadCenterPanel/ChatInput/RichTextEditor.tsx @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, ClipboardEvent } from 'react' import { MessageStatus } from '@janhq/core' import hljs from 'highlight.js' @@ -67,7 +67,7 @@ const RichTextEditor = ({ placeholder, spellCheck, }: RichTextEditorProps) => { - const [editor] = useState(() => withHistory(withReact(createEditor()))) + const editor = useMemo(() => withHistory(withReact(createEditor())), []) const currentLanguage = useRef('plaintext') const hasStartBackticks = useRef(false) const hasEndBackticks = useRef(false) @@ -80,6 +80,8 @@ const RichTextEditor = ({ const { sendChatMessage } = useSendChatMessage() const { stopInference } = useActiveModel() + const largeContentThreshold = 1000 + // The decorate function identifies code blocks and marks the ranges const decorate = useCallback( (entry: [any, any]) => { @@ -324,6 +326,16 @@ const RichTextEditor = ({ [currentPrompt, editor, messages] ) + const handlePaste = (event: ClipboardEvent) => { + const clipboardData = event.clipboardData || (window as any).clipboardData + const pastedData = clipboardData.getData('text') + + if (pastedData.length > largeContentThreshold) { + event.preventDefault() // Prevent the default paste behavior + Transforms.insertText(editor, pastedData) // Insert the content directly into the editor + } + } + return ( { + // Skip decorate if content exceeds threshold + if ( + currentPrompt.length > largeContentThreshold || + !currentPrompt.length + ) + return [] + return decorate(entry) + }} renderLeaf={renderLeaf} // Pass the renderLeaf function onKeyDown={handleKeyDown} + onPaste={handlePaste} // Add the custom paste handler className={twMerge( className, disabled && diff --git a/web/screens/Thread/ThreadCenterPanel/ChatInput/index.tsx b/web/screens/Thread/ThreadCenterPanel/ChatInput/index.tsx index 5662cd0c01..fbca6d2904 100644 --- a/web/screens/Thread/ThreadCenterPanel/ChatInput/index.tsx +++ b/web/screens/Thread/ThreadCenterPanel/ChatInput/index.tsx @@ -337,7 +337,7 @@ const ChatInput = () => { {activeSettingInputBox && (