forked from Sushants-Git/naash
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspinner.ts
139 lines (111 loc) · 3.73 KB
/
spinner.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import readline from 'readline';
type SpinnerFrame = [string, string]; // [animation, state]
interface SpinnerOptions {
interval?: number;
stream?: NodeJS.WriteStream;
color?: boolean;
}
interface AnsiColors {
cyan: string;
blue: string;
reset: string;
}
class CompactAISpinner {
private readonly frames: SpinnerFrame[];
private readonly interval: number;
private readonly stream: NodeJS.WriteStream;
private readonly color: boolean;
private frameIndex: number;
private isSpinning: boolean;
private spinnerInterval: NodeJS.Timeout | null;
constructor(options: SpinnerOptions = {}) {
this.frames = [
// Brain processing animation
['⟨ ◠◡◠ ⟩', ' think'],
['⟨ ◡◠◡ ⟩', ' learn'],
['⟨ ◠◡◠ ⟩', ' neural'],
['⟨ ◡◠◡ ⟩', ' sync '],
// Circuit flow animation
['[←↔→]', ' parse'],
['[→↔←]', ' map '],
['[←↔→]', ' flow '],
['[→↔←]', ' link '],
// Data pulse animation
['(∙∵∙)', ' data '],
['(∙∴∙)', ' proc '],
['(∙∵∙)', ' calc '],
['(∙∴∙)', ' eval '],
// Matrix scan animation
['⟦░▒▓⟧', ' scan '],
['⟦▒▓░⟧', ' read '],
['⟦▓░▒⟧', ' load '],
['⟦░▒▓⟧', ' feed ']
];
this.interval = options.interval ?? 100;
this.stream = options.stream ?? process.stdout;
this.color = options.color ?? true;
this.frameIndex = 0;
this.isSpinning = false;
this.spinnerInterval = null;
}
private get colors(): AnsiColors {
const noColor = { cyan: '', blue: '', reset: '' };
if (!this.color) return noColor;
return {
cyan: '\x1b[36m',
blue: '\x1b[34m',
reset: '\x1b[0m'
};
}
public start(text = ''): void {
if (this.isSpinning) return;
this.isSpinning = true;
this.frameIndex = 0;
// Hide cursor
this.stream.write('\x1B[?25l');
this.render(text);
this.spinnerInterval = setInterval(() => {
this.render(text);
}, this.interval) as NodeJS.Timeout;
// Handle process exit
this.setupExitHandlers();
}
private setupExitHandlers(): void {
// Ensure cleanup on process exit
process.on('exit', this.stop.bind(this));
process.on('SIGINT', () => {
this.stop();
process.exit(0);
});
process.on('SIGTERM', this.stop.bind(this));
}
private render(text: string): void {
const [animation, state] = this.frames[this.frameIndex];
const { cyan, blue, reset } = this.colors;
const color = this.frameIndex < 8 ? cyan : blue;
readline.clearLine(this.stream, 0);
readline.cursorTo(this.stream, 0);
this.stream.write(
`${color}${animation}${state}${reset} ${text}`
);
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
}
public stop(): void {
if (!this.isSpinning) return;
if (this.spinnerInterval) {
clearInterval(this.spinnerInterval);
this.spinnerInterval = null;
}
this.isSpinning = false;
// Clear line and reset cursor
readline.clearLine(this.stream, 0);
readline.cursorTo(this.stream, 0);
// Show cursor
this.stream.write('\x1B[?25h');
}
// Method to check if spinner is currently active
public isActive(): boolean {
return this.isSpinning;
}
}
export default CompactAISpinner;