Files
MetonaEditor/site/demo.html
T
thzxx e83fc211dc feat: v0.2.0 — TypeScript full rewrite, 95%+ core coverage
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
2026-07-24 22:28:38 +08:00

280 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>