Editor/UI/LibraryManagerInjector.cs
using System;
using Editor;
using DiagnosticsLog = Sandbox.SecBox.Bridge.DiagnosticsLog;

namespace Sandbox.SecBox.UI;

// Orchestrates injection of SecBox UI into the Library Manager dock. The dock
// is created lazily by the editor (only when the user opens View > Library
// Manager). Even after the dock exists, its children are transient:
//
//   - LibraryManagerDock has an [EditorEvent.Frame] -> Rebuild loop that
//     can recreate ListLocal / ListGlobal.
//   - LibraryDetail is replaced wholesale on every row click.
//   - LibraryDetail.FetchAndBuild() Layout.Clear()s itself after construction.
//
// Therefore we drive both decorators from per-frame EnsureInstalled calls
// rather than from one-shot installers. The work is cheap (descendant walks
// of a single dock; per-instance idempotence checks). When the dock is closed
// the cached state is reset, and the next time it reopens we wire fresh.
public static class LibraryManagerInjector
{
	static Widget _cachedDock;
	static int _frameCounter;

	// Called from SecboxBoot.Init purely for the log line confirming wiring.
	// Actual work happens on the editor's main loop via [EditorEvent.Frame].
	public static void Arm()
	{
		DiagnosticsLog.Info( "[secbox] LibraryManagerInjector armed - awaiting first frame" );
	}

	[EditorEvent.Frame]
	public static void OnFrame()
	{
		// Throttle. Frame fires at editor render rate (60+ Hz). The walk is
		// cheap but still pointless at that rate when nothing changes.
		// ~8x throttle while idle; user-perceived UI lag is negligible.
		if ( (_frameCounter++ & 0b111) != 0 ) return;

		Widget dock;
		try
		{
			var main = EditorWindow;
			dock = main?.DockManager?.GetDockWidget( "Library Manager" );
		}
		catch ( Exception ex )
		{
			DiagnosticsLog.Warn( $"[secbox] LibraryManagerInjector.OnFrame: lookup threw: {ex.Message}" );
			return;
		}

		if ( dock == null || !dock.IsValid )
		{
			// User closed the Library Manager tab (or never opened it).
			if ( _cachedDock != null )
			{
				_cachedDock = null;
				DiagnosticsLog.Trace( "[secbox] LibraryManagerInjector: dock gone - state reset" );
			}
			return;
		}

		if ( !ReferenceEquals( dock, _cachedDock ) )
		{
			_cachedDock = dock;
			DiagnosticsLog.Trace( "[secbox] LibraryManagerInjector: new dock instance detected" );
		}

		try
		{
			LibraryRowBadge.EnsureInstalled( dock );
			LibraryDetailPanel.EnsureInstalled( dock );
		}
		catch ( Exception ex )
		{
			DiagnosticsLog.Error( "[secbox] LibraryManagerInjector.OnFrame: ensure threw", ex );
		}
	}

	[EditorEvent.Hotload]
	public static void OnHotload()
	{
		_cachedDock = null;
		DiagnosticsLog.Trace( "[secbox] LibraryManagerInjector: hotload - state reset" );
	}
}