Initial commit: MetonaEditor v0.1.0

This commit is contained in:
2026-07-23 16:23:07 +08:00
commit 22e867eda8
32 changed files with 17621 additions and 0 deletions
+248
View File
@@ -0,0 +1,248 @@
/**
* MetonaEditor - 轻量级 Markdown Editor 库
* @module metona-editor
* @version 0.1.0
* @author thzxx
* @description 现代化、零依赖、易扩展、易维护、易使用的 Markdown 编辑器库。单文件,开箱即用。
* @license MIT
*
* 用法:
* // 浏览器
* <script src="dist/metona-editor.js"></script>
* const editor = MeEditor.create('#editor', { mode: 'split' });
*
* // ES Module
* import MeEditor from 'metona-editor';
* const editor = MeEditor.create(container, options);
*
* // 直接使用类
* import { MarkdownEditor } from 'metona-editor';
* const editor = new MarkdownEditor(container, options);
*/
import { MarkdownEditor } from './core.js';
import { parseMarkdown, safeUrl, slugify } from './parser.js';
import { themeUtils } from './themes.js';
import { i18nUtils } from './i18n.js';
import { pluginUtils, presetPlugins } from './plugins.js';
import { animationUtils } from './animations.js';
import { DEFAULTS, ICONS, THEMES, EDIT_MODES, DEFAULT_TOOLBAR, TOOLBAR_ACTIONS } from './constants.js';
// 版本信息
const VERSION = '0.1.0';
/**
* 全局默认插件队列
* 通过 beforeCreate 钩子自动应用到所有后续创建的编辑器实例
*/
const globalPlugins = [];
// 注册全局钩子:每个新实例创建时自动安装全局默认插件
// 注意:core.js 构造函数中 beforeCreate 在 config.plugins 安装之前触发,
// 且 this._plugins 已初始化为 [],因此此处调用 editor.use() 安全。
MarkdownEditor.on('beforeCreate', (editor) => {
if (!editor || editor._destroyed) return;
globalPlugins.forEach((p) => {
try { editor.use(p); } catch (e) { console.error('MeEditor global plugin install error:', e); }
});
});
/**
* 创建编辑器实例(工厂函数,推荐用法)
* @param {string|HTMLElement} container - 容器选择器或 DOM 元素
* @param {Object} [options] - 配置项(见 DEFAULTS
* @returns {MarkdownEditor} 编辑器实例
*/
function create(container, options = {}) {
return new MarkdownEditor(container, options);
}
/**
* 注册全局默认插件(作用于所有后续创建的编辑器实例)
* @param {string|Object} plugin - 预设插件名 或 插件对象
* @param {Object} [options] - 插件配置(覆盖预设默认值)
* @returns {typeof api} 返回 api 自身以支持链式调用
*/
function use(plugin, options = {}) {
let p = plugin;
if (typeof plugin === 'string') {
p = presetPlugins[plugin];
if (!p) {
console.warn(`MeEditor: preset plugin "${plugin}" not found`);
return api;
}
}
if (!p || typeof p !== 'object') {
console.warn('MeEditor: invalid plugin');
return api;
}
const merged = (options && typeof options === 'object' && Object.keys(options).length > 0)
? { ...p, ...options }
: p;
globalPlugins.push(merged);
return api;
}
/**
* 注册全局事件钩子(转发到 MarkdownEditor 静态钩子,所有实例共享)
* 可用钩子:beforeCreate / afterCreate / beforeRender / afterRender / beforeDestroy / afterDestroy
* @param {string} name - 钩子名
* @param {Function} fn - 回调 (editor) => void
* @returns {Function} 取消注册函数
*/
function on(name, fn) {
return MarkdownEditor.on(name, fn);
}
/**
* 注销全局事件钩子
*/
function off(name, fn) {
MarkdownEditor.off(name, fn);
return api;
}
/**
* 切换全局主题(转发到 themeUtils
*/
function setTheme(theme, options) {
if (themeUtils && typeof themeUtils.switchTheme === 'function') {
themeUtils.switchTheme(theme, options);
}
return api;
}
/**
* 切换全局语言(转发到 i18nUtils)
*/
function setLocale(locale) {
if (i18nUtils && typeof i18nUtils.setCurrentLocale === 'function') {
i18nUtils.setCurrentLocale(locale);
}
return api;
}
/**
* 销毁所有全局资源(主题监听、i18n 监听、动画、全局插件队列)
* 注意:此方法不销毁已创建的编辑器实例,请单独调用每个实例的 destroy()
*/
function destroy() {
if (themeUtils) {
if (typeof themeUtils.clearThemeListeners === 'function') {
try { themeUtils.clearThemeListeners(); } catch (_) {}
}
if (typeof themeUtils.unwatchSystemTheme === 'function') {
try { themeUtils.unwatchSystemTheme(); } catch (_) {}
}
}
if (i18nUtils && typeof i18nUtils.clearLocaleListeners === 'function') {
try { i18nUtils.clearLocaleListeners(); } catch (_) {}
}
if (animationUtils && typeof animationUtils.cancelAll === 'function') {
try { animationUtils.cancelAll(); } catch (_) {}
}
globalPlugins.length = 0;
}
/**
* 获取库运行状态
*/
function getStatus() {
return {
version: VERSION,
theme: (themeUtils && typeof themeUtils.getCurrentTheme === 'function') ? themeUtils.getCurrentTheme() : 'auto',
locale: (i18nUtils && typeof i18nUtils.getCurrentLocale === 'function') ? i18nUtils.getCurrentLocale() : 'zh-CN',
globalPlugins: globalPlugins.map((p) => p.name || 'custom'),
presetPlugins: Object.keys(presetPlugins || {}),
};
}
// 初始化主题与国际化(浏览器环境)
if (typeof themeUtils === 'object' && typeof themeUtils.initTheme === 'function') {
try { themeUtils.initTheme(); } catch (_) {}
}
if (typeof i18nUtils === 'object' && typeof i18nUtils.initI18n === 'function') {
try { i18nUtils.initI18n(); } catch (_) {}
}
/**
* 顶层 API 对象(默认导出)
*/
const api = {
// 版本
VERSION,
version: VERSION,
// 核心类
MarkdownEditor,
Editor: MarkdownEditor,
// 工厂函数(推荐入口)
create,
// 顶层 API
use,
on,
off,
setTheme,
setLocale,
destroy,
getStatus,
// 内置解析器(可单独使用或替换)
parseMarkdown,
safeUrl,
slugify,
// 工具集
themes: themeUtils,
i18n: i18nUtils,
animations: animationUtils,
plugins: pluginUtils,
presetPlugins,
// 常量
DEFAULTS,
ICONS,
THEMES,
EDIT_MODES,
DEFAULT_TOOLBAR,
TOOLBAR_ACTIONS,
};
// 浏览器环境全局注册(UMD/CDN 场景:window.MeEditor
if (typeof window !== 'undefined') {
window.MeEditor = api;
}
export default api;
export {
api,
api as meEditor,
api as MeEditor,
MarkdownEditor,
MarkdownEditor as Editor,
create,
use,
on,
off,
setTheme,
setLocale,
destroy,
getStatus,
parseMarkdown,
safeUrl,
slugify,
themeUtils,
i18nUtils,
pluginUtils,
presetPlugins,
animationUtils,
DEFAULTS,
ICONS,
THEMES,
EDIT_MODES,
DEFAULT_TOOLBAR,
TOOLBAR_ACTIONS,
VERSION,
};