Skip to content

Feat/new editor layout#221

Draft
Code-Victor wants to merge 27 commits intojamesmurdza:mainfrom
Code-Victor:feat/new-editor-layout
Draft

Feat/new editor layout#221
Code-Victor wants to merge 27 commits intojamesmurdza:mainfrom
Code-Victor:feat/new-editor-layout

Conversation

@Code-Victor
Copy link
Collaborator

@Code-Victor Code-Victor commented Feb 2, 2026

🚀 New Editor Layout with Dockview Integration

📹 Demo

image

Video Demo Link

📋 Overview

This PR introduces a complete redesign of the editor layout system using Dockview, a professional-grade layout management library that provides VS Code-like panel management, drag-and-drop functionality, and flexible workspace customization.

Key Achievements

Professional UI/UX - VS Code-quality panel management
Drag & Drop - Move panels between containers with state preservation
Terminal Persistence - XTerm instances survive panel moves
Split Views - Create complex layouts with splits and tabs
Auto-Hide Panels - Smart visibility management
Keyboard Shortcuts - Power user workflows
Type Safety - Full TypeScript coverage with proper types


🎯 Problem Statement

Before This PR

Our previous editor layout suffered from several limitations:

  1. Rigid Layout - Fixed panel positions, no customization
  2. No Drag & Drop - Couldn't rearrange workspace
  3. State Loss - Terminals lost state when panels closed
  4. Poor UX - No split views, limited multitasking
  5. Maintainability - Complex imperative DOM manipulation

Impact on Users

  • Developers couldn't create their preferred workspace layout
  • Terminal sessions were lost when navigating away
  • No side-by-side code + terminal workflows
  • Limited productivity for complex tasks

💡 Solution

Architecture

We implemented a three-tier layout system:

GridviewReact (Application Layout)
├─ Sidebar Panel (File Explorer)
├─ Main Dock Panel
│  └─ DockviewReact (Editor Workspace)
│     ├─ Editor Panels
│     ├─ Preview Panel
│     └─ Terminal Panels
├─ Terminal Dock Panel
│  └─ DockviewReact (Terminal Workspace)
│     └─ Terminal Panels Only
└─ Chat Panel (AI Assistant)

Core Components

1. Layout System (web/components/project/layout/)

Main Layout (index.tsx)

  • Orchestrates GridviewReact and DockviewReact instances
  • Manages container references (gridRef, dockRef, terminalRef)
  • Handles drag-and-drop events
  • Implements keyboard shortcuts

Panel Components

  • editor-panel.tsx - Monaco editor wrapper
  • terminal-panel.tsx - XTerm terminal wrapper
  • preview-panel.tsx - Live preview iframe
  • chat-panel.tsx - AI chat interface
  • sidebar-panel.tsx - File explorer

Tab Components (tab-components.tsx)

  • Custom tab appearance for each panel type
  • File icons for editor tabs
  • Unsaved state indicators (dot vs X)
  • Close button overrides for cleanup logic

Utilities (utils/)

  • index.ts - Drop handling, position calculation, layout defaults
  • shortcuts.tsx - Global keyboard shortcuts

2. Context Providers

ContainerContext (context/container-context.tsx)

interface ContainerContextType {
  gridRef: React.MutableRefObject<GridviewApi | undefined>
  dockRef: React.MutableRefObject<DockviewApi | undefined>
  terminalRef: React.MutableRefObject<DockviewApi | undefined>
}
  • Provides refs to all dockview containers
  • Enables cross-component panel management

ProjectContext (context/project-context.tsx)

  • Centralized project and user data
  • Reduces prop drilling
  • Type-safe data access

3. Enhanced Terminal Management

TerminalContext (Enhanced)

interface TerminalState {
  id: string
  terminal: Terminal | null  // XTerm instance
  isBusy: boolean            // Command execution state
}

Key Features:

  • Terminal instances persist outside React lifecycle
  • Command execution tracking with busy state
  • Grace period for prompt detection (550ms)
  • Explicit disposal pattern (only on close button)

Terminal Panel (terminal-panel.tsx)

  • Simplified lifecycle - no automatic cleanup
  • Removed onDidRemovePanel handler
  • Relies on explicit close via tab button

Terminal Component (terminals/terminal.tsx)

// Skip creation if instance exists
if (term) {
  console.log("Terminal already exists, skipping creation")
  return
}

// Reattach DOM element instead of recreating
if (terminalElement && terminalElement.parentElement !== terminalContainerRef.current) {
  terminalContainerRef.current.appendChild(terminalElement)
  setTimeout(() => fitAddonRef.current?.fit(), 10)
}

