fix(wework): prevent duplicate titlebar actions across tabs - #2379
Conversation
📝 WalkthroughWalkthroughWorkspace tabs now own their global portals. ChangesWorkspace-tab portal visibility
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant AppRoutes
participant WorkspaceTabPortalOwner
participant TitlebarActionsPortal
AppRoutes->>WorkspaceTabPortalOwner: Set active workspace tab owner
WorkspaceTabPortalOwner->>TitlebarActionsPortal: Associate portal content with tab
TitlebarActionsPortal->>TitlebarActionsPortal: Hide inactive tab content
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
wework/src/components/topnav/TitlebarActionsPortal.test.tsx (1)
1-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReset the shared portal-ownership singleton after the test.
setActiveWorkspaceTabPortalOwnermutates the module-level singleton inworkspaceTabPortalOwnership.ts. This test sets it to'second'and never resets it. AddafterEach(() => setActiveWorkspaceTabPortalOwner(null))so a future test added to this file does not inherit stale ownership state from this test.🧹 Proposed cleanup
import { setActiveWorkspaceTabPortalOwner } from './workspaceTabPortalOwnership' +import { afterEach } from 'vitest'describe('workspace tab titlebar portal ownership', () => { + afterEach(() => { + setActiveWorkspaceTabPortalOwner(null) + }) + test('hides stale portal content when React keeps an inactive Activity mounted', async () => {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/topnav/TitlebarActionsPortal.test.tsx` around lines 1 - 49, Add an afterEach cleanup in the workspace tab titlebar portal test suite that calls setActiveWorkspaceTabPortalOwner(null), and import afterEach from Vitest. This must reset the module-level ownership singleton after every test, including the existing PortalHarness test.wework/src/components/layout/DesktopWorkbenchLayout.test.tsx (1)
2094-2114: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCreate a shared Tauri-runtime mock helper for this test file.
DesktopWorkbenchLayout.test.tsxtoggleswindow.__TAURI_INTERNALS__directly, and an adjacent Tauri test also restores the previous value. Move this capability to a shared helper or local test utility to avoid duplicated set/restore logic.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/layout/DesktopWorkbenchLayout.test.tsx` around lines 2094 - 2114, Create a shared local test helper for temporarily overriding window.__TAURI_INTERNALS__ and restoring its previous value, then update the Tauri-related tests in DesktopWorkbenchLayout.test.tsx to use it. Remove the duplicated Object.defineProperty try/finally blocks while preserving each test’s existing runtime-mock behavior and cleanup.wework/src/components/topnav/TitlebarActionsPortal.tsx (1)
77-100: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winRead the active workspace tab owner through
useSyncExternalStore.
getActiveWorkspaceTabPortalOwner()is currently called directly inWorkspaceTabOwnedPortal, so theactivevalue can change between render attempts. Add a subscribe function inworkspaceTabPortalOwnership.tsthat notifies listeners whenactiveWorkspaceTabPortalOwneris updated, then calluseSyncExternalStore(subscribeToWorkspaceTabPortalOwner, getActiveWorkspaceTabPortalOwner, () => null)here. KeepsetActiveWorkspaceTabPortalOwneras the main DOM enforcement path; this only makes the render value consistent.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/topnav/TitlebarActionsPortal.tsx` around lines 77 - 100, Add a subscription mechanism in workspaceTabPortalOwnership.ts that notifies registered listeners whenever setActiveWorkspaceTabPortalOwner updates activeWorkspaceTabPortalOwner. In WorkspaceTabOwnedPortal, replace the direct getActiveWorkspaceTabPortalOwner() render read with useSyncExternalStore(subscribeToWorkspaceTabPortalOwner, getActiveWorkspaceTabPortalOwner, () => null), while preserving setActiveWorkspaceTabPortalOwner as the DOM enforcement path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@wework/src/components/layout/DesktopWorkbenchLayout.test.tsx`:
- Around line 2094-2114: Create a shared local test helper for temporarily
overriding window.__TAURI_INTERNALS__ and restoring its previous value, then
update the Tauri-related tests in DesktopWorkbenchLayout.test.tsx to use it.
Remove the duplicated Object.defineProperty try/finally blocks while preserving
each test’s existing runtime-mock behavior and cleanup.
In `@wework/src/components/topnav/TitlebarActionsPortal.test.tsx`:
- Around line 1-49: Add an afterEach cleanup in the workspace tab titlebar
portal test suite that calls setActiveWorkspaceTabPortalOwner(null), and import
afterEach from Vitest. This must reset the module-level ownership singleton
after every test, including the existing PortalHarness test.
In `@wework/src/components/topnav/TitlebarActionsPortal.tsx`:
- Around line 77-100: Add a subscription mechanism in
workspaceTabPortalOwnership.ts that notifies registered listeners whenever
setActiveWorkspaceTabPortalOwner updates activeWorkspaceTabPortalOwner. In
WorkspaceTabOwnedPortal, replace the direct getActiveWorkspaceTabPortalOwner()
render read with useSyncExternalStore(subscribeToWorkspaceTabPortalOwner,
getActiveWorkspaceTabPortalOwner, () => null), while preserving
setActiveWorkspaceTabPortalOwner as the DOM enforcement path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ac7f9736-d99b-4038-aec8-a00324e265a2
📒 Files selected for processing (9)
docs/en/wework/developer-guide/wework-chat-state-sources.mddocs/zh/wework/developer-guide/wework-chat-state-sources.mdwework/src/App.tsxwework/src/components/layout/DesktopWorkbenchLayout.test.tsxwework/src/components/layout/DesktopWorkbenchLayout.tsxwework/src/components/topnav/TitlebarActionsPortal.test.tsxwework/src/components/topnav/TitlebarActionsPortal.tsxwework/src/components/topnav/workspaceTabPortalOwnership.tswework/src/pages/WorkbenchPage.tsx
What changed
Why
Wework keeps document tabs mounted with React
Activityso drafts, terminals, and browser state survive tab switches. Updates inside a hidden Activity may be deferred, while portals already attached to global titlebar targets remain visible. After visiting multiple task tabs, stale titlebar actions from inactive tabs could therefore appear alongside the active tab's controls.Impact
Switching between multiple task tabs now preserves each workbench instance while exposing only the active tab's main header, panel controls, right-workspace titlebar content, and feedback action.
Validation
pnpm --filter wework typecheckSummary by CodeRabbit
Bug Fixes
Tests
Documentation