You are an expert Unity game developer with a strong focus on writing efficient, clean, and production-ready code. Your scripts are always optimized for performance and adhere to best practices. You meticulously remove all comments from your code, ensuring it's concise and self-explanatory. You possess an unwavering commitment to fully implementing all requested features and functionality. You do not compromise on implementation; partial implementations are strictly unacceptable. You are provided with a feature request or a coding task, and you will produce the complete Unity C# script that fully addresses the task's specifications. If you need clarification, ask specific questions. You understand that your code will be directly used in a production environment and that quality is paramount.
Your primary language of communication is Russian.

Here are some specific aspects to always consider:
Performance: Prioritize code that runs fast and consumes minimal resources.
Clean Code: Write code that is easy to read, understand, and maintain.
Best Practices: Utilize Unity conventions and coding standards.
No Comments: Ensure the code is self-documenting, no comments are allowed.
Complete Implementation: All features must be fully implemented as specified. Partial solutions are not allowed.
Error Handling: Include robust error handling to prevent unexpected behaviors.
Clarity: Use clear variable and function names to enhance code readability.
Efficiency: Utilize Unity APIs efficiently to avoid overhead.
Reusability: Where applicable, code should be easily reusable in different contexts.

using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
using System.Linq;
using System.Collections.Generic;
using System.IO;

public class DialogueGraphView : GraphView
{
    public DialogueGraph dialogueGraph;
    private Dictionary<string, BaseNodeElement> nodeLookup = new Dictionary<string, BaseNodeElement>();
    private string fileName = "NewDialogueGraph";
    private DialogueEditorWindow editorWindow;
    private bool isDirty = false;

    public DialogueGraphView(DialogueEditorWindow editorWindow)
    {
        this.editorWindow = editorWindow;
        
        this.StretchToParentSize();
        
        SetupZoom(ContentZoomer.DefaultMinScale * 0.5f, ContentZoomer.DefaultMaxScale * 2f);
        this.AddManipulator(new ContentDragger());
        this.AddManipulator(new SelectionDragger());
        this.AddManipulator(new RectangleSelector());
        graphViewChanged += OnGraphViewChanged;
        GridBackground grid = new GridBackground();
        Insert(0, grid);
        grid.StretchToParentSize();

        
        AddStyles();
    }
    
    private void AddStyles()
    {
        
        Toolbar toolbar = new Toolbar();
        
        TextField fileNameTextField = new TextField("Имя файла:");
        fileNameTextField.SetValueWithoutNotify(fileName);
        fileNameTextField.MarkDirtyRepaint();
        fileNameTextField.RegisterValueChangedCallback(evt => fileName = evt.newValue);
        toolbar.Add(fileNameTextField);

        Button saveButton = new Button(() => SaveGraph(fileName)) { text = "Сохранить" };
        toolbar.Add(saveButton);

        Button loadButton = new Button(() => LoadGraph(fileName)) { text = "Загрузить" };
        toolbar.Add(loadButton);

        Add(toolbar);

        
        toolbar.style.backgroundColor = new StyleColor(new Color(90f / 255f, 90f / 255f, 90f / 255f));
        toolbar.style.borderTopColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
        toolbar.style.borderBottomColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
        toolbar.style.borderTopLeftRadius = 5f;
        toolbar.style.borderTopRightRadius = 5f;
        toolbar.style.borderBottomLeftRadius = 5f;
        toolbar.style.borderBottomRightRadius = 5f;
        toolbar.style.paddingTop = 5f;
        toolbar.style.paddingBottom = 5f;
        toolbar.style.marginBottom = 5f;
        
        
        toolbar.Query<Button>().ForEach(button =>
        {
            button.style.color = new StyleColor(new Color(200f / 255f, 200f / 255f, 200f / 255f));
            button.style.backgroundColor = new StyleColor(new Color(90f / 255f, 90f / 255f, 90f / 255f));
            button.style.borderLeftColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            button.style.borderRightColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            button.style.borderTopColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            button.style.borderBottomColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            button.style.borderTopLeftRadius = 5f;
            button.style.borderTopRightRadius = 5f;
            button.style.borderBottomLeftRadius = 5f;
            button.style.borderBottomRightRadius = 5f;
            button.style.paddingLeft = 5f;
            button.style.paddingRight = 5f;
            button.style.paddingTop = 5f;
            button.style.paddingBottom = 5f;
            button.style.marginBottom = 5f;

            button.RegisterCallback<MouseEnterEvent>(evt =>
            {
                button.style.backgroundColor = new StyleColor(new Color(120f / 255f, 120f / 255f, 120f / 255f));
            });
            button.RegisterCallback<MouseLeaveEvent>(evt =>
            {
                button.style.backgroundColor = new StyleColor(new Color(90f / 255f, 90f / 255f, 90f / 255f));
            });
        });

        
        toolbar.Query<Label>().ForEach(label =>
        {
            label.style.color = new StyleColor(new Color(200f / 255f, 200f / 255f, 200f / 255f));
        });
    }
    
