diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index eeeb4a2..c9bc620 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -17,7 +17,7 @@ import { import Link from 'next/link'; import { useTheme } from '@/contexts/ThemeContext'; import { useConfig } from '@/contexts/ConfigContext'; -import { ChevronUpDownIcon, CheckIcon, SettingsIcon, KeyIcon, PaletteIcon, DocumentIcon, UserIcon, DownloadIcon } from '@/components/icons/Icons'; +import { ChevronUpDownIcon, CheckIcon, SettingsIcon, KeyIcon, PaletteIcon, DocumentIcon, UserIcon, DownloadIcon, ChevronRightIcon } from '@/components/icons/Icons'; import { getAppConfig, getFirstVisit, setFirstVisit } from '@/lib/client/dexie'; import { useDocuments } from '@/contexts/DocumentContext'; import { ConfirmDialog } from '@/components/ConfirmDialog'; @@ -574,36 +574,36 @@ export function SettingsModal({ className = '' }: { className?: string }) { - {isChangelogOpen && ( + {isChangelogOpen ? ( setIsChangelogOpen(false)} /> - )} + ) : ( + <> + {/* Mobile: 2x2 grid nav */} +
+ {visibleSections.map((section) => { + const Icon = section.icon; + return ( + + ); + })} +
- {/* Mobile: 2x2 grid nav */} -
- {visibleSections.map((section) => { - const Icon = section.icon; - return ( - - ); - })} -
- -
+
{/* Desktop: vertical sidebar */}
-
+ + + )} @@ -1391,6 +1393,11 @@ function SettingsChangelogPanel({ const [expanded, setExpanded] = useState>({}); const [bodies, setBodies] = useState>({}); const normalizedAppVersion = normalizeVersion(appVersion || ''); + const isAbortError = (err: unknown): boolean => { + return err instanceof DOMException + ? err.name === 'AbortError' + : !!(typeof err === 'object' && err && 'name' in err && (err as { name?: string }).name === 'AbortError'); + }; useEffect(() => { const controller = new AbortController(); @@ -1406,9 +1413,10 @@ function SettingsChangelogPanel({ setExpanded((prev) => ({ ...prev, [entry.tag_name]: true })); } } catch (err) { + if (isAbortError(err)) return; setError(err instanceof Error ? err.message : 'Failed to load changelog'); } finally { - setLoading(false); + if (!controller.signal.aborted) setLoading(false); } } void loadManifest(); @@ -1429,7 +1437,8 @@ function SettingsChangelogPanel({ try { const body = await fetchChangelogReleaseBody(manifestUrl, entry.body_path, controller.signal); setBodies((prev) => ({ ...prev, [tag]: body })); - } catch { + } catch (err) { + if (isAbortError(err)) return; // Keep entry expanded; inline fallback appears below. } })); @@ -1439,8 +1448,18 @@ function SettingsChangelogPanel({ }, [expanded, manifest, manifestUrl, bodies]); return ( -
-
+
+
+

Changelog

@@ -1449,30 +1468,24 @@ function SettingsChangelogPanel({ : 'Release history from GitHub'}

-
-
+
{loading && ( -
+
Loading changelog…
)} {!loading && error && ( -
+

Could not load changelog right now.

{error}

Open GitHub Releases @@ -1480,7 +1493,7 @@ function SettingsChangelogPanel({ )} {!loading && !error && manifest.length === 0 && ( -
+
No releases found.
)} @@ -1489,42 +1502,46 @@ function SettingsChangelogPanel({ const isCurrent = normalizedAppVersion && normalizeVersion(entry.tag_name) === normalizedAppVersion; const body = bodies[entry.tag_name]; const isExpanded = !!expanded[entry.tag_name]; + const normalizedTag = normalizeVersion(entry.tag_name); + const normalizedName = normalizeVersion(entry.name || ''); + const showName = Boolean(entry.name) && normalizedName !== normalizedTag; return ( -
+
{isExpanded && ( -
+
{body ? (
@@ -1538,7 +1555,7 @@ function SettingsChangelogPanel({ href={entry.html_url} target="_blank" rel="noreferrer" - className="text-xs font-medium text-accent hover:underline" + className="inline-flex text-xs font-medium text-accent hover:underline transition-all duration-200 ease-in-out transform hover:scale-[1.02]" > View on GitHub