feat(plugins): plugin system v2
- depends: declarative plugin dependencies with topological sort (Kahn algorithm)
- async plugins: install() returning Promise auto-await
- editor.unuse(name): uninstall individual plugins
- pluginUtils.validateConfig(schema, config): configuration validation
- plugin priority field controls install order within same dependency level
feat(i18n): instance-level locale isolation, pluralization, dynamic loading
- createInstanceI18n(): per-editor independent locale contexts
- editor.setLocale()/getLocale()/t() instance methods
- Plural rules: t('items', { count: 5 }) auto-selects one/other/few/many
- i18nUtils.loadRemote(url, locale): fetch translation packs from remote
- editor.on('localeChange') event
feat(core): toolbar & shortcut customization, context menu, toast
- editor.registerShortcut(combo, handler): custom keyboard shortcuts
- editor.configureToolbar(tools): dynamic toolbar rebuild
- editor.removeToolbarButton(action): remove single button
- editor.registerContextMenu(items): right-click context menu
- editor.toast(msg, {type, duration, animation}): toast notifications
style: toast notifications, context menu, dropdown CSS
- .me-toast with success/error/warning/info types, fade/slide animations
- .me-context-menu with items, separators, shortcut hints
test: 22 new tests covering plugin deps, unuse, shortcuts, toast, i18n plural (521 total)
chore: bump version 0.1.5 → 0.1.6 across all files, site, types
402 lines
12 KiB
TypeScript
402 lines
12 KiB
TypeScript
// Type definitions for MetonaEditor v0.1.6
|
||
// Project: https://git.metona.cn/MetonaTeam/MetonaEditor
|
||
// Author: thzxx
|
||
|
||
declare const VERSION: string;
|
||
|
||
/** 视图模式 */
|
||
export type EditMode = 'edit' | 'split' | 'preview';
|
||
|
||
/** 主题名 */
|
||
export type ThemeName = 'light' | 'dark' | 'auto' | 'warm' | string;
|
||
|
||
/** 渲染环境,传给自定义 render 函数 */
|
||
export interface RenderEnv {
|
||
highlight?: (code: string, lang: string) => string;
|
||
locale?: string;
|
||
[key: string]: unknown;
|
||
}
|
||
|
||
/** 工具栏项:动作名或 '|'(分隔符) */
|
||
export type ToolbarItem = string | '|';
|
||
|
||
/** 编辑器配置 */
|
||
export interface MarkdownEditorOptions {
|
||
/** 初始内容 */
|
||
value?: string;
|
||
/** 占位符 */
|
||
placeholder?: string;
|
||
/** 视图模式,默认 'split' */
|
||
mode?: EditMode;
|
||
/** 高度:数字为 px,字符串原样使用,默认 400 */
|
||
height?: number | string;
|
||
/** 工具栏按钮序列,传 false 隐藏工具栏 */
|
||
toolbar?: ToolbarItem[] | false;
|
||
/** 是否显示字数统计状态栏,默认 true */
|
||
wordCount?: boolean;
|
||
/** 自动聚焦,默认 false */
|
||
autofocus?: boolean;
|
||
/** 拼写检查,默认 false */
|
||
spellcheck?: boolean;
|
||
/** 历史栈上限,默认 100 */
|
||
historyLimit?: number;
|
||
/** 分屏模式下同步滚动,默认 true */
|
||
syncScroll?: boolean;
|
||
/** Tab 插入的空格数,0 表示插入 \t,默认 2 */
|
||
tabSize?: number;
|
||
/** 只读模式,默认 false */
|
||
readOnly?: boolean;
|
||
/** 历史栈防抖延迟(ms),默认 400 */
|
||
historyDebounce?: number;
|
||
/** 主题,默认 'auto' */
|
||
theme?: ThemeName;
|
||
/** 国际化语言,默认 'zh-CN' */
|
||
locale?: string;
|
||
/** 自定义 Markdown 渲染函数,覆盖内置解析器 */
|
||
render?: (markdown: string, env: RenderEnv) => string;
|
||
/** 自定义代码高亮函数 */
|
||
highlight?: (code: string, lang: string) => string;
|
||
/** 自定义 HTML 净化函数(接收渲染后 HTML,返回安全 HTML) */
|
||
sanitize?: (html: string) => string;
|
||
/** 自定义容器 className */
|
||
className?: string;
|
||
/** 容器内联样式对象 */
|
||
style?: { [key: string]: string };
|
||
/** 实例级插件列表 */
|
||
plugins?: Array<string | PluginObject>;
|
||
/** 唯一 id */
|
||
id?: string;
|
||
|
||
// 生命周期回调
|
||
onCreate?: (editor: MarkdownEditor) => void;
|
||
onDestroy?: (editor: MarkdownEditor) => void;
|
||
onInput?: (value: string, editor: MarkdownEditor) => void;
|
||
onChange?: (value: string, editor: MarkdownEditor) => void;
|
||
onFocus?: (editor: MarkdownEditor) => void;
|
||
onBlur?: (editor: MarkdownEditor) => void;
|
||
onSave?: (value: string, editor: MarkdownEditor) => void;
|
||
onModeChange?: (mode: EditMode, editor: MarkdownEditor) => void;
|
||
onFullscreen?: (fullscreen: boolean, editor: MarkdownEditor) => void;
|
||
}
|
||
|
||
/** 插件对象 */
|
||
export interface PluginObject {
|
||
name: string;
|
||
description?: string;
|
||
[key: string]: any;
|
||
install?: (editor: MarkdownEditor) => void;
|
||
destroy?: (editor: MarkdownEditor) => void;
|
||
}
|
||
|
||
/** 工具栏按钮配置(addToolbarButton 用) */
|
||
export interface ToolbarButtonConfig {
|
||
action: string;
|
||
title?: string;
|
||
text?: string;
|
||
icon?: string;
|
||
onClick?: (editor: MarkdownEditor) => void;
|
||
handler?: (...args: any[]) => void;
|
||
}
|
||
|
||
/** 统计信息 */
|
||
export interface EditorStats {
|
||
characters: number;
|
||
words: number;
|
||
chineseChars: number;
|
||
englishWords: number;
|
||
lines: number;
|
||
readingTime: number;
|
||
}
|
||
|
||
/** 编辑器状态 */
|
||
export interface EditorStatus {
|
||
id: string;
|
||
mode: EditMode;
|
||
theme: ThemeName;
|
||
locale: string;
|
||
fullscreen: boolean;
|
||
readOnly: boolean;
|
||
disabled: boolean;
|
||
destroyed: boolean;
|
||
plugins: string[];
|
||
}
|
||
|
||
/** 事件名 */
|
||
export type EditorEventName =
|
||
| 'input' | 'change' | 'focus' | 'blur' | 'save'
|
||
| 'modeChange' | 'fullscreen' | 'autosave' | 'destroy'
|
||
| 'beforeRender' | 'afterRender';
|
||
|
||
/** 全局钩子名 */
|
||
export type HookName =
|
||
| 'beforeCreate' | 'afterCreate'
|
||
| 'beforeRender' | 'afterRender'
|
||
| 'beforeDestroy' | 'afterDestroy';
|
||
|
||
/** MarkdownEditor 编辑器类 */
|
||
export class MarkdownEditor {
|
||
constructor(container: string | HTMLElement, options?: MarkdownEditorOptions);
|
||
|
||
/** 实例 id */
|
||
readonly id: string;
|
||
/** 容器元素 */
|
||
readonly container: HTMLElement;
|
||
/** 配置(只读视图) */
|
||
readonly config: MarkdownEditorOptions;
|
||
/** 根元素 */
|
||
readonly el: HTMLElement;
|
||
readonly toolbarEl: HTMLElement;
|
||
readonly bodyEl: HTMLElement;
|
||
readonly editorPane: HTMLElement;
|
||
readonly previewPane: HTMLElement;
|
||
readonly dividerEl: HTMLElement;
|
||
readonly textarea: HTMLTextAreaElement;
|
||
readonly previewEl: HTMLElement;
|
||
readonly statusEl: HTMLElement | null;
|
||
|
||
// 静态钩子
|
||
static on(name: HookName, fn: (editor: MarkdownEditor) => void): () => void;
|
||
static off(name: HookName, fn: (editor: MarkdownEditor) => void): void;
|
||
static trigger(name: HookName, editor: MarkdownEditor): void;
|
||
|
||
// 内容
|
||
getValue(): string;
|
||
setValue(markdown: string, opts?: { silent?: boolean }): this;
|
||
getHTML(): string;
|
||
/** 强制刷新预览(内容未变时手动触发重渲染) */
|
||
refresh(): this;
|
||
insert(text: string, opts?: { replace?: boolean }): this;
|
||
wrap(before: string, after?: string): this;
|
||
|
||
// 选区/焦点
|
||
focus(): this;
|
||
blur(): this;
|
||
enable(): this;
|
||
disable(): this;
|
||
isDisabled(): boolean;
|
||
setReadOnly(readOnly: boolean): this;
|
||
isReadOnly(): boolean;
|
||
|
||
// 事件
|
||
on(name: EditorEventName, fn: (...args: any[]) => void): () => void;
|
||
off(name: EditorEventName, fn: (...args: any[]) => void): this;
|
||
|
||
// 命令
|
||
exec(action: string, ...args: any[]): this;
|
||
|
||
// 历史栈
|
||
undo(): this;
|
||
redo(): this;
|
||
canUndo(): boolean;
|
||
canRedo(): boolean;
|
||
|
||
// 模式与全屏
|
||
setMode(mode: EditMode): this;
|
||
getMode(): EditMode;
|
||
toggleFullscreen(): this;
|
||
isFullscreen(): boolean;
|
||
exitFullscreen(): this;
|
||
|
||
// 统计与状态
|
||
getStats(): EditorStats;
|
||
getStatus(): EditorStatus;
|
||
|
||
// 主题(v0.1.5: 实例级主题管理)
|
||
setTheme(theme: ThemeName): this;
|
||
getTheme(): string;
|
||
getThemeContext(): {
|
||
get(): string;
|
||
set(theme: string): string;
|
||
toggle(): string;
|
||
getResolved(): string;
|
||
getConfig(): object;
|
||
getVars(): object;
|
||
syncWithElement(element: HTMLElement, opts?: object): () => void;
|
||
adopt(): () => void;
|
||
watch(source: Function | string): () => void;
|
||
} | null;
|
||
|
||
// 插件
|
||
use(plugin: string | PluginObject, options?: object): this;
|
||
getPlugins(): PluginObject[];
|
||
addToolbarButton(config: ToolbarButtonConfig): this;
|
||
|
||
// 生命周期
|
||
destroy(): void;
|
||
isDestroyed(): boolean;
|
||
}
|
||
|
||
/** MarkdownEditor 别名 */
|
||
export const Editor: typeof MarkdownEditor;
|
||
|
||
/** 工厂函数:创建编辑器实例 */
|
||
export function create(container: string | HTMLElement, options?: MarkdownEditorOptions): MarkdownEditor;
|
||
|
||
/** 注册全局默认插件(作用于所有后续创建的实例) */
|
||
export function use(plugin: string | PluginObject, options?: object): typeof api;
|
||
|
||
/** 注册全局事件钩子 */
|
||
export function on(name: HookName, fn: (editor: MarkdownEditor) => void): () => void;
|
||
/** 注销全局事件钩子 */
|
||
export function off(name: HookName, fn: (editor: MarkdownEditor) => void): typeof api;
|
||
|
||
/** 切换全局主题 */
|
||
export function setTheme(theme: ThemeName, options?: object): typeof api;
|
||
/** 切换全局语言 */
|
||
export function setLocale(locale: string): typeof api;
|
||
/** 销毁所有全局资源 */
|
||
export function destroy(): void;
|
||
/** 获取库运行状态 */
|
||
export function getStatus(): {
|
||
version: string;
|
||
theme: string;
|
||
locale: string;
|
||
globalPlugins: string[];
|
||
presetPlugins: string[];
|
||
};
|
||
|
||
// ============ 解析器 ============
|
||
/** Markdown 解析函数 */
|
||
export function parseMarkdown(markdown: string, env?: RenderEnv): string;
|
||
/** URL 安全过滤(增强版) */
|
||
export function safeUrl(url: string): string;
|
||
/** 生成标题锚点 id(Unicode NFKC 规范化) */
|
||
export function slugify(text: string): string;
|
||
/** 清除渲染缓存(主题切换等场景) */
|
||
export function clearRenderCache(): void;
|
||
|
||
// ============ 插件 ============
|
||
/** 预设插件表 */
|
||
export const presetPlugins: {
|
||
autoSave: PluginObject;
|
||
exportTool: PluginObject;
|
||
searchReplace: PluginObject;
|
||
[key: string]: PluginObject;
|
||
};
|
||
|
||
/** 插件工具 */
|
||
export const pluginUtils: {
|
||
createManager(): PluginManager;
|
||
register(name: string, plugin: PluginObject): PluginManager;
|
||
unregister(name: string): PluginManager;
|
||
get(name: string): PluginObject | null;
|
||
has(name: string): boolean;
|
||
getAll(): PluginObject[];
|
||
getNames(): string[];
|
||
enable(name: string): PluginManager;
|
||
disable(name: string): PluginManager;
|
||
isEnabled(name: string): boolean;
|
||
getPreset(name: string): PluginObject | null;
|
||
getAllPresets(): { [key: string]: PluginObject };
|
||
createPlugin(config: object): PluginObject;
|
||
validatePlugin(plugin: PluginObject): { valid: boolean; errors: string[] };
|
||
};
|
||
|
||
/** 插件管理器 */
|
||
export class PluginManager {
|
||
register(name: string, plugin: PluginObject): this;
|
||
unregister(name: string): this;
|
||
get(name: string): PluginObject | null;
|
||
has(name: string): boolean;
|
||
getAll(): PluginObject[];
|
||
getNames(): string[];
|
||
enable(name: string): this;
|
||
disable(name: string): this;
|
||
isEnabled(name: string): boolean;
|
||
destroy(): void;
|
||
}
|
||
|
||
// ============ 常量 ============
|
||
export const DEFAULTS: Readonly<MarkdownEditorOptions>;
|
||
export const ICONS: { [key: string]: string };
|
||
export const THEMES: { [key: string]: any };
|
||
export const EDIT_MODES: EditMode[];
|
||
export const DEFAULT_TOOLBAR: ToolbarItem[];
|
||
export const TOOLBAR_ACTIONS: string[];
|
||
|
||
// ============ 工具集 ============
|
||
export const themeUtils: {
|
||
initTheme(): void;
|
||
switchTheme(theme: ThemeName): void;
|
||
toggleTheme(): void;
|
||
getCurrentTheme(): string;
|
||
getResolvedTheme(): string;
|
||
applyTheme(theme: ThemeName): void;
|
||
registerTheme(name: string, config: { extends?: string; [key: string]: any }): void;
|
||
unregisterTheme(name: string): void;
|
||
getAllThemes(): object;
|
||
getThemeNames(): string[];
|
||
hasTheme(name: string): boolean;
|
||
watchSystemTheme(): void;
|
||
unwatchSystemTheme(): void;
|
||
addThemeListener(fn: (theme: string, resolved: string) => void): () => void;
|
||
removeThemeListener(fn: (theme: string, resolved: string) => void): void;
|
||
clearThemeListeners(): void;
|
||
// v0.1.5 新增
|
||
exportCSSVars(el?: HTMLElement): Record<string, string>;
|
||
getCSSVariable(name: string, el?: HTMLElement): string;
|
||
applyThemeToElement(theme: string, target: HTMLElement): void;
|
||
followExternalTheme(opts: object, onChange: (theme: string) => void): () => void;
|
||
adoptFromParent(container: HTMLElement, onDetected: (theme: string) => void): () => void;
|
||
watch(source: Function | string, onChange: (theme: string) => void): () => void;
|
||
createInstanceTheme(editor: object): object;
|
||
[key: string]: any;
|
||
};
|
||
|
||
export const i18nUtils: {
|
||
initI18n(): void;
|
||
t(key: string, params?: object): string;
|
||
getCurrentLocale(): string;
|
||
setCurrentLocale(locale: string): void;
|
||
loadLocale(locale: string, messages: object): void;
|
||
addLocaleListener(fn: (locale: string) => void): () => void;
|
||
removeLocaleListener(fn: (locale: string) => void): void;
|
||
clearLocaleListeners(): void;
|
||
[key: string]: any;
|
||
};
|
||
|
||
export const animationUtils: {
|
||
cancelAll(): void;
|
||
[key: string]: any;
|
||
};
|
||
|
||
// ============ 默认导出 ============
|
||
export interface MeEditorAPI {
|
||
VERSION: string;
|
||
version: string;
|
||
MarkdownEditor: typeof MarkdownEditor;
|
||
Editor: typeof MarkdownEditor;
|
||
create: typeof create;
|
||
use: typeof use;
|
||
on: typeof on;
|
||
off: typeof off;
|
||
setTheme: typeof setTheme;
|
||
setLocale: typeof setLocale;
|
||
destroy: typeof destroy;
|
||
getStatus: typeof getStatus;
|
||
parseMarkdown: typeof parseMarkdown;
|
||
safeUrl: typeof safeUrl;
|
||
slugify: typeof slugify;
|
||
themes: typeof themeUtils;
|
||
i18n: typeof i18nUtils;
|
||
animations: typeof animationUtils;
|
||
plugins: typeof pluginUtils;
|
||
presetPlugins: typeof presetPlugins;
|
||
DEFAULTS: typeof DEFAULTS;
|
||
ICONS: typeof ICONS;
|
||
THEMES: typeof THEMES;
|
||
EDIT_MODES: typeof EDIT_MODES;
|
||
DEFAULT_TOOLBAR: typeof DEFAULT_TOOLBAR;
|
||
TOOLBAR_ACTIONS: typeof TOOLBAR_ACTIONS;
|
||
}
|
||
|
||
/** 默认导出 API 对象 */
|
||
declare const api: MeEditorAPI;
|
||
export default api;
|
||
|
||
export {
|
||
api,
|
||
api as meEditor,
|
||
api as MeEditor,
|
||
};
|