Skip to content

🤖 feat: RightSidebar tiling & terminals - #1340

Merged
ammario merged 53 commits into
mainfrom
window-layout-614q
Jan 8, 2026
Merged

ammario merged 53 commits into
mainfrom
window-layout-614q

Conversation

@ammar-agent

@ammar-agent ammar-agent commented Dec 27, 2025 •

Copy link
Copy Markdown
Collaborator

Summary

Implements IDE-like "dock-lite" functionality for the RightSidebar with persistent nested splits, tabsets, drag-and-drop tab moving (including reordering), and stable embedded Terminal behavior.

Key Changes

Layout & Persistence

  • Per-workspace layout persistence via getRightSidebarLayoutKey(workspaceId)
  • Unified sidebar width across all tabs (single RIGHT_SIDEBAR_WIDTH_KEY)
  • Terminal tabs are no longer default - users add them via the "+" button

Drag & Drop

  • SidebarDragLayer component for consistent drag preview across panes
  • DragAwarePanelResizeHandle disables pointer events during tab drags
  • Edge drop zones always rendered (visibility toggled) to avoid mount/unmount issues

Terminal Integration

  • TerminalSessionRouter singleton centralizes terminal streaming/subscriptions
  • Removed distracting blue focus border from embedded terminal
  • Ghost tab reconciliation via terminal.listSessions on mount

Test Infrastructure

  • dragElement helper using programmatic DragEvent dispatch (works in Xvfb)
  • E2E tests: tab reordering, interactions, split layout rendering
  • UI tests refactored to assert visible state instead of localStorage internals

Testing

# Unit tests
bun test src/browser/utils/rightSidebarLayout.test.ts
bun test src/node/services/terminalService.test.ts

# UI integration tests  
TEST_INTEGRATION=1 bun x jest tests/ui/rightSidebar.integration.test.ts

# E2E tests
MUX_E2E_LOAD_DIST=1 xvfb-run -a bun x playwright test tests/e2e/scenarios/sidebarDragDrop.spec.ts
MUX_E2E_LOAD_DIST=1 xvfb-run -a bun x playwright test tests/e2e/scenarios/terminal.spec.ts

Generated with mux • Model: anthropic:claude-opus-4-5 • Thinking: high

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/utils/rightSidebarLayout.ts Outdated
@ammar-agent
ammar-agent force-pushed the window-layout-614q branch 3 times, most recently from ff4881d to 0fd9247 Compare December 27, 2025 18:51
@ammar-agent

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/components/RightSidebar.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/components/RightSidebar.tsx
@ammar-agent

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/components/TerminalView.tsx
@ammar-agent

Copy link
Copy Markdown
Collaborator Author

@codex review

1 similar comment
@ammar-agent

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/browser/components/RightSidebar/TerminalTab.tsx Outdated
@ammar-agent

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ammar-agent
ammar-agent force-pushed the window-layout-614q branch 2 times, most recently from 65f6c0e to fd4ea21 Compare December 28, 2025 18:45
@ammar-agent
ammar-agent force-pushed the window-layout-614q branch 6 times, most recently from 0e4391d to 5ea4d1e Compare January 7, 2026 16:46
@ammario ammario changed the title 🤖 feat: RightSidebar dock-lite with drag-and-drop and e2e tests 🤖 feat: RightSidebar tiling & terminals Jan 7, 2026
@ammar-agent
ammar-agent force-pushed the window-layout-614q branch 2 times, most recently from 6bed7a8 to bc1aa89 Compare January 7, 2026 23:02
Implements an IDE-like dock-lite sidebar with nested split/tabset layout:

- Terminal tab embedded in RightSidebar (stable, not destroyed on tab switch)
- Tab dragging with @dnd-kit for reliable click vs drag disambiguation
- Edge docking to create horizontal/vertical splits
- Unified sidebar width across all tabs (resizing affects all equally)
- Layout state persisted in localStorage

