revert: remove syntax highlighting feature entirely
Syntax highlighting overlay approach proved too fragile: - pixel alignment between textarea and overlay unreliable - transparent textarea caused invisible content bugs - dynamic gutter width positioning added complexity Removed: - _highlightEditor, _doHighlight, toggleSyntaxHighlight, _positionHighlightLayer - highlightLayer DOM element creation - .me-highlight-layer, .mh-*, .me-textarea-highlight CSS - syntaxHighlight config option from DEFAULTS - highlight toggle button from demo.html Kept: Mermaid, fileSystem, preview links, all other v0.1.14 features
This commit is contained in:
@@ -68,7 +68,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Micr
|
||||
<div class="spacer"></div>
|
||||
<button data-cmd="zen" title="Zen 专注模式">🧘 Zen</button>
|
||||
<button data-cmd="outline" title="切换大纲面板">📋 大纲</button>
|
||||
<button data-cmd="highlight" title="切换语法高亮">🎨 高亮</button>
|
||||
<button data-cmd="wrap" title="切换自动换行">↩ 换行</button>
|
||||
<button data-cmd="toast" title="Toast 通知演示">💬 Toast</button>
|
||||
<button data-cmd="openFile" title="打开磁盘文件">📂 打开</button>
|
||||
@@ -199,7 +198,6 @@ var editor=MeEditor.create('#editor',{
|
||||
value:demoMd,
|
||||
mode:'split',height:620,
|
||||
lineNumbers:true,autoBrackets:true,wordCount:true,
|
||||
syntaxHighlight:true,
|
||||
theme:'auto',locale:'zh-CN',
|
||||
outline:false,
|
||||
plugins:['autoSave','exportTool','searchReplace','imagePaste','shortcutHelp','fileSystem'],
|
||||
@@ -229,7 +227,6 @@ bar.addEventListener('click',function(e){
|
||||
if(cmd==='mode'){editor.setMode(btn.dataset.arg);updateModeBtns(btn.dataset.arg);}
|
||||
else if(cmd==='zen'){editor.toggleZen();btn.classList.toggle('on',editor.isZen());}
|
||||
else if(cmd==='outline'){toggleOutline(btn);}
|
||||
else if(cmd==='highlight'){editor.toggleSyntaxHighlight();btn.classList.toggle('on',editor.config.syntaxHighlight);}
|
||||
else if(cmd==='wrap'){editor.toggleWordWrap();btn.classList.toggle('on',!editor.isWordWrap());}
|
||||
else if(cmd==='toast'){
|
||||
var types=['success','error','warning','info'];
|
||||
|
||||
@@ -62,8 +62,6 @@ export const DEFAULTS = Object.freeze({
|
||||
wordWrap: true,
|
||||
// 最大字符数(v0.1.14,0=不限)
|
||||
maxLength: 0,
|
||||
// 语法高亮编辑区(v0.1.14)
|
||||
syntaxHighlight: true,
|
||||
// 主题:light / dark / auto / warm / 自定义
|
||||
theme: 'auto',
|
||||
// 国际化
|
||||
|
||||
+1
-93
@@ -6,7 +6,7 @@
|
||||
* v0.1.6: 插件依赖拓扑排序、实例 i18n、快捷键/工具栏定制、右键菜单、Toast
|
||||
* v0.1.7: 行号装订线、自动格式化、括号自动闭合、拖放、大纲面板
|
||||
* v0.1.12: 工具栏键盘导航、aria-live、Zen模式、滚动同步v2、WordWrap
|
||||
* v0.1.14: 语法高亮编辑区、图片上传钩子、预览链接拦截、Mermaid、文件存储
|
||||
* v0.1.14: 预览链接拦截、Mermaid、文件存储
|
||||
*/
|
||||
|
||||
import { generateId, escapeHTML, isBrowser } from './utils.js';
|
||||
@@ -115,7 +115,6 @@ class MarkdownEditor {
|
||||
this._zenMode = false; // v0.1.12: Zen 模式
|
||||
this._wordWrap = true; // v0.1.12: 自动换行
|
||||
this._syncing = false; // 滚动同步锁
|
||||
this._hlRaf = null; // v0.1.14: 高亮节流
|
||||
|
||||
// 渲染函数:自定义覆盖内置解析器
|
||||
this._renderFn = (typeof this.config.render === 'function') ? this.config.render : parseMarkdown;
|
||||
@@ -217,12 +216,6 @@ class MarkdownEditor {
|
||||
if (!this.config.lineNumbers) gutter.style.display = 'none';
|
||||
editorInner.appendChild(gutter);
|
||||
|
||||
// 语法高亮叠加层(v0.1.14)
|
||||
const highlightLayer = document.createElement('pre');
|
||||
highlightLayer.className = 'me-highlight-layer';
|
||||
highlightLayer.setAttribute('aria-hidden', 'true');
|
||||
editorInner.appendChild(highlightLayer);
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.className = 'me-textarea';
|
||||
textarea.spellcheck = !!this.config.spellcheck;
|
||||
@@ -230,12 +223,6 @@ class MarkdownEditor {
|
||||
textarea.style.tabSize = String(this.config.tabSize || 2);
|
||||
textarea.placeholder = this.config.placeholder || i18nT('placeholder');
|
||||
textarea.setAttribute('aria-label', i18nT('edit'));
|
||||
if (this.config.syntaxHighlight) {
|
||||
textarea.classList.add('me-textarea-highlight');
|
||||
}
|
||||
if (highlightLayer && !this.config.syntaxHighlight) {
|
||||
highlightLayer.hidden = true;
|
||||
}
|
||||
editorInner.appendChild(textarea);
|
||||
|
||||
editorPane.appendChild(editorInner);
|
||||
@@ -276,7 +263,6 @@ class MarkdownEditor {
|
||||
this.editorPane = editorPane;
|
||||
this.editorInner = editorInner;
|
||||
this.gutter = gutter;
|
||||
this.highlightLayer = highlightLayer;
|
||||
this.previewPane = previewPane;
|
||||
this.dividerEl = divider;
|
||||
this.textarea = textarea;
|
||||
@@ -343,7 +329,6 @@ class MarkdownEditor {
|
||||
this._updateWordCount();
|
||||
this._renderGutter();
|
||||
this._updateOutline();
|
||||
this._highlightEditor(); // v0.1.14
|
||||
this._emit('input', this._value);
|
||||
this._emit('change', this._value);
|
||||
if (typeof this.config.onInput === 'function') {
|
||||
@@ -1319,82 +1304,6 @@ class MarkdownEditor {
|
||||
requestAnimationFrame(() => { this._syncing = false; });
|
||||
}
|
||||
|
||||
// ============ 语法高亮编辑区(v0.1.14) ============
|
||||
|
||||
_highlightEditor() {
|
||||
if (!this.highlightLayer || !this.config.syntaxHighlight) return;
|
||||
// rAF 节流,避免高频更新
|
||||
if (this._hlRaf) return;
|
||||
this._hlRaf = requestAnimationFrame(() => {
|
||||
this._hlRaf = null;
|
||||
this._doHighlight();
|
||||
});
|
||||
}
|
||||
|
||||
_doHighlight() {
|
||||
if (!this.highlightLayer || this.highlightLayer.hidden || this._mode === 'preview') return;
|
||||
const text = this._value || '';
|
||||
if (!text) { this.highlightLayer.innerHTML = ''; return; }
|
||||
if (text.length > 50000) { this.highlightLayer.innerHTML = escapeHTML(text) + '\n'; return; }
|
||||
|
||||
let html = escapeHTML(text);
|
||||
this._positionHighlightLayer();
|
||||
|
||||
// 正则按优先级:先匹配更具体的模式,避免被泛模式吞掉
|
||||
// 1. 图片(先于链接)
|
||||
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<span class="mh-image">$&</span>');
|
||||
// 2. 链接
|
||||
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<span class="mh-link">$&</span>');
|
||||
// 3. 行内代码(先于粗体/斜体,避免 `**` 被误匹配)
|
||||
html = html.replace(/`([^`]+)`/g, '<span class="mh-code">$&</span>');
|
||||
// 4. 粗体 **text** / __text__
|
||||
html = html.replace(/(\*\*|__)(.+?)\1/g, '<span class="mh-bold">$&</span>');
|
||||
// 5. 删除线
|
||||
html = html.replace(/~~(.+?)~~/g, '<span class="mh-strike">$&</span>');
|
||||
// 6. 斜体 *text* / _text_(在粗体之后,避免 ** 被拆成两个斜体)
|
||||
html = html.replace(/(?<!\*)\*(?!\*)([^*\n]+?)\*(?!\*)/g, '<span class="mh-italic">$&</span>');
|
||||
html = html.replace(/(?<!_)_(?!_)([^_\n]+?)_(?!_)/g, '<span class="mh-italic">$&</span>');
|
||||
// 7. 标题(行级)
|
||||
html = html.replace(/^(#{1,6})\s+(.+)$/gm, '<span class="mh-heading">$&</span>');
|
||||
// 8. 列表标记
|
||||
html = html.replace(/^(\s*[-*+]|\d+\.)\s/gm, '<span class="mh-list">$&</span>');
|
||||
// 9. 引用
|
||||
html = html.replace(/^(>)\s?/gm, '<span class="mh-quote">$&</span>');
|
||||
// 10. 水平线
|
||||
html = html.replace(/^([-*_]{3,})\s*$/gm, '<span class="mh-hr">$&</span>');
|
||||
|
||||
this.highlightLayer.innerHTML = html + '\n';
|
||||
// 同步滚动
|
||||
if (this.textarea) this.highlightLayer.scrollTop = this.textarea.scrollTop;
|
||||
}
|
||||
|
||||
/** 切换语法高亮(v0.1.14) */
|
||||
toggleSyntaxHighlight() {
|
||||
this.config.syntaxHighlight = !this.config.syntaxHighlight;
|
||||
if (this.textarea) {
|
||||
this.textarea.classList.toggle('me-textarea-highlight', this.config.syntaxHighlight);
|
||||
}
|
||||
if (this.highlightLayer) {
|
||||
this.highlightLayer.hidden = !this.config.syntaxHighlight;
|
||||
}
|
||||
if (this.config.syntaxHighlight) {
|
||||
this._positionHighlightLayer();
|
||||
this._doHighlight();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
_positionHighlightLayer() {
|
||||
if (!this.highlightLayer) return;
|
||||
// 动态设置左边距:有行号时对齐行号宽度,无行号时从 0 开始
|
||||
if (this.gutter && this.config.lineNumbers && this.gutter.style.display !== 'none') {
|
||||
const w = this.gutter.getBoundingClientRect().width;
|
||||
this.highlightLayer.style.left = w + 'px';
|
||||
} else {
|
||||
this.highlightLayer.style.left = '0';
|
||||
}
|
||||
}
|
||||
|
||||
_updateWordCount() {
|
||||
if (!this.config.wordCount || !this.statusEl) return;
|
||||
const text = this._value || '';
|
||||
@@ -1781,7 +1690,6 @@ class MarkdownEditor {
|
||||
if (this._renderRaf) cancelAnimationFrame(this._renderRaf);
|
||||
if (this._historyTimer) clearTimeout(this._historyTimer);
|
||||
if (this._outlineTimer) clearTimeout(this._outlineTimer);
|
||||
if (this._hlRaf) cancelAnimationFrame(this._hlRaf);
|
||||
|
||||
this._cleanups.forEach((fn) => { try { fn(); } catch (_) {} });
|
||||
this._cleanups = [];
|
||||
|
||||
@@ -132,31 +132,6 @@ const generateCSS = () => {
|
||||
|
||||
/* ============ 编辑区内层(v0.1.7) ============ */
|
||||
.me-editor-inner { display: flex; height: 100%; overflow: hidden; position: relative; }
|
||||
/* 语法高亮叠加层(v0.1.14) */
|
||||
.me-highlight-layer {
|
||||
position: absolute; top: 0; right: 0; bottom: 0;
|
||||
padding: 16px 18px; margin: 0; border: 0; outline: 0; overflow: hidden;
|
||||
font-family: var(--md-mono); font-size: 13.5px; line-height: 1.7;
|
||||
white-space: pre-wrap; word-wrap: break-word;
|
||||
pointer-events: none; background: var(--md-textarea-bg);
|
||||
color: var(--md-text); z-index: 0;
|
||||
}
|
||||
/* textarea 在高亮模式下透明背景,露出下方高亮层 */
|
||||
.me-textarea-highlight {
|
||||
background: transparent !important;
|
||||
}
|
||||
/* 隐藏高亮层(语法高亮关闭时) */
|
||||
.me-highlight-layer[hidden] { display: none; }
|
||||
.mh-heading { color: var(--md-accent); font-weight: 700; }
|
||||
.mh-bold { color: var(--md-text); font-weight: 700; }
|
||||
.mh-italic { font-style: italic; }
|
||||
.mh-strike { color: var(--md-muted); text-decoration: line-through; }
|
||||
.mh-code { background: var(--md-code-bg); border-radius: 3px; padding: 0 2px; }
|
||||
.mh-link { color: var(--md-accent); text-decoration: underline; }
|
||||
.mh-image { color: #8b5cf6; }
|
||||
.mh-list { color: var(--md-accent); font-weight: 600; }
|
||||
.mh-quote { color: var(--md-muted); }
|
||||
.mh-hr { color: var(--md-muted); }
|
||||
.me-gutter {
|
||||
flex: 0 0 auto;
|
||||
min-width: 36px;
|
||||
|
||||
Reference in New Issue
Block a user