Skip to content

Fine grained reactivity via Jotai#174

Merged
streamer45 merged 13 commits intomainfrom
jotai
Mar 19, 2026
Merged

Fine grained reactivity via Jotai#174
streamer45 merged 13 commits intomainfrom
jotai

Conversation

@streamer45
Copy link
Owner

@streamer45 streamer45 commented Mar 18, 2026

Summary

PR replaces coarse-grained Zustand subscriptions with per-entity Jotai atoms in the three highest-frequency state paths: compositor layers, session node states, and node parameters.


Staging: Open in Devin

@streamer45 streamer45 added this to the v0.4.0 milestone Mar 18, 2026
@streamer45 streamer45 self-assigned this Mar 18, 2026
staging-devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@staging-devin-ai-integration staging-devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 10 additional findings in Devin Review.

Staging: Open in Devin
Debug

Playground

Comment on lines +174 to +181
// Null out removed layers in this store. Do NOT call layerAtoms.remove() —
// the atomFamily cache is global (shared across all compositor stores), and
// removing an entry would invalidate other stores that share the same layer ID.
for (const prevId of prevIds) {
if (!newIdSet.has(prevId)) {
store.set(layerAtoms(prevId), null);
}
}
Copy link
Contributor

@staging-devin-ai-integration staging-devin-ai-integration bot Mar 18, 2026

Choose a reason for hiding this comment

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

🚩 Global atomFamily cache grows unboundedly for compositor overlay IDs

The layerAtoms, textOverlayAtoms, and imageOverlayAtoms atomFamily caches are global (shared across all compositor stores). The comment at ui/src/hooks/compositorAtoms.ts:174-176 explains that remove() isn't called because it would invalidate other stores sharing the same layer ID. However, text/image overlays use crypto.randomUUID() for IDs (ui/src/hooks/compositorOverlays.ts:338,409), meaning each add/remove cycle creates a permanent atomFamily entry. Additionally, selectedLayerKindAtom (ui/src/hooks/compositorAtoms.ts:85-92) creates phantom entries when checking if an ID belongs to a different layer type (e.g., calling get(layerAtoms(textOverlayId)) creates a null entry in layerAtoms for a text overlay ID). For long-running sessions with frequent overlay additions/removals, this accumulates stale entries. Consider periodic cleanup when a compositor unmounts, or a WeakRef-based approach.

Staging: Open in Devin

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

staging-devin-ai-integration[bot]

This comment was marked as resolved.

staging-devin-ai-integration[bot]

This comment was marked as resolved.

staging-devin-ai-integration[bot]

This comment was marked as resolved.

@streamer45 streamer45 merged commit 731f04d into main Mar 19, 2026
16 checks passed
@streamer45 streamer45 deleted the jotai branch March 19, 2026 15:26
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