release: v0.1.2 — readOnly, new syntax, plugins, optimizations

Features:
- feat(core): readOnly mode with config option and setReadOnly() API
- feat(parser): superscript ^text^ and subscript ~text~ support
- feat(parser): highlight/mark ==text== syntax
- feat(parser): table column alignment with colons (:---, :---:, ---:)
- feat(parser): emoji shortcodes 😄😊 (80+ common emojis)
- feat(plugins): imagePaste preset — paste clipboard images as base64
- feat(core): instance-level beforeRender/afterRender events
- feat(styles): print stylesheet (@media print)
- feat(styles): mark element CSS styling
- feat(styles): readOnly mode CSS (.me-readonly)

Optimizations:
- perf(core): history debounce now configurable via historyDebounce option
- perf(core): skip _render() in edit-only mode (already guarded)
- fix(core): divider drag division-by-zero guard
- fix(core): config.style deep merge with DEFAULTS
- fix(core): scroll position preserved across mode switches

Tests:
- 9 new test cases: readOnly (3), parser syntax (6)
- Total: 418 tests passing (+9 from v0.1.1)

Chores:
- version bump 0.1.1 → 0.1.2
- TypeScript definitions updated for new APIs
This commit is contained in:
2026-07-23 20:53:36 +08:00
parent e3142a17a8
commit 12875921eb
18 changed files with 257 additions and 26 deletions
+38
View File
@@ -1241,3 +1241,41 @@ describe('MarkdownEditor - 零散分支补全', () => {
ed.destroy();
});
});
describe('MarkdownEditor - v0.1.2 readOnly 模式', () => {
test('config.readOnly 设置 textarea readonly', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { readOnly: true, value: 'locked' });
expect(ed.textarea.readOnly).toBe(true);
expect(ed.el.classList.contains('me-readonly')).toBe(true);
ed.destroy();
});
test('setReadOnly 动态切换', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, {});
expect(ed.isReadOnly()).toBe(false);
ed.setReadOnly(true);
expect(ed.isReadOnly()).toBe(true);
expect(ed.textarea.readOnly).toBe(true);
expect(ed.el.classList.contains('me-readonly')).toBe(true);
ed.setReadOnly(false);
expect(ed.isReadOnly()).toBe(false);
expect(ed.textarea.readOnly).toBe(false);
expect(ed.el.classList.contains('me-readonly')).toBe(false);
ed.destroy();
});
test('readOnly 模式下内容仍可读取', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { readOnly: true, value: '# hello' });
expect(ed.getValue()).toBe('# hello');
ed.destroy();
});
});
+39 -4
View File
@@ -228,10 +228,10 @@ describe('parseMarkdown - 表格', () => {
const html = parseMarkdown(md);
expect(html).toContain('<table>');
expect(html).toContain('<thead>');
expect(html).toContain('<th>a</th>');
expect(html).toContain('<th>b</th>');
expect(html).toContain('<td>1</td>');
expect(html).toContain('<td>2</td>');
expect(html).toContain('<th style="text-align:left">a</th>');
expect(html).toContain('<th style="text-align:left">b</th>');
expect(html).toContain('<td style="text-align:left">1</td>');
expect(html).toContain('<td style="text-align:left">2</td>');
});
test('表格被 me-table-wrap 包裹', () => {
@@ -351,3 +351,38 @@ describe('slugify', () => {
expect(slugify('-hello-')).toBe('hello');
});
});
describe('parseMarkdown - v0.1.2 新语法', () => {
test('高亮标记 ==text==', () => {
const html = parseMarkdown('==highlight==');
expect(html).toContain('<mark>highlight</mark>');
});
test('上标 ^text^', () => {
const html = parseMarkdown('x^2^');
expect(html).toContain('<sup>2</sup>');
});
test('下标 ~text~', () => {
const html = parseMarkdown('H~2~O');
expect(html).toContain('<sub>2</sub>');
});
test('Emoji 短码 :smile:', () => {
const html = parseMarkdown(':smile: :rocket:');
expect(html).toContain('😊');
expect(html).toContain('🚀');
});
test('未知 Emoji 短码保留原文', () => {
const html = parseMarkdown(':unknown_emoji:');
expect(html).toContain(':unknown_emoji:');
});
test('表格对齐 :---: 居中', () => {
const md = '| a | b |\n| :---: | ---: |\n| 1 | 2 |';
const html = parseMarkdown(md);
expect(html).toContain('text-align:center');
expect(html).toContain('text-align:right');
});
});