BREAKING CHANGE: All source files converted from JavaScript to TypeScript. - 12 .ts source files with strict types, full EditorOptions/Plugin/Token interfaces - 7 .ts test files, 610 total tests (27 new), 7 suites all passing - tsc --noEmit: 0 errors - rollup-plugin-typescript build: 5 artifacts (UMD/ESM/CJS/Min/DTS) - @babel/preset-typescript for jest - New tsconfig.json, updated babel/jest/rollup configs - Coverage: parser 99.5%, utils 95.7%, themes 96.2%, core 88.8%, plugins 89.5% - Removed types/ folder (types now inline in .ts + auto-generated .d.ts) - Desktop-only, no backward compatibility
280 lines
13 KiB
HTML
280 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN" data-md-theme="auto">
|
||
<head>
|
||
<meta charset="utf-8"/>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📝</text></svg>"/>
|
||
<title>MetonaEditor v0.2.0 — 全功能演示</title>
|
||
<style>
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
:root{--app-bg:#f5f6f8;--app-text:#1a1a2e;--app-card:#fff;--app-border:#e5e7eb;--app-accent:#3b82f6;--app-accent2:#8b5cf6;--header-bg:linear-gradient(135deg,#1e293b 0%,#0f172a 100%);--header-text:#f1f5f9;--badge-bg:rgba(255,255,255,.12);--badge-text:#e2e8f0}
|
||
[data-md-theme=dark]{--app-bg:#0f1117;--app-text:#e2e8f0;--app-card:#1a1d27;--app-border:#2d3140;--header-bg:linear-gradient(135deg,#0a0d15 0%,#161923 100%)}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;background:var(--app-bg);color:var(--app-text);min-height:100vh;line-height:1.6;transition:background .3s,color .3s}
|
||
|
||
.header{background:var(--header-bg);color:var(--header-text);padding:28px 24px 20px;text-align:center;position:relative;overflow:hidden}
|
||
.header::before{content:'';position:absolute;top:-60px;right:-60px;width:300px;height:300px;background:radial-gradient(circle,rgba(139,92,246,.3) 0%,transparent 70%);border-radius:50%}
|
||
.header h1{font-size:1.85rem;font-weight:800;letter-spacing:-.5px;position:relative}
|
||
.header h1 span.grad{background:linear-gradient(135deg,var(--app-accent),var(--app-accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
||
.header .badges{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:12px;position:relative}
|
||
.header .badge{padding:4px 12px;border-radius:20px;font-size:13px;font-weight:500;background:var(--badge-bg);color:var(--badge-text);border:1px solid rgba(255,255,255,.1)}
|
||
|
||
.toolbar-strip{display:flex;align-items:center;gap:8px;padding:8px 20px;background:var(--app-card);border-bottom:1px solid var(--app-border);flex-wrap:wrap;position:sticky;top:0;z-index:50;transition:background .3s}
|
||
.toolbar-strip label{font-size:12px;font-weight:500;color:var(--app-text);opacity:.7;white-space:nowrap}
|
||
.toolbar-strip select{padding:4px 8px;border:1px solid var(--app-border);border-radius:6px;font-size:12px;background:var(--app-bg);color:var(--app-text);cursor:pointer}
|
||
.toolbar-strip button{padding:5px 12px;border:1px solid var(--app-border);border-radius:6px;font-size:12px;font-weight:500;background:var(--app-card);color:var(--app-text);cursor:pointer;transition:all .15s;white-space:nowrap}
|
||
.toolbar-strip button:hover{background:var(--app-accent);color:#fff;border-color:var(--app-accent)}
|
||
.toolbar-strip button.accent{background:var(--app-accent);color:#fff;border-color:var(--app-accent)}
|
||
.toolbar-strip button.on{background:var(--app-accent);color:#fff;border-color:var(--app-accent)}
|
||
.toolbar-strip .spacer{flex:1}
|
||
|
||
.editor-section{max-width:1280px;margin:20px auto;padding:0 20px}
|
||
.editor-card{border-radius:12px;overflow:hidden;box-shadow:0 4px 24px rgba(0,0,0,.06);background:var(--app-card);border:1px solid var(--app-border);transition:.3s}
|
||
.editor-card:hover{box-shadow:0 8px 36px rgba(0,0,0,.1)}
|
||
|
||
.info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;max-width:1280px;margin:20px auto;padding:0 20px}
|
||
.info-card{background:var(--app-card);border:1px solid var(--app-border);border-radius:10px;padding:16px 18px;transition:.3s}
|
||
.info-card:hover{box-shadow:0 4px 16px rgba(0,0,0,.08)}
|
||
.info-card .icon{font-size:22px;margin-bottom:6px}
|
||
.info-card h4{font-size:13px;font-weight:650;margin-bottom:3px}
|
||
.info-card p{font-size:12px;color:var(--app-text);opacity:.65;margin:0}
|
||
.footer{text-align:center;padding:28px 24px;font-size:13px;color:var(--app-text);opacity:.5}
|
||
.footer a{color:var(--app-accent);text-decoration:none}
|
||
@media(max-width:768px){.header h1{font-size:1.4rem}.toolbar-strip{padding:6px 10px;gap:4px}.toolbar-strip button{padding:4px 8px;font-size:11px}.info-grid{grid-template-columns:1fr 1fr}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="header">
|
||
<h1><span class="grad">Metona</span>Editor</h1>
|
||
<div class="badges">
|
||
<span class="badge">v0.2.0</span>
|
||
<span class="badge">TypeScript</span>
|
||
<span class="badge">583 tests</span>
|
||
<span class="badge">6 插件</span>
|
||
<span class="badge">中文优先</span>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="toolbar-strip" id="bar">
|
||
<a href="index.html" style="text-decoration:none;font-size:18px;margin-right:4px" title="首页">🏠</a>
|
||
<label>主题</label>
|
||
<select id="selTheme"><option value="auto">🌓 跟随系统</option><option value="light">☀️ 亮色</option><option value="dark">🌙 暗色</option><option value="warm">🔥 暖色</option></select>
|
||
<label>语言</label>
|
||
<select id="selLocale"><option value="zh-CN">中文</option><option value="en-US">English</option></select>
|
||
<label>模式</label>
|
||
<button data-cmd="mode" data-arg="edit">编辑</button>
|
||
<button data-cmd="mode" data-arg="split" class="on">分屏</button>
|
||
<button data-cmd="mode" data-arg="preview">预览</button>
|
||
<div class="spacer"></div>
|
||
<button data-cmd="zen" title="Zen 专注模式">🧘 Zen</button>
|
||
<button data-cmd="outline" title="切换大纲面板">📋 大纲</button>
|
||
<button data-cmd="wrap" title="切换自动换行">↩ 换行</button>
|
||
<button data-cmd="toast" title="Toast 通知演示">💬 Toast</button>
|
||
<button data-cmd="openFile" title="打开磁盘文件">📂 打开</button>
|
||
<button data-cmd="saveFile" title="保存到磁盘">💾 保存</button>
|
||
</div>
|
||
|
||
<div class="editor-section">
|
||
<div class="editor-card"><div id="editor"></div></div>
|
||
</div>
|
||
|
||
<div class="info-grid">
|
||
<div class="info-card"><div class="icon">🎨</div><h4>三重强调 + 链接内格式</h4><p>***粗斜体*** / [**粗体链接**](url) 等语法正确渲染 ← v0.1.15</p></div>
|
||
<div class="info-card"><div class="icon">🔧</div><h4>块级处理器表驱动</h4><p>registerBlockHandler 注册自定义语法,parseTokens/renderTokens 分离</p></div>
|
||
<div class="info-card"><div class="icon">📂</div><h4>磁盘文件读写</h4><p>File System Access API 打开/保存真实磁盘 .md 文件</p></div>
|
||
<div class="info-card"><div class="icon">🔢</div><h4>行号装订线</h4><p>当前行高亮,与编辑区滚动同步</p></div>
|
||
<div class="info-card"><div class="icon">🧘</div><h4>Zen 专注模式</h4><p>工具栏自动隐藏,鼠标移到顶部滑入</p></div>
|
||
<div class="info-card"><div class="icon">⌨️</div><h4>快捷键面板</h4><p>按 ? 查看全部快捷键,Ctrl+F/H 搜索替换</p></div>
|
||
<div class="info-card"><div class="icon">📊</div><h4>Mermaid 图表</h4><p>```mermaid 代码块,加载 Mermaid.js 即可渲染</p></div>
|
||
<div class="info-card"><div class="icon">🔌</div><h4>6 个预设插件</h4><p>autoSave / exportTool / searchReplace / imagePaste / shortcutHelp / fileSystem</p></div>
|
||
<div class="info-card"><div class="icon">🛡️</div><h4>XSS 安全防护</h4><p>HTML 转义 · URL 协议过滤 · sanitize 钩子</p></div>
|
||
</div>
|
||
|
||
<footer class="footer">
|
||
MetonaEditor v0.1.15 ·
|
||
<a href="index.html">首页</a> ·
|
||
<a href="docs.html">API 文档</a> ·
|
||
<a href="https://git.metona.cn/MetonaTeam/MetonaEditor" target="_blank" rel="noopener">源码仓库</a> · MIT
|
||
</footer>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||
<script src="../dist/metona-editor.js"></script>
|
||
<script>
|
||
(function(){
|
||
var demoMd=[
|
||
'# 🚀 MetonaEditor 全功能演示',
|
||
'',
|
||
'> 轻量、零依赖、中文优先的 **Markdown Editor** 库。',
|
||
'> 单文件打包,开箱即用。所有功能均在此页面启用。',
|
||
'',
|
||
'## ✨ 核心特性',
|
||
'',
|
||
'- 语法高亮编辑区(源码实时着色)',
|
||
'- 行号装订线 + 当前行高亮',
|
||
'- 智能 Enter(列表/引用自动延续)',
|
||
'- 括号/引号自动闭合',
|
||
'- 拖放图片/文件到编辑区',
|
||
'- Zen 专注模式 + 大纲面板',
|
||
'- 磁盘文件读写(File System Access API)',
|
||
'- 6 个预设插件开箱即用',
|
||
'',
|
||
'## 📐 数学公式',
|
||
'',
|
||
'行内:$E = mc^2$',
|
||
'块级:$$\\int_{0}^{\\infty} e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$',
|
||
'',
|
||
'## 📊 Mermaid 图表(需加载 Mermaid.js)',
|
||
'',
|
||
'```mermaid',
|
||
'graph TD',
|
||
' A[Markdown] --> B[解析器]',
|
||
' B --> C[HTML]',
|
||
' C --> D[预览]',
|
||
'```',
|
||
'',
|
||
'## 📋 表格与对齐',
|
||
'',
|
||
'| 版本 | 日期 | 测试 | 主题 |',
|
||
'| :--- | :---: | ---: | --- |',
|
||
'| v0.1.15 | 2026-07 | 558 | 三重强调+链接格式+转义+Token分离+块级表驱动 |',
|
||
'| v0.1.14 | 2026-07 | 535+ | 语法高亮+Mermaid+文件系统 |',
|
||
'| v0.1.12 | 2026-07 | 535 | A11y+Zen+滚动同步 |',
|
||
'| v0.1.7 | 2026-07 | 532 | 编辑体验 |',
|
||
'',
|
||
'## ✅ 任务列表',
|
||
'',
|
||
'- [x] 解析器增强(数学公式/脚注/嵌套列表等)',
|
||
'- [x] 主题系统(实例隔离 + 外部跟随)',
|
||
'- [x] 插件 v2 + i18n 增强',
|
||
'- [x] 编辑体验(行号/自动格式化/括号闭合)',
|
||
'- [x] 语法高亮 + 文件系统 + Mermaid',
|
||
'- [ ] vs-code 插件',
|
||
'',
|
||
'## 🎯 嵌套列表',
|
||
'',
|
||
'1. 一级有序',
|
||
' 1. 二级有序(自动递增)',
|
||
' 2. 第二项',
|
||
'2. 回到一级',
|
||
' - 混杂无序列表',
|
||
' - 第二项',
|
||
' - [x] 三级嵌套 + 任务',
|
||
'',
|
||
'## 💻 代码块',
|
||
'',
|
||
'```js',
|
||
'import MeEditor from "@metona-team/metona-editor";',
|
||
'const editor = MeEditor.create("#editor", {',
|
||
' mode: "split",',
|
||
' syntaxHighlight: true,',
|
||
' lineNumbers: true,',
|
||
' autoBrackets: true,',
|
||
' plugins: ["autoSave","fileSystem","shortcutHelp"],',
|
||
'});',
|
||
'```',
|
||
'',
|
||
'## 🎨 文本样式',
|
||
'',
|
||
'- **粗体** / __另一种粗体__',
|
||
'- ***粗斜体*** / ___另一种粗斜体___ ← v0.1.15',
|
||
'- *斜体* / _另一种斜体_',
|
||
'- ~~删除线~~ / ==高亮标记==',
|
||
'- 链接内格式:[**粗体链接**](https://example.com) ← v0.1.15',
|
||
'- 化学:H~2~O / 数学:x^2^',
|
||
'- 代码:`const x = 42;`',
|
||
'- Emoji::smile: :rocket: :fire: :rainbow: :coffee: :hamburger:',
|
||
'',
|
||
'## 📌 脚注',
|
||
'',
|
||
'这是一个带脚注的句子[^1],另一个脚注[^note]。',
|
||
'',
|
||
'[^1]: 脚注可以包含 **格式化** 和 `代码`。',
|
||
'[^note]: 第二个脚注的说明。',
|
||
'',
|
||
'---',
|
||
'',
|
||
'> **提示:** 按 `?` 查看快捷键 · 拖放图片到编辑区 · Ctrl+F 搜索 · Ctrl+H 替换',
|
||
'> 在列表行尾 Enter 体验智能延续 · 选中文字后按 `(` 体验自动包裹',
|
||
'> 点击「打开」按钮加载磁盘 .md 文件 · 点击「保存」写入磁盘',
|
||
].join('\n');
|
||
|
||
var editor=MeEditor.create('#editor',{
|
||
value:demoMd,
|
||
mode:'split',height:620,
|
||
lineNumbers:true,autoBrackets:true,wordCount:true,
|
||
theme:'auto',locale:'zh-CN',
|
||
outline:false,
|
||
plugins:['autoSave','exportTool','searchReplace','imagePaste','shortcutHelp','fileSystem'],
|
||
onLinkClick:function(uri,text,ed){
|
||
ed.toast('链接: '+uri,{type:'info',duration:2000});
|
||
}
|
||
});
|
||
|
||
// ===== 控制栏按钮 =====
|
||
var bar=document.getElementById('bar');
|
||
|
||
// 主题
|
||
document.getElementById('selTheme').addEventListener('change',function(){
|
||
editor.setTheme(this.value);
|
||
});
|
||
|
||
// 语言
|
||
document.getElementById('selLocale').addEventListener('change',function(){
|
||
editor.setLocale(this.value);
|
||
});
|
||
|
||
// 通用按钮
|
||
bar.addEventListener('click',function(e){
|
||
var btn=e.target.closest('button[data-cmd]');
|
||
if(!btn)return;
|
||
var cmd=btn.dataset.cmd;
|
||
if(cmd==='mode'){editor.setMode(btn.dataset.arg);updateModeBtns(btn.dataset.arg);}
|
||
else if(cmd==='zen'){editor.toggleZen();btn.classList.toggle('on',editor.isZen());}
|
||
else if(cmd==='outline'){toggleOutline(btn);}
|
||
else if(cmd==='wrap'){editor.toggleWordWrap();btn.classList.toggle('on',!editor.isWordWrap());}
|
||
else if(cmd==='toast'){
|
||
var types=['success','error','warning','info'];
|
||
var msgs={success:'✅ 操作成功',error:'❌ 操作失败',warning:'⚠️ 注意',info:'ℹ️ 提示信息'};
|
||
var t=types[Math.floor(Math.random()*4)];
|
||
editor.toast(msgs[t],{type:t,duration:2000});
|
||
}
|
||
else if(cmd==='openFile'){editor.openFile?.();}
|
||
else if(cmd==='saveFile'){editor.saveFile?.();}
|
||
});
|
||
|
||
function updateModeBtns(mode){
|
||
bar.querySelectorAll('[data-cmd=mode]').forEach(function(b){b.classList.toggle('on',b.dataset.arg===mode)});
|
||
}
|
||
|
||
function toggleOutline(btn){
|
||
editor.config.outline=!editor.config.outline;
|
||
if(editor.config.outline){editor._buildOutline();btn.classList.add('on')}
|
||
else{var p=editor.el.querySelector('.me-outline');if(p)p.remove();btn.classList.remove('on')}
|
||
}
|
||
|
||
// 快捷键
|
||
editor.registerShortcut('Ctrl+Shift+T',function(){
|
||
editor.toast('自定义快捷键已触发!',{type:'info'});
|
||
},'Toast 通知演示');
|
||
|
||
// 事件监听
|
||
editor.on('themeChange',function(d){document.getElementById('selTheme').value=d.theme});
|
||
editor.on('modeChange',function(m){updateModeBtns(m)});
|
||
editor.on('fileOpened',function(d){editor.toast('已打开: '+d.name,{type:'success'})});
|
||
editor.on('fileSaved',function(){editor.toast('文件已保存',{type:'success'})});
|
||
editor.on('linkClick',function(d){editor.toast('链接点击: '+d.href,{type:'info',duration:1500})});
|
||
|
||
// Mermaid 图表渲染(v0.1.14)
|
||
function renderMermaid(){
|
||
if(typeof mermaid==='undefined')return;
|
||
try{mermaid.run({querySelector:'.me-mermaid .mermaid'})}catch(e){}
|
||
}
|
||
mermaid.initialize({startOnLoad:false,theme:'default'});
|
||
editor.on('afterRender',renderMermaid);
|
||
setTimeout(renderMermaid,300);
|
||
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|