feat(ui): overhaul landing page with new design and features
This commit completely replaces the previous simple service dashboard with a full-featured, professional landing page for the 'Tootaio' brand. The new design is modern, responsive, and uses a card-based layout. Key features include a language switcher for English/Chinese, user-friendly subdomain URLs instead of IP:port links, and a structured layout with a header, hero section, and footer.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
.vscode/settings.json
|
||||
|
||||
709
index.html
709
index.html
@@ -1,155 +1,606 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>🌐 My Services Hub</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/tailwindcss@4.1.4/index.min.css"
|
||||
/>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tootaio - 一站式开发者服务平台</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
:root {
|
||||
--primary: #6366f1;
|
||||
--primary-dark: #4f46e5;
|
||||
--secondary: #8b5cf6;
|
||||
--light: #f8fafc;
|
||||
--dark: #1e293b;
|
||||
--gray: #64748b;
|
||||
--card-bg: #ffffff;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
.dark-mode {
|
||||
--light: #0f172a;
|
||||
--dark: #f1f5f9;
|
||||
--gray: #cbd5e1;
|
||||
--card-bg: #1e293b;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: var(--light);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
background: linear-gradient(120deg, var(--primary), var(--secondary));
|
||||
color: white;
|
||||
padding: 1rem 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.language-switcher {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 4rem 1rem;
|
||||
background: linear-gradient(45deg, #4f46e5, #7c3aed);
|
||||
color: white;
|
||||
border-radius: 0 0 20px 20px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.2rem;
|
||||
max-width: 700px;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
background-color: white;
|
||||
color: var(--primary);
|
||||
padding: 12px 30px;
|
||||
border-radius: 50px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
font-size: 2.2rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.services {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
.services-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.service-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 1.5rem;
|
||||
background: linear-gradient(45deg, var(--primary), var(--secondary));
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card-header h3 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.card-body p {
|
||||
color: var(--gray);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.card-link {
|
||||
display: inline-block;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.other-links {
|
||||
padding: 3rem 0;
|
||||
background-color: rgba(99, 102, 241, 0.05);
|
||||
border-radius: 20px;
|
||||
margin: 3rem 0;
|
||||
}
|
||||
|
||||
.links-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.link-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background-color: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
text-decoration: none;
|
||||
color: var(--dark);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.link-item:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.link-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(45deg, var(--primary), var(--secondary));
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--dark);
|
||||
color: white;
|
||||
padding: 3rem 0 2rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.footer-column {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.footer-column h3 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1.2rem;
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.footer-column a {
|
||||
display: block;
|
||||
color: #cbd5e1;
|
||||
text-decoration: none;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
|
||||
.footer-column a:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
text-align: center;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nav-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-menu-btn {
|
||||
display: none;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.mobile-menu-btn {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen text-white font-sans tracking-wide">
|
||||
<main class="flex flex-col items-center px-6 py-10 space-y-24">
|
||||
<!-- Header -->
|
||||
<header class="text-center space-y-4">
|
||||
<h1 class="text-5xl font-bold">🌐 My Network Services</h1>
|
||||
<p class="text-gray-300 text-lg">
|
||||
All services hosted under the same IP, organized for your convenience 💡
|
||||
</p>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container nav-container">
|
||||
<div class="logo">
|
||||
<i class="fas fa-cube"></i>
|
||||
<span data-lang="en">Tootaio</span>
|
||||
<span data-lang="zh" style="display: none;">Tootaio</span>
|
||||
</div>
|
||||
<nav class="nav-links">
|
||||
<a href="#" data-lang="en">Home</a>
|
||||
<a href="#" data-lang="en">Services</a>
|
||||
<a href="#" data-lang="en">About</a>
|
||||
<a href="#" data-lang="en">Contact</a>
|
||||
|
||||
<a href="#" data-lang="zh" style="display: none;">首页</a>
|
||||
<a href="#" data-lang="zh" style="display: none;">服务</a>
|
||||
<a href="#" data-lang="zh" style="display: none;">关于</a>
|
||||
<a href="#" data-lang="zh" style="display: none;">联系</a>
|
||||
</nav>
|
||||
<button class="language-switcher" id="languageSwitcher">
|
||||
<span data-lang="en">中文</span>
|
||||
<span data-lang="zh" style="display: none;">English</span>
|
||||
</button>
|
||||
<button class="mobile-menu-btn">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Services Section -->
|
||||
<section class="w-full max-w-7xl">
|
||||
<h2 class="text-3xl font-semibold mb-6">🛎️ Hosted Services</h2>
|
||||
<div
|
||||
id="service-grid"
|
||||
class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8"
|
||||
></div>
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1 data-lang="en">Your All-in-One Developer Platform</h1>
|
||||
<h1 data-lang="zh" style="display: none;">一站式开发者服务平台</h1>
|
||||
<p data-lang="en">Tootaio provides a suite of powerful tools and services for developers, gamers, and creators. Everything you need in one place.</p>
|
||||
<p data-lang="zh" style="display: none;">Tootaio 为开发者、游戏玩家和创作者提供一套强大的工具和服务。您所需的一切,尽在一处。</p>
|
||||
<a href="#services" class="cta-button" data-lang="en">Explore Services</a>
|
||||
<a href="#services" class="cta-button" data-lang="zh" style="display: none;">探索服务</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- External Links Section -->
|
||||
<section class="w-full max-w-4xl border-t border-white/20 pt-12">
|
||||
<h2 class="text-3xl font-semibold mb-6">📎 External Resources</h2>
|
||||
<div id="external-links" class="grid grid-cols-1 sm:grid-cols-2 gap-6"></div>
|
||||
<section class="services" id="services">
|
||||
<div class="container">
|
||||
<h2 class="section-title" data-lang="en">Our Services</h2>
|
||||
<h2 class="section-title" data-lang="zh" style="display: none;">我们的服务</h2>
|
||||
|
||||
<div class="services-grid">
|
||||
<!-- Main Site -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">The main portal to all Tootaio services and resources.</p>
|
||||
<p data-lang="zh" style="display: none;">通往所有 Tootaio 服务和资源的主门户。</p>
|
||||
<a href="https://tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gitea -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>git.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Self-hosted Gitea open source community platform.</p>
|
||||
<p data-lang="zh" style="display: none;">自托管的 Gitea 开源社区平台。</p>
|
||||
<a href="https://git.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- NAS -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>nas.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Cloud storage solution for your files and data.</p>
|
||||
<p data-lang="zh" style="display: none;">为您的文件和数据提供的云存储解决方案。</p>
|
||||
<a href="https://nas.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Torrent -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>torrent.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Download torrent files to our server with high speed.</p>
|
||||
<p data-lang="zh" style="display: none;">借助服务器高速下载 Torrent 文件。</p>
|
||||
<a href="https://torrent.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wiki -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>wiki.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Wiki for our studio's games and project planning.</p>
|
||||
<p data-lang="zh" style="display: none;">我们工作室游戏和项目策划的维基百科。</p>
|
||||
<a href="https://wiki.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kenney Assets -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>kenney-assets.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Unofficial mirror for Kenney's game assets.</p>
|
||||
<p data-lang="zh" style="display: none;">Kenney 游戏资源的非官方镜像站。</p>
|
||||
<a href="https://kenney-assets.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Itch.io Godot Downloader -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>itch-gd-dl.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Itch.io Godot game crawler and downloader.</p>
|
||||
<p data-lang="zh" style="display: none;">Itch.io Godot 游戏爬取和下载工具。</p>
|
||||
<a href="https://itch-gd-dl.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF Tools -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>pdf.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Online PDF tools for all your document needs.</p>
|
||||
<p data-lang="zh" style="display: none;">满足您所有文档需求的在线 PDF 工具。</p>
|
||||
<a href="https://pdf.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Life Restart -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>life-restart.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Mirror site for the Life Restart Simulator game.</p>
|
||||
<p data-lang="zh" style="display: none;">人生重开模拟器游戏的镜像站。</p>
|
||||
<a href="https://life-restart.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Memos -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>memos.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">Note-taking space for your ideas and thoughts.</p>
|
||||
<p data-lang="zh" style="display: none;">记录您的想法和思考的笔记空间。</p>
|
||||
<a href="https://memos.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JSON Hero -->
|
||||
<div class="service-card">
|
||||
<div class="card-header">
|
||||
<h3>json.tootaio.com</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p data-lang="en">JSON Hero viewer for visualizing and editing JSON data.</p>
|
||||
<p data-lang="zh" style="display: none;">用于可视化和编辑 JSON 数据的 JSON Hero 查看器。</p>
|
||||
<a href="https://json.tootaio.com" class="card-link" target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="text-gray-400 text-sm text-center pt-8 border-t border-white/10 w-full">
|
||||
© 2024-2025 Xiaomai. All services self-hosted 🧠💻<br />
|
||||
Powered by TailwindCSS · Script-enhanced · Inspired by Productivity ⚙️
|
||||
<section class="other-links">
|
||||
<div class="container">
|
||||
<h2 class="section-title" data-lang="en">Other Links</h2>
|
||||
<h2 class="section-title" data-lang="zh" style="display: none;">其他链接</h2>
|
||||
|
||||
<div class="links-grid">
|
||||
<a href="https://kingsmai.github.io" class="link-item" target="_blank">
|
||||
<div class="link-icon">
|
||||
<i class="fas fa-blog"></i>
|
||||
</div>
|
||||
<span data-lang="en">Kingsmai's Personal Blog</span>
|
||||
<span data-lang="zh" style="display: none;">Kingsmai 的个人博客</span>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/kingsmai" class="link-item" target="_blank">
|
||||
<div class="link-icon">
|
||||
<i class="fab fa-github"></i>
|
||||
</div>
|
||||
<span data-lang="en">Kingsmai's GitHub</span>
|
||||
<span data-lang="zh" style="display: none;">Kingsmai 的 GitHub</span>
|
||||
</a>
|
||||
|
||||
<a href="https://discord.com/invite/sJcv7ZM" class="link-item" target="_blank">
|
||||
<div class="link-icon">
|
||||
<i class="fab fa-discord"></i>
|
||||
</div>
|
||||
<span data-lang="en">Studio Discord Community</span>
|
||||
<span data-lang="zh" style="display: none;">工作室 Discord 社区</span>
|
||||
</a>
|
||||
|
||||
<a href="https://space.bilibili.com/670118055" class="link-item" target="_blank">
|
||||
<div class="link-icon">
|
||||
<i class="fas fa-video"></i>
|
||||
</div>
|
||||
<span data-lang="en">Studio BiliBili Space</span>
|
||||
<span data-lang="zh" style="display: none;">工作室 BiliBili 空间</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-column">
|
||||
<h3 data-lang="en">Tootaio</h3>
|
||||
<h3 data-lang="zh" style="display: none;">Tootaio</h3>
|
||||
<a href="#" data-lang="en">About Us</a>
|
||||
<a href="#" data-lang="en">Our Team</a>
|
||||
<a href="#" data-lang="en">Careers</a>
|
||||
|
||||
<a href="#" data-lang="zh" style="display: none;">关于我们</a>
|
||||
<a href="#" data-lang="zh" style="display: none;">我们的团队</a>
|
||||
<a href="#" data-lang="zh" style="display: none;">招聘信息</a>
|
||||
</div>
|
||||
|
||||
<div class="footer-column">
|
||||
<h3 data-lang="en">Services</h3>
|
||||
<h3 data-lang="zh" style="display: none;">服务</h3>
|
||||
<a href="https://git.tootaio.com" target="_blank">Gitea</a>
|
||||
<a href="https://nas.tootaio.com" target="_blank">Cloud Storage</a>
|
||||
<a href="https://wiki.tootaio.com" target="_blank">Project Wiki</a>
|
||||
<a href="https://pdf.tootaio.com" target="_blank">PDF Tools</a>
|
||||
</div>
|
||||
|
||||
<div class="footer-column">
|
||||
<h3 data-lang="en">Connect</h3>
|
||||
<h3 data-lang="zh" style="display: none;">联系</h3>
|
||||
<a href="https://github.com/kingsmai" target="_blank">GitHub</a>
|
||||
<a href="https://discord.com/invite/sJcv7ZM" target="_blank">Discord</a>
|
||||
<a href="https://space.bilibili.com/670118055" target="_blank">BiliBili</a>
|
||||
<a href="mailto:contact@tootaio.com">Email</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="copyright">
|
||||
<p>© 2023 Tootaio. <span data-lang="en">All rights reserved.</span><span data-lang="zh" style="display: none;">版权所有。</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const hostIP = window.location.hostname;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const languageSwitcher = document.getElementById('languageSwitcher');
|
||||
let currentLang = 'en';
|
||||
|
||||
const services = [
|
||||
{ port: 81, name: "🏠 Personal Homepage", desc: "Work in progress. Stay tuned!", tags: ["Web"] },
|
||||
{ port: 40069, name: "☁ Nextcloud", desc: "Self-hosted cloud storage", secured: true, tags: ["Cloud", "Storage"] },
|
||||
{ port: 1957, name: "🌦️ Malaysia Weather Radar", desc: "Historical weather radar visualization", tags: ["Data", "Weather"] },
|
||||
{ port: 1025, name: "🎮 Kenney's Assets", desc: "Open game art & asset collection", tags: ["Assets", "Games"] },
|
||||
{ port: 1031, name: "🌐 Itch io PCK dl", desc: "Download Godot pck files from itch.io", tags: ["Games", "Tools"] },
|
||||
{ port: 3000, name: "📁 Gitea", desc: "Self-hosted Git service", tags: ["Code", "Git"] },
|
||||
{ port: 5230, name: "📝 Memos", desc: "Lightweight self-hosted note-taking", tags: ["Notes", "Productivity"] },
|
||||
{ port: 10001, name: "📄 Stirling PDF", desc: "Powerful locally hosted web based PDF manipulation tool", tags: ["Tools", "PDF"] },
|
||||
{ port: 40178, name: "📊 JSON Hero", desc: "Visual JSON explorer", tags: ["Tools", "JSON"] },
|
||||
{ port: 24680, name: "🛠️ 1Panel Dashboard", desc: "Unified server management panel", tags: ["Admin", "Dashboard"] },
|
||||
{ port: 2001, name: "🤵 Life Restart", desc: "A game about life and death", tags: ["Games", "Simulation"] },
|
||||
{ port: 8848, name: "Vue Pure Admin", desc: "Demo site for Vue Pure Admin", tags: ["Admin", "Demo"] },
|
||||
];
|
||||
languageSwitcher.addEventListener('click', function() {
|
||||
// Toggle language
|
||||
currentLang = currentLang === 'en' ? 'zh' : 'en';
|
||||
|
||||
const externalLinks = [
|
||||
{ name: "🔗 GitHub", desc: "Code repositories and projects", url: "https://github.com/kingsmai" },
|
||||
{ name: "📘 Blog", desc: "Personal dev blog and updates", url: "https://kingsmai.github.io" },
|
||||
{ name: "💬 Discord Server", desc: "Join the dev chat room", url: "https://discord.gg/sJcv7ZM" },
|
||||
];
|
||||
|
||||
const container = document.getElementById("service-grid");
|
||||
const extContainer = document.getElementById("external-links");
|
||||
|
||||
async function checkStatus(service) {
|
||||
try {
|
||||
await fetch(`${service.secured ? "https" : "http"}://${hostIP}:${service.port}`, {
|
||||
method: "HEAD",
|
||||
mode: "no-cors",
|
||||
// Update all elements with data-lang attribute
|
||||
document.querySelectorAll('[data-lang]').forEach(element => {
|
||||
if (element.getAttribute('data-lang') === currentLang) {
|
||||
element.style.display = element.tagName === 'SPAN' ? 'inline' : 'block';
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
for (const service of services) {
|
||||
const online = await checkStatus(service);
|
||||
// Update button text
|
||||
const buttonText = currentLang === 'en' ? '中文' : 'English';
|
||||
languageSwitcher.querySelector('span').textContent = buttonText;
|
||||
});
|
||||
|
||||
const card = document.createElement("a");
|
||||
card.href = `${service.secured ? "https" : "http"}://${hostIP}:${service.port}`;
|
||||
card.target = "_blank";
|
||||
card.rel = "noopener noreferrer";
|
||||
card.className =
|
||||
"rounded-xl overflow-hidden p-6 backdrop-blur-md bg-white/10 hover:bg-white/20 transition duration-300 border border-white/20 shadow-xl transform hover:scale-[1.03]";
|
||||
// Mobile menu toggle (optional enhancement)
|
||||
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="flex flex-col h-full">
|
||||
<h2 class="text-2xl font-bold mb-2">${service.name}</h2>
|
||||
<p class="text-gray-300 flex-grow">${service.desc}</p>
|
||||
<div class="mt-4 flex justify-between items-center text-sm">
|
||||
<div class="text-gray-400">Port: ${service.port}</div>
|
||||
<div class="${online ? "bg-green-600" : "bg-red-600"} text-white px-2 py-1 rounded-full text-xs">
|
||||
${online ? "🟢 Online" : "🔴 Offline"}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
${service.tags
|
||||
.map(
|
||||
(tag) =>
|
||||
`<span class="bg-blue-800 text-white text-xs px-2 py-1 rounded-full">${tag}</span>`
|
||||
)
|
||||
.join("")}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(card);
|
||||
}
|
||||
})();
|
||||
|
||||
for (const link of externalLinks) {
|
||||
const card = document.createElement("a");
|
||||
card.href = link.url;
|
||||
card.target = "_blank";
|
||||
card.rel = "noopener noreferrer";
|
||||
card.className =
|
||||
"rounded-lg border border-white/20 backdrop-blur-md bg-white/10 hover:bg-white/20 transition p-5 shadow-lg flex flex-col";
|
||||
|
||||
card.innerHTML = `
|
||||
<h3 class="text-xl font-semibold mb-1">${link.name}</h3>
|
||||
<p class="text-gray-300 text-sm">${link.desc}</p>
|
||||
`;
|
||||
|
||||
extContainer.appendChild(card);
|
||||
}
|
||||
mobileMenuBtn.addEventListener('click', function() {
|
||||
navLinks.style.display = navLinks.style.display === 'flex' ? 'none' : 'flex';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user