pcu 0.5.6 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/bin/pcu.js +1 -1
  2. package/dist/index.js +9559 -32
  3. package/dist/index.js.map +1 -1
  4. package/package.json +36 -84
  5. package/src/cli/commands/checkCommand.ts +227 -0
  6. package/src/cli/commands/initCommand.ts +394 -0
  7. package/src/cli/commands/securityCommand.ts +569 -0
  8. package/src/cli/commands/updateCommand.ts +245 -0
  9. package/src/cli/formatters/outputFormatter.ts +830 -0
  10. package/src/cli/formatters/progressBar.ts +700 -0
  11. package/src/cli/index.ts +565 -0
  12. package/src/cli/interactive/interactivePrompts.ts +517 -0
  13. package/src/cli/options/globalOptions.ts +380 -0
  14. package/src/cli/options/index.ts +5 -0
  15. package/src/cli/themes/colorTheme.ts +379 -0
  16. package/src/cli/validators/commandValidator.ts +395 -0
  17. package/src/cli/validators/index.ts +5 -0
  18. package/src/index.ts +4 -0
  19. package/README.ja.md +0 -582
  20. package/README.md +0 -690
  21. package/README.zh-CN.md +0 -630
  22. package/dist/application/services/CatalogUpdateService.d.ts +0 -209
  23. package/dist/application/services/CatalogUpdateService.d.ts.map +0 -1
  24. package/dist/application/services/CatalogUpdateService.js +0 -836
  25. package/dist/application/services/CatalogUpdateService.js.map +0 -1
  26. package/dist/application/services/WorkspaceService.d.ts +0 -139
  27. package/dist/application/services/WorkspaceService.d.ts.map +0 -1
  28. package/dist/application/services/WorkspaceService.js +0 -340
  29. package/dist/application/services/WorkspaceService.js.map +0 -1
  30. package/dist/cli/commands/CheckCommand.d.ts +0 -40
  31. package/dist/cli/commands/CheckCommand.d.ts.map +0 -1
  32. package/dist/cli/commands/CheckCommand.js +0 -177
  33. package/dist/cli/commands/CheckCommand.js.map +0 -1
  34. package/dist/cli/commands/InitCommand.d.ts +0 -53
  35. package/dist/cli/commands/InitCommand.d.ts.map +0 -1
  36. package/dist/cli/commands/InitCommand.js +0 -338
  37. package/dist/cli/commands/InitCommand.js.map +0 -1
  38. package/dist/cli/commands/SecurityCommand.d.ts +0 -113
  39. package/dist/cli/commands/SecurityCommand.d.ts.map +0 -1
  40. package/dist/cli/commands/SecurityCommand.js +0 -410
  41. package/dist/cli/commands/SecurityCommand.js.map +0 -1
  42. package/dist/cli/commands/UpdateCommand.d.ts +0 -44
  43. package/dist/cli/commands/UpdateCommand.d.ts.map +0 -1
  44. package/dist/cli/commands/UpdateCommand.js +0 -189
  45. package/dist/cli/commands/UpdateCommand.js.map +0 -1
  46. package/dist/cli/formatters/OutputFormatter.d.ts +0 -116
  47. package/dist/cli/formatters/OutputFormatter.d.ts.map +0 -1
  48. package/dist/cli/formatters/OutputFormatter.js +0 -664
  49. package/dist/cli/formatters/OutputFormatter.js.map +0 -1
  50. package/dist/cli/formatters/ProgressBar.d.ts +0 -195
  51. package/dist/cli/formatters/ProgressBar.d.ts.map +0 -1
  52. package/dist/cli/formatters/ProgressBar.js +0 -622
  53. package/dist/cli/formatters/ProgressBar.js.map +0 -1
  54. package/dist/cli/index.d.ts +0 -12
  55. package/dist/cli/index.d.ts.map +0 -1
  56. package/dist/cli/index.js +0 -492
  57. package/dist/cli/index.js.map +0 -1
  58. package/dist/cli/interactive/InteractivePrompts.d.ts +0 -85
  59. package/dist/cli/interactive/InteractivePrompts.d.ts.map +0 -1
  60. package/dist/cli/interactive/InteractivePrompts.js +0 -434
  61. package/dist/cli/interactive/InteractivePrompts.js.map +0 -1
  62. package/dist/cli/options/GlobalOptions.d.ts +0 -117
  63. package/dist/cli/options/GlobalOptions.d.ts.map +0 -1
  64. package/dist/cli/options/GlobalOptions.js +0 -278
  65. package/dist/cli/options/GlobalOptions.js.map +0 -1
  66. package/dist/cli/options/index.d.ts +0 -5
  67. package/dist/cli/options/index.d.ts.map +0 -1
  68. package/dist/cli/options/index.js +0 -5
  69. package/dist/cli/options/index.js.map +0 -1
  70. package/dist/cli/themes/ColorTheme.d.ts +0 -211
  71. package/dist/cli/themes/ColorTheme.d.ts.map +0 -1
  72. package/dist/cli/themes/ColorTheme.js +0 -267
  73. package/dist/cli/themes/ColorTheme.js.map +0 -1
  74. package/dist/cli/validators/CommandValidator.d.ts +0 -60
  75. package/dist/cli/validators/CommandValidator.d.ts.map +0 -1
  76. package/dist/cli/validators/CommandValidator.js +0 -319
  77. package/dist/cli/validators/CommandValidator.js.map +0 -1
  78. package/dist/cli/validators/index.d.ts +0 -5
  79. package/dist/cli/validators/index.d.ts.map +0 -1
  80. package/dist/cli/validators/index.js +0 -5
  81. package/dist/cli/validators/index.js.map +0 -1
  82. package/dist/common/config/Config.d.ts +0 -142
  83. package/dist/common/config/Config.d.ts.map +0 -1
  84. package/dist/common/config/Config.js +0 -382
  85. package/dist/common/config/Config.js.map +0 -1
  86. package/dist/common/config/ConfigLoader.d.ts +0 -49
  87. package/dist/common/config/ConfigLoader.d.ts.map +0 -1
  88. package/dist/common/config/ConfigLoader.js +0 -180
  89. package/dist/common/config/ConfigLoader.js.map +0 -1
  90. package/dist/common/config/PackageFilterConfig.d.ts +0 -56
  91. package/dist/common/config/PackageFilterConfig.d.ts.map +0 -1
  92. package/dist/common/config/PackageFilterConfig.js +0 -94
  93. package/dist/common/config/PackageFilterConfig.js.map +0 -1
  94. package/dist/common/config/index.d.ts +0 -8
  95. package/dist/common/config/index.d.ts.map +0 -1
  96. package/dist/common/config/index.js +0 -8
  97. package/dist/common/config/index.js.map +0 -1
  98. package/dist/common/error-handling/ErrorTracker.d.ts +0 -48
  99. package/dist/common/error-handling/ErrorTracker.d.ts.map +0 -1
  100. package/dist/common/error-handling/ErrorTracker.js +0 -93
  101. package/dist/common/error-handling/ErrorTracker.js.map +0 -1
  102. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts +0 -74
  103. package/dist/common/error-handling/UserFriendlyErrorHandler.d.ts.map +0 -1
  104. package/dist/common/error-handling/UserFriendlyErrorHandler.js +0 -703
  105. package/dist/common/error-handling/UserFriendlyErrorHandler.js.map +0 -1
  106. package/dist/common/error-handling/index.d.ts +0 -11
  107. package/dist/common/error-handling/index.d.ts.map +0 -1
  108. package/dist/common/error-handling/index.js +0 -9
  109. package/dist/common/error-handling/index.js.map +0 -1
  110. package/dist/common/logger/Logger.d.ts +0 -110
  111. package/dist/common/logger/Logger.d.ts.map +0 -1
  112. package/dist/common/logger/Logger.js +0 -289
  113. package/dist/common/logger/Logger.js.map +0 -1
  114. package/dist/common/logger/index.d.ts +0 -6
  115. package/dist/common/logger/index.d.ts.map +0 -1
  116. package/dist/common/logger/index.js +0 -6
  117. package/dist/common/logger/index.js.map +0 -1
  118. package/dist/common/types/cli.d.ts +0 -265
  119. package/dist/common/types/cli.d.ts.map +0 -1
  120. package/dist/common/types/cli.js +0 -5
  121. package/dist/common/types/cli.js.map +0 -1
  122. package/dist/common/types/core.d.ts +0 -270
  123. package/dist/common/types/core.d.ts.map +0 -1
  124. package/dist/common/types/core.js +0 -32
  125. package/dist/common/types/core.js.map +0 -1
  126. package/dist/common/types/index.d.ts +0 -8
  127. package/dist/common/types/index.d.ts.map +0 -1
  128. package/dist/common/types/index.js +0 -8
  129. package/dist/common/types/index.js.map +0 -1
  130. package/dist/common/utils/VersionChecker.d.ts +0 -54
  131. package/dist/common/utils/VersionChecker.d.ts.map +0 -1
  132. package/dist/common/utils/VersionChecker.js +0 -180
  133. package/dist/common/utils/VersionChecker.js.map +0 -1
  134. package/dist/common/utils/async.d.ts +0 -74
  135. package/dist/common/utils/async.d.ts.map +0 -1
  136. package/dist/common/utils/async.js +0 -228
  137. package/dist/common/utils/async.js.map +0 -1
  138. package/dist/common/utils/format.d.ts +0 -32
  139. package/dist/common/utils/format.d.ts.map +0 -1
  140. package/dist/common/utils/format.js +0 -121
  141. package/dist/common/utils/format.js.map +0 -1
  142. package/dist/common/utils/git.d.ts +0 -44
  143. package/dist/common/utils/git.d.ts.map +0 -1
  144. package/dist/common/utils/git.js +0 -147
  145. package/dist/common/utils/git.js.map +0 -1
  146. package/dist/common/utils/index.d.ts +0 -12
  147. package/dist/common/utils/index.d.ts.map +0 -1
  148. package/dist/common/utils/index.js +0 -12
  149. package/dist/common/utils/index.js.map +0 -1
  150. package/dist/common/utils/string.d.ts +0 -56
  151. package/dist/common/utils/string.d.ts.map +0 -1
  152. package/dist/common/utils/string.js +0 -134
  153. package/dist/common/utils/string.js.map +0 -1
  154. package/dist/common/utils/validation.d.ts +0 -88
  155. package/dist/common/utils/validation.d.ts.map +0 -1
  156. package/dist/common/utils/validation.js +0 -308
  157. package/dist/common/utils/validation.js.map +0 -1
  158. package/dist/domain/entities/Catalog.d.ts +0 -117
  159. package/dist/domain/entities/Catalog.d.ts.map +0 -1
  160. package/dist/domain/entities/Catalog.js +0 -240
  161. package/dist/domain/entities/Catalog.js.map +0 -1
  162. package/dist/domain/entities/Package.d.ts +0 -143
  163. package/dist/domain/entities/Package.d.ts.map +0 -1
  164. package/dist/domain/entities/Package.js +0 -272
  165. package/dist/domain/entities/Package.js.map +0 -1
  166. package/dist/domain/entities/Workspace.d.ts +0 -95
  167. package/dist/domain/entities/Workspace.d.ts.map +0 -1
  168. package/dist/domain/entities/Workspace.js +0 -173
  169. package/dist/domain/entities/Workspace.js.map +0 -1
  170. package/dist/domain/repositories/WorkspaceRepository.d.ts +0 -41
  171. package/dist/domain/repositories/WorkspaceRepository.d.ts.map +0 -1
  172. package/dist/domain/repositories/WorkspaceRepository.js +0 -8
  173. package/dist/domain/repositories/WorkspaceRepository.js.map +0 -1
  174. package/dist/domain/value-objects/CatalogCollection.d.ts +0 -106
  175. package/dist/domain/value-objects/CatalogCollection.d.ts.map +0 -1
  176. package/dist/domain/value-objects/CatalogCollection.js +0 -230
  177. package/dist/domain/value-objects/CatalogCollection.js.map +0 -1
  178. package/dist/domain/value-objects/PackageCollection.d.ts +0 -122
  179. package/dist/domain/value-objects/PackageCollection.d.ts.map +0 -1
  180. package/dist/domain/value-objects/PackageCollection.js +0 -263
  181. package/dist/domain/value-objects/PackageCollection.js.map +0 -1
  182. package/dist/domain/value-objects/Version.d.ts +0 -141
  183. package/dist/domain/value-objects/Version.d.ts.map +0 -1
  184. package/dist/domain/value-objects/Version.js +0 -268
  185. package/dist/domain/value-objects/Version.js.map +0 -1
  186. package/dist/domain/value-objects/WorkspaceConfig.d.ts +0 -144
  187. package/dist/domain/value-objects/WorkspaceConfig.d.ts.map +0 -1
  188. package/dist/domain/value-objects/WorkspaceConfig.js +0 -357
  189. package/dist/domain/value-objects/WorkspaceConfig.js.map +0 -1
  190. package/dist/domain/value-objects/WorkspaceId.d.ts +0 -51
  191. package/dist/domain/value-objects/WorkspaceId.d.ts.map +0 -1
  192. package/dist/domain/value-objects/WorkspaceId.js +0 -104
  193. package/dist/domain/value-objects/WorkspaceId.js.map +0 -1
  194. package/dist/domain/value-objects/WorkspacePath.d.ts +0 -75
  195. package/dist/domain/value-objects/WorkspacePath.d.ts.map +0 -1
  196. package/dist/domain/value-objects/WorkspacePath.js +0 -128
  197. package/dist/domain/value-objects/WorkspacePath.js.map +0 -1
  198. package/dist/index.d.ts +0 -25
  199. package/dist/index.d.ts.map +0 -1
  200. package/dist/infrastructure/cache/Cache.d.ts +0 -161
  201. package/dist/infrastructure/cache/Cache.d.ts.map +0 -1
  202. package/dist/infrastructure/cache/Cache.js +0 -398
  203. package/dist/infrastructure/cache/Cache.js.map +0 -1
  204. package/dist/infrastructure/cache/index.d.ts +0 -6
  205. package/dist/infrastructure/cache/index.d.ts.map +0 -1
  206. package/dist/infrastructure/cache/index.js +0 -6
  207. package/dist/infrastructure/cache/index.js.map +0 -1
  208. package/dist/infrastructure/external-services/NpmRegistryService.d.ts +0 -145
  209. package/dist/infrastructure/external-services/NpmRegistryService.d.ts.map +0 -1
  210. package/dist/infrastructure/external-services/NpmRegistryService.js +0 -466
  211. package/dist/infrastructure/external-services/NpmRegistryService.js.map +0 -1
  212. package/dist/infrastructure/file-system/FileSystemService.d.ts +0 -120
  213. package/dist/infrastructure/file-system/FileSystemService.d.ts.map +0 -1
  214. package/dist/infrastructure/file-system/FileSystemService.js +0 -663
  215. package/dist/infrastructure/file-system/FileSystemService.js.map +0 -1
  216. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts +0 -57
  217. package/dist/infrastructure/repositories/FileWorkspaceRepository.d.ts.map +0 -1
  218. package/dist/infrastructure/repositories/FileWorkspaceRepository.js +0 -179
  219. package/dist/infrastructure/repositories/FileWorkspaceRepository.js.map +0 -1
