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:
2026-01-31 23:47:49 +00:00
parent faad3c3981
commit 56d6b31b2f
5 changed files with 132 additions and 0 deletions

View File

@@ -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
}
}