From 2a35fde032c637a910c6d51429a2cd535ebfe8f9 Mon Sep 17 00:00:00 2001
From: utopiaeh
Date: Thu, 27 Aug 2026 20:07:18 +0300
Subject: [PATCH] feat: add store review link and error log, harden persisted
settings
---
components/ErrorBoundary.tsx | 42 +++++++++++++++++
components/HelpPanel.tsx | 14 ++++++
components/OriginMappingsEditor.tsx | 2 +
components/options/ErrorLogSection.tsx | 38 +++++++++++++++
components/options/ProtectedSitesSection.tsx | 1 +
entrypoints/background/index.ts | 16 +++++--
entrypoints/options/App.tsx | 19 ++++++--
entrypoints/options/main.tsx | 8 +++-
entrypoints/popup/main.tsx | 8 +++-
hooks/useBrowserName.ts | 3 --
package.json | 2 +-
public/icon/128-linked.png | Bin 14800 -> 14706 bytes
public/icon/16-linked.png | Bin 946 -> 945 bytes
public/icon/32-linked.png | Bin 2507 -> 2491 bytes
public/icon/48-linked.png | Bin 4671 -> 4634 bytes
public/icon/96-linked.png | Bin 10542 -> 10499 bytes
store/clearLog.ts | 11 +++--
store/errorLog.ts | 42 +++++++++++++++++
store/settings.test.ts | 31 +++++++++++-
store/settings.ts | 31 +++++-------
store/storageError.ts | 13 +++++
utils/browser-info.ts | 9 ----
utils/clear-status.ts | 2 -
utils/clearing.browser.test.ts | 2 -
utils/clearing.firefox.test.ts | 1 -
utils/clearing.ts | 47 +------------------
utils/data-types.ts | 3 --
utils/error-log.ts | 31 ++++++++++++
utils/i18n.ts | 8 ++++
utils/settings-defaults.ts | 23 +++++++++
utils/settings-transfer.test.ts | 19 +++++++-
utils/settings-transfer.ts | 19 ++++----
utils/storage-adapter.test.ts | 4 --
utils/storage-adapter.ts | 25 ++++------
34 files changed, 342 insertions(+), 132 deletions(-)
create mode 100644 components/ErrorBoundary.tsx
create mode 100644 components/options/ErrorLogSection.tsx
create mode 100644 store/errorLog.ts
create mode 100644 store/storageError.ts
create mode 100644 utils/error-log.ts
create mode 100644 utils/settings-defaults.ts
diff --git a/components/ErrorBoundary.tsx b/components/ErrorBoundary.tsx
new file mode 100644
index 0000000..8f88e98
--- /dev/null
+++ b/components/ErrorBoundary.tsx
@@ -0,0 +1,42 @@
+import { Component, type ErrorInfo, type ReactNode } from 'react';
+import { useSettingsStore } from '../store/settings';
+
+interface Props {
+ children: ReactNode;
+}
+
+interface State {
+ error: Error | null;
+}
+
+export class ErrorBoundary extends Component {
+ override state: State = { error: null };
+
+ static getDerivedStateFromError(error: Error): State {
+ return { error };
+ }
+
+ override componentDidCatch(error: Error, info: ErrorInfo): void {
+ console.error('Bleep UI crashed:', error, info.componentStack);
+ }
+
+ override render() {
+ if (!this.state.error) return this.props.children;
+ return (
+
+
Something went wrong.
+
{this.state.error.message}
+
+
+ );
+ }
+}
diff --git a/components/HelpPanel.tsx b/components/HelpPanel.tsx
index b1319a4..8c489ed 100644
--- a/components/HelpPanel.tsx
+++ b/components/HelpPanel.tsx
@@ -1,7 +1,10 @@
import { useTranslation } from '../hooks/useTranslation';
+import { isGeckoBased } from '../utils/browser-info';
import type { TranslationKey } from '../utils/i18n';
const GITHUB_ISSUES_URL = 'https://github.com/utopiaeh/bleep/issues';
+const CHROME_REVIEW_URL = 'https://chromewebstore.google.com/detail/dnihnhdbmgjfkdlkahlbbfhginfglomj/reviews';
+const FIREFOX_REVIEW_URL = 'https://addons.mozilla.org/en-US/firefox/addon/bleep-cache-cleaner/reviews/';
const ITEMS: Array<{ title: TranslationKey; text: TranslationKey }> = [
{ title: 'helpPopupTitle', text: 'helpPopupText' },
@@ -37,6 +40,17 @@ export function HelpPanel() {
{t('helpFeedbackLink')}
+
+
+
Error log
+ {entries.length > 0 && (
+
+ )}
+
+ {entries.length === 0 ? (
+
Nothing logged.
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/components/options/ProtectedSitesSection.tsx b/components/options/ProtectedSitesSection.tsx
index ea50eb7..3e2e816 100644
--- a/components/options/ProtectedSitesSection.tsx
+++ b/components/options/ProtectedSitesSection.tsx
@@ -15,6 +15,7 @@ export function ProtectedSitesSection({ value, onChange }: ProtectedSitesSection
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={t('protectedSitesPlaceholder')}
+ maxLength={5000}
rows={2}
className="w-full rounded-md border border-stone-300 bg-stone-50 dark:border-stone-600 dark:bg-stone-800 px-3 py-1.5 text-sm placeholder:text-stone-500 focus:outline-none focus:border-blue-500 resize-y"
/>
diff --git a/entrypoints/background/index.ts b/entrypoints/background/index.ts
index 9cb851c..b959606 100644
--- a/entrypoints/background/index.ts
+++ b/entrypoints/background/index.ts
@@ -1,8 +1,11 @@
import { browser } from 'wxt/browser';
import { defineBackground } from 'wxt/utils/define-background';
import { linkedOriginsFor, tabOrigin } from '../../utils/clearing';
+import { installGlobalErrorLogging } from '../../utils/error-log';
import { readPersistedSettingsRaw } from '../../utils/storage-adapter';
+installGlobalErrorLogging();
+
const SETTINGS_KEY = 'cache-cleaner-settings';
const DEFAULT_ICON = {
@@ -81,10 +84,13 @@ export default defineBackground(() => {
if (areaName !== 'sync' || !Object.keys(changes).some((key) => key.startsWith(SETTINGS_KEY)))
return;
invalidateMappingSettingsCache();
- browser.tabs.query({ active: true }).then((tabs) => {
- for (const tab of tabs) {
- if (tab.id != null) updateIcon(tab.id, tab.url);
- }
- });
+ browser.tabs
+ .query({ active: true })
+ .then((tabs) => {
+ for (const tab of tabs) {
+ if (tab.id != null) updateIcon(tab.id, tab.url);
+ }
+ })
+ .catch(() => {});
});
});
diff --git a/entrypoints/options/App.tsx b/entrypoints/options/App.tsx
index d6e7b3a..3326c70 100644
--- a/entrypoints/options/App.tsx
+++ b/entrypoints/options/App.tsx
@@ -5,6 +5,7 @@ import { HelpPanel } from '../../components/HelpPanel';
import { OriginMappingsEditor } from '../../components/OriginMappingsEditor';
import { BackupSection } from '../../components/options/BackupSection';
import { ClearLogSection } from '../../components/options/ClearLogSection';
+import { ErrorLogSection } from '../../components/options/ErrorLogSection';
import { GlobalSection } from '../../components/options/GlobalSection';
import { LanguageSection } from '../../components/options/LanguageSection';
import { ProtectedSitesSection } from '../../components/options/ProtectedSitesSection';
@@ -19,6 +20,7 @@ import { useTheme } from '../../hooks/useTheme';
import { useTranslation } from '../../hooks/useTranslation';
import { recordClear } from '../../store/clearLog';
import { useSettingsStore } from '../../store/settings';
+import { useStorageErrorStore } from '../../store/storageError';
import { isGeckoBased } from '../../utils/browser-info';
import { runClear } from '../../utils/clear-status';
import {
@@ -138,8 +140,6 @@ export default function App() {
}, [visitedSites, visitedSiteFilter]);
async function handleGlobalClear() {
- // Only Chrome can exclude specific origins from an unscoped clear at all, and only
- // for sites we actually know the real origin of (i.e. ones in your history).
const excludeOrigins = allVisitedSites
.filter((site) => isProtectedSite(protectedSites, site.hostname))
.map((site) => site.origin);
@@ -244,8 +244,6 @@ export default function App() {
async function clearVisitedSite(site: VisitedSite): Promise