4sp-dv-latest 1.0.5 → 1.0.7

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.
@@ -6,71 +6,16 @@
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.48/logged-in/">
9
+ <base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.44/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
- <style>
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
14
 
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
- }
15
+ <script src="https://cdn.jsdelivr.net/npm/fireworks-js@2.x/dist/index.umd.js"></script>
41
16
 
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 {
17
+ <style>
18
+ body {
74
19
  background-color: #040404;
75
20
  color: #c0c0c0;
76
21
  font-family: 'Geist', sans-serif;
@@ -103,6 +48,27 @@
103
48
  z-index: 60;
104
49
  transition: background-color 0.3s ease, border-color 0.3s ease;
105
50
  position: relative;
51
+ overflow: visible;
52
+ }
53
+
54
+ /* --- FIREWORKS CONTAINER --- */
55
+ #fireworks-container {
56
+ position: absolute;
57
+ top: 0;
58
+ left: 0;
59
+ width: 100%;
60
+ height: 100%;
61
+ pointer-events: none; /* Let clicks pass through */
62
+ z-index: 1; /* Sit above background */
63
+ opacity: 0;
64
+ transition: opacity 0.5s ease;
65
+ overflow: hidden;
66
+ }
67
+
68
+ /* Ensure navbar content sits ABOVE the fireworks */
69
+ #navbar-container > *:not(#fireworks-container) {
70
+ position: relative;
71
+ z-index: 10;
106
72
  }
107
73
 
108
74
  /* --- FIXED OVERLAY LOGIC --- */
@@ -229,15 +195,15 @@
229
195
  z-index: 50;
230
196
  }
231
197
 
232
- /* --- DISABLE ANIMATIONS FOR AUTH BTN --- */
233
198
  #auth-btn, #auth-btn:hover, #auth-btn:active {
234
199
  transform: none !important;
235
200
  transition: none !important;
236
201
  background-color: transparent !important;
237
- z-index: auto !important;
202
+ z-index: 65 !important;
203
+ cursor: pointer !important;
238
204
  }
239
205
 
240
- /* Auth Menu & Settings */
206
+ /* --- Auth Menu & Settings (Updated for dynamic theming) --- */
241
207
  .auth-menu {
242
208
  position: absolute;
243
209
  right: 0;
@@ -250,7 +216,7 @@
250
216
  display: none;
251
217
  flex-direction: column;
252
218
  gap: 0.5rem;
253
- z-index: 50;
219
+ z-index: 70;
254
220
  box-shadow: 0 10px 30px rgba(0,0,0,0.6);
255
221
  transform-origin: top right;
256
222
  }
@@ -271,9 +237,10 @@
271
237
  align-items: center;
272
238
  gap: 0.75rem;
273
239
  padding: 0.75rem 1rem;
274
- color: var(--menu-text, #d1d5db);
275
- background: #0a0a0a;
276
- border: 1px solid #333;
240
+ /* UPDATED: Uses variables for dynamic theme support */
241
+ color: var(--menu-item-text, #d1d5db);
242
+ background: var(--menu-item-bg, #0a0a0a);
243
+ border: 1px solid var(--menu-item-border, #333);
277
244
  border-radius: 1rem;
278
245
  text-decoration: none;
279
246
  font-size: 0.9rem;
@@ -283,9 +250,10 @@
283
250
  }
284
251
 
285
252
  .auth-menu-item:hover {
286
- background-color: #000;
287
- border-color: #fff;
288
- color: #fff;
253
+ /* UPDATED: Uses variables for dynamic theme support */
254
+ background-color: var(--menu-item-hover-bg, #000);
255
+ border-color: var(--menu-item-hover-border, #fff);
256
+ color: var(--menu-item-hover-text, #fff);
289
257
  transform: translateY(-2px) scale(1.02);
290
258
  box-shadow: 0 5px 15px rgba(255,255,255,0.05);
291
259
  }
@@ -294,21 +262,6 @@
294
262
  transform: translateY(0) scale(0.98);
295
263
  }
296
264
 
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;
308
- }
309
-
310
-
311
-
312
265
  .auth-header {
313
266
  padding: 0.5rem 1rem;
314
267
  border-bottom: 1px solid var(--menu-divider, #333);
@@ -378,13 +331,13 @@
378
331
  }
379
332
 
380
333
  .settings-box {
381
- border: 1px solid #333; border-radius: 1.5rem;
334
+ border: 1px solid #333; border-radius: 1rem;
382
335
  background-color: #000000; padding: 1.5rem; margin-bottom: 1.5rem;
383
336
  }
384
337
 
385
338
  .input-text-style, .input-select-style {
386
339
  width: 100%; padding: 0.75rem; border: 1px solid #252525;
387
- background-color: #111111; border-radius: 1rem; color: #c0c0c0; transition: all 0.2s;
340
+ background-color: #111111; border-radius: 0.5rem; color: #c0c0c0; transition: all 0.2s;
388
341
  }
389
342
  .input-text-style:focus, .input-select-style:focus {
390
343
  border-color: #505050; outline: none; box-shadow: 0 0 0 2px rgba(80, 80, 80, 0.5);
@@ -392,24 +345,13 @@
392
345
 
393
346
  .input-key-style {
394
347
  width: 4rem; padding: 0.75rem; border: 1px solid #252525;
395
- background-color: #111111; border-radius: 1rem; color: #c0c0c0;
348
+ background-color: #111111; border-radius: 0.5rem; color: #c0c0c0;
396
349
  transition: all 0.2s; font-size: 1.1rem; text-align: center;
397
350
  font-weight: 500; text-transform: uppercase; caret-color: transparent;
398
351
  }
399
352
  .input-key-style:focus {
400
353
  border-color: #505050; outline: none; box-shadow: 0 0 0 2px rgba(80, 80, 80, 0.5);
401
354
  }
402
-
403
- /* Preference Toggles */
404
- .icon-btn.off {
405
- border-color: #ef4444;
406
- color: #ef4444;
407
- background-color: rgba(239, 68, 68, 0.05);
408
- }
409
- .icon-btn.off:hover {
410
- background-color: rgba(239, 68, 68, 0.1);
411
- border-color: #f87171;
412
- }
413
355
  .input-key-style::placeholder { font-size: 1rem; text-transform: none; }
414
356
 
415
357
  .general-message-area { min-height: 20px; margin-top: 1rem; font-weight: 400; }
@@ -464,21 +406,8 @@
464
406
  }
465
407
  .shake-anim { animation: shake 0.4s cubic-bezier(.36,.07,.19,.97) both; }
466
408
 
467
- /* Animation Control */
468
- body.no-animations *,
469
- body.no-animations *:hover,
470
- body.no-animations *:active,
471
- body.no-animations *:focus {
472
- transition: none !important;
473
- animation: none !important;
474
- transform: none !important;
475
- box-shadow: none !important;
476
- }
477
-
478
- body.no-animations .auth-menu-item .fa-gear,
479
- body.no-animations .nav-tab .fa-gear {
480
- transition: none !important;
481
- transform: none !important;
409
+ body.no-animations * {
410
+ transition: none !important; animation: none !important; transform: none !important;
482
411
  }
483
412
 
484
413
  /* Bubbly Spring Transition */
@@ -615,17 +544,6 @@
615
544
  </style>
616
545
  </head>
617
546
  <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
-
629
547
  <div id="lock-screen" class="w-full h-full flex flex-col items-center justify-center p-4">
630
548
  <div class="max-w-5xl w-full bg-[#040404] flex flex-col md:flex-row border border-[#252525] rounded-3xl overflow-hidden shadow-2xl">
631
549
  <div class="flex-1 p-8 flex flex-col justify-center items-center border-b md:border-b-0 md:border-r border-[#252525]">
@@ -656,6 +574,8 @@
656
574
  </div>
657
575
 
658
576
  <div id="navbar-container">
577
+ <div id="fireworks-container"></div>
578
+
659
579
  <img src="https://cdn.jsdelivr.net/npm/4sp-asset-library@latest/logo.png" id="navbar-logo" class="navbar-logo" alt="Logo">
660
580
 
661
581
  <div class="tab-wrapper">
@@ -712,29 +632,13 @@
712
632
  <h3 class="text-3xl font-bold text-white mb-6">General Settings</h3>
713
633
 
714
634
  <div class="settings-box">
715
- <h3 class="text-xl font-bold text-white mb-2">Account Details & Preferences</h3>
716
-
635
+ <h3 class="text-xl font-bold text-white mb-2">Account Username</h3>
717
636
  <label class="block text-gray-400 text-sm mb-2 font-light">New Username</label>
718
- <div class="flex gap-2 mb-6">
637
+ <div class="flex gap-2">
719
638
  <input type="text" id="settings-username-input" class="input-text-style" placeholder="Enter username">
720
639
  <button id="save-username-btn" class="btn-toolbar-style btn-primary-override">Save</button>
721
640
  </div>
722
641
  <p id="username-msg" class="general-message-area text-sm mt-2"></p>
723
-
724
- <div class="flex gap-8 items-center pt-4 border-t border-[#1a1a1a]">
725
- <div class="flex flex-col items-center gap-2">
726
- <button id="toggle-animations-btn" class="icon-btn" title="Toggle Navbar Animations">
727
- <i class="fa-solid fa-wand-magic-sparkles"></i>
728
- </button>
729
- <span class="text-xs text-gray-500">Animations</span>
730
- </div>
731
- <div class="flex flex-col items-center gap-2">
732
- <button id="toggle-sounds-btn" class="icon-btn" title="Toggle UI Sounds">
733
- <i class="fa-solid fa-volume-high"></i>
734
- </button>
735
- <span class="text-xs text-gray-500">Sounds</span>
736
- </div>
737
- </div>
738
642
  </div>
739
643
 
740
644
  <h3 class="text-xl font-bold text-white mb-2 mt-6">Organization</h3>
@@ -787,8 +691,7 @@
787
691
 
788
692
  <label class="block text-gray-400 text-xs mb-2 font-light">Background Color</label>
789
693
  <div class="flex flex-wrap gap-2 mb-6" id="pfp-color-grid">
790
- <!-- Colors generated by JS -->
791
- </div>
694
+ </div>
792
695
 
793
696
  <button id="save-letter-pfp-btn" class="btn-toolbar-style btn-primary-override w-full justify-center">Set Letter Avatar</button>
794
697
  </div>
@@ -809,8 +712,7 @@
809
712
 
810
713
  <h3 class="text-xl font-bold text-white mb-2">Navigation Bar Theme</h3>
811
714
  <div class="settings-box p-4">
812
- <div id="theme-picker-container">
813
- </div>
715
+ <div id="theme-picker-container"></div>
814
716
  </div>
815
717
  </div>
816
718
 
@@ -949,7 +851,7 @@
949
851
  const displayUsername = document.getElementById('display-username');
950
852
  const displayEmail = document.getElementById('display-email');
951
853
 
952
- const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.48/logged-in/';
854
+ const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.44/logged-in/';
953
855
 
954
856
  // Preload Logos
955
857
  const preloadImgs = [
@@ -963,8 +865,10 @@
963
865
  const USER_DATA_KEY = 'local_user_data';
964
866
  const LAST_PAGE_KEY = 'local_last_page';
965
867
  const OWNER_EMAIL = "4simpleproblems@gmail.com"; // For admin checks
868
+ const FIREWORKS_SETTINGS_KEY = 'fireworks_settings';
966
869
 
967
- const PAGE_DATA = {
870
+ // RENAMED TO AVOID CONFLICTS
871
+ const CLIENT_PAGE_DATA = {
968
872
  "dashboard": { "name": "Dashboard", "icon": "fa-solid fa-house-user", "url": "dashboard.html" },
969
873
  "soundboard": { "name": "Soundboard", "icon": "fa-solid fa-volume-up", "url": "soundboard.html" },
970
874
  "notes": { "name": "Notes", "icon": "fa-solid fa-sticky-note", "url": "notes.html" },
@@ -980,6 +884,27 @@
980
884
  let currentUser = null;
981
885
  let userDataUnsubscribe = null;
982
886
 
887
+ // --- FIREWORKS ENGINE INITIALIZATION ---
888
+ const fwContainer = document.getElementById('fireworks-container');
889
+
890
+ // HARDCODED SETTINGS AS REQUESTED
891
+ const fireworks = new Fireworks.default(fwContainer, {
892
+ autoresize: true,
893
+ opacity: 1.0, // Hardcoded
894
+ acceleration: 1.05,
895
+ friction: 0.97,
896
+ gravity: 1.5,
897
+ particles: 50, // Hardcoded
898
+ traceLength: 3,
899
+ traceSpeed: 10,
900
+ explosion: 5,
901
+ intensity: 5, // Hardcoded
902
+ flickering: 50,
903
+ lineStyle: 'round',
904
+ rocketsPoint: { min: 50, max: 50 }
905
+ });
906
+
907
+
983
908
  // --- ADMIN KEYBINDS LOGIC (Updated to Match admin_keybinds.js exactly) ---
984
909
  function initAdminKeybinds(db, auth, user) {
985
910
  console.log("[Admin Keybinds] Initializing for", user.uid);
@@ -1240,7 +1165,7 @@
1240
1165
 
1241
1166
  let currentPath = activePageUrl.replace(BASE_URL, '');
1242
1167
 
1243
- Object.entries(PAGE_DATA).forEach(([key, page]) => {
1168
+ Object.entries(CLIENT_PAGE_DATA).forEach(([key, page]) => {
1244
1169
  const isAuth = page.url === currentPath;
1245
1170
  const tab = document.createElement('a');
1246
1171
  tab.className = `nav-tab ${isAuth ? 'active' : ''}`;
@@ -1316,7 +1241,7 @@
1316
1241
  }, 50);
1317
1242
  }
1318
1243
 
1319
- // Auth Menu
1244
+ // Auth Menu (Wait for DOMContentLoaded equivalent in module)
1320
1245
  if (authBtn) {
1321
1246
  authBtn.addEventListener('click', (e) => {
1322
1247
  e.stopPropagation();
@@ -1439,9 +1364,6 @@
1439
1364
  }
1440
1365
 
1441
1366
  authBtn.innerHTML = pfpHtml;
1442
- authBtn.style.padding = '0'; // Remove padding to let avatar fill
1443
- authBtn.style.overflow = 'hidden'; // Ensure roundness
1444
- authBtn.style.border = '1px solid #4b5563'; // Maintain border
1445
1367
  }
1446
1368
 
1447
1369
  // --- Auto-Login Logic ---
@@ -1452,19 +1374,9 @@
1452
1374
  unlockBtn.classList.add('hidden');
1453
1375
  codeInput.classList.add('hidden');
1454
1376
 
1455
- // Timeout Race to prevent infinite "Verifying..."
1456
- const timeoutPromise = new Promise((_, reject) =>
1457
- setTimeout(() => reject(new Error("Verification timed out.")), 10000)
1458
- );
1459
-
1460
1377
  try {
1461
1378
  const docRef = doc(db, "access_codes", savedCode);
1462
-
1463
- // Race the Firestore connection
1464
- const docSnap = await Promise.race([
1465
- getDoc(docRef),
1466
- timeoutPromise
1467
- ]);
1379
+ const docSnap = await getDoc(docRef);
1468
1380
 
1469
1381
  if (docSnap.exists() && docSnap.data().active) {
1470
1382
  const codeData = docSnap.data();
@@ -1480,12 +1392,9 @@
1480
1392
  currentUser = userData;
1481
1393
  }
1482
1394
 
1483
- // Always fetch fresh to re-verify admin status (also raced)
1395
+ // Always fetch fresh to re-verify admin status
1484
1396
  if (codeData.ownerUid) {
1485
- await Promise.race([
1486
- fetchUserData(codeData.ownerUid),
1487
- timeoutPromise
1488
- ]);
1397
+ await fetchUserData(codeData.ownerUid);
1489
1398
  }
1490
1399
 
1491
1400
  launchApp();
@@ -1496,7 +1405,7 @@
1496
1405
  }
1497
1406
  } catch(e) {
1498
1407
  console.error("Auto-login error:", e);
1499
- resetLockScreen("Connection issue. Please login again.");
1408
+ resetLockScreen("Connection failed. Retrying...");
1500
1409
  }
1501
1410
  }
1502
1411
  }
@@ -1577,7 +1486,7 @@
1577
1486
 
1578
1487
  // Priority: Last Page > > Default
1579
1488
  if (lastPage) {
1580
- const isValid = Object.values(PAGE_DATA).some(p => p.url === lastPage);
1489
+ const isValid = Object.values(CLIENT_PAGE_DATA).some(p => p.url === lastPage);
1581
1490
  if (isValid) startPage = lastPage;
1582
1491
  }
1583
1492
 
@@ -1633,7 +1542,7 @@
1633
1542
  if (loader && loaderTitle && loaderBar) {
1634
1543
  // Determine Title
1635
1544
  let pageTitle = "Loading...";
1636
- const pageEntry = Object.values(PAGE_DATA).find(p => p.url === cleanPageName || p.url.includes(lookupName));
1545
+ const pageEntry = Object.values(CLIENT_PAGE_DATA).find(p => p.url === cleanPageName || p.url.includes(lookupName));
1637
1546
  if (pageEntry) pageTitle = pageEntry.name;
1638
1547
 
1639
1548
  loaderTitle.textContent = pageTitle;
@@ -2238,8 +2147,42 @@
2238
2147
  if (!res.ok) return;
2239
2148
  let themes = await res.json();
2240
2149
 
2150
+ // --- INJECT CUSTOM "THE NEW YEAR" THEME & REMOVE ROYAL ---
2151
+ // 1. Filter out Royal
2152
+ themes = themes.filter(t => t.name !== 'Royal');
2153
+
2154
+ // 2. Inject The New Year (Deep Blue)
2155
+ const newYearTheme = {
2156
+ "name": "The New Year",
2157
+ "navbar-bg": "#020617", // Very deep blue (Slate-950)
2158
+ "navbar-border": "#1e293b",
2159
+ "navbar-text": "#e2e8f0",
2160
+ "tab-text": "#94a3b8",
2161
+ "tab-hover-text": "#ffffff",
2162
+ "tab-hover-bg": "rgba(255, 255, 255, 0.1)",
2163
+ "tab-active-text": "#fcd34d", // Gold accent for festive feel
2164
+ "tab-active-bg": "rgba(252, 211, 77, 0.15)",
2165
+ "tab-active-border": "#fcd34d",
2166
+ "accent-color": "#fcd34d",
2167
+ "logo-src": "https://cdn.jsdelivr.net/npm/4sp-asset-library@latest/logo.png",
2168
+
2169
+ // --- UPDATED: AUTH MENU SPECIFIC THEME COLORS ---
2170
+ "menu-bg": "#020617",
2171
+ "menu-border": "#fcd34d", // Gold border for menu
2172
+ "menu-divider": "#1e293b",
2173
+ "menu-username-text": "#fcd34d", // Gold Username
2174
+ "menu-email-text": "#94a3b8",
2175
+ "menu-item-bg": "#0f172a", // Dark Slate for items
2176
+ "menu-item-text": "#cbd5e1",
2177
+ "menu-item-hover-bg": "rgba(252, 211, 77, 0.1)", // Gold tint on hover
2178
+ "menu-item-hover-text": "#fcd34d"
2179
+ };
2180
+
2181
+ themes.push(newYearTheme);
2182
+ // --------------------------------------------------------
2183
+
2241
2184
  // --- Sorting Logic from settings.js ---
2242
- const orderedThemeNames = ['Dark', 'Light', 'Christmas'];
2185
+ const orderedThemeNames = ['Dark', 'Light', 'Christmas', 'The New Year']; // Prioritize New Year
2243
2186
  const sortedThemes = [];
2244
2187
 
2245
2188
  orderedThemeNames.forEach(name => {
@@ -2304,22 +2247,33 @@
2304
2247
  function applyTheme(theme) {
2305
2248
  const root = document.documentElement;
2306
2249
 
2250
+ // --- FIREWORKS LOGIC ---
2251
+ if (theme.name === 'The New Year') {
2252
+ document.getElementById('fireworks-container').style.opacity = '1';
2253
+ fireworks.start();
2254
+ } else {
2255
+ document.getElementById('fireworks-container').style.opacity = '0';
2256
+ fireworks.stop();
2257
+ }
2258
+ // -----------------------
2259
+
2307
2260
  // --- Logo Path Modification ---
2308
2261
  let logoSrc;
2309
2262
  const isLightTheme = lightThemeNames.includes(theme.name);
2310
2263
 
2311
2264
  if (isLightTheme) {
2312
2265
  logoSrc = 'https://cdn.jsdelivr.net/npm/4sp-dv@latest/images/logo-dark.png';
2313
- root.style.setProperty('--menu-username-text', '#000000');
2314
- root.style.setProperty('--menu-email-text', '#444444');
2266
+ // Default light theme variables if not specified in theme
2267
+ if(!theme['menu-username-text']) root.style.setProperty('--menu-username-text', '#000000');
2268
+ if(!theme['menu-email-text']) root.style.setProperty('--menu-email-text', '#444444');
2315
2269
  } else if (theme.name === 'Christmas') {
2316
2270
  logoSrc = 'https://cdn.jsdelivr.net/npm/4sp-dv@latest/images/logo-christmas.png';
2317
- root.style.setProperty('--menu-username-text', 'white');
2318
- root.style.setProperty('--menu-email-text', '#9ca3af');
2271
+ if(!theme['menu-username-text']) root.style.setProperty('--menu-username-text', 'white');
2272
+ if(!theme['menu-email-text']) root.style.setProperty('--menu-email-text', '#9ca3af');
2319
2273
  } else {
2320
2274
  logoSrc = 'https://cdn.jsdelivr.net/npm/4sp-asset-library@latest/logo.png';
2321
- root.style.setProperty('--menu-username-text', 'white');
2322
- root.style.setProperty('--menu-email-text', '#9ca3af');
2275
+ if(!theme['menu-username-text']) root.style.setProperty('--menu-username-text', 'white');
2276
+ if(!theme['menu-email-text']) root.style.setProperty('--menu-email-text', '#9ca3af');
2323
2277
  }
2324
2278
 
2325
2279
  // Apply CSS Variables from theme object (overrides defaults if present)
@@ -2368,7 +2322,8 @@
2368
2322
  localStorage.setItem('user-navbar-theme', JSON.stringify(theme));
2369
2323
 
2370
2324
  // Apply Glide Button Color Sync
2371
- root.style.setProperty('--glide-btn-color', 'var(--tab-text)');
2325
+ const glideColor = theme['tab-active-text'] || theme['accent-color'] || '#ffffff';
2326
+ root.style.setProperty('--glide-btn-color', glideColor);
2372
2327
 
2373
2328
  // Also update user doc if logged in
2374
2329
  if (currentUser) {
@@ -2632,41 +2587,6 @@
2632
2587
 
2633
2588
  loadThemes();
2634
2589
  loadPanicKeys();
2635
-
2636
- // --- App Preference Toggles Init ---
2637
- const animBtn = document.getElementById('toggle-animations-btn');
2638
- const soundBtn = document.getElementById('toggle-sounds-btn');
2639
-
2640
- const updateToggleUI = () => {
2641
- const isEnabled = window.areAnimationsEnabled;
2642
- animBtn.classList.toggle('off', !isEnabled);
2643
- animBtn.innerHTML = isEnabled ? '<i class="fa-solid fa-wand-magic-sparkles"></i>' : '<i class="fa-solid fa-wand-magic"></i>';
2644
-
2645
- // Apply to body
2646
- if (isEnabled) document.body.classList.remove('no-animations');
2647
- else document.body.classList.add('no-animations');
2648
-
2649
- soundBtn.classList.toggle('off', window.isMuted);
2650
- soundBtn.innerHTML = window.isMuted ? '<i class="fa-solid fa-volume-xmark"></i>' : '<i class="fa-solid fa-volume-high"></i>';
2651
- };
2652
-
2653
- if (animBtn) {
2654
- animBtn.onclick = () => {
2655
- window.areAnimationsEnabled = !window.areAnimationsEnabled;
2656
- localStorage.setItem('app_animations_enabled', window.areAnimationsEnabled);
2657
- updateToggleUI();
2658
- };
2659
- }
2660
-
2661
- if (soundBtn) {
2662
- soundBtn.onclick = () => {
2663
- window.isMuted = !window.isMuted;
2664
- localStorage.setItem('app_is_muted', window.isMuted);
2665
- updateToggleUI();
2666
- };
2667
- }
2668
-
2669
- updateToggleUI();
2670
2590
  }
2671
2591
 
2672
2592
  checkAutoLogin();
@@ -2678,26 +2598,14 @@
2678
2598
  <script>
2679
2599
  // --- Sound & Notification Logic ---
2680
2600
  const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
2681
-
2682
- // Sync with global state
2683
- Object.defineProperty(window, 'isMuted', {
2684
- get: () => localStorage.getItem('app_is_muted') === 'true',
2685
- set: (v) => localStorage.setItem('app_is_muted', v)
2686
- });
2687
-
2688
- Object.defineProperty(window, 'areAnimationsEnabled', {
2689
- get: () => localStorage.getItem('app_animations_enabled') !== 'false',
2690
- set: (v) => localStorage.setItem('app_animations_enabled', v)
2691
- });
2692
-
2693
- // Initial Class Set
2694
- if (!window.areAnimationsEnabled) document.body.classList.add('no-animations');
2601
+ let isMuted = false;
2602
+ let areAnimationsEnabled = true;
2695
2603
 
2696
2604
  // EXPOSE PlayTypeSound GLOBALLY
2697
2605
  // Debounce to prevent rapid-fire stuttering if called too fast
2698
2606
  let typeSoundTimeout = null;
2699
2607
  window.playTypeSound = function() {
2700
- if (window.isMuted) return;
2608
+ if (isMuted) return;
2701
2609
  if (audioCtx.state === 'suspended') audioCtx.resume();
2702
2610
 
2703
2611
  // Small debounce
@@ -2738,7 +2646,7 @@
2738
2646
  };
2739
2647
 
2740
2648
  function playClickSound() {
2741
- if (window.isMuted) return;
2649
+ if (isMuted) return;
2742
2650
  if (audioCtx.state === 'suspended') audioCtx.resume();
2743
2651
 
2744
2652
  const osc = audioCtx.createOscillator();
@@ -2753,12 +2661,8 @@
2753
2661
  osc.stop(audioCtx.currentTime + 0.015);
2754
2662
  }
2755
2663
 
2756
- // --- App Preferences State ---
2757
- window.isMuted = localStorage.getItem('app_is_muted') === 'true';
2758
- window.areAnimationsEnabled = localStorage.getItem('app_animations_enabled') !== 'false'; // Default true
2759
-
2760
2664
  window.playMechClick = function(isReverse = false) {
2761
- if (window.isMuted || !window.areAnimationsEnabled) return;
2665
+ if (isMuted) return;
2762
2666
  if (audioCtx.state === 'suspended') audioCtx.resume();
2763
2667
  const now = audioCtx.currentTime;
2764
2668
 
@@ -2799,7 +2703,7 @@
2799
2703
  osc.stop(now + 0.02);
2800
2704
  };
2801
2705
 
2802
-
2706
+ // Removed Gear Animation Logic
2803
2707
 
2804
2708
  const notificationContainer = document.getElementById('notification-container');
2805
2709
  function showNotification(message, iconClass = 'fa-solid fa-info-circle', type = 'info') {
@@ -2839,27 +2743,6 @@
2839
2743
  document.addEventListener('paste', (e) => {
2840
2744
  if (e.target.closest('input, textarea')) showNotification('Pasted from clipboard', 'fa-solid fa-paste', 'info');
2841
2745
  });
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
- });
2863
2746
  </script>
2864
2747
  </body>
2865
2748
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "4sp-dv-latest",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "The latest HTML file of the 4SP Version 5 Client.",
5
5
  "license": "ISC",
6
6
  "author": "",