mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: implement chat draft persistence
- Add chatDraft field to GlobalState type in global-settings.ts - Implement draft saving/loading in ChatView component with 500ms debounce - Add message handlers for draft persistence in webviewMessageHandler - Add new WebviewMessage and ExtensionMessage types for draft operations - Clear draft when task starts to avoid interference - Persist both text and images in draft Fixes #8236
This commit is contained in:
parent
0e1b23d09c
commit
aebd78e079
5 changed files with 70 additions and 0 deletions
|
|
@ -43,6 +43,14 @@ export const globalSettingsSchema = z.object({
|
|||
taskHistory: z.array(historyItemSchema).optional(),
|
||||
dismissedUpsells: z.array(z.string()).optional(),
|
||||
|
||||
// Chat draft persistence
|
||||
chatDraft: z
|
||||
.object({
|
||||
text: z.string(),
|
||||
images: z.array(z.string()),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
// Image generation settings (experimental) - flattened for simplicity
|
||||
openRouterImageApiKey: z.string().optional(),
|
||||
openRouterImageGenerationSelectedModel: z.string().optional(),
|
||||
|
|
|
|||
|
|
@ -3058,5 +3058,27 @@ export const webviewMessageHandler = async (
|
|||
})
|
||||
break
|
||||
}
|
||||
case "persistDraft": {
|
||||
// Save the draft to global state
|
||||
await updateGlobalState("chatDraft", { text: message.text || "", images: message.images || [] })
|
||||
break
|
||||
}
|
||||
case "clearPersistedDraft": {
|
||||
// Clear the persisted draft
|
||||
await updateGlobalState("chatDraft", undefined)
|
||||
break
|
||||
}
|
||||
case "requestPersistedDraft": {
|
||||
// Send the persisted draft to the webview
|
||||
const draft = getGlobalState("chatDraft")
|
||||
if (draft) {
|
||||
await provider.postMessageToWebview({
|
||||
type: "persistedDraft",
|
||||
text: draft.text,
|
||||
images: draft.images,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ export interface ExtensionMessage {
|
|||
| "commands"
|
||||
| "insertTextIntoTextarea"
|
||||
| "dismissedUpsells"
|
||||
| "persistedDraft"
|
||||
text?: string
|
||||
payload?: any // Add a generic payload for now, can refine later
|
||||
action?:
|
||||
|
|
|
|||
|
|
@ -227,6 +227,9 @@ export interface WebviewMessage {
|
|||
| "editQueuedMessage"
|
||||
| "dismissUpsell"
|
||||
| "getDismissedUpsells"
|
||||
| "persistDraft"
|
||||
| "clearPersistedDraft"
|
||||
| "requestPersistedDraft"
|
||||
text?: string
|
||||
editedMessageContent?: string
|
||||
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud"
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
// Has to be after api_req_finished are all reduced into api_req_started messages.
|
||||
const apiMetrics = useMemo(() => getApiMetrics(modifiedMessages), [modifiedMessages])
|
||||
|
||||
// Initialize input value from persisted draft if available
|
||||
const [inputValue, setInputValue] = useState("")
|
||||
const inputValueRef = useRef(inputValue)
|
||||
const textAreaRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
|
@ -224,6 +225,28 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
inputValueRef.current = inputValue
|
||||
}, [inputValue])
|
||||
|
||||
// Load persisted draft on mount
|
||||
useEffect(() => {
|
||||
// Request the persisted draft from the extension
|
||||
vscode.postMessage({ type: "requestPersistedDraft" })
|
||||
}, [])
|
||||
|
||||
// Save draft when input changes (debounced)
|
||||
useEffect(() => {
|
||||
// Don't save empty drafts or when there's an active task
|
||||
if (!task && (inputValue.trim() || selectedImages.length > 0)) {
|
||||
const timer = setTimeout(() => {
|
||||
vscode.postMessage({
|
||||
type: "persistDraft",
|
||||
text: inputValue,
|
||||
images: selectedImages,
|
||||
})
|
||||
}, 500) // Debounce for 500ms
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [inputValue, selectedImages, task])
|
||||
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true
|
||||
return () => {
|
||||
|
|
@ -582,6 +605,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
// setPrimaryButtonText(undefined)
|
||||
// setSecondaryButtonText(undefined)
|
||||
disableAutoScrollRef.current = false
|
||||
|
||||
// Clear the persisted draft when chat is reset (task started)
|
||||
vscode.postMessage({ type: "clearPersistedDraft" })
|
||||
}, [])
|
||||
|
||||
/**
|
||||
|
|
@ -793,6 +819,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
break
|
||||
}
|
||||
break
|
||||
case "persistedDraft":
|
||||
// Restore the persisted draft if we don't have an active task
|
||||
if (!task && message.text !== undefined) {
|
||||
setInputValue(message.text)
|
||||
if (message.images && message.images.length > 0) {
|
||||
setSelectedImages(message.images)
|
||||
}
|
||||
}
|
||||
break
|
||||
case "selectedImages":
|
||||
// Only handle selectedImages if it's not for editing context
|
||||
// When context is "edit", ChatRow will handle the images
|
||||
|
|
@ -845,6 +880,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
handleSetChatBoxMessage,
|
||||
handlePrimaryButtonClick,
|
||||
handleSecondaryButtonClick,
|
||||
task,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue