diff --git a/CHANGELOG.md b/CHANGELOG.md
index a13e379d9..7c73c39e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
NOTE: you can generate this with `node warm.js`. it might still not be perfectly sorted...
+## december 2025
+
+- 2025-12-08 Add Helix keybindings support to REPL editor - Added `codemirror-helix` package integration, enabling Helix editor keybindings as a new option in the REPL settings alongside Vim, Emacs, and VSCode modes
+
## november 2025
- 2025-11-27T22:03:53+01:00 [perf] in `noise`, let noiseMix do the disconnect when it exists by jeromew in: [#1783](https://codeberg.org/uzu/strudel/pulls/1783)
diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs
index 81dc87a52..bc13f3234 100644
--- a/packages/codemirror/keybindings.mjs
+++ b/packages/codemirror/keybindings.mjs
@@ -6,6 +6,7 @@ import { emacs } from '@replit/codemirror-emacs';
import { vim, Vim } from '@replit/codemirror-vim';
// import { vim } from './vim_test.mjs';
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
+import { helix } from 'codemirror-helix';
import { logger } from '@strudel/core';
const vscodePlugin = ViewPlugin.fromClass(
@@ -127,6 +128,7 @@ try {
const keymaps = {
vim,
emacs,
+ helix,
codemirror: () => keymap.of(defaultKeymap),
vscode: vscodeExtension,
};
diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json
index a3735491f..419464441 100644
--- a/packages/codemirror/package.json
+++ b/packages/codemirror/package.json
@@ -44,6 +44,7 @@
"@replit/codemirror-emacs": "^6.1.0",
"@replit/codemirror-vim": "^6.3.0",
"@replit/codemirror-vscode-keymap": "^6.0.2",
+ "codemirror-helix": "^0.5.0",
"@strudel/core": "workspace:*",
"@strudel/draw": "workspace:*",
"@strudel/tonal": "workspace:*",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 23903fcac..709a5b664 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,7 +41,7 @@ importers:
version: 2.2.7
'@vitest/coverage-v8':
specifier: 3.0.4
- version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))
+ version: 3.0.4(vitest@3.0.4)
'@vitest/ui':
specifier: ^3.0.4
version: 3.0.4(vitest@3.0.4)
@@ -221,6 +221,9 @@ importers:
'@tonaljs/tonal':
specifier: ^4.10.0
version: 4.10.0
+ codemirror-helix:
+ specifier: ^0.5.0
+ version: 0.5.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
nanostores:
specifier: ^0.11.3
version: 0.11.3
@@ -3536,6 +3539,15 @@ packages:
resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ codemirror-helix@0.5.0:
+ resolution: {integrity: sha512-hI56hf9VGz53H1YvL6H1GC7HtP6te8vX+MsIHaE9J7Q3PQ6KFapKtIRg6lqSH898ikHWpMCPu42r6HJN0IfVLA==}
+ peerDependencies:
+ '@codemirror/commands': ^6.0.0
+ '@codemirror/language': ^6.0.0
+ '@codemirror/search': ^6.0.0
+ '@codemirror/state': ^6.0.0
+ '@codemirror/view': ^6.0.0
+
collapse-white-space@2.1.0:
resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
@@ -10554,7 +10566,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@vitest/coverage-v8@3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0))':
+ '@vitest/coverage-v8@3.0.4(vitest@3.0.4)':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
@@ -11254,6 +11266,14 @@ snapshots:
cmd-shim@6.0.3: {}
+ codemirror-helix@0.5.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2):
+ dependencies:
+ '@codemirror/commands': 6.8.0
+ '@codemirror/language': 6.10.8
+ '@codemirror/search': 6.5.8
+ '@codemirror/state': 6.5.1
+ '@codemirror/view': 6.36.2
+
collapse-white-space@2.1.0: {}
color-convert@2.0.1:
diff --git a/website/src/pages/technical-manual/helix.mdx b/website/src/pages/technical-manual/helix.mdx
new file mode 100644
index 000000000..1de42d1e1
--- /dev/null
+++ b/website/src/pages/technical-manual/helix.mdx
@@ -0,0 +1,84 @@
+---
+title: Helix Keybindings
+layout: ../../layouts/MainLayout.astro
+---
+
+# Helix Keybindings in the REPL
+
+The Strudel REPL supports [Helix editor](https://helix-editor.com/) keybindings through the `codemirror-helix` extension. Helix is a post-modern modal text editor with a focus on selection-first editing and multiple cursors.
+
+## Enabling Helix Mode
+
+1. Open the Settings panel in the REPL (click the settings icon or use the keyboard shortcut)
+2. Find the "Keybindings" section
+3. Select "Helix" from the available options
+
+## Key Differences from Vim
+
+Helix uses a selection-first approach, which means:
+
+- **Selection → Action** (Helix) vs **Action → Motion** (Vim)
+- For example, to delete a word in Helix: `w` (select word) → `d` (delete)
+- In Vim, it would be: `dw` (delete word)
+
+## Common Helix Commands
+
+### Normal Mode
+
+- `i` — Enter insert mode before selection
+- `a` — Enter insert mode after selection
+- `v` — Enter visual/select mode
+- `w` — Select next word
+- `e` — Select to end of word
+- `b` — Select previous word
+- `x` — Select/extend line
+- `d` — Delete selection
+- `c` — Change selection (delete and enter insert mode)
+- `y` — Yank (copy) selection
+- `p` — Paste after selection
+- `u` — Undo
+- `U` — Redo
+- `/` — Search
+- `n` — Select next search match
+- `N` — Select previous search match
+
+### Movement
+
+- `h, j, k, l` — Move left, down, up, right
+- `gg` — Go to start of document
+- `ge` — Go to end of document
+- `{` / `}` — Move to previous/next paragraph
+- `%` — Match bracket
+
+### Multi-cursor
+
+- `C` — Duplicate cursor to line below
+- `Alt-C` — Duplicate cursor to line above
+- `,` — Remove primary cursor
+- `Alt-,` — Remove all secondary cursors
+
+## Strudel-Specific Features
+
+Currently, Helix mode in Strudel provides standard Helix keybindings. Unlike Vim mode, there are no custom Strudel-specific commands (like `:w` to evaluate) yet.
+
+To evaluate code while in Helix mode, use the standard shortcuts:
+- `Ctrl+Enter` or `Alt+Enter` — Evaluate code
+- `Alt+.` — Stop playback
+
+## Resources
+
+- [Helix Documentation](https://docs.helix-editor.com/)
+- [Helix Keymap](https://docs.helix-editor.com/keymap.html)
+- [codemirror-helix on npm](https://www.npmjs.com/package/codemirror-helix)
+- [codemirror-helix on GitLab](https://gitlab.com/_rvidal/codemirror-helix)
+
+## Notes
+
+- The `codemirror-helix` extension is described as an "initial version" and may not implement all Helix features
+- Behavior may differ slightly from the native Helix editor
+- If you encounter issues, you can switch back to other keybinding modes in Settings
+- The keybinding preference is saved in your browser's local storage
+
+## Contributing
+
+If you'd like to add Strudel-specific Helix commands (similar to Vim's `:w` for evaluate), contributions are welcome! See the implementation in `/packages/codemirror/keybindings.mjs` for reference.
diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx
index f46fa661c..3cc8d6390 100644
--- a/website/src/repl/components/panel/SettingsTab.jsx
+++ b/website/src/repl/components/panel/SettingsTab.jsx
@@ -220,7 +220,7 @@ export function SettingsTab({ started }) {
settingsMap.setKey('keybindings', keybindings)}
- items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs', vscode: 'VSCode' }}
+ items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs', helix: 'Helix', vscode: 'VSCode' }}
>