extracted responsibilities from Program.cs (208→46 lines) and ReplLoop.cs (274→174 lines) into focused service classes: HeaderRenderer, SessionManager, ApplicationStartup, ResponseStreamer, SpinnerService, UsageDisplayer, and ContextCompactionService. Each class now has a single, well-defined responsibility, improving testability and maintainability.
111 lines
3.3 KiB
C#
111 lines
3.3 KiB
C#
using System.Reflection;
|
|
using Spectre.Console;
|
|
using AnchorCli.OpenRouter;
|
|
|
|
namespace AnchorCli;
|
|
|
|
/// <summary>
|
|
/// Renders the application header, including ASCII art logo and configuration info table.
|
|
/// </summary>
|
|
internal sealed class HeaderRenderer
|
|
{
|
|
private readonly string _model;
|
|
private readonly string _endpoint;
|
|
private readonly string _providerName;
|
|
private readonly ModelInfo? _modelInfo;
|
|
private readonly TokenTracker? _tokenTracker;
|
|
|
|
public HeaderRenderer(
|
|
string model,
|
|
string endpoint,
|
|
string providerName,
|
|
ModelInfo? modelInfo = null,
|
|
TokenTracker? tokenTracker = null)
|
|
{
|
|
_model = model;
|
|
_endpoint = endpoint;
|
|
_providerName = providerName;
|
|
_modelInfo = modelInfo;
|
|
_tokenTracker = tokenTracker;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renders the full header including logo, subtitle, and info table.
|
|
/// </summary>
|
|
public void Render()
|
|
{
|
|
RenderLogo();
|
|
RenderSubtitle();
|
|
RenderInfoTable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renders the ASCII art logo.
|
|
/// </summary>
|
|
public void RenderLogo()
|
|
{
|
|
var fontStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("AnchorCli.Assets.3d.flf");
|
|
if (fontStream != null)
|
|
{
|
|
var font = FigletFont.Load(fontStream);
|
|
AnsiConsole.Write(
|
|
new FigletText(font, "anchor")
|
|
.Color(Color.CornflowerBlue));
|
|
}
|
|
else
|
|
{
|
|
AnsiConsole.Write(
|
|
new FigletText("anchor")
|
|
.Color(Color.CornflowerBlue));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renders the subtitle rule.
|
|
/// </summary>
|
|
public void RenderSubtitle()
|
|
{
|
|
AnsiConsole.Write(
|
|
new Rule("[dim]AI-powered coding assistant[/]")
|
|
.RuleStyle(Style.Parse("cornflowerblue dim"))
|
|
.LeftJustified());
|
|
AnsiConsole.WriteLine();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renders the configuration info table.
|
|
/// </summary>
|
|
public void RenderInfoTable()
|
|
{
|
|
var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
|
|
|
|
var table = new Table()
|
|
.Border(TableBorder.Rounded)
|
|
.BorderColor(Color.Grey)
|
|
.AddColumn(new TableColumn("[dim]Setting[/]").NoWrap())
|
|
.AddColumn(new TableColumn("[dim]Value[/]"));
|
|
|
|
table.AddRow("[grey]Model[/]", $"[cyan]{Markup.Escape(_modelInfo?.Name ?? _model)}[/]");
|
|
table.AddRow("[grey]Provider[/]", $"[blue]{_providerName}[/]");
|
|
table.AddRow("[grey]Endpoint[/]", $"[dim]{_endpoint}[/]");
|
|
table.AddRow("[grey]Version[/]", $"[magenta]{version}[/]");
|
|
|
|
if (_modelInfo?.Pricing != null && _tokenTracker != null)
|
|
{
|
|
var inM = _tokenTracker.InputPrice * 1_000_000m;
|
|
var outM = _tokenTracker.OutputPrice * 1_000_000m;
|
|
table.AddRow("[grey]Pricing[/]",
|
|
$"[yellow]${inM:F2}[/][dim]/M in[/] [yellow]${outM:F2}[/][dim]/M out[/]");
|
|
}
|
|
|
|
if (_modelInfo != null)
|
|
{
|
|
table.AddRow("[grey]Context[/]",
|
|
$"[dim]{_modelInfo.ContextLength:N0} tokens[/]");
|
|
}
|
|
|
|
AnsiConsole.Write(table);
|
|
AnsiConsole.WriteLine();
|
|
}
|
|
}
|