Skip to content

Commit cf53be7

Browse files
committed
code review
1 parent a23e404 commit cf53be7

24 files changed

+409
-362
lines changed

webview-ui/src/App.tsx

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useRef, useState, useMemo } from "react"
1+
import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"
22
import { useEvent } from "react-use"
33
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
44

@@ -26,6 +26,29 @@ import { STANDARD_TOOLTIP_DELAY } from "./components/ui/standard-tooltip"
2626

2727
type Tab = "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
2828

29+
interface HumanRelayDialogState {
30+
isOpen: boolean
31+
requestId: string
32+
promptText: string
33+
}
34+
35+
interface DeleteMessageDialogState {
36+
isOpen: boolean
37+
messageTs: number
38+
}
39+
40+
interface EditMessageDialogState {
41+
isOpen: boolean
42+
messageTs: number
43+
text: string
44+
images?: string[]
45+
}
46+
47+
// Memoize dialog components to prevent unnecessary re-renders
48+
const MemoizedDeleteMessageDialog = React.memo(DeleteMessageDialog)
49+
const MemoizedEditMessageDialog = React.memo(EditMessageDialog)
50+
const MemoizedHumanRelayDialog = React.memo(HumanRelayDialog)
51+
2952
const tabsByMessageAction: Partial<Record<NonNullable<ExtensionMessage["action"]>, Tab>> = {
3053
chatButtonClicked: "chat",
3154
settingsButtonClicked: "settings",
@@ -57,30 +80,18 @@ const App = () => {
5780
const [showAnnouncement, setShowAnnouncement] = useState(false)
5881
const [tab, setTab] = useState<Tab>("chat")
5982

60-
const [humanRelayDialogState, setHumanRelayDialogState] = useState<{
61-
isOpen: boolean
62-
requestId: string
63-
promptText: string
64-
}>({
83+
const [humanRelayDialogState, setHumanRelayDialogState] = useState<HumanRelayDialogState>({
6584
isOpen: false,
6685
requestId: "",
6786
promptText: "",
6887
})
6988

70-
const [deleteMessageDialogState, setDeleteMessageDialogState] = useState<{
71-
isOpen: boolean
72-
messageTs: number
73-
}>({
89+
const [deleteMessageDialogState, setDeleteMessageDialogState] = useState<DeleteMessageDialogState>({
7490
isOpen: false,
7591
messageTs: 0,
7692
})
7793

78-
const [editMessageDialogState, setEditMessageDialogState] = useState<{
79-
isOpen: boolean
80-
messageTs: number
81-
text: string
82-
images?: string[]
83-
}>({
94+
const [editMessageDialogState, setEditMessageDialogState] = useState<EditMessageDialogState>({
8495
isOpen: false,
8596
messageTs: 0,
8697
text: "",
@@ -233,15 +244,15 @@ const App = () => {
233244
showAnnouncement={showAnnouncement}
234245
hideAnnouncement={() => setShowAnnouncement(false)}
235246
/>
236-
<HumanRelayDialog
247+
<MemoizedHumanRelayDialog
237248
isOpen={humanRelayDialogState.isOpen}
238249
requestId={humanRelayDialogState.requestId}
239250
promptText={humanRelayDialogState.promptText}
240251
onClose={() => setHumanRelayDialogState((prev) => ({ ...prev, isOpen: false }))}
241252
onSubmit={(requestId, text) => vscode.postMessage({ type: "humanRelayResponse", requestId, text })}
242253
onCancel={(requestId) => vscode.postMessage({ type: "humanRelayCancel", requestId })}
243254
/>
244-
<DeleteMessageDialog
255+
<MemoizedDeleteMessageDialog
245256
open={deleteMessageDialogState.isOpen}
246257
onOpenChange={(open) => setDeleteMessageDialogState((prev) => ({ ...prev, isOpen: open }))}
247258
onConfirm={() => {
@@ -252,7 +263,7 @@ const App = () => {
252263
setDeleteMessageDialogState((prev) => ({ ...prev, isOpen: false }))
253264
}}
254265
/>
255-
<EditMessageDialog
266+
<MemoizedEditMessageDialog
256267
open={editMessageDialogState.isOpen}
257268
onOpenChange={(open) => setEditMessageDialogState((prev) => ({ ...prev, isOpen: open }))}
258269
onConfirm={() => {

webview-ui/src/components/chat/ChatTextArea.tsx

Lines changed: 15 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { VolumeX, Pin, Check, Image, WandSparkles, SendHorizontal } from "lucide
2929
import { IndexingStatusBadge } from "./IndexingStatusBadge"
3030
import { cn } from "@/lib/utils"
3131
import { usePromptHistory } from "./hooks/usePromptHistory"
32+
import { EditModeControls } from "./EditModeControls"
3233

3334
interface ChatTextAreaProps {
3435
inputValue: string
@@ -806,77 +807,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
806807
/>
807808
)
808809

809-
// Helper function to render edit mode controls
810-
const renderEditModeControls = () => (
811-
<div
812-
className={cn(
813-
"flex",
814-
"items-center",
815-
"justify-between",
816-
"absolute",
817-
"bottom-2",
818-
"left-2",
819-
"right-2",
820-
"z-30",
821-
)}>
822-
<div className={cn("flex", "items-center", "gap-1", "flex-1", "min-w-0")}>
823-
<div className="shrink-0">{renderModeSelector()}</div>
824-
</div>
825-
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0", "ml-2")}>
826-
<Button
827-
variant="secondary"
828-
size="sm"
829-
onClick={onCancel}
830-
disabled={sendingDisabled}
831-
className="text-xs bg-vscode-toolbar-hoverBackground hover:bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground">
832-
Cancel
833-
</Button>
834-
<StandardTooltip content={t("chat:addImages")}>
835-
<button
836-
aria-label={t("chat:addImages")}
837-
disabled={shouldDisableImages}
838-
onClick={!shouldDisableImages ? onSelectImages : undefined}
839-
className={cn(
840-
"relative inline-flex items-center justify-center",
841-
"bg-transparent border-none p-1.5",
842-
"rounded-md min-w-[28px] min-h-[28px]",
843-
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
844-
"transition-all duration-150",
845-
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
846-
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
847-
"active:bg-[rgba(255,255,255,0.1)]",
848-
!shouldDisableImages && "cursor-pointer",
849-
shouldDisableImages &&
850-
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
851-
)}>
852-
<Image className="w-4 h-4" />
853-
</button>
854-
</StandardTooltip>
855-
<StandardTooltip content={t("chat:save.tooltip")}>
856-
<button
857-
aria-label={t("chat:save.tooltip")}
858-
disabled={sendingDisabled}
859-
onClick={!sendingDisabled ? onSend : undefined}
860-
className={cn(
861-
"relative inline-flex items-center justify-center",
862-
"bg-transparent border-none p-1.5",
863-
"rounded-md min-w-[28px] min-h-[28px]",
864-
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
865-
"transition-all duration-150",
866-
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
867-
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
868-
"active:bg-[rgba(255,255,255,0.1)]",
869-
!sendingDisabled && "cursor-pointer",
870-
sendingDisabled &&
871-
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
872-
)}>
873-
<SendHorizontal className="w-4 h-4" />
874-
</button>
875-
</StandardTooltip>
876-
</div>
877-
</div>
878-
)
879-
880810
// Helper function to get API config dropdown options
881811
const getApiConfigOptions = useMemo(() => {
882812
const pinnedConfigs = (listApiConfigMeta || [])
@@ -1305,7 +1235,20 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
13051235
{renderTextAreaSection()}
13061236
</div>
13071237

1308-
{isEditMode && renderEditModeControls()}
1238+
{isEditMode && (
1239+
<EditModeControls
1240+
mode={mode}
1241+
onModeChange={handleModeChange}
1242+
modeShortcutText={modeShortcutText}
1243+
customModes={customModes}
1244+
customModePrompts={customModePrompts}
1245+
onCancel={onCancel}
1246+
onSend={onSend}
1247+
onSelectImages={onSelectImages}
1248+
sendingDisabled={sendingDisabled}
1249+
shouldDisableImages={shouldDisableImages}
1250+
/>
1251+
)}
13091252
</div>
13101253

13111254
{selectedImages.length > 0 && (
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React from "react"
2+
import { Mode } from "@roo/modes"
3+
import { Button, StandardTooltip } from "@/components/ui"
4+
import { Image, SendHorizontal } from "lucide-react"
5+
import { cn } from "@/lib/utils"
6+
import ModeSelector from "./ModeSelector"
7+
import { useAppTranslation } from "@/i18n/TranslationContext"
8+
9+
interface EditModeControlsProps {
10+
mode: Mode
11+
onModeChange: (value: Mode) => void
12+
modeShortcutText: string
13+
customModes: any
14+
customModePrompts: any
15+
onCancel?: () => void
16+
onSend: () => void
17+
onSelectImages: () => void
18+
sendingDisabled: boolean
19+
shouldDisableImages: boolean
20+
}
21+
22+
export const EditModeControls: React.FC<EditModeControlsProps> = ({
23+
mode,
24+
onModeChange,
25+
modeShortcutText,
26+
customModes,
27+
customModePrompts,
28+
onCancel,
29+
onSend,
30+
onSelectImages,
31+
sendingDisabled,
32+
shouldDisableImages,
33+
}) => {
34+
const { t } = useAppTranslation()
35+
36+
return (
37+
<div
38+
className={cn(
39+
"flex",
40+
"items-center",
41+
"justify-between",
42+
"absolute",
43+
"bottom-2",
44+
"left-2",
45+
"right-2",
46+
"z-30",
47+
)}>
48+
<div className={cn("flex", "items-center", "gap-1", "flex-1", "min-w-0")}>
49+
<div className="shrink-0">
50+
<ModeSelector
51+
value={mode}
52+
title={t("chat:selectMode")}
53+
onChange={onModeChange}
54+
triggerClassName="w-full"
55+
modeShortcutText={modeShortcutText}
56+
customModes={customModes}
57+
customModePrompts={customModePrompts}
58+
/>
59+
</div>
60+
</div>
61+
<div className={cn("flex", "items-center", "gap-0.5", "shrink-0", "ml-2")}>
62+
<Button
63+
variant="secondary"
64+
size="sm"
65+
onClick={onCancel}
66+
disabled={sendingDisabled}
67+
className="text-xs bg-vscode-toolbar-hoverBackground hover:bg-vscode-button-secondaryBackground text-vscode-button-secondaryForeground">
68+
Cancel
69+
</Button>
70+
<StandardTooltip content={t("chat:addImages")}>
71+
<button
72+
aria-label={t("chat:addImages")}
73+
disabled={shouldDisableImages}
74+
onClick={!shouldDisableImages ? onSelectImages : undefined}
75+
className={cn(
76+
"relative inline-flex items-center justify-center",
77+
"bg-transparent border-none p-1.5",
78+
"rounded-md min-w-[28px] min-h-[28px]",
79+
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
80+
"transition-all duration-150",
81+
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
82+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
83+
"active:bg-[rgba(255,255,255,0.1)]",
84+
!shouldDisableImages && "cursor-pointer",
85+
shouldDisableImages &&
86+
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
87+
)}>
88+
<Image className="w-4 h-4" />
89+
</button>
90+
</StandardTooltip>
91+
<StandardTooltip content={t("chat:save.tooltip")}>
92+
<button
93+
aria-label={t("chat:save.tooltip")}
94+
disabled={sendingDisabled}
95+
onClick={!sendingDisabled ? onSend : undefined}
96+
className={cn(
97+
"relative inline-flex items-center justify-center",
98+
"bg-transparent border-none p-1.5",
99+
"rounded-md min-w-[28px] min-h-[28px]",
100+
"opacity-60 hover:opacity-100 text-vscode-descriptionForeground hover:text-vscode-foreground",
101+
"transition-all duration-150",
102+
"hover:bg-[rgba(255,255,255,0.03)] hover:border-[rgba(255,255,255,0.15)]",
103+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-vscode-focusBorder",
104+
"active:bg-[rgba(255,255,255,0.1)]",
105+
!sendingDisabled && "cursor-pointer",
106+
sendingDisabled &&
107+
"opacity-40 cursor-not-allowed grayscale-[30%] hover:bg-transparent hover:border-[rgba(255,255,255,0.08)] active:bg-transparent",
108+
)}>
109+
<SendHorizontal className="w-4 h-4" />
110+
</button>
111+
</StandardTooltip>
112+
</div>
113+
</div>
114+
)
115+
}

webview-ui/src/components/chat/MessageModificationConfirmationDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export const MessageModificationConfirmationDialog: React.FC<MessageModification
2727
const { t } = useAppTranslation()
2828

2929
const isEdit = type === "edit"
30-
const title = isEdit ? t("common:confirmation.edit_message") : t("common:confirmation.delete_message")
31-
const description = isEdit ? t("common:confirmation.edit_warning") : t("common:confirmation.delete_warning")
30+
const title = isEdit ? t("common:confirmation.editMessage") : t("common:confirmation.deleteMessage")
31+
const description = isEdit ? t("common:confirmation.editWarning") : t("common:confirmation.deleteWarning")
3232

3333
return (
3434
<AlertDialog open={open} onOpenChange={onOpenChange}>

0 commit comments

Comments
 (0)