From 4726064a0669d09e2333b1cd07aca2bd4de09b3e Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 30 Oct 2026 02:15:58 +0000 Subject: [PATCH 1/2] test(next): cover multi-view split drag focus and highlights Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f2b75a141ca1dfd30e2 --- .../SidebarMultiViewDropTests.swift | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift index 0956906a3c7c..0c5b83f6fbf9 100644 --- a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift @@ -14,6 +14,61 @@ import Testing /// way a press does, unless Option files it away. The window shows `k1` (two panes) on the left of `k0` before each /// test. These run in the serialized `SidebarMultiViewWindowTests`. extension SidebarMultiViewWindowTests { + /// A workspace drag keeps the focused row strong and every workspace already in the view lit while the + /// existing pane drop overlay previews the split. This is deliberately frame-by-frame: a drag must not + /// transiently navigate to the row being carried or clear the other visible rows. + @Test func aRowDraggedToASplitEdgePreservesFocusAndVisibleRowHighlightsDuringThePreview() async throws { + try await Self.withMultiView { + let f = try await Fixture() + defer { f.close() } + try f.dragRow("k1", to: .left) + try #require(f.shown == ["k0", "k1"]) + let k1 = try #require(f.visible.first { $0.workspace.id == "k1" }) + let pane = try #require(k1.panes.values.first) + let paneFrame = pane.view.convert(pane.view.bounds, to: nil) + let splitPoint = try #require(f.controller.window).convertPoint(toScreen: NSPoint( + x: paneFrame.midX, y: paneFrame.minY + paneFrame.height * 0.15)) + + let start = try f.rowPoint("k2") + f.list.mouseDown(with: try f.event(.leftMouseDown, at: start)) + for dy in [CGFloat(-6), -12, -18] { + f.list.mouseDragged(with: try f.event(.leftMouseDragged, + at: NSPoint(x: start.x, y: start.y + dy))) + f.frame() + #expect(f.state.workspaceID == "k0", "the held drag focused k2 at \(dy) pt") + #expect(f.model.activeWorkspaceID?.rawValue == "k0") + #expect(f.onScreen == ["k0", "k1"]) + #expect(SidebarPointerGestureTests.filledRows(f.list) == ["k0", "k1"], + "visible rows lost their fills at \(dy) pt") + } + + let content = try #require(f.controller.content).contentView + let inside = content.convert(content.bounds, to: nil) + f.list.mouseDragged(with: try f.event(.leftMouseDragged, + at: NSPoint(x: inside.midX, y: inside.midY))) + #expect(f.services.dragSession.drag != nil) + f.services.dragSession.update(splitPoint) + f.frame() + + let drag = try #require(f.services.dragSession.drag) + #expect(drag.workspaceOutcome == .window(id: f.state.id, target: .beside(.bottom))) + #expect(f.state.workspaceID == "k0", "the split preview changed focus") + #expect(f.onScreen == ["k0", "k1"]) + #expect(SidebarPointerGestureTests.filledRows(f.list) == ["k0", "k1"]) + #expect(k1.layoutView.dropHighlightShowing, "the pane drop overlay did not show the split indicator") + #expect(k1.layoutView.dropHighlightFrameInWindow != nil) + + f.services.dragSession.finish(commit: true) + f.services.dragSession.finishLanding() + f.frame() + #expect(f.state.workspaceID == "k0", "the split drop moved focus") + #expect(f.onScreen == f.shown) + #expect(f.shown.contains("k2"), "the dropped workspace did not join the current view") + #expect(SidebarPointerGestureTests.filledRows(f.list) == f.shown, + "the sidebar did not light every workspace in the view") + } + } + /// The fixture with `k1` dropped on the left edge of `k0`. static func besideFixture() async throws -> Fixture { let f = try await Fixture() From b8d3683967bafe76036b0ef959955831cbba84a5 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Sun, 1 Nov 2026 03:36:16 +0000 Subject: [PATCH 2/2] test(next): count the on-screen soft fill when checking multi-view row highlights SidebarPointerGestureTests.filledRows only sees selection-colored fills; the workspace shown beside the focused one draws its soft fill through isOnScreen, so the split-drag test could never see k1 lit. Co-Authored-By: Claude Opus 5.5 --- .../SidebarMultiViewDropTests.swift | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift index 0c5b83f6fbf9..10076b33d95b 100644 --- a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/SidebarMultiViewDropTests.swift @@ -38,7 +38,7 @@ extension SidebarMultiViewWindowTests { #expect(f.state.workspaceID == "k0", "the held drag focused k2 at \(dy) pt") #expect(f.model.activeWorkspaceID?.rawValue == "k0") #expect(f.onScreen == ["k0", "k1"]) - #expect(SidebarPointerGestureTests.filledRows(f.list) == ["k0", "k1"], + #expect(f.litRows == ["k0", "k1"], "visible rows lost their fills at \(dy) pt") } @@ -54,7 +54,7 @@ extension SidebarMultiViewWindowTests { #expect(drag.workspaceOutcome == .window(id: f.state.id, target: .beside(.bottom))) #expect(f.state.workspaceID == "k0", "the split preview changed focus") #expect(f.onScreen == ["k0", "k1"]) - #expect(SidebarPointerGestureTests.filledRows(f.list) == ["k0", "k1"]) + #expect(f.litRows == ["k0", "k1"]) #expect(k1.layoutView.dropHighlightShowing, "the pane drop overlay did not show the split indicator") #expect(k1.layoutView.dropHighlightFrameInWindow != nil) @@ -64,7 +64,7 @@ extension SidebarMultiViewWindowTests { #expect(f.state.workspaceID == "k0", "the split drop moved focus") #expect(f.onScreen == f.shown) #expect(f.shown.contains("k2"), "the dropped workspace did not join the current view") - #expect(SidebarPointerGestureTests.filledRows(f.list) == f.shown, + #expect(f.litRows == f.shown, "the sidebar did not light every workspace in the view") } } @@ -178,6 +178,16 @@ extension SidebarMultiViewWindowTests { } extension SidebarMultiViewWindowTests.Fixture { + /// The realized workspace rows that draw a fill in multi-view: the selection-colored ones + /// (`SidebarPointerGestureTests.filledRows`) plus the soft fill of a row whose workspace is on screen beside + /// the focused one (`WorkspaceRowView.isOnScreen`). + var litRows: Set { + SidebarPointerGestureTests.filledRows(list).union(list.rowViews.compactMap { key, view -> String? in + guard case let .workspace(id) = key, let row = view as? WorkspaceRowView, row.isOnScreen else { return nil } + return id.rawValue + }) + } + /// The `index`th pane of `id`'s screen, and a point in the middle of its body on screen. func paneCenter(of id: String, index: Int) throws -> (PaneController, CGPoint) { let content = try #require(visible.first { $0.workspace.id == id }, "\(id) is not on screen")