Files
MetonaToast/site/index.html
T
tianhao 95f5652e9c fix: CJS 产物改 .cjs 扩展名并提升 default 导出 — 修复 require() 兼容性
- 包声明 type:module 时 .js 产物被 Node 按 ESM 解析:Node 16/18 下 require 报 ERR_REQUIRE_ESM,Node 20+ 拿到 namespace 对象(success/version 等属性 undefined)
- dist/metona-toast.cjs.js → .cjs,dist/react.cjs.js → .cjs
- 主包 CJS footer 将 default 提升为 module.exports:require() 直接返回 MeToast(兼容 .default / .MeToast / .Met / .Toast / .VERSION 所有用法)
- 版本 0.5.1(0.5.0 已发布带问题产物)
- 真实消费者验证:CJS/ESM/React 子包四路加载全部正常
2026-08-08 15:25:47 +08:00

481 lines
23 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">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<title>MetonaToast — 轻量精致的 Toast 通知库</title>
<meta name="description" content="MetonaToast 是轻量、零依赖、精致美观的 Toast 通知库。107 种图标、11 种动画、主题系统、国际化、插件架构、React 适配器。">
<style>
:root {
--bg: #070b14;
--surface: rgba(255,255,255,0.03);
--surface-hover: rgba(255,255,255,0.06);
--border: rgba(255,255,255,0.08);
--text: #e8ecf4;
--muted: #8b94a7;
--accent1: #6366f1;
--accent2: #a855f7;
--accent3: #ec4899;
--grad: linear-gradient(135deg, #818cf8, #c084fc, #f472b6);
--radius: 16px;
}
* { margin:0; padding:0; box-sizing:border-box; }
html { scroll-behavior:smooth; }
body {
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;
background:var(--bg); color:var(--text); line-height:1.7; overflow-x:hidden;
background-image:
radial-gradient(ellipse 80% 50% at 50% -20%, rgba(99,102,241,0.15), transparent),
radial-gradient(ellipse 60% 40% at 90% 10%, rgba(168,85,247,0.08), transparent),
radial-gradient(ellipse 50% 40% at 10% 30%, rgba(236,72,153,0.06), transparent);
}
/* 背景网格 */
body::before {
content:''; position:fixed; inset:0; pointer-events:none; z-index:0;
background-image:
linear-gradient(rgba(255,255,255,0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.025) 1px, transparent 1px);
background-size:56px 56px;
mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, black 40%, transparent 100%);
-webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, black 40%, transparent 100%);
}
a { color:#a5b4fc; text-decoration:none; transition:color .2s; }
a:hover { color:#c7d2fe; }
::selection { background:rgba(139,92,246,.4); }
.grad-text {
background:var(--grad);
-webkit-background-clip:text; background-clip:text;
-webkit-text-fill-color:transparent; color:transparent;
}
/* ===== Nav ===== */
.nav {
position:sticky; top:0; z-index:100;
background:rgba(7,11,20,0.7);
backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
border-bottom:1px solid var(--border);
}
.nav-inner {
max-width:1180px; margin:0 auto; display:flex; align-items:center;
justify-content:space-between; padding:0 28px; height:60px;
}
.nav-logo { font-size:19px; font-weight:800; letter-spacing:.2px; cursor:pointer; }
.nav-links { display:flex; gap:26px; }
.nav-links a { color:var(--muted); font-size:14px; font-weight:500; position:relative; }
.nav-links a::after {
content:''; position:absolute; left:0; right:100%; bottom:-6px; height:2px;
background:var(--grad); border-radius:2px; transition:right .25s;
}
.nav-links a:hover::after { right:0; }
.nav-links a:hover { color:var(--text); }
/* ===== Hero ===== */
.hero { text-align:center; padding:110px 24px 60px; position:relative; z-index:1; }
.hero-badge {
display:inline-flex; align-items:center; gap:8px;
background:rgba(99,102,241,0.1); border:1px solid rgba(99,102,241,0.3);
color:#a5b4fc; font-size:12px; font-weight:600; letter-spacing:1px;
padding:6px 16px; border-radius:999px; margin-bottom:26px;
}
.hero-badge .dot {
width:7px; height:7px; border-radius:50%; background:#34d399;
box-shadow:0 0 12px #34d399; animation:pulse 2s infinite;
}
@keyframes pulse { 50% { opacity:.4; } }
.hero h1 { font-size:clamp(42px, 7vw, 76px); font-weight:800; letter-spacing:-1px; line-height:1.1; }
.hero h1 .grad-text { animation:shimmer 6s linear infinite; }
@keyframes shimmer {
0%,100% { filter:hue-rotate(0deg); }
50% { filter:hue-rotate(30deg); }
}
.hero-sub {
color:var(--muted); font-size:clamp(15px, 2.2vw, 18px);
max-width:640px; margin:18px auto 0;
}
.hero-badges { display:flex; gap:10px; justify-content:center; margin-top:26px; flex-wrap:wrap; }
.hero-badges span {
background:var(--surface); border:1px solid var(--border); border-radius:999px;
padding:5px 14px; font-size:12.5px; color:#cbd5e1;
transition:border-color .2s, transform .2s;
}
.hero-badges span:hover { border-color:rgba(129,140,248,.5); transform:translateY(-2px); }
.hero-actions { margin-top:36px; display:flex; gap:14px; justify-content:center; flex-wrap:wrap; }
.btn {
padding:12px 30px; border-radius:12px; font-size:14.5px; font-weight:600;
cursor:pointer; transition:transform .2s, box-shadow .2s, border-color .2s;
border:1px solid transparent;
}
.btn-primary {
background:var(--grad); color:#fff;
box-shadow:0 8px 30px -8px rgba(139,92,246,.6);
}
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 12px 40px -8px rgba(139,92,246,.8); }
.btn-ghost { background:var(--surface); border-color:var(--border); color:var(--text); }
.btn-ghost:hover { border-color:rgba(129,140,248,.5); transform:translateY(-2px); background:var(--surface-hover); }
/* 浮动光球 */
.orb { position:absolute; border-radius:50%; filter:blur(70px); pointer-events:none; z-index:0; }
.orb-1 { width:340px; height:340px; background:rgba(99,102,241,.25); top:-80px; left:12%; animation:float 12s ease-in-out infinite; }
.orb-2 { width:260px; height:260px; background:rgba(236,72,153,.18); top:40px; right:10%; animation:float 15s ease-in-out infinite reverse; }
.orb-3 { width:200px; height:200px; background:rgba(168,85,247,.2); bottom:-40px; left:40%; animation:float 18s ease-in-out infinite 2s; }
@keyframes float { 0%,100%{transform:translateY(0) translateX(0);} 50%{transform:translateY(-30px) translateX(20px);} }
/* ===== 数据统计 ===== */
.stats { position:relative; z-index:1; max-width:1000px; margin:10px auto 0; padding:0 24px; }
.stats-grid {
display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:1px;
background:var(--border); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden;
}
.stat { background:rgba(10,15,26,.85); padding:22px 12px; text-align:center; backdrop-filter:blur(8px); }
.stat-num { font-size:26px; font-weight:800; }
.stat-label { font-size:12px; color:var(--muted); margin-top:2px; letter-spacing:.5px; }
/* ===== 通用 Section ===== */
.container { position:relative; z-index:1; max-width:1100px; margin:0 auto; padding:80px 24px; }
.section-title { font-size:30px; font-weight:800; text-align:center; letter-spacing:-.5px; }
.section-desc { color:var(--muted); text-align:center; margin-top:8px; font-size:15px; }
/* ===== 特性卡片 ===== */
.features { display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); gap:16px; margin-top:44px; }
.feature {
background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
padding:26px 24px; transition:transform .25s, border-color .25s, background .25s;
position:relative; overflow:hidden;
}
.feature::before {
content:''; position:absolute; top:0; left:0; right:0; height:2px;
background:var(--grad); opacity:0; transition:opacity .25s;
}
.feature:hover { transform:translateY(-4px); border-color:rgba(129,140,248,.4); background:var(--surface-hover); }
.feature:hover::before { opacity:1; }
.feature-icon { font-size:26px; margin-bottom:12px; display:inline-block; }
.feature h3 { font-size:15.5px; margin-bottom:6px; font-weight:700; }
.feature p { color:var(--muted); font-size:13px; line-height:1.65; }
.feature code {
background:rgba(99,102,241,0.15); color:#a5b4fc;
padding:1px 6px; border-radius:5px; font-size:12px;
}
/* ===== 代码块 ===== */
.code-block { max-width:760px; margin:36px auto 0; }
.terminal {
background:#0a0f1c; border:1px solid var(--border); border-radius:12px; overflow:hidden;
box-shadow:0 20px 60px -20px rgba(0,0,0,.6);
}
.terminal-bar {
display:flex; align-items:center; gap:7px; padding:11px 16px;
background:rgba(255,255,255,0.03); border-bottom:1px solid var(--border);
}
.terminal-bar .t-dot { width:11px; height:11px; border-radius:50%; }
.terminal-bar .t-dot:nth-child(1){ background:#ff5f57; } .terminal-bar .t-dot:nth-child(2){ background:#febc2e; } .terminal-bar .t-dot:nth-child(3){ background:#28c840; }
.terminal-bar .t-title { margin-left:10px; font-size:11.5px; color:var(--muted); letter-spacing:.5px; }
.terminal pre {
padding:20px 22px; overflow-x:auto;
font:13px/1.8 'SF Mono','Cascadia Code','Fira Code',Consolas,monospace;
color:#cbd5e1;
}
.terminal pre .kw { color:#c084fc; } .terminal pre .fn { color:#60a5fa; }
.terminal pre .s { color:#34d399; } .terminal pre .cm { color:#475569; }
.terminal pre .num { color:#f472b6; }
/* ===== 主题预览 ===== */
.theme-demo { display:flex; gap:12px; justify-content:center; flex-wrap:wrap; margin-top:36px; }
.theme-btn {
padding:11px 22px; border-radius:10px; border:1px solid var(--border);
cursor:pointer; background:var(--surface); color:var(--text);
font-size:13.5px; font-weight:500; transition:all .2s;
}
.theme-btn:hover { border-color:rgba(129,140,248,.5); transform:translateY(-2px); }
.theme-btn.active {
background:var(--grad); color:#fff; border-color:transparent;
box-shadow:0 6px 24px -6px rgba(139,92,246,.6);
}
/* ===== 环境支持 ===== */
.env-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(160px, 1fr)); gap:12px; margin-top:40px; }
.env-card {
background:var(--surface); border:1px solid var(--border); border-radius:12px;
padding:20px 14px; text-align:center; transition:border-color .2s, transform .2s;
}
.env-card:hover { border-color:rgba(129,140,248,.4); transform:translateY(-3px); }
.env-card .e-ico { font-size:26px; }
.env-card h3 { font-size:14px; margin:8px 0 2px; }
.env-card p { font-size:12.5px; color:var(--muted); }
/* ===== CTA ===== */
.cta {
max-width:760px; margin:0 auto; text-align:center;
background:var(--grad); border-radius:20px; padding:56px 32px;
position:relative; overflow:hidden;
}
.cta::before {
content:''; position:absolute; inset:0;
background:radial-gradient(ellipse 60% 60% at 50% 120%, rgba(255,255,255,.25), transparent);
}
.cta h2 { font-size:30px; font-weight:800; color:#fff; position:relative; }
.cta p { color:rgba(255,255,255,.85); margin-top:10px; position:relative; }
.cta .btn { margin-top:26px; background:#fff; color:#6d28d9; position:relative; }
.cta .btn:hover { transform:translateY(-2px); }
footer { text-align:center; padding:44px 24px; color:var(--muted); font-size:13px; border-top:1px solid var(--border); position:relative; z-index:1; }
footer a { color:#a5b4fc; }
/* 滚动淡入 */
.reveal { opacity:0; transform:translateY(24px); transition:opacity .6s ease, transform .6s ease; }
.reveal.visible { opacity:1; transform:none; }
@media (max-width:768px) {
.hero { padding:80px 20px 44px; }
.nav-links { gap:16px; }
.container { padding:56px 20px; }
}
</style>
</head>
<body>
<nav class="nav">
<div class="nav-inner">
<div class="nav-logo"><span class="grad-text">MetonaToast</span></div>
<div class="nav-links">
<a href="index.html">首页</a>
<a href="docs.html">文档</a>
<a href="demo.html">演示</a>
<a href="https://git.metona.cn/MetonaTeam/MetonaToast" target="_blank">Gitea</a>
</div>
</div>
</nav>
<header class="hero">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<div class="hero-badge"><span class="dot"></span> v0.5.1 · 385 测试 · 覆盖率 96%</div>
<h1><span class="grad-text">MetonaToast</span></h1>
<p class="hero-sub">轻量 · 零依赖 · 精致美观的 Toast 通知库。TypeScript 严格模式,gzip 不到 10KB,一切开箱即用。</p>
<div class="hero-badges">
<span>📦 &lt;10KB gzip</span>
<span>🎨 107 图标</span>
<span>🎭 11 动画</span>
<span>🧩 4 插件</span>
<span>🔗 17 钩子</span>
<span>⚛️ React 适配器</span>
<span>🌍 国际化</span>
</div>
<div class="hero-actions">
<button class="btn btn-primary" onclick="demoQuickStart()">✨ 快速预览</button>
<button class="btn btn-ghost" onclick="location.href='docs.html'">📖 查看文档</button>
<button class="btn btn-ghost" onclick="location.href='demo.html'">🎮 在线演示</button>
</div>
</header>
<section class="stats reveal">
<div class="stats-grid">
<div class="stat"><div class="stat-num grad-text">&lt;10KB</div><div class="stat-label">gzip 体积</div></div>
<div class="stat"><div class="stat-num grad-text">107</div><div class="stat-label">内置图标</div></div>
<div class="stat"><div class="stat-num grad-text">11</div><div class="stat-label">动画效果</div></div>
<div class="stat"><div class="stat-num grad-text">17</div><div class="stat-label">生命周期钩子</div></div>
<div class="stat"><div class="stat-num grad-text">385</div><div class="stat-label">测试用例</div></div>
<div class="stat"><div class="stat-num grad-text">96%</div><div class="stat-label">行覆盖率</div></div>
</div>
</section>
<main class="container">
<h2 class="section-title reveal">核心特性</h2>
<p class="section-desc reveal">你需要的一切,都在一个不到 10KB 的包中</p>
<div class="features">
<div class="feature reveal">
<div class="feature-icon"></div>
<h3>零依赖</h3>
<p>TypeScript 严格模式,纯原生实现,无任何运行时依赖。兼容所有现代浏览器。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🎨</div>
<h3>107 种图标类型</h3>
<p>success / error / warning / info / loading 及 100+ 扩展类型,覆盖所有常见场景。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🎭</div>
<h3>11 种动画 + 自定义</h3>
<p>每种感官差异明显,<code>animations.register()</code> 注册自定义动画,动态注入 keyframes 真实生效。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🖌️</div>
<h3>主题系统</h3>
<p>light / dark / auto / warm 内置主题,<code>registerTheme()</code> 自定义,跟随系统自动切换。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🌍</div>
<h3>国际化</h3>
<p>zh-CN / en-US 完整翻译,<code>addTranslations()</code> 扩展任意语言,RTL 完整适配。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🧩</div>
<h3>插件系统</h3>
<p>4 款预设:keyboard / persistence / accessibility / dedupe,支持自定义插件架构。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🔗</div>
<h3>17 个钩子</h3>
<p>完整生命周期钩子,<code>beforeShow</code> / <code>beforeClose</code> / <code>beforeUpdate</code> 支持拦截。</p>
</div>
<div class="feature reveal">
<div class="feature-icon"></div>
<h3>拖拽关闭</h3>
<p>任意方向拖动即关,阈值 <code>dragThreshold</code> 可配置。Pointer Events 全端一致。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">⚛️</div>
<h3>React 适配器</h3>
<p><code>@metona-team/metona-toast/react</code> — useToast hook + 声明式 Toast 组件,主包保持零依赖。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">🛡️</div>
<h3>错误捕获</h3>
<p>全局 <code>onError</code> 回调捕获钩子与定时器异常,方便接入 Sentry、DataDog。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">⏱️</div>
<h3>暂停恢复</h3>
<p>悬停暂停、拖拽暂停、手动 pause/resume,剩余时间精确保持不丢失。</p>
</div>
<div class="feature reveal">
<div class="feature-icon">📘</div>
<h3>类型安全</h3>
<p>严格模式源码,类型自动生成零漂移。加载态转换 id 稳定,原地更新。</p>
</div>
</div>
</main>
<section class="container" style="padding-top:0">
<h2 class="section-title reveal">快速开始</h2>
<p class="section-desc reveal">浏览器上仅注册两个全局变量:<code>MeToast</code><code>Met</code>,完全等价</p>
<div class="code-block reveal">
<div class="terminal">
<div class="terminal-bar">
<span class="t-dot"></span><span class="t-dot"></span><span class="t-dot"></span>
<span class="t-title">quickstart.js</span>
</div>
<pre><span class="cm"># 安装</span>
npm install @metona-team/metona-toast
<span class="cm">// 导入</span>
<span class="kw">import</span> MeToast <span class="kw">from</span> <span class="s">'@metona-team/metona-toast'</span>;
<span class="cm">// 使用</span>
MeToast.<span class="fn">success</span>(<span class="s">'操作成功!'</span>);
MeToast.<span class="fn">error</span>(<span class="s">'操作失败!'</span>);
MeToast.<span class="fn">loading</span>(<span class="s">'加载中...'</span>);
<span class="cm">// CDN 直接引用</span>
<span class="kw">&lt;script</span> <span class="fn">src</span>=<span class="s">"https://git.metona.cn/MetonaTeam/MetonaToast/raw/branch/master/dist/metona-toast.min.js"</span><span class="kw">&gt;&lt;/script&gt;</span></pre>
</div>
</div>
</section>
<section class="container" style="padding-top:0">
<h2 class="section-title reveal">高级功能速览</h2>
<p class="section-desc reveal">不仅仅是 Toast — 确认框、输入框、进度条、倒计时、队列、Action,应有尽有</p>
<div class="code-block reveal">
<div class="terminal">
<div class="terminal-bar">
<span class="t-dot"></span><span class="t-dot"></span><span class="t-dot"></span>
<span class="t-title">advanced.js</span>
</div>
<pre><span class="cm">// Promise 风格 — 自动 loading → success/error</span>
<span class="kw">await</span> MeToast.<span class="fn">promise</span>(<span class="fn">fetch</span>(<span class="s">'/api/data'</span>), {
<span class="fn">loading</span>: <span class="s">'加载中...'</span>, <span class="fn">success</span>: <span class="s">'完成'</span>, <span class="fn">error</span>: <span class="s">'失败'</span>
});
<span class="cm">// 确认 / 输入对话框</span>
<span class="kw">const</span> ok = <span class="kw">await</span> MeToast.<span class="fn">confirm</span>(<span class="s">'确定删除?'</span>, { <span class="fn">confirmText</span>: <span class="s">'删除'</span> });
<span class="kw">const</span> name = <span class="kw">await</span> MeToast.<span class="fn">prompt</span>(<span class="s">'请输入姓名'</span>);
<span class="cm">// Action Toast — 内嵌操作按钮</span>
MeToast.<span class="fn">action</span>(<span class="s">'文件已删除'</span>, [
{ <span class="fn">text</span>: <span class="s">'撤销'</span>, <span class="fn">onClick</span>: () => <span class="fn">restore</span>() },
{ <span class="fn">text</span>: <span class="s">'查看'</span>, <span class="fn">onClick</span>: () => <span class="fn">open</span>(), <span class="fn">close</span>: <span class="kw">false</span> },
]);
<span class="cm">// 加载 → 链式转换(id 稳定)</span>
<span class="kw">const</span> loading = MeToast.<span class="fn">loading</span>(<span class="s">'正在提交...'</span>);
<span class="fn">setTimeout</span>(() => loading.<span class="fn">success</span>(<span class="s">'提交成功!'</span>), <span class="num">2000</span>);</pre>
</div>
</div>
</section>
<section class="container" style="padding-top:0">
<h2 class="section-title reveal">主题切换</h2>
<p class="section-desc reveal">点击按钮实时预览内置主题,切换结果自动持久化</p>
<div class="theme-demo reveal">
<button class="theme-btn" onclick="switchTheme('light')">☀️ 浅色 Light</button>
<button class="theme-btn" onclick="switchTheme('dark')">🌙 深色 Dark</button>
<button class="theme-btn" onclick="switchTheme('auto')">🔄 自动 Auto</button>
<button class="theme-btn" onclick="switchTheme('warm')">🔥 暖色 Warm</button>
</div>
</section>
<section class="container" style="padding-top:0">
<h2 class="section-title reveal">环境支持</h2>
<p class="section-desc reveal">所有现代浏览器原生支持,无需 polyfill</p>
<div class="env-grid reveal">
<div class="env-card"><div class="e-ico">🌐</div><h3>Chrome</h3><p>64+</p></div>
<div class="env-card"><div class="e-ico">🦊</div><h3>Firefox</h3><p>63+</p></div>
<div class="env-card"><div class="e-ico">🧭</div><h3>Safari</h3><p>11+</p></div>
<div class="env-card"><div class="e-ico">🪟</div><h3>Edge</h3><p>79+</p></div>
<div class="env-card"><div class="e-ico">📱</div><h3>iOS Safari</h3><p>11+</p></div>
<div class="env-card"><div class="e-ico">🤖</div><h3>Android</h3><p>64+</p></div>
<div class="env-card"><div class="e-ico">📦</div><h3>Node.js</h3><p>16+</p></div>
<div class="env-card"><div class="e-ico">📘</div><h3>TypeScript</h3><p>4.5+</p></div>
</div>
</section>
<section class="container" style="padding-top:0">
<div class="cta reveal">
<h2>开始使用 MetonaToast</h2>
<p>一条命令安装,立即体验精致优雅的 Toast 通知</p>
<button class="btn" onclick="location.href='demo.html'">🎮 打开在线演示</button>
</div>
</section>
<footer>
MetonaToast v0.5.1 · MIT License ·
<a href="https://git.metona.cn/MetonaTeam/MetonaToast" target="_blank">Gitea</a> ·
<a href="docs.html">文档</a> ·
<a href="demo.html">演示</a>
</footer>
<script src="../dist/metona-toast.js"></script>
<script>
MeToast.configure({ position: 'top-right', duration: 4000, theme: 'dark', animation: 'slide' });
function demoQuickStart() {
MeToast.success('✨ 欢迎使用 MetonaToast');
setTimeout(() => MeToast.info('💡 点击 Toast 或点击 × 即可关闭'), 800);
setTimeout(() => MeToast.warning('⚠️ 拖动 Toast 也能关闭', { draggable: true }), 1600);
setTimeout(() => MeToast.success('🎭 试试 bounce 动画', { animation: 'bounce' }), 2400);
}
function switchTheme(t) {
MeToast.themes.switchTheme(t);
MeToast.configure({ theme: t });
document.querySelectorAll('.theme-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
const names = { light: '浅色', dark: '深色', auto: '自动', warm: '暖色' };
MeToast.info('已切换到「' + names[t] + '」主题', { theme: t });
}
// 滚动淡入
const observer = new IntersectionObserver(entries => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); observer.unobserve(e.target); } });
}, { threshold: 0.12 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
// 页面加载自动演示
setTimeout(demoQuickStart, 600);
</script>
</body>
</html>