Skip to content

feat(toolbar): real-time SSE sync for created & deleted annotations#24

Merged
raimannma merged 1 commit into
mainfrom
feat/sse-created-deleted-sync
Jun 30, 2026
Merged

feat(toolbar): real-time SSE sync for created & deleted annotations#24
raimannma merged 1 commit into
mainfrom
feat/sse-created-deleted-sync

Conversation

@raimannma

Copy link
Copy Markdown
Owner

What

Extends the toolbar's existing EventSource listener (which only handled annotation.updated) to also handle annotation.created and annotation.deleted, giving the toolbar live two-way sync with the server.

  • created: adds the remote annotation to local state, deduped by id so the server's SSE echo of our own optimistic create isn't double-added. Placement/rearrange kinds are skipped (they have their own local state); resolved/dismissed are skipped via isRenderableAnnotation.
  • deleted: removes the annotation from local state. The removal logic (feedback exit animation + placement/rearrange reverse-lookups) is factored into a shared removeAnnotationLocally helper reused by the updated handler, so annotation.updated behavior is unchanged.
  • race handling: the optimistic-create id swap in addAnnotation is now dedup-aware — if the SSE created echo lands before the POST response resolves, the optimistic copy is dropped instead of renamed (which would otherwise duplicate the marker).

Server

No server changes needed: mcp/src/server/store.ts already emits annotation.created/annotation.updated/annotation.deleted, and http.ts (/sessions/:id/events) already broadcasts every event type to browser SSE clients. Only the client handler was missing.

Reimplemented, not cherry-picked

Concept from upstream benjitaylor#116 by @remorses. That PR is stale and bundles already-merged draw-mode code, and its rewritten handler dropped this fork's placement/rearrange special-casing. Reimplemented the ~50-line SSE concept cleanly against current code, keeping the fork's behavior intact.

Behavior changes to flag for maintainer

  • The dedup-aware id swap is a small change to the existing create flow — worth a glance.
  • SSE handlers intentionally do not fire the onAnnotationAdd/onAnnotationDelete callbacks for remote events (those callbacks fire only for local user actions, as before), avoiding a stale-closure annotationsRef. Flag if you'd prefer remote events to invoke callbacks too.

Build / test

pnpm build passes; pnpm --filter agentation test passes (103 tests, incl. 3 new for the dedup-on-created helper).

🤖 Generated with Claude Code

The SSE listener in the toolbar only handled "annotation.updated"
(resolved/dismissed removal). Extend the same EventSource handler to
also react to "annotation.created" and "annotation.deleted" so changes
made by the agent or other connected clients sync into the view live.

- created: add the remote annotation to local state, deduped by id so
  we don't double-add the server's echo of our own optimistic create.
  The optimistic-create id swap is now dedup-aware to cover the race
  where the SSE echo lands before the POST response resolves.
- deleted: remove the annotation from local state (shared removal path
  with updated, preserving the feedback exit animation and the
  placement/rearrange reverse-lookups).

The server already emits and broadcasts all three event types, so only
the client handler needed wiring. Added a unit test for the
dedup-on-created logic.

Reimplemented from the ~50-line SSE concept in upstream
benjitaylor#116 by @remorses rather than cherry-picked: the
upstream PR is stale and bundles already-merged draw-mode code, and its
handler dropped this fork's placement/rearrange handling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@raimannma raimannma self-assigned this Jun 30, 2026
@raimannma raimannma merged commit b85b159 into main Jun 30, 2026
@raimannma raimannma deleted the feat/sse-created-deleted-sync branch June 30, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant