Files
eSIM-Tools/index.html
Abner 62e1363563 feat: 添加 Ko-fi 赞助按钮和改进样式
- 在首页、Giffgaff 和 Simyo 页面中添加了 Ko-fi 赞助按钮,方便用户支持项目。
- 引入了新的 `.header-actions` CSS 类,用于统一管理按钮组的布局和间距。
- 优化了按钮的样式,使其更具现代感和一致性,包括悬停效果和焦点状态。
- 为新增的 Ko-fi 按钮添加了相应的国际化文本和 ARIA 标签。
- 调整了移动端响应式样式,确保按钮在小屏幕设备上也能良好显示。
- 统一了 GitHub 和 Ko-fi 按钮的样式,使其在视觉上更协调。
- 更新了设计系统 CSS 文件,添加了新的按钮组样式和响应式适配。
2026-03-19 19:10:01 +08:00

400 lines
18 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-page="home">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="eSIM 工具集,支持 Giffgaff 与 Simyo 的 eSIM 申请与管理、二维码生成及验证码处理。">
<!-- Sentry 错误监控配置(必须在其他脚本之前) -->
<script>
// Sentry 配置 - 在 Netlify 部署时会通过环境变量注入
window.SENTRY_DSN = ''; // 在 Netlify 环境变量中配置
window.SENTRY_ENVIRONMENT = 'production';
window.SENTRY_RELEASE = 'esim-tools@unknown';
</script>
<script src="/src/js/sentry-loader.js"></script>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://browser.sentry-cdn.com https://www.googletagmanager.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src 'self' data:; connect-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.google-analytics.com https://www.googletagmanager.com https://qrcode.show https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com https://*.sentry.io; img-src 'self' data: https:; frame-src 'self' https://challenges.cloudflare.com https://www.google.com;">
<title>eSIM Tools - Giffgaff & Simyo</title>
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#6366f1">
<!-- 性能优化:预连接与预加载关键资源 -->
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<link rel="preconnect" href="https://browser.sentry-cdn.com" crossorigin>
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
<link rel="preconnect" href="https://qrcode.show" crossorigin>
<link rel="preconnect" href="https://api.giffgaff.com" crossorigin>
<link rel="preconnect" href="https://appapi.simyo.nl" crossorigin>
<!-- Favicon -->
<link rel="icon" href="/src/assets/favicon.ico" type="image/x-icon">
<!-- 关键CSS优先preload 后备 rel=stylesheet -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" as="style" onload="this.rel='stylesheet'">
<noscript><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"></noscript>
<!-- Font Awesome直接引入避免 onload 触发器被 CSP 阻断导致样式未生效 -->
<link rel="stylesheet" href="/src/assets/fontawesome/all.min.css">
<!-- Font Awesome 资源路径woff2在 /src/assets/fontawesome/,确保已发布且 CSP 允许 font-src 'self' -->
<!-- 本地样式文件 -->
<link rel="preload" href="/src/styles/design-system.css" as="style" onload="this.rel='stylesheet'">
<link rel="preload" href="/src/styles/animations.css" as="style" onload="this.rel='stylesheet'">
<link rel="stylesheet" href="/src/styles/fa-local.css">
<link rel="stylesheet" href="/src/styles/notification.css">
<meta name="build-rev" content="v1">
<style>
/* Design System: 语义化令牌与统一规范 */
:root{
/* Brand */
--brand-start:#6366f1; /* indigo-500 */
--brand-end:#8b5cf6; /* violet-500 */
--accent-ok:#10b981; /* emerald-500 */
--accent-warn:#f59e0b; /* amber-500 */
--accent-danger:#ef4444; /* red-500 */
/* Surface & Text */
/* 背景改为淡色 #f2f5c4 */
--bg-grad-start:#f2f5c4;
--bg-grad-end:#f1f5bb;
--surface:#ffffff;
--surface-alpha:92%;
--text-primary:#1f2937; /* slate-800 */
--text-secondary:#374151; /* gray-700 */
--text-muted:#6b7280; /* gray-500 */
/* Radius & Elevation & Spacing */
--radius-lg:18px;
--shadow-sm:0 4px 10px rgba(0,0,0,.08);
--shadow-md:0 10px 30px rgba(0,0,0,.10);
--shadow-lg:0 18px 40px rgba(0,0,0,.15);
--ring:0 0 0 3px rgba(99,102,241,.35);
--s1:8px; --s2:12px; --s3:16px; --s4:20px; --s5:24px; --s6:32px;
/* Motion */
--ease-standard:cubic-bezier(.2,.8,.2,1);
--duration-fast:.18s;
--duration-normal:.28s;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, var(--bg-grad-start) 0%, var(--bg-grad-end) 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: var(--s5);
color: var(--text-primary);
}
.main-container{
background: color-mix(in oklab, white var(--surface-alpha), transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: var(--radius-lg);
padding: clamp(20px, 4vw, 40px);
box-shadow: var(--shadow-lg);
max-width: 960px;
width: 100%;
text-align: center;
border: 1px solid color-mix(in oklab, white 70%, transparent);
}
.language-switcher{
display:flex;
justify-content:flex-end;
width:100%;
margin-bottom: var(--s3);
}
.lang-switch{
display:inline-flex;
align-items:center;
gap:6px;
padding:6px 16px;
border-radius:999px;
border:1px solid rgba(99,102,241,.25);
background: rgba(255,255,255,.7);
color: var(--text-primary);
font-weight:600;
cursor:pointer;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.lang-switch:hover{
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.lang-switch:focus-visible{
outline:none;
box-shadow: var(--ring);
}
.header{ margin-bottom: var(--s6); }
.header h1{
font-size: clamp(1.9rem, 3vw, 2.6rem);
font-weight: 800;
color: var(--text-primary);
margin-bottom: var(--s2);
letter-spacing: .2px;
display:flex;align-items:center;justify-content:center;gap:10px;
}
.header .subtitle{
font-size: clamp(.95rem, 1.9vw, 1.12rem);
color: var(--text-muted);
margin: 0;
}
.header-actions{
display: flex;
gap: var(--s2);
justify-content: center;
flex-wrap: wrap;
margin-top: var(--s3);
}
.github-btn, .kofi-btn{
font-size: 16px;
font-weight: 600;
padding: 10px 18px;
display:inline-flex;
align-items:center;
justify-content:center;
border-radius: 999px;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard);
gap: 8px;
box-shadow: var(--shadow-sm);
text-decoration: none !important;
outline: none;
will-change: transform;
border: 1px solid rgba(255,255,255,.18);
}
.github-btn{ background: #24292e; color:#fff !important; }
.github-btn:hover{ transform: translateY(-1px); box-shadow: var(--shadow-md); background:#111; }
.kofi-btn{ background: #FF5E5B; color:#fff !important; }
.kofi-btn:hover{ transform: translateY(-1px); box-shadow: var(--shadow-md); background:#e64a47; }
.github-btn:focus-visible, .kofi-btn:focus-visible{ outline: none; box-shadow: var(--ring); }
.tools-grid{
display:grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: clamp(16px, 3vw, 28px);
margin-top: var(--s6);
}
@media (max-width: 720px){ .tools-grid{ grid-template-columns: 1fr; } }
.tool-card{
background: var(--surface);
border-radius: var(--radius-lg);
padding: clamp(20px, 3.2vw, 28px);
box-shadow: var(--shadow-md);
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
border: 2px solid transparent;
position: relative;
overflow: hidden;
will-change: transform, box-shadow;
}
.tool-card::before{
content: '';
position: absolute;
inset: 0 0 auto 0;
height: 4px;
background: linear-gradient(90deg, var(--primary), var(--secondary));
}
.tool-card:hover{
transform: translateY(-6px);
box-shadow: var(--shadow-lg);
border-color: color-mix(in oklab, var(--primary) 40%, transparent);
}
.tool-card:focus-within{ box-shadow: var(--ring); }
/* 品牌色注入 */
.giffgaff-card{ --primary:#ff6b00; --secondary:#ff8c42; }
.simyo-card{ --primary:#e74c3c; --secondary:#c0392b; }
.tool-icon{
font-size: clamp(2.2rem, 3.2vw, 2.8rem);
color: var(--primary);
margin-bottom: var(--s3);
}
.tool-title{
font-size: clamp(1.2rem, 2.2vw, 1.4rem);
font-weight: 800;
color: var(--text-primary);
margin-bottom: var(--s2);
}
.tool-description{
color: var(--text-muted);
margin-bottom: var(--s4);
line-height: 1.65;
}
.tool-features{
list-style: none;
padding: 0;
margin-bottom: var(--s4);
text-align: left;
}
.tool-features li{
display: flex;
align-items: baseline;
gap: 10px;
padding: 6px 0;
color: var(--text-secondary);
}
.tool-features li i{ color: var(--primary); flex: 0 0 auto; }
.tool-features li span{ flex: 1 1 auto; }
.tool-btn{
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: #fff;
border: none;
padding: 12px 22px;
border-radius: 999px;
font-size: 1rem;
font-weight: 800;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 10px;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), filter var(--duration-fast) var(--ease-standard);
box-shadow: 0 6px 16px color-mix(in oklab, var(--primary) 35%, transparent);
outline: none;
will-change: transform, filter;
}
.tool-btn:hover{ transform: translateY(-2px); text-decoration: none; color: #fff; filter: brightness(1.03); }
.tool-btn:focus-visible{ box-shadow: var(--ring); }
.footer{
margin-top: var(--s6);
padding-top: var(--s5);
border-top: 1px solid color-mix(in oklab, #ecf0f1 85%, transparent);
color: #6b7280;
font-size: .94rem;
line-height: 1.6;
text-align: center;
}
/* 入场动画IntersectionObserver 触发,避免 setTimeout 抖动 */
.reveal{ opacity: 0; transform: translateY(12px); transition: opacity .5s var(--ease-standard), transform .5s var(--ease-standard); will-change: opacity, transform; }
.reveal.show{ opacity: 1; transform: translateY(0); }
/* 降低动效:尊重用户的减少动态偏好 */
@media (prefers-reduced-motion: reduce){
.reveal{ transition: none; transform: none; opacity: 1; }
.tool-card, .github-btn, .tool-btn{ transition: none!important; }
}
/* 小屏进一步优化 */
@media (max-width: 360px){
.github-btn{ padding: 9px 14px; font-size: 14px; }
.tool-btn{ padding: 10px 18px; font-size: .95rem; }
}
</style>
<!-- Google tag (gtag.js) 延迟加载 -->
<script type="module" src="/src/js/captcha-client.js"></script>
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-HGX38HMNL1"></script>
</head>
<body aria-label="eSIM 工具选择">
<div class="main-container" role="region" aria-label="eSIM 工具选择">
<div class="language-switcher">
<button id="languageSwitcher" class="lang-switch" type="button" aria-label="切换语言到英文">🌐 English</button>
</div>
<div class="header">
<h1><i class="fas fa-sim-card" aria-hidden="true"></i> <span class="header-title-text">eSIM 工具集</span></h1>
<p class="subtitle">轻松管理您的 Giffgaff 与 Simyo eSIM 配置</p>
<div class="header-actions">
<a href="https://github.com/Silentely/eSIM-Tools" target="_blank" rel="noopener noreferrer" class="github-btn" aria-label="在新窗口打开 GitHub 仓库">
<i class="fab fa-github" aria-hidden="true"></i> GitHub
</a>
<a href="https://ko-fi.com/debug402" target="_blank" rel="noopener noreferrer" class="kofi-btn" aria-label="Support me on Ko-fi">
<i class="fas fa-coffee" aria-hidden="true"></i> <span>Support Me</span>
</a>
</div>
</div>
<div class="tools-grid" aria-live="polite">
<!-- Giffgaff工具卡片 -->
<section class="tool-card giffgaff-card reveal" tabindex="-1" aria-labelledby="giffgaff-title" aria-describedby="giffgaff-desc">
<div class="tool-icon" aria-hidden="true">
<i class="fas fa-mobile-alt"></i>
</div>
<h2 id="giffgaff-title" class="tool-title"><span class="tool-title-text">Giffgaff eSIM 工具</span></h2>
<p id="giffgaff-desc" class="tool-description">
<span class="tool-description-text">完整的 Giffgaff eSIM 申请与管理,支持 OAuth 认证、MFA 验证与 GraphQL API 调用</span>
</p>
<ul class="tool-features">
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>OAuth 2.0 PKCE 认证</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>邮件验证码与 MFA 支持</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>GraphQL API 集成</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>自动生成 eSIM 二维码</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>完善的错误处理</span></li>
</ul>
<a href="/giffgaff" class="tool-btn" aria-describedby="giffgaff-desc">
<i class="fas fa-arrow-right" aria-hidden="true"></i><span>使用 Giffgaff 工具</span>
</a>
</section>
<!-- Simyo工具卡片 -->
<section class="tool-card simyo-card reveal" tabindex="-1" aria-labelledby="simyo-title" aria-describedby="simyo-desc">
<div class="tool-icon" aria-hidden="true">
<i class="fas fa-sim-card"></i>
</div>
<h2 id="simyo-title" class="tool-title"><span class="tool-title-text">Simyo 工具</span></h2>
<p id="simyo-desc" class="tool-description">
<span class="tool-description-text">专为 Simyo NL 用户的 eSIM 管理,支持设备更换与短信验证码处理</span>
</p>
<ul class="tool-features">
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>简单快捷的登录验证</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>设备更换完整流程</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>短信验证码支持</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>一键生成二维码</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>安装确认功能</span></li>
</ul>
<a href="/simyo" class="tool-btn" aria-describedby="simyo-desc">
<i class="fas fa-arrow-right" aria-hidden="true"></i><span>使用 Simyo 工具</span>
</a>
</section>
</div>
<footer class="footer" aria-label="免责声明与隐私说明">
<p>
<i class="fas fa-info-circle me-2" aria-hidden="true"></i>
本工具仅供个人使用,请遵守相关服务条款。<br>
<i class="fas fa-shield-alt me-2" aria-hidden="true"></i>
所有数据处理均在本地进行,保护您的隐私安全。
</p>
</footer>
</div>
<!-- 将 bootstrap 脚本 defer减少阻塞 -->
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<!-- 首页逻辑脚本(无内联) -->
<script defer src="/src/js/home.js"></script>
<!-- 统一版权页脚(自动注入年份/品牌) -->
<script type="module" src="/src/js/bootstrap-footer.js"></script>
<!-- 国际化初始化 -->
<script type="module" src="/src/js/init-home-i18n.js"></script>
<!-- 通知系统初始化 -->
<script type="module">
import NotificationService from '/src/js/modules/notification-service.js';
document.addEventListener('DOMContentLoaded', () => {
NotificationService.init();
});
</script>
<!-- 性能优化脚本 -->
<script src="/src/js/performance.js"></script>
</body>
</html>