import { spinner } from '@clack/prompts'
import pc from 'picocolors'
export { pc }
export function formatBytes(bytes: number): string {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i] ?? 'TB'}`
}
// Adapter to provide ora-like interface using clack spinner
export interface SpinnerLike {
text: string
start(): SpinnerLike
succeed(text?: string): SpinnerLike
fail(text?: string): SpinnerLike
}
export function createSpinner(text: string): SpinnerLike {
// when no tty is attached, or a headless env var is set, return a silent spinner
if (!process.stdout.isTTY || process.env.WISPCTL_NO_PROGRESS === '1' || process.env.CI === 'true') {
let currentText = text
return {
get text() {
return currentText
},
set text(newText: string) {
currentText = newText
},
start() {
return this
},
succeed(message?: string) {
console.log(`✓ ${message ?? currentText}`)
return this
},
fail(message?: string) {
console.error(`✗ ${message ?? currentText}`)
return this
},
}
}
const s = spinner()
let currentText = text
let started = false
return {
get text() {
return currentText
},
set text(newText: string) {
currentText = newText
if (started) {
s.message(newText)
}
},
start() {
started = true
s.start(currentText)
return this
},
succeed(message?: string) {
s.stop(pc.green('✓ ') + (message ?? currentText))
started = false
return this
},
fail(message?: string) {
s.stop(pc.red('✗ ') + (message ?? currentText))
started = false
return this
},
}
}