diff --git a/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx b/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx index d35fbf1a01..3f00aad490 100644 --- a/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx +++ b/frontend/libs/components/promptbox/src/lib/MentionTextarea.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { twMerge } from "tailwind-merge"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faUser } from "@fortawesome/pro-solid-svg-icons"; @@ -24,7 +24,6 @@ interface MentionTextareaProps { onBlur?: () => void; disabled?: boolean; colorMap: Record; - maxHeight?: number; } interface MentionState { @@ -241,7 +240,7 @@ function MentionDropdown({ // MentionTextarea Component // --------------------------------------------------------------------------- -export function MentionTextarea({ +export const MentionTextarea = forwardRef(function MentionTextarea({ value, onChange, mentionItems, @@ -252,9 +251,9 @@ export function MentionTextarea({ onBlur, disabled, colorMap, - maxHeight, -}: MentionTextareaProps) { +}, ref) { const editorRef = useRef(null); + useImperativeHandle(ref, () => editorRef.current!, []); const isInternalUpdate = useRef(false); const isComposing = useRef(false); const pendingCaret = useRef(null); @@ -498,6 +497,17 @@ export function MentionTextarea({ return; } } + + // Shift+Enter (or Cmd+Enter on Mac): insert a newline instead of + // letting the contentEditable create a
+ if (e.key === "Enter" && (e.shiftKey || e.metaKey)) { + e.preventDefault(); + document.execCommand("insertLineBreak"); + // Trigger input handler so the parent value stays in sync + handleInput(); + return; + } + externalOnKeyDown?.(e); }, [ @@ -506,6 +516,7 @@ export function MentionTextarea({ filteredItems, handleSelect, externalOnKeyDown, + handleInput, ], ); @@ -553,7 +564,6 @@ export function MentionTextarea({ className, "outline-none whitespace-pre-wrap break-words overflow-y-auto", )} - style={maxHeight ? { maxHeight } : undefined} /> {mentionState.isOpen && filteredItems.length > 0 && ( @@ -569,4 +579,4 @@ export function MentionTextarea({ )}
); -} +}); diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx index ccb029a272..0ac2096f12 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx @@ -172,8 +172,9 @@ export const PromptBoxVideo = ({ const toggleExpand = () => { setIsExpanded((prev) => { const next = !prev; - if (textareaRef.current) { - textareaRef.current.style.height = next ? EXPANDED_HEIGHT : "auto"; + const el = (mentionEditorRef.current ?? textareaRef.current) as HTMLElement | null; + if (el) { + el.style.height = next ? EXPANDED_HEIGHT : "auto"; } return next; }); @@ -226,17 +227,18 @@ export const PromptBoxVideo = ({ useState(aspectRatioOptions); const textareaRef = useRef(null); + const mentionEditorRef = useRef(null); + // Apply height constraints to whichever editor element is active useEffect(() => { - if (textareaRef.current) { - // Hard pixel limit so the resize handle can never exceed viewport + const el = (mentionEditorRef.current ?? textareaRef.current) as HTMLElement | null; + if (el) { const maxH = isExpanded ? 500 : Math.min(window.innerHeight - 700, 500); - textareaRef.current.style.maxHeight = `${Math.max(maxH, 88)}px`; - textareaRef.current.style.minHeight = "0"; + el.style.maxHeight = `${Math.max(maxH, 88)}px`; + el.style.minHeight = "0"; if (!isExpanded) { - // Cap auto-grow at ~5.5em so it doesn't fight with manual resize - const capped = Math.min(textareaRef.current.scrollHeight, 88); - textareaRef.current.style.minHeight = `${capped}px`; + const capped = Math.min(el.scrollHeight, 88); + el.style.minHeight = `${capped}px`; } } }); @@ -894,11 +896,11 @@ export const PromptBoxVideo = ({
{hasAnyMentionables ? (