Files
2026-07-13 13:34:48 +08:00

103 lines
2.1 KiB
TypeScript

import chalk from 'chalk'
// Spinner frames
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
interface LoadingState {
intervalId: ReturnType<typeof setInterval> | null
frameIndex: number
message: string
isActive: boolean
}
const state: LoadingState = {
intervalId: null,
frameIndex: 0,
message: '',
isActive: false,
}
function updateSpinner(): void {
if (!state.isActive) return
// Clear the current line
process.stdout.write('\r')
process.stdout.write(' '.repeat(100)) // Clear with spaces
process.stdout.write('\r')
// Display the spinner with message
const frame = chalk.cyan(spinnerFrames[state.frameIndex])
const message = state.message ? ` ${state.message}` : ''
process.stdout.write(`${frame}${message}`)
// Update frame index
state.frameIndex = (state.frameIndex + 1) % spinnerFrames.length
}
function startSpinner(message: string): void {
// If already running, just update the message
if (state.isActive) {
state.message = message
return
}
state.isActive = true
state.message = message
state.frameIndex = 0
// Hide cursor
process.stdout.write('\x1b[?25l')
// Start the animation
updateSpinner()
state.intervalId = setInterval(updateSpinner, 80)
}
function stopSpinner(): void {
if (!state.isActive) return
state.isActive = false
// Clear the interval
if (state.intervalId) {
clearInterval(state.intervalId)
state.intervalId = null
}
// Clear the line
process.stdout.write('\r')
process.stdout.write(' '.repeat(100))
process.stdout.write('\r')
// Show cursor
process.stdout.write('\x1b[?25h')
}
/**
* Control the loading spinner
* @param show - true to show/update spinner, false to hide it
* @param message - message to display with the spinner (optional)
*/
export function loading(show: boolean, message?: string): void {
if (show) {
startSpinner(message || 'Loading...')
} else {
stopSpinner()
}
}
// Cleanup on process exit
process.on('exit', () => {
stopSpinner()
})
process.on('SIGINT', () => {
stopSpinner()
process.exit(0)
})
process.on('SIGTERM', () => {
stopSpinner()
process.exit(0)
})