release: v0.1.12 — accessibility, Zen mode, scroll sync v2, shortcutHelp plugin
feat(a11y): toolbar keyboard navigation (Arrow keys, Home, End)
feat(a11y): aria-live region for screen reader announcements on mode change
feat(a11y): enhance focus-visible style (outline-offset: 2px, hide on mouse focus)
feat(core): Zen mode (editor.toggleZen() / exec('zen'))
- Toggle with Ctrl+Shift+Z shortcut concept
- Auto-hide toolbar and statusbar, mouse to top edge reveals toolbar
- Centered editor pane with max-width constraint
feat(core): heading-aware scroll sync v2
- Sync preview scroll to nearest heading before cursor position
- Falls back to proportional sync when no headings found
feat(core): Word wrap toggle (editor.toggleWordWrap() / setWordWrap(bool))
feat(plugins): shortcutHelp preset plugin
- Press '?' to open keyboard shortcuts overlay panel
- Lists built-in + user-registered shortcuts
- Click overlay or press Escape to close
style: Zen mode CSS, sr-only class, focus-visible refinement
test: 3 new tests for Zen mode, WordWrap, shortcutHelp (535 total)
chore: bump version 0.1.11 → 0.1.12 across all files
This commit is contained in:
+91
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* MetonaEditor Plugins — 插件系统 v2
|
||||
* @module plugins
|
||||
* @version 0.1.11
|
||||
* @version 0.1.12
|
||||
* @description 插件管理器 + 预设插件 + 依赖/生命周期/异步/卸载
|
||||
*
|
||||
* v0.1.6 增强:
|
||||
@@ -510,6 +510,95 @@ const imagePastePlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 快捷键帮助插件(v0.1.12)
|
||||
* 按 ? 弹出快捷键列表面板
|
||||
*/
|
||||
const shortcutHelpPlugin = {
|
||||
name: 'shortcutHelp',
|
||||
version: '0.1.0',
|
||||
description: '按 ? 查看快捷键列表',
|
||||
priority: 200,
|
||||
|
||||
_onKeydown: null,
|
||||
_panel: null,
|
||||
|
||||
install(editor) {
|
||||
if (!editor || !editor.textarea || typeof document === 'undefined') return;
|
||||
|
||||
this._injectStyle();
|
||||
this._onKeydown = (e) => {
|
||||
// ? 键(Shift+/),且不在输入框中时
|
||||
if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
this._open(editor);
|
||||
}
|
||||
if (e.key === 'Escape' && this._panel) {
|
||||
this._close();
|
||||
}
|
||||
};
|
||||
editor.textarea.addEventListener('keydown', this._onKeydown);
|
||||
},
|
||||
|
||||
_injectStyle() {
|
||||
if (document.getElementById('me-shortcut-style')) return;
|
||||
const style = document.createElement('style');
|
||||
style.id = 'me-shortcut-style';
|
||||
style.textContent = `.me-shortcut-overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:50;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center}.me-shortcut-panel{background:var(--md-bg,#fff);border-radius:12px;padding:24px;max-width:560px;width:90%;max-height:80vh;overflow-y:auto;box-shadow:0 12px 40px rgba(0,0,0,0.3)}.me-shortcut-panel h3{font-size:16px;margin:0 0 16px;color:var(--md-text)}.me-shortcut-panel table{width:100%;border-collapse:collapse;font-size:13px}.me-shortcut-panel td{padding:6px 10px;border-bottom:1px solid var(--md-border)}.me-shortcut-panel td:first-child{font-family:var(--md-mono);font-size:12px;color:var(--md-accent);white-space:nowrap;width:40%}.me-shortcut-panel td:last-child{color:var(--md-text)}.me-shortcut-panel .me-shortcut-close{position:absolute;top:16px;right:20px;background:none;border:none;font-size:20px;cursor:pointer;color:var(--md-muted)}`;
|
||||
document.head.appendChild(style);
|
||||
},
|
||||
|
||||
_open(editor) {
|
||||
if (this._panel) { this._close(); return; }
|
||||
|
||||
const builtin = [
|
||||
['Ctrl+B', '粗体'], ['Ctrl+I', '斜体'], ['Ctrl+U', '下划线'],
|
||||
['Ctrl+K', '链接'], ['Ctrl+E', '行内代码'],
|
||||
['Ctrl+1/2/3', '标题 H1/H2/H3'], ['Ctrl+Q', '引用'],
|
||||
['Ctrl+Z', '撤销'], ['Ctrl+Y / Ctrl+Shift+Z', '重做'],
|
||||
['Ctrl+S', '保存'], ['Ctrl+F', '查找'], ['Ctrl+H', '替换'],
|
||||
['Tab', '缩进'], ['Shift+Tab', '反缩进'],
|
||||
['Ctrl+Shift+T', 'Zen 模式演示快捷键'],
|
||||
['?', '显示/隐藏快捷键帮助'],
|
||||
];
|
||||
|
||||
const custom = (editor.getShortcuts && editor.getShortcuts()) || [];
|
||||
const customRows = custom.map((s) => [s.combo, s.description || s.combo]);
|
||||
const allRows = [...builtin, ...customRows];
|
||||
|
||||
let rows = '';
|
||||
allRows.forEach(([combo, desc]) => {
|
||||
rows += `<tr><td>${combo}</td><td>${desc}</td></tr>`;
|
||||
});
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'me-shortcut-overlay';
|
||||
overlay.innerHTML = `<div class="me-shortcut-panel"><h3>⌨️ 快捷键</h3><button class="me-shortcut-close">×</button><table>${rows}</table></div>`;
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay || e.target.classList.contains('me-shortcut-close')) {
|
||||
this._close();
|
||||
}
|
||||
});
|
||||
document.body.appendChild(overlay);
|
||||
this._panel = overlay;
|
||||
},
|
||||
|
||||
_close() {
|
||||
if (this._panel) {
|
||||
this._panel.remove();
|
||||
this._panel = null;
|
||||
}
|
||||
},
|
||||
|
||||
destroy(editor) {
|
||||
this._close();
|
||||
if (this._onKeydown && editor && editor.textarea) {
|
||||
editor.textarea.removeEventListener('keydown', this._onKeydown);
|
||||
}
|
||||
this._onKeydown = null;
|
||||
},
|
||||
};
|
||||
|
||||
// ============ 预设插件表 ============
|
||||
|
||||
const presetPlugins = {
|
||||
@@ -517,6 +606,7 @@ const presetPlugins = {
|
||||
exportTool: exportToolPlugin,
|
||||
searchReplace: searchReplacePlugin,
|
||||
imagePaste: imagePastePlugin,
|
||||
shortcutHelp: shortcutHelpPlugin,
|
||||
};
|
||||
|
||||
// ============ 实用工具 ============
|
||||
|
||||
Reference in New Issue
Block a user