release: v0.1.5 — theme system usability, extensibility, external theme following

feat(themes): comprehensive theme system enhancement
- Fix resolveTheme('auto') always resolves to system theme (no stale override)
- Add per-instance theme isolation via createInstanceTheme()
- Add MarkdownEditor.setTheme()/getTheme()/getThemeContext() instance methods
- Add 'themeChange' instance event with { theme, resolved, config } payload
- Multi-instance independent themes (no cross-contamination)

feat(themes): external theme following (v0.1.5)
- followExternalTheme() — follow theme via data attr, CSS class, or callback
- adoptFromParent() — inherit theme from parent container
- watch() — watch external theme source (function or selector)

feat(themes): extensibility (v0.1.5)
- registerTheme() supports 'extends' option for theme inheritance
- exportCSSVars() — export all CSS variable values from element
- getCSSVariable() — query single CSS variable
- applyThemeToElement() — apply theme to any DOM element

chore: bump version 0.1.4 → 0.1.5 across all files
test(themes): 30 new tests covering all v0.1.5 theme APIs (499 total)
This commit is contained in:
2026-07-24 09:53:20 +08:00
parent f8b9f4a761
commit 517dc34435
16 changed files with 1087 additions and 277 deletions
+45 -8
View File
@@ -1,7 +1,7 @@
/**
* MetonaEditor Core - 编辑器核心
* @module core
* @version 0.1.4
* @version 0.1.5
* @description MarkdownEditor 类实现:DOM 构建、事件、渲染、历史栈、选区操作、模式切换
*/
@@ -11,7 +11,7 @@ import { injectStyles } from './styles.js';
import { t as i18nT, getCurrentLocale, getLocaleDirection } from './i18n.js';
import { parseMarkdown } from './parser.js';
import { presetPlugins } from './plugins.js';
import { getTheme, applyTheme, saveTheme, getThemeConfig, setThemeVariables } from './themes.js';
import { resolveTheme, getThemeConfig, setThemeVariables, createInstanceTheme } from './themes.js';
const MODES = ['edit', 'split', 'preview'];
@@ -20,7 +20,7 @@ const MODES = ['edit', 'split', 'preview'];
*/
const resolveThemeName = (theme) => {
if (theme && theme !== 'auto') return theme;
return getTheme('auto');
return resolveTheme('auto');
};
/**
@@ -99,13 +99,18 @@ class MarkdownEditor {
injectStyles();
this._buildDOM();
// 主题同步:用户显式传 theme 时覆盖全局 CSS 变量并持久化,否则尊重 localStorage
// 实例级主题管理(v0.1.5: 隔离 CSS 变量到 wrapper,支持独立主题)
this._themeCtx = createInstanceTheme(this);
// 向后兼容:用户显式传 theme 时同步到全局并持久化
if (options.theme) {
applyTheme(this.config.theme);
saveTheme(this.config.theme);
// 全局同步(保留原有行为)
if (typeof document !== 'undefined') {
document.documentElement.setAttribute('data-md-theme', resolveTheme(options.theme));
}
}
// CSS 变量限定到当前实例 wrapper 元素,避免污染全局样式
// 确保 CSS 变量作用到实例 wrapper
const resolvedThemeConfig = getThemeConfig(this.config.theme);
setThemeVariables(resolvedThemeConfig, this.el);
@@ -981,6 +986,38 @@ class MarkdownEditor {
isDestroyed() { return this._destroyed; }
/**
* 设置实例主题(v0.1.5: 实例隔离,不影响其他编辑器)
* @param {string} theme - 主题名
* @returns {MarkdownEditor}
*/
setTheme(theme) {
if (!theme) return this;
this.config.theme = theme;
if (this._themeCtx) {
this._themeCtx.set(theme);
}
return this;
}
/**
* 获取实例当前主题名
* @returns {string}
*/
getTheme() {
if (this._themeCtx) return this._themeCtx.get();
return this.config.theme || 'auto';
}
/**
* 获取实例主题上下文(高级 API)
* 提供 syncWithElement / adopt / watch 等外部跟随能力
* @returns {Object|null}
*/
getThemeContext() {
return this._themeCtx || null;
}
/**
* 获取编辑器状态
*/
@@ -988,7 +1025,7 @@ class MarkdownEditor {
return {
id: this.id,
mode: this._mode,
theme: this.config.theme,
theme: this.getTheme(),
locale: getCurrentLocale(),
fullscreen: this._fullscreen,
readOnly: this.config.readOnly || false,