// Copyright (c) Microsoft. All rights reserved.
using Harness.ConsoleReactiveFramework;
namespace Harness.ConsoleReactiveComponents;
///
/// Props for .
///
public record TextPanelProps : ConsoleReactiveProps
{
/// Gets the items to render in the panel. Each item is a pre-rendered
/// console string (may include ANSI escape sequences and newlines).
public IReadOnlyList Items { get; init; } = [];
}
///
/// A component that renders a list of pre-rendered string items vertically.
/// Designed for rendering dynamic items in a non-scroll region that may be
/// re-rendered on each update. If the component's
/// exceeds the number of output lines, leftover lines are erased.
///
public class TextPanel : ConsoleReactiveComponent
{
///
/// Calculates the height (in lines) needed to render all items,
/// accounting for terminal line wrapping at the specified width.
///
/// The items to measure.
/// The terminal width in columns. When 0 or negative, wrapping is ignored.
/// The total number of physical lines all items will occupy.
public static int CalculateHeight(IReadOnlyList items, int terminalWidth = 0)
{
int total = 0;
for (int i = 0; i < items.Count; i++)
{
total += AnsiEscapes.CountPhysicalLines(items[i], terminalWidth);
}
return total;
}
///
public override void RenderCore(TextPanelProps props, ConsoleReactiveState state)
{
int currentRow = 0;
for (int i = 0; i < props.Items.Count; i++)
{
string text = props.Items[i];
string[] lines = text.Split('\n');
int itemLineCount = AnsiEscapes.CountPhysicalLines(text, props.Width);
int itemRow = 0;
for (int j = 0; j < lines.Length && itemRow < itemLineCount; j++)
{
int linePhysicalRows = props.Width > 0
? Math.Max(1, (AnsiEscapes.VisibleLength(lines[j]) - 1) / props.Width + 1)
: 1;
Console.Write(AnsiEscapes.MoveAndEraseLine(props.Y + currentRow));
Console.Write(lines[j]);
currentRow += linePhysicalRows;
itemRow += linePhysicalRows;
}
}
// If the component height exceeds the output, erase leftover lines
if (props.Height > currentRow)
{
for (int i = currentRow; i < props.Height; i++)
{
Console.Write(AnsiEscapes.MoveAndEraseLine(props.Y + i));
}
}
}
}