4sp-dv-latest 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/4simpleproblems_v5_latest.html +123 -65
- package/package.json +1 -1
|
@@ -6,13 +6,71 @@
|
|
|
6
6
|
<title>4SP - VERSION 5 CLIENT</title>
|
|
7
7
|
<link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/npm/4sp-dv@latest/images/logo.png">
|
|
8
8
|
|
|
9
|
-
<base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.
|
|
9
|
+
<base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.48/logged-in/">
|
|
10
10
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap" rel="stylesheet">
|
|
12
12
|
|
|
13
13
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
|
|
14
14
|
<style>
|
|
15
|
-
|
|
15
|
+
/* --- PRELOADER --- */
|
|
16
|
+
#app-preloader {
|
|
17
|
+
position: fixed;
|
|
18
|
+
inset: 0;
|
|
19
|
+
background-color: #000000;
|
|
20
|
+
z-index: 99999;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
align-items: flex-end;
|
|
24
|
+
justify-content: flex-end;
|
|
25
|
+
padding: 3rem;
|
|
26
|
+
transition: opacity 0.5s ease-out, visibility 0.5s;
|
|
27
|
+
}
|
|
28
|
+
#app-preloader.fade-out {
|
|
29
|
+
opacity: 0;
|
|
30
|
+
visibility: hidden;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.preloader-logo {
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 1.5rem;
|
|
36
|
+
right: 1.5rem;
|
|
37
|
+
height: 3rem;
|
|
38
|
+
width: auto;
|
|
39
|
+
opacity: 0.5; /* Initial opacity per universal style, not darkened */
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.preloader-content {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
align-items: flex-end;
|
|
46
|
+
gap: 0.5rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.preloader-title {
|
|
50
|
+
color: #fff;
|
|
51
|
+
font-size: 1.875rem; /* 3xl */
|
|
52
|
+
font-weight: 300;
|
|
53
|
+
font-style: italic;
|
|
54
|
+
font-family: 'Geist', sans-serif;
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.preloader-bar-container {
|
|
59
|
+
width: 16rem; /* 64 */
|
|
60
|
+
height: 0.25rem; /* 1 */
|
|
61
|
+
background-color: #333;
|
|
62
|
+
border-radius: 9999px;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.preloader-bar {
|
|
67
|
+
height: 100%;
|
|
68
|
+
background-color: #fff;
|
|
69
|
+
width: 0%;
|
|
70
|
+
transition: width 0.5s ease-out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
body {
|
|
16
74
|
background-color: #040404;
|
|
17
75
|
color: #c0c0c0;
|
|
18
76
|
font-family: 'Geist', sans-serif;
|
|
@@ -236,13 +294,21 @@
|
|
|
236
294
|
transform: translateY(0) scale(0.98);
|
|
237
295
|
}
|
|
238
296
|
|
|
239
|
-
.auth-menu-item
|
|
240
|
-
display: inline-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
297
|
+
.auth-menu-item i, .nav-tab i, .settings-tab i {
|
|
298
|
+
display: inline-flex;
|
|
299
|
+
align-items: center;
|
|
300
|
+
justify-content: center;
|
|
301
|
+
width: 1.1em;
|
|
302
|
+
height: 1.1em;
|
|
303
|
+
line-height: 1;
|
|
304
|
+
position: relative;
|
|
305
|
+
top: 1px;
|
|
306
|
+
flex-shrink: 0;
|
|
307
|
+
margin: 0;
|
|
244
308
|
}
|
|
245
309
|
|
|
310
|
+
|
|
311
|
+
|
|
246
312
|
.auth-header {
|
|
247
313
|
padding: 0.5rem 1rem;
|
|
248
314
|
border-bottom: 1px solid var(--menu-divider, #333);
|
|
@@ -549,6 +615,17 @@
|
|
|
549
615
|
</style>
|
|
550
616
|
</head>
|
|
551
617
|
<body>
|
|
618
|
+
<!-- PRELOADER -->
|
|
619
|
+
<div id="app-preloader">
|
|
620
|
+
<img src="https://cdn.jsdelivr.net/npm/4sp-asset-library@latest/logo.png" class="preloader-logo" alt="Logo" onerror="this.style.display='none'">
|
|
621
|
+
<div class="preloader-content">
|
|
622
|
+
<h2 class="preloader-title">4simpleproblems Version 5 Client</h2>
|
|
623
|
+
<div class="preloader-bar-container">
|
|
624
|
+
<div id="preloader-bar" class="preloader-bar"></div>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
628
|
+
|
|
552
629
|
<div id="lock-screen" class="w-full h-full flex flex-col items-center justify-center p-4">
|
|
553
630
|
<div class="max-w-5xl w-full bg-[#040404] flex flex-col md:flex-row border border-[#252525] rounded-3xl overflow-hidden shadow-2xl">
|
|
554
631
|
<div class="flex-1 p-8 flex flex-col justify-center items-center border-b md:border-b-0 md:border-r border-[#252525]">
|
|
@@ -872,7 +949,7 @@
|
|
|
872
949
|
const displayUsername = document.getElementById('display-username');
|
|
873
950
|
const displayEmail = document.getElementById('display-email');
|
|
874
951
|
|
|
875
|
-
const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.
|
|
952
|
+
const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.48/logged-in/';
|
|
876
953
|
|
|
877
954
|
// Preload Logos
|
|
878
955
|
const preloadImgs = [
|
|
@@ -1375,9 +1452,19 @@
|
|
|
1375
1452
|
unlockBtn.classList.add('hidden');
|
|
1376
1453
|
codeInput.classList.add('hidden');
|
|
1377
1454
|
|
|
1455
|
+
// Timeout Race to prevent infinite "Verifying..."
|
|
1456
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
1457
|
+
setTimeout(() => reject(new Error("Verification timed out.")), 10000)
|
|
1458
|
+
);
|
|
1459
|
+
|
|
1378
1460
|
try {
|
|
1379
1461
|
const docRef = doc(db, "access_codes", savedCode);
|
|
1380
|
-
|
|
1462
|
+
|
|
1463
|
+
// Race the Firestore connection
|
|
1464
|
+
const docSnap = await Promise.race([
|
|
1465
|
+
getDoc(docRef),
|
|
1466
|
+
timeoutPromise
|
|
1467
|
+
]);
|
|
1381
1468
|
|
|
1382
1469
|
if (docSnap.exists() && docSnap.data().active) {
|
|
1383
1470
|
const codeData = docSnap.data();
|
|
@@ -1393,9 +1480,12 @@
|
|
|
1393
1480
|
currentUser = userData;
|
|
1394
1481
|
}
|
|
1395
1482
|
|
|
1396
|
-
// Always fetch fresh to re-verify admin status
|
|
1483
|
+
// Always fetch fresh to re-verify admin status (also raced)
|
|
1397
1484
|
if (codeData.ownerUid) {
|
|
1398
|
-
await
|
|
1485
|
+
await Promise.race([
|
|
1486
|
+
fetchUserData(codeData.ownerUid),
|
|
1487
|
+
timeoutPromise
|
|
1488
|
+
]);
|
|
1399
1489
|
}
|
|
1400
1490
|
|
|
1401
1491
|
launchApp();
|
|
@@ -1406,7 +1496,7 @@
|
|
|
1406
1496
|
}
|
|
1407
1497
|
} catch(e) {
|
|
1408
1498
|
console.error("Auto-login error:", e);
|
|
1409
|
-
resetLockScreen("Connection
|
|
1499
|
+
resetLockScreen("Connection issue. Please login again.");
|
|
1410
1500
|
}
|
|
1411
1501
|
}
|
|
1412
1502
|
}
|
|
@@ -2709,60 +2799,7 @@
|
|
|
2709
2799
|
osc.stop(now + 0.02);
|
|
2710
2800
|
};
|
|
2711
2801
|
|
|
2712
|
-
// Gear Clicking Sequence
|
|
2713
|
-
let gearHoverStartTime = 0;
|
|
2714
|
-
let currentGearRotation = 0;
|
|
2715
|
-
let currentAnimatingGear = null;
|
|
2716
|
-
const gearClickDelays = [0, 120, 240, 360]; // Adjusted for 0.5s total
|
|
2717
|
-
|
|
2718
|
-
const handleGearMouseOver = (e) => {
|
|
2719
|
-
const target = e.target.closest('#auth-settings-btn, #nav-settings-tab');
|
|
2720
|
-
if (target && currentAnimatingGear !== target) {
|
|
2721
|
-
currentAnimatingGear = target;
|
|
2722
|
-
const icon = target.querySelector('.fa-gear');
|
|
2723
|
-
if (!icon) return;
|
|
2724
|
-
|
|
2725
|
-
gearHoverStartTime = Date.now();
|
|
2726
|
-
gearClickDelays.forEach((delay, index) => {
|
|
2727
|
-
setTimeout(() => {
|
|
2728
|
-
if (target.matches(':hover')) {
|
|
2729
|
-
if (window.areAnimationsEnabled) {
|
|
2730
|
-
currentGearRotation = (index + 1) * 45;
|
|
2731
|
-
icon.style.transform = `rotate(${currentGearRotation}deg)`;
|
|
2732
|
-
}
|
|
2733
|
-
window.playMechClick(false);
|
|
2734
|
-
}
|
|
2735
|
-
}, delay);
|
|
2736
|
-
});
|
|
2737
|
-
}
|
|
2738
|
-
};
|
|
2739
2802
|
|
|
2740
|
-
const handleGearMouseOut = (e) => {
|
|
2741
|
-
const target = e.target.closest('#auth-settings-btn, #nav-settings-tab');
|
|
2742
|
-
if (target && !target.contains(e.relatedTarget)) {
|
|
2743
|
-
currentAnimatingGear = null;
|
|
2744
|
-
const icon = target.querySelector('.fa-gear');
|
|
2745
|
-
if (!icon) return;
|
|
2746
|
-
|
|
2747
|
-
const elapsed = Date.now() - gearHoverStartTime;
|
|
2748
|
-
const clickCount = gearClickDelays.filter(d => d <= elapsed).length;
|
|
2749
|
-
|
|
2750
|
-
for (let i = 0; i < clickCount; i++) {
|
|
2751
|
-
setTimeout(() => {
|
|
2752
|
-
if (!target.matches(':hover')) {
|
|
2753
|
-
if (window.areAnimationsEnabled) {
|
|
2754
|
-
currentGearRotation -= 45;
|
|
2755
|
-
icon.style.transform = `rotate(${currentGearRotation}deg)`;
|
|
2756
|
-
}
|
|
2757
|
-
window.playMechClick(true);
|
|
2758
|
-
}
|
|
2759
|
-
}, gearClickDelays[i]);
|
|
2760
|
-
}
|
|
2761
|
-
}
|
|
2762
|
-
};
|
|
2763
|
-
|
|
2764
|
-
document.addEventListener('mouseover', handleGearMouseOver);
|
|
2765
|
-
document.addEventListener('mouseout', handleGearMouseOut);
|
|
2766
2803
|
|
|
2767
2804
|
const notificationContainer = document.getElementById('notification-container');
|
|
2768
2805
|
function showNotification(message, iconClass = 'fa-solid fa-info-circle', type = 'info') {
|
|
@@ -2802,6 +2839,27 @@
|
|
|
2802
2839
|
document.addEventListener('paste', (e) => {
|
|
2803
2840
|
if (e.target.closest('input, textarea')) showNotification('Pasted from clipboard', 'fa-solid fa-paste', 'info');
|
|
2804
2841
|
});
|
|
2842
|
+
|
|
2843
|
+
// --- PRELOADER REMOVAL ---
|
|
2844
|
+
window.addEventListener('load', () => {
|
|
2845
|
+
const preloader = document.getElementById('app-preloader');
|
|
2846
|
+
const bar = document.getElementById('preloader-bar');
|
|
2847
|
+
|
|
2848
|
+
if (preloader && bar) {
|
|
2849
|
+
// Animate bar to 100%
|
|
2850
|
+
setTimeout(() => {
|
|
2851
|
+
bar.style.width = '100%';
|
|
2852
|
+
|
|
2853
|
+
// Then fade out
|
|
2854
|
+
setTimeout(() => {
|
|
2855
|
+
preloader.classList.add('fade-out');
|
|
2856
|
+
setTimeout(() => {
|
|
2857
|
+
preloader.remove();
|
|
2858
|
+
}, 500);
|
|
2859
|
+
}, 600); // Wait for bar animation
|
|
2860
|
+
}, 100);
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2805
2863
|
</script>
|
|
2806
2864
|
</body>
|
|
2807
2865
|
</html>
|