Skip to content

fix: prevent blank page when switching workspaces after creating a new one#19

Merged
lyx-tec merged 1 commit into
mainfrom
fix/workspace-switch-blank
Jul 15, 2026
Merged

fix: prevent blank page when switching workspaces after creating a new one#19
lyx-tec merged 1 commit into
mainfrom
fix/workspace-switch-blank

Conversation

@lyx-tec

@lyx-tec lyx-tec commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Problem

After creating a new workspace, switching back to any existing workspace causes the entire window to render blank (no UI elements at all). Normal workspace switching without creating a new one works fine.

Root Cause — Two Intertwined Issues

1. workspaceIdAtom was a Jotai derived atom with unreliable async reactivity

workspaceIdAtom was defined as a computed atom that derived its value from the WOS cache:

const workspaceIdAtom = atom((get) => {
    const windowData = WOS.getObjectValue<WaveWindow>(..., get);
    return windowData?.workspaceid ?? null;
});

During reinitWave, WOS.reloadWaveObject() updates the WOS cache via globalStore.set(). In normal workspace switching, Jotai detects the change and triggers re-render. But after a CreateWorkspace -> SwitchWorkspace cycle, the async chain across multiple await calls could silently break Jotai's dependency tracking — the atoms updated but React never re-rendered, leaving #main with zero children.

Fix: Changed workspaceIdAtom to a writable PrimitiveAtom<string>, explicitly set via globalStore.set(atoms.workspaceId, ws.oid) in both initWave and reinitWave. Also set staticTabId and activeTabIdAtom explicitly before reinit.

2. Electron WebContentsView rendering pipeline broke after off-screen positioning

The switchworkspace handler moved BrowserViews off-screen using positionTabOffScreen (setBounds({x: -15000, y: -15000, ...})). When switching back, repositioning from extreme negative coordinates back to (0,0) could cause the compositor to not resume rendering.

Fix: Changed switchworkspace to detach BrowserViews via contentView.removeChildView() instead of positionTabOffScreen. The reuse path's addChildView() gives Electron a clean attach cycle.

Additional Defensive Fixes

Change File Purpose
addChildView() in REUSE path emain/emain-window.ts Ensure BrowserView is attached to current window after window recreation
webContents.isDestroyed() check emain/emain-tabview.ts WaveTabView.isDestroyed is a custom property not reflecting Electron teardown
Stale-tab sweep in finalizePositioning emain/emain-window.ts Iterate wcvCache for any tab belonging to this window still at (0,0)
Remove init CSS class from body frontend/wave.ts HTML template has <body class="init"> which was never removed
Await reloadAllWorkspaceTabs frontend/wave.ts Previously fire-and-forget — reinit reported done before tab data arrived
WPS workspace re-subscribe on reinit frontend/wave.ts Clear old subscription, subscribe to new workspace on switch

…w one

Root cause: after creating a new workspace, switching back to an
existing workspace leaves the page blank. Two intertwined issues:

1. workspaceIdAtom was a Jotai derived atom depending on WOS cache
   updates — the async chain across multiple awaits in reinitWave
   could silently fail to trigger React re-render after a
   CreateWorkspace → SwitchWorkspace cycle. Changed to a writable
   PrimitiveAtom explicitly set via globalStore.set().

2. Electron WebContentsView rendering pipeline could break when
   repositioning views from off-screen (-15000,-15000) back to
   (0,0). Changed switchworkspace to detach BrowserViews via
   removeChildView instead of positionTabOffScreen, giving Electron
   a clean attach cycle on reuse.

Additional defensive fixes:
- REUSE path now calls contentView.addChildView() to guarantee
  BrowserView is attached to the current window
- getOrCreateWebViewForTab checks webContents.isDestroyed() to
  filter Electron-destroyed views (WaveTabView.isDestroyed is a
  separate custom property that doesn't reflect Electron teardown)
- Sweep wcvCache in finalizePositioning for stale on-screen tabs
- Remove 'init' CSS class from body in initWaveWrap finally
- Set activeTabIdAtom and staticTabId before reinitWave
- Make reloadAllWorkspaceTabs/loadAllWorkspaceTabs await all tab
  loads before considering initialization complete
- Re-subscribe to WPS workspace events on reinit
@lyx-tec
lyx-tec merged commit c6e2003 into main Jul 15, 2026
6 checks passed
@lyx-tec
lyx-tec deleted the fix/workspace-switch-blank branch July 15, 2026 11:37
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