Add update contracts, IPC progress & providers
Introduce a new update subsystem: shared contracts for manifests, messages, paths and state (LanMountainDesktop.Shared.Contracts.Update). Add IPC and reporting infrastructure for installer progress (IUpdateProgressReporter, LauncherUpdateProgressIpcServer, NullUpdateProgressReporter) and integrate progress/complete reporting into UpdateEngineService. Add multiple update service components and providers (CompositeManifestProvider, GithubReleaseManifestProvider, PlondsApiManifestProvider, UpdateDownloadEngine, UpdateOrchestrator, UpdateInstallGateway, CLI launcher bridge, launcher bridge interfaces, observable helper, state store, progress subject, JSON context). Update settings and models to support UseGhProxyMirror and PLONDS/GitHub fallback logic, plus localization strings and UI/viewmodel files for update settings and progress. Misc: installer script tweak and a small change in Plonds generator. This adds end-to-end support for checking, downloading and reporting update progress and results.
2026-05-03 19:31:04 +08:00
|
|
|
namespace LanMountainDesktop.Shared.Contracts.Update;
|
|
|
|
|
|
|
|
|
|
public static class UpdatePaths
|
|
|
|
|
{
|
Introduce render gate and chart caching
Replace UI DispatcherTimer polling with a StudySnapshotRenderGate across multiple widgets to queue and apply only the latest analytics snapshot; components updated include StudyDeductionReasonsWidget, StudyEnvironmentWidget, StudyInterruptDensityWidget, StudyNoiseCurveWidget. Add StudySnapshotRenderGate implementation to coordinate rendering and monitoring leases and update subscription/lease lifecycle handling (subscribe/unsubscribe, Acquire/Dispose leases, Clear/Dispose gate). Rewrite chart controls (StudyNoiseCurveChartControl and StudyNoiseDistributionScatterChartControl) to use stable logical-time origins, split series into static vs dynamic tails, add geometry/sample caching, stable jitter/coordinate mapping helpers, and expose internal helpers & counts for testing. Add unit tests (StudyComponentRenderingTests) covering the render gate and chart behaviors (layer counts, logical X mapping, stable jitter, cache rebuild). These changes improve rendering correctness and performance by avoiding redundant renders and enabling deterministic chart layout.
2026-05-06 16:00:45 +08:00
|
|
|
private const string LauncherDirectoryName = ".Launcher";
|
Add update contracts, IPC progress & providers
Introduce a new update subsystem: shared contracts for manifests, messages, paths and state (LanMountainDesktop.Shared.Contracts.Update). Add IPC and reporting infrastructure for installer progress (IUpdateProgressReporter, LauncherUpdateProgressIpcServer, NullUpdateProgressReporter) and integrate progress/complete reporting into UpdateEngineService. Add multiple update service components and providers (CompositeManifestProvider, GithubReleaseManifestProvider, PlondsApiManifestProvider, UpdateDownloadEngine, UpdateOrchestrator, UpdateInstallGateway, CLI launcher bridge, launcher bridge interfaces, observable helper, state store, progress subject, JSON context). Update settings and models to support UseGhProxyMirror and PLONDS/GitHub fallback logic, plus localization strings and UI/viewmodel files for update settings and progress. Misc: installer script tweak and a small change in Plonds generator. This adds end-to-end support for checking, downloading and reporting update progress and results.
2026-05-03 19:31:04 +08:00
|
|
|
private const string UpdateDirectoryName = "update";
|
|
|
|
|
private const string IncomingDirectoryName = "incoming";
|
|
|
|
|
private const string ObjectsDirectoryName = "objects";
|
|
|
|
|
private const string SnapshotsDirectoryName = "snapshots";
|
|
|
|
|
|
|
|
|
|
public static string ResolveLauncherRoot(string appBaseDirectory)
|
|
|
|
|
{
|
|
|
|
|
var trimmed = appBaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
|
|
|
|
var parent = Path.GetDirectoryName(trimmed);
|
|
|
|
|
return string.IsNullOrWhiteSpace(parent) ? appBaseDirectory : parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetLauncherDataRoot(string launcherRoot)
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(launcherRoot, LauncherDirectoryName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetIncomingDirectory(string launcherRoot)
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(launcherRoot, LauncherDirectoryName, UpdateDirectoryName, IncomingDirectoryName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetObjectsDirectory(string launcherRoot)
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(GetIncomingDirectory(launcherRoot), ObjectsDirectoryName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetSnapshotsDirectory(string launcherRoot)
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(launcherRoot, LauncherDirectoryName, SnapshotsDirectoryName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetDownloadMarkerPath(string launcherRoot)
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(GetIncomingDirectory(launcherRoot), ".download-complete");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetPlondsFileMapName() => "plonds-filemap.json";
|
|
|
|
|
public static string GetPlondsSignatureName() => "plonds-filemap.sig";
|
|
|
|
|
public static string GetPlondsUpdateMetadataName() => "plonds-update.json";
|
|
|
|
|
public static string GetLegacyFileMapName() => "files.json";
|
|
|
|
|
public static string GetLegacySignatureName() => "files.json.sig";
|
|
|
|
|
public static string GetLegacyArchiveName() => "update.zip";
|
|
|
|
|
public static string GetPublicKeyFileName() => "public-key.pem";
|
|
|
|
|
|
|
|
|
|
public static string GetPlondsFileMapPath(string launcherRoot)
|
|
|
|
|
=> Path.Combine(GetIncomingDirectory(launcherRoot), GetPlondsFileMapName());
|
|
|
|
|
|
|
|
|
|
public static string GetPlondsSignaturePath(string launcherRoot)
|
|
|
|
|
=> Path.Combine(GetIncomingDirectory(launcherRoot), GetPlondsSignatureName());
|
|
|
|
|
|
|
|
|
|
public static string GetPlondsUpdateMetadataPath(string launcherRoot)
|
|
|
|
|
=> Path.Combine(GetIncomingDirectory(launcherRoot), GetPlondsUpdateMetadataName());
|
|
|
|
|
|
|
|
|
|
public static string GetDownloadMarkerContent(string manifestSha256, string targetVersion, int objectCount)
|
|
|
|
|
{
|
|
|
|
|
return $$"""
|
|
|
|
|
{
|
|
|
|
|
"manifestSha256": "{{manifestSha256}}",
|
|
|
|
|
"targetVersion": "{{targetVersion}}",
|
|
|
|
|
"objectCount": {{objectCount}},
|
|
|
|
|
"completedAt": "{{DateTimeOffset.UtcNow:O}}"
|
|
|
|
|
}
|
|
|
|
|
""";
|
|
|
|
|
}
|
|
|
|
|
}
|