Skip to content

fix(editor): draw a newly opened document tab on its first paint - #43

Closed
pythonlearner1025 wants to merge 1 commit into
mainfrom
fix/new-tab-viewport-empty
Closed

pythonlearner1025 wants to merge 1 commit into
mainfrom
fix/new-tab-viewport-empty

Conversation

@pythonlearner1025

Copy link
Copy Markdown
Member

What part this touches

The editor's centre area, in packages/editor. The centre strip holds one tab per open document. The viewport is one three.js canvas, owned by the viewer, that the editor moves into whichever tab panel is active. The panel layout in WindowPanesLayout.tsx renders only the active tab's panel, so every tab change unmounts the old panel and mounts a new, empty one. ThreeEditorComponent.tsx is the component that moves the canvas into the mounted panel.

Documents live in packages/editor/src/documents. DocumentStore.open(path) adds a tab for a file and then activates it. Each of those two steps triggers a React render.

The bug

Reported by the owner: "newly opened tabs viewport remain empty until i switch to another viewport tab and then switch back to the newly opened one."

Step by step, measured in a headless run:

  1. The user opens a file. The store adds the tab and renders. The new panel has not mounted yet.
  2. The effect that moves the canvas depends on the container ref's current value. React reads that value during render, one commit behind. On this render it sees the ref change and runs as a no-op, because the old panel is still mounted.
  3. The store activates the tab and renders again. The panel swaps now. The effect reads the same stale value it already used and skips.
  4. The canvas stays inside the detached panel. The visible panel is empty, and the canvas reports a size of 0 by 0.
  5. A plain tab switch renders once, so the stale value alternates and the effect runs. That is why switching away and back repairs it.

Proof of the mechanism, without a code change: after open, one extra React render with no tab change put the canvas back. The script is in the evidence folder.

The fix

  • The canvas container is a ref callback keyed on the viewer, not a value ref read by an effect. The mounted node itself reports its mount, so the canvas moves on every mount.
  • The unused useRef import goes.
  • Nothing changes in the document store, the tab strip, or the viewport.

The risk trade

  • The ref callback runs on every mount and unmount of the container. Moving one DOM node is cheap, and open times stayed between 2 ms and 100 ms.
  • A material document still draws nothing, before and after this fix, on any tab. That is a separate bug, not covered here.
  • Rejected alternative: an extra store render after open. It hides the timing instead of fixing it.

Tests

  • New packages/kite3d/test/editor-open-tab.test.ts drives the Files panel double-click headless and asserts the canvas is mounted and drawn on the first paint. Without the fix it fails with expected { mounted: false, drawn: false } to deeply equal { mounted: true, drawn: true }.
  • npm test -w packages/kite3d: 7 pass. npm run typecheck, npm run lint, npm run build:editor: pass, re-run after the rebase onto the Resources tab commit.
  • Headless Chromium, three runs of four document kinds with a fresh session each, first paint against the switch-back paint: blank first paints 9 of 12 before, 3 of 12 after, and the three remaining are the material document that draws nothing on either build.
  • Evidence with per-kind canvas crops and run data: ~/games/terminator-evidence/docs/evidence/kite3d-open-tab/.

Deploy

The editor ships inside the @kite3d/editor and kite3d packages. Nothing ships until this lands on main and a release runs.

git checkout main && git pull
npm run build
npm run release:dry
npm run release

Rollback: revert the squash commit on main, rebuild, and release the next alpha.

git revert <squash sha>

🤖 Generated with Claude Code

A newly opened tab showed an empty viewport until the user switched
to another tab and back. The centre strip renders only the active tab
panel, so every tab change mounts a new canvas container. The effect
that moves viewer.container into it depended on the container ref's
current value, which React reads during render, one commit behind.
DocumentStore.open renders twice, once for the new tab and once for
its activation, so the first render ran the effect as a no-op and the
second read the same stale value and skipped it. The canvas stayed in
the detached panel. A plain tab switch renders once, which is why
switching away and back repaired it.

Fix: a ref callback keyed on the viewer, so the mounted node itself
reports its mount and the container moves on every mount.

Verification: headless Chromium, three runs of four document kinds
with a fresh session each: first paint blank 9 of 12 before, 3 of 12
after, and the remaining three are a material document that draws
nothing before and after and on any tab, a separate bug. The new
packages/kite3d/test/editor-open-tab.test.ts fails without the fix.
npm test -w packages/kite3d 7 pass; typecheck, lint, build:editor pass.
Evidence under ~/games/terminator-evidence/docs/evidence/kite3d-open-tab/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@pythonlearner1025
pythonlearner1025 deleted the fix/new-tab-viewport-empty branch September 16, 2026 23:13
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