Skip to content

Commit a5cebaf

Browse files
author
Gurasuraisu
committed
Improve recovery mode
1 parent da951aa commit a5cebaf

3 files changed

Lines changed: 219 additions & 66 deletions

File tree

recovery/index.html

Lines changed: 216 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -181,19 +181,40 @@
181181
<h1 data-lang-key="header">Recovery Mode</h1>
182182
<p data-lang-key="description">Your Polygol instance may be corrupted or broken. Use these tools to perform a factory reset.</p>
183183

184-
<div style="display: grid;gap: 20px;grid-auto-flow: column;grid-template-columns: repeat(2, minmax(0, 1fr));">
184+
<div style="display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); text-align: left;">
185185
<div class="card">
186186
<span class="material-symbols-rounded" style="font-size: 32px;">info</span>
187187
<h2>Information</h2>
188-
<p>This tool will not affect your device. These actions are irreversible.</p>
188+
<p>Recovery mode will not affect your device.<br>These actions are irreversible.<br>Back up your data at polygol.github.io/transfer</p>
189+
</div>
190+
191+
<div class="card">
192+
<span class="material-symbols-rounded" style="font-size: 32px;">build</span>
193+
<h2>System data</h2>
194+
<p>Fix issues without losing data.</p>
195+
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-top: 15px;">
196+
<button id="btn-clear-offline" class="btn-action">Clear offline data or cache</button>
197+
<button id="btn-del-script" class="btn-action">Remove startup script</button>
198+
<button id="btn-reset-waves" class="btn-action">Reset Waves</button>
199+
</div>
200+
</div>
201+
202+
<div class="card">
203+
<span class="material-symbols-rounded" style="font-size: 32px;">account_circle</span>
204+
<h2>User data</h2>
205+
<p>Fix issues with apps or customization.</p>
206+
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-top: 15px;">
207+
<button id="btn-repair-wp" class="btn-action">Repair wallpapers</button>
208+
<button id="btn-clear-apps" class="btn-destructive">Clear app data</button>
209+
</div>
189210
</div>
190211

191212
<div class="card">
192213
<span class="material-symbols-rounded" style="font-size: 32px;">delete_forever</span>
193214
<h2>Factory Reset</h2>
194215
<p>Deletes all your settings, wallpapers and app data and restores them to factory settings.<br>This will delete all current data on this device.</p>
195216
<div style="display: flex;gap: 14px;">
196-
<button id="full-reset" class="btn-destructive">Reset</button>
217+
<button id="full-reset" class="btn-destructive">Erase everything</button>
197218
</div>
198219
</div>
199220
</div>
@@ -218,15 +239,19 @@ <h2>Factory Reset</h2>
218239
});
219240
}
220241

242+
const btnClearCache = document.getElementById('btn-clear-offline');
243+
const btnDelScript = document.getElementById('btn-del-script');
244+
const btnResetWaves = document.getElementById('btn-reset-waves');
245+
const btnRepairWp = document.getElementById('btn-repair-wp');
246+
const btnClearApps = document.getElementById('btn-clear-apps');
221247
const fullResetBtn = document.getElementById('full-reset');
222248
const returnHomeBtn = document.getElementById('return-home');
223249
const statusDiv = document.getElementById('status');
224-
const translatableElements = document.querySelectorAll('[data-lang-key]');
225250

226251
function logStatus(message, isError = false) {
227252
console.log(message);
228-
statusDiv.textContent = `> ${message}`;
229-
statusDiv.style.color = isError ? '#ef9a9a' : '#a5d6a7';
253+
statusDiv.textContent = `${message}`;
254+
statusDiv.style.color = isError ? '#ef9a9a' : 'var(--text-color)';
230255
}
231256

232257
// Helper to spawn app and wipe it
@@ -259,82 +284,210 @@ <h2>Factory Reset</h2>
259284
});
260285
}
261286

