feat(app): sidebar Back/Forward route-history controls#66
Draft
brsbl wants to merge 3 commits into
Draft
Conversation
Add browser-style Back/Forward navigation to the left sidebar that moves through the app-shell route history. - New useAppRouteHistoryNavigation hook (app-route-history.ts) tracks the actual React Router entries visited while mounted (including duplicate same-URL pushes with distinct location.keys). PUSH appends and clears the forward stack, REPLACE updates the current slot, POP reconciles to a known entry by key and treats an unrecorded key as the app-owned history boundary. Back/Forward move by a real navigate(delta) that skips equal-URL slots so one click lands on the nearest visibly different route. - New SidebarHistoryNavigationControls renders two accessible icon buttons (Go back / Go forward) using the Button primitive + sidebar tokens, native disabled state, aria-hidden icons, and an onNavigate hook for closing the mobile drawer. - Wire the controls into AppSidebar's primary-actions area above New Thread / New Manager, passing closeOnMobile so the compact drawer closes after an enabled press. Placement keeps the controls out of the macOS titlebar drag region and hidden in the icon-collapsed sidebar. Tests cover initial disabled state, push/back/forward, duplicate same-URL skipping, forward-stack clearing, replace, native/unknown POP boundaries, the onNavigate contract, and sidebar placement with unchanged desktop chrome. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace inline types in function signatures with named aliases per AGENTS: - AppRouteNavigationType for reduceHistory's navigation-kind parameter. - NavigateToOptions for the test navigateTo helper's options argument. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lisions BrowserRouter labels unkeyed document-history entries "default", so a POP can share the mounted entry's key while pointing at a different URL. Two stale-state bugs followed: - The effect deduped on location.key, so a same-key POP was dropped entirely and never reduced. Dedupe on the location object identity instead (React Router hands out a fresh location per navigation). - The POP reducer reconciled by key alone, so it could snap to a recorded slot whose URL no longer matches. Require key AND normalized URL to match; otherwise treat the popped route as the app-owned history boundary. Adds a regression test with MemoryRouter entries that intentionally share the "default" key but have different URLs (verified to fail before this fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds browser-style Back and Forward navigation buttons to the left sidebar that move through the bb app-shell route history. This is app-route history only — it does not touch the in-thread browser tab history in
BrowserTabContent.The controls live in the sidebar primary-actions area, above New Thread / New Manager (keeping them clear of the macOS titlebar/traffic-light drag region and hidden in the icon-collapsed sidebar).
What changed
apps/app/src/lib/app-route-history.ts— newuseAppRouteHistoryNavigationhook. Tracks the actual React Router entries visited while mounted, including duplicate same-URL pushes (distinctlocation.keys):PUSHappends after the current slot and drops the forward stack.REPLACEoverwrites the current slot (no extra Back entry).POPreconciles to a known slot bylocation.key; an unrecorded key is treated as the app-owned history boundary (stack resets to the current route) so the controls never step into unrecorded/off-app history.canGoBack/canGoForwardandgoBack/goForwardskip equal-URL slots and move by a realnavigate(delta), so one click lands on the nearest visibly different route (no fake no-op steps) while preserving router/browser state.apps/app/src/components/sidebar/SidebarHistoryNavigationControls.tsx— two accessible icon buttons (Go back/Go forward) built on theButtonprimitive + sidebar tokens, nativedisabled,aria-hiddenicons,type="button", matchingtitle/aria-label. Exposes anonNavigatecallback so the sidebar can close the mobile drawer after an enabled press. Owns its own row spacing.apps/app/src/components/sidebar/AppSidebar.tsx— renders the controls insideapp-sidebar-primary-actionsaboveProjectListActionButtons, passingcloseOnMobileasonNavigate. Existing top-reserve / macOS chrome behavior is untouched.Tests
SidebarHistoryNavigationControls.test.tsx(10 tests): initial disabled state, accessibility attributes, push enables Back only, Back→Forward URL + disabled-state updates, duplicate same-URL skipping (A→B→B: one Back lands on A, one Forward returns to B), forward-stack clearing (A→B→C, Back, push D), replace updates the slot, native POP reconciles to a known entry, unknown POP boundary, and theonNavigate(drawer-close) contract.AppLayout.test.tsx: controls render above the primary actions, Back before Forward, with exactly one sidebar toggle (desktop chrome unchanged). Existing chrome/resize tests remain valid.Validation
pnpm exec turbo run typecheck --filter=@bb/app— passpnpm exec turbo run lint --filter=@bb/app— passpnpm exec turbo run test --filter=@bb/app --force— 910 passed (139 files), including the new suitesLive browser UI verification was not run: no browser-automation tooling is available in this environment, and the only running server is the installed prod build, which does not include these worktree changes. Behavior is covered by the automated tests above.
🤖 Generated with Claude Code