// Don't dispose on unmount - keep alive
return () => {
  console.log("EditorTerminal unmounting, keeping terminal alive")
}

🎨 Drag & Drop Implementation

Challenges Solved

  1. State Preservation - Terminal instances must survive moves
  2. Intent Detection - Distinguish split vs tab creation
  3. Empty Containers - Allow drops even when no panels exist
  4. Bidirectional Movement - Terminal ↔ Dock in both directions
  5. Auto-Hide Logic - Smart panel visibility management

Drop Event Flow

User drags terminal panel
        ↓
onDidDrop event fires
        ↓
getData() extracts panelId
        ↓
Validate terminal panel
        ↓
Calculate drop position:
  • left/right/top/bottom → split
  • center → add as tab
        ↓
Move panel to target container
        ↓
Check if source container empty
        ↓
Auto-hide if no panels remain

Implementation Details

Position Mapping

const DIRECTION_MAP = {
  left: 'left',
  right: 'right',
  top: 'above',     // Note: top → above
  bottom: 'below',   // Note: bottom → below
  center: undefined  // Center = add as tab
}

Utility Function (utils/index.ts)

export function handleTerminalDrop({
  event,
  sourceContainerRef,
  targetContainerRef,
}: HandleTerminalDropConfig): HandleTerminalDropResult {
  // 1. Extract panel info from event
  // 2. Validate terminal panel
  // 3. Check cross-container move
  // 4. Calculate drop position
  // 5. Move panel with state preservation
  // 6. Return result with message
}

Auto-Hide Logic (tab-components.tsx)

const closeActionOverride = () => {
  // Dispose terminal
  terminal.terminal.dispose()
  setTerminals(prev => prev.filter(t => t.id !== terminalId))
  api.close()

  // Auto-hide if last terminal
  if (terminalRef.current?.panels.length === 1) {
    gridRef.current?.getPanel("terminal")?.api.setVisible(false)
  }
}

⌨️ Keyboard Shortcuts

Shortcut Action Notes
Cmd/Ctrl + L Toggle Chat Panel AI assistant
`Ctrl + `` Toggle Terminal Panel Creates terminal if none exist
Cmd/Ctrl + B Toggle Sidebar File explorer
`Ctrl + Shift + `` New Terminal Always creates new terminal
Cmd/Ctrl + S Save File In Monaco editor
Esc Close AI Generate Widget When focused

Implementation: utils/shortcuts.tsx

  • Global keyboard shortcuts via window event listeners
  • Monaco editor shortcuts via monaco.addCommand()
  • Proper dependency arrays to prevent stale closures

🧪 Testing Considerations

Manual Testing Checklist

  • Create new file → opens in editor panel
  • Drag editor tab between groups
  • Drag terminal from terminal dock to main dock
  • Drag terminal back to terminal dock
  • Split terminal panel (edge drops)
  • Add terminal as tab (center drop)
  • Close last terminal → panel auto-hides
  • Run command → creates terminal in terminal dock
  • Stop command → closes terminal, auto-hides if last
  • Keyboard shortcuts work globally
  • Terminal state persists during moves
  • File icons display correctly
  • Unsaved indicator shows/hides
  • Close dialog shows for unsaved files
  • Preview panel works
  • Chat panel toggles
  • Sidebar toggles

Edge Cases Handled

  1. Empty Container Drops - Accept drags even when container is empty
  2. Non-Terminal Filters - Reject non-terminal panels from terminal dock
  3. Same Container Moves - Let Dockview handle natively
  4. Terminal Reattachment - Use appendChild() instead of terminal.open()
  5. Stale Closures - Proper dependency arrays in useEffect

📚 Documentation

New Documentation

  1. Dockview Mental Model (web/docs/DOCKVIEW_MENTAL_MODEL.md)
    • Comprehensive guide to our layout system
    • Architecture diagrams
    • Component types and lifecycle
    • Drag & drop implementation details
    • Best practices and common patterns
    • Debugging tips

Code Comments

  • JSDoc comments on all utility functions
  • Inline comments explaining complex logic
  • Type annotations for clarity
  • Examples in function documentation

🐛 Known Issues & Future Work

Known Limitations

  1. Monaco Widget Lifecycle - Generate/diff widgets use imperative creation to avoid React conflicts
  2. Maximum Terminals - Currently limited to 4 terminals (can be increased)
  3. Layout Persistence - Layout resets on page refresh (could save to localStorage)

