Ver código fonte

feat: prompt IDE support change height (#2232)

Joel 1 ano atrás
pai
commit
20bd33fada

+ 18 - 9
web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx

@@ -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 && (

+ 91 - 0
web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx

@@ -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)

+ 11 - 3
web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx

@@ -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 && (