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 { PropsWithChildren } from 'react'
|
||||||
|
|
||||||
|
import { useMediaQuery } from '@janhq/joi'
|
||||||
import { useAtomValue } from 'jotai'
|
import { useAtomValue } from 'jotai'
|
||||||
|
|
||||||
import { twMerge } from 'tailwind-merge'
|
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'
|
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
||||||
|
|
||||||
const CenterPanelContainer = ({ children }: PropsWithChildren) => {
|
const CenterPanelContainer = ({ children }: PropsWithChildren) => {
|
||||||
const reduceTransparent = useAtomValue(reduceTransparentAtom)
|
const reduceTransparent = useAtomValue(reduceTransparentAtom)
|
||||||
|
const matches = useMediaQuery('(max-width: 880px)')
|
||||||
|
const showLeftPanel = useAtomValue(showLeftPanelAtom)
|
||||||
|
const showRightPanel = useAtomValue(showRightPanelAtom)
|
||||||
|
const mainViewState = useAtomValue(mainViewStateAtom)
|
||||||
return (
|
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
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'h-full w-full overflow-hidden bg-[hsla(var(--center-panel-bg))]',
|
'h-full w-full overflow-hidden bg-[hsla(var(--center-panel-bg))]',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
import { useAtomValue, useSetAtom } from 'jotai'
|
import { useAtomValue, useSetAtom } from 'jotai'
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
|||||||
type Props = PropsWithChildren
|
type Props = PropsWithChildren
|
||||||
|
|
||||||
const DEFAULT_LEFT_PANEL_WIDTH = 200
|
const DEFAULT_LEFT_PANEL_WIDTH = 200
|
||||||
const LEFT_PANEL_WIDTH = 'leftPanelWidth'
|
export const LEFT_PANEL_WIDTH = 'leftPanelWidth'
|
||||||
|
|
||||||
const LeftPanelContainer = ({ children }: Props) => {
|
const LeftPanelContainer = ({ children }: Props) => {
|
||||||
const [leftPanelRef, setLeftPanelRef] = useState<HTMLDivElement | null>(null)
|
const [leftPanelRef, setLeftPanelRef] = useState<HTMLDivElement | null>(null)
|
||||||
|
|||||||
@ -37,7 +37,7 @@ const MainViewContainer = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={twMerge('relative flex w-full')}>
|
<div className={twMerge('relative flex w-[calc(100%-48px)]')}>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<m.div
|
<m.div
|
||||||
key={mainViewState}
|
key={mainViewState}
|
||||||
|
|||||||
@ -16,13 +16,13 @@ import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
|||||||
|
|
||||||
type Props = PropsWithChildren
|
type Props = PropsWithChildren
|
||||||
|
|
||||||
const DEFAULT_RIGTH_PANEL_WIDTH = 280
|
const DEFAULT_RIGHT_PANEL_WIDTH = 280
|
||||||
const RIGHT_PANEL_WIDTH = 'rightPanelWidth'
|
export const RIGHT_PANEL_WIDTH = 'rightPanelWidth'
|
||||||
|
|
||||||
const RightPanelContainer = ({ children }: Props) => {
|
const RightPanelContainer = ({ children }: Props) => {
|
||||||
const [isResizing, setIsResizing] = useState(false)
|
const [isResizing, setIsResizing] = useState(false)
|
||||||
const [threadRightPanelWidth, setRightPanelWidth] = useState(
|
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>(
|
const [rightPanelRef, setRightPanelRef] = useState<HTMLDivElement | null>(
|
||||||
null
|
null
|
||||||
@ -55,11 +55,11 @@ const RightPanelContainer = ({ children }: Props) => {
|
|||||||
mouseMoveEvent.clientX <
|
mouseMoveEvent.clientX <
|
||||||
200
|
200
|
||||||
) {
|
) {
|
||||||
setRightPanelWidth(DEFAULT_RIGTH_PANEL_WIDTH)
|
setRightPanelWidth(DEFAULT_RIGHT_PANEL_WIDTH)
|
||||||
setIsResizing(false)
|
setIsResizing(false)
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
RIGHT_PANEL_WIDTH,
|
RIGHT_PANEL_WIDTH,
|
||||||
String(DEFAULT_RIGTH_PANEL_WIDTH)
|
String(DEFAULT_RIGHT_PANEL_WIDTH)
|
||||||
)
|
)
|
||||||
setShowRightPanel(false)
|
setShowRightPanel(false)
|
||||||
} else {
|
} else {
|
||||||
@ -77,8 +77,8 @@ const RightPanelContainer = ({ children }: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localStorage.getItem(RIGHT_PANEL_WIDTH) === null) {
|
if (localStorage.getItem(RIGHT_PANEL_WIDTH) === null) {
|
||||||
setRightPanelWidth(DEFAULT_RIGTH_PANEL_WIDTH)
|
setRightPanelWidth(DEFAULT_RIGHT_PANEL_WIDTH)
|
||||||
localStorage.setItem(RIGHT_PANEL_WIDTH, String(DEFAULT_RIGTH_PANEL_WIDTH))
|
localStorage.setItem(RIGHT_PANEL_WIDTH, String(DEFAULT_RIGHT_PANEL_WIDTH))
|
||||||
}
|
}
|
||||||
window.addEventListener('mousemove', resize)
|
window.addEventListener('mousemove', resize)
|
||||||
window.addEventListener('mouseup', stopResizing)
|
window.addEventListener('mouseup', stopResizing)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* 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 { MessageStatus } from '@janhq/core'
|
||||||
import hljs from 'highlight.js'
|
import hljs from 'highlight.js'
|
||||||
@ -67,7 +67,7 @@ const RichTextEditor = ({
|
|||||||
placeholder,
|
placeholder,
|
||||||
spellCheck,
|
spellCheck,
|
||||||
}: RichTextEditorProps) => {
|
}: RichTextEditorProps) => {
|
||||||
const [editor] = useState(() => withHistory(withReact(createEditor())))
|
const editor = useMemo(() => withHistory(withReact(createEditor())), [])
|
||||||
const currentLanguage = useRef<string>('plaintext')
|
const currentLanguage = useRef<string>('plaintext')
|
||||||
const hasStartBackticks = useRef<boolean>(false)
|
const hasStartBackticks = useRef<boolean>(false)
|
||||||
const hasEndBackticks = useRef<boolean>(false)
|
const hasEndBackticks = useRef<boolean>(false)
|
||||||
@ -80,6 +80,8 @@ const RichTextEditor = ({
|
|||||||
const { sendChatMessage } = useSendChatMessage()
|
const { sendChatMessage } = useSendChatMessage()
|
||||||
const { stopInference } = useActiveModel()
|
const { stopInference } = useActiveModel()
|
||||||
|
|
||||||
|
const largeContentThreshold = 1000
|
||||||
|
|
||||||
// The decorate function identifies code blocks and marks the ranges
|
// The decorate function identifies code blocks and marks the ranges
|
||||||
const decorate = useCallback(
|
const decorate = useCallback(
|
||||||
(entry: [any, any]) => {
|
(entry: [any, any]) => {
|
||||||
@ -324,6 +326,16 @@ const RichTextEditor = ({
|
|||||||
[currentPrompt, editor, messages]
|
[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 (
|
return (
|
||||||
<Slate
|
<Slate
|
||||||
editor={editor}
|
editor={editor}
|
||||||
@ -362,9 +374,18 @@ const RichTextEditor = ({
|
|||||||
>
|
>
|
||||||
<Editable
|
<Editable
|
||||||
ref={textareaRef}
|
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
|
renderLeaf={renderLeaf} // Pass the renderLeaf function
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
onPaste={handlePaste} // Add the custom paste handler
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
className,
|
className,
|
||||||
disabled &&
|
disabled &&
|
||||||
|
|||||||
@ -337,7 +337,7 @@ const ChatInput = () => {
|
|||||||
{activeSettingInputBox && (
|
{activeSettingInputBox && (
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
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',
|
!activeThread && 'bg-transparent',
|
||||||
stateModel.loading && 'bg-transparent'
|
stateModel.loading && 'bg-transparent'
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user