Add import template into tui. Fix tests that fail on macos. Fix some updates.

This commit is contained in:
2026-05-29 18:16:00 +02:00
parent 85746c3306
commit 2f8dad7d8d
20 changed files with 1748 additions and 46 deletions

View File

@@ -0,0 +1,121 @@
/**
* Formats multi-line dialog messages for readable terminal display.
*
* Ink's `wrap="wrap"` breaks long lines mid-word, which looks broken for
* dot-separated template validation paths. We pre-split on newlines and break
* long lines at `.` segment boundaries instead.
*/
/**
* Hard-wraps text when a single segment still exceeds the maximum width.
*/
function hardWrapLine(line: string, maxWidth: number): string[] {
if (line.length <= maxWidth) {
return [line];
}
const wrapped: string[] = [];
let remaining = line;
while (remaining.length > maxWidth) {
wrapped.push(remaining.slice(0, maxWidth));
remaining = ` ${remaining.slice(maxWidth)}`;
}
if (remaining.length > 0) {
wrapped.push(remaining);
}
return wrapped;
}
/**
* Breaks a long line at dot-separated segments, indenting continuations.
*/
function breakLongLineAtDots(line: string, maxWidth: number): string[] {
const segments: string[] = [];
let segmentStart = 0;
for (let index = 0; index < line.length; index += 1) {
if (line[index] === "." && index > 0) {
segments.push(line.slice(segmentStart, index + 1));
segmentStart = index + 1;
}
}
if (segmentStart < line.length) {
segments.push(line.slice(segmentStart));
}
if (segments.length === 0) {
return hardWrapLine(line, maxWidth);
}
const lines: string[] = [];
let current = "";
for (const segment of segments) {
const candidate = current + segment;
if (candidate.length > maxWidth && current.length > 0) {
lines.push(current);
current = ` ${segment}`;
continue;
}
if (candidate.length > maxWidth) {
lines.push(...hardWrapLine(segment, maxWidth));
current = "";
continue;
}
current = candidate;
}
if (current.length > 0) {
lines.push(current);
}
return lines;
}
/**
* Splits a dialog message into display lines that fit the available width.
*/
export function formatDialogMessageLines(
message: string,
contentWidth: number,
): string[] {
const output: string[] = [];
for (const rawLine of message.split("\n")) {
const line = rawLine.trimEnd();
if (line.length === 0) {
continue;
}
if (line.length <= contentWidth) {
output.push(line);
continue;
}
output.push(...breakLongLineAtDots(line, contentWidth));
}
return output;
}
/**
* Computes dialog width from the terminal size.
*/
export function getMessageDialogWidth(terminalColumns: number): number {
return Math.min(Math.max(terminalColumns - 4, 60), 100);
}
/** Inner text width after dialog border and horizontal padding. */
export function getMessageContentWidth(dialogWidth: number): number {
return Math.max(dialogWidth - 6, 40);
}
/** Maximum number of body lines shown before truncating with a summary. */
export const MAX_MESSAGE_DIALOG_LINES = 24;