Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions webapp/static/js/sticky-notes.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,11 +722,15 @@
const nextSeq = (this._pendingSeq.get(id) || 0) + 1;
this._pendingSeq.set(id, nextSeq);
} catch(_) {}
this._persistCacheFromMemory();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unbounded localStorage writes on every keystroke

Medium Severity

_persistCacheFromMemory() is called inside _queueSave, which fires on every textarea input event (every keystroke). This synchronously iterates all notes, runs JSON.stringify on the entire collection, and writes to localStorage on the main thread — on every single character typed. The existing _saveDebounced() on the next line was intentionally debounced at 500ms to avoid this exact kind of overhead. The persist call bypasses that debouncing entirely, potentially causing noticeable input lag when many notes exist.

Fix in Cursor Fix in Web

this._saveDebounced();
this._ensureBackgroundAutoFlush();
}

_flushPendingKeepalive(){
try {
this._persistCacheFromMemory();
} catch(_) {}
try {
const combined = new Map();
try {
Expand Down Expand Up @@ -982,6 +986,7 @@
}
}
}
this._persistCacheFromMemory();
if ((!this._pending || this._pending.size === 0) && (!this._inFlight || this._inFlight.size === 0)) {
this._stopBackgroundAutoFlush();
}
Expand All @@ -999,6 +1004,7 @@
}
this._pending.delete(id);
await this._sendUpdate(id, data);
this._persistCacheFromMemory();
if ((!this._pending || this._pending.size === 0) && (!this._inFlight || this._inFlight.size === 0)) {
this._stopBackgroundAutoFlush();
}
Expand Down Expand Up @@ -1351,6 +1357,16 @@
localStorage.setItem(this._cacheKey, JSON.stringify(payload));
} catch(_) {}
}

_persistCacheFromMemory(){
try {
const all = [];
for (const [, entry] of this.notes.entries()) {
if (entry && entry.data) all.push(entry.data);
}
this._saveCache(all);
} catch(_) {}
}
}

// Expose
Expand Down
Loading