523 lines
15 KiB
HTML
523 lines
15 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.1.13 — 在线演示</title>
|
||
<style>
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
:root {
|
||
--app-bg: #f5f6f8;
|
||
--app-text: #1a1a2e;
|
||
--app-card: #ffffff;
|
||
--app-border: #e5e7eb;
|
||
--app-accent: #3b82f6;
|
||
--app-accent2: #8b5cf6;
|
||
|
||
/* dark overrides */
|
||
--header-bg: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
||
--header-text: #f1f5f9;
|
||
--badge-bg: rgba(255,255,255,0.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 0.3s, color 0.3s;
|
||
}
|
||
|
||
/* ====== Header ====== */
|
||
.header {
|
||
background: var(--header-bg);
|
||
color: var(--header-text);
|
||
padding: 32px 24px 24px;
|
||
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,0.3) 0%, transparent 70%);
|
||
border-radius: 50%;
|
||
}
|
||
.header h1 {
|
||
font-size: 2rem;
|
||
font-weight: 800;
|
||
letter-spacing: -0.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: 14px;
|
||
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,0.1);
|
||
}
|
||
|
||
/* ====== Toolbar Strip ====== */
|
||
.toolbar-strip {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px 24px;
|
||
background: var(--app-card);
|
||
border-bottom: 1px solid var(--app-border);
|
||
flex-wrap: wrap;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 50;
|
||
transition: background 0.3s;
|
||
}
|
||
.toolbar-strip label {
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: var(--app-text);
|
||
opacity: 0.7;
|
||
white-space: nowrap;
|
||
}
|
||
.toolbar-strip select {
|
||
padding: 5px 10px;
|
||
border: 1px solid var(--app-border);
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
background: var(--app-bg);
|
||
color: var(--app-text);
|
||
cursor: pointer;
|
||
}
|
||
.toolbar-strip button {
|
||
padding: 6px 14px;
|
||
border: 1px solid var(--app-border);
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
background: var(--app-card);
|
||
color: var(--app-text);
|
||
cursor: pointer;
|
||
transition: all 0.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 .spacer { flex: 1; }
|
||
|
||
.toggle-group { display: flex; gap: 2px; }
|
||
.toggle-group button {
|
||
border-radius: 0;
|
||
margin: 0;
|
||
}
|
||
.toggle-group button:first-child { border-radius: 6px 0 0 6px; }
|
||
.toggle-group button:last-child { border-radius: 0 6px 6px 0; }
|
||
.toggle-group button.on { background: var(--app-accent); color: #fff; border-color: var(--app-accent); }
|
||
|
||
/* ====== Editor Container ====== */
|
||
.editor-section {
|
||
max-width: 1200px;
|
||
margin: 24px auto;
|
||
padding: 0 24px;
|
||
}
|
||
.editor-card {
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
box-shadow: 0 4px 24px rgba(0,0,0,0.06);
|
||
background: var(--app-card);
|
||
border: 1px solid var(--app-border);
|
||
transition: box-shadow 0.3s, background 0.3s;
|
||
}
|
||
.editor-card:hover { box-shadow: 0 8px 36px rgba(0,0,0,0.1); }
|
||
|
||
/* ====== Info Cards ====== */
|
||
.info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||
gap: 16px;
|
||
max-width: 1200px;
|
||
margin: 24px auto;
|
||
padding: 0 24px;
|
||
}
|
||
.info-card {
|
||
background: var(--app-card);
|
||
border: 1px solid var(--app-border);
|
||
border-radius: 10px;
|
||
padding: 18px 20px;
|
||
transition: box-shadow 0.3s;
|
||
}
|
||
.info-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
|
||
.info-card .icon { font-size: 24px; margin-bottom: 8px; }
|
||
.info-card h4 { font-size: 14px; font-weight: 650; margin-bottom: 4px; }
|
||
.info-card p { font-size: 12px; color: var(--app-text); opacity: 0.65; margin: 0; }
|
||
|
||
/* ====== Footer ====== */
|
||
.footer {
|
||
text-align: center;
|
||
padding: 32px 24px;
|
||
font-size: 13px;
|
||
color: var(--app-text);
|
||
opacity: 0.5;
|
||
}
|
||
.footer a { color: var(--app-accent); text-decoration: none; }
|
||
|
||
/* ====== Responsive ====== */
|
||
@media (max-width: 768px) {
|
||
.header h1 { font-size: 1.5rem; }
|
||
.toolbar-strip { padding: 8px 12px; gap: 6px; }
|
||
.toolbar-strip button { padding: 5px 10px; font-size: 12px; }
|
||
.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.1.13</span>
|
||
<span class="badge">零依赖</span>
|
||
<span class="badge">532 tests</span>
|
||
<span class="badge">MIT</span>
|
||
<span class="badge">中文优先</span>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="toolbar-strip" id="controlBar">
|
||
<a href="index.html" style="text-decoration:none;font-size:18px;margin-right:8px;" title="返回首页">🏠</a>
|
||
|
||
<label>主题</label>
|
||
<select id="themeSelect">
|
||
<option value="auto">🌓 跟随系统</option>
|
||
<option value="light">☀️ 亮色</option>
|
||
<option value="dark">🌙 暗色</option>
|
||
<option value="warm">🔥 暖色</option>
|
||
</select>
|
||
|
||
<label>语言</label>
|
||
<select id="localeSelect">
|
||
<option value="zh-CN">中文</option>
|
||
<option value="en-US">English</option>
|
||
</select>
|
||
|
||
<label>模式</label>
|
||
<div class="toggle-group" id="modeBtns">
|
||
<button data-mode="edit">编辑</button>
|
||
<button data-mode="split" class="on">分屏</button>
|
||
<button data-mode="preview">预览</button>
|
||
</div>
|
||
|
||
<div class="spacer"></div>
|
||
|
||
<label>行号</label>
|
||
<div class="toggle-group" id="lineNumBtns">
|
||
<button class="on">开</button>
|
||
<button>关</button>
|
||
</div>
|
||
|
||
<label>大纲</label>
|
||
<div class="toggle-group" id="outlineBtns">
|
||
<button>开</button>
|
||
<button class="on">关</button>
|
||
</div>
|
||
|
||
<button onclick="demoToast()" title="演示 Toast 通知">💬 Toast</button>
|
||
<button onclick="demoOutline()" 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>数学公式、脚注、定义列表、嵌套列表、表格对齐、任务列表、Emoji</p>
|
||
</div>
|
||
<div class="info-card">
|
||
<div class="icon">⌨️</div>
|
||
<h4>快捷键</h4>
|
||
<p>Ctrl+B 粗体 · Ctrl+I 斜体 · Ctrl+S 保存 · Ctrl+F 搜索 · Ctrl+H 替换</p>
|
||
</div>
|
||
<div class="info-card">
|
||
<div class="icon">🎨</div>
|
||
<h4>4 套主题</h4>
|
||
<p>亮色 · 暗色 · 暖色 · 跟随系统,实例级隔离,外部主题跟随</p>
|
||
</div>
|
||
<div class="info-card">
|
||
<div class="icon">🔌</div>
|
||
<h4>4 个插件</h4>
|
||
<p>自动保存草稿 · 导出 MD/HTML · 搜索替换 · 图片粘贴</p>
|
||
</div>
|
||
<div class="info-card">
|
||
<div class="icon">🧩</div>
|
||
<h4>编辑增强</h4>
|
||
<p>行号装订线 · 智能 Enter · 括号自动闭合 · 拖放文件 · 大纲面板</p>
|
||
</div>
|
||
<div class="info-card">
|
||
<div class="icon">🛡️</div>
|
||
<h4>安全</h4>
|
||
<p>XSS 防护 · URL 协议过滤 · HTML 实体保护 · sanitize 钩子</p>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="footer">
|
||
MetonaEditor v0.1.13 ·
|
||
<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 License
|
||
</footer>
|
||
|
||
<script src="../dist/metona-editor.js"></script>
|
||
<script>
|
||
(function () {
|
||
const demoMd = [
|
||
'# 🚀 MetonaEditor 演示',
|
||
'',
|
||
'> 轻量、零依赖、精致美观的 **Markdown Editor** 库。',
|
||
'> 单文件,开箱即用,中文优先。',
|
||
'',
|
||
'## ✨ 核心特性',
|
||
'',
|
||
'- 零依赖 · 单文件打包 · UMD / ESM / CJS',
|
||
'- 三模式视图 · 分屏拖拽 · 滚动同步',
|
||
'- 行号装订线 · 当前行高亮',
|
||
'- 智能 Enter · 括号自动闭合',
|
||
'- 4 套主题 · 实例级隔离 · 外部跟随',
|
||
'- 插件系统 v2 · 依赖排序 · 异步加载',
|
||
'',
|
||
'## 📐 数学公式',
|
||
'',
|
||
'行内公式:爱因斯坦质能方程 $E = mc^2$',
|
||
'',
|
||
'块级公式:',
|
||
'$$',
|
||
'\\int_{0}^{\\infty} e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}',
|
||
'$$',
|
||
'',
|
||
'## 📋 表格与对齐',
|
||
'',
|
||
'| 版本 | 日期 | 测试数 |',
|
||
'| :--- | :---: | ---: |',
|
||
'| v0.1.13 | 2026-07 | 532+ |',
|
||
'| v0.1.6 | 2026-07 | 521 |',
|
||
'| v0.1.5 | 2026-07 | 499 |',
|
||
'',
|
||
'## ✅ 任务列表',
|
||
'',
|
||
'- [x] 解析器增强(11 项新语法)',
|
||
'- [x] 主题系统(实例隔离 + 外部跟随)',
|
||
'- [x] 插件 v2 + i18n 增强',
|
||
'- [x] 编辑体验(行号 / 自动格式化 / 括号闭合)',
|
||
'- [ ] 更多语言翻译包',
|
||
'- [ ] VS Code 插件',
|
||
'',
|
||
'## 🎯 嵌套列表',
|
||
'',
|
||
'1. 一级有序',
|
||
' 1. 二级有序',
|
||
' 2. 二级第二项',
|
||
'2. 回到一级',
|
||
' - 混杂无序',
|
||
' - 第二项',
|
||
' - 三级嵌套',
|
||
'',
|
||
'## 📖 定义列表',
|
||
'',
|
||
'MetonaEditor',
|
||
': 一款轻量、零依赖的中文优先 Markdown 编辑器库。',
|
||
': 支持 CommonMark + GFM 扩展语法。',
|
||
'',
|
||
'Markdown',
|
||
': 由 John Gruber 创建的轻量级标记语言。',
|
||
'',
|
||
'## 🔗 链接与图片',
|
||
'',
|
||
'访问 [MetonaEditor 仓库](https://git.metona.cn/MetonaTeam/MetonaEditor \'点击访问源码\')',
|
||
'',
|
||
'图片示例:',
|
||
'',
|
||
'## 💻 代码块',
|
||
'',
|
||
'```js',
|
||
'import MeEditor from \'@metona-team/metona-editor\';',
|
||
'',
|
||
'const editor = MeEditor.create(\'#editor\', {',
|
||
' value: \'# Hello World\',',
|
||
' mode: \'split\',',
|
||
' lineNumbers: true,',
|
||
' autoBrackets: true,',
|
||
' plugins: [\'autoSave\', \'searchReplace\'],',
|
||
' onChange: (value) => console.log(value),',
|
||
'});',
|
||
'```',
|
||
'',
|
||
'## 🎨 文本样式',
|
||
'',
|
||
'- **粗体文本** 与 __另一种粗体__',
|
||
'- *斜体文本* 与 _另一种斜体_',
|
||
'- ~~删除线~~ 与 ==高亮标记==',
|
||
'- 化学式:H~2~O 与 数学:x^2^ + y^2^ = z^2^',
|
||
'- 行内代码:`const x = 42;`',
|
||
'- Emoji::smile: :rocket: :fire: :tada: :rainbow: :coffee:',
|
||
'',
|
||
'## 📌 脚注',
|
||
'',
|
||
'这是一个带脚注的句子[^1],这是另一个脚注[^note]。',
|
||
'',
|
||
'[^1]: 第一个脚注的详细说明内容。',
|
||
'[^note]: 脚注可以包含 **格式化** 和 `代码`。',
|
||
'',
|
||
'---',
|
||
'',
|
||
'> **提示:** 试试拖放图片到编辑器、按 `Ctrl+F` 搜索、`Ctrl+H` 替换、',
|
||
'> 在列表行尾按 `Enter` 体验智能延续、选中文字后输入 `(` 体验自动包裹。',
|
||
].join('\n');
|
||
|
||
const editor = window.MeEditor.create('#editor', {
|
||
value: demoMd,
|
||
mode: 'split',
|
||
height: 620,
|
||
lineNumbers: true,
|
||
autoBrackets: true,
|
||
wordCount: true,
|
||
outline: false,
|
||
theme: 'auto',
|
||
locale: 'zh-CN',
|
||
plugins: ['autoSave', 'exportTool', 'searchReplace', 'imagePaste'],
|
||
});
|
||
|
||
// ====== Control Bar Handlers ======
|
||
|
||
// Theme
|
||
document.getElementById('themeSelect').addEventListener('change', function () {
|
||
editor.setTheme(this.value);
|
||
});
|
||
|
||
// Locale
|
||
document.getElementById('localeSelect').addEventListener('change', function () {
|
||
editor.setLocale(this.value);
|
||
});
|
||
|
||
// Mode buttons
|
||
document.querySelectorAll('#modeBtns button').forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
editor.setMode(this.dataset.mode);
|
||
document.querySelectorAll('#modeBtns button').forEach(function (b) { b.classList.remove('on'); });
|
||
this.classList.add('on');
|
||
});
|
||
});
|
||
|
||
// Line numbers toggle
|
||
var lineNumBtns = document.querySelectorAll('#lineNumBtns button');
|
||
lineNumBtns[0].addEventListener('click', function () {
|
||
editor.config.lineNumbers = true;
|
||
if (editor.gutter) editor.gutter.style.display = '';
|
||
editor._renderGutter();
|
||
lineNumBtns[0].classList.add('on');
|
||
lineNumBtns[1].classList.remove('on');
|
||
});
|
||
lineNumBtns[1].addEventListener('click', function () {
|
||
editor.config.lineNumbers = false;
|
||
if (editor.gutter) editor.gutter.style.display = 'none';
|
||
lineNumBtns[1].classList.add('on');
|
||
lineNumBtns[0].classList.remove('on');
|
||
});
|
||
|
||
// Outline toggle
|
||
var outlineBtns = document.querySelectorAll('#outlineBtns button');
|
||
outlineBtns[0].addEventListener('click', function () {
|
||
editor.config.outline = true;
|
||
editor._buildOutline();
|
||
outlineBtns[0].classList.add('on');
|
||
outlineBtns[1].classList.remove('on');
|
||
});
|
||
outlineBtns[1].addEventListener('click', function () {
|
||
editor.config.outline = false;
|
||
var panel = editor.el.querySelector('.me-outline');
|
||
if (panel) panel.remove();
|
||
outlineBtns[1].classList.add('on');
|
||
outlineBtns[0].classList.remove('on');
|
||
});
|
||
|
||
// Toast demo
|
||
window.demoToast = function () {
|
||
var types = ['success', 'error', 'warning', 'info'];
|
||
var type = types[Math.floor(Math.random() * types.length)];
|
||
var messages = {
|
||
success: '✅ 内容已自动保存',
|
||
error: '❌ 操作失败,请重试',
|
||
warning: '⚠️ 有未保存的更改',
|
||
info: 'ℹ️ 这是来自插件系统的通知',
|
||
};
|
||
editor.toast(messages[type], { type: type, duration: 2500 });
|
||
};
|
||
|
||
// Outline demo
|
||
window.demoOutline = function () {
|
||
if (editor.config.outline) {
|
||
editor.config.outline = false;
|
||
var panel = editor.el.querySelector('.me-outline');
|
||
if (panel) panel.remove();
|
||
outlineBtns[0].classList.remove('on');
|
||
outlineBtns[1].classList.add('on');
|
||
} else {
|
||
editor.config.outline = true;
|
||
editor._buildOutline();
|
||
outlineBtns[0].classList.add('on');
|
||
outlineBtns[1].classList.remove('on');
|
||
}
|
||
};
|
||
|
||
// Register custom shortcut
|
||
editor.registerShortcut('Ctrl+Shift+T', function () {
|
||
editor.toast('快捷键 Ctrl+Shift+T 已触发!', { type: 'info' });
|
||
}, 'Toast test');
|
||
|
||
// Listen for theme change to update select
|
||
editor.on('themeChange', function (data) {
|
||
document.getElementById('themeSelect').value = data.theme;
|
||
});
|
||
|
||
// Sync editor mode button state
|
||
editor.on('modeChange', function (mode) {
|
||
document.querySelectorAll('#modeBtns button').forEach(function (b) {
|
||
b.classList.toggle('on', b.dataset.mode === mode);
|
||
});
|
||
});
|
||
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|