diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Focus/KeyRouter+Context.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Focus/KeyRouter+Context.swift index 0c3a4a19c942..6830f5b0aa87 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Focus/KeyRouter+Context.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/Focus/KeyRouter+Context.swift @@ -204,7 +204,7 @@ extension KeyRouter { var modelPage: PageWebView? if case .page(_, _) = controller.focus.state.resolved, let pane = controller.focus.state.resolved.pane, case .page(let view)? = controller.content?.paneController(key: pane)?.currentContent { - modelPage = view.content as? PageWebView + modelPage = PageWebView.shown(in: view.content) } return Self.keyPage(modelPage: modelPage, in: controller.window) } diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/InternalPages/InternalPageView.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/InternalPages/InternalPageView.swift index 94a4210bbeb5..6fc90bbcd2c5 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/InternalPages/InternalPageView.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/InternalPages/InternalPageView.swift @@ -80,7 +80,7 @@ final class InternalPageView: NSView { /// or the view a native page names (``InternalPageFocusing``). var focusTarget: NSView { if let input = (content as? any PrimaryInputTarget)?.primaryInputView { return input } - if let page = content as? PageWebView { return page.focusView } + if let page = PageWebView.shown(in: content) { return page.focusView } return (content as? any InternalPageFocusing)?.focusTarget ?? content } diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift index 3e83dac61dd2..25306555db55 100644 --- a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift +++ b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift @@ -39,7 +39,7 @@ enum TabContent { @MainActor var inputReadiness: PageInputReadiness? { switch self { case .agent(let view): view.inputReadiness - case .page(let view): (view.content as? PageWebView)?.inputReadiness + case .page(let view): (view.content as? PageWebView)?.inputReadiness // not a container's page: docked Channels focuses no input, a hold ate j/k/r (#33869) default: nil } } diff --git a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift new file mode 100644 index 000000000000..775efd98523c --- /dev/null +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift @@ -0,0 +1,47 @@ +import AppKit +import CmuxNextPages +import Testing +@testable import CmuxNextApp + +/// Follow-up of #29386 (review note 3): the docked Home's Channels page is a page INSIDE the dock's view +/// (`HomeDockView`, a ``PageWebViewContainer``), not the tab's content itself. #29386 taught +/// `TabContent.childWindowTab` to find it through ``PageWebView/shown(in:)``; the key router's focused page +/// and the tab's focus target still looked for `content as? PageWebView` only, so for Channels the focus +/// target was the dock view (it takes no keys), not the page. Those readers must find the page a container +/// shows, by the same rule. The type-ahead must not (see its test). +@MainActor +struct HomeDockChannelsPageReadersTests { + /// A container like the docked Home in Channels mode: it shows a page inside its own view. + final class Dock: NSView, PageWebViewContainer { + var page: PageWebView? + var shownPageView: PageWebView? { page } + } + + private func dockShowingPage() throws -> (InternalPageView, PageWebView) { + let page = try #require(PageWebView(descriptor: PageDescriptor(id: "test.dock-readers", resource: "files", namespaces: []), routes: [])) + let dock = Dock(frame: NSRect(x: 0, y: 0, width: 400, height: 300)) + dock.page = page + dock.addSubview(page) + return (InternalPageView(key: "tab_dock", page: .homeDock, content: dock), page) + } + + /// Review of #33869 (FIX at 181170d6294): the Channels page focuses no input when it opens, so its readiness stays + /// not ready until the person focuses a field. A type-ahead hold on it held and then dropped the page's own bare keys + /// (j, k, r, t, c, e). The type-ahead must not read a page that a container shows. + @Test func theTypeAheadDoesNotHoldKeysForThePageTheDockShows() throws { + let (view, _) = try dockShowingPage() + #expect(TabContent.page(view).inputReadiness == nil, "the type-ahead would hold Channels' own bare keys") + } + + @Test func theTabsFocusTargetIsThePageTheDockShows() throws { + let (view, page) = try dockShowingPage() + #expect(view.focusTarget === page.focusView, "the tab's focus target is the dock view, not the Channels page") + } + + @Test func aPageTabIsStillItsOwnPage() throws { + let page = try #require(PageWebView(descriptor: PageDescriptor(id: "test.dock-readers-own", resource: "files", namespaces: []), routes: [])) + let view = InternalPageView(key: "tab_page", page: .homeTab, content: page) + #expect(TabContent.page(view).inputReadiness === page.inputReadiness) + #expect(view.focusTarget === page.focusView) + } +}