Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6733393
chore(terminal): delete disabled SurfacePool machinery
arzafran Jul 9, 2026
b13bf09
refactor(contentview): extract TabItemView.swift (mechanical move)
arzafran Jul 9, 2026
3a6620d
refactor(contentview): extract FileDropOverlayView.swift (mechanical …
arzafran Jul 9, 2026
3938784
refactor(contentview): extract CommandPaletteSearchEngine.swift (mech…
arzafran Jul 9, 2026
dd1fe83
fix(terminal): fix lost Ghostty focus push after reparent resume
arzafran Jul 9, 2026
4b17278
refactor(terminal): dedupe triplicated hide/retry block in synchroniz…
arzafran Jul 9, 2026
5b236db
refactor: split free-standing types out of AppDelegate.swift
arzafran Jul 9, 2026
5303045
refactor(browser): consolidate hosted-inspector dock detection into I…
arzafran Jul 9, 2026
fde4223
fix(workspace): unify remote-state reset between configure and discon…
arzafran Jul 9, 2026
e8d4c2a
refactor(workspace-remote): dedup SSH quoting, connection policy, and…
arzafran Jul 9, 2026
32e8c20
refactor: extract handleCustomShortcut app-shortcut dispatch table
arzafran Jul 9, 2026
d161e9f
fix(contentview): widen access for symbols split across the #94 file …
arzafran Jul 9, 2026
33132b1
refactor(workspace-remote): split WorkspaceRemoteDaemon.swift by type
arzafran Jul 9, 2026
2140f5e
refactor(terminal): split IME and SwiftUI wrapper out of GhosttyTermi…
arzafran Jul 9, 2026
5294e0c
refactor: consolidate command-palette per-window state into one dicti…
arzafran Jul 9, 2026
5563548
refactor(browser): split BrowserPanel.swift along concern seams
arzafran Jul 9, 2026
5f44a87
refactor(contentview): extract SidebarDragDrop.swift and SidebarVisua…
arzafran Jul 9, 2026
e9c9ee8
refactor(terminal): split RenderStats debug introspection out of Ghos…
arzafran Jul 9, 2026
f10f59e
refactor(browser): split WebViewRepresentable out of BrowserPanelView…
arzafran Jul 9, 2026
593ea63
refactor(workspace-remote): split WorkspaceRemoteSessionController by…
arzafran Jul 9, 2026
aac4826
refactor(contentview): extract CommandPaletteController for palette s…
arzafran Jul 9, 2026
0d6e802
refactor(workspace): extract Persistence/Layout/Bonsplit from Workspa…
arzafran Jul 9, 2026
8ad5fef
Merge remote-tracking branch 'origin/main' into train-cv
arzafran Jul 9, 2026
32a95f9
Merge branch 'train-cv' into train-ad
arzafran Jul 9, 2026
b5e2c7c
Merge branch 'train-ad' into train-gv
arzafran Jul 9, 2026
6d0a63a
Merge branch 'train-gv' into train-ws
arzafran Jul 9, 2026
03a2920
Merge branch 'train-ws' into train-br
arzafran Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 168 additions & 8 deletions GhosttyTabs.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

17,649 changes: 7,376 additions & 10,273 deletions Sources/AppDelegate.swift

Large diffs are not rendered by default.

76 changes: 16 additions & 60 deletions Sources/BrowserWindowPortal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -951,8 +951,8 @@ final class WindowBrowserHostView: NSView {
}

private func hostedInspectorDividerCandidate(in slot: WindowBrowserSlotView) -> HostedInspectorDividerHit? {
let inspectorCandidates = Self.visibleDescendants(in: slot)
.filter { Self.isVisibleHostedInspectorCandidate($0) && Self.isInspectorView($0) }
let inspectorCandidates = InspectorDock.visibleDescendants(in: slot)
.filter { InspectorDock.isVisibleCandidate($0) && InspectorDock.isInspectorView($0) }
.sorted { lhs, rhs in
let lhsFrame = slot.convert(lhs.bounds, from: lhs)
let rhsFrame = slot.convert(rhs.bounds, from: rhs)
Expand Down Expand Up @@ -987,9 +987,9 @@ final class WindowBrowserHostView: NSView {
guard let containerView = inspectorView.superview else { break }

let pageCandidates = containerView.subviews.compactMap { candidate -> (view: NSView, dockSide: HostedInspectorDockSide)? in
guard Self.isVisibleHostedInspectorSiblingCandidate(candidate) else { return nil }
guard InspectorDock.isVisibleSiblingCandidate(candidate, requireMinWidth: false) else { return nil }
guard candidate !== inspectorView else { return nil }
guard Self.verticalOverlap(between: candidate.frame, and: inspectorView.frame) > 8 else {
guard InspectorDock.verticalOverlap(between: candidate.frame, and: inspectorView.frame) > 8 else {
return nil
}
guard let dockSide = HostedInspectorDockSide.resolve(
Expand Down Expand Up @@ -1035,13 +1035,13 @@ final class WindowBrowserHostView: NSView {
private func hostedInspectorDividerCandidateScore(_ hit: HostedInspectorDividerHit) -> CGFloat {
let pageFrame = hit.slotView.convert(hit.pageView.bounds, from: hit.pageView)
let inspectorFrame = hit.slotView.convert(hit.inspectorView.bounds, from: hit.inspectorView)
let overlap = Self.verticalOverlap(between: pageFrame, and: inspectorFrame)
let overlap = InspectorDock.verticalOverlap(between: pageFrame, and: inspectorFrame)
let coverageWidth = max(pageFrame.maxX, inspectorFrame.maxX) - min(pageFrame.minX, inspectorFrame.minX)
return (overlap * 1_000) + coverageWidth + pageFrame.width
}

private func hostedInspectorPageCandidateScore(_ pageView: NSView, inspectorView: NSView) -> CGFloat {
let overlap = Self.verticalOverlap(between: pageView.frame, and: inspectorView.frame)
let overlap = InspectorDock.verticalOverlap(between: pageView.frame, and: inspectorView.frame)
let coverageWidth = max(pageView.frame.maxX, inspectorView.frame.maxX) - min(pageView.frame.minX, inspectorView.frame.minX)
return (overlap * 1_000) + coverageWidth + pageView.frame.width
}
Expand Down Expand Up @@ -1083,8 +1083,8 @@ final class WindowBrowserHostView: NSView {
minimumInspectorWidth: Self.minimumHostedInspectorWidth,
reason: reason
)
return !Self.rectApproximatelyEqual(oldPageFrame, hit.pageView.frame, epsilon: 0.5) ||
!Self.rectApproximatelyEqual(oldInspectorFrame, hit.inspectorView.frame, epsilon: 0.5)
return !InspectorDock.rectApproximatelyEqual(oldPageFrame, hit.pageView.frame, epsilon: 0.5) ||
!InspectorDock.rectApproximatelyEqual(oldInspectorFrame, hit.inspectorView.frame, epsilon: 0.5)
}

@discardableResult
Expand All @@ -1107,8 +1107,8 @@ final class WindowBrowserHostView: NSView {

let oldPageFrame = hit.pageView.frame
let oldInspectorFrame = hit.inspectorView.frame
let pageChanged = !Self.rectApproximatelyEqual(pageFrame, oldPageFrame, epsilon: 0.5)
let inspectorChanged = !Self.rectApproximatelyEqual(inspectorFrame, oldInspectorFrame, epsilon: 0.5)
let pageChanged = !InspectorDock.rectApproximatelyEqual(pageFrame, oldPageFrame, epsilon: 0.5)
let inspectorChanged = !InspectorDock.rectApproximatelyEqual(inspectorFrame, oldInspectorFrame, epsilon: 0.5)
guard pageChanged || inspectorChanged else {
return (pageFrame, inspectorFrame)
}
Expand Down Expand Up @@ -1145,49 +1145,11 @@ final class WindowBrowserHostView: NSView {
return (pageFrame, inspectorFrame)
}

private static func verticalOverlap(between lhs: NSRect, and rhs: NSRect) -> CGFloat {
max(0, min(lhs.maxY, rhs.maxY) - max(lhs.minY, rhs.minY))
}

private static func rectApproximatelyEqual(_ lhs: NSRect, _ rhs: NSRect, epsilon: CGFloat = 0.01) -> Bool {
abs(lhs.origin.x - rhs.origin.x) <= epsilon &&
abs(lhs.origin.y - rhs.origin.y) <= epsilon &&
abs(lhs.size.width - rhs.size.width) <= epsilon &&
abs(lhs.size.height - rhs.size.height) <= epsilon
}

private static func sizeApproximatelyEqual(_ lhs: NSSize, _ rhs: NSSize, epsilon: CGFloat = 0.01) -> Bool {
abs(lhs.width - rhs.width) <= epsilon &&
abs(lhs.height - rhs.height) <= epsilon
}

private static func visibleDescendants(in root: NSView) -> [NSView] {
var descendants: [NSView] = []
var stack = Array(root.subviews.reversed())
while let view = stack.popLast() {
descendants.append(view)
stack.append(contentsOf: view.subviews.reversed())
}
return descendants
}

private static func isInspectorView(_ view: NSView) -> Bool {
String(describing: type(of: view)).contains("WKInspector")
}

private static func isVisibleHostedInspectorCandidate(_ view: NSView) -> Bool {
!view.isHidden &&
view.alphaValue > 0 &&
view.frame.width > 1 &&
view.frame.height > 1
}

private static func isVisibleHostedInspectorSiblingCandidate(_ view: NSView) -> Bool {
!view.isHidden &&
view.alphaValue > 0 &&
view.frame.height > 1
}

private static func collectSplitDividerRegions(
in view: NSView,
hostView: WindowBrowserHostView,
Expand Down Expand Up @@ -2400,15 +2362,10 @@ final class WindowBrowserPortal: HostedViewPortalRegistry {
private static func hasVisibleInspectorDescendant(in root: NSView) -> Bool {
var stack: [NSView] = [root]
while let current = stack.popLast() {
if current !== root {
let className = String(describing: type(of: current))
if className.contains("WKInspector"),
!current.isHidden,
current.alphaValue > 0,
current.frame.width > 1,
current.frame.height > 1 {
return true
}
if current !== root,
InspectorDock.isInspectorView(current),
InspectorDock.isVisibleCandidate(current) {
return true
}
stack.append(contentsOf: current.subviews)
}
Expand Down Expand Up @@ -2468,7 +2425,7 @@ final class WindowBrowserPortal: HostedViewPortalRegistry {
var count = 0
while let current = stack.popLast() {
for subview in current.subviews {
if String(describing: type(of: subview)).contains("WKInspector") {
if InspectorDock.isInspectorView(subview) {
count += 1
}
stack.append(subview)
Expand Down Expand Up @@ -2519,8 +2476,7 @@ final class WindowBrowserPortal: HostedViewPortalRegistry {
if view === primaryWebView { continue }
let className = String(describing: type(of: view))
guard className.contains("WK") else { continue }
if className.contains("WKInspector") &&
(view.isHidden || view.alphaValue <= 0 || view.frame.width <= 1 || view.frame.height <= 1) {
if InspectorDock.isInspectorView(view) && !InspectorDock.isVisibleCandidate(view) {
continue
}
append(view)
Expand Down
Loading
Loading