Future Enhancements

  1. Layout Persistence

    • Save layout configuration to localStorage
    • Restore user's preferred layout on reload
  2. Panel Serialization

    • Save open files and terminal sessions
    • Restore full workspace state
  3. Custom Layouts

    • Preset layouts (e.g., "Debug", "Write", "Review")
    • User-defined layout templates
  4. Enhanced Terminal

    • Terminal tabs with custom names
    • Terminal splitting within terminal dock
    • Terminal search and scrollback
  5. Performance

    • Virtual scrolling for many panels
    • Lazy loading of panel content
    • Memory management for inactive terminals

🙏 Acknowledgments

  • Dockview Library - mathuo/dockview
  • XTerm.js - Terminal emulation
  • Monaco Editor - Code editing

🔗 Related Issues

  • Fixes #XXX - Rigid editor layout
  • Fixes #XXX - Terminal state loss
  • Fixes #XXX - No drag and drop support
  • Implements feature request #XXX - Split views

📝 Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Comments added for complex logic
  • Documentation created (Mental Model doc)
  • Manual testing completed
  • No console errors
  • TypeScript compilation successful
  • All merge conflicts resolved
  • Backwards compatibility maintained

🎬 Next Steps

After merge:

  1. Monitor Performance - Watch for any performance issues in production
  2. Gather Feedback - User feedback on new layout system
  3. Iterate - Implement enhancements based on usage patterns
  4. Documentation - Update user-facing docs and tutorials
  5. Training - Create video tutorials for new features

Thank you for reviewing! Please test the drag & drop functionality and provide feedback on the UX. 🙌

- Removed old project layout components and replaced with a new Dock component structure.
- Introduced ChatPanel and integrated it within the Dock layout.
- Added new PreviewPanel and SidebarPanel components for enhanced file and GitHub synchronization.
- Implemented TerminalRightHeaderActions for terminal management.
- Created utility functions for handling terminal drops and managing global shortcuts.
- Updated global keyboard shortcuts for better user experience.
- Added watermark components for main and terminal views.
@vercel
Copy link

vercel bot commented Feb 2, 2026

@Code-Victor is attempting to deploy a commit to the jamesmurdza's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Code-Victor Code-Victor marked this pull request as draft February 2, 2026 16:09
@jamesmurdza
Copy link
Owner

Nice work! I found two bugs. The first happens sometimes when the page is loaded:

Screenshot 2026-02-02 at 8 31 21 PM

(It also happens when a message is sent in the AI chat.)

The second one happens sometime when a file is opened from the sidebar:

Screenshot 2026-02-02 at 8 45 06 PM

@Code-Victor
Copy link
Collaborator Author

Thank you @jamesmurdza for the feedback.

The first error is from the AI integration which hasn't been implemented yet. It should be done within today and tomorrow.

As for the second, I'll look into it and get back to you.

@Code-Victor
Copy link
Collaborator Author

@jamesmurdza the later issue should be fixed now

@jamesmurdza
Copy link
Owner

@Code-Victor How are we supposed to test this if we get the first error when loading the page?

Cannot read properties of undefined (reading 'then')
components/project/chat/components/generated-files-preview.tsx (246:10) @ GeneratedFilesPreview.useEffect

@Code-Victor
Copy link
Collaborator Author

@jamesmurdza I'm sorry about that. I'm actively working to resolve the issue. However, the error only appears in sandboxes that have an existing AI chat conversation (specifically those with AI edits). In the meantime, you can try creating a new sandbox or opening one without an active AI chat conversation. Thank you.

@jamesmurdza
Copy link
Owner

@jamesmurdza I'm sorry about that. I'm actively working to resolve the issue. However, the error only appears in sandboxes that have an existing AI chat conversation (specifically those with AI edits). In the meantime, you can try creating a new sandbox or opening one without an active AI chat conversation. Thank you.

Sounds good! Thanks for the explanation.

@jamesmurdza
Copy link
Owner

@Code-Victor @abdulrehmann231 Nice work, it's close! We still have some improvements to make. Have a look at this recording:

Untitled.-.01.mp4

@abdulrehmann231
Copy link
Collaborator

Hey @jamesmurdza , I will check this out and update you.
Thanks

@abdulrehmann231
Copy link
Collaborator

Collaborator

Hey @jamesmurdza , can you please retest this

@jamesmurdza
Copy link
Owner

@abdulrehmann231 I'm getting this:

Untitled.2.mov

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.

3 participants