refactor(core): 拆分 4 模块 + 插件状态 Symbol 化 + eslint type-aware

- core.ts 从 1162 行降至 865 行:命令/浮动工具栏/右键菜单/大纲
  拆至 commands.ts / floating-toolbar.ts / context-menu.ts / outline.ts
  (原型安装,API 与测试完全兼容)
- 6 个预设插件状态改用 Symbol 键存储,定义 EditorLike 契约接口,
  消灭 (editor as any).__xxx 魔法属性
- 实现 maxLength(textarea maxlength + 程序化入口截断)与
  zenMode 初始状态(此前为 dead config)
- 搜索面板补齐 matchCase/wholeWord(翻译键已有但未实现,
  中文全字边界感知)
- 分隔条 localStorage key 加入实例 id 隔离
- eslint 启用 type-aware 规则(consistent-type-imports /
  no-unnecessary-type-assertion),0 errors
This commit is contained in:
2026-08-09 08:59:37 +08:00
parent ddc650feeb
commit fae31bba60
12 changed files with 972 additions and 447 deletions
+113
View File
@@ -2301,3 +2301,116 @@ describe('MarkdownEditor - v0.2.3 getStatus', () => {
ed.destroy();
});
});
// ============ v0.2.5 maxLength / zenMode / 分隔条隔离 ============
describe('MarkdownEditor - v0.2.5 maxLength', () => {
test('setValue 超长内容被截断', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { maxLength: 10 });
ed.setValue('0123456789ABC');
expect(ed.getValue()).toBe('0123456789');
expect(ed.textarea.value).toBe('0123456789');
ed.destroy();
});
test('insert 超长内容被截断', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { maxLength: 5, value: 'ab' });
ed.focus();
ed.textarea.setSelectionRange(2, 2);
ed.insert('CDEFG');
expect(ed.getValue()).toBe('abCDE');
ed.destroy();
});
test('textarea maxlength 属性已绑定', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { maxLength: 42 });
expect(ed.textarea.maxLength).toBe(42);
ed.destroy();
});
test('maxLength 为 0 时不限制', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, {});
expect(ed.textarea.maxLength).toBe(-1);
const long = 'x'.repeat(10000);
ed.setValue(long);
expect(ed.getValue().length).toBe(10000);
ed.destroy();
});
});
describe('MarkdownEditor - v0.2.5 zenMode 初始状态', () => {
test('zenMode: true 启动即进入 Zen 模式', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { zenMode: true });
expect(ed.isZen()).toBe(true);
expect(ed.el.classList.contains('me-zen')).toBe(true);
expect(ed.toolbarEl.style.opacity).toBe('0');
ed.destroy();
});
test('默认不进入 Zen 模式', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, {});
expect(ed.isZen()).toBe(false);
expect(ed.el.classList.contains('me-zen')).toBe(false);
ed.destroy();
});
test('zenMode 初始开启后 toggleZen 可关闭', () => {
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { zenMode: true });
ed.toggleZen();
expect(ed.isZen()).toBe(false);
expect(ed.el.classList.contains('me-zen')).toBe(false);
ed.destroy();
});
});
describe('MarkdownEditor - v0.2.5 分隔条实例隔离', () => {
test('分隔条位置按实例 id 保存', () => {
localStorage.clear();
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const ed = new MarkdownEditor(c, { id: 'divider-test-1' });
ed.editorPane.style.flex = '0 0 30%';
ed._saveDividerPosition();
expect(localStorage.getItem('metona-editor-divider-divider-test-1')).toBe('0 0 30%');
expect(localStorage.getItem('metona-editor-divider')).toBeNull();
ed.destroy();
});
test('不同实例互不覆盖分隔条位置', () => {
localStorage.clear();
document.body.innerHTML = '';
const c = document.createElement('div');
document.body.appendChild(c);
const a = new MarkdownEditor(c, { id: 'dva' });
const b = new MarkdownEditor(c, { id: 'dvb' });
a.editorPane.style.flex = '0 0 20%';
b.editorPane.style.flex = '0 0 70%';
a._saveDividerPosition();
b._saveDividerPosition();
expect(localStorage.getItem('metona-editor-divider-dva')).toBe('0 0 20%');
expect(localStorage.getItem('metona-editor-divider-dvb')).toBe('0 0 70%');
a.destroy(); b.destroy();
});
});