refactor(workspace): split workspace.tsx into panels and hooks#131
Merged
Conversation
workspace.tsx had grown to ~1,728 lines and was the highest-friction file in the repo. Extract panel-switching JSX (header, graph, terminal, dialog wiring) into app/src/renderer/workspace/*.tsx, and data-fetching/watcher/ terminal-session logic into new hooks under app/src/renderer/hooks/, leaving workspace.tsx as a ~600-line composition root. Also fixes the two no-unstable-nested-components lint warnings in WorktreeSidebar.tsx by hoisting its inline context-menu builders to module-level functions. Pure refactor, no behavior change — verified via full E2E suite plus a manual pnpm dev boot. Closes #117
Contributor
There was a problem hiding this comment.
Pull request overview
Refactors the renderer workspace route by decomposing the previously very large workspace.tsx into focused panel components and reusable hooks, aiming to reduce coupling and make workspace feature development easier while preserving existing behavior.
Changes:
- Extracted top-level UI sections into dedicated panels/components (header, graph tab, terminal tab, dialogs).
- Moved stateful workspace behaviors (worktree selection, terminals, watchers, UI persistence, recent workspaces, etc.) into hooks under
app/src/renderer/hooks/. - Refactored
WorktreeSidebarcontext/quick-action menus to module-level builders to address unstable nested component lint warnings.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| app/src/renderer/routes/workspace.tsx | Turns workspace.tsx into a composition root by wiring extracted panels + hooks. |
| app/src/renderer/workspace/WorktreeSidebar.tsx | Hoists menu-building logic to module scope and applies formatting/refactor cleanup. |
| app/src/renderer/workspace/WorkspaceHeader.tsx | New header component encapsulating titlebar chrome + workspace switcher. |
| app/src/renderer/workspace/GraphTabPanel.tsx | New graph tab wrapper that owns graph + diff panel wiring. |
| app/src/renderer/workspace/TerminalTabPanel.tsx | New terminal tab wrapper for session tabs, shell picker, and panes. |
| app/src/renderer/workspace/WorkspaceDialogs.tsx | New component bundling workspace modal dialogs into one place. |
| app/src/renderer/hooks/useWorktreeSelection.ts | New hook encapsulating initial selection + switch/delete/create-hook flows. |
| app/src/renderer/hooks/useTerminalManager.tsx | New hook encapsulating terminal lifecycle, buffers, and context menu actions. |
| app/src/renderer/hooks/useWorkspaceFileWatchers.ts | New hook consolidating watchers/subscriptions and workspace-switch cleanup. |
| app/src/renderer/hooks/useWorkspaceUiPersistence.ts | New hook for sessionStorage mirroring and sidebar shortcut wiring. |
| app/src/renderer/hooks/useRemoteOps.ts | New hook for fetch/pull/push actions and in-flight state mirroring. |
| app/src/renderer/hooks/useRecentWorkspaces.ts | New hook for recent workspace list loading + navigation switching. |
| app/src/renderer/hooks/useCommitDiffState.ts | New hook for commit/range diff selection state used by the graph tab. |
| app/src/renderer/hooks/useScaffoldKickoff.ts | New hook for “new from idea” scaffold kickoff behavior. |
| app/src/renderer/hooks/useAgentConfig.ts | New hook for loading the AI agent configuration. |
| app/src/renderer/hooks/useAvailableShells.ts | New hook for loading shells used by the terminal shell picker. |
| app/src/renderer/hooks/useAutoUpdateListeners.ts | New hook wiring updater IPC events into the update store. |
| app/src/renderer/hooks/useEditorTabsForWorktree.ts | New hook deriving editor tabs scoped to the active worktree. |
| app/src/renderer/hooks/useFileEditorActions.ts | New hook for open/save/reload editor tab operations. |
- WorktreeSidebar: reuse the shared toast-context ToastFn type instead of a local duplicate that could drift from ToastData['variant']. - Export TerminalLayout from workspace-store and use it instead of a bare string in useWorkspaceUiPersistence and TerminalTabPanel. - WorkspaceHeader: drop the recentWorkspaces prop — it was never read (the switcher always reloads fresh via loadRecentWorkspaces), matching behavior already present before the split.
# Conflicts: # app/src/renderer/routes/workspace.tsx # app/src/renderer/workspace/WorktreeSidebar.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type
Summary
workspace.tsxinto focused panel components (WorkspaceHeader,GraphTabPanel,TerminalTabPanel,WorkspaceDialogs) and data-fetching/subscription hooks underapp/src/renderer/hooks/(terminal session management, worktree selection, commit diff state, file watchers, UI persistence, etc.), leavingworkspace.tsxas a ~600-line composition root.no-unstable-nested-componentslint warnings inWorktreeSidebar.tsxby hoisting its inline context-menu builders (used by the row right-click menu and the "…" quick-actions menu) to module-level functions that take their dependencies as parameters.Why
workspace.tsxwas ~3x the next-largest renderer file and the highest-friction file in the repo — most feature work lands there. Splitting it into panels/hooks unblocks other renderer work (#117).Screenshots / recordings
N/A — pure refactor, no visual or behavioral change.
Breaking changes
None.
Test plan
pnpm lint,pnpm typecheck,pnpm testall pass.pnpm --filter app build && pnpm test:e2e— full E2E suite passes (14 spec files, all green), includingdaily-workflow,worktree-workflow,commit-workflow,file-editor-workflow,agent-workflow, andsidebar-collapse— the specs most likely to catch a regression from this move.pnpm devand confirmed the app launches cleanly with no renderer console errors.workspace.tsxinternals existed, so no other files needed updates beyond the two touched here.Closes #117