Last active
March 28, 2026 11:47
-
-
Save agoalofalife/1d79166f206679c3cfbf993963c92efd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| const readline = require('readline'); | |
| const { execSync } = require('child_process'); | |
| // Enable these key bindings for word navigation (Alt+Left/Right) | |
| const customKeyBindings = { | |
| // Alt+Left (move word left) - various terminal escape sequences | |
| '\x1bb': 'wordLeft', // Meta+b (Option as Meta) | |
| '\x1b[1;3D': 'wordLeft', // Alt+Left | |
| '\x1b[1;9D': 'wordLeft', // Alt+Left (iTerm2) | |
| // Alt+Right (move word right) | |
| '\x1bf': 'wordRight', // Meta+f (Option as Meta) | |
| '\x1b[1;3C': 'wordRight', // Alt+Right | |
| '\x1b[1;9C': 'wordRight', // Alt+Right (iTerm2) | |
| // Cmd+V paste (Terminal sends this when "Use Option as Meta" is off) | |
| '\x16': 'paste', // Ctrl+V fallback | |
| }; | |
| const rl = readline.createInterface({ | |
| input: process.stdin, | |
| output: process.stdout, | |
| prompt: '' | |
| }); | |
| // Clipboard functions for macOS | |
| function copyToClipboard(text) { | |
| try { | |
| execSync('pbcopy', { input: text }); | |
| return true; | |
| } catch (e) { | |
| return false; | |
| } | |
| } | |
| function pasteFromClipboard() { | |
| try { | |
| return execSync('pbpaste', { encoding: 'utf8' }); | |
| } catch (e) { | |
| return ''; | |
| } | |
| } | |
| // Handle custom key bindings | |
| process.stdin.on('keypress', (char, key) => { | |
| if (!key) return; | |
| const seq = key.sequence; | |
| const binding = customKeyBindings[seq]; | |
| if (binding === 'wordLeft') { | |
| // Move cursor to previous word boundary | |
| const line = rl.line; | |
| const cursor = rl.cursor; | |
| let newCursor = cursor; | |
| // Skip spaces, then skip word characters | |
| while (newCursor > 0 && line[newCursor - 1] === ' ') newCursor--; | |
| while (newCursor > 0 && line[newCursor - 1] !== ' ') newCursor--; | |
| // Move cursor | |
| process.stdout.write(`\x1b[${cursor - newCursor}D`); | |
| rl.cursor = newCursor; | |
| } | |
| if (binding === 'wordRight') { | |
| const line = rl.line; | |
| const cursor = rl.cursor; | |
| let newCursor = cursor; | |
| // Skip word characters, then skip spaces | |
| while (newCursor < line.length && line[newCursor] !== ' ') newCursor++; | |
| while (newCursor < line.length && line[newCursor] === ' ') newCursor++; | |
| if (newCursor > cursor) { | |
| process.stdout.write(`\x1b[${newCursor - cursor}C`); | |
| rl.cursor = newCursor; | |
| } | |
| } | |
| if (binding === 'paste' || (key.ctrl && key.name === 'v')) { | |
| const clipboard = pasteFromClipboard(); | |
| if (clipboard) { | |
| // Insert clipboard content at cursor position | |
| const before = rl.line.slice(0, rl.cursor); | |
| const after = rl.line.slice(rl.cursor); | |
| const firstLine = clipboard.split('\n')[0]; // Only first line | |
| rl.line = before + firstLine + after; | |
| rl.cursor = before.length + firstLine.length; | |
| // Redraw line | |
| process.stdout.clearLine(0); | |
| process.stdout.cursorTo(0); | |
| process.stdout.write(rl.line); | |
| process.stdout.cursorTo(rl.cursor); | |
| } | |
| } | |
| }); | |
| // Enable keypress events | |
| readline.emitKeypressEvents(process.stdin); | |
| if (process.stdin.isTTY) { | |
| process.stdin.setRawMode(true); | |
| } | |
| console.log("Start typing (Ctrl+C to exit):"); | |
| console.log(" Alt+← / Alt+→ - Jump by word"); | |
| console.log(" Ctrl+V - Paste from clipboard\n"); | |
| rl.on('line', (line) => { | |
| process.stdout.write('\x1b[1A\x1b[2K'); | |
| const timestamp = new Date().toLocaleTimeString(); | |
| console.log(`[${timestamp}] ${line}`); | |
| // Copy output to clipboard automatically (optional) | |
| // copyToClipboard(line); | |
| }); | |
| rl.on('close', () => { | |
| console.log('\nExiting...'); | |
| process.exit(0); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment