diff --git a/docs/metona-editor-demo.html b/docs/metona-editor-demo.html index 79d8528..5281b54 100644 --- a/docs/metona-editor-demo.html +++ b/docs/metona-editor-demo.html @@ -4,518 +4,272 @@ -MetonaEditor v0.1.10 — 在线演示 +MetonaEditor v0.2.4 — 全功能演示
-

MetonaEditor

+

MetonaEditor v0.2.4

- v0.1.10 - 零依赖 - 532 tests - MIT - 中文优先 + TypeScript + 零运行时依赖 + 725 tests + 6 个插件 + 桌面端优先
-
- 🏠 - +
+ 🏠 - - + - - + -
- - - -
- + + +
- - -
- - -
- - -
- - -
- - - + + + + + + +
-
-
-
+
-
-
📝
-

丰富语法

-

数学公式、脚注、定义列表、嵌套列表、表格对齐、任务列表、Emoji

-
-
-
⌨️
-

快捷键

-

Ctrl+B 粗体 · Ctrl+I 斜体 · Ctrl+S 保存 · Ctrl+F 搜索 · Ctrl+H 替换

-
-
-
🎨
-

4 套主题

-

亮色 · 暗色 · 暖色 · 跟随系统,实例级隔离,外部主题跟随

-
-
-
🔌
-

4 个插件

-

自动保存草稿 · 导出 MD/HTML · 搜索替换 · 图片粘贴

-
-
-
🧩
-

编辑增强

-

行号装订线 · 智能 Enter · 括号自动闭合 · 拖放文件 · 大纲面板

-
-
-
🛡️
-

安全

-

XSS 防护 · URL 协议过滤 · HTML 实体保护 · sanitize 钩子

-
+
🏗️

TypeScript 全模块重构

12 个 .ts 源文件,严格模式,完整类型导出

+
📝

自研解析器 99% 行覆盖

CommonMark + GFM:粗斜体、链接内格式、脚注、数学公式等

+
📂

磁盘文件读写

File System Access API 打开 / 保存真实 .md 文件

+
🔢

行号装订线

当前行高亮,与编辑区滚动同步

+
🧘

Zen 专注模式

工具栏自动隐藏,鼠标移到顶部滑入

+
⌨️

快捷键面板

按 ? 查看全部快捷键,Ctrl+F/H 搜索替换

+
📊

Mermaid 图表