262-
async function performFullReset() {
263-
const confirmationText = 'Confirm that you want to erase all user data. This action is not reversible.';
264-
265-
if (!confirm(confirmationText)) {
266-
logStatus('Reset cancelled by user.');
287+
// Action: Clear Cache & Swap
288+
btnClearCache.addEventListener('click', async () => {
289+
if (!confirm('Clear offline data? This will redownload the latest version of Polygol from the internet.')) return;
290+
try {
291+
if ('serviceWorker' in navigator) {
292+
const registrations = await navigator.serviceWorker.getRegistrations();
293+
await Promise.all(registrations.map(reg => reg.unregister()));
294+
}
295+
if ('caches' in window) {
296+
const cacheNames = await caches.keys();
297+
await Promise.all(cacheNames.map(name => caches.delete(name)));
298+
}
299+
if ('indexedDB' in window) {
300+
await new Promise((resolve) => {
301+
const req = window.indexedDB.deleteDatabase('PolygolSystemVaultDB');
302+
req.onsuccess = resolve;
303+
req.onerror = resolve; // Ignore errors
304+
req.onblocked = resolve;
305+
});
306+
}
307+
logStatus('Offline data cleared.');
308+
} catch (e) {
309+
logStatus(`Failed to clear cache: ${e.message}`, true);
310+
}
311+
});
312+
313+
// Action: Remove Startup Script
314+
btnDelScript.addEventListener('click', () => {
315+
if (localStorage.getItem('customStartupScript')) {
316+
localStorage.removeItem('customStartupScript');
317+
logStatus('Startup script removed.');
318+
} else {
319+
logStatus('No startup script found.');
320+
}
321+
});
322+
323+
// Action: Reset Waves Remote
324+
btnResetWaves.addEventListener('click', () => {
325+
localStorage.removeItem('waves_host_config');
326+
localStorage.removeItem('waves_known_devices');
327+
localStorage.removeItem('waves_discovery_enabled');
328+
logStatus('Waves pairings reset.');
329+
});
330+
331+
// Action: Repair Wallpapers
332+
btnRepairWp.addEventListener('click', () => {
333+
const wps = localStorage.getItem('recentWallpapers');
334+
if (!wps) {
335+
logStatus('No wallpapers found to repair.');
267336
return;
268337
}
269338

270339
try {
271-
// 0. Wipe External Apps (Must be done before clearing LS which holds the app list)
272-
logStatus('Scanning for installed apps...');
340+
let parsed = JSON.parse(wps);
341+
if (!Array.isArray(parsed)) throw new Error("Invalid format");
342+
343+
const defaults = {
344+
font: 'Inter', weight: '700', color: '#ffffff',
345+
colorEnabled: false, stackEnabled: false,
346+
showSeconds: true, showWeather: true,
347+
alignment: 'center', clockSize: '0',
348+
clockPosX: '50', clockPosY: '50',
349+
roundness: '0', letterSpacing: '0', textCase: 'none'
350+
};
351+
352+
const defaultEffects = { blur: '0', brightness: '100', contrast: '100', saturate: '100', hue: '0', vignette: '0' };
353+
354+
parsed.forEach(wp => {
355+
// 1. Ensure clockStyles object exists
356+
if (!wp.clockStyles || typeof wp.clockStyles !== 'object') wp.clockStyles = {};
357+
358+
// 2. Fill missing basic properties
359+
Object.entries(defaults).forEach(([key, val]) => {
360+
if (wp.clockStyles[key] === undefined || wp.clockStyles[key] === null) {
361+
wp.clockStyles[key] = val;
362+
}
363+
});
364+
365+
// 3. Heal wallpaper effects structure
366+
if (!wp.clockStyles.wallpaperEffects || typeof wp.clockStyles.wallpaperEffects !== 'object') {
367+
wp.clockStyles.wallpaperEffects = {
368+
light: { ...defaultEffects },
369+
dark: { ...defaultEffects }
370+
};
371+
} else {
372+
['light', 'dark'].forEach(theme => {
373+
if (!wp.clockStyles.wallpaperEffects[theme]) {
374+
wp.clockStyles.wallpaperEffects[theme] = { ...defaultEffects };
375+
} else {
376+
Object.entries(defaultEffects).forEach(([k, v]) => {
377+
if (wp.clockStyles.wallpaperEffects[theme][k] === undefined) {
378+
wp.clockStyles.wallpaperEffects[theme][k] = v;
379+
}
380+
});
381+
}
382+
});
383+
}
384+
385+
// 4. Validate widgetLayout
386+
if (!Array.isArray(wp.widgetLayout)) wp.widgetLayout = [];
387+
});
388+
389+
localStorage.setItem('recentWallpapers', JSON.stringify(parsed));
390+
logStatus('Wallpapers repaired.');
391+
} catch (e) {
392+
alert('Could not repair wallpapers, as their structures are critically corrupted. Back up your data and perform a factory reset.', true);
393+
}
394+
});
395+
396+
async function secureConfirmation(actionName, warningText) {
397+
for (let i = 1; i <= 4; i++) {
398+
const confirmed = confirm(`${warningText}\n\nStep ${i} of 5: Press OK to continue.`);
399+
if (!confirmed) return false;
400+
}
401+
402+
const safetyKeys = ["RESET", "CONFIRM", "DELETE", "PROCEED"];
403+
const randomKey = safetyKeys[Math.floor(Math.random() * safetyKeys.length)];
404+
405+
const finalInput = prompt(
406+
`Please type the word below to finish ${actionName}:\n\n` +
407+
`Type this word: ${randomKey}`
408+
);
409+
410+
return finalInput === randomKey;
411+
}
412+
413+
// Action: Clear App Data
414+
btnClearApps.addEventListener('click', async () => {
415+
const warning = 'CLEAR APP DATA: This clears all in-app data and permissions. This cannot be undone.';
416+
const isVerified = await secureConfirmation("App Data Deletion", warning);
417+
418+
if (isVerified) {
419+
logStatus('Deleting data from apps...');
273420
const appsJson = localStorage.getItem('userInstalledApps');
274421
if (appsJson) {
275422
const apps = JSON.parse(appsJson);
276423
const urls = Object.values(apps).map(a => a.url);
277-
logStatus(`Found ${urls.length} apps. Wiping external data...`);
278-
279424
for (const url of urls) {
280-
logStatus(`Wiping ${url}...`);
281-
await wipeExternalApp(url);
425+
try { await wipeExternalApp(url); } catch(e) {}
282426
}
283427
}
284428

285-
// 1. Clear Storages
286-
logStatus('Clearing System Local Storage & Session Storage...');
287-
localStorage.clear();
288-
sessionStorage.clear();
289-
logStatus('Storages cleared.');
429+
localStorage.removeItem('appUsage');
430+
localStorage.removeItem('appLastOpened');
431+
localStorage.removeItem('appPermissions');
432+
localStorage.removeItem('appUsageHourly');
433+
localStorage.removeItem('registeredOSKs');
434+
435+
logStatus('App data cleared.');
436+
alert('App data cleared.');
437+
} else {
438+
logStatus('App data deletion cancelled.');
439+
}
440+
});
290441

291-
// 2. Unregister Service Workers
292-
if ('serviceWorker' in navigator) {
293-
logStatus('Unregistering service workers...');
294-
const registrations = await navigator.serviceWorker.getRegistrations();
295-
if (registrations.length) {
296-
await Promise.all(registrations.map(reg => reg.unregister()));
297-
logStatus(`${registrations.length} service worker(s) unregistered.`);
298-
} else {
299-
logStatus('No active service workers found.');
442+
async function performFullReset() {
443+
const warning = 'FACTORY RESET: This erases EVERY setting and app. This cannot be undone.';
444+
const isVerified = await secureConfirmation("Factory Reset", warning);
445+
446+
if (isVerified) {
447+
logStatus('All confirmations accepted. Proceeding with reset...');
448+
try {
449+
// 0. Wipe External Apps
450+
logStatus('Deleting data from apps...');
451+
const appsJson = localStorage.getItem('userInstalledApps');
452+
if (appsJson) {
453+
const apps = JSON.parse(appsJson);
454+
const urls = Object.values(apps).map(a => a.url);
455+
for (const url of urls) {
456+
try { await wipeExternalApp(url); } catch(e) {}
457+
}
300458
}
301-
}
302459

303-
// 3. Clear All Caches
304-
if ('caches' in window) {
305-
logStatus('Purging all caches...');
306-
const cacheNames = await caches.keys();
307-
if (cacheNames.length) {
308-
await Promise.all(cacheNames.map(name => caches.delete(name)));
309-
logStatus(`${cacheNames.length} cache(s) purged.`);
310-
} else {
311-
logStatus('No caches found.');
460+
// 1. Clear All Storage
461+
logStatus('Deleting LocalStorage...');
462+
localStorage.clear();
463+
sessionStorage.clear();
464+
465+
// 2. Unregister SW & Caches
466+
if ('serviceWorker' in navigator) {
467+
const regs = await navigator.serviceWorker.getRegistrations();
468+
await Promise.all(regs.map(reg => reg.unregister()));
312469
}
313-
}
314-
315-
// 4. Clear IndexedDB
316-
if ('indexedDB' in window && window.indexedDB.databases) {
317-
logStatus('Deleting IndexedDB databases...');
318-
const dbs = await window.indexedDB.databases();
319-
if (dbs.length) {
320-
await Promise.all(dbs.map(db => new Promise((resolve, reject) => {
321-
const req = window.indexedDB.deleteDatabase(db.name);
322-
req.onsuccess = resolve;
323-
req.onerror = reject;
324-
req.onblocked = () => console.warn(`IndexedDB ${db.name} is blocked and cannot be deleted right now.`);
470+
if ('caches' in window) {
471+
const names = await caches.keys();
472+
await Promise.all(names.map(n => caches.delete(n)));
473+
}
474+
475+
// 3. Delete All Databases
476+
if ('indexedDB' in window && window.indexedDB.databases) {
477+
const dbs = await window.indexedDB.databases();
478+
await Promise.all(dbs.map(db => new Promise((res) => {
479+
const req = indexedDB.deleteDatabase(db.name);
480+
req.onsuccess = res; req.onerror = res; req.onblocked = res;
325481
})));
326-
logStatus(`${dbs.length} IndexedDB database(s) deleted.`);
327-
} else {
328-
logStatus('No IndexedDB databases found.');
329482
}
330-
}
331-
332-
logStatus('Full system reset complete. You can now safely return home.');
333-
alert('Recovery complete! Please return to the main page.');
334483

335-
} catch (err) {
336-
logStatus(`An error occurred during reset: ${err.message}`, true);
337-
alert('An error occurred. Check the status log for details.');
484+
logStatus('Factory reset complete.', false);
485+
alert('System restored to factory settings.');
486+
} catch (err) {
487+
logStatus(`Reset failed: ${err.message}`, true);
488+
}
489+
} else {
490+
logStatus('Reset cancelled.');
338491
}
339492
}
340493

sw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const CORE_CACHE_VERSION = 'Bismuth 17.1.2.32.4';
1+
const CORE_CACHE_VERSION = 'Bismuth 17.1.2.32.5';
22
const CORE_CACHE_NAME = `polygol-core-${CORE_CACHE_VERSION}`;
33
const APPS_CACHE_NAME = 'polygol-apps';
44

transfer/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ <h2>Import Data</h2>
223223

224224
function logStatus(message, isError = false) {
225225
console.log(message);
226-
statusDiv.textContent = `> ${message}`;
227-
statusDiv.style.color = isError ? '#ef9a9a' : '#a5d6a7';
226+
statusDiv.textContent = `${message}`;
227+
statusDiv.style.color = isError ? '#ef9a9a' : 'var(--text-color)';
228228
}
229229

230230
// --- File & Blob Utilities ---

0 commit comments

Comments
 (0)