Technical highlights:
- Migrated from react-dnd HTML5Backend to @dnd-kit with PointerSensor
- 8px distance activation constraint prevents accidental drags
- DragOverlay for smooth cursor-following preview
- No tooltip interference (not built on HTML5 Drag API)
- Proper MUX_ROOT support for e2e tests

---
_Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
When switching between workspaces (e.g., SSH → Local), the TerminalTab
was passing the old workspace's session ID to TerminalView before the
useEffect synced the state. This caused errors like 'trying to open SSH
terminal for local project'.

Fix: Read sessionId directly from the Map instead of caching in useState.
The Map lookup always returns the correct session for the current
workspaceId, eliminating the timing bug.

---
_Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
Show 'Connecting...' overlay until we receive the initial screenState from
the backend. This prevents the flash of stale/empty terminal content that
was visible when switching between terminal tabs.

- Add isLoading state, reset to true on sessionId change
- Set isLoading=false when onScreenState callback fires
- Render loading overlay with visibility:hidden on terminal container
- Keep terminal mounted but hidden during loading for instant reveal
The attach stream already yields screenState as its first message, making
the standalone getScreenState endpoint redundant. The internal
terminalService.getScreenState() method is kept since attach uses it.

- Remove schema definition from api.ts
- Remove router endpoint from router.ts
- Remove test for the standalone endpoint
- Update stale comment in terminalService.ts
- rightSidebarLayout.test.ts: Update tests to match new default tabs
  (costs, review) - terminal is no longer in default layout
- terminalService.test.ts: Fix openWindow expectation to include
  undefined sessionId arg, simplify session creation test
Terminal is no longer a default tab - tests must click the '+' button
to add one before testing terminal functionality.

- terminal.spec.ts: Use ui.metaSidebar.addTerminal() instead of selectTab
- sidebarDragDrop.spec.ts: Add terminal before tests that use it
- ui.ts: Add addTerminal() helper method
- Terminal tabs now use 'Close terminal' button as selector (name is dynamic)
- Use per-workspace layout key (right-sidebar:layout:{workspaceId})
- Skip drag reorder tests on Linux/Xvfb (flaky with programmatic DnD)
- addTerminal() helper uses robust locator instead of name regex
- Add CLOSE_TAB keybind (Ctrl/Cmd+W)
- Only terminal tabs are closeable (other tabs ignore the keybind)
- Add to KeybindsSection settings UI
- Move + (New terminal) button to be left-aligned next to tabs
- Add keybind help (⌘W on Mac, Ctrl+W elsewhere) to close button tooltip
- Set CLOSE_TAB keybind to require Cmd on Mac (matches Ghostty behavior)
Wrap term.write() and term.clear() calls in try-catch to handle
intermittent 'memory access out of bounds' errors from xterm's WASM.
These errors can occur during normal operation and should not break
the terminal or leave it stuck in 'Connecting' state.
The terminal (ghostty-web) captures keyboard events before they reach
the window listener. Using capture phase intercepts the event first,
allowing Cmd/Ctrl+W to close the active terminal tab even when the
terminal has focus.
Replace the popout terminal behavior with integrated terminal:
- Ctrl/Cmd+T now adds a new terminal tab to the right sidebar
- Terminal button in workspace header also uses integrated terminal
- Command palette 'New Terminal Window' still opens popout for flexibility
- 'Open Terminal Window for Workspace...' opens popout for other workspaces

