From d77a4a200ede662f6e8d4b98bf0c7fef5cfb2afd Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 6 Sep 2024 11:11:27 -0600 Subject: [PATCH] fix: compaction on narrow and tall screens --- src/components/icon.tsx | 2 +- src/components/spinner.tsx | 2 +- src/components/stages.tsx | 99 ++++++++++++++++++--------------- test/components/stages.test.tsx | 2 +- 4 files changed, 56 insertions(+), 49 deletions(-) diff --git a/src/components/icon.tsx b/src/components/icon.tsx index d1ffba0..b69ca0b 100644 --- a/src/components/icon.tsx +++ b/src/components/icon.tsx @@ -17,7 +17,7 @@ export function Icon({ }): React.ReactNode { if (!icon) return false return ( - + {icon.color && {icon.figure}} {!icon.color && {icon.figure}} diff --git a/src/components/spinner.tsx b/src/components/spinner.tsx index 48b1ecb..e284e64 100644 --- a/src/components/spinner.tsx +++ b/src/components/spinner.tsx @@ -101,7 +101,7 @@ export function SpinnerOrErrorOrChildren({ }): React.ReactElement { if (children) { return ( - + {props.label && props.labelPosition === 'left' && {props.label} } {children} {props.label && props.labelPosition === 'right' && {props.label}} diff --git a/src/components/stages.tsx b/src/components/stages.tsx index 7eb16c5..7dc0088 100644 --- a/src/components/stages.tsx +++ b/src/components/stages.tsx @@ -129,7 +129,7 @@ function StageInfos({ if (kv.type === 'dynamic-key-value') { return ( - + - {kv.value && ( - - {kv.value} - - )} - + + + {kv.value && ( + + {kv.value} + + )} + + ) } @@ -250,7 +251,7 @@ function Stage({ readonly error?: Error }): React.ReactElement { return ( - + {(status === 'current' || status === 'failed') && ( )} @@ -307,7 +308,7 @@ function StageEntries({ */} {[...stageTracker.entries()].map(([stage, status]) => ( - + {compactionLevel === 0 ? ( ) : ( @@ -390,6 +391,19 @@ export function determineCompactionLevel( rows: number, columns: number, ): {compactionLevel: number; totalHeight: number} { + // We don't have access to the exact stage time, so we're taking a conservative estimate of + // 10 characters + 1 character for the space between the stage and timer, + // examples: 999ms (5), 59.99s (6), 59m 59.99s (10), 23h 59m (7) + const estimatedTimeLength = 11 + + const calculateWrappedHeight = (length: number): number => { + if (length > columns) { + return Math.ceil(length / columns) + } + + return 1 + } + const calculateHeightOfBlock = (block: FormattedKeyValue[] | undefined): number => { if (!block) return 0 return block.reduce((acc, info) => { @@ -398,7 +412,7 @@ export function determineCompactionLevel( if (info.value.length > columns) { // if the message is longer than the terminal width, add the number of lines - return acc + Math.ceil(info.value.length / columns) + return acc + calculateWrappedHeight(info.value.length) } // if the message is multiline, add the number of lines @@ -408,9 +422,10 @@ export function determineCompactionLevel( const {label = '', value} = info // if there's no value we still add 1 for the label if (!value) return acc + 1 - if (label.length + Number(': '.length) + value.length > columns) { + const totalLength = `${label}: ${value}`.length + if (totalLength > columns) { // if the value is longer than the terminal width, add the number of lines - return acc + Math.ceil(value.length / columns) + return acc + calculateWrappedHeight(totalLength) } return acc + value.split('\n').length @@ -420,25 +435,16 @@ export function determineCompactionLevel( const calculateHeightOfStage = (stage: string): number => { const status = stageTracker.get(stage) ?? 'pending' const skipped = status === 'skipped' ? ' - Skipped' : '' - // We don't have access to the exact stage time, so we're taking a conservative estimate of - // 10 characters + 1 character for the space between the stage and timer, - // examples: 999ms (5), 59.99s (6), 59m 59.99s (10), 23h 59m (7) - const stageTimeLength = hasStageTime ? 11 : 0 - if ( - // 1 for the left margin - 1 + - design.icons[status].paddingLeft + - design.icons[status].figure.length + - design.icons[status].paddingRight + - stage.length + - skipped.length + - stageTimeLength > - columns - ) { - return Math.ceil(stage.length / columns) - } + const stageTimeLength = hasStageTime ? estimatedTimeLength : 0 + const totalLength = + design.icons[status].paddingLeft + + design.icons[status].figure.length + + design.icons[status].paddingRight + + stage.length + + skipped.length + + stageTimeLength - return 1 + return calculateWrappedHeight(totalLength) } const calculateWidthOfCompactStage = (stage: string): number => { @@ -473,14 +479,15 @@ export function determineCompactionLevel( const stageSpecificBlockHeight = calculateHeightOfBlock(stageSpecificBlock) // 3 at minimum because: 1 for marginTop on entire component, 1 for marginBottom on entire component, 1 for paddingBottom on StageEntries const paddings = 3 + (preStagesBlock ? 1 : 0) + (postStagesBlock ? 1 : 0) + (title ? 1 : 0) - + const elapsedTimeHeight = hasElapsedTime ? calculateWrappedHeight('Elapsed Time:'.length + estimatedTimeLength) : 0 + const titleHeight = title ? calculateWrappedHeight(title.length) : 0 const totalHeight = stagesHeight + preStagesBlockHeight + postStagesBlockHeight + stageSpecificBlockHeight + - (title ? 1 : 0) + - (hasElapsedTime ? 1 : 0) + + elapsedTimeHeight + + titleHeight + paddings + // add one for good measure - iTerm2 will flicker on every render if the height is exactly the same as the terminal height so it's better to be safe 1 @@ -550,7 +557,7 @@ export function Stages({ title, }, stdout.rows - 1, - stdout.columns, + stdout.columns - 1, ).compactionLevel, ) @@ -567,7 +574,7 @@ export function Stages({ title, }, stdout.rows - 1, - stdout.columns, + stdout.columns - 1, ).compactionLevel, ) }, [ @@ -597,7 +604,7 @@ export function Stages({ title, }, stdout.rows - 1, - stdout.columns, + stdout.columns - 1, ).compactionLevel, ) } @@ -616,7 +623,7 @@ export function Stages({ const postStages = filterInfos(postStagesBlock ?? [], actualLevelOfCompaction, 5) const stageSpecific = filterInfos(stageSpecificBlock ?? [], actualLevelOfCompaction, 7) // Reduce padding if the compaction level is 8 - const padding = actualLevelOfCompaction === 8 ? 0 : 1 + const padding = actualLevelOfCompaction >= 8 ? 0 : 1 return ( @@ -651,7 +658,7 @@ export function Stages({ )} {hasElapsedTime && ( - + Elapsed Time: diff --git a/test/components/stages.test.tsx b/test/components/stages.test.tsx index f3729b2..147fd84 100644 --- a/test/components/stages.test.tsx +++ b/test/components/stages.test.tsx @@ -593,7 +593,7 @@ describe('determineCompactionLevel', () => { it('returns 7 if window is too narrow to show all stage-specific blocks', () => { const {compactionLevel, totalHeight} = determineCompactionLevel(inputs, 6, 10) // height is taller here since lines are being wrapped to accommodate the narrow window - expect(totalHeight).to.equal(34) + expect(totalHeight).to.equal(36) expect(compactionLevel).to.equal(7) })