forked from bombshell-dev/clack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspinner-cancel-advanced.ts
More file actions
150 lines (125 loc) · 4.47 KB
/
spinner-cancel-advanced.ts
File metadata and controls
150 lines (125 loc) · 4.47 KB
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
140
141
142
143
144
145
146
147
148
149
150
import { setTimeout as sleep } from 'node:timers/promises';
import * as p from '@clack/prompts';
async function main() {
p.intro('Advanced Spinner Cancellation Demo');
// First demonstrate a visible spinner with no user input needed
p.note('First, we will show a basic spinner (press CTRL+C to cancel)', 'Demo Part 1');
const demoSpinner = p.spinner({
indicator: 'dots',
onCancel: () => {
p.note('Initial spinner was cancelled with CTRL+C', 'Demo Cancelled');
},
});
demoSpinner.start('Loading demo resources');
// Update spinner message a few times to show activity
for (let i = 0; i < 5; i++) {
if (demoSpinner.isCancelled) break;
await sleep(1000);
demoSpinner.message(`Loading demo resources (${i + 1}/5)`);
}
if (!demoSpinner.isCancelled) {
demoSpinner.stop('Demo resources loaded successfully');
}
// Only continue with the rest of the demo if the initial spinner wasn't cancelled
if (!demoSpinner.isCancelled) {
// Stage 1: Get user input with multiselect
p.note("Now let's select some languages to process", 'Demo Part 2');
const languages = await p.multiselect({
message: 'Select programming languages to process:',
options: [
{ value: 'typescript', label: 'TypeScript' },
{ value: 'javascript', label: 'JavaScript' },
{ value: 'python', label: 'Python' },
{ value: 'rust', label: 'Rust' },
{ value: 'go', label: 'Go' },
],
required: true,
});
// Handle cancellation of the multiselect
if (p.isCancel(languages)) {
p.cancel('Operation cancelled during language selection.');
process.exit(0);
}
// Stage 2: Show a spinner that can be cancelled
const processSpinner = p.spinner({
indicator: 'dots',
onCancel: () => {
p.note(
'You cancelled during processing. Any completed work will be saved.',
'Processing Cancelled'
);
},
});
processSpinner.start('Starting to process selected languages...');
// Process each language with individual progress updates
let completedCount = 0;
const totalLanguages = languages.length;
for (const language of languages) {
// Skip the rest if cancelled
if (processSpinner.isCancelled) break;
// Update spinner message with current language
processSpinner.message(`Processing ${language} (${completedCount + 1}/${totalLanguages})`);
try {
// Simulate work - longer pause to give time to test CTRL+C
await sleep(2000);
completedCount++;
} catch (error) {
// Handle errors but continue if not cancelled
if (!processSpinner.isCancelled) {
p.note(`Error processing ${language}: ${error.message}`, 'Error');
}
}
}
// Stage 3: Handle completion based on cancellation status
if (!processSpinner.isCancelled) {
processSpinner.stop(`Processed ${completedCount}/${totalLanguages} languages successfully`);
// Stage 4: Additional user input based on processing results
if (completedCount > 0) {
const action = await p.select({
message: 'What would you like to do with the processed data?',
options: [
{ value: 'save', label: 'Save results', hint: 'Write to disk' },
{ value: 'share', label: 'Share results', hint: 'Upload to server' },
{ value: 'analyze', label: 'Further analysis', hint: 'Generate reports' },
],
});
if (p.isCancel(action)) {
p.cancel('Operation cancelled at final stage.');
process.exit(0);
}
// Stage 5: Final action with a timer spinner
p.note('Now demonstrating a timer-style spinner', 'Final Stage');
const finalSpinner = p.spinner({
indicator: 'timer', // Use timer indicator for variety
onCancel: () => {
p.note(
'Final operation was cancelled, but processing results are still valid.',
'Final Stage Cancelled'
);
},
});
finalSpinner.start(`Performing ${action} operation...`);
try {
// Simulate final action with incremental updates
for (let i = 0; i < 3; i++) {
if (finalSpinner.isCancelled) break;
await sleep(1500);
finalSpinner.message(`Performing ${action} operation... Step ${i + 1}/3`);
}
if (!finalSpinner.isCancelled) {
finalSpinner.stop(`${action} operation completed successfully`);
}
} catch (error) {
if (!finalSpinner.isCancelled) {
finalSpinner.stop(`Error during ${action}: ${error.message}`);
}
}
}
}
}
p.outro('Advanced demo completed. Thanks for trying out the spinner cancellation features!');
}
main().catch((error) => {
console.error('Unexpected error:', error);
process.exit(1);
});