    private void ApplyStylesToNode(BaseNodeElement node)
    {
        if (node is DialogueNodeElement)
        {
            node.style.borderLeftColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderRightColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderTopColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderBottomColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderTopLeftRadius = 10f;
            node.style.borderTopRightRadius = 10f;
            node.style.borderBottomLeftRadius = 10f;
            node.style.borderBottomRightRadius = 10f;
            node.style.paddingLeft = 10f;
            node.style.paddingRight = 10f;
            node.style.paddingTop = 10f;
            node.style.paddingBottom = 10f;
            node.style.backgroundColor = new StyleColor(new Color(70f / 255f, 70f / 255f, 70f / 255f));

            node.titleContainer.style.backgroundColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.titleContainer.style.borderTopLeftRadius = 5f;
            node.titleContainer.style.borderTopRightRadius = 5f;
            node.titleContainer.style.paddingLeft = 10f;
            node.titleContainer.style.paddingRight = 10f;
            node.titleContainer.style.paddingTop = 5f;
            node.titleContainer.style.paddingBottom = 5f;
        }
        else if (node is StartNodeElement)
        {
            node.style.borderLeftColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderRightColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderTopColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderBottomColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderTopLeftRadius = 10f;
            node.style.borderTopRightRadius = 10f;
            node.style.borderBottomLeftRadius = 10f;
            node.style.borderBottomRightRadius = 10f;
            node.style.paddingLeft = 10f;
            node.style.paddingRight = 10f;
            node.style.paddingTop = 10f;
            node.style.paddingBottom = 10f;
            node.style.backgroundColor = new StyleColor(new Color(50f / 255f, 80f / 255f, 50f / 255f));

            node.titleContainer.style.backgroundColor = new StyleColor(new Color(30f / 255f, 60f / 255f, 30f / 255f));
            node.titleContainer.style.borderTopLeftRadius = 5f;
            node.titleContainer.style.borderTopRightRadius = 5f;
            node.titleContainer.style.paddingLeft = 10f;
            node.titleContainer.style.paddingRight = 10f;
            node.titleContainer.style.paddingTop = 5f;
            node.titleContainer.style.paddingBottom = 5f;
        }
        else if (node is EndNodeElement)
        {
            node.style.borderLeftColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderRightColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderTopColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderBottomColor = new StyleColor(new Color(50f / 255f, 50f / 255f, 50f / 255f));
            node.style.borderTopLeftRadius = 10f;
            node.style.borderTopRightRadius = 10f;
            node.style.borderBottomLeftRadius = 10f;
            node.style.borderBottomRightRadius = 10f;
            node.style.paddingLeft = 10f;
            node.style.paddingRight = 10f;
            node.style.paddingTop = 10f;
            node.style.paddingBottom = 10f;
            node.style.backgroundColor = new StyleColor(new Color(80f / 255f, 50f / 255f, 50f / 255f));

            node.titleContainer.style.backgroundColor = new StyleColor(new Color(60f / 255f, 30f / 255f, 30f / 255f));
            node.titleContainer.style.borderTopLeftRadius = 5f;
            node.titleContainer.style.borderTopRightRadius = 5f;
            node.titleContainer.style.paddingLeft = 10f;
            node.titleContainer.style.paddingRight = 10f;
            node.titleContainer.style.paddingTop = 5f;
            node.titleContainer.style.paddingBottom = 5f;
        }

        
        node.Query<Port>().ForEach(port =>
        {
            port.style.backgroundColor = new StyleColor(new Color(90f / 255f, 90f / 255f, 90f / 255f));
            port.style.borderTopLeftRadius = 5f;
            port.style.borderTopRightRadius = 5f;
            port.style.borderBottomLeftRadius = 5f;
            port.style.borderBottomRightRadius = 5f;
            port.style.paddingLeft = 5f;
            port.style.paddingRight = 5f;
            port.style.paddingTop = 5f;
            port.style.paddingBottom = 5f;
        });
    }

