Last active
November 29, 2025 04:14
-
-
Save sairajchouhan/8086c05939b7409761526bc0b1b4b2bb 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
| // ==UserScript== | |
| // @name TUF Vim Mode | |
| // @namespace http://tampermonkey.net/ | |
| // @version 12.0 | |
| // @description Vim support for takeUforward code editor | |
| // @author You | |
| // @match https://*.takeuforward.org/* | |
| // @connect unpkg.com | |
| // @connect cdn.jsdelivr.net | |
| // @grant GM_xmlhttpRequest | |
| // @grant unsafeWindow | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| const pageWindow = unsafeWindow; | |
| const LOG_PREFIX = '%c[TUF VIM]'; | |
| const LOG_STYLE = 'background: #000; color: #0f0; font-weight: bold; padding: 2px 4px; border-radius: 2px;'; | |
| const URLS = [ | |
| 'https://unpkg.com/monaco-vim/dist/monaco-vim.min.js', | |
| 'https://cdn.jsdelivr.net/npm/monaco-vim/dist/monaco-vim.min.js' | |
| ]; | |
| const fetchScript = (url) => { | |
| return new Promise((resolve, reject) => { | |
| GM_xmlhttpRequest({ | |
| method: "GET", | |
| url: url, | |
| onload: (res) => (res.status === 200 ? resolve(res.responseText) : reject(`Status ${res.status}`)), | |
| onerror: (err) => reject(err) | |
| }); | |
| }); | |
| }; | |
| const loadVimEngine = async () => { | |
| let vimCode = null; | |
| for (const url of URLS) { | |
| try { | |
| vimCode = await fetchScript(url); | |
| break; | |
| } catch (err) { console.warn(`Failed ${url}`, err); } | |
| } | |
| if (!vimCode) throw new Error("Download failed."); | |
| const mockModule = { exports: {} }; | |
| const mockRequire = (dependency) => { | |
| if (dependency.includes('monaco-editor')) return pageWindow.monaco; | |
| return {}; | |
| }; | |
| const runner = new Function('module', 'exports', 'require', 'define', vimCode); | |
| runner(mockModule, mockModule.exports, mockRequire, undefined); | |
| return mockModule.exports || window.MonacoVim; | |
| }; | |
| const createPhantomNode = () => { | |
| let statusNode = document.getElementById('vim-phantom-node'); | |
| if (statusNode) return statusNode; | |
| statusNode = document.createElement('div'); | |
| statusNode.id = 'vim-phantom-node'; | |
| statusNode.style.cssText = 'display: none !important; width: 0; height: 0; overflow: hidden;'; | |
| document.body.appendChild(statusNode); | |
| return statusNode; | |
| }; | |
| const initVim = async () => { | |
| try { | |
| const MonacoVim = await loadVimEngine(); | |
| const phantomNode = createPhantomNode(); | |
| console.log(LOG_PREFIX, LOG_STYLE, 'Engine Loaded. Silent Mode Active.'); | |
| setInterval(() => { | |
| if (!pageWindow.monaco || !pageWindow.monaco.editor) return; | |
| const editors = pageWindow.monaco.editor.getEditors(); | |
| editors.forEach(editor => { | |
| if (editor._vimAttached) return; | |
| if (editor.getOption(pageWindow.monaco.editor.EditorOption.readOnly)) return; | |
| if (!pageWindow.monaco.Range || !pageWindow.monaco.KeyCode) return; | |
| try { | |
| const vimMode = MonacoVim.initVimMode(editor, phantomNode); | |
| editor._vimAttached = true; | |
| editor.layout(); | |
| } catch (e) { | |
| console.error('Vim Attach Error:', e); | |
| } | |
| }); | |
| }, 1000); | |
| } catch (err) { | |
| console.error('Vim Load Error:', err); | |
| } | |
| }; | |
| let attempts = 0; | |
| const checkLoader = setInterval(() => { | |
| attempts++; | |
| if (pageWindow.monaco) { | |
| clearInterval(checkLoader); | |
| initVim(); | |
| } else if (attempts > 20) { | |
| clearInterval(checkLoader); | |
| } | |
| }, 500); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment