4sp-dv-latest 1.0.0 → 1.0.2

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,7 +6,7 @@
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.15/logged-in/">
9
+ <base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.18/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
 
@@ -162,15 +162,14 @@
162
162
  }
163
163
  .icon-btn:hover { background-color: #374151; color: white; }
164
164
 
165
- .auth-menu, .pin-context-menu {
165
+ .auth-menu {
166
166
  position: absolute; right: 0; top: 50px; width: 14rem;
167
167
  background: var(--menu-bg, #000);
168
168
  border: 1px solid var(--menu-border, #374151);
169
169
  border-radius: 0.75rem;
170
170
  padding: 0.5rem; display: none; flex-direction: column; gap: 0.25rem; z-index: 50;
171
171
  }
172
- .pin-context-menu { right: auto; left: 0; width: 12rem; }
173
- .auth-menu.open, .pin-context-menu.open { display: flex; }
172
+ .auth-menu.open { display: flex; }
174
173
 
175
174
  .auth-menu-item {
176
175
  display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem;
@@ -261,6 +260,18 @@
261
260
  color: #6366f1;
262
261
  }
263
262
 
263
+ .btn-toolbar-style.btn-primary-override-danger {
264
+ justify-content: center;
265
+ background-color: rgba(220, 38, 38, 0.1);
266
+ border: 1px solid #dc2626;
267
+ color: #dc2626;
268
+ }
269
+ .btn-toolbar-style.btn-primary-override-danger:hover {
270
+ background-color: rgba(220, 38, 38, 0.15);
271
+ border-color: #ef4444;
272
+ color: #ef4444;
273
+ }
274
+
264
275
  .settings-tab {
265
276
  width: 100%;
266
277
  justify-content: flex-start;
@@ -410,23 +421,6 @@
410
421
  </div>
411
422
 
412
423
  <div class="auth-controls-wrapper">
413
- <div class="relative" id="pin-wrapper">
414
- <button id="pin-btn" class="icon-btn" title="Pin this page">
415
- <i class="fa-solid fa-thumbtack"></i>
416
- </button>
417
- <div id="pin-menu" class="pin-context-menu">
418
- <div class="auth-menu-item" id="repin-btn">
419
- <i class="fa-solid fa-thumbtack w-4"></i> Repin
420
- </div>
421
- <div class="auth-menu-item text-red-400 hover:text-red-300" id="remove-pin-btn">
422
- <i class="fa-solid fa-xmark w-4"></i> Remove Pin
423
- </div>
424
- <div class="auth-menu-item text-red-400 hover:text-red-300" id="hide-pin-btn">
425
- <i class="fa-solid fa-eye-slash w-4"></i> Hide Button
426
- </div>
427
- </div>
428
- </div>
429
-
430
424
  <div class="relative">
431
425
  <button id="auth-btn" class="icon-btn">
432
426
  <i class="fa-solid fa-user"></i>
@@ -439,9 +433,6 @@
439
433
  <a href="#" class="auth-menu-item" onclick="loadPage('settings.html'); return false;">
440
434
  <i class="fa-solid fa-gear w-4"></i> Settings
441
435
  </a>
442
- <div class="auth-menu-item hidden" id="show-pin-menu-item">
443
- <i class="fa-solid fa-eye w-4"></i> Show Pin Button
444
- </div>
445
436
  <div class="auth-menu-item text-red-400 hover:text-red-300" id="logout-btn">
446
437
  <i class="fa-solid fa-right-from-bracket w-4"></i> Disconnect
447
438
  </div>
@@ -474,6 +465,7 @@
474
465
  <div id="settings-main-view">
475
466
  <div id="tab-general" class="settings-section">
476
467
  <h3 class="text-3xl font-bold text-white mb-6">General Settings</h3>
468
+
477
469
  <div class="settings-box">
478
470
  <h3 class="text-xl font-bold text-white mb-2">Account Username</h3>
479
471
  <label class="block text-gray-400 text-sm mb-2 font-light">New Username</label>
@@ -483,6 +475,28 @@
483
475
  </div>
484
476
  <p id="username-msg" class="general-message-area text-sm mt-2"></p>
485
477
  </div>
478
+
479
+ <h3 class="text-xl font-bold text-white mb-2 mt-6">Organization</h3>
480
+ <div class="settings-box bg-cyan-500/10 border-cyan-500/50 p-4">
481
+ <p class="text-sm font-light text-cyan-300 mb-3">
482
+ Visit the official 4SP Organization website for updates and more tools.
483
+ </p>
484
+ <a href="https://4sp-organization.github.io/" target="_blank" class="btn-toolbar-style w-full justify-center bg-cyan-500/10 border-cyan-500 text-cyan-500 hover:bg-cyan-500/20 hover:text-cyan-400">
485
+ <i class="fa-solid fa-globe mr-2"></i> Visit Website
486
+ </a>
487
+ </div>
488
+
489
+ <h3 class="text-xl font-bold text-white mb-2 mt-6">Disconnect Client</h3>
490
+ <div class="settings-box bg-red-900/10 border-red-700/50 p-4">
491
+ <p class="text-sm font-light text-red-300 mb-3">
492
+ <i class="fa-solid fa-triangle-exclamation mr-1"></i>
493
+ Disconnect this client from your account. You will need to generate a new code to reconnect.
494
+ </p>
495
+ <button id="disconnect-account-btn" class="btn-toolbar-style btn-primary-override-danger w-full justify-center">
496
+ <i class="fa-solid fa-power-off mr-2"></i> Disconnect Account
497
+ </button>
498
+ </div>
499
+
486
500
  </div>
487
501
 
488
502
  <div id="tab-personalization" class="settings-section hidden">
@@ -569,18 +583,19 @@
569
583
  <h3 class="text-3xl font-bold text-white mb-6">About 4SP</h3>
570
584
  <div class="settings-box p-6 flex flex-col items-center text-center">
571
585
  <img src="https://cdn.jsdelivr.net/npm/4sp-asset-library@latest/logo.png" class="h-24 mb-4" alt="Logo">
572
- <h1 class="text-3xl font-bold text-white mb-2">4SimpleProblems</h1>
573
- <p class="text-gray-400 mb-6 max-w-lg font-light">A comprehensive student toolkit and entertainment platform.</p>
586
+ <h1 class="text-3xl font-bold text-white mb-2">(4SP) 4simpleproblems</h1>
587
+ <p class="text-gray-400 mb-6 max-w-lg font-light">From a soundboard, to a full downloadable client of a platform.</p>
574
588
 
575
- <div class="inline-block bg-[#0a0a0a] border border-[#333] px-4 py-2 rounded-full mb-8">
576
- <span class="text-gray-500 text-sm">Version</span>
577
- <span class="text-indigo-400 font-mono font-bold ml-2">5.0.0 DV</span>
578
- </div>
589
+ <div class="inline-block bg-[#0a0a0a] border border-[#333] px-4 py-2 rounded-[14px] mb-8">
590
+ <span class="text-gray-500 text-sm">Version:</span>
591
+ <span class="text-indigo-400 font-mono font-bold ml-2">5.0.0 (DV)</span>
592
+ </div>
593
+
579
594
 
580
595
  <div class="flex gap-4">
581
- <a href="#" class="text-gray-400 hover:text-white transition"><i class="fa-brands fa-github fa-xl"></i></a>
582
- <a href="#" class="text-gray-400 hover:text-white transition"><i class="fa-brands fa-youtube fa-xl"></i></a>
583
- <a href="#" class="text-gray-400 hover:text-white transition"><i class="fa-brands fa-discord fa-xl"></i></a>
596
+ <a href="https://github.com/4simpleproblems-v5" class="text-gray-400 hover:text-white transition"><i class="fa-brands fa-github fa-xl"></i></a>
597
+ <a href="https://youtube.com/4simpleproblems" class="text-gray-400 hover:text-white transition"><i class="fa-brands fa-youtube fa-xl"></i></a>
598
+ <a href="https://x.com/@4simpleproblems" class="text-gray-400 hover:text-white transition"><i class="fa-brands fa-discord fa-xl"></i></a>
584
599
  </div>
585
600
  </div>
586
601
  </div>
@@ -1806,15 +1821,6 @@
1806
1821
  const glideLeft = document.getElementById('glide-left');
1807
1822
  const glideRight = document.getElementById('glide-right');
1808
1823
 
1809
- // Pin Elements
1810
- const pinWrapper = document.getElementById('pin-wrapper');
1811
- const pinBtn = document.getElementById('pin-btn');
1812
- const pinMenu = document.getElementById('pin-menu');
1813
- const repinBtn = document.getElementById('repin-btn');
1814
- const removePinBtn = document.getElementById('remove-pin-btn');
1815
- const hidePinBtn = document.getElementById('hide-pin-btn');
1816
- const showPinMenuItem = document.getElementById('show-pin-menu-item');
1817
-
1818
1824
  // Auth Elements
1819
1825
  const authBtn = document.getElementById('auth-btn');
1820
1826
  const authMenu = document.getElementById('auth-menu');
@@ -1822,10 +1828,8 @@
1822
1828
  const displayUsername = document.getElementById('display-username');
1823
1829
  const displayEmail = document.getElementById('display-email');
1824
1830
 
1825
- const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.15/logged-in/';
1831
+ const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.18/logged-in/';
1826
1832
  const STORAGE_KEY = 'local_access_code';
1827
- const PINNED_PAGE_KEY = 'local_pinned_page';
1828
- const PIN_HIDDEN_KEY = 'local_pin_hidden';
1829
1833
  const USER_DATA_KEY = 'local_user_data';
1830
1834
  const LAST_PAGE_KEY = 'local_last_page';
1831
1835
  const OWNER_EMAIL = "4simpleproblems@gmail.com"; // For admin checks
@@ -1844,6 +1848,7 @@
1844
1848
  };
1845
1849
 
1846
1850
  let currentUser = null;
1851
+ let userDataUnsubscribe = null;
1847
1852
 
1848
1853
  // --- ADMIN KEYBINDS LOGIC (Integrated) ---
1849
1854
  function initAdminKeybinds(db, auth, user) {
@@ -2038,9 +2043,6 @@
2038
2043
  // --- Navbar Logic ---
2039
2044
  function renderNavbar(activePageUrl) {
2040
2045
  const tabsContainerEl = document.getElementById('tabs-container');
2041
- const pinBtnEl = document.getElementById('pin-btn');
2042
- const pinWrapperEl = document.getElementById('pin-wrapper');
2043
- const showPinMenuItemEl = document.getElementById('show-pin-menu-item');
2044
2046
 
2045
2047
  // Glide Elements
2046
2048
  const glideLeft = document.getElementById('glide-left');
@@ -2105,86 +2107,6 @@
2105
2107
 
2106
2108
  // Initial check
2107
2109
  setTimeout(updateScrollGilders, 50);
2108
-
2109
- // Update Pin Button State
2110
- const pinned = localStorage.getItem(PINNED_PAGE_KEY);
2111
- if (pinBtnEl) {
2112
- if (pinned && pinned === currentPath) {
2113
- pinBtnEl.style.color = '#4f46e5';
2114
- } else {
2115
- pinBtnEl.style.color = '#d1d5db';
2116
- }
2117
- }
2118
-
2119
- // Update Pin Visibility
2120
- const isHidden = localStorage.getItem(PIN_HIDDEN_KEY) === 'true';
2121
- if (isHidden) {
2122
- if (pinWrapperEl) pinWrapperEl.classList.add('hidden');
2123
- if (showPinMenuItemEl) {
2124
- showPinMenuItemEl.classList.remove('hidden');
2125
- showPinMenuItemEl.onclick = () => {
2126
- localStorage.setItem(PIN_HIDDEN_KEY, 'false');
2127
- renderNavbar(activePageUrl);
2128
- };
2129
- }
2130
- } else {
2131
- if (pinWrapperEl) pinWrapperEl.classList.remove('hidden');
2132
- if (showPinMenuItemEl) showPinMenuItemEl.classList.add('hidden');
2133
- }
2134
- }
2135
-
2136
- // --- Pin Logic ---
2137
- function getCurrentPage() {
2138
- const currentFrameSrc = appFrame.contentWindow.location.href;
2139
- let pageName = currentFrameSrc.replace(BASE_URL, '');
2140
- if (!pageName || pageName === 'about:blank') pageName = window.lastLoadedPage || 'dashboard.html';
2141
- return pageName;
2142
- }
2143
-
2144
- // Pin Button
2145
- if (pinBtn) {
2146
- pinBtn.addEventListener('click', () => {
2147
- const pageName = getCurrentPage();
2148
- const currentPinned = localStorage.getItem(PINNED_PAGE_KEY);
2149
-
2150
- if (currentPinned === pageName) {
2151
- localStorage.removeItem(PINNED_PAGE_KEY);
2152
- } else {
2153
- localStorage.setItem(PINNED_PAGE_KEY, pageName);
2154
- }
2155
- renderNavbar(pageName);
2156
- });
2157
-
2158
- // Context Menu
2159
- pinBtn.addEventListener('contextmenu', (e) => {
2160
- e.preventDefault();
2161
- if (pinMenu) pinMenu.classList.toggle('open');
2162
- if (authMenu) authMenu.classList.remove('open');
2163
- });
2164
- }
2165
-
2166
- if (repinBtn) {
2167
- repinBtn.addEventListener('click', () => {
2168
- localStorage.setItem(PINNED_PAGE_KEY, getCurrentPage());
2169
- renderNavbar(getCurrentPage());
2170
- if (pinMenu) pinMenu.classList.remove('open');
2171
- });
2172
- }
2173
-
2174
- if (removePinBtn) {
2175
- removePinBtn.addEventListener('click', () => {
2176
- localStorage.removeItem(PINNED_PAGE_KEY);
2177
- renderNavbar(getCurrentPage());
2178
- if (pinMenu) pinMenu.classList.remove('open');
2179
- });
2180
- }
2181
-
2182
- if (hidePinBtn) {
2183
- hidePinBtn.addEventListener('click', () => {
2184
- localStorage.setItem(PIN_HIDDEN_KEY, 'true');
2185
- renderNavbar(getCurrentPage());
2186
- if (pinMenu) pinMenu.classList.remove('open');
2187
- });
2188
2110
  }
2189
2111
 
2190
2112
  // Auth Menu
@@ -2192,7 +2114,6 @@
2192
2114
  authBtn.addEventListener('click', (e) => {
2193
2115
  e.stopPropagation();
2194
2116
  if (authMenu) authMenu.classList.toggle('open');
2195
- if (pinMenu) pinMenu.classList.remove('open');
2196
2117
  });
2197
2118
  }
2198
2119
 
@@ -2200,42 +2121,56 @@
2200
2121
  if (authMenu && authBtn && !authMenu.contains(e.target) && !authBtn.contains(e.target)) {
2201
2122
  authMenu.classList.remove('open');
2202
2123
  }
2203
- if (pinMenu && pinBtn && !pinMenu.contains(e.target) && !pinBtn.contains(e.target)) {
2204
- pinMenu.classList.remove('open');
2205
- }
2206
2124
  });
2207
2125
 
2208
2126
  if (logoutBtn) {
2209
2127
  logoutBtn.addEventListener('click', () => {
2210
2128
  localStorage.removeItem(STORAGE_KEY);
2211
2129
  localStorage.removeItem(USER_DATA_KEY);
2130
+ if (userDataUnsubscribe) userDataUnsubscribe();
2212
2131
  location.reload();
2213
2132
  });
2214
2133
  }
2215
2134
 
2216
2135
  async function fetchUserData(ownerUid) {
2217
- try {
2218
- // Parallel check for User Data and Admin Status
2219
- const [userDoc, adminDoc] = await Promise.all([
2220
- getDoc(doc(db, "users", ownerUid)),
2221
- getDoc(doc(db, "admins", ownerUid))
2222
- ]);
2223
-
2224
- if (userDoc.exists()) {
2225
- const data = userDoc.data();
2226
- const userData = {
2227
- uid: ownerUid,
2228
- username: data.username || "User",
2229
- email: data.email || "No email"
2230
- };
2231
- localStorage.setItem(USER_DATA_KEY, JSON.stringify(userData));
2232
- updateUIWithUser(userData);
2233
- currentUser = userData; // Set global
2234
- }
2136
+ // Setup Real-time Listener for User Data (PFP Sync)
2137
+ if (userDataUnsubscribe) userDataUnsubscribe();
2235
2138
 
2236
- // Check Admin Status
2139
+ try {
2140
+ // Initial fetch to check admin status (less frequent change)
2141
+ const adminDoc = await getDoc(doc(db, "admins", ownerUid));
2142
+
2143
+ userDataUnsubscribe = onSnapshot(doc(db, "users", ownerUid), (docSnap) => {
2144
+ if (docSnap.exists()) {
2145
+ const data = docSnap.data();
2146
+ // Merge with existing local data to preserve properties not in basic user doc if any
2147
+ const newUserData = {
2148
+ ...currentUser,
2149
+ uid: ownerUid,
2150
+ username: data.username || "User",
2151
+ email: data.email || "No email",
2152
+ pfpType: data.pfpType,
2153
+ customPfp: data.customPfp,
2154
+ pfpLetterBg: data.pfpLetterBg,
2155
+ navbarTheme: data.navbarTheme // Sync theme if updated remotely
2156
+ };
2157
+
2158
+ localStorage.setItem(USER_DATA_KEY, JSON.stringify(newUserData));
2159
+ updateUIWithUser(newUserData);
2160
+ currentUser = newUserData; // Update global state
2161
+
2162
+ // If settings overlay is open, we might need to update preview there too
2163
+ if (!document.getElementById('settings-overlay').classList.contains('hidden')) {
2164
+ // Dispatch event or manually update if elements exist
2165
+ const preview = document.getElementById('pfp-preview');
2166
+ if (preview) updateSettingsPfpPreview(newUserData, preview);
2167
+ }
2168
+ }
2169
+ });
2170
+
2171
+ // Check Admin Status (kept separate as it's static usually)
2237
2172
  let isAdmin = false;
2238
- if (userData && userData.email === OWNER_EMAIL) isAdmin = true;
2173
+ if (currentUser && currentUser.email === OWNER_EMAIL) isAdmin = true;
2239
2174
  if (adminDoc.exists()) isAdmin = true;
2240
2175
 
2241
2176
  if (isAdmin) {
@@ -2250,11 +2185,27 @@
2250
2185
  initAnalytics(db, ownerUid);
2251
2186
 
2252
2187
  } catch (e) {
2253
- console.error("Error fetching user data:", e);
2254
- // Even on error, try init analytics as anon/fallback
2188
+ console.error("Error setting up user listener:", e);
2255
2189
  initAnalytics(db, ownerUid);
2256
2190
  }
2257
2191
  }
2192
+
2193
+ // Helper to update the settings panel preview if open
2194
+ function updateSettingsPfpPreview(user, previewEl) {
2195
+ const pfpType = user.pfpType || 'letter';
2196
+ if (pfpType === 'custom' && user.customPfp) {
2197
+ previewEl.style.backgroundImage = `url('${user.customPfp}')`;
2198
+ previewEl.style.backgroundColor = "transparent";
2199
+ previewEl.style.backgroundSize = "cover";
2200
+ previewEl.innerText = "";
2201
+ } else {
2202
+ const letter = (user.username || 'U').charAt(0).toUpperCase();
2203
+ const bgColor = user.pfpLetterBg || '#3B82F6';
2204
+ previewEl.style.backgroundImage = 'none';
2205
+ previewEl.style.backgroundColor = bgColor;
2206
+ previewEl.innerText = letter;
2207
+ }
2208
+ }
2258
2209
 
2259
2210
  function updateUIWithUser(userData) {
2260
2211
  if (!userData) return;
@@ -2397,15 +2348,11 @@
2397
2348
  // Determine start page
2398
2349
  let startPage = 'dashboard.html';
2399
2350
  const lastPage = localStorage.getItem(LAST_PAGE_KEY);
2400
- const pinned = localStorage.getItem(PINNED_PAGE_KEY);
2401
-
2402
- // Priority: Last Page > Pinned > Default
2351
+
2352
+ // Priority: Last Page > > Default
2403
2353
  if (lastPage) {
2404
2354
  const isValid = Object.values(PAGE_DATA).some(p => p.url === lastPage);
2405
2355
  if (isValid) startPage = lastPage;
2406
- } else if (pinned) {
2407
- const isValid = Object.values(PAGE_DATA).some(p => p.url === pinned);
2408
- if (isValid) startPage = pinned;
2409
2356
  }
2410
2357
 
2411
2358
  loadPage(startPage);
@@ -2637,11 +2584,20 @@
2637
2584
 
2638
2585
  // --- Settings Data & Actions ---
2639
2586
 
2640
- // --- 1. General: Username ---
2587
+ // --- 1. General: Username & New Actions ---
2641
2588
  const settingsUsernameInput = document.getElementById('settings-username-input');
2642
2589
  const saveUsernameBtn = document.getElementById('save-username-btn');
2643
2590
  const usernameMsg = document.getElementById('username-msg');
2644
2591
 
2592
+ // Add Disconnect Listener
2593
+ document.getElementById('disconnect-account-btn').addEventListener('click', () => {
2594
+ // Re-use existing logout logic
2595
+ localStorage.removeItem(STORAGE_KEY);
2596
+ localStorage.removeItem(USER_DATA_KEY);
2597
+ if (userDataUnsubscribe) userDataUnsubscribe();
2598
+ location.reload();
2599
+ });
2600
+
2645
2601
  async function checkProfanity(text) {
2646
2602
  try {
2647
2603
  const res = await fetch(`https://www.purgomalum.com/service/containsprofanity?text=${encodeURIComponent(text)}`);
@@ -2679,9 +2635,7 @@
2679
2635
 
2680
2636
  // Save
2681
2637
  await updateDoc(doc(db, "users", currentUser.uid), { username: newName });
2682
- currentUser.username = newName;
2683
- localStorage.setItem(USER_DATA_KEY, JSON.stringify(currentUser));
2684
- updateUIWithUser(currentUser);
2638
+ // Local update happens via onSnapshot listener now
2685
2639
  usernameMsg.innerText = "Saved!";
2686
2640
  usernameMsg.className = "text-green-400 text-xs mt-2 min-h-[20px]";
2687
2641
 
@@ -2754,10 +2708,7 @@
2754
2708
  pfpLetterBg: pfpState.letterColor,
2755
2709
  letterAvatarColor: pfpState.letterColor // Legacy support
2756
2710
  });
2757
- // Update local logic if needed
2758
- currentUser.pfpType = 'letter';
2759
- currentUser.pfpLetterBg = pfpState.letterColor;
2760
- localStorage.setItem(USER_DATA_KEY, JSON.stringify(currentUser));
2711
+ // Local update handled by snapshot
2761
2712
  saveLetterPfpBtn.innerText = "Saved!";
2762
2713
  setTimeout(() => saveLetterPfpBtn.innerText = "Set Letter Avatar", 2000);
2763
2714
  } catch(e) {
@@ -2923,21 +2874,21 @@
2923
2874
  try {
2924
2875
  submitCropBtn.disabled = true;
2925
2876
  submitCropBtn.textContent = "Saving...";
2877
+
2878
+ // 1. Update Firestore
2926
2879
  await updateDoc(doc(db, "users", currentUser.uid), {
2927
2880
  customPfp: base64,
2928
2881
  pfpType: 'custom'
2929
2882
  });
2930
2883
 
2884
+ // 2. Update Local State Immediately
2931
2885
  currentUser.customPfp = base64;
2932
2886
  currentUser.pfpType = 'custom';
2933
2887
  localStorage.setItem(USER_DATA_KEY, JSON.stringify(currentUser));
2888
+ updateUIWithUser(currentUser);
2889
+ updateSettingsPfpPreview(currentUser, pfpPreview);
2934
2890
 
2935
- // Update UI
2936
- pfpPreview.innerText = "";
2937
- pfpPreview.style.backgroundImage = `url('${base64}')`;
2938
- pfpPreview.style.backgroundColor = "transparent";
2939
- pfpPreview.style.backgroundSize = "cover";
2940
-
2891
+ // Update UI Feedback
2941
2892
  cropperModal.style.display = 'none';
2942
2893
  saveUploadPfpBtn.innerText = "Saved!"; // Feedback on the main btn too
2943
2894
  } catch (e) {
@@ -3151,7 +3102,7 @@
3151
3102
  // Global store for active keys (in-memory)
3152
3103
  window.activePanicKeys = [];
3153
3104
 
3154
- async function loadPanicKeys() {
3105
+ async function loadPanicKeys() {
3155
3106
  try {
3156
3107
  const settingsMap = await getPanicKeySettings();
3157
3108
  window.activePanicKeys = []; // Reset
@@ -3311,4 +3262,4 @@
3311
3262
 
3312
3263
  </script>
3313
3264
  </body>
3314
- </html>
3265
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "4sp-dv-latest",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "The latest HTML file of the 4SP Version 5 Client.",
5
5
  "license": "ISC",
6
6
  "author": "",