renderModal function
Renders body as a centered bordered dialog over baseView.
Lines wider than the box are cut (never wrapped); the box is clamped to the screen. ANSI SGR sequences pass through width accounting.
Implementation
String renderModal(
String baseView,
List<String> body, {
ModalChrome chrome = const ModalChrome(),
required int screenW,
required int screenH,
}) {
if (screenW < 10 || screenH < 5) return baseView;
final padding = chrome.padding < 0 ? 0 : chrome.padding;
var contentWidth = 0;
for (final line in [...body, ...chrome.footer]) {
final width = Style.visibleLength(line);
if (width > contentWidth) contentWidth = width;
}
var boxWidth = chrome.width > 0
? chrome.width
: contentWidth + padding * 2 + 2; // content + padding + borders
boxWidth = boxWidth.clamp(4, screenW - 2);
final innerWidth = boxWidth - 2;
final usableWidth = (innerWidth - padding * 2).clamp(1, innerWidth);
final content = <String>[
for (final line in body) _cut(line, usableWidth),
if (chrome.footer.isNotEmpty) ...[
_rule(usableWidth),
for (final line in chrome.footer) _cut(line, usableWidth),
],
];
// Box chrome (borders, title, padding) comes from the Style-driven
// PanelComponent — never hand-drawn here.
var boxLines = PanelComponent(
content: content.join('\n'),
title: chrome.title.isEmpty ? null : chrome.title,
padding: padding,
width: innerWidth + 2,
renderConfig: RenderConfig(terminalWidth: screenW),
).render().split('\n');
if (chrome.maxHeight > 0 && boxLines.length > chrome.maxHeight) {
boxLines = boxLines.sublist(0, chrome.maxHeight);
}
if (boxLines.length > screenH) {
boxLines = boxLines.sublist(0, screenH);
}
final x = ((screenW - Style.visibleLength(boxLines.first)) ~/ 2).clamp(
0,
screenW,
);
var y = ((screenH - boxLines.length) ~/ 2).clamp(0, screenH);
final base = baseView.split('\n');
while (base.length < screenH) {
base.add('');
}
for (var i = 0; i < boxLines.length && y + i < base.length; i++) {
if (y + i < 0) continue;
base[y + i] = _overlayLine(base[y + i], boxLines[i], x, screenW);
}
return base.join('\n');
}