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:
xiaomai
2025-09-11 15:17:12 +08:00
parent b05b5af5f1
commit 4e86ab5627
2 changed files with 594 additions and 142 deletions

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@
# Built Visual Studio Code Extensions
*.vsix
.vscode/settings.json

View File

@@ -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>
</header>
</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>
<!-- 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>
<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>
<!-- 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>
<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>
<!-- Footer -->
<footer class="text-gray-400 text-sm text-center pt-8 border-t border-white/10 w-full">
&copy; 2024-2025 Xiaomai. All services self-hosted 🧠💻<br />
Powered by TailwindCSS · Script-enhanced · Inspired by Productivity ⚙️
</footer>
</main>
<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>
<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>&copy; 2023 Tootaio. <span data-lang="en">All rights reserved.</span><span data-lang="zh" style="display: none;">版权所有。</span></p>
</div>
</div>
</footer>
<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" },
];
// 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';
}
});
const container = document.getElementById("service-grid");
const extContainer = document.getElementById("external-links");
// Update button text
const buttonText = currentLang === 'en' ? '中文' : 'English';
languageSwitcher.querySelector('span').textContent = buttonText;
});
async function checkStatus(service) {
try {
await fetch(`${service.secured ? "https" : "http"}://${hostIP}:${service.port}`, {
method: "HEAD",
mode: "no-cors",
});
return true;
} catch {
return false;
}
}
// Mobile menu toggle (optional enhancement)
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
const navLinks = document.querySelector('.nav-links');
(async () => {
for (const service of services) {
const online = await checkStatus(service);
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]";
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>