claude-devtools:navigation-scroll
The `claude-devtools:navigation-scroll` command documents tab navigation and scroll orchestration in Claude DevTools, covering the nonce-based navigation request model, Redux store actions, controller hook implementation, and scroll precedence system. Use this when implementing or debugging `useTabNavigationController`, understanding how error highlights coordinate with search scrolling, or resolving navigation request conflicts and auto-scroll interactions across tabs.
mkdir -p ~/.claude/commands && curl -fsSL https://raw.githubusercontent.com/matt1398/claude-devtools/HEAD/.claude/commands/devtools/navigation-scroll.md -o ~/.claude/commands/claude-devtools-navigation-scroll.mdnavigation-scroll.md
# Navigation & Scroll Orchestration
How tab navigation (error highlights, search scrolling, auto-scroll) works end-to-end.
## Architecture
### Navigation Request Model (Nonce-Based)
```typescript
// src/renderer/types/tabs.ts
interface TabNavigationRequest {
id: string; // crypto.randomUUID() — fresh nonce per click
kind: 'error' | 'search' | 'autoBottom';
highlight: 'red' | 'yellow' | 'none';
payload: ErrorNavigationPayload | SearchNavigationPayload | {};
source: 'notification' | 'triggerPreview' | 'commandPalette' | 'sessionOpen';
}
// Stored on Tab:
interface Tab {
pendingNavigation?: TabNavigationRequest; // Set by enqueue, cleared by consume
lastConsumedNavigationId?: string; // Tracks last processed request
}
```
### Store Actions (tabSlice.ts)
| Action | Purpose |
|--------|---------|
| `enqueueTabNavigation(tabId, request)` | Set `pendingNavigation` on a tab |
| `consumeTabNavigation(tabId, requestId)` | Clear `pendingNavigation`, record `lastConsumedNavigationId` |
### Navigation Sources
| Source | Slice | Creates |
|--------|-------|---------|
| Notification click / test trigger | `notificationSlice.navigateToError()` | `ErrorNavigationRequest` (red) |
| CommandPalette search result | `tabSlice.navigateToSession()` | `SearchNavigationRequest` (yellow) |
### Controller Hook: `useTabNavigationController`
**Location:** `src/renderer/hooks/useTabNavigationController.ts`
Phase state machine:
```
idle → pending → expanding → scrolling → highlighting → complete → idle
```
Key behaviors:
- **Active-tab-only:** Ignores `!isActiveTab` to prevent cross-tab races
- **Nonce dedup:** `activeRequestIdRef.current === pendingNavigation.id` prevents reprocessing
- **Failure debounce:** 500ms cooldown after failed navigation (`lastFailureAtRef`)
- **Abort support:** New navigation aborts in-progress one via `AbortController`
- **Highlight-first:** Highlight is set BEFORE scroll (best-effort scroll, guaranteed highlight)
### Scroll Precedence (ChatHistory.tsx)
Three scroll systems compete — navigation wins:
| System | Guard | Priority |
|--------|-------|----------|
| Navigation scroll | Controller's `executeNavigation` | Highest |
| Scroll restore (tab switch) | `!shouldDisableAutoScroll` | Medium |
| Auto-scroll to bottom | `disabled: shouldDisableAutoScroll` | Lowest |
`shouldDisableAutoScroll` is `true` during ANY navigation phase or when `pendingNavigation` exists.
## Key Files
| File | Role |
|------|------|
| `src/renderer/hooks/useTabNavigationController.ts` | Unified navigation controller |
| `src/renderer/hooks/navigation/utils.ts` | Shared helpers (scroll calc, element lookup, visibility) |
| `src/renderer/components/chat/ChatHistory.tsx` | Scroll restore + auto-scroll coordination |
| `src/renderer/store/slices/tabSlice.ts` | `enqueueTabNavigation`, `consumeTabNavigation`, `navigateToSession` |
| `src/renderer/store/slices/notificationSlice.ts` | `navigateToError` |
| `src/renderer/store/slices/sessionDetailSlice.ts` | `fetchSessionDetail` (sets `conversationLoading`) |
| `src/renderer/types/tabs.ts` | `TabNavigationRequest` types + factory helpers |
## Common Bug Patterns
### 1. Scroll Restore Overrides Navigation
**Symptom:** Scrolls to target, then snaps back to top/previous position.
**Root cause:** The scroll restore effect fires after `consumeTabNavigation` clears `pendingNavigation`. If the guard only checks `!pendingNavigation`, it triggers while navigation highlight is still active.
**Fix pattern:** Guard scroll restore with `!shouldDisableAutoScroll` instead of `!pendingNavigation`. The controller's `shouldDisableAutoScroll` covers the FULL lifecycle (pending → complete), not just while `pendingNavigation` exists.
**Additional:** Save scroll position when `shouldDisableAutoScroll` transitions true→false (navigation completed) to prevent stale `savedScrollTop` from being restored later.
```typescript
// ChatHistory.tsx — scroll restore effect
useEffect(() => {
const wasDisabled = prevShouldDisableRef.current;
prevShouldDisableRef.current = shouldDisableAutoScroll;
// Navigation just completed — save current position, skip restore
if (wasDisabled && !shouldDisableAutoScroll && scrollContainerRef.current) {
saveScrollPosition(scrollContainerRef.current.scrollTop);
return;
}
if (isThisTabActive && savedScrollTop !== undefined && !conversationLoading && !shouldDisableAutoScroll) {
// ... restore logic
}
}, [isThisTabActive, savedScrollTop, conversationLoading, shouldDisableAutoScroll, saveScrollPosition]);
```
### 2. Redundant `fetchSessionDetail` Unmounts ChatHistory
**Symptom:** Navigation doesn't scroll at all, or session "reloads" unnecessarily.
**Root cause:** `navigateToSession` or `navigateToError` calls `fetchSessionDetail` even when the session is already loaded in an existing tab. This sets `conversationLoading: true`, causing ChatHistory to unmount (show loading spinner) and remount — losing scroll container and controller state.
**Fix pattern:** Only call `fetchSessionDetail` for NEW tabs. For existing tabs, `setActiveTab` already handles the fetch when `sessionChanged` is true.
```typescript
// tabSlice.ts — navigateToSession
if (existingTab) {
state.setActiveTab(existingTab.id);
// NO fetchSessionDetail — setActiveTab handles it
} else {
state.openTab({ ... });
void state.fetchSessionDetail(projectId, sessionId); // Only for new tabs
}
```
### 3. Highlight Not Showing (Strict Post-Scroll Gates)
**Symptom:** Scrolls to correct location but no red/yellow highlight ring appears.
**Root cause:** `executeErrorNavigation` / `executeSearchNavigation` returns `false` after scroll due to strict gates:
- `userInterrupted` — any accidental wheel/touch event during smooth scroll
- `isElementVisibleInContainer` — element partially off-screen after centering (tall elements)
- Element not found within 600ms timeout
When `success = false`, `executeNavigation` clears all highUse this agent when CLAUDE.md files or .claude/rules/ files may have drifted from the actual codebase state. This includes after significant refactors, feature additions, file renames, directory restructuring, or when you suspect documentation no longer matches reality. Use proactively after any large code change.\\n\\nExamples:\\n\\n- Example 1:\\n user: \"Refactor the services directory to split analysis into separate modules\"\\n assistant: \"I've completed the refactoring of the services directory. Let me now launch the claude-md-auditor agent to ensure all CLAUDE.md files reflect these structural changes.\"\\n <commentary>\\n Since a significant refactor was performed that likely changed file paths, exports, and directory structure documented in CLAUDE.md files, use the Task tool to launch the claude-md-auditor agent.\\n </commentary>\\n\\n- Example 2:\\n user: \"Add a new notifications feature with hooks, components, and a store slice\"\\n assistant: \"The notifications feature is now implemented. I'll use the claude-md-auditor agent to update the documentation to include the new files and patterns.\"\\n <commentary>\\n A new feature was added with multiple new files across components, hooks, and store slices. The CLAUDE.md files likely need updating to document these additions. Use the Task tool to launch the claude-md-auditor agent.\\n </commentary>\\n\\n- Example 3:\\n user: \"Rename isRealUserMessage to isParsedRealUserMessage across the codebase\"\\n assistant: \"The rename is complete across all source and test files. Now I'll launch the claude-md-auditor agent to update any documentation references to the old function name.\"\\n <commentary>\\n A function was renamed which is likely documented in CLAUDE.md type guard tables and conventions sections. Use the Task tool to launch the claude-md-auditor agent to fix stale references.\\n </commentary>\\n\\n- Example 4:\\n user: \"Can you audit the CLAUDE.md files to make sure they're up to date?\"\\n assistant: \"I'll launch the claude-md-auditor agent to systematically verify all documentation against the actual codebase.\"\\n <commentary>\\n The user explicitly requested a documentation audit. Use the Task tool to launch the claude-md-auditor agent.\\n </commentary>
Use this agent when the user wants to fix all code quality issues in the project, including linting, formatting, and unused code detection. This agent runs `pnpm fix` followed by `pnpm quality` in a loop, delegating each iteration to a subagent, until all issues are resolved.\\n\\nExamples:\\n\\n- User: \"Fix all the quality issues\"\\n Assistant: \"I'll launch the quality-fixer agent to iteratively fix all linting, formatting, and quality issues.\"\\n (Uses Task tool to launch quality-fixer agent)\\n\\n- User: \"Run quality checks and fix everything\"\\n Assistant: \"Let me use the quality-fixer agent to handle that.\"\\n (Uses Task tool to launch quality-fixer agent)\\n\\n- User: \"Make sure the code passes all checks\"\\n Assistant: \"I'll use the quality-fixer agent to ensure all quality checks pass.\"\\n (Uses Task tool to launch quality-fixer agent)\\n\\n- After completing a large refactor or feature implementation:\\n Assistant: \"Now that the changes are complete, let me launch the quality-fixer agent to ensure everything passes quality checks.\"\\n (Uses Task tool to launch quality-fixer agent)
ChatGroup architecture — how conversation data flows from raw JSONL to rendered chat groups. Use when working on UserGroup, AIGroup, SystemGroup, display items, tool linking, chunks, or the rendering hierarchy.
Design system and visual language — theming, CSS variables, Tailwind config, component styling patterns, icon usage, animations, and z-index layers. Use when creating or modifying UI components, working with the dark/light theme, or debugging visual issues.
Explains what "Visible Context" is — the 6 trackable token categories, what falls outside tracking, how it's displayed, and why it matters. Use when someone asks about visible context, token attribution, or context window usage.
Markdown search logic — how in-session and cross-session search works. Use when working on SearchBar, search highlighting, searchHighlightUtils, markdownTextSearch, or SessionSearcher.