diff --git a/README.md b/README.md index a84937a..a2d2d06 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > 轻量、零依赖、精致美观的 Markdown Editor 库。单文件,开箱即用,中文优先。 -[![npm version](https://img.shields.io/badge/version-0.1.0-blue.svg)](https://www.npmjs.com/package/@metona-team/metona-editor) +[![npm version](https://img.shields.io/badge/version-0.1.1-blue.svg)](https://www.npmjs.com/package/@metona-team/metona-editor) [![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) [![tests](https://img.shields.io/badge/tests-407%20passed-brightgreen.svg)](./tests) [![coverage](https://img.shields.io/badge/coverage-97%25-brightgreen.svg)](./tests) diff --git a/package-lock.json b/package-lock.json index 5bce97c..9cf823b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@metona-team/metona-editor", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@metona-team/metona-editor", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "devDependencies": { "@babel/core": "^7.22.0", diff --git a/package.json b/package.json index 4b2550d..2d00cb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metona-team/metona-editor", - "version": "0.1.0", + "version": "0.1.1", "description": "轻量、零依赖、精致美观的 Markdown Editor 库。单文件,开箱即用。", "type": "module", "main": "dist/metona-editor.js", diff --git a/site/demo.html b/site/demo.html index 3e2a851..cff6b25 100644 --- a/site/demo.html +++ b/site/demo.html @@ -305,7 +305,7 @@ '', '| 名称 | 值 |', '| --- | --- |', - '| 版本 | 0.1.0 |', + '| 版本 | 0.1.1 |', '| 依赖 | 零 |', '| 测试 | 390+ |', '', diff --git a/src/animations.js b/src/animations.js index d968888..57189c8 100644 --- a/src/animations.js +++ b/src/animations.js @@ -1,8 +1,9 @@ /** * MetonaEditor Animations - 动画管理 * @module animations - * @version 0.1.0 - * @description 动画注册与管理 + * @version 0.1.1 + * @description 动画注册与管理(当前为 CSS 动画元数据管理,供未来扩展如 Toast/通知组件的进出场动画使用; + * 内置 7 种预设动画曲线配置;编辑器核心当前未直接调用此模块) */ import { ANIMATIONS } from './constants.js'; diff --git a/src/constants.js b/src/constants.js index 283254d..f1baf3b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,7 +1,7 @@ /** * MetonaEditor Constants - 常量定义 * @module constants - * @version 0.1.0 + * @version 0.1.1 * @description 默认配置、主题、动画、工具栏等常量 */ diff --git a/src/core.js b/src/core.js index f851265..cafeed1 100644 --- a/src/core.js +++ b/src/core.js @@ -1,7 +1,7 @@ /** * MetonaEditor Core - 编辑器核心 * @module core - * @version 0.1.0 + * @version 0.1.1 * @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 } from './themes.js'; +import { getTheme, applyTheme, saveTheme, getThemeConfig, setThemeVariables } from './themes.js'; const MODES = ['edit', 'split', 'preview']; @@ -104,6 +104,10 @@ class MarkdownEditor { saveTheme(this.config.theme); } + // 将 CSS 变量限定到当前实例的 wrapper 元素,避免污染全局样式 + const resolvedThemeConfig = getThemeConfig(this.config.theme); + setThemeVariables(resolvedThemeConfig, this.el); + this._buildToolbar(); this._bindEvents(); @@ -773,8 +777,9 @@ class MarkdownEditor { const ta = this.textarea; const start = ta.selectionStart; const end = ta.selectionEnd; - const replace = opts.replace || false; - ta.value = ta.value.slice(0, replace ? start : start) + text + ta.value.slice(replace ? end : end); + const replace = opts.replace === true; + // replace=true: 替换选区内容;replace=false: 在光标处插入,保留选区 + ta.value = ta.value.slice(0, start) + text + ta.value.slice(replace ? end : start); ta.focus(); const pos = start + text.length; ta.selectionStart = ta.selectionEnd = pos; diff --git a/src/i18n.js b/src/i18n.js index eef9dfc..36e545c 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -1,7 +1,7 @@ /** * MetonaEditor i18n - 国际化管理 * @module i18n - * @version 0.1.0 + * @version 0.1.1 * @description 多语言支持、语言切换和翻译管理 */ diff --git a/src/icons.js b/src/icons.js index abb9b4d..838a244 100644 --- a/src/icons.js +++ b/src/icons.js @@ -1,7 +1,7 @@ /** * MetonaEditor Icons — 工具栏图标SVG定义 * @module icons - * @version 0.1.0 + * @version 0.1.1 * @description 工具栏格式化按钮 SVG 图标 */ diff --git a/src/index.js b/src/index.js index e120dc6..6ed6837 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ /** * MetonaEditor - 轻量级 Markdown Editor 库 * @module metona-editor - * @version 0.1.0 + * @version 0.1.1 * @author thzxx * @description 现代化、零依赖、易扩展、易维护、易使用的 Markdown 编辑器库。单文件,开箱即用。 * @license MIT @@ -29,7 +29,7 @@ import { animationUtils } from './animations.js'; import { DEFAULTS, ICONS, THEMES, EDIT_MODES, DEFAULT_TOOLBAR, TOOLBAR_ACTIONS } from './constants.js'; // 版本信息 -const VERSION = '0.1.0'; +const VERSION = '0.1.1'; /** * 全局默认插件队列 diff --git a/src/locales.js b/src/locales.js index 5bf6b39..fa90840 100644 --- a/src/locales.js +++ b/src/locales.js @@ -1,7 +1,7 @@ /** * MetonaEditor Locales — 国际化翻译数据 * @module locales - * @version 0.1.0 + * @version 0.1.1 * @description 内置 zh-CN / en-US 完整翻译 */ diff --git a/src/parser.js b/src/parser.js index 5056c61..fcdcd49 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,7 +1,7 @@ /** * MetonaEditor Parser - 轻量 Markdown 解析器 * @module parser - * @version 0.1.0 + * @version 0.1.1 * @description 自研零依赖 Markdown 解析器,覆盖 CommonMark 子集 + GFM 扩展 * * 支持语法: @@ -286,16 +286,16 @@ const renderInline = (text, env) => { return `${url}`; }); - // 6. 粗体 **text** / __text__ - s = s.replace(/\*\*([^*]+)\*\*/g, '$1'); - s = s.replace(/__([^_]+)__/g, '$1'); + // 6. 粗体 **text** / __text__(内容首字符不能是分隔符,避免 *** 歧义) + s = s.replace(/\*\*([^*].*?)\*\*/g, '$1'); + s = s.replace(/__([^_].*?)__/g, '$1'); - // 7. 斜体 *text* / _text_ + // 7. 斜体 *text* / _text_(贪婪匹配,避免匹配 ** 或 __ 前缀) s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1$2'); s = s.replace(/(^|[^_])_([^_\n]+)_/g, '$1$2'); - // 8. 删除线 ~~text~~ - s = s.replace(/~~([^~]+)~~/g, '$1'); + // 8. 删除线 ~~text~~(非贪婪匹配,支持内含单个 ~ 字符) + s = s.replace(/~~(.+?)~~/g, '$1'); // 9. 还原行内代码 s = s.replace(/\u0000(\d+)\u0000/g, (m, idx) => `${escapeHTML(codes[+idx])}`); diff --git a/src/plugins.js b/src/plugins.js index cac1e86..47efb81 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -1,7 +1,7 @@ /** * MetonaEditor Plugins - 插件系统 * @module plugins - * @version 0.1.0 + * @version 0.1.1 * @description 插件管理器 + 预设插件(autoSave / exportTool / searchReplace) * * 插件约定: diff --git a/src/styles.js b/src/styles.js index d11c74f..5940d7e 100644 --- a/src/styles.js +++ b/src/styles.js @@ -1,7 +1,7 @@ /** * MetonaEditor Styles - 编辑器样式 * @module styles - * @version 0.1.0 + * @version 0.1.1 * @description 编辑器 UI 样式注入与管理 */ diff --git a/src/themes.js b/src/themes.js index 9ef02db..1e3a5ba 100644 --- a/src/themes.js +++ b/src/themes.js @@ -1,7 +1,7 @@ /** * MetonaEditor Themes - 主题管理 * @module themes - * @version 0.1.0 + * @version 0.1.1 * @description 主题系统、自定义主题和主题切换 */ @@ -70,11 +70,13 @@ export const applyTheme = (theme) => { /** * 设置主题CSS变量 + * @param {Object} config - 主题配置对象 + * @param {HTMLElement} [target] - 目标元素,不传则设置到 documentElement(全局默认) */ -export const setThemeVariables = (config) => { +export const setThemeVariables = (config, target) => { if (typeof document === 'undefined' || !config || typeof config !== 'object') return; - const root = document.documentElement; + const root = target || document.documentElement; // 基础变量(保留用于全局与组件回退) root.style.setProperty('--md-bg', config.bg); root.style.setProperty('--md-text', config.text); diff --git a/src/utils.js b/src/utils.js index 60aa9af..28fb656 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,7 +1,7 @@ /** * MetonaEditor Utils - 工具函数 * @module utils - * @version 0.1.0 + * @version 0.1.1 * @description 通用工具函数集合 */ diff --git a/tests/core.test.js b/tests/core.test.js index 880b215..d0c77d2 100644 --- a/tests/core.test.js +++ b/tests/core.test.js @@ -132,6 +132,22 @@ describe('MarkdownEditor - 内容 API', () => { expect(ed.getValue()).toBe('aXbc'); }); + test('insert replace:true 替换选区', () => { + ed.setValue('hello world'); + ed.textarea.selectionStart = 0; + ed.textarea.selectionEnd = 5; + ed.insert('hi', { replace: true }); + expect(ed.getValue()).toBe('hi world'); + }); + + test('insert replace:false 在光标处插入(保留选区后的内容)', () => { + ed.setValue('abcdef'); + ed.textarea.selectionStart = 3; + ed.textarea.selectionEnd = 3; + ed.insert('X'); + expect(ed.getValue()).toBe('abcXdef'); + }); + test('wrap 包裹选区', () => { ed.setValue('hello'); ed.textarea.selectionStart = 0; diff --git a/types/index.d.ts b/types/index.d.ts index ab1407f..01fb37c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,5 +1,5 @@ // Type definitions for MetonaEditor -// Project: https://git.metona.cn/MetonaTeam/MetonaToast +// Project: https://git.metona.cn/MetonaTeam/MetonaEditor // Author: thzxx declare const VERSION: string;