perf(word-addin): cut streaming and chat-open hot-path costs in the tracked-edits runtime
Three fixes to the same runtime, all about respecting WKWebView's single main thread: work done per SSE chunk or per Word round-trip competes directly with paint, scroll handling, and typing. 1. PARSE STREAMED REDLINES ONCE PER FRAME, NOT PER CHUNK WHY: projectRedlineStream re-parses the accumulated answer from index zero, so invoking it synchronously on every SSE chunk makes a stream O(n^2) -- the longer the answer, the more each keystroke-sized chunk costs, exactly while the transcript is animating. HOW: the chunk handler now only flags redlineParsePending; the projection runs inside the same requestAnimationFrame callback that already coalesces the transcript publish, i.e. once per painted frame with the latest snapshot. Terminal paths stay exact: success flushes synchronously and still runs the streamComplete pass, and the catch/abort path flushes before markIncompleteRedlines, so a sealed edit in a trailing un-flushed chunk still applies on cancel. A sendIsCurrent() predicate (currency minus the abort bit) guards the deferred parse so a rAF firing after a session switch cannot schedule Word edits under the new generation -- deliberately not the stricter requestIsCurrent, because an aborted-but-current stream must keep the edits it already received. 2. STOP THE EDIT CONTROLLER FROM RECREATING handleChat MID-STREAM WHY: the hook returned a fresh object literal carrying editStateByKey, and handleChat listed that controller in its deps -- so every receiving->applying->pending transition recreated handleChat and re-rendered everything holding it, defeating the message-ref mirroring built precisely to keep it stable. HOW: the controller now exposes a useMemo'd streamController (processLiveRedlines / markIncompleteRedlines / waitForMessageEdits, all useCallback-stable) separate from editStateByKey. The chat hook receives only the stable streamController, so handleChat's identity survives the whole stream; components that render edit state still consume editStateByKey and re-render on real state changes. 3. RESTORE A CHAT'S TRACKED EDITS IN ONE Word.run BATCH WHY: opening a chat ran one serialized Word.run (~4 context.sync() host round-trips) per stored edit behind the global mutation queue -- pane readiness was linear in chat history, and every user action queued behind the backlog. Each sync is a WKWebView<->host hop. HOW: restoreTrackedEdits(descriptors) performs one Word.run for the whole set: all getBookmarkRangeOrNullObject lookups load before one sync, then items, verification, stale-bookmark deletes, and tracking -- a constant ~4 syncs total. Missing bookmarks are null objects, never batch failures; per-edit classification (not-found / resolved / view-only / restored) is preserved verbatim; if Word fails the shared batch outright, each edit retries sequentially via restoreTrackedEditNow so one bad object cannot sink the rest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015U1iPxsADQ2yuksNmDuvvW
| Repository | open-legal-products/mike |
|---|---|
| Author | Amal <mamalanand3@gmail.com> |
| Authored | |
| Committed | |
| Parents | 80ac1dca |
| Stats | 5 files changed , +301 , -40 |
| Part of | Bring the Word add-in chat experience in line with the web app |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-72254ef3.md
from inside the repo you want the change in.