Editor/AutoRig/SettingsDialog.cs

An editor UI dialog for autorig settings. It displays and edits cloud rigging options (server port, disk GB, docker image, timeouts), persists them to autorig_dl/settings.json, and applies them by calling VastSettings.Apply and setting VastRigSession timeouts.

File Access
using System;
using System.IO;
using AutoRig.Vast;
using Editor;
using Editor.AutoRig.Vast;
using Sandbox;

namespace AutoRig.Editor;

/// <summary>
/// The gear dialog: everything user-adjustable that is not a per-model toggle.
/// Today that is the cloud-rigging transport (server port, disk, docker image,
/// timeouts), persisted to autorig_dl/settings.json and applied on save AND on
/// editor load (AutoRigWindow calls <see cref="LoadAndApply"/>).
/// </summary>
public sealed class SettingsDialog : Widget
{
    public static string SettingsPath => Path.Combine(
        Project.Current?.GetAssetsPath() ?? ".", "autorig_dl", "settings.json" );

    readonly LineEdit _port;
    readonly LineEdit _disk;
    readonly LineEdit _image;
    readonly LineEdit _bootMinutes;
    readonly LineEdit _rigMinutes;
    readonly Label _status;

    /// <summary>Reads settings.json (missing/corrupt → defaults) and applies it.</summary>
    public static VastSettings LoadAndApply()
    {
        var settings = File.Exists( SettingsPath )
            ? VastSettings.Parse( File.ReadAllText( SettingsPath ) )
            : new VastSettings();
        settings.Apply();
        VastRigSession.BootTimeout = TimeSpan.FromMinutes(
            Math.Clamp( settings.BootTimeoutMinutes, 3, 60 ) );
        VastRigSession.RigTimeout = TimeSpan.FromMinutes(
            Math.Clamp( settings.RigTimeoutMinutes, 5, 240 ) );
        return settings;
    }

    public SettingsDialog( Widget parent ) : base( parent )
    {
        WindowTitle = "Auto Rigger Settings";
        SetWindowIcon( "settings" );
        WindowFlags = WindowFlags.Dialog | WindowFlags.CloseButton | WindowFlags.WindowTitle;
        MinimumSize = new Vector2( 460, 300 );

        var settings = LoadAndApply();

        Layout = Layout.Column();
        Layout.Margin = UiSpacing.Section;
        Layout.Spacing = UiSpacing.Group;

        var header = new Label( "Cloud rigging (vast.ai)", this );
        header.SetStyles( $"font-weight: 600; color: {Theme.Blue.Hex};" );
        Layout.Add( header );
        var hint = new Label(
            "These control the rented instance's rig server. Defaults are fine "
            + "unless a network blocks the port or a model needs more disk/time.", this );
        hint.SetStyles( $"color: {Theme.TextLight.Hex};" );
        hint.WordWrap = true;
        Layout.Add( hint );

        _port = AddRow( "Server port", settings.ServerPort.ToString(),
            "TCP port the remote rig server listens on (1024-65535)." );
        _disk = AddRow( "Disk (GB)", settings.DiskGb.ToString(),
            "Disk requested with the rental - checkpoints need headroom (16-512)." );
        _image = AddRow( "Docker image", settings.DockerImage,
            "Container image for the instance. Needs CUDA PyTorch for the model pipelines." );
        _bootMinutes = AddRow( "Boot timeout (min)", settings.BootTimeoutMinutes.ToString(),
            "How long to wait for the instance to become ready (3-60)." );
        _rigMinutes = AddRow( "Rig timeout (min)", settings.RigTimeoutMinutes.ToString(),
            "How long the remote rig may run before we give up (5-240)." );

        Layout.AddStretchCell();
        _status = new Label( "", this );
        Layout.Add( _status );

        var buttons = Layout.AddRow();
        buttons.Spacing = UiSpacing.Related;
        buttons.AddStretchCell();
        var reset = new Button( "Reset to defaults", "restart_alt", this );
        reset.Clicked = () =>
        {
            var fresh = new VastSettings();
            _port.Text = fresh.ServerPort.ToString();
            _disk.Text = fresh.DiskGb.ToString();
            _image.Text = fresh.DockerImage;
            _bootMinutes.Text = fresh.BootTimeoutMinutes.ToString();
            _rigMinutes.Text = fresh.RigTimeoutMinutes.ToString();
        };
        buttons.Add( reset );
        var save = new Button.Primary( "Save", "save", this );
        save.Clicked = Save;
        buttons.Add( save );
    }

    LineEdit AddRow( string label, string value, string tooltip )
    {
        var row = Layout.AddRow();
        row.Spacing = UiSpacing.Related;
        var caption = new Label( label, this );
        caption.MinimumWidth = 140;
        caption.ToolTip = tooltip;
        row.Add( caption );
        var edit = new LineEdit( this ) { Text = value, ToolTip = tooltip };
        row.Add( edit, 1 );
        return edit;
    }

    void Save()
    {
        var settings = new VastSettings
        {
            ServerPort = ParseInt( _port.Text, 8188 ),
            DiskGb = ParseInt( _disk.Text, 32 ),
            DockerImage = string.IsNullOrWhiteSpace( _image.Text )
                ? new VastSettings().DockerImage
                : _image.Text.Trim(),
            BootTimeoutMinutes = ParseInt( _bootMinutes.Text, 12 ),
            RigTimeoutMinutes = ParseInt( _rigMinutes.Text, 30 ),
        };
        Directory.CreateDirectory( Path.GetDirectoryName( SettingsPath )! );
        File.WriteAllText( SettingsPath, settings.Serialize() );
        LoadAndApply();
        _status.Text = "Saved.";
        _status.SetStyles( $"color: {Theme.Green.Hex};" );
    }

    static int ParseInt( string text, int fallback )
        => int.TryParse( text?.Trim(), out var value ) ? value : fallback;
}