@@ -264,11 +264,13 @@ await File.WriteAllTextAsync(cachePath,
264264 if ( asset is not null ) downloadSize += asset . Size ;
265265 }
266266
267+ var changelog = ExtractChangelog ( release . Body ) ;
268+
267269 _lastCheckResult = new UpdateCheckResult
268270 {
269271 UpdateAvailable = changedCount > 0 || deletedCount > 0 ,
270272 NewVersion = remoteVersion ,
271- Changelog = release . Body ,
273+ Changelog = changelog ,
272274 DownloadSize = downloadSize ,
273275 ChangedPackages = changedPackages . ToList ( ) ,
274276 ChangedFileCount = changedCount ,
@@ -282,7 +284,7 @@ await File.WriteAllTextAsync(cachePath,
282284 await hubContext . Clients . Group ( "update" ) . SendAsync ( "UpdateAvailable" , new UpdateAvailableInfo
283285 {
284286 Version = remoteVersion ,
285- Changelog = release . Body ,
287+ Changelog = changelog ,
286288 DownloadSize = downloadSize ,
287289 ChangedPackages = changedPackages . ToList ( )
288290 } , ct ) ;
@@ -301,7 +303,7 @@ await File.WriteAllTextAsync(cachePath,
301303 catch ( Exception ex )
302304 {
303305 logger . LogError ( ex , "检查更新失败" ) ;
304- _status = new UpdateStatusInfo { State = UpdateState . Error , Error = ex . Message } ;
306+ _status = new UpdateStatusInfo { State = UpdateState . Error , Error = "检查更新失败,请检查网络连接" } ;
305307 await BroadcastStatus ( ) ;
306308 return _lastCheckResult ?? new UpdateCheckResult ( ) ;
307309 }
@@ -361,22 +363,24 @@ public async Task DownloadUpdateAsync(CancellationToken ct = default)
361363
362364 var zipPath = Path . Combine ( stagingDir , zipName ) ;
363365
364- // Download with progress
365- using var response = await client . GetAsync ( asset . BrowserDownloadUrl ,
366- HttpCompletionOption . ResponseHeadersRead , token ) ;
367- response . EnsureSuccessStatusCode ( ) ;
368-
369- await using var stream = await response . Content . ReadAsStreamAsync ( token ) ;
370- await using var fileStream = File . Create ( zipPath ) ;
371- var buffer = new byte [ 81920 ] ;
372- int bytesRead ;
373- while ( ( bytesRead = await stream . ReadAsync ( buffer , token ) ) > 0 )
366+ // Download with progress (scoped to release file handle before extraction)
374367 {
375- await fileStream . WriteAsync ( buffer . AsMemory ( 0 , bytesRead ) , token ) ;
376- downloadedBytes += bytesRead ;
377- _status . DownloadedBytes = downloadedBytes ;
378- _status . Progress = totalBytes > 0 ? ( double ) downloadedBytes / totalBytes * 100 : 0 ;
379- await BroadcastStatus ( ) ;
368+ using var response = await client . GetAsync ( asset . BrowserDownloadUrl ,
369+ HttpCompletionOption . ResponseHeadersRead , token ) ;
370+ response . EnsureSuccessStatusCode ( ) ;
371+
372+ await using var stream = await response . Content . ReadAsStreamAsync ( token ) ;
373+ await using var fileStream = File . Create ( zipPath ) ;
374+ var buffer = new byte [ 81920 ] ;
375+ int bytesRead ;
376+ while ( ( bytesRead = await stream . ReadAsync ( buffer , token ) ) > 0 )
377+ {
378+ await fileStream . WriteAsync ( buffer . AsMemory ( 0 , bytesRead ) , token ) ;
379+ downloadedBytes += bytesRead ;
380+ _status . DownloadedBytes = downloadedBytes ;
381+ _status . Progress = totalBytes > 0 ? ( double ) downloadedBytes / totalBytes * 100 : 0 ;
382+ await BroadcastStatus ( ) ;
383+ }
380384 }
381385
382386 logger . LogInformation ( "已下载: {Name} ({Size:N0} bytes)" , zipName , new FileInfo ( zipPath ) . Length ) ;
@@ -471,8 +475,9 @@ await File.WriteAllTextAsync(updateManifestPath,
471475 logger . LogError ( ex , "下载更新失败" ) ;
472476 if ( Directory . Exists ( stagingDir ) )
473477 Directory . Delete ( stagingDir , true ) ;
474- _status = new UpdateStatusInfo { State = UpdateState . Error , Error = ex . Message } ;
478+ _status = new UpdateStatusInfo { State = UpdateState . Error , Error = "下载更新失败,请重试" } ;
475479 await BroadcastStatus ( ) ;
480+ trayService . ShowNotification ( "XUnityToolkit" , "下载更新失败,请重试" ) ;
476481 throw ;
477482 }
478483 finally
@@ -641,6 +646,33 @@ public async Task AutoCheckOnStartupAsync()
641646 }
642647 }
643648
649+ /// <summary>
650+ /// Extracts the "### Changelog" section from GitHub release body,
651+ /// stripping download links, installation instructions, etc.
652+ /// </summary>
653+ private static string ? ExtractChangelog ( string ? releaseBody )
654+ {
655+ if ( string . IsNullOrWhiteSpace ( releaseBody ) ) return null ;
656+
657+ var startIndex = releaseBody . IndexOf ( "### Changelog" , StringComparison . OrdinalIgnoreCase ) ;
658+ if ( startIndex < 0 ) return releaseBody ; // No Changelog section found, return full body
659+
660+ // Skip past the "### Changelog" line
661+ var contentStart = releaseBody . IndexOf ( '\n ' , startIndex ) ;
662+ if ( contentStart < 0 ) return null ;
663+ contentStart ++ ; // Skip the newline
664+
665+ // Find the end (next ## or ### section, or end of string)
666+ var endIndex = releaseBody . IndexOf ( "\n ##" , contentStart , StringComparison . Ordinal ) ;
667+
668+ var content = endIndex >= 0
669+ ? releaseBody [ contentStart ..endIndex ]
670+ : releaseBody [ contentStart ..] ;
671+
672+ var trimmed = content . Trim ( ) ;
673+ return trimmed . Length > 0 ? trimmed : null ;
674+ }
675+
644676 private Task BroadcastStatus ( )
645677 {
646678 return hubContext . Clients . Group ( "update" ) . SendAsync ( "UpdateStatus" , _status ) ;
0 commit comments