Editor/MarkupNode.cs
using System.Collections.Generic;

namespace XGUI.XGUIEditor
{
	public enum NodeType { Element, Text, RazorBlock }

	// Now stores Razor statements and code blocks in a RazorBlock node
	public class MarkupNode
	{
		public NodeType Type;
		public string TagName; // For elements
		public string TextContent; // For text nodes, or for RazorBlock content
		public Dictionary<string, string> Attributes = new();
		public List<MarkupNode> Children = new();

		public MarkupNode Parent;

		public override string ToString()
		{
			if ( Parent == null )
				return "Window Content";

			if ( Type == NodeType.Element )
			{
				return $"<{TagName}>";
			}
			else if ( Type == NodeType.Text )
			{
				return TextContent;
			}
			else if ( Type == NodeType.RazorBlock )
			{
				var codeType = "Code Block";
				if ( TextContent.StartsWith( "@{" ) )
					codeType = "Razor Block";
				else if ( TextContent.StartsWith( "@if" ) )
					codeType = "If Statement";
				else if ( TextContent.StartsWith( "@foreach" ) )
					codeType = "For Each Loop";
				else if ( TextContent.StartsWith( "@for" ) )
					codeType = "For Loop";
				else if ( TextContent.StartsWith( "@while" ) )
					codeType = "While Loop";
				else if ( TextContent.StartsWith( "@switch" ) )
					codeType = "Switch Statement";
				else if ( TextContent.StartsWith( "@case" ) )
					codeType = "Case Statement";
				else if ( TextContent.StartsWith( "@default" ) )
					codeType = "Default Statement";
				return $"({codeType}) {TextContent}";
			}
			return base.ToString();
		}

		public void TryModifyStyle( string name, string value )
		{
			// If the style attribute doesn't exist and value is null or empty,
			// there's nothing to do
			if ( !Attributes.ContainsKey( "style" ) && (value == null || value == "") )
				return;

			// Initialize style string
			string style = Attributes.ContainsKey( "style" ) ? Attributes["style"] : "";

			// If value is null or empty, remove the property entirely
			if ( value == null || value == "" )
			{
				// Create a more robust pattern to catch all variations of the property
				// This handles: name:value; or name: value; with any amount of whitespace
				string pattern = $@"(^|\s){name}\s*:\s*[^;]*;\s*";
				style = System.Text.RegularExpressions.Regex.Replace( style, pattern, " " );

				// Clean up any double-spaces that might result from the replacement
				while ( style.Contains( "  " ) )
					style = style.Replace( "  ", " " );

				// Remove the style attribute if it's empty after removing properties
				style = style.Trim();
				if ( string.IsNullOrWhiteSpace( style ) )
				{
					Attributes.Remove( "style" );
					return;
				}
			}
			else
			{
				// Add or modify the style property
				bool propertyExists = false;

				// Check if the property exists using a more precise pattern
				string pattern = $@"(^|\s){name}\s*:";
				if ( System.Text.RegularExpressions.Regex.IsMatch( style, pattern ) )
				{
					// Replace the existing property with the new value
					propertyExists = true;
					pattern = $@"(^|\s){name}\s*:\s*[^;]*;?";
					style = System.Text.RegularExpressions.Regex.Replace( style, pattern, $"$1{name}: {value};" );
				}

				if ( !propertyExists )
				{
					// Add the new property
					if ( !string.IsNullOrWhiteSpace( style ) )
					{
						// Ensure the style ends with a semicolon before adding the new property
						if ( !style.EndsWith( ";" ) )
							style += ";";

						// Add a space for readability
						style += " ";
					}

					style += $"{name}: {value};";
				}
			}

			// Clean up the style string and ensure it's well-formatted
			style = CleanupStyleString( style );

			// Update or remove the style attribute
			if ( string.IsNullOrWhiteSpace( style ) )
				Attributes.Remove( "style" );
			else
				Attributes["style"] = style;
		}

		private string CleanupStyleString( string style )
		{
			// Handle null or empty styles
			if ( string.IsNullOrWhiteSpace( style ) )
				return "";

			// Remove extra spaces
			while ( style.Contains( "  " ) )
				style = style.Replace( "  ", " " );

			// Ensure proper spacing around semicolons
			style = style.Replace( " ;", ";" );

			// Keep a space after semicolons for readability
			style = style.Replace( ";", "; " );

			// Remove semicolon-space at the end if present
			if ( style.EndsWith( "; " ) )
				style = style.Substring( 0, style.Length - 2 );

			// Ensure proper spacing around colons
			style = style.Replace( " :", ":" );
			style = System.Text.RegularExpressions.Regex.Replace( style, ":([^\\s])", ": $1" );

			// Remove whitespace at the beginning and end
			style = style.Trim();

			// Handle the case where we might have accidentally added an extra semicolon at the end
			if ( style.EndsWith( ";" ) )
				style = style.Substring( 0, style.Length - 1 );

			return style;
		}
	}
}