Skip to content

Improve realtime collaboration feedback and previews#523

Merged
Aymericr merged 1 commit into
mainfrom
feat/realtime-collaboration-sfx
Jul 20, 2026
Merged

Improve realtime collaboration feedback and previews#523
Aymericr merged 1 commit into
mainfrom
feat/realtime-collaboration-sfx

Conversation

@Aymericr

@Aymericr Aymericr commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • expose host-owned remote selection highlights without changing local selection state
  • render native placement previews for walls, openings, roof attachments, and catalog items
  • add bounded spatial audio feedback for remote collaboration activity

Verification

  • bun run check-types
  • bun run build
  • focused editor, viewer, node preview, and SFX tests (11 passing)

Note

Medium Risk
Touches placement lifecycles across many node tools and shared viewer selection/outliner paths; regressions could show stale ghosts or wrong highlights, though SFX and preview changes are largely additive with tests.

Overview
Improves realtime collaboration feedback and placement preview parity between 2D floor plan and 3D.

Remote selection — The viewer gains externalSelectedIds / setExternalSelectedIds so a host can drive outliner selection highlights without mutating the user’s editable selection. Outliner sync also reacts to geometryRevision so highlights stay aligned after mesh updates.

Unified placement preview — Tools and draft-item flows publish transient nodes into usePlacementPreview. FloorplanNodePreview now builds floorplan geometry with siblings, level data, theme palette, and optional interaction flags so 2D ghosts match committed symbols. Door/window tools publish previews for on-wall drafts and off-host floating ghosts (synthetic host wall); roof accessories (chimney, dormer, skylight, solar panel) and item drafts wire the same store with explicit clear on commit/cancel.

Registry tooling — Kinds register lazy preview modules (including wall segment extrusion preview and catalog ItemPreview). preloadRegistryToolModules preloads tool, preview, renderer, system, and move modules before the lazy tool mounts so first placement stays synchronous under dev bundling.

AudioplaySFX accepts source, stereo, and volumeMultiplier for quieter, spatial remote cues; rate limiting is per source, remote yields to recent local plays, and playback is skipped while Howler sounds are still loading to avoid async Web Audio errors.

Reviewed by Cursor Bugbot for commit e1a4dba. Bugbot is set up for automated code reviews on this repo. Configure here.

usePlacementPreview.getState().set(
node,
node.parentId ? (useScene.getState().nodes[node.parentId as AnyNodeId] ?? null) : null,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale item placement ghost

High Severity

The useDraftNode hook initializes the usePlacementPreview store only when a draft node is created or adopted, but doesn't update it as the node's position changes in the scene. This causes UI components relying on usePlacementPreview to render a stale ghost of the item at its initial position, appearing alongside the correctly rendered live item.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1ab9f92. Configure here.

if (obj) outliner.hoveredObjects.push(obj)
}
}, [selection, hoveredId, outliner, nodes])
}, [selection, externalSelectedIds, hoveredId, outliner, nodes, geometryRevision])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Editor ignores remote highlights

High Severity

The externalSelectedIds are now included in the viewer's OutlinerSync for displaying selections, but the editor's EditorOutlinerSync and SelectionMaterialSync haven't been updated to also incorporate these IDs. This means collaborator selections aren't visible in the editor.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1ab9f92. Configure here.

if (live?.type !== 'door') return
draftRef.current = live
publishPlacementPreview(live, parentNode)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Opening draft ghosts duplicate

Medium Severity

On-wall door/window drafts are already scene nodes rendered by the floorplan registry, and publishDraftPreview also pushes that same live node into usePlacementPreview. Split 2D+3D placement stacks two identical opening symbols and darkens the ghost via overlapping opacity.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1ab9f92. Configure here.

@Aymericr
Aymericr force-pushed the feat/realtime-collaboration-sfx branch from 1ab9f92 to 5444022 Compare July 20, 2026 17:39

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5444022. Configure here.

// Howler queues per-play mutations while a sound is loading. If its global
// AudioContext is replaced before that queue drains, stereo setup can try
// to connect nodes from different contexts and throw asynchronously.
if (sound.state() !== 'loaded') return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SFX debounce blocks retries

High Severity

The lastPlayedAt timestamp is recorded before verifying the sound asset is loaded. If the sound isn't ready, the SFX is skipped, but the debounce window is still consumed. This incorrectly prevents the SFX from playing for minIntervalMs, even after the asset becomes available.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5444022. Configure here.

@Aymericr
Aymericr force-pushed the feat/realtime-collaboration-sfx branch from 5444022 to e1a4dba Compare July 20, 2026 18:25
@Aymericr
Aymericr merged commit 34b601b into main Jul 20, 2026
2 checks passed
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