fix: performance issue when pasting long content into the chat input box (#4240)
This commit is contained in:
parent
3341a3be15
commit
21389070fb
@ -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 (
|
||||
<div className={twMerge('flex h-full w-full')}>
|
||||
<div
|
||||
className={twMerge('flex h-full w-full')}
|
||||
style={{
|
||||
maxWidth: matches
|
||||
? '100%'
|
||||
: mainViewState === MainViewState.Thread
|
||||
? `calc(100% - (${showRightPanel ? Number(localStorage.getItem(RIGHT_PANEL_WIDTH)) : 0}px + ${showLeftPanel ? Number(localStorage.getItem(LEFT_PANEL_WIDTH)) : 0}px))`
|
||||
: '100%',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={twMerge(
|
||||
'h-full w-full overflow-hidden bg-[hsla(var(--center-panel-bg))]',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
||||
type Props = PropsWithChildren
|
||||
|
||||
const DEFAULT_LEFT_PANEL_WIDTH = 200
|
||||
const LEFT_PANEL_WIDTH = 'leftPanelWidth'
|
||||
export const LEFT_PANEL_WIDTH = 'leftPanelWidth'
|
||||
|
||||
const LeftPanelContainer = ({ children }: Props) => {
|
||||
const [leftPanelRef, setLeftPanelRef] = useState<HTMLDivElement | null>(null)
|
||||
|
||||
@ -37,7 +37,7 @@ const MainViewContainer = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={twMerge('relative flex w-full')}>
|
||||
<div className={twMerge('relative flex w-[calc(100%-48px)]')}>
|
||||
<div className="w-full">
|
||||
<m.div
|
||||
key={mainViewState}
|
||||
|
||||
@ -16,13 +16,13 @@ import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
||||
|
||||
type Props = PropsWithChildren
|
||||
|
||||
const DEFAULT_RIGTH_PANEL_WIDTH = 280
|
||||
const RIGHT_PANEL_WIDTH = 'rightPanelWidth'
|
||||
const DEFAULT_RIGHT_PANEL_WIDTH = 280
|
||||
export const RIGHT_PANEL_WIDTH = 'rightPanelWidth'
|
||||
|
||||
const RightPanelContainer = ({ children }: Props) => {
|
||||
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<HTMLDivElement | null>(
|
||||
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)
|
||||
|
||||
@ -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<string>('plaintext')
|
||||
const hasStartBackticks = useRef<boolean>(false)
|
||||
const hasEndBackticks = useRef<boolean>(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<HTMLDivElement>) => {
|
||||
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 (
|
||||
<Slate
|
||||
editor={editor}
|
||||
@ -362,9 +374,18 @@ const RichTextEditor = ({
|
||||
>
|
||||
<Editable
|
||||
ref={textareaRef}
|
||||
decorate={decorate} // Pass the decorate function
|
||||
decorate={(entry) => {
|
||||
// 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 &&
|
||||
|
||||
@ -337,7 +337,7 @@ const ChatInput = () => {
|
||||
{activeSettingInputBox && (
|
||||
<div
|
||||
className={twMerge(
|
||||
'absolute bottom-[6px] left-[1px] flex w-[calc(100%-10px)] items-center justify-between rounded-b-lg bg-[hsla(var(--center-panel-bg))] p-3 pr-0',
|
||||
'absolute bottom-[5px] left-[1px] flex w-[calc(100%-10px)] items-center justify-between rounded-b-lg bg-[hsla(var(--center-panel-bg))] p-3 pr-0',
|
||||
!activeThread && 'bg-transparent',
|
||||
stateModel.loading && 'bg-transparent'
|
||||
)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user