mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Fix: Chat UI consistency and layout shifts (#5237)
This commit is contained in:
parent
e4b051bb71
commit
abea1920ac
4 changed files with 83 additions and 77 deletions
|
|
@ -142,7 +142,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
|
|||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
padding: isExpanded ? "8px 0" : "8px 0 0 0",
|
||||
padding: isExpanded ? "8px 0" : "2px 0 0 0",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={toggleExpanded}>
|
||||
|
|
|
|||
|
|
@ -787,10 +787,10 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
"relative",
|
||||
"flex",
|
||||
"flex-col",
|
||||
"gap-2",
|
||||
"gap-1",
|
||||
"bg-editor-background",
|
||||
"m-2 mt-1",
|
||||
"p-1.5",
|
||||
"px-1.5",
|
||||
"pb-1",
|
||||
"outline-none",
|
||||
"border",
|
||||
"border-none",
|
||||
|
|
@ -998,7 +998,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
/>
|
||||
)}
|
||||
|
||||
<div className={cn("flex", "justify-between", "items-center", "mt-auto", "pt-0.5")}>
|
||||
<div className={cn("flex", "justify-between", "items-center", "mt-auto")}>
|
||||
<div className={cn("flex", "items-center", "gap-1", "min-w-0")}>
|
||||
<div className="shrink-0">
|
||||
<ModeSelector
|
||||
|
|
|
|||
|
|
@ -1424,6 +1424,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
vscode.postMessage({ type: "condenseTaskContextRequest", text: taskId })
|
||||
}
|
||||
|
||||
const areButtonsVisible = showScrollToBottom || primaryButtonText || secondaryButtonText || isStreaming
|
||||
|
||||
return (
|
||||
<div className={isHidden ? "hidden" : "fixed top-0 left-0 right-0 bottom-0 flex flex-col overflow-hidden"}>
|
||||
{(showAnnouncement || showAnnouncementModal) && (
|
||||
|
|
@ -1525,7 +1527,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
// but becomes scrollable when the viewport is too small
|
||||
*/}
|
||||
{!task && (
|
||||
<div className="mb-[-2px] flex-initial min-h-0">
|
||||
<div className="mb-1 flex-initial min-h-0">
|
||||
<AutoApproveMenu />
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1536,7 +1538,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
key={task.ts} // trick to make sure virtuoso re-renders when task changes, and we use initialTopMostItemIndex to start at the bottom
|
||||
className="scrollable grow overflow-y-scroll mb-[5px]"
|
||||
className="scrollable grow overflow-y-scroll mb-1"
|
||||
// increasing top by 3_000 to prevent jumping around when user collapses a row
|
||||
increaseViewportBy={{ top: 3_000, bottom: Number.MAX_SAFE_INTEGER }} // hack to make sure the last message is always rendered to get truly perfect scroll to bottom animation when new messages are added (Number.MAX_SAFE_INTEGER is safe for arithmetic operations, which is all virtuoso uses this value for in src/sizeRangeSystem.ts)
|
||||
data={groupedMessages} // messages is the raw format returned by extension, modifiedMessages is the manipulated structure that combines certain messages of related type, and visibleMessages is the filtered structure that removes messages that should not be rendered
|
||||
|
|
@ -1552,83 +1554,87 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
initialTopMostItemIndex={groupedMessages.length - 1}
|
||||
/>
|
||||
</div>
|
||||
<AutoApproveMenu />
|
||||
{showScrollToBottom ? (
|
||||
<div className="flex px-[15px] pt-[10px]">
|
||||
<StandardTooltip content={t("chat:scrollToBottom")}>
|
||||
<div
|
||||
className="bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_55%,_transparent)] rounded-[3px] overflow-hidden cursor-pointer flex justify-center items-center flex-1 h-[25px] hover:bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_90%,_transparent)] active:bg-[color-mix(in_srgb,_var(--vscode-toolbar-hoverBackground)_70%,_transparent)]"
|
||||
onClick={() => {
|
||||
scrollToBottomSmooth()
|
||||
disableAutoScrollRef.current = false
|
||||
}}>
|
||||
<span className="codicon codicon-chevron-down text-[18px]"></span>
|
||||
</div>
|
||||
</StandardTooltip>
|
||||
</div>
|
||||
) : (
|
||||
<div className={`flex-initial min-h-0 ${!areButtonsVisible ? "mb-1" : ""}`}>
|
||||
<AutoApproveMenu />
|
||||
</div>
|
||||
{areButtonsVisible && (
|
||||
<div
|
||||
className={`flex ${
|
||||
primaryButtonText || secondaryButtonText || isStreaming ? "px-[15px] pt-[10px]" : "p-0"
|
||||
} ${
|
||||
primaryButtonText || secondaryButtonText || isStreaming
|
||||
? enableButtons || (isStreaming && !didClickCancel)
|
||||
className={`flex h-9 items-center mb-1 px-[15px] ${
|
||||
showScrollToBottom
|
||||
? "opacity-100"
|
||||
: enableButtons || (isStreaming && !didClickCancel)
|
||||
? "opacity-100"
|
||||
: "opacity-50"
|
||||
: "opacity-0"
|
||||
}`}>
|
||||
{primaryButtonText && !isStreaming && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
primaryButtonText === t("chat:retry.title")
|
||||
? t("chat:retry.tooltip")
|
||||
: primaryButtonText === t("chat:save.title")
|
||||
? t("chat:save.tooltip")
|
||||
: primaryButtonText === t("chat:approve.title")
|
||||
? t("chat:approve.tooltip")
|
||||
: primaryButtonText === t("chat:runCommand.title")
|
||||
? t("chat:runCommand.tooltip")
|
||||
: primaryButtonText === t("chat:startNewTask.title")
|
||||
? t("chat:startNewTask.tooltip")
|
||||
: primaryButtonText === t("chat:resumeTask.title")
|
||||
? t("chat:resumeTask.tooltip")
|
||||
: primaryButtonText === t("chat:proceedAnyways.title")
|
||||
? t("chat:proceedAnyways.tooltip")
|
||||
: primaryButtonText ===
|
||||
t("chat:proceedWhileRunning.title")
|
||||
? t("chat:proceedWhileRunning.tooltip")
|
||||
: undefined
|
||||
}>
|
||||
<VSCodeButton
|
||||
appearance="primary"
|
||||
disabled={!enableButtons}
|
||||
className={secondaryButtonText ? "flex-1 mr-[6px]" : "flex-[2] mr-0"}
|
||||
onClick={() => handlePrimaryButtonClick(inputValue, selectedImages)}>
|
||||
{primaryButtonText}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
{(secondaryButtonText || isStreaming) && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
isStreaming
|
||||
? t("chat:cancel.tooltip")
|
||||
: secondaryButtonText === t("chat:startNewTask.title")
|
||||
? t("chat:startNewTask.tooltip")
|
||||
: secondaryButtonText === t("chat:reject.title")
|
||||
? t("chat:reject.tooltip")
|
||||
: secondaryButtonText === t("chat:terminate.title")
|
||||
? t("chat:terminate.tooltip")
|
||||
: undefined
|
||||
}>
|
||||
{showScrollToBottom ? (
|
||||
<StandardTooltip content={t("chat:scrollToBottom")}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!enableButtons && !(isStreaming && !didClickCancel)}
|
||||
className={isStreaming ? "flex-[2] ml-0" : "flex-1 ml-[6px]"}
|
||||
onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}>
|
||||
{isStreaming ? t("chat:cancel.title") : secondaryButtonText}
|
||||
className="flex-[2]"
|
||||
onClick={() => {
|
||||
scrollToBottomSmooth()
|
||||
disableAutoScrollRef.current = false
|
||||
}}>
|
||||
<span className="codicon codicon-chevron-down"></span>
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
) : (
|
||||
<>
|
||||
{primaryButtonText && !isStreaming && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
primaryButtonText === t("chat:retry.title")
|
||||
? t("chat:retry.tooltip")
|
||||
: primaryButtonText === t("chat:save.title")
|
||||
? t("chat:save.tooltip")
|
||||
: primaryButtonText === t("chat:approve.title")
|
||||
? t("chat:approve.tooltip")
|
||||
: primaryButtonText === t("chat:runCommand.title")
|
||||
? t("chat:runCommand.tooltip")
|
||||
: primaryButtonText === t("chat:startNewTask.title")
|
||||
? t("chat:startNewTask.tooltip")
|
||||
: primaryButtonText === t("chat:resumeTask.title")
|
||||
? t("chat:resumeTask.tooltip")
|
||||
: primaryButtonText ===
|
||||
t("chat:proceedAnyways.title")
|
||||
? t("chat:proceedAnyways.tooltip")
|
||||
: primaryButtonText ===
|
||||
t("chat:proceedWhileRunning.title")
|
||||
? t("chat:proceedWhileRunning.tooltip")
|
||||
: undefined
|
||||
}>
|
||||
<VSCodeButton
|
||||
appearance="primary"
|
||||
disabled={!enableButtons}
|
||||
className={secondaryButtonText ? "flex-1 mr-[6px]" : "flex-[2] mr-0"}
|
||||
onClick={() => handlePrimaryButtonClick(inputValue, selectedImages)}>
|
||||
{primaryButtonText}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
{(secondaryButtonText || isStreaming) && (
|
||||
<StandardTooltip
|
||||
content={
|
||||
isStreaming
|
||||
? t("chat:cancel.tooltip")
|
||||
: secondaryButtonText === t("chat:startNewTask.title")
|
||||
? t("chat:startNewTask.tooltip")
|
||||
: secondaryButtonText === t("chat:reject.title")
|
||||
? t("chat:reject.tooltip")
|
||||
: secondaryButtonText === t("chat:terminate.title")
|
||||
? t("chat:terminate.tooltip")
|
||||
: undefined
|
||||
}>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
disabled={!enableButtons && !(isStreaming && !didClickCancel)}
|
||||
className={isStreaming ? "flex-[2] ml-0" : "flex-1 ml-[6px]"}
|
||||
onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}>
|
||||
{isStreaming ? t("chat:cancel.title") : secondaryButtonText}
|
||||
</VSCodeButton>
|
||||
</StandardTooltip>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ const TaskItem = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex-1">
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* Header with metadata */}
|
||||
<TaskItemHeader item={item} isSelectionMode={isSelectionMode} onDelete={onDelete} />
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue