diff --git a/Services/LayoutStore.Snapshots.cs b/Services/LayoutStore.Snapshots.cs
index 3846174..b6042ff 100644
--- a/Services/LayoutStore.Snapshots.cs
+++ b/Services/LayoutStore.Snapshots.cs
@@ -56,10 +56,18 @@ public partial class LayoutStore : IDisposable
/// after its factory scenes have been seeded.
public void PrepareLayoutSnapshots(string buildId)
{
- if (GetSnapshotState(DefaultsInitializedKey) != "1" && HasStoredScenes())
+ ArgumentException.ThrowIfNullOrWhiteSpace(buildId);
+ var defaultsInitialized = GetSnapshotState(DefaultsInitializedKey) == "1";
+ if (!defaultsInitialized && HasStoredScenes())
+ {
CaptureDefaults();
+ defaultsInitialized = true;
+ }
- if (string.IsNullOrWhiteSpace(GetSnapshotState(CurrentBuildIdKey)))
+ var currentBuildId = GetSnapshotState(CurrentBuildIdKey);
+ if (defaultsInitialized && !string.Equals(currentBuildId, buildId, StringComparison.Ordinal))
+ RestoreDefaults(buildId);
+ else if (!defaultsInitialized && string.IsNullOrWhiteSpace(currentBuildId))
SetSnapshotState(CurrentBuildIdKey, buildId);
}
@@ -121,6 +129,45 @@ public partial class LayoutStore : IDisposable
tx.Commit();
}
+ private void RestoreDefaults(string buildId)
+ {
+ var columnsByTable = SnapshotTables.ToDictionary(table => table, ReadColumnNames);
+ using var tx = _connection.BeginTransaction();
+
+ foreach (var table in SnapshotTables.Reverse())
+ {
+ using var delete = _connection.CreateCommand();
+ delete.Transaction = tx;
+ delete.CommandText = $"DELETE FROM {table};";
+ delete.ExecuteNonQuery();
+ }
+
+ foreach (var table in SnapshotTables)
+ {
+ var columns = string.Join(", ", columnsByTable[table].Select(QuoteIdentifier));
+ using var copy = _connection.CreateCommand();
+ copy.Transaction = tx;
+ copy.CommandText = $"INSERT INTO {table} ({columns}) SELECT {columns} FROM Default{table};";
+ copy.ExecuteNonQuery();
+ }
+
+ using (var clearSettings = _connection.CreateCommand())
+ {
+ clearSettings.Transaction = tx;
+ clearSettings.CommandText = "DELETE FROM Settings WHERE Key LIKE 'BackgroundPath_%' OR Key LIKE 'BackgroundUseDefault_%';";
+ clearSettings.ExecuteNonQuery();
+ }
+ using (var copySettings = _connection.CreateCommand())
+ {
+ copySettings.Transaction = tx;
+ copySettings.CommandText = "INSERT INTO Settings (Key, Value) SELECT Key, Value FROM DefaultLayoutSetting;";
+ copySettings.ExecuteNonQuery();
+ }
+
+ SetSnapshotState(CurrentBuildIdKey, buildId, tx);
+ tx.Commit();
+ }
+
private bool HasStoredScenes()
{
using var cmd = _connection.CreateCommand();
@@ -136,9 +183,10 @@ public partial class LayoutStore : IDisposable
return cmd.ExecuteScalar() as string;
}
- private void SetSnapshotState(string key, string value)
+ private void SetSnapshotState(string key, string value, SqliteTransaction? transaction = null)
{
using var cmd = _connection.CreateCommand();
+ cmd.Transaction = transaction;
cmd.CommandText = """
INSERT INTO LayoutSnapshotState (Key, Value) VALUES ($key, $value)
ON CONFLICT(Key) DO UPDATE SET Value = excluded.Value;
diff --git a/ytLive.Tests/LayoutStoreSnapshotTests.cs b/ytLive.Tests/LayoutStoreSnapshotTests.cs
index dc17c4a..3febf26 100644
--- a/ytLive.Tests/LayoutStoreSnapshotTests.cs
+++ b/ytLive.Tests/LayoutStoreSnapshotTests.cs
@@ -48,6 +48,38 @@ public sealed class LayoutStoreSnapshotTests
}
}
+ [Fact]
+ public void DifferentBuild_RestoresTheDefaultSnapshotBeforeLoading()
+ {
+ var path = Path.Combine(Path.GetTempPath(), $"ytLlive-layout-snapshot-{Guid.NewGuid():N}.db");
+ try
+ {
+ using (var store = new LayoutStore(path))
+ {
+ store.Save(new[] { MakeScene("Factory title") }, null, null);
+ store.PrepareLayoutSnapshots("build-a");
+ store.Save(new[] { MakeScene("Creator edit") }, null, null);
+ }
+
+ using (var reopened = new LayoutStore(path))
+ {
+ reopened.PrepareLayoutSnapshots("build-b");
+ var restored = Assert.Single(reopened.Load());
+ Assert.Equal("Factory title", Assert.Single(restored.Elements.OfType()).Name);
+
+ using var connection = GetConnection(path);
+ using var command = connection.CreateCommand();
+ command.CommandText = "SELECT Value FROM LayoutSnapshotState WHERE Key = 'CurrentBuildId';";
+ Assert.Equal("build-b", command.ExecuteScalar());
+ }
+ }
+ finally
+ {
+ SqliteConnection.ClearAllPools();
+ try { File.Delete(path); } catch { /* best-effort cleanup */ }
+ }
+ }
+
private static Scene MakeScene(string sourceName)
=> new()
{