refactor: apply Single Responsibility Principle to Program.cs and ReplLoop.cs
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.
This commit is contained in:
110
HeaderRenderer.cs
Normal file
110
HeaderRenderer.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user