Editor/Mcp/BridgeUiTools.cs

Editor tool wrapper for UI-related MCP (management/control panel) commands. It exposes static methods that call McpGate.Run to patch razor files, create screen or world panels, and scaffold razor UI components.

File AccessNetworking
// AUTO-GENERATED by scripts/emit-mcp-wrappers.mjs — DO NOT EDIT.
// Regenerate: node scripts/extract-manifest.mjs && node scripts/emit-mcp-wrappers.mjs
// Source of truth: sbox-mcp-server/src/tools/ (zod schemas) → scripts/tools-manifest.json

using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Editor.Mcp;

/// <summary>
/// Generate Razor UI panels: screen-space HUDs, world-space panels, and custom PanelComponent
/// scaffolds.
/// </summary>
[McpToolset( "bridge_ui", "Generate Razor UI panels: screen-space HUDs, world-space panels, and custom PanelComponent scaffolds." )]
public static class BridgeUiTools
{
	/// <summary>
	/// PATCH an existing .razor file (razor_lint's companion FIXER — razor_lint flags 'PanelComponent
	/// without BuildHash', this adds it). NOT a scaffold: it EDITS the file in place, inserting
	/// 'protected override int BuildHash() =&gt; System.HashCode.Combine( ... )' at the end of the
	/// first @code block, folding the fields and auto-properties declared there. HEURISTIC string/regex
	/// parsing — review the diff: computed (=&gt;) properties, statics, consts, events and Action/Func
	/// members are skipped; reference-typed members fold by REFERENCE (a mutated List re-renders only
	/// if Count-style state is also folded); TimeSince/TimeUntil/RealTimeSince/RealTimeUntil members
	/// fold as whole seconds (1 Hz re-render, not every frame); braces inside @code string literals can
	/// confuse the block parser. If no members are found a '=&gt; 0' stub with a TODO is inserted
	/// (silences the lint but does NOT re-render — replace it). Returns { patched, path,
	/// hashedMembers[], buildHash, note } on success, { alreadyPresent: true, path } when a BuildHash
	/// already exists (file left UNCHANGED — extend it by hand), or { error } for non-PanelComponent
	/// files, missing @code, or a path outside the project. Follow with trigger_hotload, then
	/// razor_lint to confirm the finding cleared.
	/// </summary>
	/// <param name="path">Project-relative path to the .razor file to patch, e.g. 'Code/UI/MyHud.razor' (from razor_lint's finding.file or list_project_files). Must '@inherits PanelComponent'; .razor.scss files are rejected.</param>
	[McpTool( "add_panel_buildhash" )]
	public static Task<object> AddPanelBuildhash( string path )
		=> McpGate.Run( "add_panel_buildhash", McpGate.Args( ( "path", path ) ) );

	/// <summary>
	/// Create a new GameObject with a ScreenPanel component for full-screen UI overlay (HUD, menus,
	/// etc.). Returns { created, gameObject:{ id, name, components, ... } }. NOTE: panelComponent is
	/// looked up in the TypeLibrary and SILENTLY skipped if the type isn't loaded (freshly generated
	/// panels need trigger_hotload first) — check gameObject.components to confirm it attached.
	/// </summary>
	/// <param name="name">Name for the UI GameObject. Defaults to 'Screen Panel'.</param>
	/// <param name="zIndex">Z-index for layering multiple screen panels.</param>
	/// <param name="panelComponent">Name of a Razor PanelComponent to add (e.g. 'GameHud').</param>
	/// <param name="parent">GUID of parent GameObject.</param>
	[McpTool( "add_screen_panel" )]
	public static Task<object> AddScreenPanel( string name = null, double? zIndex = null, string panelComponent = null, string parent = null )
		=> McpGate.Run( "add_screen_panel", McpGate.Args( ( "name", name ), ( "zIndex", zIndex ), ( "panelComponent", panelComponent ), ( "parent", parent ) ) );

	/// <summary>
	/// Create a new GameObject with a WorldPanel component for in-world 3D UI (health bars, signs,
	/// nameplates). Returns { created, gameObject:{ id, name, components, ... } }. As with
	/// add_screen_panel, panelComponent is SILENTLY skipped if the type isn't in the TypeLibrary
	/// (trigger_hotload first, then verify via gameObject.components). For CLICKABLE world UI the scene
	/// also needs a Sandbox.WorldInput (see create_worldpanel_ui).
	/// </summary>
	/// <param name="name">Name for the UI GameObject. Defaults to 'World Panel'.</param>
	/// <param name="position">World position for the panel — object {x,y,z} or comma string "x,y,z". As "x,y,z" (or JSON {x,y,z}).</param>
	/// <param name="rotation">Rotation as euler angles. As "pitch,yaw,roll" degrees.</param>
	/// <param name="worldScale">Scale of the world panel. Smaller = smaller in world.</param>
	/// <param name="lookAtCamera">Whether the panel always faces the camera (billboard).</param>
	/// <param name="panelComponent">Name of a Razor PanelComponent to add (e.g. 'NpcNameplate').</param>
	/// <param name="parent">GUID of parent GameObject.</param>
	[McpTool( "add_world_panel" )]
	public static Task<object> AddWorldPanel( string name = null, string position = null, string rotation = null, double? worldScale = null, bool? lookAtCamera = null, string panelComponent = null, string parent = null )
		=> McpGate.Run( "add_world_panel", McpGate.Args( ( "name", name ), ( "position", position ), ( "rotation", rotation ), ( "worldScale", worldScale ), ( "lookAtCamera", lookAtCamera ), ( "panelComponent", panelComponent ), ( "parent", parent ) ) );

	/// <summary>
	/// Create a Razor UI component file (.razor). Returns { created, path, componentName }; errors if
	/// the file already exists. NOTE: the current handler generates one fixed basic panel (a root div +
	/// label bound to a Title [Property]) from name+directory only —
	/// panelType/content/styles/includeStyles are not applied and no .scss is written; for custom
	/// markup or a PanelComponent you can host via add_screen_panel, write the files with write_file
	/// and check them with razor_lint. Follow with trigger_hotload, then get_compile_errors.
	/// </summary>
	/// <param name="name">Component name (e.g. 'GameHud', 'MainMenu').</param>
	/// <param name="directory">Subdirectory under code/ for the file. Defaults to 'UI'.</param>
	/// <param name="panelType">Type of panel to generate: 'basic' (simple panel), 'hud' (health/score overlay), 'menu' (title + buttons). Defaults to 'basic'. One of: basic | hud | menu.</param>
	/// <param name="description">Description of what this UI panel does.</param>
	/// <param name="includeStyles">Generate a companion .razor.scss stylesheet. Defaults to true.</param>
	/// <param name="content">Raw .razor content (skips boilerplate generation).</param>
	/// <param name="styles">Raw .razor.scss content (only used with raw content mode).</param>
	[McpTool( "create_razor_ui" )]
	public static Task<object> CreateRazorUi( string name, string directory = null, string panelType = null, string description = null, bool? includeStyles = null, string content = null, string styles = null )
		=> McpGate.Run( "create_razor_ui", McpGate.Args( ( "name", name ), ( "directory", directory ), ( "panelType", panelType ), ( "description", description ), ( "includeStyles", includeStyles ), ( "content", content ), ( "styles", styles ) ) );
}