diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs
index 16c7ec99e..9b217cfed 100644
--- a/packages/codemirror/keybindings.mjs
+++ b/packages/codemirror/keybindings.mjs
@@ -6,7 +6,6 @@ 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, commands } from 'codemirror-helix';
import { logger } from '@strudel/core';
const vscodePlugin = ViewPlugin.fromClass(
@@ -21,70 +20,6 @@ const vscodePlugin = ViewPlugin.fromClass(
);
const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []);
-function replEval(view) {
- try {
- // Dispatch a dedicated evaluate event first
- let handled = false;
- try {
- const ev = new CustomEvent('repl-evaluate', { detail: { source: 'vim', view }, cancelable: true });
- handled = document.dispatchEvent(ev) === false; // false means preventDefault was called
- } catch (e) {
- console.error('Error dispatching repl-evaluate event', e);
- }
- if (handled) {
- return;
- }
- // Try Ctrl+Enter first if not handled by custom event
- const ctrlEnter = new KeyboardEvent('keydown', {
- key: 'Enter',
- code: 'Enter',
- ctrlKey: true,
- bubbles: true,
- cancelable: true,
- });
- view?.dom?.dispatchEvent?.(ctrlEnter);
- // If not handled (no handler called preventDefault), try Alt+Enter as
- // fallback
- if (!ctrlEnter.defaultPrevented) {
- const altEnter = new KeyboardEvent('keydown', {
- key: 'Enter',
- code: 'Enter',
- altKey: true,
- bubbles: true,
- cancelable: true,
- });
- view?.dom?.dispatchEvent?.(altEnter);
- }
- } catch (e) {
- console.error('Error dispatching repl evaluation event', e);
- }
-}
-
-function replStop(view) {
- try {
- // First try dispatching our custom stop event, then fallback to Alt+.
- let handled = false;
- try {
- const ev = new CustomEvent('repl-stop', { detail: { source: 'vim', view }, cancelable: true });
- handled = document.dispatchEvent(ev) === false;
- } catch (e) {
- console.error('Error dispatching repl-stop event', e);
- }
- if (!handled) {
- const altDot = new KeyboardEvent('keydown', {
- key: '.',
- code: 'Period',
- altKey: true,
- bubbles: true,
- cancelable: true,
- });
- view?.dom?.dispatchEvent?.(altDot);
- }
- } catch (e) {
- console.error('Error dispatching repl stop event', e);
- }
-}
-
// Map Vim :w to trigger the same action as evaluation. We dispatch a custom
// event 'repl-evaluate' that the editor listens for, and also simulate
// Ctrl+Enter/Alt+Enter as a fallback. We log to the Strudel logger so it
@@ -112,9 +47,29 @@ try {
// :q to pause/stop
Vim.defineEx('quit', 'q', (cm) => {
- const view = cm.cm6;
- logger('[vim] :q — stopping repl');
- replStop(view);
+ try {
+ const view = cm?.view || cm;
+ // First try dispatching our custom stop event, then fallback to Alt+.
+ let handled = false;
+ try {
+ const ev = new CustomEvent('repl-stop', { detail: { source: 'vim', view }, cancelable: true });
+ handled = document.dispatchEvent(ev) === false;
+ } catch (e) {
+ console.error('Error dispatching repl-stop event', e);
+ }
+ if (!handled) {
+ const altDot = new KeyboardEvent('keydown', {
+ key: '.',
+ code: 'Period',
+ altKey: true,
+ bubbles: true,
+ cancelable: true,
+ });
+ view?.dom?.dispatchEvent?.(altDot);
+ }
+ } catch (e) {
+ console.error('Error dispatching :q stop event', e);
+ }
});
// :w to evaluate
@@ -128,7 +83,38 @@ try {
} catch (e) {
console.error('Error logging Vim :w evaluation', e);
}
- replEval(view);
+ // Dispatch a dedicated evaluate event first
+ let handled = false;
+ try {
+ const ev = new CustomEvent('repl-evaluate', { detail: { source: 'vim', view }, cancelable: true });
+ handled = document.dispatchEvent(ev) === false; // false means preventDefault was called
+ } catch (e) {
+ console.error('Error dispatching repl-evaluate event', e);
+ }
+ if (handled) {
+ return;
+ }
+ // Try Ctrl+Enter first if not handled by custom event
+ const ctrlEnter = new KeyboardEvent('keydown', {
+ key: 'Enter',
+ code: 'Enter',
+ ctrlKey: true,
+ bubbles: true,
+ cancelable: true,
+ });
+ view?.dom?.dispatchEvent?.(ctrlEnter);
+ // If not handled (no handler called preventDefault), try Alt+Enter as
+ // fallback
+ if (!ctrlEnter.defaultPrevented) {
+ const altEnter = new KeyboardEvent('keydown', {
+ key: 'Enter',
+ code: 'Enter',
+ altKey: true,
+ bubbles: true,
+ cancelable: true,
+ });
+ view?.dom?.dispatchEvent?.(altEnter);
+ }
} catch (e) {
console.error('Error dispatching :w evaluation event', e);
}
@@ -138,49 +124,11 @@ try {
console.error('Vim ex command setup failed (defineEx missing or Vim unavailable)', e);
}
-// Map Helix :w to trigger the same action as evaluation. We dispatch a custom
-// event 'repl-evaluate' that the editor listens for, and also simulate
-// Ctrl+Enter/Alt+Enter as a fallback. We log to the Strudel logger so it
-// appears in the Console panel.
-const helixCommands = commands.of([
- {
- // :w to evaluate
- name: 'write',
- aliases: ['w'],
- help: 'Repl-eval',
- handler(view, args) {
- try {
- view?.focus?.(); // Let the app know this came from Helix :w
- logger('[helix] :w — evaluating code');
- replEval(view);
- } catch (e) {
- console.error('Error dispatching helix :w evaluation event', e);
- }
- },
- },
- {
- // :q to pause/stop
- name: 'quit',
- aliases: ['q'],
- help: 'Repl-stop',
- handler(view, args) {
- try {
- view?.focus?.(); // Let the app know this came from Helix :q
- logger('[helix] :q — stopping repl');
- replStop(view);
- } catch (e) {
- console.error('Error dispatching helix :q stop event', e);
- }
- },
- },
-]);
-
const keymaps = {
vim,
emacs,
codemirror: () => keymap.of(defaultKeymap),
vscode: vscodeExtension,
- helix: () => [helix(), helixCommands],
};
export { Vim } from '@replit/codemirror-vim';
diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json
index d2432ef83..a18019e37 100644
--- a/packages/codemirror/package.json
+++ b/packages/codemirror/package.json
@@ -44,7 +44,6 @@
"@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 9930ef857..20c96e9d2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -251,9 +251,6 @@ 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
@@ -3660,15 +3657,6 @@ 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==}
@@ -11411,14 +11399,6 @@ 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
deleted file mode 100644
index 36f058985..000000000
--- a/website/src/pages/technical-manual/helix.mdx
+++ /dev/null
@@ -1,85 +0,0 @@
----
-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 a385fd48a..f01c1669c 100644
--- a/website/src/repl/components/panel/SettingsTab.jsx
+++ b/website/src/repl/components/panel/SettingsTab.jsx
@@ -235,7 +235,7 @@ function MainSettingsContent({ started }) {
settingsMap.setKey('keybindings', keybindings)}
- items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs', helix: 'Helix', vscode: 'VSCode' }}
+ items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs', vscode: 'VSCode' }}
>