Add quick-win features: copy-on-select, middle-click paste, visual bell, zoom pane, clickable URLs, draggable dividers
- Copy on select: Auto-copy selected text to clipboard - Middle-click paste: Quick paste with middle mouse button - Visual bell: Subtle flash when commands complete - Zoom pane: Cmd+Shift+Enter to maximize/restore split pane - Clickable URLs: Cmd+click to open URLs in browser - Draggable dividers: Resize split panes by dragging divider - Updated help and keyboard shortcuts documentation
This commit is contained in:
@@ -50,6 +50,8 @@ class MainWindowController: NSWindowController {
|
||||
var snippetPanel: SnippetPanel!
|
||||
var aiPanel: AIGeneratorPanel!
|
||||
var settingsWindow: SettingsWindowController!
|
||||
var zoomedPane: TerminalSession?
|
||||
var preZoomSplitView: NSSplitView?
|
||||
|
||||
convenience init() {
|
||||
let window = NSWindow(
|
||||
@@ -202,6 +204,10 @@ class MainWindowController: NSWindowController {
|
||||
closePaneItem.keyEquivalentModifierMask = [.command, .shift]
|
||||
fileMenu.addItem(closePaneItem)
|
||||
|
||||
let zoomPaneItem = NSMenuItem(title: "Zoom Pane", action: #selector(toggleZoomPane), keyEquivalent: "\r")
|
||||
zoomPaneItem.keyEquivalentModifierMask = [.command, .shift]
|
||||
fileMenu.addItem(zoomPaneItem)
|
||||
|
||||
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"))
|
||||
@@ -552,6 +558,7 @@ class MainWindowController: NSWindowController {
|
||||
Cmd+W - Close Tab
|
||||
Cmd+D - Split Pane Vertically (side-by-side)
|
||||
Cmd+Shift+D - Split Pane Horizontally (top-bottom)
|
||||
Cmd+Shift+Enter - Zoom/Unzoom Pane
|
||||
Cmd+K - Clear Screen
|
||||
Cmd+Shift+K - Clear Scrollback
|
||||
|
||||
@@ -561,6 +568,7 @@ class MainWindowController: NSWindowController {
|
||||
Cmd+A - Select All
|
||||
Cmd+F - Find in Terminal
|
||||
Cmd+R - Search History
|
||||
Middle-click - Paste
|
||||
|
||||
Tools:
|
||||
Cmd+K - AI Command Generator
|
||||
@@ -579,6 +587,8 @@ class MainWindowController: NSWindowController {
|
||||
Up/Down - Navigate Command History
|
||||
Double-click Tab - Rename Tab
|
||||
Click × button - Close split pane
|
||||
Cmd+Click URL - Open in browser
|
||||
Select text - Auto-copy to clipboard
|
||||
"""
|
||||
alert.alertStyle = .informational
|
||||
alert.addButton(withTitle: "OK")
|
||||
@@ -602,6 +612,7 @@ class MainWindowController: NSWindowController {
|
||||
splitView?.isVertical = true // Side-by-side (vertical divider)
|
||||
splitView?.dividerStyle = .thin
|
||||
splitView?.autoresizingMask = [.width, .height]
|
||||
splitView?.delegate = self
|
||||
|
||||
// Move current session to split view with close button
|
||||
currentSession.scrollView.removeFromSuperview()
|
||||
@@ -646,6 +657,7 @@ class MainWindowController: NSWindowController {
|
||||
splitView?.isVertical = false // Top-bottom (horizontal divider)
|
||||
splitView?.dividerStyle = .thin
|
||||
splitView?.autoresizingMask = [.width, .height]
|
||||
splitView?.delegate = self
|
||||
|
||||
// Move current session to split view with close button
|
||||
currentSession.scrollView.removeFromSuperview()
|
||||
@@ -759,6 +771,53 @@ class MainWindowController: NSWindowController {
|
||||
}
|
||||
}
|
||||
|
||||
@objc func toggleZoomPane() {
|
||||
if zoomedPane != nil {
|
||||
// Unzoom - restore split view
|
||||
unzoomPane()
|
||||
} else {
|
||||
// Zoom current pane
|
||||
zoomPane()
|
||||
}
|
||||
}
|
||||
|
||||
private func zoomPane() {
|
||||
guard panes.count > 1 else { return }
|
||||
|
||||
// Save current split view state
|
||||
preZoomSplitView = splitView
|
||||
zoomedPane = panes[activePaneIndex]
|
||||
|
||||
// Remove split view
|
||||
splitView?.removeFromSuperview()
|
||||
|
||||
// Show only the active pane
|
||||
if let zoomed = zoomedPane {
|
||||
zoomed.scrollView.removeFromSuperview()
|
||||
zoomed.scrollView.frame = contentView.bounds
|
||||
contentView.addSubview(zoomed.scrollView)
|
||||
zoomed.textView.window?.makeFirstResponder(zoomed.textView)
|
||||
}
|
||||
}
|
||||
|
||||
private func unzoomPane() {
|
||||
guard let zoomed = zoomedPane, let savedSplit = preZoomSplitView else { return }
|
||||
|
||||
// Remove zoomed pane
|
||||
zoomed.scrollView.removeFromSuperview()
|
||||
|
||||
// Restore split view
|
||||
contentView.addSubview(savedSplit)
|
||||
savedSplit.frame = contentView.bounds
|
||||
|
||||
// Clear zoom state
|
||||
zoomedPane = nil
|
||||
preZoomSplitView = nil
|
||||
|
||||
// Focus the previously zoomed pane
|
||||
zoomed.textView.window?.makeFirstResponder(zoomed.textView)
|
||||
}
|
||||
|
||||
@objc func clearHistory() {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Clear Command History?"
|
||||
@@ -813,3 +872,19 @@ class MainWindowController: NSWindowController {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSSplitView Delegate
|
||||
extension MainWindowController: NSSplitViewDelegate {
|
||||
func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
|
||||
return 100 // Minimum pane size
|
||||
}
|
||||
|
||||
func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
|
||||
let totalSize = splitView.isVertical ? splitView.bounds.width : splitView.bounds.height
|
||||
return totalSize - 100 // Minimum pane size
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user