Skip to content

fix(wework): prevent duplicate titlebar actions across tabs - #2379

Merged
qdaxb merged 2 commits into
mainfrom
fix/wework-multi-tab-titlebar
Aug 3, 2026
Merged

fix(wework): prevent duplicate titlebar actions across tabs#2379
qdaxb merged 2 commits into
mainfrom
fix/wework-multi-tab-titlebar

Conversation

@qdaxb

@qdaxb qdaxb commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What changed

  • associate global Wework titlebar portals with their owning workspace document tab
  • synchronize portal visibility from the active tab boundary so hidden React Activities cannot leave stale controls visible
  • propagate route activity into the desktop workbench as an additional guard
  • add regression coverage for inactive tabs and deferred Activity updates
  • document the portal ownership rule in the Chinese and English developer guides

Why

Wework keeps document tabs mounted with React Activity so 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 typecheck
  • focused Vitest suite: 3 files, 162 tests
  • Prettier and ESLint on all changed Wework files
  • pre-push Wework ESLint, TypeScript, and full unit test checks
  • isolated real Tauri verification with two persistent task tabs; after switching between both tabs, exactly one visible main-header action group, panel-action group, and feedback button remained

Summary by CodeRabbit

  • Bug Fixes

    • Fixed stale titlebar and workspace controls appearing when switching between workbench tabs.
    • Ensured only the active tab displays its header, panel actions, workspace title, feedback entry, and related content.
    • Improved visibility handling for inactive tabs during transitions.
  • Tests

    • Added regression coverage for tab switching and portal visibility behavior.
  • Documentation

    • Added English and Chinese guidance for managing global UI elements across multiple workbench tabs.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Workspace tabs now own their global portals. AppRoutes synchronizes the active owner. Inactive titlebar content and workbench headers are hidden. Tests and developer documentation cover multi-tab behavior.

Changes

Workspace-tab portal visibility

Layer / File(s) Summary
Portal ownership and visibility
wework/src/components/topnav/TitlebarActionsPortal.tsx, wework/src/components/topnav/workspaceTabPortalOwnership.ts, wework/src/components/topnav/TitlebarActionsPortal.test.tsx
Portal wrappers associate titlebar content with workspace tabs and hide content owned by inactive tabs. Tests cover owner switching.
Active owner integration
wework/src/App.tsx
Each workspace tab is wrapped with WorkspaceTabPortalOwner. A layout effect updates the active portal owner.
Workbench visibility and validation
wework/src/components/layout/DesktopWorkbenchLayout.tsx, wework/src/pages/WorkbenchPage.tsx, wework/src/components/layout/DesktopWorkbenchLayout.test.tsx, docs/en/wework/developer-guide/wework-chat-state-sources.md, docs/zh/wework/developer-guide/wework-chat-state-sources.md
routeActive controls desktop workbench visibility. Tests and English and Chinese documentation cover inactive-tab portal behavior.

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
Loading

Possibly related PRs

Suggested reviewers: micro66, sdadunderscoresdad

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing duplicate titlebar actions across workspace tabs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/wework-multi-tab-titlebar

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qdaxb
qdaxb marked this pull request as ready for review August 3, 2026 06:22
@qdaxb
qdaxb enabled auto-merge August 3, 2026 06:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
wework/src/components/topnav/TitlebarActionsPortal.test.tsx (1)

1-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reset the shared portal-ownership singleton after the test.

setActiveWorkspaceTabPortalOwner mutates the module-level singleton in workspaceTabPortalOwnership.ts. This test sets it to 'second' and never resets it. Add afterEach(() => 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 value

Create a shared Tauri-runtime mock helper for this test file.

DesktopWorkbenchLayout.test.tsx toggles window.__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 win

Read the active workspace tab owner through useSyncExternalStore.

getActiveWorkspaceTabPortalOwner() is currently called directly in WorkspaceTabOwnedPortal, so the active value can change between render attempts. Add a subscribe function in workspaceTabPortalOwnership.ts that notifies listeners when activeWorkspaceTabPortalOwner is updated, then call useSyncExternalStore(subscribeToWorkspaceTabPortalOwner, getActiveWorkspaceTabPortalOwner, () => null) here. Keep setActiveWorkspaceTabPortalOwner as 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

📥 Commits

Reviewing files that changed from the base of the PR and between c560fc5 and a97c07e.

📒 Files selected for processing (9)
  • docs/en/wework/developer-guide/wework-chat-state-sources.md
  • docs/zh/wework/developer-guide/wework-chat-state-sources.md
  • wework/src/App.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.tsx
  • wework/src/components/topnav/TitlebarActionsPortal.test.tsx
  • wework/src/components/topnav/TitlebarActionsPortal.tsx
  • wework/src/components/topnav/workspaceTabPortalOwnership.ts
  • wework/src/pages/WorkbenchPage.tsx

@qdaxb
qdaxb added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit eac02cc Aug 3, 2026
70 checks passed
@qdaxb
qdaxb deleted the fix/wework-multi-tab-titlebar branch August 3, 2026 06:53
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