    public void AddNode(DialogueNodeData nodeData)
    {
        var node = CreateNodeElement(nodeData);
        ApplyStylesToNode(node);
        AddElement(node);
        node.ValidateNode();
        SetDirty();
    }

    private BaseNodeElement CreateNodeElement(DialogueNodeData nodeData)
    {
        BaseNodeElement node = null;
        if (nodeData is StartNodeData)
            node = new StartNodeElement(nodeData as StartNodeData, this);
        else if (nodeData is DialogueNodeDataWithContent)
            node = new DialogueNodeElement(nodeData as DialogueNodeDataWithContent, this);
        else if (nodeData is EndNodeData)
            node = new EndNodeElement(nodeData as EndNodeData, this);
        if (node != null)
        {
            node.SetPosition(new Rect(nodeData.position, node.GetDefaultSize()));
            nodeLookup[nodeData.id] = node;
        }
        return node;
    }

    public List<DialogueNodeData> GetAllNodeData()
    {
        return nodeLookup.Values.Select(n => n.nodeData).ToList();
    }

    public List<DialogueConnection> GetAllConnections()
    {
        List<DialogueConnection> connections = new List<DialogueConnection>();
        foreach (var edge in edges)
        {
            if (edge.output.node == null || edge.input.node == null)
                continue;
            var fromNode = edge.output.node as BaseNodeElement;
            var toNode = edge.input.node as BaseNodeElement;
            if (fromNode == null || toNode == null)
                continue;
            DialogueConnection connection = new DialogueConnection
            {
                fromNodeId = fromNode.nodeData.id,
                toNodeId = toNode.nodeData.id
            };
            connections.Add(connection);
        }
        return connections;
    }

    public void LoadGraph(string fileName)
    {
        string directory = "Assets/DialogueGraphs";
        string path = Path.Combine(directory, fileName + ".asset");
        if (!File.Exists(path))
        {
            EditorUtility.DisplayDialog("Ошибка", $"Файл не найден: {path}", "OK");
            return;
        }
        dialogueGraph = AssetDatabase.LoadAssetAtPath<DialogueGraph>(path);
        if (dialogueGraph != null)
        {
            this.fileName = fileName;
            editorWindow.SetFileName(this.fileName);
            ClearGraph();
            foreach (var nodeData in dialogueGraph.nodes)
            {
                AddNode(nodeData);
            }
            CreateConnections();
            isDirty = false;
        }
        else
        {
            EditorUtility.DisplayDialog("Ошибка", "Не удалось загрузить граф.", "OK");
        }
    }

    private void ClearGraph()
    {
        foreach (var edge in edges.ToList())
        {
            RemoveElement(edge);
        }
        foreach (var node in nodes.ToList())
        {
            RemoveElement(node);
        }
        nodeLookup.Clear();
    }

    private void CreateConnections()
    {
        foreach (var connection in dialogueGraph.connections)
        {
            if (!nodeLookup.ContainsKey(connection.fromNodeId) || !nodeLookup.ContainsKey(connection.toNodeId))
                continue;
            var fromNode = nodeLookup[connection.fromNodeId];
            var toNode = nodeLookup[connection.toNodeId];
            Port outputPort = fromNode.outputContainer.Query<Port>().ToList().FirstOrDefault(p => p.portName == "Выход");
            Port inputPort = toNode.inputContainer.Query<Port>().ToList().FirstOrDefault(p => p.portName == "Вход");
            if (outputPort == null || inputPort == null)
                continue;
            Edge edge = new Edge
            {
                output = outputPort,
                input = inputPort
            };
            edge.input.Connect(edge);
            edge.output.Connect(edge);
            Add(edge);
        }
        foreach (var node in nodeLookup.Values)
        {
            node.ValidateNode();
        }
    }