@@ -0,0 +1,700 @@
1
+ /**
2
+ * Enhanced Progress Bar
3
+ *
4
+ * Provides beautiful progress indicators for CLI operations
5
+ * with multiple styles and themes.
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+
10
+ export interface ProgressBarOptions {
11
+ text?: string;
12
+ total?: number;
13
+ style?: 'default' | 'gradient' | 'fancy' | 'minimal' | 'rainbow' | 'neon';
14
+ showSpeed?: boolean;
15
+ }
16
+
17
+ export class ProgressBar {
18
+ private percentageBar: PercentageProgressBar | null = null;
19
+ private current = 0;
20
+ private total = 0;
21
+ private text = '';
22
+ private startTime: number = 0;
23
+ private style: string;
24
+ private showSpeed: boolean;
25
+
26
+ constructor(options: ProgressBarOptions = {}) {
27
+ this.text = options.text || 'Processing...';
28
+ this.total = options.total || 0;
29
+ this.style = options.style || 'default';
30
+ this.showSpeed = options.showSpeed ?? true;
31
+ }
32
+
33
+ /**
34
+ * Start the progress bar
35
+ */
36
+ start(text?: string): void {
37
+ this.text = text || this.text;
38
+ this.startTime = Date.now();
39
+
40
+ // 在开始新进度条前,彻底清理可能的残留内容
41
+ this.clearPreviousOutput();
42
+
43
+ // 强制使用percentageBar,即使没有total也要创建
44
+ // 这样可以避免spinner导致的冲突问题
45
+ const effectiveTotal = this.total > 0 ? this.total : 1; // 如果没有total,设为1避免除零错误
46
+
47
+ this.percentageBar = new PercentageProgressBar(40, {
48
+ style: this.style,
49
+ showStats: this.showSpeed,
50
+ multiLine: true, // 使用多行模式减少闪烁
51
+ });
52
+ this.percentageBar.start(effectiveTotal, this.text);
53
+ }
54
+
55
+ /**
56
+ * Update progress with text
57
+ */
58
+ update(text: string, current?: number, total?: number): void {
59
+ this.text = text;
60
+ if (current !== undefined) this.current = current;
61
+ if (total !== undefined) this.total = total;
62
+
63
+ // 只使用percentageBar,不使用spinner
64
+ if (this.percentageBar) {
65
+ this.percentageBar.update(this.current, text);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Increment progress
71
+ */
72
+ increment(amount = 1, text?: string): void {
73
+ this.current += amount;
74
+ if (text) this.text = text;
75
+
76
+ // 只使用percentageBar,不使用spinner
77
+ if (this.percentageBar) {
78
+ this.percentageBar.update(this.current, text);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Mark as succeeded
84
+ */
85
+ succeed(text?: string): void {
86
+ // 只使用percentageBar,不使用spinner
87
+ if (this.percentageBar) {
88
+ const successText = text || this.getCompletionText();
89
+ this.percentageBar.complete(successText);
90
+ console.log(this.getSuccessMessage(successText));
91
+ this.percentageBar = null;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Mark as failed
97
+ */
98
+ fail(text?: string): void {
99
+ // 只使用percentageBar,不使用spinner
100
+ if (this.percentageBar) {
101
+ const failText = text || this.getFailureText();
102
+ console.log(this.getFailureMessage(failText));
103
+ this.percentageBar = null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Get styled success message
109
+ */
110
+ private getSuccessMessage(text: string): string {
111
+ const elapsed = this.getElapsedTime();
112
+ switch (this.style) {
113
+ case 'gradient':
114
+ return `${chalk.magenta.bold('✨')} ${chalk.green(text)} ${chalk.gray(elapsed)}`;
115
+ case 'fancy':
116
+ return `${chalk.cyan('🎉')} ${chalk.green.bold(text)} ${chalk.cyan('🎉')} ${chalk.gray(elapsed)}`;
117
+ case 'minimal':
118
+ return chalk.green(text);
119
+ case 'rainbow':
120
+ return `${chalk.magenta('🌈')} ${chalk.green(text)} ${chalk.gray(elapsed)}`;
121
+ case 'neon':
122
+ return `${chalk.green.bold('⚡ SUCCESS')} ${chalk.green(text)} ${chalk.gray(elapsed)}`;
123
+ default:
124
+ return `${chalk.green('✅')} ${chalk.green(text)} ${chalk.gray(elapsed)}`;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Get styled failure message
130
+ */
131
+ private getFailureMessage(text: string): string {
132
+ const elapsed = this.getElapsedTime();
133
+ switch (this.style) {
134
+ case 'gradient':
135
+ return `${chalk.red.bold('💥')} ${chalk.red(text)} ${chalk.gray(elapsed)}`;
136
+ case 'fancy':
137
+ return `${chalk.red('💔')} ${chalk.red.bold(text)} ${chalk.red('💔')} ${chalk.gray(elapsed)}`;
138
+ case 'minimal':
139
+ return chalk.red(text);
140
+ case 'rainbow':
141
+ return `${chalk.red('⚠️')} ${chalk.red(text)} ${chalk.gray(elapsed)}`;
142
+ case 'neon':
143
+ return `${chalk.red.bold('⚡ ERROR')} ${chalk.red(text)} ${chalk.gray(elapsed)}`;
144
+ default:
145
+ return `${chalk.red('❌')} ${chalk.red(text)} ${chalk.gray(elapsed)}`;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Get completion text with stats
151
+ */
152
+ private getCompletionText(): string {
153
+ const elapsed = this.getElapsedTime();
154
+ const speed = this.getAverageSpeed();
155
+ return `${this.text} completed ${speed} ${elapsed}`;
156
+ }
157
+
158
+ /**
159
+ * Get failure text
160
+ */
161
+ private getFailureText(): string {
162
+ return `${this.text} failed`;
163
+ }
164
+
165
+ /**
166
+ * Get elapsed time formatted
167
+ */
168
+ private getElapsedTime(): string {
169
+ const elapsed = Date.now() - this.startTime;
170
+ if (elapsed < 1000) return `(${elapsed}ms)`;
171
+ if (elapsed < 60000) return `(${(elapsed / 1000).toFixed(1)}s)`;
172
+ return `(${Math.floor(elapsed / 60000)}m ${Math.floor((elapsed % 60000) / 1000)}s)`;
173
+ }
174
+
175
+ /**
176
+ * Get average processing speed
177
+ */
178
+ private getAverageSpeed(): string {
179
+ const elapsed = Date.now() - this.startTime;
180
+ if (elapsed === 0 || this.current === 0) return '';
181
+ const speed = Math.round((this.current / elapsed) * 1000);
182
+ return speed > 0 ? `(${speed}/s)` : '';
183
+ }
184
+
185
+ /**
186
+ * Mark as warning
187
+ */
188
+ warn(text?: string): void {
189
+ // 只使用percentageBar,不使用spinner
190
+ if (this.percentageBar) {
191
+ const warnText = text || this.text;
192
+ console.log(this.getWarningMessage(warnText));
193
+ this.percentageBar = null;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Mark as info
199
+ */
200
+ info(text?: string): void {
201
+ // 只使用percentageBar,不使用spinner
202
+ if (this.percentageBar) {
203
+ const infoText = text || this.text;
204
+ console.log(this.getInfoMessage(infoText));
205
+ this.percentageBar = null;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Get styled warning message
211
+ */
212
+ private getWarningMessage(text: string): string {
213
+ const elapsed = this.getElapsedTime();
214
+ switch (this.style) {
215
+ case 'gradient':
216
+ return `${chalk.yellow.bold('⚡')} ${chalk.yellow(text)} ${chalk.gray(elapsed)}`;
217
+ case 'fancy':
218
+ return `${chalk.yellow('⚠️')} ${chalk.yellow.bold(text)} ${chalk.yellow('⚠️')} ${chalk.gray(elapsed)}`;
219
+ case 'minimal':
220
+ return chalk.yellow(text);
221
+ case 'rainbow':
222
+ return `${chalk.yellow('⚠️')} ${chalk.yellow(text)} ${chalk.gray(elapsed)}`;
223
+ case 'neon':
224
+ return `${chalk.yellow.bold('⚡ WARNING')} ${chalk.yellow(text)} ${chalk.gray(elapsed)}`;
225
+ default:
226
+ return `${chalk.yellow('⚠️')} ${chalk.yellow(text)} ${chalk.gray(elapsed)}`;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Get styled info message
232
+ */
233
+ private getInfoMessage(text: string): string {
234
+ const elapsed = this.getElapsedTime();
235
+ switch (this.style) {
236
+ case 'gradient':
237
+ return `${chalk.blue.bold('ℹ️')} ${chalk.blue(text)} ${chalk.gray(elapsed)}`;
238
+ case 'fancy':
239
+ return `${chalk.blue('💡')} ${chalk.blue.bold(text)} ${chalk.blue('💡')} ${chalk.gray(elapsed)}`;
240
+ case 'minimal':
241
+ return chalk.blue(text);
242
+ case 'rainbow':
243
+ return `${chalk.blue('ℹ️')} ${chalk.blue(text)} ${chalk.gray(elapsed)}`;
244
+ case 'neon':
245
+ return `${chalk.blue.bold('⚡ INFO')} ${chalk.blue(text)} ${chalk.gray(elapsed)}`;
246
+ default:
247
+ return `${chalk.blue('ℹ️')} ${chalk.blue(text)} ${chalk.gray(elapsed)}`;
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Stop the progress bar
253
+ */
254
+ stop(): void {
255
+ // 只使用percentageBar,不使用spinner
256
+ if (this.percentageBar) {
257
+ this.percentageBar = null;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Clear previous output to prevent residual progress bars
263
+ */
264
+ private clearPreviousOutput(): void {
265
+ // 清理可能的残留进度条显示(最多清理5行,应该足够了)
266
+ for (let i = 0; i < 5; i++) {
267
+ process.stdout.write('\x1b[1A\r\x1b[K'); // 上移一行并清除
268
+ }
269
+ // 确保光标在正确位置
270
+ process.stdout.write('\r');
271
+ }
272
+
273
+ /**
274
+ * Create a multi-step progress indicator
275
+ */
276
+ static createMultiStep(steps: string[]): MultiStepProgress {
277
+ return new MultiStepProgress(steps);
278
+ }
279
+
280
+ /**
281
+ * Create a beautiful gradient progress bar
282
+ */
283
+ static createGradient(options?: Partial<ProgressBarOptions>): ProgressBar {
284
+ return new ProgressBar({
285
+ style: 'gradient',
286
+ showSpeed: true,
287
+ ...options,
288
+ });
289
+ }
290
+
291
+ /**
292
+ * Create a fancy progress bar with decorations
293
+ */
294
+ static createFancy(options?: Partial<ProgressBarOptions>): ProgressBar {
295
+ return new ProgressBar({
296
+ style: 'fancy',
297
+ showSpeed: true,
298
+ ...options,
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Create a minimal clean progress bar
304
+ */
305
+ static createMinimal(options?: Partial<ProgressBarOptions>): ProgressBar {
306
+ return new ProgressBar({
307
+ style: 'minimal',
308
+ showSpeed: false,
309
+ ...options,
310
+ });
311
+ }
312
+
313
+ /**
314
+ * Create a rainbow themed progress bar
315
+ */
316
+ static createRainbow(options?: Partial<ProgressBarOptions>): ProgressBar {
317
+ return new ProgressBar({
318
+ style: 'rainbow',
319
+ showSpeed: true,
320
+ ...options,
321
+ });
322
+ }
323
+
324
+ /**
325
+ * Create a neon style progress bar
326
+ */
327
+ static createNeon(options?: Partial<ProgressBarOptions>): ProgressBar {
328
+ return new ProgressBar({
329
+ style: 'neon',
330
+ showSpeed: true,
331
+ ...options,
332
+ });
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Multi-step progress indicator
338
+ */
339
+ export class MultiStepProgress {
340
+ private currentStep = 0;
341
+ private steps: string[] = [];
342
+
343
+ constructor(steps: string[]) {
344
+ this.steps = steps;
345
+ }
346
+
347
+ start(): void {
348
+ console.log(chalk.bold('\n📋 Progress Steps:\n'));
349
+ this.renderSteps();
350
+ }
351
+
352
+ next(text?: string): void {
353
+ if (this.currentStep < this.steps.length) {
354
+ const stepText = text || this.steps[this.currentStep];
355
+ console.log(` ${chalk.green('✓')} ${stepText}`);
356
+ this.currentStep++;
357
+
358
+ if (this.currentStep < this.steps.length) {
359
+ console.log(` ${chalk.cyan('→')} ${this.steps[this.currentStep]}`);
360
+ }
361
+ }
362
+ }
363
+
364
+ complete(): void {
365
+ console.log(chalk.green('\n🎉 All steps completed!\n'));
366
+ }
367
+
368
+ private renderSteps(): void {
369
+ this.steps.forEach((step, index) => {
370
+ const prefix = index === 0 ? chalk.cyan('→') : ' ';
371
+ const style = index < this.currentStep ? chalk.green : chalk.gray;
372
+ console.log(` ${prefix} ${style(step)}`);
373
+ });
374
+ console.log('');
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Progress bar with percentage and beautiful visual effects
380
+ */
381
+ export class PercentageProgressBar {
382
+ private current = 0;
383
+ private total = 0;
384
+ private text = '';
385
+ private lastRender = '';
386
+ private startTime = 0;
387
+ private style: string;
388
+ private useMultiLine = true;
389
+ private isFirstRender = true;
390
+
391
+ constructor(
392
+ private readonly width = 40,
393
+ private readonly options: { style?: string; showStats?: boolean; multiLine?: boolean } = {}
394
+ ) {
395
+ this.style = options.style || 'gradient';
396
+ this.useMultiLine = options.multiLine ?? true; // 默认使用多行模式来减少闪烁
397
+ }
398
+
399
+ start(total: number, text: string): void {
400
+ this.total = total;
401
+ this.current = 0;
402
+ this.text = text;
403
+ this.startTime = Date.now();
404
+ this.isFirstRender = true; // 重置首次渲染标记
405
+
406
+ // 清理可能的残留输出
407
+ this.clearPreviousLines();
408
+
409
+ this.render();
410
+ }
411
+
412
+ /**
413
+ * Clear any previous output lines to prevent conflicts
414
+ */
415
+ private clearPreviousLines(): void {
416
+ // 更强力的清理:清理多行可能的残留内容
417
+ for (let i = 0; i < 6; i++) {
418
+ process.stdout.write('\x1b[1A\r\x1b[2K'); // 上移一行并完全清除该行
419
+ }
420
+ // 回到起始位置
421
+ process.stdout.write('\r');
422
+ }
423
+
424
+ update(current: number, text?: string): void {
425
+ this.current = current;
426
+ if (text) this.text = text;
427
+ this.render();
428
+ }
429
+
430
+ increment(amount = 1, text?: string): void {
431
+ this.current += amount;
432
+ if (text) this.text = text;
433
+ this.render();
434
+ }
435
+
436
+ complete(text?: string): void {
437
+ this.current = this.total;
438
+ if (text) this.text = text;
439
+ this.render();
440
+ console.log(''); // New line after completion
441
+ }
442
+
443
+ private render(): void {
444
+ const percentage = Math.round((this.current / this.total) * 100);
445
+ const filledWidth = Math.round((this.current / this.total) * this.width);
446
+ const emptyWidth = this.width - filledWidth;
447
+
448
+ if (this.useMultiLine) {
449
+ this.renderMultiLine(percentage, filledWidth, emptyWidth);
450
+ } else {
451
+ this.renderSingleLine(percentage, filledWidth, emptyWidth);
452
+ }
453
+ }
454
+
455
+ private renderMultiLine(percentage: number, filledWidth: number, emptyWidth: number): void {
456
+ const currentText = `${this.getStyledPrefix()} ${this.text}`;
457
+
458
+ // 构建进度条
459
+ let bar: string;
460
+ switch (this.style) {
461
+ case 'gradient':
462
+ bar = this.renderGradientBar(filledWidth, emptyWidth);
463
+ break;
464
+ case 'fancy':
465
+ bar = this.renderFancyBar(filledWidth, emptyWidth);
466
+ break;
467
+ case 'minimal':
468
+ bar = this.renderMinimalBar(filledWidth, emptyWidth);
469
+ break;
470
+ case 'blocks':
471
+ bar = this.renderBlockBar(filledWidth, emptyWidth);
472
+ break;
473
+ default:
474
+ bar = this.renderDefaultBar(filledWidth, emptyWidth);
475
+ }
476
+
477
+ let progressLine = `(${this.current}/${this.total}) [${bar}] ${this.getStyledPercentage(percentage)}`;
478
+
479
+ // Add stats if enabled
480
+ if (this.options.showStats && this.startTime > 0) {
481
+ const elapsed = Date.now() - this.startTime;
482
+ const speed = elapsed > 0 ? Math.round((this.current / elapsed) * 1000) : 0;
483
+ if (speed > 0) {
484
+ progressLine += ` ${chalk.gray(`${speed}/s`)}`;
485
+ }
486
+ }
487
+
488
+ if (this.isFirstRender) {
489
+ // 第一次渲染:输出两行
490
+ console.log(currentText);
491
+ console.log(progressLine);
492
+ this.isFirstRender = false;
493
+ } else {
494
+ // 后续更新:回到两行前的位置,分别更新这两行
495
+ // 光标上移两行,清除文字行,输出新文字行
496
+ process.stdout.write('\x1b[2A\r\x1b[2K' + currentText + '\n');
497
+ // 清除进度条行,输出新进度条行
498
+ process.stdout.write('\r\x1b[2K' + progressLine);
499
+ }
500
+
501
+ this.lastRender = progressLine;
502
+
503
+ if (this.current >= this.total) {
504
+ process.stdout.write('\n');
505
+ }
506
+ }
507
+
508
+ private renderSingleLine(percentage: number, filledWidth: number, emptyWidth: number): void {
509
+ let bar: string;
510
+ switch (this.style) {
511
+ case 'gradient':
512
+ bar = this.renderGradientBar(filledWidth, emptyWidth);
513
+ break;
514
+ case 'fancy':
515
+ bar = this.renderFancyBar(filledWidth, emptyWidth);
516
+ break;
517
+ case 'minimal':
518
+ bar = this.renderMinimalBar(filledWidth, emptyWidth);
519
+ break;
520
+ case 'blocks':
521
+ bar = this.renderBlockBar(filledWidth, emptyWidth);
522
+ break;
523
+ default:
524
+ bar = this.renderDefaultBar(filledWidth, emptyWidth);
525
+ }
526
+
527
+ let progressText = `${this.getStyledPrefix()} ${this.text} (${this.current}/${this.total}) [${bar}] ${this.getStyledPercentage(percentage)}`;
528
+
529
+ // Add stats if enabled
530
+ if (this.options.showStats && this.startTime > 0) {
531
+ const elapsed = Date.now() - this.startTime;
532
+ const speed = elapsed > 0 ? Math.round((this.current / elapsed) * 1000) : 0;
533
+ if (speed > 0) {
534
+ progressText += ` ${chalk.gray(`${speed}/s`)}`;
535
+ }
536
+ }
537
+
538
+ // Clear previous line and render new one
539
+ if (this.lastRender) {
540
+ process.stdout.write('\r\x1b[K');
541
+ }
542
+
543
+ process.stdout.write(progressText);
544
+ this.lastRender = progressText;
545
+
546
+ if (this.current >= this.total) {
547
+ process.stdout.write('\n');
548
+ }
549
+ }
550
+
551
+ private renderGradientBar(filledWidth: number, emptyWidth: number): string {
552
+ const colors = [chalk.red, chalk.yellow, chalk.green, chalk.cyan, chalk.blue, chalk.magenta];
553
+
554
+ let filledBar = '';
555
+ for (let i = 0; i < filledWidth; i++) {
556
+ const colorIndex = Math.floor((i / this.width) * colors.length);
557
+ const colorFn = colors[Math.min(colorIndex, colors.length - 1)];
558
+ if (colorFn) {
559
+ filledBar += colorFn('█');
560
+ }
561
+ }
562
+
563
+ const emptyBar = chalk.gray('░'.repeat(emptyWidth));
564
+ return filledBar + emptyBar;
565
+ }
566
+
567
+ private renderFancyBar(filledWidth: number, emptyWidth: number): string {
568
+ const filledBar = chalk.cyan('▓'.repeat(filledWidth));
569
+ const emptyBar = chalk.gray('░'.repeat(emptyWidth));
570
+ return filledBar + emptyBar;
571
+ }
572
+
573
+ private renderMinimalBar(filledWidth: number, emptyWidth: number): string {
574
+ const filledBar = chalk.white('━'.repeat(filledWidth));
575
+ const emptyBar = chalk.gray('─'.repeat(emptyWidth));
576
+ return filledBar + emptyBar;
577
+ }
578
+
579
+ private renderBlockBar(filledWidth: number, emptyWidth: number): string {
580
+ const filledBar = chalk.green('■'.repeat(filledWidth));
581
+ const emptyBar = chalk.gray('□'.repeat(emptyWidth));
582
+ return filledBar + emptyBar;
583
+ }
584
+
585
+ private renderDefaultBar(filledWidth: number, emptyWidth: number): string {
586
+ const filledBar = chalk.green('█'.repeat(filledWidth));
587
+ const emptyBar = chalk.gray('░'.repeat(emptyWidth));
588
+ return filledBar + emptyBar;
589
+ }
590
+
591
+ private getStyledPrefix(): string {
592
+ switch (this.style) {
593
+ case 'gradient':
594
+ return chalk.magenta('▶');
595
+ case 'fancy':
596
+ return chalk.cyan('★');
597
+ case 'minimal':
598
+ return chalk.gray('•');
599
+ case 'blocks':
600
+ return chalk.green('◆');
601
+ default:
602
+ return chalk.cyan('●');
603
+ }
604
+ }
605
+
606
+ private getStyledPercentage(percentage: number): string {
607
+ if (percentage < 25) return chalk.red.bold(`${percentage}%`);
608
+ if (percentage < 50) return chalk.yellow.bold(`${percentage}%`);
609
+ if (percentage < 75) return chalk.blue.bold(`${percentage}%`);
610
+ if (percentage < 100) return chalk.cyan.bold(`${percentage}%`);
611
+ return chalk.green.bold(`${percentage}%`);
612
+ }
613
+
614
+ /**
615
+ * Create a gradient percentage progress bar
616
+ */
617
+ static createGradient(width = 40): PercentageProgressBar {
618
+ return new PercentageProgressBar(width, {
619
+ style: 'gradient',
620
+ showStats: true,
621
+ multiLine: true,
622
+ });
623
+ }
624
+
625
+ /**
626
+ * Create a fancy percentage progress bar
627
+ */
628
+ static createFancy(width = 40): PercentageProgressBar {
629
+ return new PercentageProgressBar(width, { style: 'fancy', showStats: true, multiLine: true });
630
+ }
631
+
632
+ /**
633
+ * Create a minimal percentage progress bar
634
+ */
635
+ static createMinimal(width = 40): PercentageProgressBar {
636
+ return new PercentageProgressBar(width, {
637
+ style: 'minimal',
638
+ showStats: false,
639
+ multiLine: true,
640
+ });
641
+ }
642
+
643
+ /**
644
+ * Create a block-style percentage progress bar
645
+ */
646
+ static createBlocks(width = 40): PercentageProgressBar {
647
+ return new PercentageProgressBar(width, { style: 'blocks', showStats: true, multiLine: true });
648
+ }
649
+ }
650
+
651
+ /**
652
+ * Progress manager for batch operations
653
+ */
654
+ export class BatchProgressManager {
655
+ private bars: Map<string, ProgressBar> = new Map();
656
+ private totalOperations = 0;
657
+ private completedOperations = 0;
658
+
659
+ createBar(id: string, options?: ProgressBarOptions): ProgressBar {
660
+ const bar = new ProgressBar(options);
661
+ this.bars.set(id, bar);
662
+ return bar;
663
+ }
664
+
665
+ getBar(id: string): ProgressBar | undefined {
666
+ return this.bars.get(id);
667
+ }
668
+
669
+ setTotal(total: number): void {
670
+ this.totalOperations = total;
671
+ }
672
+
673
+ updateOverall(text: string): void {
674
+ const percentage =
675
+ this.totalOperations > 0
676
+ ? Math.round((this.completedOperations / this.totalOperations) * 100)
677
+ : 0;
678
+
679
+ console.log(
680
+ chalk.cyan(
681
+ `📊 Overall Progress: ${percentage}% (${this.completedOperations}/${this.totalOperations})`
682
+ )
683
+ );
684
+ if (text) {
685
+ console.log(chalk.gray(` → ${text}`));
686
+ }
687
+ }
688
+
689
+ completeOperation(text?: string): void {
690
+ this.completedOperations++;
691
+ if (text) {
692
+ this.updateOverall(text);
693
+ }
694
+ }
695
+
696
+ cleanup(): void {
697
+ this.bars.forEach((bar) => bar.stop());
698
+ this.bars.clear();
699
+ }
700
+ }