diff --git a/Lattice.Core/Abstractions/IContextService.cs b/Lattice.Core/Abstractions/IContextService.cs
new file mode 100644
index 0000000..67537ab
--- /dev/null
+++ b/Lattice.Core/Abstractions/IContextService.cs
@@ -0,0 +1,27 @@
+namespace Lattice.Core.Abstractions;
+
+///
+/// Сервис управления контекстом приложения и связанными командами.
+///
+public interface IContextService
+{
+ ///
+ /// Имя текущего активного контекста.
+ ///
+ string CurrentContext { get; }
+
+ ///
+ /// Возникает при смене фокуса между вкладками с разными ContextGroup.
+ ///
+ event EventHandler? ContextChanged;
+
+ ///
+ /// Устанавливает активный контекст. Вызывается UI-слоем при активации вкладки.
+ ///
+ void SetContext(string contextGroup);
+
+ ///
+ /// Проверяет, должна ли команда быть видимой в текущем контексте.
+ ///
+ bool IsCommandVisible(string commandId, string commandContext);
+}
diff --git a/Lattice.Core/Abstractions/IDockableComponent.cs b/Lattice.Core/Abstractions/IDockableComponent.cs
new file mode 100644
index 0000000..3aac000
--- /dev/null
+++ b/Lattice.Core/Abstractions/IDockableComponent.cs
@@ -0,0 +1,33 @@
+namespace Lattice.Core.Abstractions;
+
+///
+/// Описывает компонент, который может быть размещен внутри узла компоновки Lattice.
+///
+public interface IDockableComponent
+{
+ ///
+ /// Уникальный строковый идентификатор компонента (например, "SolutionExplorer").
+ ///
+ string UniqueId { get; }
+
+ ///
+ /// Заголовок, отображаемый на вкладке или в заголовке панели.
+ ///
+ string DisplayName { get; }
+
+ ///
+ /// Ключ иконки (для Segoe Fluent Icons или путей к ресурсам).
+ ///
+ string? IconKey { get; }
+
+ ///
+ /// Группа контекста (например, "CodeEditor", "Debugger").
+ /// Определяет, какие панели инструментов будут активны.
+ ///
+ string ContextGroup { get; }
+
+ ///
+ /// Указывает, разрешено ли закрывать данный компонент пользователем.
+ ///
+ bool CanClose { get; }
+}
diff --git a/Lattice.Core/Abstractions/ILayoutElement.cs b/Lattice.Core/Abstractions/ILayoutElement.cs
new file mode 100644
index 0000000..bf70297
--- /dev/null
+++ b/Lattice.Core/Abstractions/ILayoutElement.cs
@@ -0,0 +1,42 @@
+namespace Lattice.Core.Abstractions;
+
+///
+/// Представляет базовый элемент иерархии компоновки Lattice.
+///
+public interface ILayoutElement
+{
+ ///
+ /// Уникальный идентификатор элемента.
+ ///
+ Guid Id { get; }
+
+ ///
+ /// Имя элемента для отображения или идентификации в логах.
+ ///
+ string Name { get; set; }
+
+ ///
+ /// Значение ширины (в пикселях или долях "star").
+ ///
+ double WidthValue { get; set; }
+
+ ///
+ /// Указывает, является ли ширина пропорциональной (star).
+ ///
+ bool IsWidthStar { get; set; }
+
+ ///
+ /// Значение высоты (в пикселях или долях "star").
+ ///
+ double HeightValue { get; set; }
+
+ ///
+ /// Указывает, является ли высота пропорциональной (star).
+ ///
+ bool IsHeightStar { get; set; }
+
+ ///
+ /// Родительский элемент в дереве компоновки.
+ ///
+ ILayoutElement? Parent { get; set; }
+}
diff --git a/Lattice.Core/Abstractions/ILayoutService.cs b/Lattice.Core/Abstractions/ILayoutService.cs
new file mode 100644
index 0000000..1653dee
--- /dev/null
+++ b/Lattice.Core/Abstractions/ILayoutService.cs
@@ -0,0 +1,40 @@
+using Lattice.Core.Models;
+using Lattice.Core.Models.Enums;
+
+namespace Lattice.Core.Abstractions;
+
+///
+/// Сервис управления жизненным циклом макета приложения.
+///
+public interface ILayoutService
+{
+ ///
+ /// Текущий корневой узел всей структуры окон.
+ ///
+ LayoutNode? Root { get; }
+
+ ///
+ /// Событие, возникающее при любом изменении структуры (докинг, закрытие, изменение размеров).
+ ///
+ event EventHandler? LayoutUpdated;
+
+ ///
+ /// Перемещает узел в указанную позицию относительно целевого узла.
+ ///
+ void Dock(LayoutNode source, LayoutNode target, DockDirection direction);
+
+ ///
+ /// Удаляет узел из макета (например, при закрытии вкладки).
+ ///
+ void Remove(LayoutNode node);
+
+ ///
+ /// Импортирует структуру макета из снапшота.
+ ///
+ void LoadLayout(string jsonData);
+
+ ///
+ /// Экспортирует текущую структуру в строку для сохранения.
+ ///
+ string SaveLayout();
+}
diff --git a/Lattice.Core/Context/ContextManager.cs b/Lattice.Core/Context/ContextManager.cs
new file mode 100644
index 0000000..84bc996
--- /dev/null
+++ b/Lattice.Core/Context/ContextManager.cs
@@ -0,0 +1,39 @@
+using Lattice.Core.Abstractions;
+
+namespace Lattice.Core.Context;
+
+///
+/// Реализация сервиса управления контекстом приложения.
+///
+public class ContextManager : IContextService
+{
+ private string _currentContext = "Common";
+
+ ///
+ public string CurrentContext => _currentContext;
+
+ ///
+ public event EventHandler? ContextChanged;
+
+ ///
+ public void SetContext(string contextGroup)
+ {
+ if (string.IsNullOrWhiteSpace(contextGroup)) contextGroup = "Common";
+
+ if (_currentContext != contextGroup)
+ {
+ _currentContext = contextGroup;
+ ContextChanged?.Invoke(this, contextGroup);
+ }
+ }
+
+ ///
+ public bool IsCommandVisible(string commandId, string commandContext)
+ {
+ // Базовая логика: команда видима, если её контекст совпадает с текущим
+ // или если команда помечена как общая ("Common" или "Global").
+ return commandContext == "Common" ||
+ commandContext == "Global" ||
+ commandContext == _currentContext;
+ }
+}
diff --git a/Lattice.Core/Engine/LayoutManager.cs b/Lattice.Core/Engine/LayoutManager.cs
new file mode 100644
index 0000000..7637d4c
--- /dev/null
+++ b/Lattice.Core/Engine/LayoutManager.cs
@@ -0,0 +1,165 @@
+using Lattice.Core.Abstractions;
+using Lattice.Core.Models;
+using Lattice.Core.Models.Enums;
+using Microsoft.Extensions.Logging;
+
+namespace Lattice.Core.Engine;
+
+///
+/// Реализация сервиса управления макетом.
+///
+public class LayoutManager : ILayoutService
+{
+ private readonly ILogger? _logger;
+ private LayoutNode? _root;
+
+ ///
+ public LayoutNode? Root => _root;
+
+ ///
+ public event EventHandler? LayoutUpdated;
+
+ public LayoutManager(ILogger? logger = null)
+ {
+ _logger = logger;
+ }
+
+ ///
+ public void Dock(LayoutNode source, LayoutNode target, DockDirection direction)
+ {
+ if (source == target) return;
+
+ _logger?.LogDebug("Начало трансформации дерева: {Source} -> {Target} ({Direction})", source.Name, target.Name, direction);
+
+ // 1. Извлекаем источник из его текущего места в дереве
+ Remove(source);
+
+ // 2. Если докинг в центр — это логика объединения (например, в TabView)
+ // В рамках Core это может означать добавление в тот же контейнер
+ if (direction == DockDirection.Center)
+ {
+ HandleCenterDock(source, target);
+ }
+ else
+ {
+ // 3. Создаем разделение (Split)
+ HandleSideDock(source, target, direction);
+ }
+
+ LayoutUpdated?.Invoke(this, EventArgs.Empty);
+ }
+
+ ///
+ /// Логика добавления элемента в центральную часть (вкладки).
+ ///
+ private void HandleCenterDock(LayoutNode source, LayoutNode target)
+ {
+ if (target.Parent is SplitContainerNode parent)
+ {
+ parent.AddChild(source);
+ }
+ else if (target == _root)
+ {
+ // Если таргет - корень, и мы докаем в центр, создаем контейнер по умолчанию
+ var container = new SplitContainerNode(SplitOrientation.Horizontal);
+ _root = container;
+ container.AddChild(target);
+ container.AddChild(source);
+ }
+ }
+
+ ///
+ /// Логика разделения существующей области на две (Side Dock).
+ ///
+ private void HandleSideDock(LayoutNode source, LayoutNode target, DockDirection direction)
+ {
+ var orientation = (direction == DockDirection.Left || direction == DockDirection.Right)
+ ? SplitOrientation.Horizontal
+ : SplitOrientation.Vertical;
+
+ var parent = target.Parent as SplitContainerNode;
+
+ // Создаем новый сплиттер, который заменит target
+ var newContainer = new SplitContainerNode(orientation);
+
+ if (parent != null)
+ {
+ // Заменяем target на новый контейнер в списке детей родителя
+ int index = parent.Children.IndexOf(target);
+ parent.Children[index] = newContainer;
+ newContainer.Parent = parent;
+ }
+ else
+ {
+ // Если родителя нет, значит target был корнем
+ _root = newContainer;
+ }
+
+ // Настраиваем порядок в новом сплиттере
+ if (direction == DockDirection.Left || direction == DockDirection.Top)
+ {
+ newContainer.AddChild(source);
+ newContainer.AddChild(target);
+ }
+ else
+ {
+ newContainer.AddChild(target);
+ newContainer.AddChild(source);
+ }
+
+ // Корректируем размеры (например, делим пополам)
+ source.WidthValue = 0.5;
+ target.WidthValue = 0.5;
+ source.IsWidthStar = true;
+ target.IsWidthStar = true;
+ }
+
+ ///
+ public void Remove(LayoutNode node)
+ {
+ if (node.Parent is SplitContainerNode parent)
+ {
+ parent.Children.Remove(node);
+ node.Parent = null;
+
+ // Если в контейнере остался один элемент — убираем лишнюю вложенность
+ if (parent.Children.Count == 1)
+ {
+ CollapseContainer(parent);
+ }
+ }
+ else if (node == _root)
+ {
+ _root = null;
+ }
+
+ LayoutUpdated?.Invoke(this, EventArgs.Empty);
+ }
+
+ ///
+ /// Убирает ненужные контейнеры, если в них остался только один элемент.
+ ///
+ private void CollapseContainer(SplitContainerNode container)
+ {
+ var lastChild = container.Children[0];
+ var parent = container.Parent as SplitContainerNode;
+
+ if (parent != null)
+ {
+ int index = parent.Children.IndexOf(container);
+ parent.Children[index] = lastChild;
+ lastChild.Parent = parent;
+ }
+ else
+ {
+ _root = lastChild;
+ lastChild.Parent = null;
+ }
+ }
+
+ ///
+ public string SaveLayout() { /* Реализация через JsonConverter */ return string.Empty; }
+
+ ///
+ public void LoadLayout(string jsonData) { /* Реализация через JsonConverter */ }
+}
diff --git a/Lattice.Core/Lattice.Core.csproj b/Lattice.Core/Lattice.Core.csproj
new file mode 100644
index 0000000..e9f25d4
--- /dev/null
+++ b/Lattice.Core/Lattice.Core.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+ net8.0;net9.0;net10.0
+ enable
+ enable
+ Lattice.Core
+ Lattice.Core
+
+
+ FrigaT
+ FrigaT
+ https://git.frigat.duckdns.org/FrigaT/Lattice
+ https://git.frigat.duckdns.org/FrigaT/Lattice
+ Core docking and layout engine for Lattice UI (WinUI 3 / Uno Platform).
+
+
+ true
+ latest
+
+
+
+
+
+
+
+
diff --git a/Lattice.Core/Models/ActionDefinition.cs b/Lattice.Core/Models/ActionDefinition.cs
new file mode 100644
index 0000000..7e11bab
--- /dev/null
+++ b/Lattice.Core/Models/ActionDefinition.cs
@@ -0,0 +1,16 @@
+namespace Lattice.Core.Models;
+
+///
+/// Определение команды для панели инструментов или меню.
+///
+public class ActionDefinition
+{
+ public string Id { get; init; } = string.Empty;
+ public string Label { get; init; } = string.Empty;
+ public string IconKey { get; init; } = string.Empty;
+
+ ///
+ /// Контекст, в котором эта кнопка должна быть доступна (например, "C#", "XAML", "Common").
+ ///
+ public string TargetContext { get; init; } = "Common";
+}
diff --git a/Lattice.Core/Models/ContentNode.cs b/Lattice.Core/Models/ContentNode.cs
new file mode 100644
index 0000000..441edba
--- /dev/null
+++ b/Lattice.Core/Models/ContentNode.cs
@@ -0,0 +1,29 @@
+using Lattice.Core.Abstractions;
+
+namespace Lattice.Core.Models;
+
+///
+/// Узел, представляющий конечный контент (вкладку, панель инструментов или документ).
+///
+public class ContentNode : LayoutNode
+{
+ ///
+ /// Ссылка на визуальный или логический компонент, закрепленный в этом узле.
+ ///
+ public IDockableComponent? Component { get; set; }
+
+ ///
+ /// Указывает, является ли данный узел частью основной рабочей области документов.
+ ///
+ public bool IsDocumentArea { get; set; }
+
+ ///
+ /// Инициализирует новый экземпляр на основе компонента.
+ ///
+ /// Компонент содержимого.
+ public ContentNode(IDockableComponent component)
+ {
+ Component = component;
+ Name = component.DisplayName;
+ }
+}
diff --git a/Lattice.Core/Models/Enums/DockDirection.cs b/Lattice.Core/Models/Enums/DockDirection.cs
new file mode 100644
index 0000000..12d797a
--- /dev/null
+++ b/Lattice.Core/Models/Enums/DockDirection.cs
@@ -0,0 +1,11 @@
+namespace Lattice.Core.Models.Enums;
+
+public enum DockDirection
+{
+ Center,
+ Left,
+ Right,
+ Top,
+ Bottom,
+ Floating,
+}
\ No newline at end of file
diff --git a/Lattice.Core/Models/Enums/SplitOrientation.cs b/Lattice.Core/Models/Enums/SplitOrientation.cs
new file mode 100644
index 0000000..42d3f67
--- /dev/null
+++ b/Lattice.Core/Models/Enums/SplitOrientation.cs
@@ -0,0 +1,13 @@
+namespace Lattice.Core.Models.Enums;
+
+public enum SplitOrientation
+{
+ ///
+ /// Элементы располагаются друг за другом по горизонтали
+ ///
+ Horizontal,
+ ///
+ /// Элементы располагаются друг за другом по вертикали
+ ///
+ Vertical,
+}
\ No newline at end of file
diff --git a/Lattice.Core/Models/LayoutNode.cs b/Lattice.Core/Models/LayoutNode.cs
new file mode 100644
index 0000000..8da7743
--- /dev/null
+++ b/Lattice.Core/Models/LayoutNode.cs
@@ -0,0 +1,35 @@
+using Lattice.Core.Abstractions;
+
+namespace Lattice.Core.Models;
+
+///
+/// Абстрактный базовый класс для всех узлов дерева компоновки.
+///
+public abstract class LayoutNode : ILayoutElement
+{
+ ///
+ public Guid Id { get; } = Guid.NewGuid();
+
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ public double WidthValue { get; set; } = 1.0;
+
+ ///
+ public bool IsWidthStar { get; set; } = true;
+
+ ///
+ public double HeightValue { get; set; } = 1.0;
+
+ ///
+ public bool IsHeightStar { get; set; } = true;
+
+ ///
+ public ILayoutElement? Parent { get; set; }
+
+ ///
+ /// Возвращает строковое представление узла для отладки.
+ ///
+ public override string ToString() => $"{GetType().Name} [{Name}] ({Id.ToString()[..4]})";
+}
diff --git a/Lattice.Core/Models/SplitContainerNode.cs b/Lattice.Core/Models/SplitContainerNode.cs
new file mode 100644
index 0000000..64e0d6c
--- /dev/null
+++ b/Lattice.Core/Models/SplitContainerNode.cs
@@ -0,0 +1,38 @@
+using Lattice.Core.Models.Enums;
+
+namespace Lattice.Core.Models;
+
+///
+/// Узел-контейнер, разделяющий пространство между дочерними элементами в определенной ориентации.
+///
+public class SplitContainerNode : LayoutNode
+{
+ ///
+ /// Ориентация разделения (горизонтальная или вертикальная).
+ ///
+ public SplitOrientation Orientation { get; set; }
+
+ ///
+ /// Список дочерних узлов, находящихся внутри данного контейнера.
+ ///
+ public List Children { get; } = new();
+
+ ///
+ /// Инициализирует новый экземпляр .
+ ///
+ /// Ориентация контейнера.
+ public SplitContainerNode(SplitOrientation orientation)
+ {
+ Orientation = orientation;
+ }
+
+ ///
+ /// Добавляет дочерний узел в контейнер и устанавливает связь с родителем.
+ ///
+ /// Узел для добавления.
+ public void AddChild(LayoutNode child)
+ {
+ child.Parent = this;
+ Children.Add(child);
+ }
+}
diff --git a/Lattice.Core/Models/WorkspaceSnapshot.cs b/Lattice.Core/Models/WorkspaceSnapshot.cs
new file mode 100644
index 0000000..ac7bef4
--- /dev/null
+++ b/Lattice.Core/Models/WorkspaceSnapshot.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lattice.Core.Models
+{
+ internal class WorkspaceSnapshot
+ {
+ }
+}
diff --git a/Lattice.slnx b/Lattice.slnx
new file mode 100644
index 0000000..02e1f68
--- /dev/null
+++ b/Lattice.slnx
@@ -0,0 +1,3 @@
+
+
+