summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components/shared/MaxSizedBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/components/shared/MaxSizedBox.tsx')
-rw-r--r--packages/cli/src/ui/components/shared/MaxSizedBox.tsx81
1 files changed, 79 insertions, 2 deletions
diff --git a/packages/cli/src/ui/components/shared/MaxSizedBox.tsx b/packages/cli/src/ui/components/shared/MaxSizedBox.tsx
index eb5ef6b4..346472bf 100644
--- a/packages/cli/src/ui/components/shared/MaxSizedBox.tsx
+++ b/packages/cli/src/ui/components/shared/MaxSizedBox.tsx
@@ -432,8 +432,85 @@ function layoutInkElementAsStyledText(
const availableWidth = maxWidth - noWrappingWidth;
if (availableWidth < 1) {
- // No room to render the wrapping segments. TODO(jacob314): consider an alternative fallback strategy.
- output.push(nonWrappingContent);
+ // No room to render the wrapping segments. Truncate the non-wrapping
+ // content and append an ellipsis so the line always fits within maxWidth.
+
+ // Handle line breaks in non-wrapping content when truncating
+ const lines: StyledText[][] = [];
+ let currentLine: StyledText[] = [];
+ let currentLineWidth = 0;
+
+ for (const segment of nonWrappingContent) {
+ const textLines = segment.text.split('\n');
+ textLines.forEach((text, index) => {
+ if (index > 0) {
+ // New line encountered, finish current line and start new one
+ lines.push(currentLine);
+ currentLine = [];
+ currentLineWidth = 0;
+ }
+
+ if (text) {
+ const textWidth = stringWidth(text);
+
+ // When there's no room for wrapping content, be very conservative
+ // For lines after the first line break, show only ellipsis if the text would be truncated
+ if (index > 0 && textWidth > 0) {
+ // This is content after a line break - just show ellipsis to indicate truncation
+ currentLine.push({ text: '…', props: {} });
+ currentLineWidth = stringWidth('…');
+ } else {
+ // This is the first line or a continuation, try to fit what we can
+ const maxContentWidth = Math.max(0, maxWidth - stringWidth('…'));
+
+ if (textWidth <= maxContentWidth && currentLineWidth === 0) {
+ // Text fits completely on this line
+ currentLine.push({ text, props: segment.props });
+ currentLineWidth += textWidth;
+ } else {
+ // Text needs truncation
+ const codePoints = toCodePoints(text);
+ let truncatedWidth = currentLineWidth;
+ let sliceEndIndex = 0;
+
+ for (const char of codePoints) {
+ const charWidth = stringWidth(char);
+ if (truncatedWidth + charWidth > maxContentWidth) {
+ break;
+ }
+ truncatedWidth += charWidth;
+ sliceEndIndex++;
+ }
+
+ const slice = codePoints.slice(0, sliceEndIndex).join('');
+ if (slice) {
+ currentLine.push({ text: slice, props: segment.props });
+ }
+ currentLine.push({ text: '…', props: {} });
+ currentLineWidth = truncatedWidth + stringWidth('…');
+ }
+ }
+ }
+ });
+ }
+
+ // Add the last line if it has content or if the last segment ended with \n
+ if (
+ currentLine.length > 0 ||
+ (nonWrappingContent.length > 0 &&
+ nonWrappingContent[nonWrappingContent.length - 1].text.endsWith('\n'))
+ ) {
+ lines.push(currentLine);
+ }
+
+ // If we don't have any lines yet, add an ellipsis line
+ if (lines.length === 0) {
+ lines.push([{ text: '…', props: {} }]);
+ }
+
+ for (const line of lines) {
+ output.push(line);
+ }
return;
}