Closed general

3D viewer: View Cube disappears, reproducible by dragging it off to the right

Colby Knox · 2mo ago ·closed by Colby Knox

Reported by Colby from a live session: the View Cube in the native 3D viewer vanishes at times, and it reproduces consistently by dragging the cube off to the right edge of the viewport. Once it is gone there is no visible way to bring it back short of reloading the viewer.

Likely mechanism: the cube is draggable with no bounds handling, so a drag can park it entirely outside the visible viewport (or behind an overlaying layer at the right edge), and its position persists.

Suggested fix, either is fine:

  • keep a minimum sliver of the cube on-screen during drag (release outside bounds snaps it back to the nearest edge), or
  • a reset affordance: double-click anywhere on the viewer chrome (or a toolbar entry) restores the cube to its default corner.

If position is persisted per page or per user, the fix should also self-heal existing broken states: on load, clamp a stored off-screen position back into view.

2 Replies

Colby Knox · 2mo ago

Fixed and live on prod (viewer bundle 20260727a), though the mechanism turned out to be different from the one the report assumed, which is worth writing down.

Not drag bounds. The cube's viewport is already re-derived from the canvas size and clamped into range on EVERY frame, so no drag can park it outside the visible area, and a headless repro confirmed a right-edge drag just orbits the camera with the cube following correctly. There is nothing persisted to self-heal either.

The actual mechanism: the cube's own chrome could kill its render. scene2.render() was the LAST statement of the per-frame handler, so any throw above it (hover/tooltip math, a texture swap, or pick state left inconsistent by a pointer that left the cube) skipped the cube's render for that frame, and if the throwing state persisted the cube never came back. That matches the symptom exactly, including "no way to bring it back short of reloading". The pick paths were the likely trigger: they used non-null assertions on results that go null the moment the pointer leaves the cube, which is what dragging off the right edge does.

The fix: the frame body now runs defensively and the cube renders regardless (one-shot console warning if the body ever throws, so the next occurrence names itself); the null-prone pick paths return early instead of asserting; and the recursive ray walk that resolves which cube particle was hit is bounded rather than potentially spinning.

Also fixed the fork's build while in there: package.json declares an invalid "type": "app", so vite loaded its TypeScript config through require and died on the ESM-only Svelte plugin. Configs renamed to .mts, and the build runs in-container again.

Colby: since your gesture did not reproduce headlessly, please try it once more on prod. If the cube still vanishes, the console will now carry a single [view-cube] frame handler error line naming the real throw, which turns this from a hunt into a one-line fix.

Colby Knox · 2mo ago

Confirmed fixed by Colby on prod: the cube no longer disappears when dragged. Closing.

Root cause, for the record: not drag bounds (the viewport was already clamped every frame). scene2.render() was the last statement of the cube's per-frame handler, so a throw anywhere above it skipped the render, permanently once the throwing state persisted. Bundle 20260727a runs that body defensively, guards the null-prone pick paths, and bounds the recursive ray walk.

Log in to reply.