```mermaid 代码块,加载 Mermaid.js 即可渲染

+
🔌

6 个预设插件

autoSave / exportTool / searchReplace / imagePaste / shortcutHelp / fileSystem

+ diff --git a/docs/metona-editor-docs.html b/docs/metona-editor-docs.html index d6a44af..ad349b2 100644 --- a/docs/metona-editor-docs.html +++ b/docs/metona-editor-docs.html @@ -4,179 +4,68 @@ -MetonaEditor · 文档 +MetonaEditor v0.2.4 · 文档
-

文档

-

MetonaEditor v0.1.10 完整 API、配置与使用指南

+

API 文档

+

MetonaEditor v0.2.4 · TypeScript · 完整配置、API 与使用指南

@@ -191,18 +80,20 @@ 内容操作 命令执行 历史栈 - 模式与全屏 + 模式 / 全屏 / Zen 统计与状态 - 启用/禁用/只读 + 启用 / 禁用 / 只读 事件系统 插件管理 - 生命周期 + 快捷键管理 + 生命周期
静态 API
- 顶层函数 -
语法参考
- 解析器语法 + 全局函数 +
解析器
+ 语法参考 + 解析器 API
插件
- 插件约定 + 插件约定 预设插件
主题
主题系统 @@ -212,94 +103,77 @@
- +

安装与引入

-
# npm 安装
 npm install @metona-team/metona-editor
 
-# ES Module
+// TypeScript / ES Module
 import MeEditor from '@metona-team/metona-editor';
 const editor = MeEditor.create('#editor', {
   value: '# Hello World',
   mode: 'split',
 });
 
-# 浏览器 UMD
+// 浏览器 UMD
 <script src="dist/metona-editor.js"></script>
 <script>
 const editor = MeEditor.create('#editor');
 </script>
- +

全部配置项

-

所有配置均为可选,以下是默认值:

- +

所有配置均为可选。

MeEditor.create(container, {
   // 内容
-  value: '',              // 初始 Markdown 文本
-  placeholder: '',        // 占位符
+  value: '',              placeholder: '',
 
   // 视图
-  mode: 'split',          // 'edit' | 'split' | 'preview'
-  height: 400,            // 数字为 px,字符串原样使用
-  toolbar: DEFAULT_TOOLBAR, // 工具栏配置,false 隐藏
-  wordCount: true,        // 字数统计状态栏
+  mode: 'split',          height: 400,
+  toolbar: DEFAULT_TOOLBAR, wordCount: true,
+  lineNumbers: true,       outline: false,
 
   // 行为
-  autofocus: false,       // 自动聚焦
-  spellcheck: false,      // 拼写检查
-  readOnly: false,        // 只读模式
-  historyLimit: 100,      // 历史栈上限
-  historyDebounce: 400,   // 历史防抖延迟(ms)
-  syncScroll: true,       // 分屏模式同步滚动
-  tabSize: 2,             // 制表符空格数(0 表示 \t)
+  autofocus: false,       spellcheck: false,
+  readOnly: false,        tabSize: 2,
+  historyLimit: 100,      historyDebounce: 400,
+  syncScroll: true,       autoBrackets: true,
+  zenMode: false,         wordWrap: true,
 
-  // 主题与国际化
-  theme: 'auto',          // 'light' | 'dark' | 'auto' | 'warm' | 自定义
-  locale: 'zh-CN',        // 'zh-CN' | 'en-US' | 自定义
+  // 主题 / 语言
+  theme: 'auto',          locale: 'zh-CN',
 
-  // 自定义渲染
-  render: null,           // (md, env) => html
-  highlight: null,        // (code, lang) => html
-  sanitize: null,         // (html) => safeHtml
+  // 钩子
+  render: null,           highlight: null,
+  sanitize: null,
 
   // 外观
-  className: '',          // 容器额外 class
-  style: {},              // 内联样式
+  className: '',          style: {},
 
-  // 插件与回调
-  plugins: [],            // 实例级插件数组
-  onChange: null,         // (value, editor) => void
-  onInput: null,          // (value, editor) => void
-  onFocus: null,          // (editor) => void
-  onBlur: null,           // (editor) => void
-  onSave: null,           // (value, editor) => void
-  onModeChange: null,     // (mode, editor) => void
-  onFullscreen: null,    // (fullscreen, editor) => void
-  onCreate: null,         // (editor) => void
-  onDestroy: null,        // (editor) => void
+  // 插件 / 回调
+  plugins: [],
+  onChange: null,         onInput: null,
+  onFocus: null,          onBlur: null,
+  onSave: null,           onModeChange: null,
+  onFullscreen: null,    onCreate: null,
+  onDestroy: null,        onLinkClick: null,
 });
- +
-

工具栏配置

- -
// 默认工具栏(完整按钮)
+

工具栏

+
// 默认完整工具栏
 MeEditor.create(container, { toolbar: true });
 
-// 隐藏工具栏
+// 隐藏
 MeEditor.create(container, { toolbar: false });
 
-// 自定义工具栏 — '|' 为分隔符
-MeEditor.create(container, {
-  toolbar: ['bold', 'italic', '|', 'h1', 'h2', '|', 'undo', 'redo'],
-});
- +// 自定义 | 为分隔符 +MeEditor.create(container, { toolbar: ['bold', 'italic', '|', 'undo', 'redo'] });

可用动作:

bolditalicstrikethroughunderlinecode @@ -311,306 +185,246 @@ MeEditor.create

- -
-

实例 API

- + +

实例 API

内容操作

-
editor.getValue();                    // 获取 Markdown 文本
-editor.setValue(md, { silent });      // 设置内容,silent 不触发 change
-editor.getHTML();                     // 获取渲染后的 HTML
-editor.refresh();                     // 强制刷新预览(内容未变时)
-editor.insert(text, { replace });     // 光标处插入 / 替换选区
-editor.wrap(before, after);           // 选区包裹
-editor.focus();                       // 聚焦
-editor.blur();                        // 失焦
+
editor.getValue();                    // => string
+editor.setValue(md, { silent });      // => this
+editor.getHTML();                     // => string
+editor.refresh();                     // 强制重渲染
+editor.insert(text, { replace });     // => this
+editor.wrap(before, after);           // => this
+editor.focus(); blur();
-
-

命令执行

-
editor.exec(action, ...args);         // 执行命令,返回 this 链式调用
-
-editor.exec('bold').exec('h1');        // 链式调用
- +

命令执行

+
editor.exec(action) // => this, 支持链式
- - - - - - - - - - - - + + + + + + + + + + - + + + - + + +
action效果快捷键
bold加粗 **text**Ctrl+B
italic斜体 *text*Ctrl+I
strikethrough删除线 ~~text~~
underline下划线 <u>text</u>Ctrl+U
code行内代码 `text`Ctrl+E
h1/h2/h3标题 # / ## / ###Ctrl+1/2/3
quote引用 >Ctrl+Q
ul/ol无序/有序列表
indent/outdent缩进/反缩进Tab/Shift+Tab
hr水平线 ---
link插入链接 [text](url)Ctrl+K
image插入图片 ![alt](url)
bold**text**Ctrl+B
italic*text*Ctrl+I
strikethrough~~text~~
underline<u>text</u>Ctrl+U
code`text`Ctrl+E
h1/h2/h3# / ## / ###Ctrl+1/2/3
quote> textCtrl+Q
ul/ol- / 1. 列表
link[text](url)Ctrl+K
image![alt](url)
table插入表格
undo/redo撤销/重做Ctrl+Z / Ctrl+Y
hr---水平线
indent/outdent缩进/反缩进Tab / Shift+Tab
undo/redo撤销/重做Ctrl+Z / Y
edit/split/preview切换模式
fullscreen切换全屏
fullscreen全屏
zenZen 模式
wordwrap自动换行
-
-

历史栈

-
editor.undo();                        // 撤销
-editor.redo();                        // 重做
-editor.canUndo();                     // 是否可撤销
-editor.canRedo();                     // 是否可重做
+

历史栈

+
editor.undo();    editor.redo();
+editor.canUndo();  editor.canRedo();
-
-

模式与全屏

-
editor.setMode('split');              // 设置模式:edit | split | preview
-editor.getMode();                     // 获取当前模式
-editor.toggleFullscreen();            // 切换全屏
-editor.exitFullscreen();              // 退出全屏
-editor.isFullscreen();                // 是否全屏
+

模式 / 全屏 / Zen

+
editor.setMode('split');  editor.getMode();
+editor.toggleFullscreen();  isFullscreen();  exitFullscreen();
+editor.toggleZen();  isZen();
+editor.toggleWordWrap();  setWordWrap(true);  isWordWrap();
-
-

统计与状态

+

统计与状态

editor.getStats();
-// => { characters, words, chineseChars, englishWords, lines, readingTime }
-
+// => { characters, words, chineseChars, englishWords, lines, readingTime }
 editor.getStatus();
-// => { id, mode, theme, locale, fullscreen, readOnly, disabled, destroyed, plugins }
+// => { id, mode, theme, locale, fullscreen, readOnly, disabled, destroyed, plugins }
-
-

启用/禁用/只读

-
editor.enable();                      // 启用
-editor.disable();                     // 禁用
-editor.isDisabled();                  // 是否禁用
-editor.setReadOnly(true);             // 只读模式
-editor.isReadOnly();                  // 是否只读
+

启用 / 禁用 / 只读

+
editor.enable();  disable();  isDisabled();
+editor.setReadOnly(true);  isReadOnly();
-
-

事件系统

- +

事件系统

- - + + - - - + + + + + - - - + + +
事件名触发时机回调参数
inputtextarea 原生 input(value, editor)
事件触发时机参数
inputtextarea 输入(value, editor)
change内容变化(value, editor)
focus聚焦(editor)
blur失焦(editor)
saveCtrl+S(value, editor)
focus / blur聚焦/失焦(editor)
saveCtrl+S(editor)
modeChange模式切换(mode, editor)
fullscreen全屏切换(fullscreen, editor)
themeChange主题变更({ theme, resolved, config })
localeChange语言变更({ locale, direction })
zenChangeZen 切换(zen, editor)
destroy销毁()
autosaveautoSave 保存({ key, value })
beforeRender渲染前(editor)
afterRender渲染后(editor)
beforeRender / afterRender渲染前后(editor)
autosaveautoSave 触发({ key, value })
linkClick预览链接点击({ href, text })
- -

全局钩子(所有实例共享)

+

全局钩子

- - - - - - + + +
钩子触发时机
beforeCreate构造函数初始化前
afterCreate构造函数初始化完成
beforeRender每次渲染前(全局)
afterRender每次渲染后(全局)
beforeDestroydestroy 前
afterDestroydestroy 后
beforeCreate / afterCreate实例构造前后
beforeRender / afterRender全局渲染前后
beforeDestroy / afterDestroy销毁前后
-
-

插件管理

-
editor.use(plugin, options);          // 安装插件
-editor.getPlugins();                  // 获取已安装插件列表
-editor.addToolbarButton(config);      // 追加工具栏按钮
+

插件管理

+
editor.use(plugin, options);      // 安装
+editor.unuse(name);               // 卸载
+editor.getPlugins();              // 已安装列表
+editor.addToolbarButton(config);  // 追加按钮
-
-

生命周期

-
editor.destroy();                     // 销毁实例
-editor.isDestroyed();                 // 是否已销毁
+

快捷键管理

+
editor.registerShortcut('Ctrl+Shift+T', handler, 'desc');
+editor.unregisterShortcut('Ctrl+Shift+T');
+editor.getShortcuts();
- -
-

静态 API

+

生命周期 / Toast

+
editor.destroy();  isDestroyed();
+editor.toast(msg, { type, duration, animation });
+
+ +

静态 API

import MeEditor from '@metona-team/metona-editor';
 
 // 工厂函数
 MeEditor.create(container, options);
 
 // 全局默认插件
-MeEditor.use(presetPlugins.autoSave, { delay: 2000 });
-MeEditor.use('searchReplace');
+MeEditor.use('autoSave', { delay: 2000 });
 MeEditor.use(customPlugin);
 
-// 全局事件钩子
-MeEditor.on('beforeCreate', (editor) => {});
+// 全局钩子
+MeEditor.on('beforeCreate', (editor) => {});
 MeEditor.off('beforeCreate', handler);
 
-// 主题与语言
+// 全局主题 / 语言
 MeEditor.setTheme('dark');
 MeEditor.setLocale('en-US');
 
-// 状态查询
-MeEditor.getStatus(); // => { version, theme, locale, globalPlugins, presetPlugins }
-
-// 销毁全局资源
-MeEditor.destroy();
-
-// 内置解析器
-import { parseMarkdown, safeUrl, slugify } from '@metona-team/metona-editor';
-parseMarkdown('# Hello');           // => '<h1 id="hello">Hello</h1>'
-safeUrl('javascript:alert(1)');     // => ''
-slugify('Hello World');             // => 'hello-world'
+// 解析器独立使用 +import { parseMarkdown, parseTokens, renderTokens } from '@metona-team/metona-editor';
- -
-

解析器语法参考

- + +

解析器语法参考

- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + +
语法示例输出
标题# H1 ~ ###### H6<h1> ~ <h6>
段落纯文本<p>
粗体**bold** __bold__<strong>
斜体*italic* _italic_<em>
删除线~~text~~<del>
高亮标记==text==<mark>
上标x^2^<sup>
下标H~2~O<sub>
行内代码`code`<code>
代码块```lang<pre><code>
引用> quote<blockquote>
无序列表- item<ul><li>
有序列表1. item<ol><li>
任务列表- [x] done<li class="me-task-item">
水平线--- *** ___<hr>
表格| a | b |<table>(支持 :---: 列对齐)
链接[text](url)<a>
图片![alt](url)<img>
自动链接<https://...><a>
Emoji:smile: :rocket:😊 🚀(80+ 常用)
标题# H1 ~ ###### H6<h1> ~ <h6>
Setext 标题H1\n===<h1> / <h2>
粗体**bold** __bold__<strong>
粗斜体 v0.1.15***text*** ___text___<em><strong>
斜体*italic* _italic_<em>
删除线 / 高亮~~del~~ ==mark==<del> / <mark>
上标 / 下标x^2^ H~2~O<sup> / <sub>
行内代码`code`<code>
代码块```lang<pre><code>
缩进代码块 code<pre><code>
引用> quote<blockquote>
无序列表- * + item<ul><li>
有序列表1. item<ol><li>
嵌套列表多级缩进多级 <ul>/<ol>
任务列表- [x] done<li class="me-task-item">
水平线--- *** ___<hr>
表格| a | b |<table> + 列对齐
链接 v0.1.15[**t**](url)文本内支持行内格式
图片![alt](url)<img>
引用链接 v0.2.2[text][ref] + [ref]: url<a> 引用式
引用图片 v0.2.2![alt][ref] + [ref]: url<img> 引用式
自动链接<https://...><a>
数学公式$E=mc^2$<span> / <div>
脚注text[^1]<sup> + 底部
定义列表Term\n: def<dl><dt><dd>
Emoji:smile: :rocket:😊 🚀(150+)
反斜杠转义 v0.1.15\* \_取消标点特殊含义
- -

XSS 防护

-

所有文本经 HTML 转义。URL 自动过滤 javascript: vbscript: file: 及非图片 data: 协议。

+

XSS 防护

+

所有文本经 escapeHTML 转义,URL 过滤 javascript: / vbscript: / file: / 非图片 data:,提供 sanitize 钩子。

- -
-

插件约定

+

解析器 API

+

parseTokens / renderTokens

+
import { parseTokens, renderTokens } from '@metona-team/metona-editor';
 
+const { tokens, footnotes } = parseTokens('# Hello\n\n**world**');
+tokens.unshift({ type: 'hr' });  // 渲染前变换
+const html = renderTokens(tokens, env, footnotes);
+ +

registerBlockHandler

+
import { registerBlockHandler } from '@metona-team/metona-editor';
+registerBlockHandler({
+  name: 'custom', priority: 8.5,
+  test: (line) => line.match(/^:::/),
+  parse: (lines, i, match) => ({ token: { type: 'alert' }, newIndex: j })
+});
+
+ + +

插件约定

const myPlugin = {
-  name: 'myPlugin',
-  description: '我的自定义插件',
-
-  install(editor, options) {
-    // this 指向插件对象本身
-    this._timer = null;
-    editor.on('change', this._onChange);
-  },
-
-  destroy(editor) {
-    if (this._timer) clearTimeout(this._timer);
-    editor.off('change', this._onChange);
-  },
+  name: 'myPlugin', description: '我的插件',
+  install(editor) { editor.on('change', this._onChange); },
+  destroy(editor) { editor.off('change', this._onChange); },
 };
-
-

预设插件

- -

autoSave — 自动保存草稿

-
editor.use(presetPlugins.autoSave, {
-  key: 'me-draft-' + editor.id,  // localStorage key
-  delay: 1000,                    // 防抖延迟(ms)
-});
-editor.restoreDraft();             // 恢复草稿
-editor.clearDraft();               // 清除草稿
-editor.getDraftKey();              // 获取存储 key
- -

exportTool — 导出文件

-
editor.use(presetPlugins.exportTool);
-editor.exportMarkdown('my-doc.md');
-editor.exportHTML('my-doc.html', {
-  title: '文档标题',
-  css: 'body { font-family: sans-serif; }',
-  lang: 'zh-CN',
-});
- -

searchReplace — 查找替换

-
editor.use(presetPlugins.searchReplace);
-// Ctrl+F → 查找  Ctrl+H → 替换
-// Enter → 下一个  Shift+Enter → 上一个  Escape → 关闭
- -

imagePaste — 粘贴图片转 base64

-
editor.use(presetPlugins.imagePaste);
-// 在编辑区 Ctrl+V 粘贴剪贴板图片
-// 自动插入 ![](data:image/png;base64,...)
+

预设插件

+

autoSave

+
editor.use('autoSave', { key: 'draft-key', delay: 1000 });
+editor.restoreDraft();  editor.clearDraft();
+

exportTool

+
editor.use('exportTool');
+editor.exportMarkdown('doc.md');  editor.exportHTML('doc.html');
+

searchReplace

+
editor.use('searchReplace');
+// Ctrl+F 查找 / Ctrl+H 替换
+

imagePaste

+
editor.use('imagePaste');  // 粘贴图片自动转 base64
+

shortcutHelp

+
editor.use('shortcutHelp');  // 按 ? 弹出快捷键面板
+

fileSystem

+
editor.use('fileSystem');
+editor.openFile();  editor.saveFile();  editor.saveFileAs();
- -
-

主题系统

+ +

主题系统

+
// 实例级
+editor.setTheme('dark');  editor.getTheme();
 
-
// 全局切换主题
-MeEditor.setTheme('light');         // 亮色
-MeEditor.setTheme('dark');          // 暗色
-MeEditor.setTheme('warm');          // 暖色
-MeEditor.setTheme('auto');          // 自动(默认)
-
-// 实例级主题(v0.1.5,每个编辑器独立)
-editor.setTheme('dark');
-editor.getTheme();                   // 'dark'
-editor.on('themeChange', ({ theme }) => {});
-
-// 注册自定义主题(v0.1.5: 支持继承)
+// 注册自定义
 import { themeUtils } from '@metona-team/metona-editor';
-themeUtils.registerTheme('ocean', {
-  extends: 'dark',                  // 继承 dark 主题
-  accent: '#00ddff',
-});
+themeUtils.registerTheme('ocean', { extends: 'dark', accent: '#00ddff' });
 
-// 外部主题跟随(v0.1.5)
-themeUtils.followExternalTheme(
-  { element: document.body, attr: 'data-theme' },
-  (theme) => editor.setTheme(theme)
-);
-
-// 从父容器继承主题
-themeUtils.adoptFromParent(editor.container,
-  (theme) => editor.setTheme(theme)
-);
+// 外部跟随
+editor.getThemeContext().adopt();  // 从父容器继承
 
 // CSS 变量导出
-themeUtils.exportCSSVars();              // { '--md-bg': '#fff', ... }
-themeUtils.getCSSVariable('accent');    // '#3b82f6'
- -

CSS 变量限定在 .me-wrapper 元素上,不污染全局。支持 @media print 打印样式。v0.1.5 新增实例级主题隔离、外部跟随、主题继承。

+themeUtils.exportCSSVars(); themeUtils.getCSSVariable('accent');
- -
-

国际化

+ +

国际化

+
// 切换
+editor.setLocale('en-US');  MeEditor.setLocale('zh-CN');
 
-
// 切换语言
-MeEditor.setLocale('en-US');
-MeEditor.setLocale('zh-CN');
-
-// 翻译函数
+// 添加翻译
 import { i18nUtils } from '@metona-team/metona-editor';
-i18nUtils.t('bold');                 // => '粗体'(zh-CN)
-i18nUtils.t('bold');                 // => 'Bold'(en-US)
+i18nUtils.addTranslations('ja', { bold: '太字' });
 
-// 添加语言
-i18nUtils.addTranslations('ja', { bold: '太字', italic: '斜体' });
+// 远程加载
+await i18nUtils.loadRemote('/locales/ja.json', 'ja');
 
 // 格式化
-i18nUtils.formatNumber(1234567);              // => '1,234,567'
-i18nUtils.formatCurrency(99.99, 'USD');       // => '$99.99'
-i18nUtils.formatDate('2024-01-15');           // => '1/15/2024'
+i18nUtils.formatNumber(1234567); +i18nUtils.formatDate('2024-01-15');
@@ -619,24 +433,18 @@ i18nUtils.formatDate
- MetonaEditor v0.1.10 · 源码仓库 · MIT License + MetonaEditor v0.2.4 · TypeScript · 源码仓库 · MIT License
- diff --git a/docs/metona-toast-demo.html b/docs/metona-toast-demo.html index ffbc685..cc0e8ea 100644 --- a/docs/metona-toast-demo.html +++ b/docs/metona-toast-demo.html @@ -244,7 +244,7 @@
-
🛡️ onError 错误捕获 v0.1.2
+
🛡️ onError 错误捕获 v0.2.1
@@ -282,7 +282,7 @@ diff --git a/docs/metona-toast-docs.html b/docs/metona-toast-docs.html index d1bfc3d..fdb48c9 100644 --- a/docs/metona-toast-docs.html +++ b/docs/metona-toast-docs.html @@ -87,7 +87,7 @@

API 文档

-

MetonaToast v0.1.2 完整 API 参考。所有基础通知方法(show/success/error/warning/info/loading)支持两种调用形式,均可传入任何 配置项 作为可选第二参数。

+

MetonaToast v0.2.1 完整 API 参考。所有基础通知方法(show/success/error/warning/info/loading)支持两种调用形式,均可传入任何 配置项 作为可选第二参数。

show(message, opts?)

diff --git a/package.json b/package.json index 77e81f4..40fb41f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "marklite", - "version": "0.4.3", + "version": "0.4.4", "description": "Lightweight Markdown Editor for Windows", "main": "./dist/main/index.js", "scripts": { @@ -28,8 +28,8 @@ "author": "MarkLite", "license": "MIT", "dependencies": { - "@metona-team/metona-editor": "0.1.14", - "@metona-team/metona-toast": "^0.1.2", + "@metona-team/metona-editor": "0.2.4", + "@metona-team/metona-toast": "0.2.1", "dexie": "^4.0.11", "nanoid": "^5.1.5", "react": "^18.3.1", diff --git a/src/renderer/components/Editor/Editor.tsx b/src/renderer/components/Editor/Editor.tsx index ba1f38c..081c1ba 100644 --- a/src/renderer/components/Editor/Editor.tsx +++ b/src/renderer/components/Editor/Editor.tsx @@ -1,17 +1,15 @@ import React, { useEffect, useRef } from 'react' import MeEditor from '@metona-team/metona-editor' -import type { MarkdownEditor, MarkdownEditorOptions, EditMode, ThemeName } from '@metona-team/metona-editor' +import type { MarkdownEditor } from '@metona-team/metona-editor' import { useTabStore } from '../../stores/tabStore' import { setMetonaEditorGetter, useEditorStore } from '../../stores/editorStore' import { settingsRepository } from '../../db/settingsRepository' import { renderMarkdownSync } from '../../lib/markdown' import type { ThemeMode } from '../../types/settings' -// v0.1.9 demo 验证过但类型声明未收录的配置项 -interface ExtendedEditorOptions extends MarkdownEditorOptions { - lineNumbers?: boolean - autoBrackets?: boolean -} +// v0.2.4: 这些类型不再作为命名导出暴露,本地声明以保持类型安全 +type EditMode = 'edit' | 'split' | 'preview' +type ThemeName = 'light' | 'dark' | 'auto' | 'warm' | string interface EditorProps { themeMode: ThemeMode @@ -37,6 +35,7 @@ const EDITOR_PLUGINS: string[] = [ 'searchReplace', // Ctrl+F/Ctrl+H 'imagePaste', // Ctrl+V 粘贴图片 'exportTool', // 导出 MD/HTML + 'shortcutHelp', // 按 ? 弹出快捷键面板 ] /** @@ -64,7 +63,7 @@ export const Editor = React.memo(function Editor({ themeMode, onAppSave }: Edito const container = containerRef.current if (!container) return - const config: ExtendedEditorOptions = { + const config = { value: activeTab?.content ?? '', mode: mapViewMode(viewMode), height: '100%', @@ -87,6 +86,12 @@ export const Editor = React.memo(function Editor({ themeMode, onAppSave }: Edito autoBrackets: true, readOnly: viewMode === 'preview', plugins: EDITOR_PLUGINS, + // v0.2.4 新增配置项 + syncScroll: true, + wordWrap: true, + outline: false, // 使用自研 OutlinePanel + historyLimit: 100, + historyDebounce: 400, // 使用 unified 管线渲染,保留图片路径修复能力 render: (md: string) => { @@ -109,8 +114,8 @@ export const Editor = React.memo(function Editor({ themeMode, onAppSave }: Edito }, // 模式切换 → 同步到 editorStore 并持久化 - onModeChange: (mode: EditMode) => { - const mapped = reverseMapMode(mode) + onModeChange: (mode: string) => { + const mapped = reverseMapMode(mode as EditMode) setViewMode(mapped) settingsRepository.save({ viewMode: mapped }) }, @@ -118,7 +123,22 @@ export const Editor = React.memo(function Editor({ themeMode, onAppSave }: Edito // Ctrl+S → 触发应用层保存(Electron IPC 写文件系统) onSave: () => { onAppSave?.() - } + }, + + // v0.2.4 新增回调: 链接点击 → 安全打开外部链接 + onLinkClick: (href: string) => { + if (window.electronAPI?.openExternal) { + window.electronAPI.openExternal(href) + } + }, + + // v0.2.4 新增回调: 焦点事件(预留扩展点) + onFocus: () => { + // 编辑器获得焦点 + }, + onBlur: () => { + // 编辑器失去焦点 + }, } const editor = MeEditor.create(container, config) diff --git a/src/renderer/lib/toast.ts b/src/renderer/lib/toast.ts index b64cf60..b53b03c 100644 --- a/src/renderer/lib/toast.ts +++ b/src/renderer/lib/toast.ts @@ -5,6 +5,8 @@ import MeToast from '@metona-team/metona-toast' * * 替代原自研 Toast 组件(components/Toast),由 MeToast 接管全部通知渲染。 * MeToast 自管理 DOM 与样式,无需在 React 树中挂载容器组件。 + * + * v0.4.4: 升级 MeToast 0.2.1,安装 keyboard / accessibility 插件。 */ MeToast.configure({ position: 'top-right', @@ -17,8 +19,15 @@ MeToast.configure({ showProgress: true, draggable: true, locale: 'zh-CN', + width: 360, }) +// 安装内置插件 +// eslint-disable-next-line react-hooks/rules-of-hooks -- MeToast.use() 是插件安装方法,非 React Hook +MeToast.use('keyboard') // ESC 关闭所有 Toast +// eslint-disable-next-line react-hooks/rules-of-hooks +MeToast.use('accessibility') // 屏幕阅读器实时朗读 Toast 内容 + /** 与原 showToast 保持兼容的类型子集 */ export type ToastType = 'success' | 'error' | 'warning' | 'info' diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 736e152..9c88767 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -1,5 +1,5 @@ // 共享常量 — 主进程和渲染进程共用 -export const APP_VERSION = 'v0.4.3' +export const APP_VERSION = 'v0.4.4' export const MAX_FILE_SIZE = 20 * 1024 * 1024 // 20MB export const ALLOWED_EXTENSIONS = ['.md', '.markdown', '.txt'] as const export const SKIP_DIRS = new Set([