@@ -15,6 +15,7 @@ class Ora {
15
15
#lineCount = 0 ;
16
16
#frameIndex = - 1 ;
17
17
#lastSpinnerFrameTime = 0 ;
18
+ #lastIndent = 0 ;
18
19
#options;
19
20
#spinner;
20
21
#stream;
@@ -162,44 +163,44 @@ class Ora {
162
163
return this . #id !== undefined ;
163
164
}
164
165
165
- #getFullPrefixText( prefixText = this . #prefixText, postfix = ' ' ) {
166
- if ( typeof prefixText === 'string' && prefixText !== '' ) {
167
- return prefixText + postfix ;
168
- }
169
-
170
- if ( typeof prefixText === 'function' ) {
171
- return prefixText ( ) + postfix ;
166
+ #formatAffix( value , separator , placeBefore = false ) {
167
+ const resolved = typeof value === 'function' ? value ( ) : value ;
168
+ if ( typeof resolved === 'string' && resolved !== '' ) {
169
+ return placeBefore ? ( separator + resolved ) : ( resolved + separator ) ;
172
170
}
173
171
174
172
return '' ;
175
173
}
176
174
175
+ #getFullPrefixText( prefixText = this . #prefixText, postfix = ' ' ) {
176
+ return this . #formatAffix( prefixText , postfix , false ) ;
177
+ }
178
+
177
179
#getFullSuffixText( suffixText = this . #suffixText, prefix = ' ' ) {
178
- if ( typeof suffixText === 'string' && suffixText !== '' ) {
179
- return prefix + suffixText ;
180
- }
180
+ return this . #formatAffix( suffixText , prefix , true ) ;
181
+ }
181
182
182
- if ( typeof suffixText === 'function' ) {
183
- return prefix + suffixText ( ) ;
183
+ #computeLineCountFrom( text , columns ) {
184
+ let count = 0 ;
185
+ for ( const line of stripAnsi ( text ) . split ( '\n' ) ) {
186
+ count += Math . max ( 1 , Math . ceil ( stringWidth ( line ) / columns ) ) ;
184
187
}
185
188
186
- return '' ;
189
+ return count ;
187
190
}
188
191
189
192
#updateLineCount( ) {
190
193
const columns = this . #stream. columns ?? 80 ;
191
194
192
- // Use simple approximations for line calculation to avoid calling functions
195
+ // Simple side-effect free approximation (do not call functions)
193
196
const prefixText = typeof this . #prefixText === 'function' ? '' : this . #prefixText;
194
197
const suffixText = typeof this . #suffixText === 'function' ? '' : this . #suffixText;
195
- const fullPrefixText = ( typeof prefixText === 'string' && prefixText !== '' ) ? prefixText + '-' : '' ;
196
- const fullSuffixText = ( typeof suffixText === 'string' && suffixText !== '' ) ? '-' + suffixText : '' ;
197
- const fullText = ' ' . repeat ( this . #indent) + fullPrefixText + '--' + this . #text + '--' + fullSuffixText ;
198
+ const fullPrefixText = ( typeof prefixText === 'string' && prefixText !== '' ) ? prefixText + ' ' : '' ;
199
+ const fullSuffixText = ( typeof suffixText === 'string' && suffixText !== '' ) ? ' ' + suffixText : '' ;
200
+ const spinnerChar = '-' ;
201
+ const fullText = ' ' . repeat ( this . #indent) + fullPrefixText + spinnerChar + ( typeof this . #text === 'string' ? ' ' + this . #text : '' ) + fullSuffixText ;
198
202
199
- this . #lineCount = 0 ;
200
- for ( const line of stripAnsi ( fullText ) . split ( '\n' ) ) {
201
- this . #lineCount += Math . max ( 1 , Math . ceil ( stringWidth ( line , { countAnsiEscapeCodes : true } ) / columns ) ) ;
202
- }
203
+ this . #lineCount = this . #computeLineCountFrom( fullText , columns ) ;
203
204
}
204
205
205
206
get isEnabled ( ) {
@@ -264,11 +265,11 @@ class Ora {
264
265
this . #stream. clearLine ( 1 ) ;
265
266
}
266
267
267
- if ( this . #indent || this . lastIndent !== this . #indent) {
268
+ if ( this . #indent || this . # lastIndent !== this . #indent) {
268
269
this . #stream. cursorTo ( this . #indent) ;
269
270
}
270
271
271
- this . lastIndent = this . #indent;
272
+ this . # lastIndent = this . #indent;
272
273
this . #linesToClear = 0 ;
273
274
274
275
return this ;
@@ -282,15 +283,16 @@ class Ora {
282
283
this . clear ( ) ;
283
284
284
285
let frameContent = this . frame ( ) ;
285
- let actualLineCount = this . #lineCount;
286
+ const columns = this . #stream. columns ?? 80 ;
287
+ let actualLineCount = this . #computeLineCountFrom( frameContent , columns ) ;
286
288
287
289
// If content would exceed viewport height, truncate it to prevent garbage
288
290
const consoleHeight = this . #stream. rows ;
289
- if ( consoleHeight && consoleHeight > 1 && this . #lineCount > consoleHeight ) {
291
+ if ( consoleHeight && consoleHeight > 1 && actualLineCount > consoleHeight ) {
290
292
const lines = frameContent . split ( '\n' ) ;
291
293
const maxLines = consoleHeight - 1 ; // Reserve one line for truncation message
292
294
frameContent = [ ...lines . slice ( 0 , maxLines ) , '... (content truncated to fit terminal)' ] . join ( '\n' ) ;
293
- actualLineCount = maxLines + 1 ; // Truncated lines + message line
295
+ actualLineCount = this . #computeLineCountFrom ( frameContent , columns ) ;
294
296
}
295
297
296
298
this . #stream. write ( frameContent ) ;
0 commit comments