diff --git a/src/GeneralSettings.jsx b/src/GeneralSettings.jsx index 9f5a5f5..c22d448 100644 --- a/src/GeneralSettings.jsx +++ b/src/GeneralSettings.jsx @@ -53,6 +53,19 @@ function getStatusTone(state) { return 'neutral'; } +function formatCommitTimestamp(value) { + if (!value) { + return null; + } + + const parsed = new Date(value); + if (Number.isNaN(parsed.getTime())) { + return null; + } + + return parsed.toLocaleString(); +} + export default function GeneralSettings({ panel, onModelChange, @@ -80,6 +93,12 @@ export default function GeneralSettings({ const [audioInputStatus, setAudioInputStatus] = useState({ tone: 'neutral', message: '' }); const [updateStatus, setUpdateStatus] = useState(DEFAULT_UPDATE_STATUS); const [isCheckingForUpdates, setIsCheckingForUpdates] = useState(false); + const [isChangelogOpen, setIsChangelogOpen] = useState(false); + const [changelogEntries, setChangelogEntries] = useState([]); + const [changelogPage, setChangelogPage] = useState(1); + const [changelogHasMore, setChangelogHasMore] = useState(false); + const [isLoadingChangelog, setIsLoadingChangelog] = useState(false); + const [changelogError, setChangelogError] = useState(''); const [isPurgingLibraries, setIsPurgingLibraries] = useState(false); const [libraryPurgeStatus, setLibraryPurgeStatus] = useState({ tone: 'neutral', message: '' }); const [settingsHydrated, setSettingsHydrated] = useState(false); @@ -434,6 +453,46 @@ export default function GeneralSettings({ } }; + const loadChangelogPage = async (page) => { + setIsLoadingChangelog(true); + setChangelogError(''); + + try { + const result = await window.electronAPI.getChangelogPage(page); + setChangelogEntries(Array.isArray(result?.entries) ? result.entries : []); + setChangelogPage(Number.isInteger(result?.page) ? result.page : page); + setChangelogHasMore(Boolean(result?.hasMore)); + } catch (error) { + setChangelogError(`Changelog load failed: ${error.message || String(error)}`); + } finally { + setIsLoadingChangelog(false); + } + }; + + const handleToggleChangelog = async () => { + if (isChangelogOpen) { + setIsChangelogOpen(false); + return; + } + + setIsChangelogOpen(true); + if (changelogEntries.length === 0 && !isLoadingChangelog) { + await loadChangelogPage(1); + } + }; + + const handleChangelogPageChange = async (nextPage) => { + if (isLoadingChangelog || nextPage < 1) { + return; + } + + if (nextPage > changelogPage && !changelogHasMore) { + return; + } + + await loadChangelogPage(nextPage); + }; + const handlePurgeLibraries = async () => { const confirmed = window.confirm( 'Delete all Heimgeist databases, staged files, and indexes from local storage? Chat history will be kept.' @@ -672,6 +731,66 @@ export default function GeneralSettings({ {updateCheckedAtLabel &&
Last checked: {updateCheckedAtLabel}
} )} +
+ +
+ {isChangelogOpen && ( +
+ {changelogError && ( +

{changelogError}

+ )} + {!changelogError && isLoadingChangelog && ( +

Loading changelog...

+ )} + {!changelogError && !isLoadingChangelog && changelogEntries.length === 0 && ( +

No commits found.

+ )} + {!changelogError && changelogEntries.length > 0 && ( + <> + +
+ + Page {changelogPage} + +
+ + )} +
+ )} );