Compare commits

...

1 Commits

Author SHA1 Message Date
Naiyuan Qing
1b485af44c fix(desktop): expose search params from root navigation adapter
DesktopNavigationProvider stubbed `searchParams` to an empty
URLSearchParams, so any shell-level consumer of useNavigation() that
looked at query params read blanks. The miss surfaced in focus-mode:
on /inbox?issue=<id>, ChatWindow's useRouteAnchorCandidate couldn't
see the selection, so the Focus button stayed disabled.

Mirror the full location (pathname + search) from the active tab's
router — same subscription pattern TabNavigationProvider already uses
~30 lines below. InboxPage itself was fine because it's rendered
inside TabNavigationProvider; the bug only hit components mounted at
the shell root (ChatWindow, ChatFab, and any future sibling).

No test: the fix is an identical copy of a production-shipped pattern
in the same file, and the mock surface needed to exercise the adapter
(useActiveTabRouter + memory router + tab store) exceeds the fix
itself. Verified via pnpm typecheck across all packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:54:08 +08:00

View File

@@ -114,18 +114,32 @@ export function DesktopNavigationProvider({
// resolve the active router here only to subscribe once per tab switch.
const { tabId: activeTabId } = useActiveTabIdentity();
const router = useActiveTabRouter();
const [pathname, setPathname] = useState(
router?.state.location.pathname ?? "/",
// Mirror the active tab router's full location (pathname + search) so
// shell-level consumers of useNavigation() — ChatWindow in particular —
// can read URL search params. Must stay in sync with TabNavigationProvider
// below; a partial shape here (just pathname) silently broke focus-mode
// anchor resolution on `/inbox?issue=…`.
const [location, setLocation] = useState<{ pathname: string; search: string }>(
() => ({
pathname: router?.state.location.pathname ?? "/",
search: router?.state.location.search ?? "",
}),
);
useEffect(() => {
if (!router) {
setPathname("/");
setLocation({ pathname: "/", search: "" });
return;
}
setPathname(router.state.location.pathname);
setLocation({
pathname: router.state.location.pathname,
search: router.state.location.search,
});
return router.subscribe((state) => {
setPathname(state.location.pathname);
setLocation({
pathname: state.location.pathname,
search: state.location.search,
});
});
}, [activeTabId, router]);
@@ -150,8 +164,8 @@ export function DesktopNavigationProvider({
back: () => {
currentActiveTab()?.router.navigate(-1);
},
pathname,
searchParams: new URLSearchParams(),
pathname: location.pathname,
searchParams: new URLSearchParams(location.search),
openInNewTab: (path: string, title?: string) => {
// Cross-workspace "open in new tab" switches workspace and opens
// the path there; same-workspace just adds a tab in the current group.
@@ -167,7 +181,7 @@ export function DesktopNavigationProvider({
},
getShareableUrl: (path: string) => `${APP_URL}${path}`,
}),
[pathname],
[location],
);
return <NavigationProvider value={adapter}>{children}</NavigationProvider>;