Add editor experience features: auto-run, layouts, resizable panels, Emmet, vim/emacs
- Auto-run toggle with localStorage persistence (toolbar checkbox) - Layout selector: default, top/bottom, editor-only, preview-only - Resizable panels via drag dividers with double-click reset - Emmet abbreviation expansion for HTML, CSS, and JSX - Vim and Emacs keybinding modes with lazy-loaded CDN libraries - Shared preferences module for localStorage management - Editor hooks for tab switch and mode change callbacks
This commit is contained in:
109
public/js/keybindings.js
Normal file
109
public/js/keybindings.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import { getPref, setPref } from './preferences.js';
|
||||
import { getActiveEditor, setOnTabSwitch, setOnModeChange } from './editors.js';
|
||||
|
||||
let currentMode = 'default'; // 'default' | 'vim' | 'emacs'
|
||||
let activeAdapter = null; // vim or emacs adapter instance
|
||||
let vimLoaded = false;
|
||||
let emacsLoaded = false;
|
||||
|
||||
const statusBar = () => document.getElementById('vim-status-bar');
|
||||
|
||||
function loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (document.querySelector(`script[src="${src}"]`)) { resolve(); return; }
|
||||
const savedDefine = window.define;
|
||||
window.define = undefined;
|
||||
const s = document.createElement('script');
|
||||
s.src = src;
|
||||
s.onload = () => { window.define = savedDefine; resolve(); };
|
||||
s.onerror = () => { window.define = savedDefine; reject(new Error(`Failed to load ${src}`)); };
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureVim() {
|
||||
if (vimLoaded) return;
|
||||
window.monaco = monaco;
|
||||
await loadScript('https://unpkg.com/monaco-vim@0.4.2/dist/monaco-vim.js');
|
||||
vimLoaded = true;
|
||||
}
|
||||
|
||||
async function ensureEmacs() {
|
||||
if (emacsLoaded) return;
|
||||
window.monaco = monaco;
|
||||
await loadScript('https://unpkg.com/monaco-emacs/dist/monaco-emacs.js');
|
||||
emacsLoaded = true;
|
||||
}
|
||||
|
||||
function disposeAdapter() {
|
||||
if (activeAdapter) {
|
||||
activeAdapter.dispose();
|
||||
activeAdapter = null;
|
||||
}
|
||||
const bar = statusBar();
|
||||
if (bar) {
|
||||
bar.style.display = 'none';
|
||||
bar.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
async function attachToEditor(editor) {
|
||||
if (!editor) return;
|
||||
disposeAdapter();
|
||||
|
||||
if (currentMode === 'vim') {
|
||||
await ensureVim();
|
||||
const bar = statusBar();
|
||||
if (bar) bar.style.display = 'block';
|
||||
activeAdapter = MonacoVim.initVimMode(editor, bar);
|
||||
} else if (currentMode === 'emacs') {
|
||||
await ensureEmacs();
|
||||
const EmacsExtension = MonacoEmacs.EmacsExtension;
|
||||
activeAdapter = new EmacsExtension(editor);
|
||||
activeAdapter.start();
|
||||
}
|
||||
}
|
||||
|
||||
export async function setKeybindingMode(mode) {
|
||||
disposeAdapter();
|
||||
currentMode = mode;
|
||||
setPref('keybindings', mode);
|
||||
|
||||
if (mode === 'default') return;
|
||||
|
||||
const editor = getActiveEditor();
|
||||
if (editor) await attachToEditor(editor);
|
||||
}
|
||||
|
||||
export function initKeybindings() {
|
||||
currentMode = getPref('keybindings') || 'default';
|
||||
|
||||
const select = document.getElementById('keybinding-mode');
|
||||
if (select) {
|
||||
select.value = currentMode;
|
||||
select.addEventListener('change', (e) => setKeybindingMode(e.target.value));
|
||||
}
|
||||
|
||||
// Reattach on tab switch
|
||||
setOnTabSwitch((_tabId, editor) => {
|
||||
if (currentMode !== 'default' && editor) {
|
||||
attachToEditor(editor);
|
||||
}
|
||||
});
|
||||
|
||||
// Reattach on mode change
|
||||
setOnModeChange(() => {
|
||||
if (currentMode !== 'default') {
|
||||
setTimeout(() => {
|
||||
const editor = getActiveEditor();
|
||||
if (editor) attachToEditor(editor);
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
// Initial attach if non-default
|
||||
if (currentMode !== 'default') {
|
||||
const editor = getActiveEditor();
|
||||
if (editor) attachToEditor(editor);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user