Skip to content

feat(dispatch,llm): modules register their own default_bindings#70

Merged
fentas merged 1 commit into
masterfrom
feat/module-bindings
May 17, 2026
Merged

feat(dispatch,llm): modules register their own default_bindings#70
fentas merged 1 commit into
masterfrom
feat/module-bindings

Conversation

@fentas
Copy link
Copy Markdown
Owner

@fentas fentas commented May 17, 2026

Summary

Follow-up to PR #67 — modules now own their keybindings instead of having them centralized in defaults.zig.

How it works

  • Each module exposes pub const default_bindings: []const Binding = &.{ ... }.
  • Dispatcher comptime-concatenates them via all_default_bindings.
  • Proxy's stdin matcher checks user bindings first (so a user-listed override wins), then module defaults as fallback.
const matched_action = keymap.match(config.keymap.bindings, input) orelse
    keymap.match(D.allDefaultBindings(), input);

What changed

  • src/modules/llm.zig: gains pub const default_bindings = &.{ ... } — 14 entries (Alt+A/S/Shift+S/M/H/C/Shift+C/Ctrl+Shift+X + their kitty-kbd CSI-u siblings).
  • src/defaults.zig: shrinks. The LLM block is replaced by a comment pointing to the module. Core, module-agnostic bindings (Right/End/Ctrl+F/Ctrl+Tab/Ctrl+Right/Ctrl+Shift+I/Alt+i/Ctrl+Shift+D) stay.
  • src/dispatch.zig: new all_default_bindings const + allDefaultBindings() accessor.
  • src/proxy.zig: matcher consults both lists; renderHelp walks both for the Alt+H cheat-sheet.

Behavioral consequences

  • A user who removes atty.modules.llm from their modules tuple loses ALL the LLM bindings automatically — no manual cleanup of Keymap.bindings.
  • A user who adds a new module that ships bindings gets them without editing config.
  • Existing user configs are unaffected — the proxy still scans their Keymap.bindings first.

Test plan

  • zig build test — 480/480 pass (2 new dispatch tests covering walker semantics + empty case)
  • zig fmt --check — clean
  • Manual: build atty, press Alt+H — cheat-sheet still lists all LLM keys
  • Manual: remove LLM from modules tuple in config.zig, rebuild — Alt+C no longer fires

🤖 Generated with Claude Code

Modules now expose `pub const default_bindings: []const Binding`;
the dispatcher comptime-concatenates them into
`all_default_bindings`. The proxy's stdin matcher consults
user `config.keymap.bindings` first (user wins), then falls
back to `D.all_default_bindings` — so:

  - Users who don't enable a module get none of its bindings.
  - Users who enable a module get its bindings automatically
    without editing `config.keymap.bindings`.
  - Users who want to rebind a module key still can — list the
    override in `Keymap.bindings` and the first-match scan picks
    it before the module default.

LLM module's 14 default bindings (Alt+A/S/Shift+S/M/H/C/Shift+C +
Ctrl+Shift+X plus dual kitty-kbd encodings) moved from
`defaults.zig` into `atty.modules.llm.default_bindings`. defaults.zig
shrinks to the core, module-agnostic bindings (Right/End/Ctrl+F/
Ctrl+Tab/Ctrl+Right/Ctrl+Shift+I/Alt+i/Ctrl+Shift+D).

`renderHelp` walks both lists in the same order so the Alt+H
cheat-sheet still surfaces everything.

2 new dispatch tests cover: comptime-concat + skip non-declaring,
empty when no module declares.

`zig build test` (480 pass) + `zig fmt --check` clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fentas fentas merged commit 245e7e8 into master May 17, 2026
3 checks passed
@fentas fentas deleted the feat/module-bindings branch May 17, 2026 10:28
fentas added a commit that referenced this pull request May 17, 2026
docs: reflect recently-shipped features

`docs/llm.md`:
- New **Chat surfaces** section explaining Alt+C inline panel vs.
  Alt+Shift+C full overlay (same conversation ring, mutually
  exclusive cursor focus).
- New **Keybindings** section listing every shipped LLM binding +
  Alt+H cheat-sheet pointer.
- Configuration reference split into sections (Core / Chat surfaces
  / Persistence / Visual signals / Buffer sizes / Model struct) so
  the table doesn't overwhelm — and adds rows for `models`,
  `inline_chat_rows`, `overlay_open_policy`, `chat_persist_enabled`,
  `chat_persist_path`, `chat_persist_max_bytes`, `history_turns_max`,
  `max_turn_bytes`, `dialog_system_prompt`, `dialog_parse_retry_max`.
- Documents the `Model` struct with the per-model
  `history_turns_max` trim knob.

`docs/modules.md`:
- Adds `default_bindings`, `onResize`, `isInlineChatActive`,
  `extraReserveRows` to the hook list.
- New **default_bindings** section walks the module-owned keymap
  pattern (dispatcher concat + user-overrides-win precedence).
- New **extraReserveRows** section documents the inline-panel
  reservation contract used by the LLM module's Alt+C panel.
fentas added a commit that referenced this pull request May 17, 2026
…dead (#72)

Regression from #70: `default_bindings` sat at the top level of
`src/modules/llm.zig`, but `config.modules` actually stores the
RESULT of `llm.configure(.{...})` — i.e. the inner struct returned
by the factory. The dispatcher's `@hasDecl(M, "default_bindings")`
gate inspects the inner struct, so the top-level decl was invisible
and every Alt+letter LLM binding silently missed.

Fix: move the slice into the `configure()` return type alongside
`name`, `config`, the action handlers, etc. — same scope `@hasDecl`
walks for every other hook on the LLM module.

Regression test (`allDefaultBindings: picks up bindings from
configure()-style factory modules`) mirrors the real
`pub fn configure(cfg) type` pattern via a `FactoryModule(tag)`
fixture, so future module authors don't re-hit this trap.

`zig build test` (484 pass) + `zig fmt --check` clean.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot mentioned this pull request May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant