import Cocoa // MARK: - Terminal Session class TerminalSession: NSObject, NSTextViewDelegate { var textView: NSTextView! var shellTask: Process? var inputPipe: Pipe? var outputPipe: Pipe? var promptPosition = 0 var sessionID: UUID var title: String private var outputBuffer: String = "" init(title: String = "Terminal") { self.sessionID = UUID() self.title = title super.init() } func setupTextView(frame: NSRect) { let scrollView = NSScrollView(frame: frame) scrollView.autoresizingMask = [.width, .height] scrollView.hasVerticalScroller = true textView = NSTextView(frame: scrollView.bounds) textView.autoresizingMask = [.width, .height] applyTheme() textView.isEditable = true textView.delegate = self textView.font = NSFont(name: TerminalSettings.shared.fontName, size: TerminalSettings.shared.fontSize) scrollView.documentView = textView } func applyTheme() { let theme = TerminalSettings.shared.theme textView?.backgroundColor = theme.background textView?.textColor = theme.foreground textView?.insertionPointColor = theme.cursor textView?.selectedTextAttributes = [ .backgroundColor: theme.selection ] } func startShell() { shellTask = Process() inputPipe = Pipe() outputPipe = Pipe() shellTask?.executableURL = URL(fileURLWithPath: "/bin/zsh") shellTask?.arguments = ["-i"] shellTask?.environment = [ "TERM": "xterm-256color", "PS1": "$ ", "PATH": ProcessInfo.processInfo.environment["PATH"] ?? "/usr/bin:/bin:/usr/sbin:/sbin", "HOME": ProcessInfo.processInfo.environment["HOME"] ?? "" ] shellTask?.standardInput = inputPipe shellTask?.standardOutput = outputPipe shellTask?.standardError = outputPipe outputPipe?.fileHandleForReading.readabilityHandler = { [weak self] handle in let data = handle.availableData if data.count > 0, let output = String(data: data, encoding: .utf8) { DispatchQueue.main.async { self?.appendText(output) } } } do { try shellTask?.run() appendText("Enhanced Terminal v2.0\n") appendText("Type 'help' for commands, Cmd+K for AI, Cmd+P for snippets\n\n") showPrompt() } catch { appendText("Error starting shell: \(error)\n") } } func showPrompt() { appendText("$ ") promptPosition = textView.string.count } func appendText(_ text: String) { let cleaned = cleanANSI(text) let theme = TerminalSettings.shared.theme let attr = NSAttributedString(string: cleaned, attributes: [ .foregroundColor: theme.foreground, .font: NSFont(name: TerminalSettings.shared.fontName, size: TerminalSettings.shared.fontSize) ?? NSFont.monospacedSystemFont(ofSize: TerminalSettings.shared.fontSize, weight: .regular) ]) textView.textStorage?.append(attr) textView.scrollToEndOfDocument(nil) } private func cleanANSI(_ text: String) -> String { return text .replacingOccurrences(of: "\u{1B}\\[[0-9;]*[a-zA-Z]", with: "", options: .regularExpression) .replacingOccurrences(of: "\u{1B}\\][0-9];[^\u{07}]*\u{07}", with: "", options: .regularExpression) .replacingOccurrences(of: "\\]633;[^\u{07}]*\u{07}", with: "", options: .regularExpression) .replacingOccurrences(of: "\\]697;[^\u{07}]*\u{07}", with: "", options: .regularExpression) .replacingOccurrences(of: "\\]1;[^\u{07}]*\u{07}", with: "", options: .regularExpression) .replacingOccurrences(of: "\\]2;[^\u{07}]*\u{07}", with: "", options: .regularExpression) .replacingOccurrences(of: "\\]7;[^\u{07}]*\\\\", with: "", options: .regularExpression) } func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementString: String?) -> Bool { if range.location < promptPosition { return false } if replacementString == "\n" { let command = String(textView.string.suffix(from: textView.string.index(textView.string.startIndex, offsetBy: promptPosition))) executeCommand(command) return false } return true } func executeCommand(_ command: String) { appendText("\n") let trimmed = command.trimmingCharacters(in: .whitespacesAndNewlines) if trimmed == "exit" { NSApp.terminate(nil) return } if trimmed == "clear" { textView.string = "" showPrompt() return } if trimmed == "help" { showHelp() showPrompt() return } if !trimmed.isEmpty { HistoryManager.shared.addCommand(trimmed) if let data = (trimmed + "\n").data(using: .utf8) { inputPipe?.fileHandleForWriting.write(data) } } DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in self?.showPrompt() } } private func showHelp() { appendText(""" Enhanced Terminal Commands: - Cmd+T: New tab - Cmd+W: Close tab - Cmd+D: Split pane vertically - Cmd+Shift+D: Split pane horizontally - Cmd+K: AI command generation - Cmd+P: Open snippets - Cmd+R: Search history - Cmd+,: Settings - clear: Clear screen - exit: Quit application """) } func cleanup() { shellTask?.terminate() outputPipe?.fileHandleForReading.readabilityHandler = nil } deinit { cleanup() } } // MARK: - Tab Manager class TabManager { var tabs: [TerminalSession] = [] var currentIndex: Int = 0 func addTab(title: String = "Terminal") -> TerminalSession { let session = TerminalSession(title: title) tabs.append(session) currentIndex = tabs.count - 1 return session } func removeTab(at index: Int) { guard index < tabs.count else { return } tabs[index].cleanup() tabs.remove(at: index) if currentIndex >= tabs.count { currentIndex = max(0, tabs.count - 1) } } func selectTab(at index: Int) { guard index < tabs.count else { return } currentIndex = index } func getCurrentSession() -> TerminalSession? { guard currentIndex < tabs.count else { return nil } return tabs[currentIndex] } }