    public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
    {
        evt.menu.MenuItems().Clear();
        Vector2 nodePosition = this.WorldToLocal(evt.mousePosition);
        evt.menu.AppendAction("Создать стартовый узел", (action) => AddNode(new StartNodeData { position = nodePosition }), DropdownMenuAction.AlwaysEnabled);
        evt.menu.AppendAction("Создать диалоговый узел", (action) => AddNode(new DialogueNodeDataWithContent { position = nodePosition }), DropdownMenuAction.AlwaysEnabled);
        evt.menu.AppendAction("Создать конечный узел", (action) => AddNode(new EndNodeData { position = nodePosition }), DropdownMenuAction.AlwaysEnabled);
    }

    public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
    {
        return ports.Where(endPort => endPort.direction != startPort.direction && endPort.node != startPort.node).ToList();
    }

    public void SaveGraph(string fileName)
    {
        if (string.IsNullOrEmpty(fileName))
        {
            EditorUtility.DisplayDialog("Ошибка", "Имя файла не может быть пустым.", "OK");
            return;
        }
        if (!isDirty)
        {
            Debug.Log("Граф не был изменен, сохранение пропущено.");
            return;
        }
        dialogueGraph = ScriptableObject.CreateInstance<DialogueGraph>();
        dialogueGraph.nodes = GetAllNodeData();
        dialogueGraph.connections = GetAllConnections();
        string directory = "Assets/DialogueGraphs";
        if (!Directory.Exists(directory))
            Directory.CreateDirectory(directory);
        string backupPath = Path.Combine(directory, fileName + "_backup.asset");
        string path = Path.Combine(directory, fileName + ".asset");
        if (File.Exists(path))
        {
            if (File.Exists(backupPath))
            {
                File.Delete(backupPath);
            }
            File.Copy(path, backupPath);
        }
        AssetDatabase.CreateAsset(dialogueGraph, path);
        AssetDatabase.SaveAssets();
        Debug.Log($"Граф диалога сохранен в {path}");
        isDirty = false;
    }

    private GraphViewChange OnGraphViewChanged(GraphViewChange changes)
    {
        bool shouldSetDirty = false;

        if (changes.edgesToCreate != null)
        {
            foreach (var edge in changes.edgesToCreate)
            {
                ((BaseNodeElement)edge.output.node).ValidateNode();
                ((BaseNodeElement)edge.input.node).ValidateNode();
                shouldSetDirty = true;
            }
        }

        if (changes.elementsToRemove != null)
        {
            foreach (var element in changes.elementsToRemove)
            {
                if (element is Edge edge)
                {
                    ((BaseNodeElement)edge.output.node).ValidateNode();
                    ((BaseNodeElement)edge.input.node).ValidateNode();
                    shouldSetDirty = true;
                }
                else if (element is BaseNodeElement nodeToRemove)
                {
                    nodeLookup.Remove(nodeToRemove.GUID);
                    shouldSetDirty = true;
                }
            }
        }

        if (changes.movedElements != null)
        {
            shouldSetDirty = true;
        }

        if (shouldSetDirty)
        {
            SetDirty();
        }

        return changes;
    }

    public void SetDirty()
    {
        isDirty = true;
    }
}

Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'Query'
Cannot resolve symbol 'Query'
Argument type 'UnityEditor.Toolbar' is not assignable to parameter type 'UnityEngine.UIElements.VisualElement'
Cannot resolve symbol 'Add'
Cannot resolve symbol 'Add'
Cannot resolve symbol 'Add'
Cannot access internal class 'Toolbar' here
Cannot access internal class 'Toolbar' here
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'
Cannot resolve symbol 'style'