keybindings

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 4, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package keybindings ports the user-customizable keybinding layer from src/keybindings/ in CC. MVP: single-keystroke bindings, JSON config in ~/.claude/keybindings.json, resolved against bubbletea v2 KeyPressMsg (which exposes structured Code + Mod after the M-P upgrade).

Chord support (multi-keystroke like "ctrl+x ctrl+s") is intentionally deferred — CC source has it but the vast majority of CC's own defaultBindings.ts entries are single-keystroke, and chord state machines are easy to get wrong. We can add it later behind the same Resolver API.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func UserFilePath

func UserFilePath(claudeDir string) string

UserFilePath returns ~/.claude/keybindings.json (or the equivalent on Windows once internal/config supports XDG/AppData). For now we keep it simple — internal/config.UserDir is the source of truth for ~/.claude.

Types

type Binding

type Binding struct {
	Keystroke Keystroke
	Action    string
	Context   string
	Unbind    bool
}

Binding pairs a Keystroke with the action it triggers, scoped to a context. Action is empty when the user explicitly unbinds a default (Unbind == true).

func Defaults

func Defaults() []Binding

Defaults is a convenience that returns the parsed default bindings.

func LoadAll

func LoadAll(claudeDir string) ([]Binding, error)

LoadAll loads defaults + user bindings, with user entries appended last so they shadow defaults in the resolver's last-wins lookup.

func ParseBlocks

func ParseBlocks(blocks []Block) []Binding

ParseBlocks converts loaded JSON blocks into a flat []Binding list. The order is preserved, which matters: when multiple bindings match (e.g., user overrides default), the resolver picks the last one.

type Block

type Block struct {
	Context  string            `json:"context"`
	Bindings map[string]string `json:"bindings"`
	// Unbinds tracks keys explicitly set to null in JSON; we can't put
	// `null` in a map[string]string, so the loader splits these out.
	Unbinds []string `json:"-"`
}

Block is one entry in keybindings.json — a context plus a map of keystroke string → action (or null to unbind).

func DefaultBlocks

func DefaultBlocks() []Block

DefaultBlocks ports the subset of src/keybindings/defaultBindings.ts that conduit's TUI actually has handlers for. The set is intentionally narrower than CC: features descoped in the parity plan (KAIROS Brief, teammate preview, voice, message actions, transcript pager, attachments, diff dialog, model picker, scroll context) don't appear here. We add them back as those features land.

Order matters: later blocks shadow earlier ones for the same keystroke in the same context. Within a block, map iteration order is undefined, but each block's bindings are disjoint by construction so it doesn't matter.

func LoadUserFile

func LoadUserFile(path string) ([]Block, error)

LoadUserFile reads keybindings.json at path and returns parsed blocks. Missing file → no error, empty result. Returns a wrapped error if the JSON is malformed or contains an unknown shape.

func ParseUserJSON

func ParseUserJSON(data []byte) ([]Block, error)

ParseUserJSON is exposed for tests and for callers (like /doctor) that want to validate JSON without writing a file first.

type File

type File struct {
	Schema   string  `json:"$schema,omitempty"`
	Docs     string  `json:"$docs,omitempty"`
	Bindings []Block `json:"bindings"`
}

File is the top-level keybindings.json shape.

type Keystroke

type Keystroke struct {
	Key   string
	Ctrl  bool
	Alt   bool
	Shift bool
	Super bool
}

Keystroke is one parsed key chord — a key name plus modifiers. The key field is the lowercased canonical name: "a"-"z", "0"-"9", "enter", "escape", "tab", "space", "backspace", "delete", "up", "down", "left", "right", "pageup", "pagedown", "home", "end", or single printable characters like "/", "?".

func ParseKeystroke

func ParseKeystroke(input string) Keystroke

ParseKeystroke parses "ctrl+shift+k" into a Keystroke. Modifier aliases:

  • ctrl, control
  • alt, opt, option, meta (all collapse to Alt — terminals can't distinguish alt from meta on legacy keymaps)
  • shift
  • cmd, command, super, win (collapse to Super — only fires on terminals that emit kitty keyboard protocol)

Special key names: esc → escape, return → enter, space → " " (the canonical key name for spacebar in bubbletea v2 is "space", but we accept both).

func (Keystroke) String

func (k Keystroke) String() string

String renders a Keystroke back to its canonical string form, suitable for round-tripping through ParseKeystroke. Used in tests and for "current binding for action X" display in /help-style output.

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver matches incoming KeyPressMsg events against parsed bindings, scoped by active contexts (e.g. ["Chat", "Global"]).

func NewResolver

func NewResolver(bindings []Binding) *Resolver

NewResolver wraps a flat binding list. Bindings later in the slice shadow earlier ones for the same keystroke+context — that's how user overrides win over defaults.

func (*Resolver) Bindings

func (r *Resolver) Bindings() []Binding

Bindings returns the full binding list, suitable for display in /keybindings.

func (*Resolver) Resolve

func (r *Resolver) Resolve(msg tea.KeyPressMsg, activeContexts ...string) Result

Resolve looks up the action bound to msg in the given contexts. activeContexts is processed as a set; order doesn't matter.

Returns the LAST matching binding so user-supplied overrides shadow the defaults, mirroring src/keybindings/resolver.ts.

type Result

type Result struct {
	// Action is set when a non-unbind binding matched. Empty otherwise.
	Action string
	// Unbound is true when the matching binding was an explicit null —
	// the caller should NOT fall through to default behavior. This is how
	// a user disables a built-in shortcut.
	Unbound bool
	// Matched is true when any binding (action or unbind) matched.
	Matched bool
}

Result is the outcome of resolving a key press.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL