Spiral Editor
A Helix-style modal editor for the browser, built with TypeScript.
Features
- Selection-first editing - Like Helix, selections are primary
- Modal editing - Normal, Insert, and Select modes
- Multiple cursors - Full multi-selection support
- Pure state - Editor state is immutable and serializable
- No contenteditable - Clean DOM-based rendering
Quick Start
pnpm install
pnpm dev
Open http://localhost:3000
Key Bindings
Movement (Normal Mode)
| Key |
Action |
h/j/k/l |
Move cursor left/down/up/right |
w |
Select next word |
b |
Select previous word |
e |
Select to end of word |
W |
Select next WORD (whitespace-separated) |
B |
Select previous WORD |
E |
Select to end of WORD |
H/K/L |
Extend selection left/up/right by character |
g h |
Go to line start |
g l |
Go to line end |
g g |
Go to document start |
g e |
Go to document end |
0 / $ |
Go to line start / end |
Ctrl+u |
Half page up (with 5-line scrolloff) |
Ctrl+d |
Half page down (with 5-line scrolloff) |
PageUp/PageDown |
Full page scroll (with scrolloff) |
Select Mode (v)
In select mode, all movement commands extend the selection instead of moving it.
| Key |
Action |
h/j/k/l |
Extend selection left/down/up/right |
w/b/e |
Extend by word |
W/B/E |
Extend by WORD |
| Any movement |
Extends the selection |
Mode Switching
| Key |
Action |
i |
Insert mode (before cursor) |
a |
Insert mode (after cursor) |
I |
Insert at line start (first non-space) |
A |
Insert at line end |
v |
Select mode |
Esc |
Return to normal mode |
Selection
| Key |
Action |
x |
Select line |
X |
Extend selection to full lines |
% |
Select all |
; |
Collapse selection to cursor |
m |
Select matching bracket pair |
Editing
| Key |
Action |
d |
Delete selection |
c |
Change selection (delete + insert) |
y |
Yank (copy) selection |
p |
Paste |
o |
Open line below |
O |
Open line above |
J |
Join lines |
~ |
Toggle case |
D |
Delete to line end |
C |
Change to line end |
> / < |
Indent / dedent |
Ctrl+z |
Undo |
Ctrl+Shift+z |
Redo |
Insert Mode
| Key |
Action |
Esc |
Return to normal mode |
Backspace |
Delete character before |
Delete |
Delete character after |
Enter |
New line |
Tab |
Insert spaces |
| Arrow keys |
Move cursor |
Architecture
src/
├── core/ # Data models (Document, Selection, State)
├── commands/ # Pure state transformations
├── keybindings/ # Key handling and mapping
├── view/ # DOM rendering
└── editor.ts # Main orchestrator
Design Principles
- Editor state is pure data - No DOM references in state
- Rendering is a projection - View derives from state
- All edits go through commands - Pure functions: State → State
- Selections are first-class - Always at least one selection
- Deterministic state transitions - Same input → same output
Development
pnpm dev # Start dev server
pnpm build # Build for production
pnpm test # Run tests
pnpm lint # Lint code
License
MIT