feat(editor): implement multi-pane split layout#2416
Conversation
- added vertical pane creation too - vscode style keybinds - resizing of pane - few bug fixes
Greptile SummaryThis PR introduces a full multi-pane split-screen editor layout, allowing users to open multiple self-contained editor panes side-by-side, resize them via drag handles, drag tabs between panes, and navigate panes via keybindings and command palette. The implementation is large (~1500+ net lines) and touches the core editor lifecycle in
Confidence Score: 3/5The change is architecturally sound and addresses most previously-identified issues, but the double doc-sync listener on secondary panes and the async editor-creation window deserve review before merging. The implementation fixes the docSync timer race, file-loaded pane-awareness gap, and setTheme closure bug from prior review rounds. However, createPaneEditor dispatches getDocSyncListener() via appendConfig AND applyFileToEditor embeds it again in every new EditorState via exts.push, so each keystroke in a secondary pane fires the sync callback twice and emits editor-state-changed twice. Additionally, createPane renders the new pane into the DOM before createPaneEditor resolves, leaving a brief window where the visible pane has editor=null and manager.activeFile=null. src/lib/editorManager.js warrants the closest review, particularly createPaneEditor, createPane, switchFile, and the getDocSyncListener attachment sequence. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant Command as commands.js
participant EM as editorManager
participant Pane as New Pane
participant EF as EditorFile
User->>Command: split-pane-right
Command->>EM: splitPaneRight()
EM->>EM: canCreatePane(direction, sourcePane)
EM->>EM: createPaneShell() pushes to panes[]
EM->>EM: insertPaneIntoLayout(sourcePane, pane)
EM->>EM: updatePaneLayoutState() pane visible editor null
EM->>EM: animatePaneEntry(pane)
EM->>EM: await createPaneEditor(pane)
Note over EM,Pane: async gap pane in DOM pane.editor is null
EM->>Pane: new EditorView(state, parent)
EM->>Pane: appendConfig(getDocSyncListener())
EM->>EM: setupEditor(pane)
EM->>EM: updatePaneLayoutState() editor ready
EM->>EF: createUntitledPaneFile(pane)
EF->>EM: addFile(placeholderFile)
EF->>EF: makeActive() switchFile(id, pane)
EM->>EM: setActivePane(pane)
EM->>Pane: applyFileToEditor(file)
Note over Pane: exts.push(getDocSyncListener()) in new EditorState
User->>Pane: drag tab from Pane A
Pane->>EM: commitPaneTransfer() moveFileToPane(file, pane)
EM->>EF: file.makeActive() in target pane
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant Command as commands.js
participant EM as editorManager
participant Pane as New Pane
participant EF as EditorFile
User->>Command: split-pane-right
Command->>EM: splitPaneRight()
EM->>EM: canCreatePane(direction, sourcePane)
EM->>EM: createPaneShell() pushes to panes[]
EM->>EM: insertPaneIntoLayout(sourcePane, pane)
EM->>EM: updatePaneLayoutState() pane visible editor null
EM->>EM: animatePaneEntry(pane)
EM->>EM: await createPaneEditor(pane)
Note over EM,Pane: async gap pane in DOM pane.editor is null
EM->>Pane: new EditorView(state, parent)
EM->>Pane: appendConfig(getDocSyncListener())
EM->>EM: setupEditor(pane)
EM->>EM: updatePaneLayoutState() editor ready
EM->>EF: createUntitledPaneFile(pane)
EF->>EM: addFile(placeholderFile)
EF->>EF: makeActive() switchFile(id, pane)
EM->>EM: setActivePane(pane)
EM->>Pane: applyFileToEditor(file)
Note over Pane: exts.push(getDocSyncListener()) in new EditorState
User->>Pane: drag tab from Pane A
Pane->>EM: commitPaneTransfer() moveFileToPane(file, pane)
EM->>EF: file.makeActive() in target pane
Reviews (12): Last reviewed commit: "fixed bunch of issues introduced acciden..." | Re-trigger Greptile |
This PR introduces support for a Multi-Pane (Split-Screen) Editor Layout, allowing users to open multiple editor panes and view files/tabs side-by-side (similar to desktop code editors). Each pane acts as a self-contained editor workspace with its own tab bar, and tabs things. And it is not limited to editor tabs, it can work with any editor tabs such as terminal, custom, editor, media etc.
It adds few commands for this multi pane, which you can search in command palette with
panekeyword.And some keybinds: