using LlamaApp.Llama; namespace LlamaApp.Views { /// /// Pure mapping from the llama server's state to the footer's status /// indicator — the dot color, its tooltip, and whether the relaunch /// button is shown. Kept separate from so the /// rules are unit-testable (no XAML objects involved: /// is a plain struct). /// public static class ServerStatusPresentation { /// The rendered footer status: dot color, dot tooltip, and /// relaunch-button visibility. public readonly record struct Description( Windows.UI.Color Dot, string ToolTip, bool CanRelaunch); // GitHub-style palette; the red matches the delete flyout's #F85149. // The struct is initialized field-by-field rather than via // Microsoft.UI.ColorHelper.FromArgb on purpose: ColorHelper is a WinRT // projected class that needs the Windows App SDK runtime, which the // plain unit-test host doesn't have ("class not registered") — the // Color struct itself is pure managed data. private static readonly Windows.UI.Color Green = new() { A = 255, R = 0x3F, G = 0xB9, B = 0x50 }; private static readonly Windows.UI.Color Amber = new() { A = 255, R = 0xD2, G = 0x99, B = 0x22 }; private static readonly Windows.UI.Color Red = new() { A = 255, R = 0xF8, G = 0x51, B = 0x49 }; private static readonly Windows.UI.Color Gray = new() { A = 255, R = 0x8B, G = 0x94, B = 0x9E }; /// /// Maps (plus /// for the one case Stopped alone can't express — a failed first-run /// install) to the footer rendering. The relaunch button shows only /// when the server is down through no transient cause: crashed /// () or a failed /// install. It never shows during the startup /// Stopped → Starting → Running sequence, so it doesn't flash at /// launch. /// is /// — the user-facing reason /// behind a Failed state. It was set by the manager but never read /// anywhere; the dot's tooltip is the one place a user goes to ask /// "why is it red?", so the reason is included there. /// public static Description Describe( LlamaManager.ServerState status, LlamaManager.InstallState installState, string? failureMessage = null) { var detail = Sanitize(failureMessage); return status switch { LlamaManager.ServerState.Running => new(Green, "llama server: running", false), LlamaManager.ServerState.Starting => new(Amber, "llama server: starting…", false), LlamaManager.ServerState.Failed => new(Red, detail is null ? "llama server: not running (crashed or failed to start) — use the relaunch button" : $"llama server: not running — {detail} Use the relaunch button.", true), _ => installState switch { // First-run binary install: amber, and say what's happening — // the download can be hundreds of MB and otherwise has no // progress indication anywhere in the app. LlamaManager.InstallState.Installing => new(Amber, "llama server: installing llama.cpp — the first run can take a few minutes…", false), LlamaManager.InstallState.Failed => new(Gray, detail is null ? "llama server: stopped (install failed) — use the relaunch button" : $"llama server: stopped — install failed: {detail} Use the relaunch button.", true), _ => new(Gray, "llama server: stopped", false), }, }; } /// /// Prepares a raw failure message for a tooltip: first line only /// (an install exception can span several), truncated, and guaranteed /// to end with a period so the "Use the relaunch button." follow-up /// reads as a separate sentence. Null/blank → null (no detail). /// private static string? Sanitize(string? message) { if (string.IsNullOrWhiteSpace(message)) return null; var firstLine = message.Split('\n')[0].Trim().TrimEnd('\r'); const int Max = 120; if (firstLine.Length > Max) firstLine = firstLine[..Max] + "…"; return firstLine.EndsWith('.') || firstLine.EndsWith('…') ? firstLine : firstLine + "."; } } }