diff --git a/src/components/modules/comment/CommentBox/UniversalTextArea.tsx b/src/components/modules/comment/CommentBox/UniversalTextArea.tsx
index 56a723be66..80441cfb5e 100644
--- a/src/components/modules/comment/CommentBox/UniversalTextArea.tsx
+++ b/src/components/modules/comment/CommentBox/UniversalTextArea.tsx
@@ -8,6 +8,7 @@ import { useIsMobile } from '~/atoms/hooks'
 import { FloatPopover } from '~/components/ui/float-popover'
 import { TextArea } from '~/components/ui/input'
 import { useRefValue } from '~/hooks/common/use-ref-value'
+import { scrollTextareaToCursor } from '~/lib/dom'
 
 import { getRandomPlaceholder } from './constants'
 import {
@@ -26,29 +27,59 @@ export const UniversalTextArea: Component = ({ className }) => {
   const value = useCommentBoxTextValue()
 
   const taRef = useRef<HTMLTextAreaElement>(null)
-  const handleInsertEmoji = useCallback((emoji: string) => {
-    if (!taRef.current) {
-      return
-    }
+  const handleInsertEmoji = useCallback(
+    (emoji: string) => {
+      if (!taRef.current) {
+        return
+      }
 
-    const $ta = taRef.current
-    const start = $ta.selectionStart
-    const end = $ta.selectionEnd
-
-    $ta.value = `${$ta.value.substring(
-      0,
-      start,
-    )} ${emoji} ${$ta.value.substring(end, $ta.value.length)}`
-
-    setter('text', $ta.value)
-    requestAnimationFrame(() => {
-      const shouldMoveToPos = start + emoji.length + 2
-      $ta.selectionStart = shouldMoveToPos
-      $ta.selectionEnd = shouldMoveToPos
-
-      $ta.focus()
-    })
-  }, [])
+      const $ta = taRef.current
+      const start = $ta.selectionStart
+      const end = $ta.selectionEnd
+
+      $ta.value = `${$ta.value.substring(
+        0,
+        start,
+      )} ${emoji} ${$ta.value.substring(end, $ta.value.length)}`
+
+      setter('text', $ta.value)
+      requestAnimationFrame(() => {
+        const shouldMoveToPos = start + emoji.length + 2
+        $ta.selectionStart = shouldMoveToPos
+        $ta.selectionEnd = shouldMoveToPos
+
+        $ta.focus()
+      })
+    },
+    [setter],
+  )
+
+  const handleKeyDown = useCallback(
+    (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
+      if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey) {
+        e.preventDefault()
+        const $ta = taRef.current
+        if ($ta) {
+          const start = $ta.selectionStart
+          const end = $ta.selectionEnd
+          const textBefore = $ta.value.substring(0, start)
+          const textAfter = $ta.value.substring(end)
+          $ta.value = `${textBefore}\n\n${textAfter}`
+          setter('text', $ta.value)
+
+          requestAnimationFrame(() => {
+            const shouldMoveToPos = start + 2
+            $ta.selectionStart = shouldMoveToPos
+            $ta.selectionEnd = shouldMoveToPos
+            $ta.focus()
+            // 上面设置的光标,可能不在可见区域内,因此 scroll 到光标所在位置
+            scrollTextareaToCursor(taRef)
+          })
+        }
+      }
+    },
+    [setter],
+  )
 
   useEffect(() => {
     const $ta = taRef.current
@@ -80,6 +111,7 @@ export const UniversalTextArea: Component = ({ className }) => {
       wrapperClassName={className}
       ref={taRef}
       defaultValue={value}
+      onKeyDown={handleKeyDown}
       onChange={(e) => setter('text', e.target.value)}
       placeholder={placeholder}
       onCmdEnter={(e) => {
diff --git a/src/components/ui/input/TextArea.tsx b/src/components/ui/input/TextArea.tsx
index 858d342aca..0292b30c41 100644
--- a/src/components/ui/input/TextArea.tsx
+++ b/src/components/ui/input/TextArea.tsx
@@ -42,6 +42,7 @@ export const TextArea = forwardRef<
     rounded = 'xl',
     bordered = true,
     onCmdEnter,
+    onKeyDown,
     ...rest
   } = props
   const mouseX = useMotionValue(0)
@@ -54,9 +55,20 @@ export const TextArea = forwardRef<
     },
     [mouseX, mouseY],
   )
+  const handleKeyDown = useCallback(
+    (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
+      if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
+        onCmdEnter?.(e)
+      }
+      onKeyDown?.(e)
+    },
+    [onCmdEnter, onKeyDown],
+  )
   const background = useMotionTemplate`radial-gradient(320px circle at ${mouseX}px ${mouseY}px, var(--spotlight-color) 0%, transparent 85%)`
   const isMobile = useIsMobile()
-  const inputProps = useInputComposition(props)
+  const inputProps = useInputComposition(
+    Object.assign({}, props, { onKeyDown: handleKeyDown }),
+  )
   const [isFocus, setIsFocus] = useState(false)
   return (
     <div
@@ -112,13 +124,6 @@ export const TextArea = forwardRef<
           rest.onBlur?.(e)
         }}
         {...inputProps}
-        onKeyDown={(e) => {
-          if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
-            onCmdEnter?.(e)
-          }
-          rest.onKeyDown?.(e)
-          inputProps.onKeyDown?.(e)
-        }}
       />
 
       {children}
