namespace AcDream.UI.Abstractions.Panels.Settings; /// /// Retail's linked default/active window-opacity invariant: active >= default, /// ALWAYS — enforced by dragging the OTHER value, never by clamping the one being /// set. Verbatim port of ChatInterface::SetDefaultOpacity @0x004F3BC0 / /// SetActiveOpacity @0x004F3C40 (docs/research/2026-08-09-chat-retail-window-shell.md /// §3): raising the default ABOVE the current active value drags active UP to match; /// lowering the active value BELOW the current default drags default DOWN to match. /// Retail's options-page sliders share a DualHash link /// (gmChatOptionsUI::InitOptions @0x0049FC60) — this is why they visibly track /// each other while dragging. /// /// /// Shared by two independent consumers so both apply the identical link math: /// RetailWindowOpacityController (AcDream.App — the live per-window /// mechanism, mutating its own two float fields) and 's /// Chat tab (the draft slider UI, mutating a record). /// Pure functions — no window/render/state dependency — so both layers can call /// them without violating the App→Abstractions dependency direction. /// /// public static class ChatOpacityLink { /// /// Port of ChatInterface::SetDefaultOpacity @0x004F3BC0: set the default /// (unfocused) opacity to , dragging /// UP if it would otherwise fall below the new /// default. Never returns an (default, active) pair with active < default. /// public static (float DefaultOpacity, float ActiveOpacity) SetDefault( float currentActive, float newDefault) { newDefault = System.Math.Clamp(newDefault, 0f, 1f); float active = currentActive < newDefault ? newDefault : currentActive; return (newDefault, active); } /// /// Port of ChatInterface::SetActiveOpacity @0x004F3C40: set the active /// (focused) opacity to , dragging /// DOWN if it would otherwise exceed the new /// active value. Never returns an (default, active) pair with active < default. /// public static (float DefaultOpacity, float ActiveOpacity) SetActive( float currentDefault, float newActive) { newActive = System.Math.Clamp(newActive, 0f, 1f); float def = currentDefault > newActive ? newActive : currentDefault; return (def, newActive); } }