File size: 8,135 Bytes
30c9413 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="nav.title">HF Space Deployer</title>
<!-- Tailwind CSS + DaisyUI -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<!-- Custom CSS -->
<link href="/static/style.css" rel="stylesheet" type="text/css" />
<!-- HTMX -->
<script src="https://unpkg.com/[email protected]"></script>
<!-- Icons -->
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
<!-- Alpine.js for reactive components -->
<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
<!-- i18n -->
<script src="/static/i18n.js"></script>
<style>
.loading-spinner {
animation: spin 2s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body class="min-h-screen bg-base-100">
<!-- Navigation -->
<div class="navbar bg-primary text-primary-content shadow-lg sticky top-0 z-50">
<div class="container mx-auto">
<div class="flex-1">
<a href="/" class="btn btn-ghost text-xl font-bold">
<i data-lucide="rocket" class="w-6 h-6 mr-2"></i>
<span data-i18n="nav.title">HF Space Deployer</span>
</a>
</div>
<div class="flex-none space-x-2">
<button
class="btn btn-ghost btn-circle"
onclick="toggleLanguage()"
data-i18n-title="nav.language"
title="Language"
>
<span class="text-lg font-bold lang-icon">EN</span>
</button>
<button
class="btn btn-ghost btn-circle"
onclick="toggleTheme()"
data-i18n-title="nav.theme"
title="Toggle Theme"
>
<i data-lucide="sun" class="w-5 h-5 theme-icon"></i>
</button>
</div>
</div>
</div>
<!-- Toast Container -->
<div id="toast-container" class="fixed top-20 right-4 z-50 space-y-2"></div>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8 max-w-6xl">
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer class="footer footer-center p-8 bg-base-200 text-base-content mt-16">
<div>
<p class="font-bold text-lg" data-i18n="footer.title">HF Space Deployer</p>
<p class="text-sm opacity-70" data-i18n="footer.desc">Quick deployment tool</p>
</div>
<div class="flex items-center space-x-4 text-sm">
<a href="https://github.com/kfcx/HFSpaceDeploy.git" target="_blank" class="link link-hover">GitHub</a>
<span>•</span>
<a href="https://huggingface.co" target="_blank" class="link link-hover">HuggingFace</a>
</div>
</footer>
<script>
// Language icon update
function updateLanguageIcon() {
const langIcon = document.querySelector('.lang-icon');
langIcon.textContent = currentLang === 'en' ? 'EN' : '中';
}
// Override toggleLanguage to update icon
if (typeof window.originalToggleLanguage === 'undefined') {
window.originalToggleLanguage = toggleLanguage;
window.toggleLanguage = function() {
window.originalToggleLanguage();
updateLanguageIcon();
};
}
// Theme switcher - simplified for light/dark only
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
}
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
// Update theme icon
const themeIcon = document.querySelector('.theme-icon');
if (theme === 'dark') {
themeIcon.setAttribute('data-lucide', 'moon');
} else {
themeIcon.setAttribute('data-lucide', 'sun');
}
lucide.createIcons();
}
// Load saved theme (default to light)
const savedTheme = localStorage.getItem('theme') || 'light';
setTheme(savedTheme);
// Initialize Lucide icons
lucide.createIcons();
// HTMX event listeners
document.body.addEventListener('htmx:afterRequest', function(evt) {
// Reinitialize icons after HTMX requests
lucide.createIcons();
// Update translations for new content
updatePageTranslations();
});
// Copy to clipboard function
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
showToast(t('toast.copied'), 'success');
}).catch(function(err) {
showToast(t('toast.copyFailed'), 'error');
});
}
// Toast notification system
function showToast(message, type = 'info') {
const toastContainer = document.getElementById('toast-container');
const toast = document.createElement('div');
toast.className = `alert alert-${type} shadow-lg fade-in min-w-[300px]`;
const icons = {
'success': 'check-circle',
'error': 'x-circle',
'warning': 'alert-triangle',
'info': 'info'
};
toast.innerHTML = `
<i data-lucide="${icons[type]}" class="w-5 h-5"></i>
<span>${message}</span>
`;
toastContainer.appendChild(toast);
lucide.createIcons();
// Remove toast after 3 seconds
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(-10px)';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// HTMX extensions
document.body.addEventListener('htmx:responseError', function(evt) {
showToast(t('toast.requestFailed'), 'error');
});
// Global event listener for custom events
document.addEventListener('show-toast', function(event) {
const { message, type } = event.detail;
showToast(message, type);
});
// Initialize page functionality
document.addEventListener('DOMContentLoaded', function() {
// Initialize all icons
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
// Update language icon
updateLanguageIcon();
});
// Ensure Alpine.js is loaded before dependent components
if (typeof Alpine !== 'undefined') {
document.addEventListener('alpine:init', () => {
console.log('Alpine.js initialized');
});
}
</script>
</body>
</html> |