Compare commits
2 Commits
release-20
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87ddd3bb93 | ||
|
|
463249982c |
1
frontend/.gitignore
vendored
Normal file
1
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
||||||
@@ -1,101 +1,102 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta
|
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
|
|
||||||
/>
|
|
||||||
<title>VibeTorrent</title>
|
|
||||||
|
|
||||||
<!-- PWA & Mobile Capable -->
|
<head>
|
||||||
<meta name="mobile-web-app-capable" content="yes" />
|
<meta charset="utf-8" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="viewport"
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||||
<meta name="apple-mobile-web-app-title" content="VibeTorrent" />
|
<title>VibeTorrent</title>
|
||||||
<meta name="theme-color" content="#111827" />
|
|
||||||
<link rel="manifest" href="manifest.json" />
|
|
||||||
<link rel="icon" type="image/png" href="icon-192.png" />
|
|
||||||
<link rel="apple-touch-icon" href="icon-192.png" />
|
|
||||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
|
||||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
|
||||||
|
|
||||||
<!-- Trunk Assets -->
|
<!-- PWA & Mobile Capable -->
|
||||||
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="0" />
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
<link data-trunk rel="css" href="public/tailwind.css" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<link data-trunk rel="copy-file" href="manifest.json" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<link data-trunk rel="copy-file" href="icon-192.png" />
|
<meta name="apple-mobile-web-app-title" content="VibeTorrent" />
|
||||||
<link data-trunk rel="copy-file" href="icon-512.png" />
|
<meta name="theme-color" content="#111827" />
|
||||||
<link data-trunk rel="copy-file" href="sw.js" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<script>
|
<link rel="icon" type="image/png" href="icon-192.png" />
|
||||||
(function () {
|
<link rel="apple-touch-icon" href="icon-192.png" />
|
||||||
var localTheme = localStorage.getItem("vibetorrent_theme");
|
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
||||||
var t = localTheme || "dark";
|
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
||||||
if (t === "Amoled") t = "black";
|
|
||||||
if (t === "Light") t = "light";
|
|
||||||
if (t === "Dark" || t === "Midnight") t = "dark";
|
|
||||||
|
|
||||||
var theme = t.toLowerCase();
|
<!-- Trunk Assets -->
|
||||||
document.documentElement.setAttribute("data-theme", theme);
|
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="0" />
|
||||||
if (!localTheme) {
|
<link data-trunk rel="css" href="public/tailwind.css" />
|
||||||
localStorage.setItem("vibetorrent_theme", "dark");
|
<link data-trunk rel="copy-file" href="manifest.json" />
|
||||||
}
|
<link data-trunk rel="copy-file" href="icon-192.png" />
|
||||||
|
<link data-trunk rel="copy-file" href="icon-512.png" />
|
||||||
|
<link data-trunk rel="copy-file" href="sw.js" />
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var localTheme = localStorage.getItem("vibetorrent_theme");
|
||||||
|
var t = localTheme || "dark";
|
||||||
|
if (t === "Amoled") t = "black";
|
||||||
|
if (t === "Light") t = "light";
|
||||||
|
if (t === "Dark" || t === "Midnight") t = "dark";
|
||||||
|
|
||||||
var meta = document.querySelector('meta[name="theme-color"]');
|
var theme = t.toLowerCase();
|
||||||
if (meta) {
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
var colorMap = {
|
// Shadcn dark mode CSS değişkenleri .dark class ile çalışıyor
|
||||||
light: "#ffffff",
|
var darkThemes = ["dark", "black", "night", "coffee", "luxury", "business", "dracula", "halloween", "forest", "synthwave", "dim", "nord", "sunset", "cyberpunk", "abyss"];
|
||||||
cupcake: "#faf7f5",
|
if (darkThemes.indexOf(theme) !== -1) {
|
||||||
bumblebee: "#ffffff",
|
document.documentElement.classList.add("dark");
|
||||||
emerald: "#ffffff",
|
} else {
|
||||||
corporate: "#ffffff",
|
document.documentElement.classList.remove("dark");
|
||||||
synthwave: "#2d1b69",
|
}
|
||||||
retro: "#ece3ca",
|
if (!localTheme) {
|
||||||
cyberpunk: "#ffee00",
|
localStorage.setItem("vibetorrent_theme", "dark");
|
||||||
valentine: "#f0d6e8",
|
}
|
||||||
halloween: "#212121",
|
|
||||||
garden: "#e9e7e7",
|
|
||||||
forest: "#171212",
|
|
||||||
aqua: "#345da7",
|
|
||||||
lofi: "#ffffff",
|
|
||||||
pastel: "#ffffff",
|
|
||||||
fantasy: "#ffffff",
|
|
||||||
wireframe: "#ffffff",
|
|
||||||
black: "#000000",
|
|
||||||
luxury: "#09090b",
|
|
||||||
dracula: "#282a36",
|
|
||||||
cmyk: "#ffffff",
|
|
||||||
autumn: "#8C0327",
|
|
||||||
business: "#202020",
|
|
||||||
acid: "#fafafa",
|
|
||||||
lemonade: "#F1F8E8",
|
|
||||||
night: "#0f1729",
|
|
||||||
coffee: "#20161f",
|
|
||||||
winter: "#ffffff",
|
|
||||||
dark: "#1d232a",
|
|
||||||
};
|
|
||||||
var color = colorMap[theme] || "#1d232a";
|
|
||||||
meta.setAttribute("content", color);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body style="cursor: pointer;">
|
var meta = document.querySelector('meta[name="theme-color"]');
|
||||||
<div
|
if (meta) {
|
||||||
id="app-loading"
|
var colorMap = {
|
||||||
style="
|
light: "#ffffff",
|
||||||
|
cupcake: "#faf7f5",
|
||||||
|
bumblebee: "#ffffff",
|
||||||
|
emerald: "#ffffff",
|
||||||
|
corporate: "#ffffff",
|
||||||
|
synthwave: "#2d1b69",
|
||||||
|
retro: "#ece3ca",
|
||||||
|
cyberpunk: "#ffee00",
|
||||||
|
valentine: "#f0d6e8",
|
||||||
|
halloween: "#212121",
|
||||||
|
garden: "#e9e7e7",
|
||||||
|
forest: "#171212",
|
||||||
|
aqua: "#345da7",
|
||||||
|
lofi: "#ffffff",
|
||||||
|
pastel: "#ffffff",
|
||||||
|
fantasy: "#ffffff",
|
||||||
|
wireframe: "#ffffff",
|
||||||
|
black: "#000000",
|
||||||
|
luxury: "#09090b",
|
||||||
|
dracula: "#282a36",
|
||||||
|
cmyk: "#ffffff",
|
||||||
|
autumn: "#8C0327",
|
||||||
|
business: "#202020",
|
||||||
|
acid: "#fafafa",
|
||||||
|
lemonade: "#F1F8E8",
|
||||||
|
night: "#0f1729",
|
||||||
|
coffee: "#20161f",
|
||||||
|
winter: "#ffffff",
|
||||||
|
dark: "#1d232a",
|
||||||
|
};
|
||||||
|
var color = colorMap[theme] || "#1d232a";
|
||||||
|
meta.setAttribute("content", color);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="cursor: pointer;">
|
||||||
|
<div id="app-loading" style="
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
"
|
">
|
||||||
>
|
<div id="app-loading-spinner" style="
|
||||||
<div
|
|
||||||
id="app-loading-spinner"
|
|
||||||
style="
|
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border: 3px solid currentColor;
|
border: 3px solid currentColor;
|
||||||
@@ -103,21 +104,15 @@
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 0.8s linear infinite;
|
animation: spin 0.8s linear infinite;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
"
|
"></div>
|
||||||
></div>
|
<div id="app-loading-error" style="display: none; text-align: center; margin-top: 20px; padding: 0 20px">
|
||||||
<div
|
<p style="color: #ef4444; font-weight: bold; margin-bottom: 8px">
|
||||||
id="app-loading-error"
|
Uygulama yüklenemedi
|
||||||
style="display: none; text-align: center; margin-top: 20px; padding: 0 20px"
|
</p>
|
||||||
>
|
<p style="font-size: 14px; opacity: 0.7">
|
||||||
<p style="color: #ef4444; font-weight: bold; margin-bottom: 8px">
|
Bağlantınız yavaş olabilir veya bir sistem hatası oluşmuş olabilir.
|
||||||
Uygulama yüklenemedi
|
</p>
|
||||||
</p>
|
<button onclick="location.reload()" style="
|
||||||
<p style="font-size: 14px; opacity: 0.7">
|
|
||||||
Bağlantınız yavaş olabilir veya bir sistem hatası oluşmuş olabilir.
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
onclick="location.reload()"
|
|
||||||
style="
|
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
background: #3b82f6;
|
background: #3b82f6;
|
||||||
@@ -126,104 +121,105 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
"
|
">
|
||||||
>
|
Sayfayı Yenile
|
||||||
Sayfayı Yenile
|
</button>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<style>
|
</div>
|
||||||
@keyframes spin {
|
<style>
|
||||||
to {
|
@keyframes spin {
|
||||||
transform: rotate(360deg);
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.app-loaded #app-loading {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iOS Safari Click Fixes */
|
||||||
|
body {
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// App loading timeout handler
|
||||||
|
(function () {
|
||||||
|
var timeout = setTimeout(function () {
|
||||||
|
if (!document.body.classList.contains("app-loaded")) {
|
||||||
|
var spinner = document.getElementById("app-loading-spinner");
|
||||||
|
var error = document.getElementById("app-loading-error");
|
||||||
|
if (spinner) spinner.style.display = "none";
|
||||||
|
if (error) error.style.display = "block";
|
||||||
}
|
}
|
||||||
}
|
}, 15000); // 15 seconds timeout
|
||||||
|
|
||||||
body.app-loaded #app-loading {
|
// Clean up timeout if app loads
|
||||||
display: none !important;
|
var observer = new MutationObserver(function (mutations) {
|
||||||
}
|
mutations.forEach(function (mutation) {
|
||||||
|
if (
|
||||||
/* iOS Safari Click Fixes */
|
mutation.attributeName === "class" &&
|
||||||
body {
|
document.body.classList.contains("app-loaded")
|
||||||
cursor: pointer;
|
) {
|
||||||
-webkit-tap-highlight-color: transparent;
|
clearTimeout(timeout);
|
||||||
}
|
observer.disconnect();
|
||||||
|
|
||||||
summary {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// App loading timeout handler
|
|
||||||
(function () {
|
|
||||||
var timeout = setTimeout(function () {
|
|
||||||
if (!document.body.classList.contains("app-loaded")) {
|
|
||||||
var spinner = document.getElementById("app-loading-spinner");
|
|
||||||
var error = document.getElementById("app-loading-error");
|
|
||||||
if (spinner) spinner.style.display = "none";
|
|
||||||
if (error) error.style.display = "block";
|
|
||||||
}
|
|
||||||
}, 15000); // 15 seconds timeout
|
|
||||||
|
|
||||||
// Clean up timeout if app loads
|
|
||||||
var observer = new MutationObserver(function (mutations) {
|
|
||||||
mutations.forEach(function (mutation) {
|
|
||||||
if (
|
|
||||||
mutation.attributeName === "class" &&
|
|
||||||
document.body.classList.contains("app-loaded")
|
|
||||||
) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
observer.observe(document.body, { attributes: true });
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Service Worker Registration & PWA Setup -->
|
|
||||||
<script>
|
|
||||||
// Global Dropdown Closer for iOS/Mobile
|
|
||||||
document.addEventListener('click', function(event) {
|
|
||||||
const details = document.querySelectorAll('details[open]');
|
|
||||||
details.forEach(detail => {
|
|
||||||
// Eğer tıklanan yer bu details'in içinde değilse kapat
|
|
||||||
if (!detail.contains(event.target)) {
|
|
||||||
detail.removeAttribute('open');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, true); // Use capture phase for better mobile support
|
});
|
||||||
|
observer.observe(document.body, { attributes: true });
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
<!-- Service Worker Registration & PWA Setup -->
|
||||||
window.addEventListener("load", () => {
|
<script>
|
||||||
navigator.serviceWorker
|
// Global Dropdown Closer for iOS/Mobile
|
||||||
.register("/sw.js")
|
document.addEventListener('click', function (event) {
|
||||||
.then((registration) => {
|
const details = document.querySelectorAll('details[open]');
|
||||||
console.log("✅ Service Worker registered:", registration);
|
details.forEach(detail => {
|
||||||
|
// Eğer tıklanan yer bu details'in içinde değilse kapat
|
||||||
// Request notification permission after a delay (better UX)
|
if (!detail.contains(event.target)) {
|
||||||
setTimeout(() => {
|
detail.removeAttribute('open');
|
||||||
if ("Notification" in window && Notification.permission === "default") {
|
}
|
||||||
// Only request if user hasn't decided yet
|
});
|
||||||
const shouldRequest = localStorage.getItem("vibetorrent_notification_prompt_shown");
|
}, true); // Use capture phase for better mobile support
|
||||||
if (!shouldRequest) {
|
|
||||||
Notification.requestPermission().then((permission) => {
|
if ("serviceWorker" in navigator) {
|
||||||
console.log("Notification permission:", permission);
|
window.addEventListener("load", () => {
|
||||||
localStorage.setItem("vibetorrent_notification_prompt_shown", "true");
|
navigator.serviceWorker
|
||||||
});
|
.register("/sw.js")
|
||||||
}
|
.then((registration) => {
|
||||||
|
console.log("✅ Service Worker registered:", registration);
|
||||||
|
|
||||||
|
// Request notification permission after a delay (better UX)
|
||||||
|
setTimeout(() => {
|
||||||
|
if ("Notification" in window && Notification.permission === "default") {
|
||||||
|
// Only request if user hasn't decided yet
|
||||||
|
const shouldRequest = localStorage.getItem("vibetorrent_notification_prompt_shown");
|
||||||
|
if (!shouldRequest) {
|
||||||
|
Notification.requestPermission().then((permission) => {
|
||||||
|
console.log("Notification permission:", permission);
|
||||||
|
localStorage.setItem("vibetorrent_notification_prompt_shown", "true");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, 3000); // Wait 3 seconds before asking
|
}
|
||||||
})
|
}, 3000); // Wait 3 seconds before asking
|
||||||
.catch((error) => {
|
})
|
||||||
console.warn("⚠️ Service Worker registration failed:", error);
|
.catch((error) => {
|
||||||
});
|
console.warn("⚠️ Service Worker registration failed:", error);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
</script>
|
}
|
||||||
</body>
|
</script>
|
||||||
</html>
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
2853
frontend/package-lock.json
generated
2853
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,11 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/postcss": "^4.1.18",
|
||||||
"autoprefixer": "^10.4.23",
|
"autoprefixer": "^10.4.23",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
|
"postcss-cli": "^11.0.1",
|
||||||
|
"postcss-preset-env": "^11.1.3",
|
||||||
"tailwindcss": "^4.1.18"
|
"tailwindcss": "^4.1.18"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
15
frontend/postcss.config.cjs
Normal file
15
frontend/postcss.config.cjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
"postcss-preset-env": {
|
||||||
|
features: {
|
||||||
|
"nesting-rules": true,
|
||||||
|
},
|
||||||
|
browsers: [
|
||||||
|
"last 2 versions",
|
||||||
|
"iOS >= 15",
|
||||||
|
"Safari >= 15",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,7 @@ pub fn StatusBar() -> impl IntoView {
|
|||||||
if let Some(doc) = document().document_element() {
|
if let Some(doc) = document().document_element() {
|
||||||
let _ = doc.set_attribute("data-theme", &theme);
|
let _ = doc.set_attribute("data-theme", &theme);
|
||||||
// Also set class for Shadcn dark mode support
|
// Also set class for Shadcn dark mode support
|
||||||
if theme == "dark" || theme == "dracula" || theme == "dim" || theme == "abyss" {
|
if theme == "dark" || theme == "dracula" || theme == "dim" || theme == "abyss" || theme == "sunset" || theme == "cyberpunk" || theme == "nord" || theme == "business" || theme == "night" || theme == "black" || theme == "luxury" || theme == "coffee" || theme == "forest" || theme == "halloween" || theme == "synthwave" {
|
||||||
let _ = doc.class_list().add_1("dark");
|
let _ = doc.class_list().add_1("dark");
|
||||||
} else {
|
} else {
|
||||||
let _ = doc.class_list().remove_1("dark");
|
let _ = doc.class_list().remove_1("dark");
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const cargoRegistry = path.join(
|
|||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
darkMode: "class",
|
||||||
content: [
|
content: [
|
||||||
"./index.html",
|
"./index.html",
|
||||||
"./src/**/*.{rs,html}",
|
"./src/**/*.{rs,html}",
|
||||||
|
|||||||
Reference in New Issue
Block a user