Skip to content

Commit 43327ee

Browse files
committed
fix: restore previous input and images when canceling edit
When entering edit mode, save current input text and image attachments. On cancel, restore both instead of leaving the edited message content.
1 parent e51e967 commit 43327ee

File tree

1 file changed

+26
-3
lines changed

1 file changed

+26
-3
lines changed

src/browser/components/ChatInput/index.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,25 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
148148
}, []);
149149
const inputRef = useRef<HTMLTextAreaElement>(null);
150150
const modelSelectorRef = useRef<ModelSelectorRef>(null);
151+
152+
// Draft state combines text input and image attachments
153+
// Use these helpers to avoid accidentally losing images when modifying text
154+
interface DraftState {
155+
text: string;
156+
images: ImageAttachment[];
157+
}
158+
const getDraft = useCallback(
159+
(): DraftState => ({ text: input, images: imageAttachments }),
160+
[input, imageAttachments]
161+
);
162+
const setDraft = useCallback(
163+
(draft: DraftState) => {
164+
setInput(draft.text);
165+
setImageAttachments(draft.images);
166+
},
167+
[setInput]
168+
);
169+
const preEditDraftRef = useRef<DraftState>({ text: "", images: [] });
151170
const [mode, setMode] = useMode();
152171
const { recentModels, addModel, defaultModel, setDefaultModel } = useModelLRU();
153172
const commandListId = useId();
@@ -346,10 +365,11 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
346365
};
347366
}, [focusMessageInput]);
348367

349-
// When entering editing mode, populate input with message content
368+
// When entering editing mode, save current draft and populate with message content
350369
useEffect(() => {
351370
if (editingMessage) {
352-
setInput(editingMessage.content);
371+
preEditDraftRef.current = getDraft();
372+
setDraft({ text: editingMessage.content, images: [] });
353373
// Auto-resize textarea and focus
354374
setTimeout(() => {
355375
if (inputRef.current) {
@@ -360,7 +380,8 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
360380
}
361381
}, 0);
362382
}
363-
}, [editingMessage, setInput]);
383+
// eslint-disable-next-line react-hooks/exhaustive-deps -- only run when editingMessage changes
384+
}, [editingMessage]);
364385

365386
// Watch input for slash commands
366387
useEffect(() => {
@@ -829,6 +850,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
829850
// Handler for Escape in vim normal mode - cancels edit if editing
830851
const handleEscapeInNormalMode = () => {
831852
if (variant === "workspace" && editingMessage && props.onCancelEdit) {
853+
setDraft(preEditDraftRef.current);
832854
props.onCancelEdit();
833855
inputRef.current?.blur();
834856
}
@@ -884,6 +906,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
884906
if (matchesKeybind(e, KEYBINDS.CANCEL_EDIT)) {
885907
if (variant === "workspace" && editingMessage && props.onCancelEdit && !vimEnabled) {
886908
e.preventDefault();
909+
setDraft(preEditDraftRef.current);
887910
props.onCancelEdit();
888911
const isFocused = document.activeElement === inputRef.current;
889912
if (isFocused) {

0 commit comments

Comments
 (0)