perf(chat): look up chat txids through a revision-keyed index

The History tab copies every chat txid into a fresh set each frame to
badge and filter chat transactions: M string allocations for M chat
messages. ChatStore gains hasChatTxid(), an O(1) lookup in a txid index
that is rebuilt only after the store changed (append, delivery update,
erase, clear) and never built for an empty store. clear() also drops
the index, so no chat txids linger after a lock. The History tab moves
to it in the next commit.

The chat note-buffer pump, which runs every frame while chat is idle,
built the whole conversation-id list just to see whether the store was
empty; it now asks store().empty(), O(1) instead of O(M).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 09:50:03 -05:00
co-authored by Claude Opus 5.5
parent 860916d203
commit d3fa09128e
4 changed files with 109 additions and 2 deletions
+1 -1
View File
@@ -4854,7 +4854,7 @@ bool App::shouldSplitChatBuffer() {
// depleted, funded. NB: a wall-clock cooldown is deliberately NOT used — a split's outputs stay
// invisible until mined (~1 block, far longer than any UI cooldown), so we gate on a single-split-
// in-flight flag (cleared when the pipeline recovers or the watchdog fires) to prevent runaway splits.
if (chat_service_.store().conversationIds().empty() && chat_send_queue_.empty()) return false;
if (chat_service_.store().empty() && chat_send_queue_.empty()) return false; // == conversationIds().empty(), O(1)
if (!state_.connected || state_.isLocked()) return false;
// Channel busy = our own op inflight, OR a user send holds the shared channel. Lite user sends set
// lite_send_callback_; full-node user sends don't touch inflight_op_, so they're detected via the