| @@ -10,6 +10,7 @@ import produce from 'immer' | |||
| import s from './style.module.css' | |||
| import MessageTypeSelector from './message-type-selector' | |||
| import ConfirmAddVar from './confirm-add-var' | |||
| import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap' | |||
| import type { PromptRole, PromptVariable } from '@/models/debug' | |||
| import { HelpCircle, Trash03 } from '@/app/components/base/icons/src/vender/line/general' | |||
| import { Clipboard, ClipboardCheck } from '@/app/components/base/icons/src/vender/line/files' | |||
| @@ -25,7 +26,6 @@ import { useToastContext } from '@/app/components/base/toast' | |||
| import { useEventEmitterContextContext } from '@/context/event-emitter' | |||
| import { ADD_EXTERNAL_DATA_TOOL } from '@/app/components/app/configuration/config-var' | |||
| import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block' | |||
| type Props = { | |||
| type: PromptRole | |||
| isChatMode: boolean | |||
| @@ -130,7 +130,8 @@ const AdvancedPromptInput: FC<Props> = ({ | |||
| } | |||
| } | |||
| const editorHeight = isChatMode ? 'h-[200px]' : 'h-[508px]' | |||
| const minHeight = 102 | |||
| const [editorHeight, setEditorHeight] = React.useState(isChatMode ? 200 : 508) | |||
| const contextMissing = ( | |||
| <div | |||
| className='flex justify-between items-center h-11 pt-2 pr-3 pb-1 pl-4 rounded-tl-xl rounded-tr-xl' | |||
| @@ -139,7 +140,7 @@ const AdvancedPromptInput: FC<Props> = ({ | |||
| }} | |||
| > | |||
| <div className='flex items-center pr-2' > | |||
| <AlertCircle className='mr-1 w-4 h-4 text-[#F79009]'/> | |||
| <AlertCircle className='mr-1 w-4 h-4 text-[#F79009]' /> | |||
| <div className='leading-[18px] text-[13px] font-medium text-[#DC6803]'>{t('appDebug.promptMode.contextMissing')}</div> | |||
| </div> | |||
| <div | |||
| @@ -190,9 +191,19 @@ const AdvancedPromptInput: FC<Props> = ({ | |||
| </div> | |||
| )} | |||
| <div className={cn(editorHeight, 'px-4 min-h-[102px] overflow-y-auto text-sm text-gray-700')}> | |||
| <PromptEditorHeightResizeWrap | |||
| className='px-4 min-h-[102px] overflow-y-auto text-sm text-gray-700' | |||
| height={editorHeight} | |||
| minHeight={minHeight} | |||
| onHeightChange={setEditorHeight} | |||
| footer={( | |||
| <div className='pl-4 pb-2 flex'> | |||
| <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{value.length}</div> | |||
| </div> | |||
| )} | |||
| > | |||
| <PromptEditor | |||
| className={editorHeight} | |||
| className='min-h-[84px]' | |||
| value={value} | |||
| contextBlock={{ | |||
| show: true, | |||
| @@ -233,10 +244,8 @@ const AdvancedPromptInput: FC<Props> = ({ | |||
| onChange={handlePromptChange} | |||
| onBlur={handleBlur} | |||
| /> | |||
| </div> | |||
| <div className='pl-4 pb-2 flex'> | |||
| <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{value.length}</div> | |||
| </div> | |||
| </PromptEditorHeightResizeWrap> | |||
| </div> | |||
| {isShowConfirmAddVar && ( | |||
| @@ -0,0 +1,91 @@ | |||
| 'use client' | |||
| import React, { useCallback, useEffect, useState } from 'react' | |||
| import type { FC } from 'react' | |||
| import { useDebounceFn } from 'ahooks' | |||
| import cn from 'classnames' | |||
| type Props = { | |||
| className?: string | |||
| height: number | |||
| minHeight: number | |||
| onHeightChange: (height: number) => void | |||
| children: JSX.Element | |||
| footer?: JSX.Element | |||
| } | |||
| const PromptEditorHeightResizeWrap: FC<Props> = ({ | |||
| className, | |||
| height, | |||
| minHeight, | |||
| onHeightChange, | |||
| children, | |||
| footer, | |||
| }) => { | |||
| const [clientY, setClientY] = useState(0) | |||
| const [isResizing, setIsResizing] = useState(false) | |||
| const [prevUserSelectStyle, setPrevUserSelectStyle] = useState(getComputedStyle(document.body).userSelect) | |||
| const handleStartResize = useCallback((e: React.MouseEvent<HTMLElement>) => { | |||
| setClientY(e.clientY) | |||
| setIsResizing(true) | |||
| setPrevUserSelectStyle(getComputedStyle(document.body).userSelect) | |||
| document.body.style.userSelect = 'none' | |||
| }, []) | |||
| const handleStopResize = useCallback(() => { | |||
| setIsResizing(false) | |||
| document.body.style.userSelect = prevUserSelectStyle | |||
| }, [prevUserSelectStyle]) | |||
| const { run: didHandleResize } = useDebounceFn((e) => { | |||
| if (!isResizing) | |||
| return | |||
| const offset = e.clientY - clientY | |||
| let newHeight = height + offset | |||
| setClientY(e.clientY) | |||
| if (newHeight < minHeight) | |||
| newHeight = minHeight | |||
| onHeightChange(newHeight) | |||
| }, { | |||
| wait: 0, | |||
| }) | |||
| const handleResize = useCallback(didHandleResize, [isResizing, height, minHeight, clientY]) | |||
| useEffect(() => { | |||
| document.addEventListener('mousemove', handleResize) | |||
| return () => { | |||
| document.removeEventListener('mousemove', handleResize) | |||
| } | |||
| }, [handleResize]) | |||
| useEffect(() => { | |||
| document.addEventListener('mouseup', handleStopResize) | |||
| return () => { | |||
| document.removeEventListener('mouseup', handleStopResize) | |||
| } | |||
| }, [handleStopResize]) | |||
| return ( | |||
| <div | |||
| className='relative' | |||
| > | |||
| <div className={cn(className, 'overflow-y-auto')} | |||
| style={{ | |||
| height, | |||
| }} | |||
| > | |||
| {children} | |||
| </div> | |||
| {/* resize handler */} | |||
| {footer} | |||
| <div | |||
| className='absolute bottom-0 left-0 w-full flex justify-center h-2 cursor-row-resize' | |||
| onMouseDown={handleStartResize}> | |||
| <div className='w-5 h-[3px] rounded-sm bg-gray-300'></div> | |||
| </div> | |||
| </div> | |||
| ) | |||
| } | |||
| export default React.memo(PromptEditorHeightResizeWrap) | |||
| @@ -1,6 +1,6 @@ | |||
| 'use client' | |||
| import type { FC } from 'react' | |||
| import React from 'react' | |||
| import React, { useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { useBoolean } from 'ahooks' | |||
| import cn from 'classnames' | |||
| @@ -8,6 +8,7 @@ import produce from 'immer' | |||
| import { useContext } from 'use-context-selector' | |||
| import ConfirmAddVar from './confirm-add-var' | |||
| import s from './style.module.css' | |||
| import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap' | |||
| import { PromptMode, type PromptVariable } from '@/models/debug' | |||
| import Tooltip from '@/app/components/base/tooltip' | |||
| import { AppType } from '@/types/app' | |||
| @@ -125,6 +126,8 @@ const Prompt: FC<ISimplePromptInput> = ({ | |||
| setIntroduction(res.opening_statement) | |||
| showAutomaticFalse() | |||
| } | |||
| const minHeight = 228 | |||
| const [editorHeight, setEditorHeight] = useState(minHeight) | |||
| return ( | |||
| <div className={cn(!readonly ? `${s.gradientBorder}` : 'bg-gray-50', ' relative shadow-md')}> | |||
| @@ -161,7 +164,12 @@ const Prompt: FC<ISimplePromptInput> = ({ | |||
| )} | |||
| </div> | |||
| </div> | |||
| <div className='px-4 py-2 min-h-[228px] max-h-[156px] overflow-y-auto bg-white rounded-xl text-sm text-gray-700'> | |||
| <PromptEditorHeightResizeWrap | |||
| className='px-4 py-2 min-h-[228px] bg-white rounded-xl text-sm text-gray-700' | |||
| height={editorHeight} | |||
| minHeight={minHeight} | |||
| onHeightChange={setEditorHeight} | |||
| > | |||
| <PromptEditor | |||
| className='min-h-[210px]' | |||
| value={promptTemplate} | |||
| @@ -208,7 +216,7 @@ const Prompt: FC<ISimplePromptInput> = ({ | |||
| handleChange(promptTemplate, getVars(promptTemplate)) | |||
| }} | |||
| /> | |||
| </div> | |||
| </PromptEditorHeightResizeWrap> | |||
| </div> | |||
| {isShowConfirmAddVar && ( | |||