Upload via GUI (298 Dateien)

This commit is contained in:
Git Manager GUI
2026-08-07 08:53:16 +02:00
parent 12dfec7330
commit 1958a47b3a
8 changed files with 9832 additions and 8 deletions

View File

@@ -27,6 +27,11 @@ class MainForm : Form
private readonly WebView2 _web = new() { Dock = DockStyle.Fill };
private const string Host = "mystart.local";
private bool _pendingMaximize;
private System.Windows.Forms.Timer? _syncTimer;
private string? _syncFilePath; // OneDrive\MyStart\sync.json, or null if no OneDrive found
private string? _syncMarkerPath; // local record of what we last pushed/pulled, survives restarts
private string? _lastSyncedDataRaw;
private bool _syncInProgress;
public MainForm(bool startMinimized = false)
{
@@ -90,6 +95,26 @@ class MainForm : Form
"MyStart");
Directory.CreateDirectory(dataDir);
// Optional OneDrive sync: if OneDrive is set up on this PC, drop a JSON
// snapshot of the whole app state (all Reiter/profiles) in a MyStart
// subfolder there. OneDrive itself does the actual transfer between
// PCs; we just poll that file. No OneDrive -> sync silently stays off.
string? oneDriveRoot = Environment.GetEnvironmentVariable("OneDrive")
?? Environment.GetEnvironmentVariable("OneDriveConsumer")
?? Environment.GetEnvironmentVariable("OneDriveCommercial");
if (!string.IsNullOrEmpty(oneDriveRoot))
{
try
{
string syncDir = Path.Combine(oneDriveRoot, "MyStart");
Directory.CreateDirectory(syncDir);
_syncFilePath = Path.Combine(syncDir, "sync.json");
_syncMarkerPath = Path.Combine(dataDir, "sync-marker.json");
_lastSyncedDataRaw = File.Exists(_syncMarkerPath) ? File.ReadAllText(_syncMarkerPath) : null;
}
catch { _syncFilePath = null; }
}
var env = await CoreWebView2Environment.CreateAsync(null, dataDir);
await _web.EnsureCoreWebView2Async(env);
@@ -171,9 +196,122 @@ class MainForm : Form
}
};
// Kick a sync check after every completed load (first start, and any
// reload the sync pull/push itself triggers), then keep polling.
core.NavigationCompleted += async (_, e) =>
{
if (!e.IsSuccess) return;
await SyncTickAsync(isClosing: false);
if (_syncTimer == null && _syncFilePath != null)
{
_syncTimer = new System.Windows.Forms.Timer { Interval = 30_000 };
_syncTimer.Tick += async (_, _) => await SyncTickAsync(isClosing: false);
_syncTimer.Start();
}
};
core.Navigate("https://" + Host + "/index.html");
}
// --- OneDrive sync tick: export current state from the page (opaque JSON
// blob, the page understands its own structure -- we don't), compare with
// what's on disk, and push or pull whichever side is behind. "Local
// changed since last sync" always wins a push; a pull is skipped while
// the window is actively focused so we never yank a reload out from under
// someone mid-edit -- it'll catch up on the next tick after they click away. ---
private async Task SyncTickAsync(bool isClosing)
{
if (_syncFilePath == null || _syncInProgress) return;
_syncInProgress = true;
try
{
string raw = await _web.CoreWebView2.ExecuteScriptAsync(
"window.__mystartSyncExport && window.__mystartSyncExport()");
string? exportJson = JsonSerializer.Deserialize<string>(raw);
if (string.IsNullOrEmpty(exportJson)) return;
string? localDataRaw = ExtractDataRaw(exportJson);
if (localDataRaw == null) return;
string? fileFullJson = null;
string? fileDataRaw = null;
if (File.Exists(_syncFilePath))
{
fileFullJson = await File.ReadAllTextAsync(_syncFilePath);
fileDataRaw = ExtractDataRaw(fileFullJson);
}
if (localDataRaw != _lastSyncedDataRaw)
{
// Something changed here since we last synced -> push (last writer wins).
await File.WriteAllTextAsync(_syncFilePath, exportJson);
_lastSyncedDataRaw = localDataRaw;
TryWriteMarker(localDataRaw);
PostSyncStatus(true, "gesendet");
}
else if (fileDataRaw != null && fileDataRaw != localDataRaw && !isClosing)
{
// Nothing changed locally, but the shared file moved on -> pull,
// unless we're actively being used right now.
if (!ContainsFocus || WindowState == FormWindowState.Minimized)
{
string js = "window.__mystartSyncImport && window.__mystartSyncImport(" +
JsonSerializer.Serialize(fileFullJson) + ")";
await _web.CoreWebView2.ExecuteScriptAsync(js);
_lastSyncedDataRaw = fileDataRaw;
TryWriteMarker(fileDataRaw);
PostSyncStatus(true, "aktualisiert");
}
}
else
{
PostSyncStatus(true, "aktuell");
}
}
catch (Exception ex)
{
PostSyncStatus(false, ex.Message);
}
finally
{
_syncInProgress = false;
}
}
private static string? ExtractDataRaw(string json)
{
try
{
using var doc = JsonDocument.Parse(json);
return doc.RootElement.TryGetProperty("data", out var data) ? data.GetRawText() : null;
}
catch { return null; }
}
private void TryWriteMarker(string dataRaw)
{
try { if (_syncMarkerPath != null) File.WriteAllText(_syncMarkerPath, dataRaw); }
catch { /* best effort */ }
}
private void PostSyncStatus(bool ok, string message)
{
try
{
var json = JsonSerializer.Serialize(new { type = "sync-status", ok, message });
_web.CoreWebView2.PostWebMessageAsJson(json);
}
catch { /* page may be mid-reload */ }
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
// Best-effort final push; not awaited/blocking (would risk deadlocking
// the UI thread against WebView2's own message pump on shutdown). The
// 30s poll already covers normal use -- this just shaves the tail end.
if (_syncFilePath != null) _ = SyncTickAsync(isClosing: true);
}
// --- Autostart (per-user HKCU Run key, points at THIS exe with --minimized) ---
private const string RunKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
private const string RunValueName = "MyStart";