Skip to content

Commit 82090a2

Browse files
committed
support chat draft
1 parent ad201cc commit 82090a2

File tree

2 files changed

+89
-12
lines changed

2 files changed

+89
-12
lines changed

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

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ExtensionMessage } from "@roo/ExtensionMessage"
99

1010
import { vscode } from "@/utils/vscode"
1111
import { useExtensionState } from "@/context/ExtensionStateContext"
12+
import { useChatTextDraft } from "./hooks/useChatTextDraft"
1213
import { useAppTranslation } from "@/i18n/TranslationContext"
1314
import {
1415
ContextMenuOptionType,
@@ -69,6 +70,10 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
6970
ref,
7071
) => {
7172
const { t } = useAppTranslation()
73+
74+
// Chat draft persistence
75+
const { handleSendAndClearDraft } = useChatTextDraft("chat_textarea_draft", inputValue, setInputValue, onSend)
76+
7277
const {
7378
filePaths,
7479
openedTabs,
@@ -389,7 +394,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
389394
if (!sendingDisabled) {
390395
// Reset history navigation state when sending
391396
resetHistoryNavigation()
392-
onSend()
397+
handleSendAndClearDraft()
393398
}
394399
}
395400

@@ -438,22 +443,22 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
438443
}
439444
},
440445
[
441-
sendingDisabled,
442-
onSend,
443446
showContextMenu,
444-
searchQuery,
447+
handleHistoryNavigation,
445448
selectedMenuIndex,
446-
handleMentionSelect,
447-
selectedType,
449+
searchQuery,
448450
inputValue,
449-
cursorPosition,
450-
setInputValue,
451-
justDeletedSpaceAfterMention,
451+
selectedType,
452452
queryItems,
453-
allModes,
454453
fileSearchResults,
455-
handleHistoryNavigation,
454+
allModes,
455+
handleMentionSelect,
456+
sendingDisabled,
456457
resetHistoryNavigation,
458+
handleSendAndClearDraft,
459+
cursorPosition,
460+
justDeletedSpaceAfterMention,
461+
setInputValue,
457462
],
458463
)
459464

@@ -1151,7 +1156,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
11511156
iconClass="codicon-send"
11521157
title={t("chat:sendMessage")}
11531158
disabled={sendingDisabled}
1154-
onClick={onSend}
1159+
onClick={handleSendAndClearDraft}
11551160
/>
11561161
</div>
11571162
</div>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useCallback, useEffect, useRef } from "react"
2+
3+
/**
4+
* Hook for chat textarea draft persistence (localStorage).
5+
* Handles auto-save, restore on mount, and clear on send.
6+
* @param draftKey localStorage key for draft persistence
7+
* @param inputValue current textarea value
8+
* @param setInputValue setter for textarea value
9+
* @param onSend send callback
10+
*/
11+
export function useChatTextDraft(
12+
draftKey: string,
13+
inputValue: string,
14+
setInputValue: (value: string) => void,
15+
onSend: () => void,
16+
) {
17+
const saveDraftTimerRef = useRef<NodeJS.Timeout | null>(null)
18+
19+
// Restore draft on mount
20+
useEffect(() => {
21+
try {
22+
const draft = localStorage.getItem(draftKey)
23+
if (draft && !inputValue) {
24+
setInputValue(draft)
25+
}
26+
} catch (_) {
27+
// ignore
28+
}
29+
// Only run on initial mount
30+
// eslint-disable-next-line react-hooks/exhaustive-deps
31+
}, [])
32+
33+
// Periodically save draft
34+
useEffect(() => {
35+
if (saveDraftTimerRef.current) {
36+
clearInterval(saveDraftTimerRef.current)
37+
}
38+
if (inputValue && inputValue.trim()) {
39+
saveDraftTimerRef.current = setInterval(() => {
40+
try {
41+
localStorage.setItem(draftKey, inputValue)
42+
} catch (_) {
43+
// ignore
44+
}
45+
}, 5000)
46+
} else {
47+
// Remove draft if no content
48+
try {
49+
localStorage.removeItem(draftKey)
50+
} catch (_) {
51+
// ignore
52+
}
53+
}
54+
return () => {
55+
if (saveDraftTimerRef.current) {
56+
clearInterval(saveDraftTimerRef.current)
57+
}
58+
}
59+
}, [inputValue, draftKey])
60+
61+
// Clear draft after send
62+
const handleSendAndClearDraft = useCallback(() => {
63+
try {
64+
localStorage.removeItem(draftKey)
65+
} catch (_) {
66+
// ignore
67+
}
68+
onSend()
69+
}, [onSend, draftKey])
70+
71+
return { handleSendAndClearDraft }
72+
}

0 commit comments

Comments
 (0)