diff --git a/.DS_Store b/.DS_Store index 6c88134..22a716b 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/EnhancedTerminal.app/Contents/MacOS/EnhancedTerminal b/EnhancedTerminal.app/Contents/MacOS/EnhancedTerminal index 7eb7782..aa9c761 100755 Binary files a/EnhancedTerminal.app/Contents/MacOS/EnhancedTerminal and b/EnhancedTerminal.app/Contents/MacOS/EnhancedTerminal differ diff --git a/MainWindowController.swift b/MainWindowController.swift index 2e06778..04b9159 100644 --- a/MainWindowController.swift +++ b/MainWindowController.swift @@ -4,6 +4,9 @@ class MainWindowController: NSWindowController { var tabManager = TabManager() var tabBar: NSStackView! var contentView: NSView! + var splitView: NSSplitView? + var activePaneIndex: Int = 0 + var panes: [TerminalSession] = [] var historyPanel: HistorySearchPanel! var snippetPanel: SnippetPanel! var aiPanel: AIGeneratorPanel! @@ -153,6 +156,10 @@ class MainWindowController: NSWindowController { fileMenu.addItem(NSMenuItem(title: "New Tab", action: #selector(addNewTab), keyEquivalent: "t")) fileMenu.addItem(NSMenuItem(title: "Close Tab", action: #selector(closeCurrentTab), keyEquivalent: "w")) fileMenu.addItem(NSMenuItem.separator()) + fileMenu.addItem(NSMenuItem(title: "Split Pane Vertically", action: #selector(splitPaneVertically), keyEquivalent: "d")) + fileMenu.addItem(NSMenuItem(title: "Split Pane Horizontally", action: #selector(splitPaneHorizontally), keyEquivalent: "D")) + fileMenu.addItem(NSMenuItem(title: "Close Pane", action: #selector(closePane), keyEquivalent: "w")) + fileMenu.addItem(NSMenuItem.separator()) fileMenu.addItem(NSMenuItem(title: "Clear Screen", action: #selector(clearScreen), keyEquivalent: "k")) fileMenu.addItem(NSMenuItem(title: "Clear Scrollback", action: #selector(clearScrollback), keyEquivalent: "K")) @@ -500,6 +507,8 @@ class MainWindowController: NSWindowController { File: Cmd+T - New Tab Cmd+W - Close Tab + Cmd+D - Split Pane Vertically + Cmd+Shift+D - Split Pane Horizontally Cmd+K - Clear Screen Cmd+Shift+K - Clear Scrollback @@ -531,6 +540,97 @@ class MainWindowController: NSWindowController { alert.runModal() } + @objc func splitPaneVertically() { + guard let currentSession = tabManager.getCurrentSession() else { return } + + // Create split view if it doesn't exist + if splitView == nil { + splitView = NSSplitView(frame: contentView.bounds) + splitView?.isVertical = true + splitView?.dividerStyle = .thin + splitView?.autoresizingMask = [.width, .height] + + // Move current session to split view + currentSession.scrollView.removeFromSuperview() + splitView?.addArrangedSubview(currentSession.scrollView) + panes = [currentSession] + } + + // Create new pane + let newSession = TerminalSession(title: "Pane \(panes.count + 1)") + newSession.setupTextView(frame: CGRect(x: 0, y: 0, width: contentView.bounds.width / 2, height: contentView.bounds.height)) + newSession.startShell() + + splitView?.addArrangedSubview(newSession.scrollView) + panes.append(newSession) + activePaneIndex = panes.count - 1 + + // Add split view to content if not already there + if splitView?.superview == nil { + contentView.subviews.forEach { $0.removeFromSuperview() } + contentView.addSubview(splitView!) + } + + newSession.textView.window?.makeFirstResponder(newSession.textView) + } + + @objc func splitPaneHorizontally() { + guard let currentSession = tabManager.getCurrentSession() else { return } + + // Create split view if it doesn't exist + if splitView == nil { + splitView = NSSplitView(frame: contentView.bounds) + splitView?.isVertical = false + splitView?.dividerStyle = .thin + splitView?.autoresizingMask = [.width, .height] + + // Move current session to split view + currentSession.scrollView.removeFromSuperview() + splitView?.addArrangedSubview(currentSession.scrollView) + panes = [currentSession] + } + + // Create new pane + let newSession = TerminalSession(title: "Pane \(panes.count + 1)") + newSession.setupTextView(frame: CGRect(x: 0, y: 0, width: contentView.bounds.width, height: contentView.bounds.height / 2)) + newSession.startShell() + + splitView?.addArrangedSubview(newSession.scrollView) + panes.append(newSession) + activePaneIndex = panes.count - 1 + + // Add split view to content if not already there + if splitView?.superview == nil { + contentView.subviews.forEach { $0.removeFromSuperview() } + contentView.addSubview(splitView!) + } + + newSession.textView.window?.makeFirstResponder(newSession.textView) + } + + @objc func closePane() { + guard panes.count > 1, activePaneIndex < panes.count else { return } + + let pane = panes[activePaneIndex] + pane.cleanup() + pane.scrollView.removeFromSuperview() + panes.remove(at: activePaneIndex) + + if panes.count == 1 { + // Only one pane left, remove split view + splitView?.removeFromSuperview() + splitView = nil + + let remainingSession = panes[0] + remainingSession.scrollView.removeFromSuperview() + remainingSession.scrollView.frame = contentView.bounds + contentView.addSubview(remainingSession.scrollView) + panes = [] + } + + activePaneIndex = min(activePaneIndex, panes.count - 1) + } + @objc func clearHistory() { let alert = NSAlert() alert.messageText = "Clear Command History?" diff --git a/TerminalSession.swift b/TerminalSession.swift index c0633ed..f58fe57 100644 --- a/TerminalSession.swift +++ b/TerminalSession.swift @@ -402,11 +402,20 @@ class TerminalSession: NSObject, NSTextViewDelegate, NSMenuDelegate { let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines) - // Check for aliases + // Check for aliases - match the first word var actualCommand = trimmed - if let aliasCommand = TerminalSettings.shared.commandAliases[trimmed] { - actualCommand = aliasCommand - appendText("(alias: \(aliasCommand))\n") + let components = trimmed.split(separator: " ", maxSplits: 1) + if let firstWord = components.first { + let firstWordStr = String(firstWord) + if let aliasCommand = TerminalSettings.shared.commandAliases[firstWordStr] { + // Replace the alias with the actual command, keeping any arguments + if components.count > 1 { + actualCommand = aliasCommand + " " + String(components[1]) + } else { + actualCommand = aliasCommand + } + appendText("(alias: \(firstWordStr) → \(aliasCommand))\n") + } } // Track directory changes