v0.2.0: vim bindings, Shift-H/L focus nav, hot-model default #3

Merged
jpnadas merged 8 commits from feature/vim-bindings into main 2026-06-09 15:22:41 +00:00
2 changed files with 146 additions and 14 deletions
Showing only changes of commit a058665b5b - Show all commits

View File

@@ -10,6 +10,7 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Added ### Added
- Vim bindings in the input via CodeMirror 6 + `@replit/codemirror-vim` (loaded from esm.sh, no build step). Supports `i`/`Esc`, `hjkl`, word/line motions, `dd`/`yy`/`p`, undo/redo, `/` search, `:` command line. Auto-disabled on touch devices (detected via `(pointer: coarse)`) where modal editing fights the virtual keyboard. - Vim bindings in the input via CodeMirror 6 + `@replit/codemirror-vim` (loaded from esm.sh, no build step). Supports `i`/`Esc`, `hjkl`, word/line motions, `dd`/`yy`/`p`, undo/redo, `/` search, `:` command line. Auto-disabled on touch devices (detected via `(pointer: coarse)`) where modal editing fights the virtual keyboard.
- `Shift-H` / `Shift-L` cycle focus across the page controls (source → target → model → editor → button) like `Shift-Tab` / `Tab`. Also overridden in vim normal mode (replacing vim's viewport-top/bottom default) so the same shortcut works to leave the editor. - `Shift-H` / `Shift-L` cycle focus across the page controls (source → target → model → editor → button) like `Shift-Tab` / `Tab`. Also overridden in vim normal mode (replacing vim's viewport-top/bottom default) so the same shortcut works to leave the editor.
- `p` / `P` in vim normal mode paste from the system clipboard at / before the cursor (vim's default registers are skipped — the realistic source for a translator is another app, not an in-editor yank).
- On page load, the model dropdown defaults to whichever model Ollama already has hot — skips the 5-15 s disk-reload pain on the first translation. After load it follows the user's manual selection. - On page load, the model dropdown defaults to whichever model Ollama already has hot — skips the 5-15 s disk-reload pain on the first translation. After load it follows the user's manual selection.
### Changed ### Changed

View File

@@ -513,12 +513,34 @@
// Override vim's H/L (normally "viewport top/bottom") so they exit the // Override vim's H/L (normally "viewport top/bottom") so they exit the
// editor to the previous/next control, matching the same Shift-H/L // editor to the previous/next control, matching the same Shift-H/L
// contract the document handler uses elsewhere. // contract the document handler uses elsewhere. Also override p/P in
// normal mode to paste from the system clipboard (vim's default p
// pastes from its internal register — useless when the source text
// comes from another app).
if (!isCoarsePointer) { if (!isCoarsePointer) {
Vim.defineAction("finrodFocusPrev", () => moveFocus(-1)); Vim.defineAction("finrodFocusPrev", () => moveFocus(-1));
Vim.defineAction("finrodFocusNext", () => moveFocus(1)); Vim.defineAction("finrodFocusNext", () => moveFocus(1));
Vim.mapCommand("H", "action", "finrodFocusPrev", {}, { context: "normal" }); Vim.mapCommand("H", "action", "finrodFocusPrev", {}, { context: "normal" });
Vim.mapCommand("L", "action", "finrodFocusNext", {}, { context: "normal" }); Vim.mapCommand("L", "action", "finrodFocusNext", {}, { context: "normal" });
async function pasteFromClipboard(after) {
try {
const text = await navigator.clipboard.readText();
if (!text) return;
const sel = editor.state.selection.main;
const pos = after ? sel.to : sel.from;
editor.dispatch({
changes: { from: pos, insert: text },
selection: { anchor: pos + text.length },
});
} catch (e) {
console.warn("Clipboard read failed:", e);
}
}
Vim.defineAction("finrodPasteAfter", () => pasteFromClipboard(true));
Vim.defineAction("finrodPasteBefore", () => pasteFromClipboard(false));
Vim.mapCommand("p", "action", "finrodPasteAfter", {}, { context: "normal" });
Vim.mapCommand("P", "action", "finrodPasteBefore", {}, { context: "normal" });
} }
fillLangs(); fillLangs();