@@ -594,6 +594,42 @@ export default function Settings() {
594594 } ;
595595 } , [ ] ) ;
596596
597+ // Manual "Check for updates" CTA (reuses the auto-updater plugin).
598+ type UpdateState = "idle" | "checking" | "available" | "latest" | "error" | "installing" ;
599+ const [ updateState , setUpdateState ] = useState < UpdateState > ( "idle" ) ;
600+ const [ pendingUpdate , setPendingUpdate ] = useState <
601+ { version : string ; install : ( ) => Promise < void > } | null
602+ > ( null ) ;
603+
604+ const checkForUpdates = useCallback ( async ( ) => {
605+ setUpdateState ( "checking" ) ;
606+ try {
607+ const { check } = await import ( "@tauri-apps/plugin-updater" ) ;
608+ const result = await check ( ) ;
609+ if ( result ?. available ) {
610+ setPendingUpdate ( { version : result . version , install : ( ) => result . downloadAndInstall ( ) } ) ;
611+ setUpdateState ( "available" ) ;
612+ } else {
613+ setPendingUpdate ( null ) ;
614+ setUpdateState ( "latest" ) ;
615+ }
616+ } catch {
617+ setUpdateState ( "error" ) ;
618+ }
619+ } , [ ] ) ;
620+
621+ const installUpdate = useCallback ( async ( ) => {
622+ if ( ! pendingUpdate ) return ;
623+ setUpdateState ( "installing" ) ;
624+ try {
625+ await pendingUpdate . install ( ) ;
626+ const { relaunch } = await import ( "@tauri-apps/plugin-process" ) ;
627+ await relaunch ( ) ;
628+ } catch {
629+ setUpdateState ( "error" ) ;
630+ }
631+ } , [ pendingUpdate ] ) ;
632+
597633 // Menu-bar tray
598634 const [ trayCadence , setTrayCadence ] = usePref ( "tray_refresh_cadence_secs" , "120" ) ;
599635
@@ -988,6 +1024,48 @@ export default function Settings() {
9881024
9891025 < Divider />
9901026
1027+ { /* Check for updates */ }
1028+ < div className = "flex items-center justify-between py-4" >
1029+ < div className = "flex flex-col gap-0.5" >
1030+ < span className = "text-sm text-slate-400" > Updates</ span >
1031+ < span className = "text-xs text-slate-500" >
1032+ { updateState === "checking"
1033+ ? "Checking for updates…"
1034+ : updateState === "available"
1035+ ? `Update available: v${ pendingUpdate ?. version } `
1036+ : updateState === "latest"
1037+ ? "You're on the latest version."
1038+ : updateState === "installing"
1039+ ? "Downloading & installing…"
1040+ : updateState === "error"
1041+ ? "Couldn't check — try again."
1042+ : "Check GitHub Releases for a newer build." }
1043+ </ span >
1044+ </ div >
1045+ { updateState === "available" ? (
1046+ < Button
1047+ variant = "ghost"
1048+ size = "sm"
1049+ onClick = { installUpdate }
1050+ className = "h-auto px-3 py-1 text-xs text-amber-400 hover:text-amber-300"
1051+ >
1052+ Install & relaunch
1053+ </ Button >
1054+ ) : (
1055+ < Button
1056+ variant = "ghost"
1057+ size = "sm"
1058+ onClick = { checkForUpdates }
1059+ disabled = { updateState === "checking" || updateState === "installing" }
1060+ className = "h-auto px-3 py-1 text-xs text-slate-400 hover:text-slate-200"
1061+ >
1062+ { updateState === "checking" ? "Checking…" : "Check for updates" }
1063+ </ Button >
1064+ ) }
1065+ </ div >
1066+
1067+ < Divider />
1068+
9911069 { /* Links */ }
9921070 < div className = "flex flex-col gap-2 py-4" >
9931071 < h4 className = "text-xs font-semibold uppercase tracking-wider text-slate-400 mb-1" > Links</ h4 >
0 commit comments