feat: preserve input text draft across navigation#9809
feat: preserve input text draft across navigation#9809uinstinct wants to merge 14 commits intocontinuedev:mainfrom
Conversation
RomneyDa
left a comment
There was a problem hiding this comment.
@uinstinct it looks like this only works for the main input. I think it's an improvement but can we think of a simple way to have this work across conversations and in any tip tap editor when going to settings and back?
- On closing/reopening vs code it's okay to lose drafts in any editor but would be nice if main editor was saved (already works on this branch) and great if all were saved (in a way that doesn't leak local storage etc memory)
- On going to settings and back any editor's content is preserved, as well as the scroll position of the chat history in case I was editing message number 2 in a 5 message sequence
If too many tradeoffs we can merge this but let's brainstorm on full solution first. There might be a simple solution where we don't unmount the component at all when switching to settings (router level fix).
and scroll to position when going back
It currently saves all - there would not be any memory leakage, as far as I think, because there can only be 2 drafts be saved: inputDraft_chat or inputDraft_edit which get cleared during submission
I did a simple implementation for this to save the editing draft and scroll back to chat history position. feat.mp4 |
RomneyDa
left a comment
There was a problem hiding this comment.
@uinstinct I think with the positioned draft approach if the draft is not submitted and a main input message is submitted instead it will not clear the other draft so the UI will glitch around, including if you started up vs code it would "glitch" to the input draft even if it was outdated
| } | ||
| } else { | ||
| if (hasValidEditorContent(content)) { | ||
| const scrollContainer = document.querySelector( |
There was a problem hiding this comment.
let's use a more robust way to get the scroll container e.g. id or non generic class
Added removing of both drafts (editing and main input) after submission and gui window unmount. |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="gui/src/hooks/ParallelListeners.tsx">
<violation number="1" location="gui/src/hooks/ParallelListeners.tsx:270">
P2: The cleanup-only useEffect has no dependency array, so it runs after every render and clears draft keys on each re-render. Because this component re-renders from Redux state updates, saved drafts are repeatedly deleted, undermining persistence. Add a dependency array (or otherwise scope cleanup) so it only runs on unmount if that’s the intent.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="gui/src/hooks/ParallelListeners.tsx">
<violation number="1" location="gui/src/hooks/ParallelListeners.tsx:275">
P2: Cleaning localStorage drafts on ParallelListeners unmount undermines draft persistence and can wipe drafts in Strict Mode remounts or when the root component is disposed.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
Note, I'm changing this to be redux only The downside is it won't persist across hard refreshes like IDE restart |
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="gui/src/pages/gui/Chat.tsx">
<violation number="1" location="gui/src/pages/gui/Chat.tsx:370">
P2: `renderChatHistoryItem` captures `editingDraft` but omits it from `useCallback` dependencies, risking stale draft content rendering.</violation>
</file>
<file name="gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts">
<violation number="1" location="gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts:403">
P1: Draft persistence was switched from direct localStorage writes to Redux actions, but the persisted Redux session subset excludes `inputDraft` and `editingDraft`, so drafts are not saved across reloads.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts
Outdated
Show resolved
Hide resolved
Storing editor drafts (which can contain full file contents from context items) in localStorage risks hitting the ~5-10MB quota and throwing unhandled QuotaExceededError on every keystroke. Move draft state to non-persisted Redux, which avoids serialization cost, has no quota limit, and auto-clears on app close.
Drop scrollTop capture/restore since drafts are now in-memory only and won't survive a reload. Remove unused clearDrafts reducer.
faf830d to
2547760
Compare
Collapse two separate draft fields into one. If messageId is set, it's an edit of a previous message; if not, it's the main input draft.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="gui/src/pages/gui/Chat.tsx">
<violation number="1" location="gui/src/pages/gui/Chat.tsx:478">
P2: Using a single draft state for both main input and message editing can overwrite and lose an unsent main-input draft when an edit draft is created.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
gui/src/pages/gui/Chat.tsx
Outdated
| onEnter={(editorState, modifiers, editor) => | ||
| sendInput(editorState, modifiers, undefined, editor) | ||
| } | ||
| editorState={draft?.messageId ? undefined : draft?.content} |
There was a problem hiding this comment.
P2: Using a single draft state for both main input and message editing can overwrite and lose an unsent main-input draft when an edit draft is created.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At gui/src/pages/gui/Chat.tsx, line 478:
<comment>Using a single draft state for both main input and message editing can overwrite and lose an unsent main-input draft when an edit draft is created.</comment>
<file context>
@@ -482,7 +475,7 @@ export function Chat() {
sendInput(editorState, modifiers, undefined, editor)
}
- editorState={inputDraft}
+ editorState={draft?.messageId ? undefined : draft?.content}
inputId={MAIN_EDITOR_INPUT_ID}
/>
</file context>
Avoids re-rendering the entire Chat component on every keystroke. ContinueInputBox already has the context needed to resolve drafts.
TipTap is uncontrolled — it ignores editorState prop changes after initialization. Clearing the draft on empty content left the editor showing empty even though resolvedEditorState fell back to the original message. Drafts are now only cleared on submission.
|
My adjustment still feels buggy at edge cases like empty drafts etc. Can revisit |
Description
Save the unsubmitted input text so that it persists between page changes. Drafts are stored in Redux (in-memory) rather than localStorage to avoid quota issues — editor content can be large when context items embed full file contents.
resolves CON-5281
AI Code Review
@continue-reviewChecklist
Screen recording or screenshot
before.mp4
after.mp4
Notes
inputDraftandeditingDraftto ReduxsessionSlicestate, withsetInputDraft,setEditingDraft, andclearDraftsreducers.InputDraftWithPositiontype and draft key definitions fromlocalStorage.ts.ParallelListenerscleanup effect (no longer needed — Redux auto-clears on app close).Tests
sessionSlice.test.tsto include new state fields in test initial state.