Implementation:
- RightSidebar exposes handleAddTerminal via addTerminalRef prop
- AIView passes ref to both RightSidebar and WorkspaceHeader
- handleAddTerminal auto-expands sidebar if collapsed
- Use capture phase for useAIViewKeybinds so Ctrl/Cmd+T works when terminal focused
- Add tab shrinking (min-w-0, max-w-[120px], truncate) for overflow
- Add horizontal scroll to tab strip when tabs exceed container width
- Keep + button fixed with shrink-0
@ammar-agent
ammar-agent force-pushed the window-layout-614q branch 2 times, most recently from f694c81 to d8fbbe3 Compare January 8, 2026 19:29
- When closing active tab, focus next tab (or previous if no next)
- Persist terminal titles to localStorage for reload survival
- Clean up persisted titles when closing/popping-out terminals
@ammario
ammario merged commit 429ed58 into main Jan 8, 2026
21 checks passed
@ammario
ammario deleted the window-layout-614q branch January 8, 2026 20:30
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Sep 25, 2026
…orts (coder#4472)

## Summary

Deletes browser production code that only tests still called, and moves
the tests that guarded live behavior onto the live code path. Part of
the test-audit follow-up campaign (frontend area, WS7).

## What was removed

| Removed | Why it was dead | What happened to its tests |
| --- | --- | --- |
| `StatsTab` component + its test-only `_snapshot`/`_clearStats` props
and `useStatsData` overrides | No production caller since coder#2729 moved
the Stats tab to `StatsContainer` (`TimingPanel` +
`ModelBreakdownPanel`) | `StatsTab.clear.test.tsx` replaced by
`TimingPanel.clear.test.tsx`, which drives the live path: real
`WorkspaceStore` stats subscription + `APIProvider` client whose
`workspace.stats.clear` rejects |
| `compareRecords`, `compareArrays` (`useStableReference.ts`) | Only
`compareMaps` is used (`App.tsx`) | 12 comparator tests deleted; stale
"tested manually via useUnreadTracking…" comments fixed |
| `closeSplit` (`rightSidebarLayout.ts`) | Never called in production
since coder#1340 | Test deleted |
| `isActionableTaskExecutionStatus` | Last caller removed in coder#4341 | No
tests |
| `shouldRefreshInlineSkillSuggestions` | coder#4013 made inline suggestions
synchronously derived, so there is no refresh gate | Tests deleted |
| `getAnthropicThinkingDisableReason` + `hasAnthropicThinkingSignature`
| coder#1450 stopped disabling Anthropic thinking | Tests deleted; the one
transform test that used it as an oracle now asserts the preserved
signature directly |
| `useProjectGitStatuses` (`GitStatusStore.ts`) | No production caller |
None in CI |
| `buildReviewDiffPathFilter`, `normalizeReviewPanelAssistedHunks`
(test-only wrappers in `ReviewPanel.tsx`) | Production calls
`buildReviewDiffPathFilterSpecs` / `getReviewPanelPathContext` +
`normalizeAssistedReviewHunks` directly | Tests retargeted to those
production functions (`getReviewPanelPathContext` is now exported) |
| `computeTaskAwaitPollGroupInfos` (test-only wrapper) | ChatPane calls
`computeOperationalBundleInfos({ taskAwaitPollsOnly })` | Tests use a
local fixture helper around the production function |
| 2 `/fork` cases in `parser.test.ts` | Same contract and name as
`fork.test.ts` | Deleted |

Retained on purpose (test reset seams, not dead code): `__resetForTests`
(highlight worker), `resetAiSelectionIntentForTests`,
`overlayWorkspaceStoreRaw` (test-support module).

## Validation

- Sweep: every `export` under `src/browser` (excluding stories) checked
for non-test references across `src/`, `tests/`, `vscode/`, `scripts/`,
`.storybook/`; orphan-module sweep found only the two HTML entry points.
- `TimingPanel.clear.test.tsx` fails when `handleClearStats` stops
setting the error (mutation check).
- `bun test` on RightSidebar, hooks/useStableReference,
utils/{messages,agentSkills,ui,rightSidebarLayout},
stores/GitStatusStore: 1141 pass.
- `make static-check` green.

LOC: production +11 / −585; tests +134 / −355.

---

_Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking:
`high` • Cost: `$4.21`_

<!-- mux-attribution: model=anthropic:claude-opus-5-5 thinking=high
costs=4.21 -->
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.

2 participants