Add debug scrollback tab and smooth scroll wheel

This commit is contained in:
Lawrence Chen 2026-01-28 03:00:50 -08:00
parent 3877dc2d98
commit 8984e8a750
4 changed files with 59 additions and 7 deletions

View file

@ -78,6 +78,32 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
pasteboard.setString(payload, forType: .string)
}
#if DEBUG
@objc func openDebugScrollbackTab(_ sender: Any?) {
guard let tabManager else { return }
let tab = tabManager.addTab()
let config = GhosttyConfig.load()
let lineCount = min(max(config.scrollbackLimit * 2, 2000), 60000)
let command = "for i in {1..\(lineCount)}; do printf \"scrollback %06d\\n\" $i; done\n"
sendTextWhenReady(command, to: tab)
}
private func sendTextWhenReady(_ text: String, to tab: Tab, attempt: Int = 0) {
let maxAttempts = 60
if let surface = tab.focusedSurface, surface.surface != nil {
surface.sendText(text)
return
}
guard attempt < maxAttempts else {
NSLog("Debug scrollback: surface not ready after \(maxAttempts) attempts")
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
self?.sendTextWhenReady(text, to: tab, attempt: attempt + 1)
}
}
#endif
func attachUpdateAccessory(to window: NSWindow) {
titlebarAccessoryController.start()
titlebarAccessoryController.attach(to: window)

View file

@ -703,6 +703,15 @@ class TerminalSurface: Identifiable {
return ghostty_surface_needs_confirm_quit(surface)
}
func sendText(_ text: String) {
guard let surface = surface else { return }
guard let data = text.data(using: .utf8), !data.isEmpty else { return }
data.withUnsafeBytes { rawBuffer in
guard let baseAddress = rawBuffer.baseAddress?.assumingMemoryBound(to: CChar.self) else { return }
ghostty_surface_text(surface, baseAddress, UInt(rawBuffer.count))
}
}
deinit {
if ownsDisplayLink {
GhosttyApp.shared.releaseDisplayLink()
@ -1480,14 +1489,21 @@ private final class GhosttyScrollView: NSScrollView {
weak var surfaceView: GhosttyNSView?
override func scrollWheel(with event: NSEvent) {
if let surfaceView {
if window?.firstResponder !== surfaceView {
window?.makeFirstResponder(surfaceView)
}
surfaceView.scrollWheel(with: event)
guard let surfaceView else {
super.scrollWheel(with: event)
return
}
super.scrollWheel(with: event)
if window?.firstResponder !== surfaceView {
window?.makeFirstResponder(surfaceView)
}
if let surface = surfaceView.terminalSurface?.surface,
ghostty_surface_mouse_captured(surface) {
surfaceView.scrollWheel(with: event)
} else {
super.scrollWheel(with: event)
}
}
}

View file

@ -248,7 +248,8 @@ class TabManager: ObservableObject {
}
}
func addTab() {
@discardableResult
func addTab() -> Tab {
let newTab = Tab(title: "Terminal \(tabs.count + 1)")
tabs.append(newTab)
selectedTabId = newTab.id
@ -257,6 +258,7 @@ class TabManager: ObservableObject {
object: nil,
userInfo: [GhosttyNotificationKey.tabId: newTab.id]
)
return newTab
}
func moveTabToTop(_ tabId: UUID) {

View file

@ -56,6 +56,14 @@ struct cmuxApp: App {
}
}
#if DEBUG
CommandMenu("Debug") {
Button("New Tab With Large Scrollback") {
appDelegate.openDebugScrollbackTab(nil)
}
}
#endif
// New tab commands
CommandGroup(replacing: .newItem) {
Button("New Tab") {