fix(app): debounce prompt draft persistence - #40155
Conversation
Typing in the desktop chat input was lagging because every keystroke JSON-serialized the prompt store and wrote it through IPC to electron-store. Debounce those writes and update prompt+cursor in one store set so desktop input stays responsive.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
Updated the PR description to the required template and linked Fixes #36486. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
There was a problem hiding this comment.
Pull request overview
This PR reduces prompt draft persistence overhead during typing (especially on desktop) by debouncing storage writes and minimizing redundant persisted-store updates, with a unit test intended to validate write coalescing.
Changes:
- Add an optional
debounceMsto persisted targets and wrap storage with a debounced write layer. - Update prompt + cursor in a single store write to avoid duplicate persistence serialization.
- Add a unit test to validate debounced write coalescing behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/session-ui/src/v2/components/prompt-input/store.ts | Consolidates prompt+cursor updates into single store writes for fewer persisted-store updates. |
| packages/app/src/utils/persist.ts | Introduces debounceMs and debounced storage wrapper for persisted writes. |
| packages/app/src/utils/persist.test.ts | Adds coverage for debounced storage write coalescing. |
| packages/app/src/context/prompt-state.ts | Applies debounced persistence to prompt drafts and consolidates prompt+cursor writes. |
Suppressed comments (1)
packages/app/src/utils/persist.test.ts:234
- The debounce test currently relies on wall-clock timing and a Promise wrapper. Using
vi.useFakeTimers()makes this deterministic and also lets the test cover the important cleanup-flush behavior (dispose before the timer fires) that is relied on when quitting the desktop app soon after typing.
test("debounceWrites coalesces rapid setItem calls into one flush", async () => {
await new Promise<void>((resolve, reject) => {
createRoot((dispose) => {
try {
const api = persistTesting.debounceWrites(persistTesting.localStorageDirect(), 20)
api.setItem("draft", '{"v":1}')
api.setItem("draft", '{"v":2}')
api.setItem("draft", '{"v":3}')
expect(storage.calls.set).toBe(0)
setTimeout(() => {
try {
expect(storage.calls.set).toBe(1)
expect(storage.getItem("draft")).toBe('{"v":3}')
dispose()
resolve()
} catch (error) {
dispose()
reject(error)
}
}, 40)
} catch (error) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Preserve live AsyncStorage getters with a Proxy wrapper, and drive debounce tests with fake timers plus dispose-flush coverage.
|
Addressed Copilot review feedback:
|
Issue for this PR
Fixes #36486
Type of change
What does this PR do?
Desktop chat typing lags because every keystroke persists the prompt draft through IPC to electron-store (full-file rewrite), with no debounce. Other inputs do not hit this path.
This adds optional
debounceMstopersisted(), enables 300ms debounce for prompt drafts, and updates prompt+cursor in one store write so we do not serialize/persist twice per keystroke. Drafts still flush on idle, blur/unload, and dispose.How did you verify your code works?
bun test src/utils/persist.test.ts src/context/prompt-state.test.tsinpackages/appbun test src/v2/components/prompt-input/store.test.tsinpackages/session-uiScreenshots / recordings
N/A (performance fix, no visual UI change).
Checklist