diff --git a/src/hooks/common/use-input-composition.ts b/src/hooks/common/use-input-composition.ts
index fe6252acc7..267933aacd 100644
--- a/src/hooks/common/use-input-composition.ts
+++ b/src/hooks/common/use-input-composition.ts
@@ -36,12 +36,12 @@ export const useInputComposition = (
 
   const handleKeyDown: React.KeyboardEventHandler<any> = useCallback(
     (e) => {
-      onKeyDown?.(e)
-
+      // 中文正在输入时,不响应 keydown 事件
       if (isCompositionRef.current) {
         e.stopPropagation()
         return
       }
+      onKeyDown?.(e)
     },
     [onKeyDown],
   )
diff --git a/src/lib/dom.ts b/src/lib/dom.ts
index e6b99eb5e9..f83ab80ab1 100644
--- a/src/lib/dom.ts
+++ b/src/lib/dom.ts
@@ -1,4 +1,4 @@
-import type { ReactEventHandler } from 'react'
+import type { ReactEventHandler, RefObject } from 'react'
 
 export const stopPropagation: ReactEventHandler<any> = (e) =>
   e.stopPropagation()
@@ -23,3 +23,57 @@ export function escapeSelector(selector: string) {
 
 export const nextFrame = (fn: () => void) =>
   requestAnimationFrame(() => requestAnimationFrame(fn))
+
+export const textareaStyles = [
+  'font',
+  'width',
+  'padding',
+  'border',
+  'boxSizing',
+  'whiteSpace',
+  'wordWrap',
+  'lineHeight',
+  'letterSpacing',
+] as const
+export const scrollTextareaToCursor = (
+  taRef: RefObject<HTMLTextAreaElement>,
+) => {
+  const $ta = taRef.current
+  if ($ta) {
+    const div = document.createElement('div')
+    const styles = getComputedStyle($ta)
+    // 复制 textarea 的样式到 div
+    textareaStyles.forEach((style) => {
+      div.style[style] = styles[style]
+    })
+    div.style.position = 'absolute'
+    div.style.top = '-9999px'
+    div.style.left = '-9999px'
+
+    // 将文本插入到 div 中,并在光标位置添加一个 span
+    const start = $ta.selectionStart
+    const end = $ta.selectionEnd
+    const textBeforeCursor = $ta.value.substring(0, start)
+    const textAfterCursor = $ta.value.substring(end)
+    const textBeforeNode = document.createTextNode(textBeforeCursor)
+    const cursorNode = document.createElement('span')
+    cursorNode.id = 'cursor'
+    const textAfterNode = document.createTextNode(textAfterCursor)
+
+    div.appendChild(textBeforeNode)
+    div.appendChild(cursorNode)
+    div.appendChild(textAfterNode)
+    document.body.appendChild(div)
+
+    // 获取光标元素的位置
+    const cursorSpan = document.getElementById('cursor')
+    const cursorY = cursorSpan!.offsetTop
+    const lineHeight = parseInt(styles.lineHeight)
+    // 移除临时 div
+    document.body.removeChild(div)
+
+    // 计算滚动位置
+    const scrollTop = cursorY - $ta.clientHeight / 2 + lineHeight / 2
+    $ta.scrollTop = Math.max(0, scrollTop)
+  }
+}