{"TotalCount":184,"Files":[{"Ident":"sklmr.razordesigner","Path":"Editor/Document/Payloads/CheckboxPayload.cs","FileName":"CheckboxPayload.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System.Text.Json.Serialization;\r\n\r\nnamespace Grains.RazorDesigner.Document;\r\n\r\npublic sealed record CheckboxPayload : Payload\r\n{\r\n    [JsonIgnore]\r\n    public override ControlType Kind =\u003E ControlType.Checkbox;\r\n\r\n    // Checkbox label text. Overrides Payload.Content (neutral default \u0022\u0022).\r\n    public override string Content { get; init; } = \u0022\u0022;\r\n\r\n    public override Length CheckboxSize { get; init; } = Length.Px( 16 );\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Inspector/EdgesControlWidget.cs","FileName":"EdgesControlWidget.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using Editor;\r\nusing Grains.RazorDesigner.Document;\r\nusing Sandbox;\r\n\r\nnamespace Grains.RazorDesigner.Inspector;\r\n\r\n[CustomEditor( typeof( Edges ) )]\r\npublic sealed class EdgesControlWidget : ControlWidget\r\n{\r\n\tprivate const string LogPrefix = \u0022[Grains.RazorDesigner]\u0022;\r\n\r\n\tpublic override bool SupportsMultiEdit =\u003E true;\r\n\r\n\tprivate readonly EdgesProxy _proxy;\r\n\tprivate readonly SerializedObject _proxySerialized;\r\n\t// Synchronous change events on both sides; without this guard SetValue would loop.\r\n\tprivate bool _syncing;\r\n\r\n\tprivate sealed class EdgesProxy\r\n\t{\r\n\t\tpublic Length Top    { get; set; } = Length.Px( 0 );\r\n\t\tpublic Length Right  { get; set; } = Length.Px( 0 );\r\n\t\tpublic Length Bottom { get; set; } = Length.Px( 0 );\r\n\t\tpublic Length Left   { get; set; } = Length.Px( 0 );\r\n\t}\r\n\r\n\tpublic EdgesControlWidget( SerializedProperty property ) : base( property )\r\n\t{\r\n\t\tLog.Info( $\u0022{LogPrefix} EdgesControlWidget ctor for {property.Name}\u0022 );\r\n\r\n\t\tLayout = Layout.Column();\r\n\t\tLayout.Spacing = 2;\r\n\r\n\t\t_proxy = new EdgesProxy();\r\n\t\t_proxySerialized = EditorTypeLibrary.GetSerializedObject( _proxy );\r\n\r\n\t\tvar topRow = Layout.Add( Layout.Row() );\r\n\t\ttopRow.Spacing = 2;\r\n\t\tAddSide( topRow, nameof( EdgesProxy.Top    ), \u0022border_top\u0022    );\r\n\t\tAddSide( topRow, nameof( EdgesProxy.Right  ), \u0022border_right\u0022  );\r\n\r\n\t\tvar bottomRow = Layout.Add( Layout.Row() );\r\n\t\tbottomRow.Spacing = 2;\r\n\t\tAddSide( bottomRow, nameof( EdgesProxy.Bottom ), \u0022border_bottom\u0022 );\r\n\t\tAddSide( bottomRow, nameof( EdgesProxy.Left   ), \u0022border_left\u0022   );\r\n\r\n\t\t_proxySerialized.OnPropertyChanged \u002B= OnProxyChanged;\r\n\r\n\t\tSyncFromProperty();\r\n\t}\r\n\r\n\tprivate void AddSide( Layout row, string propName, string icon )\r\n\t{\r\n\t\tvar prop = _proxySerialized.GetProperty( propName );\r\n\t\tvar lengthWidget = new LengthControlWidget( prop, icon );\r\n\t\trow.Add( lengthWidget, 1 );\r\n\t}\r\n\r\n\tprotected override void PaintControl()\r\n\t{\r\n\t\t// nothing\r\n\t}\r\n\r\n\tprivate void SyncFromProperty()\r\n\t{\r\n\t\tif ( _syncing ) return;\r\n\t\t_syncing = true;\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\tvar e = SerializedProperty.GetValue\u003CEdges\u003E( Edges.Zero );\r\n\r\n\t\t\t_proxySerialized.GetProperty( nameof( EdgesProxy.Top    ) ).SetValue( e.Top );\r\n\t\t\t_proxySerialized.GetProperty( nameof( EdgesProxy.Right  ) ).SetValue( e.Right );\r\n\t\t\t_proxySerialized.GetProperty( nameof( EdgesProxy.Bottom ) ).SetValue( e.Bottom );\r\n\t\t\t_proxySerialized.GetProperty( nameof( EdgesProxy.Left   ) ).SetValue( e.Left );\r\n\t\t}\r\n\t\tfinally\r\n\t\t{\r\n\t\t\t_syncing = false;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void OnProxyChanged( SerializedProperty property )\r\n\t{\r\n\t\tif ( _syncing ) return;\r\n\t\tif ( ReadOnly || !SerializedProperty.IsEditable )\r\n\t\t\treturn;\r\n\r\n\t\t_syncing = true;\r\n\t\ttry\r\n\t\t{\r\n\t\t\tvar newValue = new Edges( _proxy.Top, _proxy.Right, _proxy.Bottom, _proxy.Left );\r\n\r\n\t\t\tLog.Info( $\u0022{LogPrefix} EdgesControlWidget OnProxyChanged {newValue}\u0022 );\r\n\r\n\t\t\tPropertyStartEdit();\r\n\t\t\tSerializedProperty.SetValue( newValue );\r\n\t\t\tSignalValuesChanged();\r\n\t\t\tPropertyFinishEdit();\r\n\t\t}\r\n\t\tfinally\r\n\t\t{\r\n\t\t\t_syncing = false;\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnValueChanged()\r\n\t{\r\n\t\tbase.OnValueChanged();\r\n\t\tSyncFromProperty();\r\n\t}\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Palette/PalettePanel.cs","FileName":"PalettePanel.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing Editor;\r\nusing Grains.RazorDesigner.Common;\r\nusing Grains.RazorDesigner.Contracts;\r\nusing Grains.RazorDesigner.Document;\r\nusing Grains.RazorDesigner.Templates;\r\nusing Sandbox;\r\n\r\nnamespace Grains.RazorDesigner.Palette;\r\n\r\npublic class PalettePanel : Widget\r\n{\r\n\tprivate const string LogPrefix = \u0022[Grains.RazorDesigner]\u0022;\r\n\tprivate const string CookiePrefix = \u0022razordesigner.palette.\u0022;\r\n\r\n\t// Click-to-add target. Window decides where the new record goes (typically active selection or root).\r\n\tpublic event Action\u003CControlType\u003E TypeAddRequested;\r\n\r\n\t// Click-to-add a saved template. Window decides where to insert.\r\n\tpublic event Action\u003CPaletteTemplate\u003E TemplateAddRequested;\r\n\r\n\tprivate readonly PaletteTemplateStore _templateStore = new();\r\n\tprivate CollapsibleSection _templatesSection;\r\n\tprivate WrapPanel _templatesWrap;\r\n\tpublic PaletteTemplateStore TemplateStore =\u003E _templateStore;\r\n\r\n\tpublic PalettePanel( Widget parent ) : base( parent )\r\n\t{\r\n\t\tLayout = Layout.Column();\r\n\t\tLayout.Margin = 0;\r\n\t\tLayout.Spacing = 0;\r\n\t\tMinimumWidth = 180;\r\n\t\tVerticalSizeMode = SizeMode.CanGrow;\r\n\r\n\t\tvar byCategory = new Dictionary\u003CControlCategory, List\u003CControlType\u003E\u003E();\r\n\t\tforeach ( ControlType type in Enum.GetValues( typeof( ControlType ) ) )\r\n\t\t{\r\n\t\t\tvar cat = ControlDefaults.For( type ).Category;\r\n\t\t\tif ( !byCategory.TryGetValue( cat, out var list ) )\r\n\t\t\t{\r\n\t\t\t\tlist = new List\u003CControlType\u003E();\r\n\t\t\t\tbyCategory[cat] = list;\r\n\t\t\t}\r\n\t\t\tlist.Add( type );\r\n\t\t}\r\n\r\n\t\t// Templates section (top of palette). Hidden when store is empty; rebuilt on Changed.\r\n\t\t_templatesSection = new CollapsibleSection( this, \u0022Templates\u0022, \u0022bookmark\u0022 );\r\n\t\t_templatesWrap = new WrapPanel( null )\r\n\t\t{\r\n\t\t\tMinItemWidth = 92,\r\n\t\t\tItemHeight = (int)( Theme.RowHeight \u002B 4 ),\r\n\t\t\tHSpacing = 4,\r\n\t\t\tVSpacing = 4,\r\n\t\t\tPaddingLeft = 4,\r\n\t\t\tPaddingTop = 4,\r\n\t\t\tPaddingRight = 14,\r\n\t\t\tPaddingBottom = 4,\r\n\t\t};\r\n\t\t_templatesSection.BodyLayout.Add( _templatesWrap );\r\n\r\n\t\tvar templatesCookie = $\u0022{CookiePrefix}templates.expanded\u0022;\r\n\t\t_templatesSection.Expanded = EditorCookie.Get\u003Cbool\u003E( templatesCookie, true );\r\n\t\t_templatesSection.ExpandedChanged \u002B= expanded =\u003E\r\n\t\t{\r\n\t\t\tEditorCookie.Set( templatesCookie, expanded );\r\n\t\t\tLog.Info( $\u0022{LogPrefix} Palette Templates {(expanded ? \u0022expanded\u0022 : \u0022collapsed\u0022)}\u0022 );\r\n\t\t};\r\n\r\n\t\tLayout.Add( _templatesSection );\r\n\r\n\t\t_templateStore.Changed \u002B= RebuildTemplatesSection;\r\n\t\t_templateStore.Scan(); // initial fill (also fires Changed and rebuilds the section)\r\n\r\n\t\tforeach ( ControlCategory cat in Enum.GetValues( typeof( ControlCategory ) ) )\r\n\t\t{\r\n\t\t\tif ( !byCategory.TryGetValue( cat, out var list ) ) continue;\r\n\r\n\t\t\tvar section = new CollapsibleSection(\r\n\t\t\t\tthis,\r\n\t\t\t\tControlDefaults.CategoryDisplayName( cat ),\r\n\t\t\t\tCategoryIcon( cat ) );\r\n\r\n\t\t\tvar wrap = new WrapPanel( null )\r\n\t\t\t{\r\n\t\t\t\tMinItemWidth = 92,\r\n\t\t\t\tItemHeight = (int)( Theme.RowHeight \u002B 4 ),\r\n\t\t\t\tHSpacing = 4,\r\n\t\t\t\tVSpacing = 4,\r\n\t\t\t\tPaddingLeft = 4,\r\n\t\t\t\tPaddingTop = 4,\r\n\t\t\t\tPaddingRight = 14,    // clear the ScrollArea\u0027s vertical scrollbar\r\n\t\t\t\tPaddingBottom = 4,\r\n\t\t\t};\r\n\t\t\tsection.BodyLayout.Add( wrap );\r\n\r\n\t\t\tforeach ( var t in list )\r\n\t\t\t\tnew PaletteTypeButton( wrap, this, t );\r\n\r\n\t\t\tvar cookieKey = $\u0022{CookiePrefix}{cat}.expanded\u0022;\r\n\t\t\tsection.Expanded = EditorCookie.Get\u003Cbool\u003E( cookieKey, DefaultExpanded( cat ) );\r\n\t\t\tsection.ExpandedChanged \u002B= expanded =\u003E\r\n\t\t\t{\r\n\t\t\t\tEditorCookie.Set( cookieKey, expanded );\r\n\t\t\t\tLog.Info( $\u0022{LogPrefix} Palette category {cat} {(expanded ? \u0022expanded\u0022 : \u0022collapsed\u0022)}\u0022 );\r\n\t\t\t};\r\n\r\n\t\t\tLayout.Add( section );\r\n\t\t}\r\n\r\n\t\tLayout.AddStretchCell();\r\n\r\n\t\tLog.Info( $\u0022{LogPrefix} PalettePanel ctor (icon grid, {byCategory.Count} categories)\u0022 );\r\n\t}\r\n\r\n\tinternal void NotifyTypeClicked( ControlType type )\r\n\t{\r\n\t\tLog.Info( $\u0022{LogPrefix} PalettePanel.NotifyTypeClicked: {type}\u0022 );\r\n\t\tTypeAddRequested?.Invoke( type );\r\n\t}\r\n\r\n\tinternal void NotifyTemplateClicked( PaletteTemplate template )\r\n\t{\r\n\t\tLog.Info( $\u0022{LogPrefix} PalettePanel.NotifyTemplateClicked: \\\u0022{template.Name}\\\u0022\u0022 );\r\n\t\tTemplateAddRequested?.Invoke( template );\r\n\t}\r\n\r\n\tinternal void RequestTemplateDelete( PaletteTemplate template )\r\n\t{\r\n\t\tvar dialog = new Editor.Dialog( this );\r\n\t\tdialog.Window.WindowTitle = \u0022Delete template\u0022;\r\n\t\tdialog.Window.SetWindowIcon( \u0022delete\u0022 );\r\n\t\tdialog.Window.SetModal( true, true );\r\n\t\tdialog.Window.MinimumWidth = 320;\r\n\r\n\t\tdialog.Layout = Layout.Column();\r\n\t\tdialog.Layout.Margin = 16;\r\n\t\tdialog.Layout.Spacing = 10;\r\n\r\n\t\tdialog.Layout.Add( new Editor.Label( dialog )\r\n\t\t{\r\n\t\t\tText = $\u0022Delete template \\\u0022{template.Name}\\\u0022?\u0022,\r\n\t\t} );\r\n\r\n\t\tvar hint = new Editor.Label( dialog )\r\n\t\t{\r\n\t\t\tText = \u0022Already-instantiated copies in open documents are unaffected.\u0022,\r\n\t\t};\r\n\t\thint.SetStyles( \u0022color: #888; font-size: 11px;\u0022 );\r\n\t\tdialog.Layout.Add( hint );\r\n\r\n\t\tvar buttonRow = dialog.Layout.Add( Layout.Row() );\r\n\t\tbuttonRow.Spacing = 6;\r\n\t\tbuttonRow.AddStretchCell();\r\n\r\n\t\tvar cancel = new Editor.Button( dialog ) { Text = \u0022Cancel\u0022, MinimumWidth = 72 };\r\n\t\tcancel.MouseLeftPress \u002B= () =\u003E dialog.Close();\r\n\t\tbuttonRow.Add( cancel );\r\n\r\n\t\tvar del = new Editor.Button( dialog ) { Text = \u0022Delete\u0022, MinimumWidth = 72 };\r\n\t\tdel.SetStyles( \u0022color: #e07070;\u0022 );\r\n\t\tdel.MouseLeftPress \u002B= () =\u003E\r\n\t\t{\r\n\t\t\tLog.Info( $\u0022{LogPrefix} Palette delete confirmed: \\\u0022{template.Name}\\\u0022\u0022 );\r\n\t\t\t_templateStore.Delete( template );\r\n\t\t\tdialog.Close();\r\n\t\t};\r\n\t\tbuttonRow.Add( del );\r\n\r\n\t\tdialog.Window.AdjustSize();\r\n\t\tdialog.Show();\r\n\t}\r\n\r\n\tprivate void RebuildTemplatesSection()\r\n\t{\r\n\t\tvar templates = _templateStore.All;\r\n\r\n\t\t// Hide the entire section (header \u002B body) when there are no templates.\r\n\t\t_templatesSection.Visible = templates.Count \u003E 0;\r\n\r\n\t\tusing ( Editor.SuspendUpdates.For( _templatesWrap ) )\r\n\t\t{\r\n\t\t\t_templatesWrap.DestroyChildren();\r\n\t\t\tforeach ( var t in templates )\r\n\t\t\t\tnew PaletteTemplateButton( _templatesWrap, this, t );\r\n\t\t}\r\n\r\n\t\t_templatesWrap.Relayout();\r\n\t\t_templatesWrap.UpdateGeometry();\r\n\t\t_templatesSection.UpdateGeometry();\r\n\t\tUpdateGeometry();\r\n\r\n\t\tLog.Info( $\u0022{LogPrefix} PalettePanel.RebuildTemplatesSection: {templates.Count} tile(s), section.Visible={_templatesSection.Visible}\u0022 );\r\n\t}\r\n\r\n\tprivate static bool DefaultExpanded( ControlCategory cat ) =\u003E\r\n\t\tcat is ControlCategory.Layout or ControlCategory.Display or ControlCategory.Input;\r\n\r\n\tprivate static string CategoryIcon( ControlCategory cat ) =\u003E cat switch\r\n\t{\r\n\t\tControlCategory.Layout   =\u003E \u0022view_quilt\u0022,\r\n\t\tControlCategory.Display  =\u003E \u0022visibility\u0022,\r\n\t\tControlCategory.Input    =\u003E \u0022edit\u0022,\r\n\t\tControlCategory.Form     =\u003E \u0022list_alt\u0022,\r\n\t\t_ =\u003E \u0022category\u0022,\r\n\t};\r\n\r\n\tprivate sealed class PaletteTypeButton : Widget\r\n\t{\r\n\t\tprivate readonly PalettePanel _owner;\r\n\t\tprivate readonly ControlType _type;\r\n\t\t// InspectorIcon comes from the contract (engine-fidelity); drag defaults from ControlDefaults.\r\n\t\tprivate readonly string _icon;\r\n\r\n\t\tpublic PaletteTypeButton( Widget parent, PalettePanel owner, ControlType type ) : base( parent )\r\n\t\t{\r\n\t\t\t_owner = owner;\r\n\t\t\t_type = type;\r\n\t\t\t_icon = ContractScanner.Table.Get( type ).InspectorIcon;\r\n\r\n\t\t\tToolTip = type.ToString();\r\n\t\t\tCursor = CursorShape.Finger;\r\n\t\t\tMouseTracking = true;\r\n\t\t\tIsDraggable = true;\r\n\t\t}\r\n\r\n\t\tprotected override void OnPaint()\r\n\t\t{\r\n\t\t\tvar rect = LocalRect.Shrink( 1 );\r\n\t\t\tPaint.Antialiasing = true;\r\n\t\t\tPaint.TextAntialiasing = true;\r\n\r\n\t\t\tvar tint = ControlPresentation.IconTint( _type );\r\n\t\t\tvar fillAlpha = Paint.HasMouseOver ? 0.35f : 0.15f;\r\n\t\t\tvar borderAlpha = Paint.HasMouseOver ? 0.55f : 0.25f;\r\n\t\t\tPaint.SetBrush( tint.WithAlpha( fillAlpha ) );\r\n\t\t\tPaint.SetPen( tint.WithAlpha( borderAlpha ) );\r\n\t\t\tPaint.DrawRect( rect, 3 );\r\n\r\n\t\t\tvar hoverOpacity = Paint.HasMouseOver ? 1f : 0.85f;\r\n\t\t\tvar iconRect = new Rect( rect.Left \u002B 4, rect.Top, 20, rect.Height );\r\n\t\t\tPaint.SetPen( tint.WithAlphaMultiplied( hoverOpacity ) );\r\n\t\t\tPaint.DrawIcon( iconRect, _icon, 16, TextFlag.Center );\r\n\r\n\t\t\tvar textRect = rect;\r\n\t\t\ttextRect.Left = iconRect.Right \u002B 2;\r\n\t\t\ttextRect.Right -= 4;\r\n\t\t\tPaint.SetPen( Theme.Text.WithAlphaMultiplied( hoverOpacity ) );\r\n\t\t\tPaint.SetDefaultFont();\r\n\t\t\tPaint.DrawText( textRect, _type.ToString(), TextFlag.LeftCenter );\r\n\t\t}\r\n\r\n\t\tprotected override void OnMouseClick( MouseEvent e )\r\n\t\t{\r\n\t\t\tbase.OnMouseClick( e );\r\n\t\t\tif ( e.LeftMouseButton )\r\n\t\t\t\t_owner.NotifyTypeClicked( _type );\r\n\t\t}\r\n\r\n\t\tprotected override void OnDragStart()\r\n\t\t{\r\n\t\t\tbase.OnDragStart();\r\n\r\n\t\t\tvar drag = new Drag( this );\r\n\t\t\tdrag.Data.Object = _type;\r\n\t\t\tdrag.Data.Text = $\u0022palette:{_type}\u0022;\r\n\t\t\tdrag.Execute();\r\n\r\n\t\t\tLog.Info( $\u0022{LogPrefix} PaletteTypeButton.OnDragStart: {_type}\u0022 );\r\n\t\t}\r\n\t}\r\n\r\n\tprivate sealed class PaletteTemplateButton : Widget\r\n\t{\r\n\t\tprivate readonly PalettePanel _owner;\r\n\t\tprivate readonly PaletteTemplate _template;\r\n\r\n\t\tpublic PaletteTemplateButton( Widget parent, PalettePanel owner, PaletteTemplate template ) : base( parent )\r\n\t\t{\r\n\t\t\t_owner = owner;\r\n\t\t\t_template = template;\r\n\r\n\t\t\tToolTip = template.Name;\r\n\t\t\tCursor = CursorShape.Finger;\r\n\t\t\tMouseTracking = true;\r\n\t\t\tIsDraggable = true;\r\n\t\t}\r\n\r\n\t\tprotected override void OnPaint()\r\n\t\t{\r\n\t\t\tvar rect = LocalRect.Shrink( 1 );\r\n\t\t\tPaint.Antialiasing = true;\r\n\t\t\tPaint.TextAntialiasing = true;\r\n\r\n\t\t\tvar tint = ControlPresentation.TemplateTint;\r\n\t\t\tvar fillAlpha = Paint.HasMouseOver ? 0.18f : 0.08f;\r\n\t\t\tvar borderAlpha = Paint.HasMouseOver ? 0.55f : 0.25f;\r\n\t\t\tPaint.SetBrush( tint.WithAlpha( fillAlpha ) );\r\n\t\t\tPaint.SetPen( tint.WithAlpha( borderAlpha ) );\r\n\t\t\tPaint.DrawRect( rect, 3 );\r\n\r\n\t\t\tvar hoverOpacity = Paint.HasMouseOver ? 1f : 0.85f;\r\n\t\t\tvar iconRect = new Rect( rect.Left \u002B 4, rect.Top, 20, rect.Height );\r\n\t\t\tvar icon = string.IsNullOrEmpty( _template.IconName ) ? \u0022bookmark\u0022 : _template.IconName;\r\n\t\t\tPaint.SetPen( tint.WithAlphaMultiplied( hoverOpacity ) );\r\n\t\t\tPaint.DrawIcon( iconRect, icon, 16, TextFlag.Center );\r\n\r\n\t\t\tvar textRect = rect;\r\n\t\t\ttextRect.Left = iconRect.Right \u002B 2;\r\n\t\t\ttextRect.Right -= 4;\r\n\t\t\tPaint.SetPen( Theme.Text.WithAlphaMultiplied( hoverOpacity ) );\r\n\t\t\tPaint.SetDefaultFont();\r\n\t\t\tPaint.DrawText( textRect, _template.Name, TextFlag.LeftCenter );\r\n\t\t}\r\n\r\n\t\tprotected override void OnMouseClick( MouseEvent e )\r\n\t\t{\r\n\t\t\tbase.OnMouseClick( e );\r\n\t\t\tif ( e.LeftMouseButton )\r\n\t\t\t\t_owner.NotifyTemplateClicked( _template );\r\n\t\t}\r\n\r\n\t\tprotected override void OnContextMenu( ContextMenuEvent e )\r\n\t\t{\r\n\t\t\tbase.OnContextMenu( e );\r\n\t\t\tvar menu = new Menu( this );\r\n\t\t\tmenu.AddOption( \u0022Delete\u2026\u0022, \u0022delete\u0022, () =\u003E _owner.RequestTemplateDelete( _template ) );\r\n\t\t\tmenu.OpenAtCursor();\r\n\t\t\te.Accepted = true;\r\n\t\t}\r\n\r\n\t\tprotected override void OnDragStart()\r\n\t\t{\r\n\t\t\tbase.OnDragStart();\r\n\t\t\tvar drag = new Drag( this );\r\n\t\t\tdrag.Data.Object = _template;\r\n\t\t\tdrag.Data.Text = $\u0022template:{_template.Name}\u0022;\r\n\t\t\tdrag.Execute();\r\n\t\t\tLog.Info( $\u0022{LogPrefix} PaletteTemplateButton.OnDragStart: \\\u0022{_template.Name}\\\u0022\u0022 );\r\n\t\t}\r\n\t}\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/CSharp/CSharpOp.cs","FileName":"CSharpOp.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Projection.CSharp;\r\n\r\npublic abstract record CSharpOp;\r\n\r\n// File-level scaffold\r\npublic sealed record HeaderBanner( string ClassName, string Namespace ) : CSharpOp;\r\npublic sealed record UsingDirective( string Namespace ) : CSharpOp;\r\npublic sealed record NamespaceOpen( string Namespace ) : CSharpOp;\r\npublic sealed record ClassOpen( string ClassName, string BaseClass ) : CSharpOp;\r\npublic sealed record ClassClose() : CSharpOp;\r\n\r\npublic sealed record FieldDecl(\r\n    string Visibility, string Type, string Name, string InitialExpr,\r\n    bool IsParameter, bool IsProperty = false ) : CSharpOp;\r\n\r\npublic sealed record MethodOpen(\r\n    string Visibility, bool IsOverride, bool IsAsync,\r\n    string ReturnType, string Name, string ParameterList ) : CSharpOp;\r\n\r\npublic sealed record MethodClose() : CSharpOp;\r\n\r\n// Body-level\r\npublic sealed record Statement( string Code ) : CSharpOp;          // single \u0060;\u0060-terminated line\r\npublic sealed record BlockOpen( string Header ) : CSharpOp;        // e.g. \u0060if ( \u003Ccond\u003E )\u0060 \u2014 applier writes \u0022\u003Cheader\u003E {\\n\u0022 and indents\r\npublic sealed record BlockClose() : CSharpOp;\r\npublic sealed record BlankLine() : CSharpOp;\r\npublic sealed record Comment( string Text ) : CSharpOp;            // \u0060// \u003Ctext\u003E\u0060\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/CSharp/CSharpProjector.cs","FileName":"CSharpProjector.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System.Collections.Generic;\r\nusing Grains.RazorDesigner.Projection.CSharp.Projectors;\r\n\r\nnamespace Grains.RazorDesigner.Projection.CSharp;\r\n\r\npublic static class CSharpProjector\r\n{\r\n    public static CSharpResult Project(\r\n        IReadOnlyWiring wiring,\r\n        bool documentHasAnyBindings )\r\n    {\r\n        if ( wiring.Symbols.Count == 0 \u0026\u0026 !documentHasAnyBindings )\r\n            return new CSharpResult( System.Array.Empty\u003CCSharpOp\u003E(), null );\r\n\r\n        var ctx = new CSharpProjectorContext( wiring );\r\n        var ops = new List\u003CCSharpOp\u003E( 64 );\r\n\r\n        ops.Add( new HeaderBanner( wiring.ClassName, wiring.Namespace ) );\r\n        ops.Add( new UsingDirective( \u0022Sandbox\u0022 ) );\r\n        ops.Add( new UsingDirective( \u0022Sandbox.UI\u0022 ) );\r\n        foreach ( var u in wiring.Usings )\r\n            ops.Add( new UsingDirective( u ) );\r\n        ops.Add( new NamespaceOpen( wiring.Namespace ) );\r\n        ops.Add( new ClassOpen( wiring.ClassName, wiring.BaseClass ) );\r\n\r\n        // Step 3: body. SymbolProjector handles grouping \u002B sorting \u002B per-kind dispatch.\r\n        SymbolProjector.EmitAll( wiring, ops, ctx );\r\n\r\n        ops.Add( new ClassClose() );\r\n\r\n        var source = CSharpApplier.Apply( ops ).Replace( \u0022\\r\\n\u0022, \u0022\\n\u0022 );\r\n\r\n        return new CSharpResult( ops, source );\r\n    }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/CSharp/Projectors/ParameterSymbolProjector.cs","FileName":"ParameterSymbolProjector.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System.Collections.Generic;\r\nusing Grains.RazorDesigner.Wiring;\r\n\r\nnamespace Grains.RazorDesigner.Projection.CSharp.Projectors;\r\n\r\npublic static class ParameterSymbolProjector\r\n{\r\n    public static void Emit( ParameterSymbol s, List\u003CCSharpOp\u003E ops, CSharpProjectorContext ctx )\r\n    {\r\n        var initial = s.Initial is null ? \u0022default\u0022 : ExpressionEmitter.Emit( s.Initial, ctx );\r\n        ops.Add( new FieldDecl(\r\n            Visibility: \u0022public\u0022, Type: s.Type, Name: s.Name,\r\n            InitialExpr: initial, IsParameter: true ) );\r\n    }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/Escape.cs","FileName":"Escape.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Projection;\r\n\r\npublic static class Escape\r\n{\r\n    public static string Html( string s )\r\n    {\r\n        if ( string.IsNullOrEmpty( s ) ) return \u0022\u0022;\r\n        return s\r\n            .Replace( \u0022\u0026\u0022, \u0022\u0026amp;\u0022 )\r\n            .Replace( \u0022\u003C\u0022, \u0022\u0026lt;\u0022 )\r\n            .Replace( \u0022\u003E\u0022, \u0022\u0026gt;\u0022 )\r\n            .Replace( \u0022\\\u0022\u0022, \u0022\u0026quot;\u0022 );\r\n    }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/IReadOnlyNode.cs","FileName":"IReadOnlyNode.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing Sandbox; // Color\r\n\r\nnamespace Grains.RazorDesigner.Projection;\r\n\r\npublic interface IReadOnlyStateRule\r\n{\r\n    Document.PseudoKind State { get; }\r\n    Document.NthChildMode NthChildMode { get; }\r\n    int NthChildArg { get; }\r\n    IAppearance Delta { get; }\r\n\r\n    public static int CompareCanonical( IReadOnlyStateRule a, IReadOnlyStateRule b )\r\n    {\r\n        int c = ((int)a.State).CompareTo( (int)b.State );\r\n        if ( c != 0 ) return c;\r\n        c = ((int)a.NthChildMode).CompareTo( (int)b.NthChildMode );\r\n        if ( c != 0 ) return c;\r\n        return a.NthChildArg.CompareTo( b.NthChildArg );\r\n    }\r\n}\r\n\r\npublic interface IReadOnlyNode\r\n{\r\n    Guid Id { get; }\r\n    string Kind { get; }         // == ControlType.ToString()\r\n    string ClassName { get; }\r\n    IAppearance Appearance { get; }\r\n    IPayload Payload { get; }\r\n    IReadOnlyList\u003CIReadOnlyNode\u003E Children { get; }                              // non-slot children\r\n    IReadOnlyDictionary\u003Cstring, IReadOnlyList\u003CIReadOnlyNode\u003E\u003E Slots { get; }    // slot-name -\u003E slot children (only SplitContainer populates)\r\n    IReadOnlyList\u003CIReadOnlyStateRule\u003E StateRules { get; }                       // per-state style deltas; canonical order not guaranteed here (the Applier sorts)\r\n}\r\n\r\npublic interface IAppearance\r\n{\r\n    // Layout\r\n    Document.Length Width { get; }\r\n    Document.Length Height { get; }\r\n\r\n    // Flex container\r\n    Document.FlexDirection Direction { get; }\r\n    Document.JustifyContent Justify { get; }\r\n    Document.AlignItems Align { get; }\r\n    float Gap { get; }\r\n    Document.Edges Padding { get; }\r\n    Document.FlexWrap Wrap { get; }\r\n\r\n    // Positioning (grd-7t2z)\r\n    Document.PositionKind Position { get; }\r\n    Document.Length Top { get; }\r\n    Document.Length Left { get; }\r\n    Document.Length Right { get; }\r\n    Document.Length Bottom { get; }\r\n\r\n    // Flex self\r\n    float FlexGrow { get; }\r\n    float FlexShrink { get; }\r\n    Document.Length FlexBasis { get; }\r\n    Document.AlignSelfKind AlignSelf { get; }\r\n\r\n    // Typography \u002B OverrideTypography\r\n    bool OverrideTypography { get; }\r\n    string FontFamily { get; }\r\n    Document.Length FontSize { get; }\r\n    int FontWeight { get; }\r\n    Color Color { get; }\r\n    Document.TextAlignment TextAlign { get; }\r\n    bool FontStyleItalic { get; }\r\n    Document.TextTransformKind TextTransform { get; }\r\n    Document.Length LetterSpacing { get; }\r\n    Document.Length LineHeight { get; }\r\n\r\n    // Background \u002B OverrideBackground\r\n    bool OverrideBackground { get; }\r\n    Color BackgroundColor { get; }\r\n    string BackgroundImage { get; }\r\n    string BackgroundSize { get; }\r\n    string BackgroundPosition { get; }\r\n    string BackgroundRepeat { get; }\r\n\r\n    // Border \u002B OverrideBorder\r\n    bool OverrideBorder { get; }\r\n    Document.Length BorderRadius { get; }\r\n    Color BorderColor { get; }\r\n    Document.Length BorderWidth { get; }\r\n\r\n    // Effects \u002B OverrideEffects\r\n    bool OverrideEffects { get; }\r\n    Document.Length BoxShadowX { get; }\r\n    Document.Length BoxShadowY { get; }\r\n    Document.Length BoxShadowBlur { get; }\r\n    Color BoxShadowColor { get; }\r\n    bool BoxShadowInset { get; }\r\n    float Opacity { get; }\r\n\r\n    // Constraints \u002B OverrideConstraints\r\n    bool OverrideConstraints { get; }\r\n    Document.Edges Margin { get; }\r\n    Document.Length MinWidth { get; }\r\n    Document.Length MaxWidth { get; }\r\n    Document.Length MinHeight { get; }\r\n    Document.Length MaxHeight { get; }\r\n\r\n    // Interaction \u002B OverrideInteraction\r\n    bool OverrideInteraction { get; }\r\n    Document.CursorKind Cursor { get; }\r\n    Document.OverflowKind Overflow { get; }\r\n    int ZIndex { get; }\r\n    bool PointerEvents { get; }\r\n}\r\n\r\npublic interface IPayload\r\n{\r\n    string Content { get; }       // Label/Button text; Checkbox label\r\n    string Placeholder { get; }   // TextEntry\r\n    string Source { get; }        // Image src\r\n    string IconName { get; }      // IconPanel glyph\r\n    Document.Length CheckboxSize { get; }  // Checkbox box size\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/Projectors/ButtonProjector.cs","FileName":"ButtonProjector.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System.Collections.Generic;\r\nusing Grains.RazorDesigner.Projection.Appearance;\r\nusing Grains.RazorDesigner.Projection.Razor;\r\n\r\nnamespace Grains.RazorDesigner.Projection.Projectors;\r\n\r\n[Projector( \u0022Button\u0022 )]\r\npublic sealed class ButtonProjector : IControlProjector\r\n{\r\n    public string Kind =\u003E \u0022Button\u0022;\r\n\r\n    public ProjectionResult Project( IReadOnlyNode node, IAppearance a, IPayload p, ProjectionContext ctx )\r\n    {\r\n        var scss = AppearanceScss.Emit(\r\n            a,\r\n            isRoot:       node.ClassName == Document.DesignerDocument.RootClassName,\r\n            isContainer:  false,\r\n            childCount:   0,\r\n            isLabel:      false,\r\n            isCheckbox:   false,\r\n            checkboxSize: default );\r\n\r\n        var nodeId = node.Id.ToString();\r\n        var ops = new PanelOp[]\r\n        {\r\n            new SetAttribute( \u0022data-grd-node-id\u0022, nodeId ),\r\n            new SetInnerText( p.Content ?? \u0022\u0022 ),\r\n        };\r\n\r\n        var razorAttrs = new[] { RazorEmit.Attr( \u0022data-grd-node-id\u0022, nodeId ) };\r\n\r\n        return new ProjectionResult(\r\n            PanelOps:        ops,\r\n            ScssLines:       scss,\r\n            RazorAttributes: razorAttrs,\r\n            RazorInnerText:  Escape.Html( p.Content ?? \u0022\u0022 ) );\r\n    }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/Projectors/FieldProjector.cs","FileName":"FieldProjector.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System.Collections.Generic;\r\nusing Grains.RazorDesigner.Projection.Appearance;\r\nusing Grains.RazorDesigner.Projection.Razor;\r\n\r\nnamespace Grains.RazorDesigner.Projection.Projectors;\r\n\r\n[Projector( \u0022Field\u0022 )]\r\npublic sealed class FieldProjector : IControlProjector\r\n{\r\n    public string Kind =\u003E \u0022Field\u0022;\r\n\r\n    public ProjectionResult Project( IReadOnlyNode node, IAppearance a, IPayload p, ProjectionContext ctx )\r\n    {\r\n        var scss = AppearanceScss.Emit(\r\n            a,\r\n            isRoot:       node.ClassName == Document.DesignerDocument.RootClassName,\r\n            isContainer:  true,\r\n            childCount:   node.Children.Count,\r\n            isLabel:      false,\r\n            isCheckbox:   false,\r\n            checkboxSize: default );\r\n\r\n        var nodeId = node.Id.ToString();\r\n        var ops = new PanelOp[]\r\n        {\r\n            new SetAttribute( \u0022data-grd-node-id\u0022, nodeId ),\r\n        };\r\n\r\n        var razorAttrs = new[] { RazorEmit.Attr( \u0022data-grd-node-id\u0022, nodeId ) };\r\n\r\n        return new ProjectionResult(\r\n            PanelOps:        ops,\r\n            ScssLines:       scss,\r\n            RazorAttributes: razorAttrs,\r\n            RazorInnerText:  null );\r\n    }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/Razor/RazorEmit.cs","FileName":"RazorEmit.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Projection.Razor;\r\n\r\npublic static class RazorEmit\r\n{\r\n    public static string Attr( string name, string value ) =\u003E $\u0022{name}=\\\u0022{Escape.Html( value )}\\\u0022\u0022;\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Projection/Tests/PanelOpExhaustivenessTest.cs","FileName":"PanelOpExhaustivenessTest.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System;\r\n\r\nnamespace Grains.RazorDesigner.Projection.Tests;\r\n\r\npublic static class PanelOpExhaustivenessTest\r\n{\r\n    public static (bool pass, string message) Run()\r\n    {\r\n        var ops = new PanelOp[]\r\n        {\r\n            new SetClass( \u0022\u0022 ),\r\n            new SetStyle( \u0022\u0022, \u0022\u0022 ),\r\n            new SetAttribute( \u0022\u0022, \u0022\u0022 ),\r\n            new SetInnerText( \u0022\u0022 ),\r\n        };\r\n        try\r\n        {\r\n            foreach ( var op in ops )\r\n                Applier.ApplyOpToScratch( op );\r\n            return (true, $\u0022PanelOpExhaustivenessTest: {ops.Length} variants OK\u0022);\r\n        }\r\n        catch ( Exception e )\r\n        {\r\n            return (false, $\u0022PanelOpExhaustivenessTest FAILED: {e.GetType().Name}: {e.Message}\u0022);\r\n        }\r\n    }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Serialization/IR/IRWriter.cs","FileName":"IRWriter.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Security.Cryptography;\r\nusing System.Text;\r\nusing System.Text.Json;\r\nusing Grains.RazorDesigner.Document;\r\n\r\nnamespace Grains.RazorDesigner.Serialization.IR;\r\n\r\npublic static class IRWriter\r\n{\r\n\tprivate const string LogPrefix = \u0022[Grains.RazorDesigner]\u0022;\r\n\r\n\t// Empty collections reused for nodes that have no slots/children/metadata.\r\n\tprivate static readonly IReadOnlyDictionary\u003Cstring, object\u003E _emptyMetadata = new Dictionary\u003Cstring, object\u003E();\r\n\tprivate static readonly IReadOnlyList\u003CIRNodeEnvelope\u003E      _emptyChildren = System.Array.Empty\u003CIRNodeEnvelope\u003E();\r\n\tprivate static readonly IReadOnlyDictionary\u003Cstring, IRNodeEnvelope\u003E _emptySlots = new Dictionary\u003Cstring, IRNodeEnvelope\u003E();\r\n\r\n\tpublic static string WriteDocument( DesignerDocument doc )\r\n\t{\r\n\t\tif ( doc is null )\r\n\t\t\tthrow new ArgumentNullException( nameof( doc ) );\r\n\r\n\t\tLog.Info( $\u0022{LogPrefix} IRWriter.WriteDocument: serialising document (root children: {doc.RootRecord.Children.Count})\u0022 );\r\n\r\n\t\tvar envelope = new IRDocumentEnvelope\r\n\t\t{\r\n\t\t\tRoot   = ToNode( doc.RootRecord ),\r\n\t\t\tWiring = doc.Wiring ?? Grains.RazorDesigner.Wiring.WiringEnvelope.Empty,\r\n\t\t};\r\n\r\n\t\tvar json = JsonSerializer.Serialize( envelope, DesignerIRJson.Options );\r\n\r\n\t\t// Normalise CRLF \u2192 LF (canonical form; .gitattributes also pins LF as a backstop).\r\n\t\tif ( json.Contains( \u0027\\r\u0027 ) )\r\n\t\t\tjson = json.Replace( \u0022\\r\\n\u0022, \u0022\\n\u0022 ).Replace( \u0022\\r\u0022, \u0022\\n\u0022 );\r\n\r\n\t\tLog.Info( $\u0022{LogPrefix} IRWriter.WriteDocument: OK ({json.Length} chars)\u0022 );\r\n\t\treturn json;\r\n\t}\r\n\r\n\tpublic static string CanonicalHash( string json )\r\n\t{\r\n\t\tif ( json is null )\r\n\t\t\tthrow new ArgumentNullException( nameof( json ) );\r\n\r\n\t\tvar bytes = Encoding.UTF8.GetBytes( json );\r\n\t\tvar hash  = SHA256.HashData( bytes );\r\n\t\treturn Convert.ToHexString( hash ).ToLowerInvariant();\r\n\t}\r\n\r\n\t// Recursively converts a ControlRecord to its IRNodeEnvelope representation.\r\n\tprivate static IRNodeEnvelope ToNode( ControlRecord r )\r\n\t{\r\n\t\tDictionary\u003Cstring, IRNodeEnvelope\u003E slotDict  = null;\r\n\t\tList\u003CIRNodeEnvelope\u003E               childList = null;\r\n\r\n\t\tforeach ( var child in r.Children )\r\n\t\t{\r\n\t\t\tif ( child.IsSlot )\r\n\t\t\t{\r\n\t\t\t\tslotDict ??= new Dictionary\u003Cstring, IRNodeEnvelope\u003E();\r\n\t\t\t\tslotDict[child.SlotName] = ToNode( child );\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tchildList ??= new List\u003CIRNodeEnvelope\u003E();\r\n\t\t\t\tchildList.Add( ToNode( child ) );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn new IRNodeEnvelope\r\n\t\t{\r\n\t\t\tId         = r.Id,\r\n\t\t\tKind       = r.Type,\r\n\t\t\tClassName  = r.ClassName,\r\n\t\t\tAppearance = r.Appearance,\r\n\t\t\tPayload    = r.Payload,\r\n\t\t\tSlots      = slotDict  is not null\r\n\t\t\t\t? (IReadOnlyDictionary\u003Cstring, IRNodeEnvelope\u003E)slotDict\r\n\t\t\t\t: _emptySlots,\r\n\t\t\tChildren   = childList is not null\r\n\t\t\t\t? (IReadOnlyList\u003CIRNodeEnvelope\u003E)childList\r\n\t\t\t\t: _emptyChildren,\r\n\t\t\tStates = r.StateRules.Count == 0\r\n\t\t\t\t? null\r\n\t\t\t\t: r.StateRules\r\n\t\t\t\t\t.OrderBy( rule =\u003E rule, Comparer\u003CStateRule\u003E.Create( StateRule.CompareCanonical ) )\r\n\t\t\t\t\t.Select( rule =\u003E new IRStateEnvelope\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tState       = rule.State,\r\n\t\t\t\t\t\tNthChildMode = rule.NthChildMode,\r\n\t\t\t\t\t\tNthChildArg  = rule.NthChildArg,\r\n\t\t\t\t\t\tDelta        = rule.Delta,\r\n\t\t\t\t\t} )\r\n\t\t\t\t\t.ToList(),\r\n\t\t\tBindings   = r.Bindings.Count == 0\r\n\t\t\t\t? System.Array.Empty\u003CGrains.RazorDesigner.Wiring.Binding\u003E()\r\n\t\t\t\t: r.Bindings.ToArray(),\r\n\r\n\t\t\t\tCustomStyles = r.CustomStyles.Count == 0 \r\n\t\t\t\t? null \r\n\t\t\t\t: new Dictionary\u003Cstring, string\u003E( r.CustomStyles ),\r\n\t\t};\r\n\t}\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Wiring/Actions/Action.cs","FileName":"Action.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System;\r\nusing System.Text.Json.Serialization;\r\n\r\nnamespace Grains.RazorDesigner.Wiring;\r\n\r\n[JsonPolymorphic( TypeDiscriminatorPropertyName = \u0022$type\u0022 )]\r\n[JsonDerivedType( typeof( SetAction ),              \u0022Set\u0022 )]\r\n[JsonDerivedType( typeof( CallAction ),             \u0022Call\u0022 )]\r\n[JsonDerivedType( typeof( IfAction ),               \u0022If\u0022 )]\r\n[JsonDerivedType( typeof( StateHasChangedAction ),  \u0022StateHasChanged\u0022 )]\r\n[JsonDerivedType( typeof( LogAction ),              \u0022Log\u0022 )]\r\n[JsonDerivedType( typeof( ReturnAction ),           \u0022Return\u0022 )]\r\n[JsonDerivedType( typeof( InlineAction ),           \u0022Inline\u0022 )]\r\npublic abstract record Action\r\n{\r\n    public Guid Id { get; init; } = Guid.NewGuid();\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Wiring/Actions/InlineAction.cs","FileName":"InlineAction.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Wiring;\r\n\r\npublic sealed record InlineAction : Action\r\n{\r\n    public string Code { get; init; } = \u0022\u0022;\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Wiring/Actions/SetAction.cs","FileName":"SetAction.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Wiring;\r\n\r\n// \u0060Target = Value;\u0060 \u2014 assignment to a Symbol field.\r\npublic sealed record SetAction : Action\r\n{\r\n    public TargetRef Target { get; init; }\r\n    public Expression Value { get; init; }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Wiring/Bindings/EventBinding.cs","FileName":"EventBinding.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using System.Collections.Generic;\r\n\r\nnamespace Grains.RazorDesigner.Wiring;\r\n\r\npublic sealed record EventBinding : Binding\r\n{\r\n    public string Event { get; init; } = \u0022\u0022;\r\n    public IReadOnlyList\u003CAction\u003E Body { get; init; } = System.Array.Empty\u003CAction\u003E();\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Wiring/Bindings/VisibleBinding.cs","FileName":"VisibleBinding.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Wiring;\r\n\r\npublic sealed record VisibleBinding : Binding\r\n{\r\n    public Expression Condition { get; init; }\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Wiring/Symbols/SymbolVisibility.cs","FileName":"SymbolVisibility.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"namespace Grains.RazorDesigner.Wiring;\r\n\r\npublic enum SymbolVisibility\r\n{\r\n    Private,\r\n    Public,\r\n    Internal,\r\n    Protected,\r\n}\r\n"},{"Ident":"sklmr.razordesigner","Path":"Editor/Canvas/DesignerCanvas.cs","FileName":"DesignerCanvas.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":306734,"Code":"using Editor;\r\nusing Grains.RazorDesigner.Document;\r\nusing Sandbox;\r\n\r\nnamespace Grains.RazorDesigner.Canvas;\r\n\r\n// Same shape as ShaderGraph PreviewPanel: override PreFrame to advance the scene.\r\npublic class DesignerCanvas : SceneRenderingWidget\r\n{\r\n\tprivate const string LogPrefix = \u0022[Grains.RazorDesigner]\u0022;\r\n\r\n\tprivate readonly DesignerScene _designerScene;\r\n\r\n\tpublic DesignerCanvas( Widget parent ) : base( parent )\r\n\t{\r\n\t\tLog.Info( $\u0022{LogPrefix} DesignerCanvas ctor\u0022 );\r\n\r\n\t\t_designerScene = new DesignerScene();\r\n\r\n\t\tScene = _designerScene.Scene;\r\n\t\tCamera = _designerScene.Camera;\r\n\r\n\t\tHorizontalSizeMode = SizeMode.Default | SizeMode.Expand;\r\n\t\tVerticalSizeMode = SizeMode.Default | SizeMode.Expand;\r\n\r\n\t\t// Without AcceptDrops, OnDragHover/OnDragDrop are never invoked.\r\n\t\tAcceptDrops = true;\r\n\r\n\t\tMouseTracking = true;\r\n\t}\r\n\r\n\tpublic DesignerScene DesignerScene =\u003E _designerScene;\r\n\r\n\t// Assigned by DesignerWindow after construction (mirrors how _viewportFrame.Canvas is wired).\r\n\tpublic OverlayController Overlay { get; set; }\r\n\r\n\tpublic CanvasViewportFrame ViewportFrame { get; set; }\r\n\r\n\tpublic event System.Action\u003CVector2, bool, Sandbox.KeyboardModifiers\u003E CanvasClicked;\r\n\tpublic event System.Action\u003CVector2, Sandbox.KeyboardModifiers\u003E CanvasMoved;\r\n\tpublic event System.Action\u003CVector2\u003E CanvasReleased;\r\n\r\n\tpublic event System.Action\u003CVector2\u003E CanvasPanDragged; // delta, screen px\r\n\r\n\t// Fires when the cursor leaves the canvas widget. Used to clear hover-pick state.\r\n\tpublic event System.Action CanvasHoverEnded;\r\n\r\n\tpublic event System.Action\u003CControlType, Vector2\u003E RecordDropped;\r\n\t// Mirrors RecordDropped but for saved palette templates.\r\n\tpublic event System.Action\u003CGrains.RazorDesigner.Templates.PaletteTemplate, Vector2\u003E TemplateDropped;\r\n\r\n\tprivate bool _middlePanning;\r\n\tprivate Vector2 _lastPanScreen;\r\n\r\n\tprivate const bool ProbeFrameCost = false;\r\n\tprivate const int ProbeWindow = 120;\r\n\tprivate readonly System.Diagnostics.Stopwatch _probeSw = new();\r\n\tprivate double _probeTickMs;\r\n\tprivate double _probeUpdateMs;\r\n\tprivate int _probeFrames;\r\n\r\n\tpublic override void OnDragHover( DragEvent ev )\r\n\t{\r\n\t\tbase.OnDragHover( ev );\r\n\t\tif ( ev.Data.Object is ControlType || ev.Data.Object is Grains.RazorDesigner.Templates.PaletteTemplate )\r\n\t\t{\r\n\t\t\tev.Action = DropAction.Copy;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic override void OnDragDrop( DragEvent ev )\r\n\t{\r\n\t\tbase.OnDragDrop( ev );\r\n\t\tif ( ev.Data.Object is ControlType type )\r\n\t\t{\r\n\t\t\tLog.Info( $\u0022{LogPrefix} DesignerCanvas drop: {type} at widget ({ev.LocalPosition.x:F0}, {ev.LocalPosition.y:F0})\u0022 );\r\n\t\t\tRecordDropped?.Invoke( type, ev.LocalPosition );\r\n\t\t}\r\n\t\telse if ( ev.Data.Object is Grains.RazorDesigner.Templates.PaletteTemplate template )\r\n\t\t{\r\n\t\t\tLog.Info( $\u0022{LogPrefix} DesignerCanvas drop: template \\\u0022{template.Name}\\\u0022 at widget ({ev.LocalPosition.x:F0}, {ev.LocalPosition.y:F0})\u0022 );\r\n\t\t\tTemplateDropped?.Invoke( template, ev.LocalPosition );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnMousePress( MouseEvent e )\r\n\t{\r\n\t\tif ( e.MiddleMouseButton )\r\n\t\t{\r\n\t\t\t// Swallow entirely \u2014 don\u0027t let base (SceneRenderingWidget) see the middle drag.\r\n\t\t\t_middlePanning = true;\r\n\t\t\t_lastPanScreen = e.ScreenPosition;\r\n\t\t\te.Accepted = true;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbase.OnMousePress( e );\r\n\t\tif ( e.LeftMouseButton || e.RightMouseButton )\r\n\t\t{\r\n\t\t\tvar pos = e.LocalPosition;\r\n\t\t\tLog.Info( $\u0022{LogPrefix} DesignerCanvas click at widget ({pos.x:F0}, {pos.y:F0}) right={e.RightMouseButton}\u0022 );\r\n\t\t\tCanvasClicked?.Invoke( pos, e.RightMouseButton, e.KeyboardModifiers );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnMouseMove( MouseEvent e )\r\n\t{\r\n\t\tif ( _middlePanning )\r\n\t\t{\r\n\t\t\tvar s = e.ScreenPosition;\r\n\t\t\tCanvasPanDragged?.Invoke( s - _lastPanScreen );\r\n\t\t\t_lastPanScreen = s;\r\n\t\t\te.Accepted = true;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbase.OnMouseMove( e );\r\n\t\tCanvasMoved?.Invoke( e.LocalPosition, e.KeyboardModifiers );\r\n\t}\r\n\r\n\tprotected override void OnMouseReleased( MouseEvent e )\r\n\t{\r\n\t\tif ( _middlePanning )\r\n\t\t{\r\n\t\t\t_middlePanning = false;\r\n\t\t\te.Accepted = true;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbase.OnMouseReleased( e );\r\n\t\tCanvasReleased?.Invoke( e.LocalPosition );\r\n\t}\r\n\r\n\tprotected override void OnMouseLeave()\r\n\t{\r\n\t\tbase.OnMouseLeave();\r\n\t\tCanvasHoverEnded?.Invoke();\r\n\t}\r\n\r\n\tprotected override void PreFrame()\r\n\t{\r\n\t\tbase.PreFrame();\r\n\r\n\t\tif ( !_designerScene.Scene.IsValid() )\r\n\t\t\treturn;\r\n\r\n\t\tif ( ProbeFrameCost ) _probeSw.Restart();\r\n\r\n\t\tusing ( _designerScene.Scene.Push() )\r\n\t\t{\r\n\t\t\t// EditorTick (not GameTick): preview scene, no SceneNetworkUpdate / fixed physics.\r\n\t\t\t_designerScene.Scene.EditorTick( RealTime.Now, RealTime.Delta );\r\n\t\t}\r\n\r\n\t\tif ( ProbeFrameCost ) { _probeTickMs \u002B= _probeSw.Elapsed.TotalMilliseconds; _probeSw.Restart(); }\r\n\r\n\t\t// Layout in framebuffer space (Size * DpiScale); DesignerScene sets Scale = DpiScale so authoring stays in logical px.\r\n\t\t_designerScene.Update( Size.x, Size.y, DpiScale );\r\n\r\n\t\tOverlay?.Tick( _designerScene, DpiScale );\r\n\r\n\t\tif ( ProbeFrameCost )\r\n\t\t{\r\n\t\t\t_probeUpdateMs \u002B= _probeSw.Elapsed.TotalMilliseconds;\r\n\t\t\tif ( \u002B\u002B_probeFrames \u003E= ProbeWindow )\r\n\t\t\t{\r\n\t\t\t\tLog.Info( $\u0022{LogPrefix} probe: EditorTick avg {_probeTickMs / _probeFrames:F3}ms | Update avg {_probeUpdateMs / _probeFrames:F3}ms (over {_probeFrames} frames, canvas {Size.x:F0}x{Size.y:F0})\u0022 );\r\n\t\t\t\t_probeTickMs = 0;\r\n\t\t\t\t_probeUpdateMs = 0;\r\n\t\t\t\t_probeFrames = 0;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnKeyPress( KeyEvent e )\r\n\t{\r\n\t\tbase.OnKeyPress( e );\r\n\r\n\t\tif ( ViewportFrame is not { CanPanZoom: true } frame ) return;\r\n\r\n\t\tvar mods  = e.KeyboardModifiers;\r\n\t\tvar ctrl  = (mods \u0026 Sandbox.KeyboardModifiers.Ctrl)  != 0;\r\n\t\tvar shift = (mods \u0026 Sandbox.KeyboardModifiers.Shift) != 0;\r\n\t\tvar alt   = (mods \u0026 Sandbox.KeyboardModifiers.Alt)   != 0;\r\n\r\n\t\tif ( !ctrl || alt ) return; // every shortcut is Ctrl-based; Alt clears it.\r\n\r\n\t\tswitch ( e.Key )\r\n\t\t{\r\n\t\t\tcase KeyCode.Num0:\r\n\t\t\t\tif ( shift )\r\n\t\t\t\t{\r\n\t\t\t\t\tLog.Info( $\u0022{LogPrefix} OnKeyPress Ctrl\u002BShift\u002B0 \u2192 ZoomToSelection\u0022 );\r\n\t\t\t\t\tZoomToSelectionViaFrame( frame );\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tLog.Info( $\u0022{LogPrefix} OnKeyPress Ctrl\u002B0 \u2192 ApplyFit\u0022 );\r\n\t\t\t\t\tframe.ApplyFit();\r\n\t\t\t\t}\r\n\t\t\t\te.Accepted = true;\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase KeyCode.Num1:\r\n\t\t\t\tif ( shift ) return;\r\n\t\t\t\tLog.Info( $\u0022{LogPrefix} OnKeyPress Ctrl\u002B1 \u2192 ResetZoomOnly\u0022 );\r\n\t\t\t\tframe.ResetZoomOnly();\r\n\t\t\t\te.Accepted = true;\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase KeyCode.Equal:\r\n\t\t\t\tif ( shift ) return;\r\n\t\t\t\tframe.ZoomBy( 1.25f );\r\n\t\t\t\te.Accepted = true;\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase KeyCode.Minus:\r\n\t\t\t\tif ( shift ) return;\r\n\t\t\t\tframe.ZoomBy( 1f / 1.25f );\r\n\t\t\t\te.Accepted = true;\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void ZoomToSelectionViaFrame( CanvasViewportFrame frame )\r\n\t{\r\n\t\tvar record = frame.Selection?.Selected;\r\n\t\tvar live = record?.LivePanel;\r\n\t\tif ( live is null || !live.IsValid )\r\n\t\t{\r\n\t\t\tLog.Info( $\u0022{LogPrefix} Ctrl\u002BShift\u002B0: no valid selection\u0022 );\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tframe.ApplyZoomToRect( live.Box.Rect, /* maxZoom */ 4f, /* padding */ 0.10f );\r\n\t}\r\n\r\n\tpublic override void OnDestroyed()\r\n\t{\r\n\t\tLog.Info( $\u0022{LogPrefix} DesignerCanvas.OnDestroyed\u0022 );\r\n\t\tbase.OnDestroyed();\r\n\t\t_designerScene.Dispose();\r\n\t}\r\n}\r\n"}]}