256 lines
9.8 KiB
Swift
256 lines
9.8 KiB
Swift
import Cocoa
|
|
|
|
class MainWindowController: NSWindowController {
|
|
var tabManager = TabManager()
|
|
var tabBar: NSStackView!
|
|
var contentView: NSView!
|
|
var currentSessionView: NSScrollView?
|
|
|
|
convenience init() {
|
|
let window = NSWindow(
|
|
contentRect: NSRect(x: 0, y: 0, width: 1000, height: 700),
|
|
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
|
backing: .buffered,
|
|
defer: false
|
|
)
|
|
window.titlebarAppearsTransparent = true
|
|
window.titleVisibility = .hidden
|
|
window.center()
|
|
window.title = "Enhanced Terminal"
|
|
window.isReleasedWhenClosed = false
|
|
|
|
self.init(window: window)
|
|
setupUI()
|
|
addNewTab()
|
|
|
|
// Force window to show
|
|
window.makeKeyAndOrderFront(nil)
|
|
window.orderFrontRegardless()
|
|
}
|
|
|
|
private func setupUI() {
|
|
guard let window = window else { return }
|
|
|
|
let mainContainer = NSView(frame: window.contentView!.bounds)
|
|
mainContainer.autoresizingMask = [.width, .height]
|
|
|
|
// Tab bar
|
|
tabBar = NSStackView(frame: NSRect(x: 0, y: mainContainer.bounds.height - 40, width: mainContainer.bounds.width, height: 40))
|
|
tabBar.orientation = .horizontal
|
|
tabBar.distribution = .fillProportionally
|
|
tabBar.spacing = 0
|
|
tabBar.autoresizingMask = [.width, .minYMargin]
|
|
tabBar.wantsLayer = true
|
|
tabBar.layer?.backgroundColor = NSColor(white: 0.15, alpha: 1.0).cgColor
|
|
|
|
// Content view
|
|
contentView = NSView(frame: NSRect(x: 0, y: 0, width: mainContainer.bounds.width, height: mainContainer.bounds.height - 40))
|
|
contentView.autoresizingMask = [.width, .height]
|
|
|
|
mainContainer.addSubview(contentView)
|
|
mainContainer.addSubview(tabBar)
|
|
|
|
window.contentView = mainContainer
|
|
|
|
setupMenuBar()
|
|
}
|
|
|
|
private func setupMenuBar() {
|
|
let mainMenu = NSMenu()
|
|
|
|
// App menu
|
|
let appMenuItem = NSMenuItem()
|
|
mainMenu.addItem(appMenuItem)
|
|
let appMenu = NSMenu()
|
|
appMenuItem.submenu = appMenu
|
|
|
|
appMenu.addItem(NSMenuItem(title: "Settings...", action: #selector(openSettings), keyEquivalent: ","))
|
|
appMenu.addItem(NSMenuItem.separator())
|
|
appMenu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
|
|
|
|
// File menu
|
|
let fileMenuItem = NSMenuItem(title: "File", action: nil, keyEquivalent: "")
|
|
mainMenu.addItem(fileMenuItem)
|
|
let fileMenu = NSMenu(title: "File")
|
|
fileMenuItem.submenu = fileMenu
|
|
|
|
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 Vertically", action: #selector(splitVertically), keyEquivalent: "d"))
|
|
|
|
let splitHorizontal = NSMenuItem(title: "Split Horizontally", action: #selector(splitHorizontally), keyEquivalent: "d")
|
|
splitHorizontal.keyEquivalentModifierMask = [.command, .shift]
|
|
fileMenu.addItem(splitHorizontal)
|
|
|
|
// Edit menu
|
|
let editMenuItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
|
|
mainMenu.addItem(editMenuItem)
|
|
let editMenu = NSMenu(title: "Edit")
|
|
editMenuItem.submenu = editMenu
|
|
|
|
editMenu.addItem(NSMenuItem(title: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c"))
|
|
editMenu.addItem(NSMenuItem(title: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v"))
|
|
editMenu.addItem(NSMenuItem(title: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a"))
|
|
editMenu.addItem(NSMenuItem.separator())
|
|
editMenu.addItem(NSMenuItem(title: "Search History", action: #selector(searchHistory), keyEquivalent: "r"))
|
|
|
|
// View menu
|
|
let viewMenuItem = NSMenuItem(title: "View", action: nil, keyEquivalent: "")
|
|
mainMenu.addItem(viewMenuItem)
|
|
let viewMenu = NSMenu(title: "View")
|
|
viewMenuItem.submenu = viewMenu
|
|
|
|
viewMenu.addItem(NSMenuItem(title: "Toggle Fullscreen", action: #selector(NSWindow.toggleFullScreen(_:)), keyEquivalent: "f"))
|
|
viewMenu.addItem(NSMenuItem(title: "Increase Font Size", action: #selector(increaseFontSize), keyEquivalent: "+"))
|
|
viewMenu.addItem(NSMenuItem(title: "Decrease Font Size", action: #selector(decreaseFontSize), keyEquivalent: "-"))
|
|
|
|
// Tools menu
|
|
let toolsMenuItem = NSMenuItem(title: "Tools", action: nil, keyEquivalent: "")
|
|
mainMenu.addItem(toolsMenuItem)
|
|
let toolsMenu = NSMenu(title: "Tools")
|
|
toolsMenuItem.submenu = toolsMenu
|
|
|
|
toolsMenu.addItem(NSMenuItem(title: "AI Command Generator", action: #selector(showAIGenerator), keyEquivalent: "k"))
|
|
toolsMenu.addItem(NSMenuItem(title: "Snippets", action: #selector(showSnippets), keyEquivalent: "p"))
|
|
toolsMenu.addItem(NSMenuItem(title: "Command Palette", action: #selector(showCommandPalette), keyEquivalent: "p"))
|
|
|
|
NSApp.mainMenu = mainMenu
|
|
}
|
|
|
|
@objc func addNewTab() {
|
|
let session = tabManager.addTab()
|
|
session.setupTextView(frame: contentView.bounds)
|
|
session.startShell()
|
|
|
|
updateTabBar()
|
|
switchToTab(index: tabManager.tabs.count - 1)
|
|
}
|
|
|
|
@objc func closeCurrentTab() {
|
|
guard tabManager.tabs.count > 1 else {
|
|
NSApp.terminate(nil)
|
|
return
|
|
}
|
|
|
|
tabManager.removeTab(at: tabManager.currentIndex)
|
|
updateTabBar()
|
|
switchToTab(index: tabManager.currentIndex)
|
|
}
|
|
|
|
private func updateTabBar() {
|
|
tabBar.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
|
|
|
for (index, session) in tabManager.tabs.enumerated() {
|
|
let tabButton = NSButton(frame: NSRect(x: 0, y: 0, width: 150, height: 40))
|
|
tabButton.title = session.title
|
|
tabButton.bezelStyle = .rounded
|
|
tabButton.tag = index
|
|
tabButton.target = self
|
|
tabButton.action = #selector(tabClicked(_:))
|
|
|
|
if index == tabManager.currentIndex {
|
|
tabButton.contentTintColor = .systemBlue
|
|
}
|
|
|
|
tabBar.addArrangedSubview(tabButton)
|
|
}
|
|
|
|
// Add new tab button
|
|
let newTabButton = NSButton(frame: NSRect(x: 0, y: 0, width: 40, height: 40))
|
|
newTabButton.title = "+"
|
|
newTabButton.bezelStyle = .rounded
|
|
newTabButton.target = self
|
|
newTabButton.action = #selector(addNewTab)
|
|
tabBar.addArrangedSubview(newTabButton)
|
|
}
|
|
|
|
@objc func tabClicked(_ sender: NSButton) {
|
|
switchToTab(index: sender.tag)
|
|
}
|
|
|
|
private func switchToTab(index: Int) {
|
|
guard index < tabManager.tabs.count else { return }
|
|
|
|
tabManager.selectTab(at: index)
|
|
|
|
contentView.subviews.forEach { $0.removeFromSuperview() }
|
|
|
|
if let session = tabManager.getCurrentSession(),
|
|
let scrollView = session.textView.enclosingScrollView {
|
|
scrollView.frame = contentView.bounds
|
|
contentView.addSubview(scrollView)
|
|
currentSessionView = scrollView
|
|
}
|
|
|
|
updateTabBar()
|
|
}
|
|
|
|
@objc func splitVertically() {
|
|
showAlert("Split panes feature coming soon!")
|
|
}
|
|
|
|
@objc func splitHorizontally() {
|
|
showAlert("Split panes feature coming soon!")
|
|
}
|
|
|
|
@objc func searchHistory() {
|
|
let panel = HistorySearchPanel()
|
|
panel.show(relativeTo: window!) { [weak self] command in
|
|
if let command = command, let session = self?.tabManager.getCurrentSession() {
|
|
session.textView.insertText(command, replacementRange: NSRange(location: session.textView.string.count, length: 0))
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func showSnippets() {
|
|
let panel = SnippetPanel()
|
|
panel.show(relativeTo: window!) { [weak self] snippet in
|
|
if let snippet = snippet, let session = self?.tabManager.getCurrentSession() {
|
|
session.textView.insertText(snippet.command, replacementRange: NSRange(location: session.textView.string.count, length: 0))
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func showAIGenerator() {
|
|
guard AIManager.shared.isAvailable() else {
|
|
showAlert("Please configure AI provider in Settings first")
|
|
return
|
|
}
|
|
|
|
let panel = AIGeneratorPanel()
|
|
panel.show(relativeTo: window!) { [weak self] command in
|
|
if let command = command, let session = self?.tabManager.getCurrentSession() {
|
|
session.textView.insertText(command, replacementRange: NSRange(location: session.textView.string.count, length: 0))
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func showCommandPalette() {
|
|
showAlert("Command palette coming soon!")
|
|
}
|
|
|
|
@objc func openSettings() {
|
|
let settingsWindow = SettingsWindowController()
|
|
settingsWindow.showWindow(nil)
|
|
}
|
|
|
|
@objc func increaseFontSize() {
|
|
TerminalSettings.shared.fontSize += 1
|
|
tabManager.tabs.forEach { $0.applyTheme() }
|
|
}
|
|
|
|
@objc func decreaseFontSize() {
|
|
TerminalSettings.shared.fontSize = max(8, TerminalSettings.shared.fontSize - 1)
|
|
tabManager.tabs.forEach { $0.applyTheme() }
|
|
}
|
|
|
|
private func showAlert(_ message: String) {
|
|
let alert = NSAlert()
|
|
alert.messageText = message
|
|
alert.alertStyle = .informational
|
|
alert.addButton(withTitle: "OK")
|
|
alert.runModal()
|
|
}
|
|
}
|