From a33a4f697d2e519ebeeff0d7b0a54db8880bf7ad Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:47:11 +0000 Subject: [PATCH 1/3] test(home): red: the page readers must find the Home dock's Channels page through its container HomeDockChannelsPageReadersTests: TabContent.inputReadiness and InternalPageView.focusTarget read only 'content as? PageWebView', so for the docked Home in Channels mode (the page inside HomeDockView, a PageWebViewContainer) the type-ahead has no input readiness and the focus target is the dock view. #29386 review note 3. Fails here. --- .../HomeDockChannelsPageReadersTests.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift diff --git a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift new file mode 100644 index 000000000000..260511bf6709 --- /dev/null +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift @@ -0,0 +1,45 @@ +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 other page readers still +/// looked for `content as? PageWebView` only, so for Channels the key router's type-ahead had no input +/// readiness (keys typed at its first open were not held for the page) and the tab's focus target was the +/// dock view, not the page. Each reader must find the page a container shows, by the same rule. +@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) + } + + @Test func theTypeAheadReadsTheInputReadinessOfThePageTheDockShows() throws { + let (view, page) = try dockShowingPage() + #expect(TabContent.page(view).inputReadiness === page.inputReadiness, + "keys typed at Channels' first open are not held for its page") + } + + @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) + } +} From 181170d62949c846c8b40cb63bf13fc0007149bf Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:49:09 +0000 Subject: [PATCH 2/3] fix(home): the type-ahead, the key router's focused page and the tab's focus target find the Home dock's Channels page #29386 review note 3. TabContent.inputReadiness, KeyRouter.focusedPage and InternalPageView.focusTarget now find a tab's page through PageWebView.shown(in:), the rule TabContent.childWindowTab already uses: the content itself when it is a page, else the page a PageWebViewContainer shows (the docked Home's Channels page while Channels shows). Before, for Channels: no type-ahead (keys typed at its first open were not held for the page), no page commands or page context from the key router, and on a WebKit fallback the dock view (which takes no keys) became first responder. Messages mode is unchanged: the dock shows no page there, and its primary input still comes first in focusTarget. --- .../CmuxNext/Sources/CmuxNextApp/Focus/KeyRouter+Context.swift | 2 +- .../Sources/CmuxNextApp/InternalPages/InternalPageView.swift | 2 +- Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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..2e8f202f1bb0 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): PageWebView.shown(in: view.content)?.inputReadiness default: nil } } From 0446888437f9408d570858aba42994338046f7d7 Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:55:24 +0000 Subject: [PATCH 3/3] fix(home): the type-ahead stays off for the Home dock's Channels page Review of #33869 (FIX at 181170d6294): TabContent.inputReadiness through PageWebView.shown(in:) gave the docked Channels page a type-ahead hold. That page focuses no input when it opens, so its readiness stays not ready and the hold ate its own bare keys (j, k, r, t, c, e) until the 2 s limit. inputReadiness reads a page tab's own page again; the test now guards that a page a container shows gets no readiness. KeyRouter.focusedPage and InternalPageView.focusTarget keep the shown(in:) rule. The standalone Channels tab (its content is the page) has the same hold on bare keys today; that is not changed here. --- .../Sources/CmuxNextApp/TabContent.swift | 2 +- .../HomeDockChannelsPageReadersTests.swift | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift b/Packages/macOS/CmuxNext/Sources/CmuxNextApp/TabContent.swift index 2e8f202f1bb0..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): PageWebView.shown(in: view.content)?.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 index 260511bf6709..775efd98523c 100644 --- a/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift +++ b/Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/HomeDockChannelsPageReadersTests.swift @@ -5,10 +5,10 @@ import Testing /// 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 other page readers still -/// looked for `content as? PageWebView` only, so for Channels the key router's type-ahead had no input -/// readiness (keys typed at its first open were not held for the page) and the tab's focus target was the -/// dock view, not the page. Each reader must find the page a container shows, by the same rule. +/// `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. @@ -25,10 +25,12 @@ struct HomeDockChannelsPageReadersTests { return (InternalPageView(key: "tab_dock", page: .homeDock, content: dock), page) } - @Test func theTypeAheadReadsTheInputReadinessOfThePageTheDockShows() throws { - let (view, page) = try dockShowingPage() - #expect(TabContent.page(view).inputReadiness === page.inputReadiness, - "keys typed at Channels' first open are not held for its 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 {