diff --git a/Lattice.Core/Engine/LayoutManager.cs b/Lattice.Core/Engine/LayoutManager.cs
index 7637d4c..5c87cc0 100644
--- a/Lattice.Core/Engine/LayoutManager.cs
+++ b/Lattice.Core/Engine/LayoutManager.cs
@@ -1,7 +1,10 @@
using Lattice.Core.Abstractions;
using Lattice.Core.Models;
using Lattice.Core.Models.Enums;
+using Lattice.Core.Persistence;
using Microsoft.Extensions.Logging;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace Lattice.Core.Engine;
@@ -158,8 +161,68 @@ public class LayoutManager : ILayoutService
}
///
- public string SaveLayout() { /* Реализация через JsonConverter */ return string.Empty; }
+ public string SaveLayout()
+ {
+ if (_root == null) return string.Empty;
+
+ var options = GetJsonOptions();
+ try
+ {
+ string json = JsonSerializer.Serialize(_root, options);
+ _logger?.LogInformation("Макет успешно экспортирован в JSON. Длина: {Length}", json.Length);
+ return json;
+ }
+ catch (Exception ex)
+ {
+ _logger?.LogError(ex, "Ошибка при сохранении макета Lattice");
+ return string.Empty;
+ }
+ }
///
- public void LoadLayout(string jsonData) { /* Реализация через JsonConverter */ }
+ public void LoadLayout(string jsonData)
+ {
+ if (string.IsNullOrWhiteSpace(jsonData)) return;
+
+ var options = GetJsonOptions();
+ try
+ {
+ var importedRoot = JsonSerializer.Deserialize(jsonData, options);
+ if (importedRoot != null)
+ {
+ // При загрузке нужно восстановить связи Parent, так как они не сериализуются (циклические ссылки)
+ RestoreParentLinks(importedRoot, null);
+ _root = importedRoot;
+ _logger?.LogInformation("Макет успешно загружен. Корневой узел: {Id}", _root.Id);
+ LayoutUpdated?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger?.LogError(ex, "Ошибка при десериализации макета Lattice");
+ }
+ }
+
+ private JsonSerializerOptions GetJsonOptions()
+ {
+ return new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ Converters = { new LayoutJsonConverter() },
+ // Игнорируем циклы, так как мы восстановим Parent вручную
+ ReferenceHandler = ReferenceHandler.IgnoreCycles
+ };
+ }
+
+ private void RestoreParentLinks(LayoutNode node, LayoutNode? parent)
+ {
+ node.Parent = parent;
+ if (node is SplitContainerNode container)
+ {
+ foreach (var child in container.Children)
+ {
+ RestoreParentLinks(child, container);
+ }
+ }
+ }
}
diff --git a/Lattice.Core/Models/ActionDefinition.cs b/Lattice.Core/Models/ActionDefinition.cs
index 7e11bab..91ec363 100644
--- a/Lattice.Core/Models/ActionDefinition.cs
+++ b/Lattice.Core/Models/ActionDefinition.cs
@@ -1,16 +1,32 @@
namespace Lattice.Core.Models;
///
-/// Определение команды для панели инструментов или меню.
+/// Определение действия (команды), которое может быть отображено в интерфейсе.
///
-public class ActionDefinition
+public record ActionDefinition
{
- public string Id { get; init; } = string.Empty;
- public string Label { get; init; } = string.Empty;
- public string IconKey { get; init; } = string.Empty;
+ ///
+ /// Уникальный идентификатор команды.
+ ///
+ public string Id { get; init; } = Guid.NewGuid().ToString();
///
- /// Контекст, в котором эта кнопка должна быть доступна (например, "C#", "XAML", "Common").
+ /// Текст кнопки.
+ ///
+ public string Label { get; init; } = "Action";
+
+ ///
+ /// Группа контекста, к которой привязана кнопка (например, "CodeEditor").
///
public string TargetContext { get; init; } = "Common";
+
+ ///
+ /// Указывает, активна ли кнопка в данный момент.
+ ///
+ public bool IsEnabled { get; set; } = true;
+
+ ///
+ /// Подсказка (Tooltip).
+ ///
+ public string Tooltip { get; init; } = string.Empty;
}
diff --git a/Lattice.Core/Persistence/LayoutJsonConverter.cs b/Lattice.Core/Persistence/LayoutJsonConverter.cs
new file mode 100644
index 0000000..5b50f0a
--- /dev/null
+++ b/Lattice.Core/Persistence/LayoutJsonConverter.cs
@@ -0,0 +1,31 @@
+using Lattice.Core.Models;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Lattice.Core.Persistence;
+
+///
+/// Конвертер для полиморфной сериализации и десериализации узлов дерева Lattice.
+///
+public class LayoutJsonConverter : JsonConverter
+{
+ public override LayoutNode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ using var jsonDoc = JsonDocument.ParseValue(ref reader);
+ var rootElement = jsonDoc.RootElement;
+
+ // Определяем тип узла по наличию специфических свойств
+ if (rootElement.TryGetProperty("Orientation", out _))
+ {
+ return JsonSerializer.Deserialize(rootElement.GetRawText(), options);
+ }
+
+ return JsonSerializer.Deserialize(rootElement.GetRawText(), options);
+ }
+
+ public override void Write(Utf8JsonWriter writer, LayoutNode value, JsonSerializerOptions options)
+ {
+ // Используем стандартную сериализацию для конкретных типов
+ JsonSerializer.Serialize(writer, (object)value, value.GetType(), options);
+ }
+}
diff --git a/Lattice.Core/README.md b/Lattice.Core/README.md
new file mode 100644
index 0000000..1d16b23
--- /dev/null
+++ b/Lattice.Core/README.md
@@ -0,0 +1,52 @@
+# Lattice.Core
+
+[](#)
+[](git.frigat.duckdns.org)
+[](#)
+
+**Lattice.Core** — это платформонезависимое ядро (Layout Engine) для построения сложных интерфейсов с системой докинга в стиле Visual Studio 2026.
+
+Библиотека является частью экосистемы **Lattice** и отвечает исключительно за математику макета, управление деревом узлов и контекстное состояние, не имея зависимостей от конкретных UI-фреймворков.
+
+## 🚀 Особенности
+
+- **Агностическая архитектура**: Полная совместимость с .NET 8+, WinUI 3 и Uno Platform.
+- **Древовидная компоновка**: Управление интерфейсом через узлы (`Split` и `Content`).
+- **Context-Aware System**: Встроенный сервис отслеживания контекста для динамического переключения панелей инструментов.
+- **Smart Docking**: Алгоритмы автоматического разделения зон и схлопывания пустых контейнеров.
+- **JSON Persistence**: Полиморфная сериализация макетов для сохранения и загрузки состояний пользователя.
+
+## 📁 Структура проекта
+
+* `Abstractions/` — Интерфейсы для расширения системы.
+* `Models/` — Базовые сущности дерева (узлы, направления, ориентация).
+* `Engine/` — `LayoutManager`, реализующий логику трансформации дерева.
+* `Context/` — Сервисы управления активными состояниями и командами.
+* `Persistence/` — Логика сохранения макета в JSON.
+
+## 🛠 Использование
+
+### Создание базового макета
+
+```csharp
+var layoutManager = new LayoutManager();
+
+// Создаем контентные узлы
+var explorer = new ContentNode(new MyToolComponent("Solution Explorer", "Explorer"));
+var editor = new ContentNode(new MyDocumentComponent("Main.cs", "CodeEditor"));
+
+// Устанавливаем редактор как корень
+layoutManager.SetRoot(editor);
+
+// Прикрепляем проводник слева от редактора
+layoutManager.Dock(explorer, editor, DockDirection.Left);
+
+//Переключение контекста
+var contextService = new ContextManager();
+
+// Вызывается при активации вкладки в UI
+contextService.SetContext("CodeEditor");
+
+// Проверка видимости команд в текущем контексте
+bool isDebugVisible = contextService.IsCommandVisible("btnDebug", "CodeEditor");
+```
\ No newline at end of file