fix: drop vim mode, pin CodeMirror to dodge esm.sh outage
esm.sh's build backend was returning HTTP 500 for unbuilt @codemirror/*@6 version ranges (out of disk). A failed top-level module import aborts the whole <script type=module>, so fillLangs()/loadModels() never ran and the language/model dropdowns came up empty despite a healthy backend. - Pin CodeMirror imports to already-built versions (view@6.36.0, state@6.7.0, commands@6.10.4), which esm.sh serves fine during the outage. - Remove vim editing mode entirely (@replit/codemirror-vim import, vim() extension, Vim.mapCommand H/L bindings, .cm-vim-panel CSS). The document-level Shift-H/L focus cycling is retained. - Bump to 0.4.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,14 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.4.0] - 2026-07-05
|
||||
|
||||
### Removed
|
||||
- Vim editing mode. Dropped the `@replit/codemirror-vim` import and its `Vim.mapCommand` H/L focus bindings. The document-level Shift-H/L focus cycling across the dropdowns/buttons is retained (it never depended on vim). One fewer esm.sh dependency.
|
||||
|
||||
### Fixed
|
||||
- Empty language and model dropdowns when esm.sh cannot serve an unpinned `@codemirror/*@6` range (their build backend was returning HTTP 500 on unbuilt versions). A failed top-level module import aborts the whole `<script type="module">`, so `fillLangs()`/`loadModels()` never ran despite a healthy backend. CodeMirror imports are now pinned to already-built versions (`view@6.36.0`, `state@6.7.0`, `commands@6.10.4`).
|
||||
|
||||
## [0.3.0] - 2026-06-09
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "finrod"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
description = "Translator UI backed by Ollama"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
|
||||
@@ -474,13 +474,6 @@
|
||||
}
|
||||
.cm-scroller { padding: 0.6rem 0.75rem; }
|
||||
.cm-content { caret-color: var(--accent); }
|
||||
/* vim's command-line / status hint inherits readable colors */
|
||||
.cm-vim-panel {
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
border-top: 1px solid var(--border);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
@@ -550,10 +543,9 @@
|
||||
</button>
|
||||
|
||||
<script type="module">
|
||||
import { EditorView, keymap, placeholder } from "https://esm.sh/@codemirror/view@6";
|
||||
import { EditorState } from "https://esm.sh/@codemirror/state@6";
|
||||
import { history, defaultKeymap, historyKeymap } from "https://esm.sh/@codemirror/commands@6";
|
||||
import { vim, Vim } from "https://esm.sh/@replit/codemirror-vim@6";
|
||||
import { EditorView, keymap, placeholder } from "https://esm.sh/@codemirror/view@6.36.0";
|
||||
import { EditorState } from "https://esm.sh/@codemirror/state@6.7.0";
|
||||
import { history, defaultKeymap, historyKeymap } from "https://esm.sh/@codemirror/commands@6.10.4";
|
||||
|
||||
const LANGUAGES = [
|
||||
"English", "Portuguese (Brazil)", "Portuguese (Portugal)", "Spanish",
|
||||
@@ -975,18 +967,13 @@
|
||||
if (file) runOCR(file);
|
||||
});
|
||||
|
||||
// --- CodeMirror editor (vim mode on real keyboards only) ----------------
|
||||
// --- CodeMirror editor --------------------------------------------------
|
||||
const isCoarsePointer = window.matchMedia("(pointer: coarse)").matches;
|
||||
let typingTimer;
|
||||
const editor = new EditorView({
|
||||
parent: document.getElementById("input"),
|
||||
state: EditorState.create({
|
||||
extensions: [
|
||||
// vim() MUST be first per @replit/codemirror-vim docs so its keymap
|
||||
// takes precedence. Skipped on touch devices — modal editing without
|
||||
// a hardware Esc key on a virtual keyboard is unusable, and IME
|
||||
// composition fights the vim keymap.
|
||||
...(isCoarsePointer ? [] : [vim()]),
|
||||
history(),
|
||||
keymap.of([
|
||||
{ key: "Mod-Enter", run: () => { translate(); return true; } },
|
||||
@@ -1214,10 +1201,8 @@
|
||||
goBtn.addEventListener("click", translate);
|
||||
|
||||
// Shift-H / Shift-L cycle focus across controls (vim-style Shift-Tab /
|
||||
// Tab). Two paths kept in sync:
|
||||
// 1. Document keydown listener — fires when focus is on a select / button
|
||||
// 2. Vim normal-mode mapping (below) — fires when focus is in the editor
|
||||
// Together they make Shift-H/L work everywhere, including escaping the editor.
|
||||
// Tab), for focus on a select / button. Inside the editor they type
|
||||
// normally.
|
||||
const focusables = [
|
||||
{ el: sourceSel, focus: () => sourceSel.focus() },
|
||||
{ el: targetSel, focus: () => targetSel.focus() },
|
||||
@@ -1239,23 +1224,12 @@
|
||||
if (!e.shiftKey) return;
|
||||
const k = e.key.toLowerCase();
|
||||
if (k !== "h" && k !== "l") return;
|
||||
// Inside the editor, vim's mapCommand handles H/L (see below). Letting
|
||||
// the event bubble here would double-jump.
|
||||
// Inside the editor, let H/L type normally.
|
||||
if (editor.dom.contains(document.activeElement)) return;
|
||||
e.preventDefault();
|
||||
moveFocus(k === "l" ? 1 : -1);
|
||||
});
|
||||
|
||||
// Override vim's H/L (normally "viewport top/bottom") so they exit the
|
||||
// editor to the previous/next control, matching the Shift-H/L contract
|
||||
// the document handler uses elsewhere.
|
||||
if (!isCoarsePointer) {
|
||||
Vim.defineAction("finrodFocusPrev", () => moveFocus(-1));
|
||||
Vim.defineAction("finrodFocusNext", () => moveFocus(1));
|
||||
Vim.mapCommand("H", "action", "finrodFocusPrev", {}, { context: "normal" });
|
||||
Vim.mapCommand("L", "action", "finrodFocusNext", {}, { context: "normal" });
|
||||
}
|
||||
|
||||
// Side drawer (Settings) wiring — opens from the top-left hamburger.
|
||||
const drawerEl = document.getElementById("drawer");
|
||||
const drawerBackdrop = document.getElementById("drawer-backdrop");
|
||||
|
||||
Reference in New Issue
Block a user