[WIP] Symphony: Improved Terminal Experience (#160)#166
[WIP] Symphony: Improved Terminal Experience (#160)#166pedramamini wants to merge 8 commits intomainfrom
Conversation
…ulation Phase 1 of terminal emulation implementation (Issue #142): - Install xterm.js v6.0.0 and addon packages: - @xterm/addon-fit for auto-resize - @xterm/addon-webgl for GPU-accelerated rendering - @xterm/addon-web-links for clickable URLs - @xterm/addon-search for Cmd+F search - @xterm/addon-unicode11 for emoji/unicode support - Create XTerminal.tsx React component with: - Full xterm.js Terminal lifecycle management - WebGL rendering with canvas fallback - Theme mapping from Maestro ThemeColors to xterm ITheme - Debounced resize handling via ResizeObserver - IPC data handlers for PTY communication - Imperative handle for parent component control - 10,000 line scrollback buffer
Code Review: XTerminal Component (Phase 1)Thanks for this well-structured PR implementing the foundation for full terminal emulation! This is a solid Phase 1 that sets up xterm.js properly. Here's my detailed feedback: ✅ Strengths
🔍 Issues & Concerns1. IPC Handler Missing (Critical)The component calls
Recommendation: Add this to ipcMain.handle('process:resize', async (_, sessionId: string, cols: number, rows: number) => {
const session = activeSessions.get(sessionId);
if (session?.pty) {
session.pty.resize(cols, rows);
}
});2. Session ID Format ConfusionThe prop type says
Recommendation: Clarify in CLAUDE.md or code comments which format is actually expected. If the terminal suffix is needed, update the comparison logic. 3. Memory Leak Risk: ResizeObserverThe ResizeObserver is created with Fix: // Remove handleResize from dependency array
useEffect(() => {
if (!containerRef.current) return;
const resizeObserver = new ResizeObserver(() => {
// Inline the resize logic or use a stable ref
if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
resizeTimeoutRef.current = setTimeout(() => {
if (fitAddonRef.current && terminalRef.current) {
fitAddonRef.current.fit();
const { cols, rows } = terminalRef.current;
onResize?.(cols, rows);
window.maestro.process.resize(sessionId, cols, rows);
}
}, 100);
});
resizeObserver.observe(containerRef.current);
return () => resizeObserver.disconnect();
}, []); // Empty deps - only create once4. Timeout Cleanup MissingThe Fix: Add cleanup to the main initialization effect: return () => {
if (resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current);
titleDisposable.dispose();
webglAddon?.dispose();
term.dispose();
// ...
};5. Data Handler Subscription CleanupThe Recommendation: Verify that the 6. Unicode11 Activation
Fix: try {
const unicode11Addon = new Unicode11Addon();
term.loadAddon(unicode11Addon);
term.unicode.activeVersion = '11';
} catch (e) {
console.warn('[XTerminal] Unicode11 addon failed to load', e);
}🤔 Questions for Clarification
📋 Suggestions for Phase 2
🔒 Security Notes✅ No security concerns - the component properly sanitizes input through xterm.js's built-in handling and doesn't execute arbitrary commands directly. 📝 Code Quality
🧪 Testing RecommendationsBefore merging, test:
🎯 Overall AssessmentThis is a solid foundation for terminal emulation with xterm.js. The main concerns are:
Recommendation: Request changes to fix the memory leak and clarify the IPC integration, then approve. The code quality is high and the architecture is sound. Would love to see the integration plan for Phase 2! This PR sets up xterm.js well, but it needs to be wired into the existing session management system to be functional. |
…ities Phase 2 of terminal emulation implementation (Issue #142): - Add TerminalTab interface for multi-tab terminal support: - id, name, shellType, pid, cwd, createdAt - state tracking (idle/busy/exited) - scrollTop and searchQuery persistence - Add ClosedTerminalTab interface for undo functionality - Stores tab config with original position - Note: PTY session cannot be restored, only settings - Extend Session interface with terminal tab fields: - terminalTabs: TerminalTab[] (optional for migration) - activeTerminalTabId: string - closedTerminalTabHistory: ClosedTerminalTab[] - Mark legacy fields as deprecated: - shellLogs: will be removed after terminal tabs migration - terminalPid: replaced by terminalTabs[].pid - Create terminalTabHelpers.ts with 9 helper functions: - getActiveTerminalTab, createTerminalTab - getTerminalTabDisplayName, getTerminalSessionId - parseTerminalSessionId, hasRunningTerminalProcess - getActiveTerminalTabCount, createClosedTerminalTab - MAX_CLOSED_TERMINAL_TABS constant (10)
Pull Request Review: Improved Terminal Experience (#160)OverviewThis PR implements Phase 1 & 2 of the terminal emulation enhancement, introducing xterm.js foundation and multi-tab terminal support. The implementation is well-structured and follows established patterns from the AI tabs system. ✅ StrengthsCode Quality
Architecture
🔍 Issues & Recommendations1. Memory Leak Risk in XTerminal (Medium Priority)Location: The // Missing cleanup
const resizeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);Fix: Add cleanup in the main useEffect: return () => {
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
}
titleDisposable.dispose();
webglAddon?.dispose();
term.dispose();
// ...
};2. Missing Dependencies in useCallback (Medium Priority)Location: The const handleResize = useCallback(() => {
// ...
}, [sessionId, onResize]); // Add onResize hereThis violates React Hooks exhaustive-deps rule and could lead to calling stale 3. Empty Dependency Array Concern (Low Priority)Location: The initialization useEffect has an empty dependency array with this comment: }, []); // Only run on mount - theme/font updates handled separatelyWhile the theme/font are handled in separate effects, Recommendation: Consider if this is intentional. If so, the comment is good. Otherwise, add a comment explaining why initial values are locked in. 4. Type Safety: Missing Type Import (Low Priority)Location: The 5. Terminal Tab State Consistency (Low Priority)Location: When creating a tab: { ...tab, pid: 0, state: 'idle' },This is correct since PTY sessions can't be restored, but consider if 6. Regex Vulnerability (Very Low Risk)Location: The regex pattern Recommendation: Use a more specific pattern: const match = terminalSessionId.match(/^([^-]+(?:-[^-]+)*)-terminal-([^-]+)$/);However, given that session IDs are generated by 🎯 Performance ConsiderationsPositive
Areas to Monitor
🔒 Security AssessmentNo Critical Issues
🧪 Test Coverage RecommendationsThe PR doesn't include tests. Consider adding:
📋 Minor Suggestions
✨ ConclusionOverall Assessment: This is high-quality foundational work that follows the project's established patterns well. The issues identified are relatively minor and mostly preventative. Recommendation: ✅ Approve with minor changes Required Before Merge:
Nice to Have:
The terminal emulation foundation is solid and ready for Phase 3+ implementations. Great work maintaining consistency with existing patterns! 🎉 |
… and migration Phase 3 of full terminal emulation implementation: Session Creation: - Import createTerminalTab helper from terminalTabHelpers.ts - Initialize terminalTabs with default terminal in createSession() - Initialize terminalTabs in handleWizardLaunchSession() - Initialize terminalTabs in all 6 worktree session creation locations Session Restoration: - Add migration logic for sessions without terminalTabs - Reset terminal tab runtime state (pid, state, exitCode) on restore - Add closedTerminalTabHistory initialization Session Persistence: - Update prepareSessionForPersistence() to clean terminal tabs - Exclude closedTerminalTabHistory from persistence - Defensive handling for undefined terminalTabs All TypeScript compilation passes. Legacy shellLogs and terminalPid fields preserved for backwards compatibility. Ref: Issue #142
PR Review: Improved Terminal Experience (#160)This PR introduces multi-tab terminal support with xterm.js integration. Here are my findings: ✅ Strengths
|
…for xterm.js Phase 4 of terminal emulation implementation: - Add spawnTerminalTab() method to ProcessManager as convenience wrapper - Add process:spawnTerminalTab IPC handler with shell settings resolution - Add spawnTerminalTab API and types to preload.ts - Update PTY data handler to send RAW output for terminal mode (no filtering) - xterm.js handles all rendering including control sequences, colors, cursor - Verify existing kill/interrupt/resize/exit methods work with terminal tab session IDs
PR Review: Improved Terminal Experience (#160)OverviewThis PR introduces multi-tab terminal support with xterm.js for full PTY emulation. This is a significant architectural improvement that brings proper terminal emulation to Maestro. ✅ Strengths1. Clean Architecture
2. Type Safety
3. Backwards Compatibility
4. Resource Management
5. User Experience
|
…erminal UI - Create TerminalTabBar.tsx following existing TabBar.tsx patterns - Implement TerminalTabComponent with state-based icon coloring (green=exited 0, red=exited non-zero, yellow=busy) - Support drag-and-drop reordering, middle-click close, double-click rename - Add overflow handling with sticky "new tab" button - Browser-style tab appearance with rounded corners
PR Review - Terminal Emulation FoundationThis PR implements a solid foundation for xterm.js-based terminal emulation. The architecture is well-structured and follows Maestro's established patterns. Here's my detailed feedback: ✅ Strengths1. Excellent Architectural Consistency
2. xterm.js Integration
3. Process Management
4. UI/UX Considerations
🔍 Issues & Concerns1. CRITICAL: Memory Leak Risk in XTerminal.tsxLocation: The component has a dependency array issue that could cause memory leaks: // Line 192: Empty dependency array
useEffect(() => {
// ...terminal initialization
return () => {
titleDisposable.dispose();
webglAddon?.dispose();
term.dispose();
};
}, []); // Only run on mount - theme/font updates handled separately
// Lines 232-241: sessionId/onData dependencies
useEffect(() => {
if (!terminalRef.current) return;
const disposable = terminalRef.current.onData((data) => {
window.maestro.process.write(sessionId, data);
onData?.(data);
});
return () => disposable.dispose();
}, [sessionId, onData]);Problem: The
Fix: Parent component should memoize the callback: const handleTerminalData = useCallback((data: string) => {
// handle data
}, [/* dependencies */]);
<XTerminal onData={handleTerminalData} />Or XTerminal should use a ref pattern: const onDataRef = useRef(onData);
onDataRef.current = onData;
useEffect(() => {
// use onDataRef.current in the handler
}, [sessionId]); // onData no longer in deps2. CRITICAL: Process Manager RAW Output LogicLocation: // For terminal mode with xterm.js, send RAW data
if (isTerminal) {
logger.debug('[ProcessManager] PTY onData (raw terminal)', 'ProcessManager', {
sessionId,
pid: ptyProcess.pid,
dataLength: data.length,
});
this.emit('data', sessionId, data);
return;
}Problem: The Question: How is
Verify this code path - otherwise terminal tabs will get filtered output instead of raw output, breaking xterm.js rendering. 3. Race Condition: Terminal Tab MigrationLocation: // Migrate sessions without terminal tabs
let terminalTabs = correctedSession.terminalTabs;
let activeTerminalTabId = correctedSession.activeTerminalTabId;
if (!terminalTabs || terminalTabs.length === 0) {
const defaultTerminalTab = createTerminalTab(
defaultShell || 'zsh',
correctedSession.cwd,
null
);
terminalTabs = [defaultTerminalTab];
activeTerminalTabId = defaultTerminalTab.id;
}Potential Issue: Migration happens during session restore, but there's no check for Edge Case: What if:
Fix: Add validation: if (!terminalTabs || terminalTabs.length === 0) {
// ... migration
} else {
// Validate activeTerminalTabId points to existing tab
if (!terminalTabs.find(t => t.id === activeTerminalTabId)) {
activeTerminalTabId = terminalTabs[0].id;
}
// ... reset runtime state
}4. Type Safety: Missing ClosedTerminalTab DefinitionThe type 5. Session Persistence IssueLocation: Session interface has these fields: terminalTabs?: TerminalTab[];
activeTerminalTabId: string;
closedTerminalTabHistory: ClosedTerminalTab[];Question: Are Looking at the migration code, it seems yes. However:
But check 6. Debounced Persistence HookLocation: The diff shows changes to this file (additions: 19, deletions: 1). This likely adds terminal tab support, but without seeing the full diff, I can't verify if terminal tab state changes trigger persistence correctly. Verify: Changes to 🎯 Code Quality & Best Practices1. Good Patterns:
2. Potential Improvements: a) Magic Numbers // XTerminal.tsx:122
}, 100); // 100ms debounce
// XTerminal.tsx:137
scrollback: 10000, // 10k linesExtract to constants: const RESIZE_DEBOUNCE_MS = 100;
const TERMINAL_SCROLLBACK_LINES = 10000;b) Hard-coded Shell Detection // process-manager.ts:1416
command: shell || (process.platform === 'win32' ? 'powershell.exe' : 'zsh'),This should use settings/defaults consistently with the rest of the app. c) Missing Error Handling // XTerminal.tsx:223-229
const unsubscribe = window.maestro.process.onData((sid, data) => {
if (sid === sessionId && terminalRef.current) {
terminalRef.current.write(data);
}
});No error handling if `write()$ throws (e.g., terminal disposed but data arrives). 🔒 Security Considerations1. No Security Issues Detected
2. Consideration: ⚡ Performance Considerations1. WebGL Renderer 2. Resize Handling 3. Tab Rendering const displayName = useMemo(() => getTerminalTabDisplayName(tab, index), [tab.name, index]);📋 Test CoverageMissing Tests:
📚 DocumentationGood:
Missing:
Suggest adding to CLAUDE.md: ### 11. Terminal Tab Management
Sessions support multiple terminal tabs with full PTY emulation:
```typescript
// Each session has terminal tabs
session.terminalTabs: TerminalTab[]
session.activeTerminalTabId: string
// Each tab maintains its own PTY
interface TerminalTab {
id: string;
pid: number;
state: 'idle' | 'busy' | 'exited';
shellType: string;
}Both branches are identical! Should be: const shortcutKey = navigator.platform?.includes('Mac') ? 'Cmd+Shift+`' : 'Ctrl+Shift+`';2. Exit Code Display 🏗️ Architecture Concerns1. Dual Process Model
Question: With multi-tab terminals, does each terminal tab get its own PTY process? If so, the naming should be:
But this means potentially many PTY processes per session. Is there a limit on terminal tabs per session? 2. State Management
But verify 📝 Suggested Changes SummaryBefore Merge:
Nice to Have:
✨ Overall AssessmentQuality: ⭐⭐⭐⭐☆ (4/5) This is a solid foundation for terminal emulation. The architecture is sound, follows project conventions, and demonstrates good engineering practices. The main concerns are around edge cases and memory management rather than fundamental design issues. Recommendation: Approve with changes - Fix the critical issues (onData deps, isTerminal flag, migration validation) before merging. The "nice to have" items can be follow-up PRs. Excellent work on Phase 1-5! The phased approach with clear commit messages makes this easy to review. Looking forward to seeing the UI integration in Phase 6+. Reviewed with ❤️ by Claude Code |
…gration Create TerminalView component that combines TerminalTabBar with XTerminal for full multi-tab terminal emulation. Integrate into MainPanel.tsx to conditionally render when in terminal mode. Key changes: - New TerminalView.tsx with PTY lifecycle management - MainPanel now renders TerminalView in terminal mode - Added 11 callback props for terminal tab management - InputArea hidden in terminal mode (xterm handles input) - global.d.ts updated with spawnTerminalTab type
Pull Request Review: Terminal Emulation ImplementationThis is a comprehensive and well-architected implementation of full terminal emulation using xterm.js. The code quality is excellent overall, with clear separation of concerns and adherence to the existing codebase patterns. Below are my detailed findings: ✅ Strengths1. Excellent Architecture & Separation of Concerns
2. Strong Type Safety
3. Proper Resource Management
4. Good User Experience Patterns
5. Backwards Compatibility
6. Consistent with Codebase Patterns
🔍 Issues & ConcernsCritical Issues1. Missing Terminal Tab Callbacks in App.tsx
|
PR Review: Terminal Emulation Implementation (Phase 1-6)This PR implements a comprehensive terminal emulation system using xterm.js with multi-tab support. Overall, this is high-quality, well-architected work that follows the existing codebase patterns closely. ✅ Strengths1. Excellent Architecture & Consistency
2. Robust State Management
3. Smart PTY Management
4. Code Quality
|
Implement Phase 7 of terminal emulation: callback handlers for terminal tab operations in App.tsx. These handlers manage terminal tabs within sessions, mirroring the AI tab management patterns. Handlers added: - handleTerminalTabSelect: Switch active terminal tab - handleTerminalTabClose: Close tab with undo history support - handleTerminalNewTab: Create new terminal tab - handleTerminalTabRename: Rename terminal tab - handleTerminalTabReorder: Drag and drop reordering - handleTerminalTabStateChange: PTY state transitions (idle/busy/exited) - handleTerminalTabCwdChange: Track working directory changes - handleTerminalTabPidChange: Track PTY process ID after spawn - _handleReopenTerminalTab: Reopen closed tab (ready for Cmd+Shift+T) All handlers include null-safety for terminalTabs and closedTerminalTabHistory arrays. Handlers are passed to MainPanel which routes them to TerminalView component.
Code Review - PR #166: Improved Terminal ExperienceThis PR implements a significant enhancement to the terminal experience by integrating xterm.js for full terminal emulation with multi-tab support. Overall, this is a well-structured and high-quality implementation that follows the project's patterns. ✅ StrengthsArchitecture & Design
Implementation Quality
Process Management
|
…gement Adds comprehensive keyboard shortcuts for terminal mode: - Ctrl+Shift+`: New terminal tab - Cmd+W: Close terminal tab (when more than one) - Cmd+Shift+T: Reopen closed terminal tab - Cmd+Shift+[/]: Navigate between terminal tabs - Cmd+1-9: Jump to specific terminal tab - Cmd+0: Jump to last terminal tab - Cmd+K: Clear terminal buffer - Cmd+F: Open terminal search - Ctrl+C: Pass through to PTY (not intercepted) Implementation details: - Added TERMINAL_TAB_SHORTCUTS to shortcuts.ts - Added terminal shortcuts handling in useMainKeyboardHandler.ts - Added TerminalViewHandle interface with clear/search methods - Updated TerminalView with forwardRef and useImperativeHandle - Added terminalViewRef and terminal search state to App.tsx - Passed terminalViewRef through MainPanel to TerminalView
PR Review: Full Terminal Emulation with xterm.jsOverviewThis is a substantial and well-structured PR implementing multi-tab terminal emulation using xterm.js. The implementation follows Maestro's established patterns closely and demonstrates good architectural discipline across 8 phases. ✅ StrengthsArchitecture & Design
Code Quality
User Experience
🔍 Issues & Concerns1. PTY Data Handling Logic Needs Review
|
Maestro Symphony Contribution
Working on #160 via Maestro Symphony.
Status: In Progress
Started: 2026-01-09T13:22:23.820Z
This PR will be updated automatically when the Auto Run completes.