# How to Configure Vim-Style Keyboard Navigation Mappings in Claudian

> Master Claudian with Vim-style navigation. Learn to configure key mappings for efficient chat scrolling and input focus. Customize your workflow today.

- Repository: [YishenTu/claudian](https://github.com/YishenTu/claudian)
- Tags: how-to-guide
- Published: 2026-03-17

---

**Claudian implements a dedicated Vim-style navigation layer that lets you scroll chat history and focus the input box using single-character key bindings, customizable through a simple `map <key> <action>` syntax.**

Claudian is an open-source chat interface that brings Vim-style keyboard navigation mappings to AI conversations. The implementation spans three core modules that handle settings persistence, text parsing, and real-time DOM event handling, allowing you to navigate entirely without touching the mouse.

## How Vim-Style Navigation Works in Claudian

The navigation system relies on a coordinated trio of components that transform user-defined text mappings into live keyboard shortcuts.

### The Settings Layer

Global defaults reside in [`src/core/types/settings.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/types/settings.ts) (lines 338-344), where the `KeyboardNavigationSettings` interface defines three configurable keys:

- `scrollUpKey`: Default `"w"`
- `scrollDownKey`: Default `"s"`  
- `focusInputKey`: Default `"i"`

These values initialize the navigation state when Claudian loads.

### The Mapping Parser

The [`src/features/settings/keyboardNavigation.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/settings/keyboardNavigation.ts) file (lines 6-60) contains the serialization logic. The `buildNavMappingText` function converts settings objects into human-readable text blocks, while `parseNavMappings` validates user input. The parser enforces strict rules: each line must follow `map <key> <action>`, keys must be single characters, and duplicates are rejected.

### The Runtime Controller

`NavigationController` in [`src/features/chat/controllers/NavigationController.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/chat/controllers/NavigationController.ts) (lines 80-107) registers DOM listeners with `capture: true` to intercept keystrokes before they bubble. It normalizes keys using `e.key.toLowerCase()`, compares them against the current settings object, and triggers either smooth scrolling via `requestAnimationFrame` or focuses the textarea. It also handles the **Escape** key to exit insert mode, completing the Vim workflow.

## Configuring Your Keyboard Mappings

You can customize bindings through three methods, all using the same `map <key> <action>` format where valid actions are `scrollUp`, `scrollDown`, and `focusInput`.

### Method 1: Programmatic Configuration

Use the `parseNavMappings` utility to validate and apply mappings from code:

```typescript
import { parseNavMappings } from '@/features/settings/keyboardNavigation';

// Define custom Vim bindings (H for up, J for down, Enter to insert)
const mappingText = `
map h scrollUp
map j scrollDown
map Enter focusInput
`;

const { settings, error } = parseNavMappings(mappingText);

if (error) {
  console.error('Validation failed:', error);
} else {
  // Persist to Claudian's data layer
  await this.saveData({ keyboardNavigation: settings });
}

```

The function returns a `KeyboardNavigationSettings` object only if all lines pass validation.

### Method 2: Direct settings.json Editing

For manual configuration, edit your [`settings.json`](https://github.com/YishenTu/claudian/blob/main/settings.json) file directly:

```json
{
  "keyboardNavigation": {
    "scrollUpKey": "h",
    "scrollDownKey": "j",
    "focusInputKey": "Enter"
  }
}

```

Claudian reloads these values on startup, and `NavigationController` picks them up via its `getSettings` callback.

### Method 3: Generating Mapping Text

To inspect or debug current mappings, use `buildNavMappingText` to serialize your settings:

```typescript
import { buildNavMappingText } from '@/features/settings/keyboardNavigation';
import type { KeyboardNavigationSettings } from '@/core/types/settings';

const current: KeyboardNavigationSettings = {
  scrollUpKey: 'h',
  scrollDownKey: 'j',
  focusInputKey: 'Enter',
};

const block = buildNavMappingText(current);
console.log(block);
// Output:
// map h scrollUp
// map j scrollDown
// map Enter focusInput

```

## Extending Navigation Actions (Advanced)

To add custom actions beyond the three defaults, modify the `NAV_ACTIONS` constant in [`src/features/settings/keyboardNavigation.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/settings/keyboardNavigation.ts) and add corresponding logic in `NavigationController.handleMessagesKeydown`:

```typescript
// In keyboardNavigation.ts
const NAV_ACTIONS = ['scrollUp', 'scrollDown', 'focusInput', 'jumpToEnd'] as const;

// In NavigationController.ts
if (key === settings.jumpToEndKey?.toLowerCase()) {
  e.preventDefault();
  const messagesEl = this.deps.getMessagesEl();
  messagesEl.scrollTop = messagesEl.scrollHeight;
  return;
}

```

You must also add `jumpToEndKey` to the `KeyboardNavigationSettings` type definition and default values in [`src/core/types/settings.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/types/settings.ts).

## Summary

- **Vim-style navigation** in Claudian uses single-character mappings for scroll and focus actions, defaulting to `w`/`s` for scrolling and `i` for insert mode.
- **Configuration storage** lives in [`src/core/types/settings.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/types/settings.ts), with runtime handling in [`NavigationController.ts`](https://github.com/YishenTu/claudian/blob/main/NavigationController.ts).
- **Text-based mappings** follow Vim's `map <key> <action>` syntax and are parsed by `parseNavMappings` in [`src/features/settings/keyboardNavigation.ts`](https://github.com/YishenTu/claudian/blob/main/src/features/settings/keyboardNavigation.ts).
- **Validation** ensures unique, single-character keys and known actions only.
- **Advanced users** can extend `NAV_ACTIONS` to add custom navigation behaviors like jumping to conversation end.

## Frequently Asked Questions

### What are the default vim-style keys in Claudian?

By default, Claudian uses `w` to scroll up, `s` to scroll down, and `i` to focus the input textarea (mimicking Vim's insert mode). These defaults are defined in [`src/core/types/settings.ts`](https://github.com/YishenTu/claudian/blob/main/src/core/types/settings.ts) and can be overridden through any configuration method.

### How do I disable vim-style navigation entirely?

While there is no explicit "disable" toggle, you can effectively disable the feature by mapping the scroll and focus actions to impossible keys (such as empty strings or unused function keys) in your [`settings.json`](https://github.com/YishenTu/claudian/blob/main/settings.json), or by ensuring no `map` entries exist in the navigation settings block, which prevents `NavigationController` from matching any keystrokes.

### Can I use multi-character keys or key combinations for navigation?

No. According to the validation logic in `parseNavMappings`, each key must be a single character. The implementation in `NavigationController` compares the pressed key directly against single-character strings in `settings.scrollUpKey`, `settings.scrollDownKey`, and `settings.focusInputKey`, making multi-character sequences or modifiers like `Ctrl` unsupported in the current architecture.

### Why aren't my new keyboard mappings working immediately?

Changes require reloading the settings object that `NavigationController` consumes via its `getSettings` callback. If editing [`settings.json`](https://github.com/YishenTu/claudian/blob/main/settings.json), restart the plugin or trigger a settings refresh. If using the programmatic API, ensure you call `saveData` to persist the new `KeyboardNavigationSettings` object so the controller receives the updated values on its next settings poll.