Fix ChatTextArea layout

This commit is contained in:
Matt Rubens
2025-01-14 23:45:43 -05:00
parent b75e105fab
commit 84a0063b99
3 changed files with 306 additions and 261 deletions

View File

@@ -0,0 +1,5 @@
---
"roo-cline": patch
---
Fix chat text input layout issues

View File

@@ -15,6 +15,7 @@ import Thumbnails from "../common/Thumbnails"
import { vscode } from "../../utils/vscode"
import { WebviewMessage } from "../../../../src/shared/WebviewMessage"
import { Mode } from "../../../../src/core/prompts/types"
import { CaretIcon } from "../common/CaretIcon"
interface ChatTextAreaProps {
inputValue: string
@@ -50,7 +51,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
ref,
) => {
const { filePaths, currentApiConfigName, listApiConfigMeta } = useExtensionState()
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
const [gitCommits, setGitCommits] = useState<any[]>([])
const [showDropdown, setShowDropdown] = useState(false)
@@ -376,7 +376,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
if (!isMouseDownOnMenu) {
setShowContextMenu(false)
}
setIsTextAreaFocused(false)
}, [isMouseDownOnMenu])
const handlePaste = useCallback(
@@ -494,12 +493,43 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
[updateCursorPosition],
)
const selectStyle = {
fontSize: "11px",
cursor: textAreaDisabled ? "not-allowed" : "pointer",
backgroundColor: "transparent",
border: "none",
color: "var(--vscode-foreground)",
opacity: textAreaDisabled ? 0.5 : 0.8,
outline: "none",
paddingLeft: "20px",
paddingRight: "6px",
WebkitAppearance: "none" as const,
MozAppearance: "none" as const,
appearance: "none" as const
}
const caretContainerStyle = {
position: "absolute" as const,
left: 6,
top: "50%",
transform: "translateY(-45%)",
pointerEvents: "none" as const,
opacity: textAreaDisabled ? 0.5 : 0.8
}
return (
<div style={{
padding: "10px 15px",
<div
className="chat-text-area"
style={{
opacity: textAreaDisabled ? 0.5 : 1,
position: "relative",
display: "flex",
flexDirection: "column",
gap: "8px",
backgroundColor: "var(--vscode-input-background)",
minHeight: "100px",
margin: "10px 15px",
padding: "8px"
}}
onDrop={async (e) => {
e.preventDefault()
@@ -552,7 +582,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}}
onDragOver={(e) => {
e.preventDefault()
}}>
}}
>
{showContextMenu && (
<div ref={contextMenuContainerRef}>
<ContextMenu
@@ -566,42 +597,31 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
/>
</div>
)}
{!isTextAreaFocused && (
<div
style={{
position: "absolute",
inset: "10px 15px",
border: "1px solid var(--vscode-input-border)",
borderRadius: 2,
pointerEvents: "none",
zIndex: 5,
}}
/>
)}
<div style={{
position: "relative",
flex: "1 1 auto",
display: "flex",
flexDirection: "column-reverse",
minHeight: 0,
overflow: "hidden"
}}>
<div
ref={highlightLayerRef}
style={{
position: "absolute",
top: 10,
left: 15,
right: 15,
bottom: 10,
inset: 0,
pointerEvents: "none",
whiteSpace: "pre-wrap",
wordWrap: "break-word",
color: "transparent",
overflow: "hidden",
backgroundColor: "var(--vscode-input-background)",
fontFamily: "var(--vscode-font-family)",
fontSize: "var(--vscode-editor-font-size)",
lineHeight: "var(--vscode-editor-line-height)",
borderRadius: 2,
borderLeft: 0,
borderRight: 0,
borderTop: 0,
borderColor: "transparent",
borderBottom: `${thumbnailsHeight + 6}px solid transparent`,
padding: "9px 9px 25px 9px",
padding: "8px",
marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
zIndex: 1
}}
/>
<DynamicTextArea
@@ -621,7 +641,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onFocus={() => setIsTextAreaFocused(true)}
onBlur={handleBlur}
onPaste={handlePaste}
onSelect={updateCursorPosition}
@@ -633,7 +652,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
onHeightChange?.(height)
}}
placeholder={placeholderText}
minRows={2}
minRows={4}
maxRows={20}
autoFocus={true}
style={{
@@ -647,20 +666,18 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
lineHeight: "var(--vscode-editor-line-height)",
resize: "none",
overflowX: "hidden",
overflowY: "scroll",
borderLeft: 0,
borderRight: 0,
borderTop: 0,
borderBottom: `${thumbnailsHeight + 6}px solid transparent`,
borderColor: "transparent",
padding: "9px 9px 25px 9px",
marginBottom: "15px",
overflowY: "auto",
border: "none",
padding: "8px",
marginBottom: thumbnailsHeight > 0 ? `${thumbnailsHeight + 16}px` : 0,
cursor: textAreaDisabled ? "not-allowed" : undefined,
flex: 1,
zIndex: 1,
flex: "0 1 auto",
zIndex: 2
}}
onScroll={() => updateHighlights()}
/>
</div>
{selectedImages.length > 0 && (
<Thumbnails
images={selectedImages}
@@ -668,53 +685,43 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
onHeightChange={handleThumbnailsHeightChange}
style={{
position: "absolute",
paddingTop: 4,
bottom: 36,
left: 22,
right: 67,
bottom: "36px",
left: "16px",
zIndex: 2,
marginBottom: "8px"
}}
/>
)}
<div
style={{
position: "absolute",
left: 25,
bottom: 20,
zIndex: 3,
<div style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: "auto",
paddingTop: "8px"
}}>
<div style={{
display: "flex",
gap: 8,
alignItems: "center"
}}
>
}}>
<div style={{ position: "relative", display: "inline-block" }}>
<select
value={mode}
disabled={textAreaDisabled}
onChange={(e) => {
const newMode = e.target.value as Mode;
setMode(newMode);
const newMode = e.target.value as Mode
setMode(newMode)
vscode.postMessage({
type: "mode",
text: newMode
});
})
}}
style={{
fontSize: "11px",
cursor: textAreaDisabled ? "not-allowed" : "pointer",
backgroundColor: "transparent",
border: "none",
color: "var(--vscode-input-foreground)",
opacity: textAreaDisabled ? 0.5 : 0.6,
outline: "none",
paddingLeft: 14,
WebkitAppearance: "none",
MozAppearance: "none",
appearance: "none",
backgroundImage: "url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\")",
backgroundRepeat: "no-repeat",
backgroundPosition: "left 0px center",
backgroundSize: "10px"
}}>
...selectStyle,
minWidth: "70px",
flex: "0 0 auto"
}}
>
<option value="code" style={{
backgroundColor: "var(--vscode-dropdown-background)",
color: "var(--vscode-dropdown-foreground)"
@@ -728,6 +735,19 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
color: "var(--vscode-dropdown-foreground)"
}}>Ask</option>
</select>
<div style={caretContainerStyle}>
<CaretIcon />
</div>
</div>
<div style={{
position: "relative",
display: "inline-block",
flex: "1 1 auto",
minWidth: 0,
maxWidth: "150px",
overflow: "hidden"
}}>
<select
value={currentApiConfigName}
disabled={textAreaDisabled}
@@ -736,21 +756,9 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
text: e.target.value
})}
style={{
fontSize: "11px",
cursor: textAreaDisabled ? "not-allowed" : "pointer",
backgroundColor: "transparent",
border: "none",
color: "var(--vscode-input-foreground)",
opacity: textAreaDisabled ? 0.5 : 0.6,
outline: "none",
paddingLeft: 14,
WebkitAppearance: "none",
MozAppearance: "none",
appearance: "none",
backgroundImage: "url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\")",
backgroundRepeat: "no-repeat",
backgroundPosition: "left 0px center",
backgroundSize: "10px"
...selectStyle,
width: "100%",
textOverflow: "ellipsis"
}}
>
{(listApiConfigMeta || [])?.map((config) => (
@@ -766,9 +774,17 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
</option>
))}
</select>
<div style={caretContainerStyle}>
<CaretIcon />
</div>
<div className="button-row" style={{ position: "absolute", right: 16, display: "flex", alignItems: "center", height: 31, bottom: 11, zIndex: 3, padding: "0 8px", justifyContent: "flex-end", backgroundColor: "var(--vscode-input-background)", }}>
<span style={{ display: "flex", alignItems: "center", gap: 12 }}>
</div>
</div>
<div style={{
display: "flex",
alignItems: "center",
gap: "12px"
}}>
<div style={{ display: "flex", alignItems: "center" }}>
{isEnhancingPrompt ? (
<span className="codicon codicon-loading codicon-modifier-spin" style={{
@@ -776,7 +792,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
opacity: 0.5,
fontSize: 16.5,
marginRight: 10
}}></span>
}} />
) : (
<span
role="button"
@@ -788,9 +804,17 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
/>
)}
</div>
<span className={`input-icon-button ${shouldDisableImages ? "disabled" : ""} codicon codicon-device-camera`} onClick={() => !shouldDisableImages && onSelectImages()} style={{ fontSize: 16.5 }} />
<span className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-send`} onClick={() => !textAreaDisabled && onSend()} style={{ fontSize: 15 }} />
</span>
<span
className={`input-icon-button ${shouldDisableImages ? "disabled" : ""} codicon codicon-device-camera`}
onClick={() => !shouldDisableImages && onSelectImages()}
style={{ fontSize: 16.5 }}
/>
<span
className={`input-icon-button ${textAreaDisabled ? "disabled" : ""} codicon codicon-send`}
onClick={() => !textAreaDisabled && onSend()}
style={{ fontSize: 15 }}
/>
</div>
</div>
</div>
)

View File

@@ -0,0 +1,16 @@
import React from 'react'
export const CaretIcon = () => (
<svg
width="10"
height="10"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="6 9 12 15 18 9" />
</svg>
)