-
Notifications
You must be signed in to change notification settings - Fork 245
feat(extensions): add session-scoped keyboard modes #708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
benvinegar
merged 4 commits into
main
from
feat/session-scoped-extension-keyboard-modes
Aug 11, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
8eeb7ac
feat(extensions): add session keyboard modes
benvinegar e83836f
refactor(extensions): simplify keyboard mode lifecycle
benvinegar 8010e6e
feat(examples): add Vim command-line navigation
benvinegar ec38ded
fix(keyboard): preserve open-menu accelerators
benvinegar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "hunkdiff": minor | ||
| --- | ||
|
|
||
| Let extensions activate visible session-scoped keyboard modes that route keys through Hunk's public semantic commands. |
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # Vim navigation extension | ||
|
|
||
| A small Vim-style normal mode for Hunk's whole review stream. It demonstrates session keyboard modes and public semantic command execution without accessing scroll boxes, renderer objects, or viewport coordinates. | ||
|
|
||
| This example is **not bundled or loaded by Hunk**. Install it explicitly if you want it. | ||
|
|
||
| ## Try it from this checkout | ||
|
|
||
| ```bash | ||
| bun run src/main.tsx -- diff --extension ./examples/extensions/vim-navigation | ||
| ``` | ||
|
|
||
| Press `F6` or choose **Extensions → Toggle Vim navigation**. The persistent status badge shows when the mode owns review-level keys; click the badge, choose the host-owned exit menu item, or press `Esc` to leave. | ||
|
|
||
| ## Install it globally | ||
|
|
||
| ```bash | ||
| mkdir -p ~/.config/hunk/extensions | ||
| cp -R examples/extensions/vim-navigation ~/.config/hunk/extensions/ | ||
| ``` | ||
|
|
||
| ## Keys | ||
|
|
||
| | Key | Action | | ||
| | ------------------- | ----------------------------------------------------------- | | ||
| | `j` / `k` | Move the current review line down/up | | ||
| | `[` / `]` | Move to the previous/next hunk | | ||
| | `gg` / `G` | Jump to the start/end of the review | | ||
| | `zt` / `zz` / `zb` | Align the current line at the top/center/bottom | | ||
| | `Ctrl-D` / `Ctrl-U` | Move down/up by half pages | | ||
| | positive digits | Prefix the next relative motion, for example `5j` or `3]` | | ||
| | `:` | Open the host-rendered Vim command line | | ||
| | `Esc` | Exit the mode (host-owned; the extension never receives it) | | ||
| | everything else | Pass through to normal Hunk routing | | ||
|
|
||
| Counts are parsed by the extension and capped at 10,000. Once a normal-mode sequence resolves, the extension calls `ctx.commands.execute(id, { count })` exactly once, so Hunk applies movement atomically. A bare `0` passes to Hunk's normal layout shortcut; `0` can extend a count that already began with `1`–`9`. | ||
|
|
||
| Pressing `:` passes the key to the example's registered command, which opens `ctx.dialogs.input()`. That focused host dialog captures typed keys ahead of the still-active session mode until Enter submits or Escape cancels. The deliberately small Ex-style command set is: | ||
|
|
||
| | Command | Action | | ||
| | --------- | ------------------------------- | | ||
| | `:top` | Jump to the start of the review | | ||
| | `:bottom` | Jump to the end of the review | | ||
|
|
||
| Unsupported commands produce an attributed warning. Absolute source-line commands such as Vim's `:100` are intentionally absent because Hunk does not expose source-line targeting as a public semantic command; relative counted movement such as `100j` remains available in normal mode. | ||
|
|
||
| The example enables Hunk's host-owned current-line marker on entry so the `z*` alignment commands have a target. It resets all pending prefix/count state on entry and exit. Invalid continuations clear pending state and pass the current key back to Hunk. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import type { HunkExtensionAPI } from "hunkdiff/extension"; | ||
| import { createVimNavigationState, executeVimCommand } from "./state"; | ||
|
|
||
| export default function (hunk: HunkExtensionAPI) { | ||
| let navigation = createVimNavigationState({ execute: () => false }); | ||
|
|
||
| hunk.registerKeyboardMode({ | ||
| id: "normal", | ||
| title: "Vim navigation", | ||
| onEnter(ctx) { | ||
| navigation = createVimNavigationState(ctx.commands); | ||
| // Alignment commands need a current-line target, so make the host-owned marker visible. | ||
| ctx.commands.execute("hunk.view.cursorLineRow"); | ||
| }, | ||
| onExit() { | ||
| navigation.reset(); | ||
| }, | ||
| onKey(key) { | ||
| return navigation.handleKey(key); | ||
| }, | ||
| }); | ||
|
|
||
| hunk.registerCommand( | ||
| { id: "command-line", title: "Open Vim command line", key: ":" }, | ||
| async (ctx) => { | ||
| if (!ctx.keyboardModes.isActive("normal")) { | ||
| ctx.notify("Enter Vim navigation before opening its command line", "info"); | ||
| return; | ||
| } | ||
|
|
||
| const input = await ctx.dialogs.input({ | ||
| title: "Vim command (:)", | ||
| placeholder: "top or bottom", | ||
| }); | ||
| if (input === null || !ctx.keyboardModes.isActive("normal")) return; | ||
|
|
||
| const result = executeVimCommand(input, ctx.commands); | ||
| if (result === "unknown") { | ||
| ctx.notify(`Unknown Vim command "${input.trim()}"`, "warning"); | ||
| } | ||
| }, | ||
| ); | ||
|
|
||
| hunk.registerCommand({ id: "toggle", title: "Toggle Vim navigation", key: "f6" }, (ctx) => { | ||
| if (ctx.keyboardModes.isActive("normal")) { | ||
| ctx.keyboardModes.exitMode(); | ||
| return; | ||
| } | ||
|
|
||
| ctx.keyboardModes.enterMode("normal"); | ||
| }); | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "name": "hunk-vim-navigation-extension", | ||
| "private": true, | ||
| "hunk": { | ||
| "extensions": [ | ||
| "./index.ts" | ||
| ] | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new Vim example and keyboard-mode implementation use two-space indentation and double-quoted strings throughout, conflicting with the repository requirement for four-space indentation and single quotes and establishing an inconsistent pattern for future extension examples.
Context Used: guidelines.mdc Cursor rule (source)
Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No change here. Hunk's repository-local
AGENTS.mdmakesoxfmtauthoritative, and this code matches the existing two-space/double-quote format enforced bybun run format:check. The cited style rule belongs to the separatemodemrepository rather thanmodem-dev/hunk.Responded by Pi using openai-codex/gpt-5.6-sol.
This comment was generated by Pi using gpt-5.6-sol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right — I misattributed a style rule from the
modemrepository tomodem-dev/hunk. Sinceoxfmtis the authoritative formatter here andbun run format:checkpasses, the two-space / double-quote style is correct and my comment was wrong.Shall I remember this as a rule: "In
modem-dev/hunk, treatoxfmt(enforced viabun run format:check) as the authoritative formatter — do not flag two-space indentation or double-quoted strings as style violations"?