@allternit/gizzi-code 1.0.0

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 (64) hide show
  1. package/WORKSPACE_INTEGRATION.md +290 -0
  2. package/chat-sessions/sessions-index.json +98 -0
  3. package/package.json +43 -0
  4. package/package.json.original +31 -0
  5. package/package.json.publish +47 -0
  6. package/src/bootstrap/state.d.ts +169 -0
  7. package/src/bootstrap/state.js +261 -0
  8. package/src/bootstrap/state.ts +371 -0
  9. package/src/context/pack-builder.d.ts +24 -0
  10. package/src/context/pack-builder.js +281 -0
  11. package/src/context/pack-builder.ts +334 -0
  12. package/src/hooks/useExitOnCtrlCD.d.ts +86 -0
  13. package/src/hooks/useExitOnCtrlCD.js +130 -0
  14. package/src/hooks/useExitOnCtrlCD.ts +202 -0
  15. package/src/hooks/useKeybinding.d.ts +89 -0
  16. package/src/hooks/useKeybinding.js +163 -0
  17. package/src/hooks/useKeybinding.ts +226 -0
  18. package/src/hooks/useTerminalSize.d.ts +41 -0
  19. package/src/hooks/useTerminalSize.js +90 -0
  20. package/src/hooks/useTerminalSize.ts +117 -0
  21. package/src/index.d.ts +23 -0
  22. package/src/index.js +24 -0
  23. package/src/index.ts +132 -0
  24. package/src/runtime/brand/brand.d.ts +488 -0
  25. package/src/runtime/brand/brand.js +464 -0
  26. package/src/runtime/brand/brand.ts +667 -0
  27. package/src/runtime/services/tokenEstimation.d.ts +187 -0
  28. package/src/runtime/services/tokenEstimation.js +423 -0
  29. package/src/runtime/services/tokenEstimation.ts +547 -0
  30. package/src/runtime/tools/Tool.d.ts +239 -0
  31. package/src/runtime/tools/Tool.js +285 -0
  32. package/src/runtime/tools/Tool.ts +524 -0
  33. package/src/scheduler/TaskScheduler.d.ts +98 -0
  34. package/src/scheduler/TaskScheduler.js +201 -0
  35. package/src/scheduler/TaskScheduler.ts +259 -0
  36. package/src/state/AppStateStore.d.ts +347 -0
  37. package/src/state/AppStateStore.js +439 -0
  38. package/src/state/AppStateStore.ts +658 -0
  39. package/src/state/WorkspaceStateStore.d.ts +43 -0
  40. package/src/state/WorkspaceStateStore.js +224 -0
  41. package/src/state/WorkspaceStateStore.ts +297 -0
  42. package/src/types/message.d.ts +190 -0
  43. package/src/types/message.js +77 -0
  44. package/src/types/message.ts +277 -0
  45. package/src/utils/format.d.ts +126 -0
  46. package/src/utils/format.js +230 -0
  47. package/src/utils/format.ts +336 -0
  48. package/src/utils/fullscreen.d.ts +161 -0
  49. package/src/utils/fullscreen.js +277 -0
  50. package/src/utils/fullscreen.ts +382 -0
  51. package/src/utils/sessionStorage.d.ts +210 -0
  52. package/src/utils/sessionStorage.js +435 -0
  53. package/src/utils/sessionStorage.ts +559 -0
  54. package/src/watcher/WorkspaceWatcher.d.ts +70 -0
  55. package/src/watcher/WorkspaceWatcher.js +191 -0
  56. package/src/watcher/WorkspaceWatcher.ts +251 -0
  57. package/src/workspace/loader.d.ts +30 -0
  58. package/src/workspace/loader.js +117 -0
  59. package/src/workspace/loader.ts +171 -0
  60. package/src/workspace/types.d.ts +91 -0
  61. package/src/workspace/types.js +7 -0
  62. package/src/workspace/types.ts +122 -0
  63. package/threads/thread_1775361175022.json +9 -0
  64. package/tsconfig.json +14 -0
@@ -0,0 +1,230 @@
1
+ /**
2
+ * @fileoverview Format utilities for gizzi-code integration
3
+ * @module utils/format
4
+ *
5
+ * Provides formatting utilities for durations, byte sizes, and numbers.
6
+ * These functions are commonly used in CLI output and display components.
7
+ */
8
+ /**
9
+ * Time unit definitions for duration formatting
10
+ * @constant {Record<string, number>}
11
+ */
12
+ const TIME_UNITS = {
13
+ millisecond: 1,
14
+ second: 1000,
15
+ minute: 60 * 1000,
16
+ hour: 60 * 60 * 1000,
17
+ day: 24 * 60 * 60 * 1000,
18
+ week: 7 * 24 * 60 * 60 * 1000,
19
+ month: 30 * 24 * 60 * 60 * 1000,
20
+ year: 365 * 24 * 60 * 60 * 1000,
21
+ };
22
+ /**
23
+ * Byte unit definitions for size formatting
24
+ * @constant {string[]}
25
+ */
26
+ const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
27
+ /**
28
+ * Formats a duration in milliseconds to a human-readable string
29
+ *
30
+ * @param {number} ms - Duration in milliseconds
31
+ * @param {DurationFormatOptions} [options] - Format options
32
+ * @returns {string} Formatted duration string
33
+ * @example
34
+ * ```typescript
35
+ * formatDuration(5000); // "5s"
36
+ * formatDuration(65000); // "1m 5s"
37
+ * formatDuration(3600000); // "1h"
38
+ * formatDuration(90000, { style: 'long' }); // "1 minute 30 seconds"
39
+ * ```
40
+ */
41
+ export function formatDuration(ms, options = {}) {
42
+ const { style = 'auto', maxUnits = 2, includeMs = false, round = false, } = options;
43
+ // Handle edge cases
44
+ if (!Number.isFinite(ms) || ms < 0) {
45
+ return style === 'long' ? 'unknown duration' : '--';
46
+ }
47
+ if (ms === 0) {
48
+ return style === 'long' ? '0 seconds' : '0s';
49
+ }
50
+ // Round if requested
51
+ const duration = round ? Math.round(ms) : ms;
52
+ // Determine appropriate units
53
+ const units = [
54
+ { value: TIME_UNITS.year, short: 'y', long: 'year' },
55
+ { value: TIME_UNITS.month, short: 'mo', long: 'month' },
56
+ { value: TIME_UNITS.week, short: 'w', long: 'week' },
57
+ { value: TIME_UNITS.day, short: 'd', long: 'day' },
58
+ { value: TIME_UNITS.hour, short: 'h', long: 'hour' },
59
+ { value: TIME_UNITS.minute, short: 'm', long: 'minute' },
60
+ { value: TIME_UNITS.second, short: 's', long: 'second' },
61
+ ];
62
+ // Add milliseconds if requested
63
+ if (includeMs || duration < TIME_UNITS.second) {
64
+ units.push({ value: TIME_UNITS.millisecond, short: 'ms', long: 'millisecond' });
65
+ }
66
+ const parts = [];
67
+ let remaining = Math.abs(duration);
68
+ for (const unit of units) {
69
+ if (remaining >= unit.value) {
70
+ const count = Math.floor(remaining / unit.value);
71
+ remaining %= unit.value;
72
+ const useLong = style === 'long';
73
+ const unitStr = useLong
74
+ ? `${unit.long}${count !== 1 ? 's' : ''}`
75
+ : unit.short;
76
+ parts.push(useLong ? `${count} ${unitStr}` : `${count}${unitStr}`);
77
+ if (parts.length >= maxUnits) {
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ if (parts.length === 0) {
83
+ return style === 'long' ? '0 seconds' : '0s';
84
+ }
85
+ return parts.join(style === 'long' ? ' ' : ' ');
86
+ }
87
+ /**
88
+ * Formats a byte size to a human-readable string
89
+ *
90
+ * @param {number} bytes - Size in bytes
91
+ * @param {ByteFormatOptions} [options] - Format options
92
+ * @returns {string} Formatted size string
93
+ * @example
94
+ * ```typescript
95
+ * formatBytes(1024); // "1 KB"
96
+ * formatBytes(1536); // "1.50 KB"
97
+ * formatBytes(1048576); // "1 MB"
98
+ * formatBytes(1024, { binary: true }); // "1 KiB"
99
+ * ```
100
+ */
101
+ export function formatBytes(bytes, options = {}) {
102
+ const { decimals = 2, binary = false, space = true } = options;
103
+ // Handle edge cases
104
+ if (!Number.isFinite(bytes)) {
105
+ return '0 B';
106
+ }
107
+ const absBytes = Math.abs(bytes);
108
+ if (absBytes === 0) {
109
+ return '0 B';
110
+ }
111
+ // Use 1024 for binary (KiB, MiB) or 1000 for decimal (KB, MB)
112
+ const base = binary ? 1024 : 1000;
113
+ const binarySuffix = binary ? 'i' : '';
114
+ // Find appropriate unit
115
+ const exponent = Math.min(Math.floor(Math.log(absBytes) / Math.log(base)), BYTE_UNITS.length - 1);
116
+ const value = bytes / Math.pow(base, exponent);
117
+ const unit = BYTE_UNITS[exponent] + (exponent > 0 ? binarySuffix : '');
118
+ // Format with specified decimal places
119
+ const formattedValue = value.toFixed(decimals);
120
+ const trimmedValue = parseFloat(formattedValue).toString();
121
+ return space ? `${trimmedValue} ${unit}` : `${trimmedValue}${unit}`;
122
+ }
123
+ /**
124
+ * Formats a number with locale-aware formatting
125
+ *
126
+ * @param {number} num - Number to format
127
+ * @param {NumberFormatOptions} [options] - Format options
128
+ * @returns {string} Formatted number string
129
+ * @example
130
+ * ```typescript
131
+ * formatNumber(1234567); // "1,234,567"
132
+ * formatNumber(1234.5678, { maximumFractionDigits: 2 }); // "1,234.57"
133
+ * formatNumber(0.001234, { minimumFractionDigits: 6 }); // "0.001234"
134
+ * ```
135
+ */
136
+ export function formatNumber(num, options = {}) {
137
+ const { minimumFractionDigits, maximumFractionDigits = 2, useGrouping = true, locale = 'en-US', } = options;
138
+ // Handle edge cases
139
+ if (!Number.isFinite(num)) {
140
+ if (Number.isNaN(num))
141
+ return 'NaN';
142
+ return num > 0 ? '∞' : '-∞';
143
+ }
144
+ try {
145
+ return new Intl.NumberFormat(locale, {
146
+ minimumFractionDigits,
147
+ maximumFractionDigits,
148
+ useGrouping,
149
+ }).format(num);
150
+ }
151
+ catch {
152
+ // Fallback for environments without Intl support
153
+ const absNum = Math.abs(num);
154
+ const sign = num < 0 ? '-' : '';
155
+ // Handle integer part with grouping
156
+ const integerPart = Math.floor(absNum);
157
+ const fractionPart = absNum - integerPart;
158
+ let result = useGrouping
159
+ ? integerPart.toLocaleString(locale)
160
+ : integerPart.toString();
161
+ // Add fraction part if needed
162
+ const minDecimals = minimumFractionDigits ?? 0;
163
+ const maxDecimals = maximumFractionDigits ?? 2;
164
+ if (fractionPart > 0 || minDecimals > 0) {
165
+ const fractionStr = fractionPart
166
+ .toFixed(maxDecimals)
167
+ .slice(2)
168
+ .padEnd(minDecimals, '0');
169
+ // Trim trailing zeros if no minimum required
170
+ const trimmedFraction = minDecimals === 0
171
+ ? fractionStr.replace(/0+$/, '')
172
+ : fractionStr;
173
+ if (trimmedFraction.length > 0) {
174
+ result += '.' + trimmedFraction;
175
+ }
176
+ }
177
+ return sign + result;
178
+ }
179
+ }
180
+ /**
181
+ * Formats a number as a percentage
182
+ *
183
+ * @param {number} value - Value to format (0.5 = 50%)
184
+ * @param {number} [decimals=1] - Number of decimal places
185
+ * @returns {string} Formatted percentage string
186
+ * @example
187
+ * ```typescript
188
+ * formatPercentage(0.5); // "50%"
189
+ * formatPercentage(0.1234, 2); // "12.34%"
190
+ * ```
191
+ */
192
+ export function formatPercentage(value, decimals = 1) {
193
+ if (!Number.isFinite(value)) {
194
+ return '--%';
195
+ }
196
+ return `${(value * 100).toFixed(decimals)}%`;
197
+ }
198
+ /**
199
+ * Formats a number as compact notation (1K, 1M, etc.)
200
+ *
201
+ * @param {number} num - Number to format
202
+ * @param {number} [decimals=1] - Number of decimal places
203
+ * @returns {string} Compact formatted string
204
+ * @example
205
+ * ```typescript
206
+ * formatCompact(1234); // "1.2K"
207
+ * formatCompact(1000000); // "1M"
208
+ * formatCompact(1234567890); // "1.2B"
209
+ * ```
210
+ */
211
+ export function formatCompact(num, decimals = 1) {
212
+ if (!Number.isFinite(num) || num === 0) {
213
+ return '0';
214
+ }
215
+ const absNum = Math.abs(num);
216
+ const sign = num < 0 ? '-' : '';
217
+ const units = ['', 'K', 'M', 'B', 'T'];
218
+ const unitIndex = Math.min(Math.floor(Math.log10(absNum) / 3), units.length - 1);
219
+ const value = absNum / Math.pow(1000, unitIndex);
220
+ const formatted = value.toFixed(decimals).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1');
221
+ return `${sign}${formatted}${units[unitIndex]}`;
222
+ }
223
+ // Default export
224
+ export default {
225
+ formatDuration,
226
+ formatBytes,
227
+ formatNumber,
228
+ formatPercentage,
229
+ formatCompact,
230
+ };
@@ -0,0 +1,336 @@
1
+ /**
2
+ * @fileoverview Format utilities for gizzi-code integration
3
+ * @module utils/format
4
+ *
5
+ * Provides formatting utilities for durations, byte sizes, and numbers.
6
+ * These functions are commonly used in CLI output and display components.
7
+ */
8
+
9
+ /**
10
+ * Time unit definitions for duration formatting
11
+ * @constant {Record<string, number>}
12
+ */
13
+ const TIME_UNITS = {
14
+ millisecond: 1,
15
+ second: 1000,
16
+ minute: 60 * 1000,
17
+ hour: 60 * 60 * 1000,
18
+ day: 24 * 60 * 60 * 1000,
19
+ week: 7 * 24 * 60 * 60 * 1000,
20
+ month: 30 * 24 * 60 * 60 * 1000,
21
+ year: 365 * 24 * 60 * 60 * 1000,
22
+ };
23
+
24
+ /**
25
+ * Byte unit definitions for size formatting
26
+ * @constant {string[]}
27
+ */
28
+ const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
29
+
30
+ /**
31
+ * Number format options
32
+ * @interface NumberFormatOptions
33
+ */
34
+ export interface NumberFormatOptions {
35
+ /** Minimum number of decimal places */
36
+ readonly minimumFractionDigits?: number;
37
+ /** Maximum number of decimal places */
38
+ readonly maximumFractionDigits?: number;
39
+ /** Use thousands separator */
40
+ readonly useGrouping?: boolean;
41
+ /** Locale for formatting (default: 'en-US') */
42
+ readonly locale?: string;
43
+ }
44
+
45
+ /**
46
+ * Duration format options
47
+ * @interface DurationFormatOptions
48
+ */
49
+ export interface DurationFormatOptions {
50
+ /** Format style (default: 'auto') */
51
+ readonly style?: 'short' | 'long' | 'narrow' | 'auto';
52
+ /** Maximum number of units to display (default: 2) */
53
+ readonly maxUnits?: number;
54
+ /** Include milliseconds in output */
55
+ readonly includeMs?: boolean;
56
+ /** Round to nearest unit */
57
+ readonly round?: boolean;
58
+ }
59
+
60
+ /**
61
+ * Byte format options
62
+ * @interface ByteFormatOptions
63
+ */
64
+ export interface ByteFormatOptions {
65
+ /** Number of decimal places (default: 2) */
66
+ readonly decimals?: number;
67
+ /** Use binary units (KiB, MiB) instead of decimal (KB, MB) */
68
+ readonly binary?: boolean;
69
+ /** Include space between value and unit */
70
+ readonly space?: boolean;
71
+ }
72
+
73
+ /**
74
+ * Formats a duration in milliseconds to a human-readable string
75
+ *
76
+ * @param {number} ms - Duration in milliseconds
77
+ * @param {DurationFormatOptions} [options] - Format options
78
+ * @returns {string} Formatted duration string
79
+ * @example
80
+ * ```typescript
81
+ * formatDuration(5000); // "5s"
82
+ * formatDuration(65000); // "1m 5s"
83
+ * formatDuration(3600000); // "1h"
84
+ * formatDuration(90000, { style: 'long' }); // "1 minute 30 seconds"
85
+ * ```
86
+ */
87
+ export function formatDuration(
88
+ ms: number,
89
+ options: DurationFormatOptions = {}
90
+ ): string {
91
+ const {
92
+ style = 'auto',
93
+ maxUnits = 2,
94
+ includeMs = false,
95
+ round = false,
96
+ } = options;
97
+
98
+ // Handle edge cases
99
+ if (!Number.isFinite(ms) || ms < 0) {
100
+ return style === 'long' ? 'unknown duration' : '--';
101
+ }
102
+
103
+ if (ms === 0) {
104
+ return style === 'long' ? '0 seconds' : '0s';
105
+ }
106
+
107
+ // Round if requested
108
+ const duration = round ? Math.round(ms) : ms;
109
+
110
+ // Determine appropriate units
111
+ const units: Array<{ value: number; short: string; long: string }> = [
112
+ { value: TIME_UNITS.year, short: 'y', long: 'year' },
113
+ { value: TIME_UNITS.month, short: 'mo', long: 'month' },
114
+ { value: TIME_UNITS.week, short: 'w', long: 'week' },
115
+ { value: TIME_UNITS.day, short: 'd', long: 'day' },
116
+ { value: TIME_UNITS.hour, short: 'h', long: 'hour' },
117
+ { value: TIME_UNITS.minute, short: 'm', long: 'minute' },
118
+ { value: TIME_UNITS.second, short: 's', long: 'second' },
119
+ ];
120
+
121
+ // Add milliseconds if requested
122
+ if (includeMs || duration < TIME_UNITS.second) {
123
+ units.push({ value: TIME_UNITS.millisecond, short: 'ms', long: 'millisecond' });
124
+ }
125
+
126
+ const parts: string[] = [];
127
+ let remaining = Math.abs(duration);
128
+
129
+ for (const unit of units) {
130
+ if (remaining >= unit.value) {
131
+ const count = Math.floor(remaining / unit.value);
132
+ remaining %= unit.value;
133
+
134
+ const useLong = style === 'long';
135
+ const unitStr = useLong
136
+ ? `${unit.long}${count !== 1 ? 's' : ''}`
137
+ : unit.short;
138
+
139
+ parts.push(useLong ? `${count} ${unitStr}` : `${count}${unitStr}`);
140
+
141
+ if (parts.length >= maxUnits) {
142
+ break;
143
+ }
144
+ }
145
+ }
146
+
147
+ if (parts.length === 0) {
148
+ return style === 'long' ? '0 seconds' : '0s';
149
+ }
150
+
151
+ return parts.join(style === 'long' ? ' ' : ' ');
152
+ }
153
+
154
+ /**
155
+ * Formats a byte size to a human-readable string
156
+ *
157
+ * @param {number} bytes - Size in bytes
158
+ * @param {ByteFormatOptions} [options] - Format options
159
+ * @returns {string} Formatted size string
160
+ * @example
161
+ * ```typescript
162
+ * formatBytes(1024); // "1 KB"
163
+ * formatBytes(1536); // "1.50 KB"
164
+ * formatBytes(1048576); // "1 MB"
165
+ * formatBytes(1024, { binary: true }); // "1 KiB"
166
+ * ```
167
+ */
168
+ export function formatBytes(
169
+ bytes: number,
170
+ options: ByteFormatOptions = {}
171
+ ): string {
172
+ const { decimals = 2, binary = false, space = true } = options;
173
+
174
+ // Handle edge cases
175
+ if (!Number.isFinite(bytes)) {
176
+ return '0 B';
177
+ }
178
+
179
+ const absBytes = Math.abs(bytes);
180
+
181
+ if (absBytes === 0) {
182
+ return '0 B';
183
+ }
184
+
185
+ // Use 1024 for binary (KiB, MiB) or 1000 for decimal (KB, MB)
186
+ const base = binary ? 1024 : 1000;
187
+ const binarySuffix = binary ? 'i' : '';
188
+
189
+ // Find appropriate unit
190
+ const exponent = Math.min(
191
+ Math.floor(Math.log(absBytes) / Math.log(base)),
192
+ BYTE_UNITS.length - 1
193
+ );
194
+
195
+ const value = bytes / Math.pow(base, exponent);
196
+ const unit = BYTE_UNITS[exponent] + (exponent > 0 ? binarySuffix : '');
197
+
198
+ // Format with specified decimal places
199
+ const formattedValue = value.toFixed(decimals);
200
+ const trimmedValue = parseFloat(formattedValue).toString();
201
+
202
+ return space ? `${trimmedValue} ${unit}` : `${trimmedValue}${unit}`;
203
+ }
204
+
205
+ /**
206
+ * Formats a number with locale-aware formatting
207
+ *
208
+ * @param {number} num - Number to format
209
+ * @param {NumberFormatOptions} [options] - Format options
210
+ * @returns {string} Formatted number string
211
+ * @example
212
+ * ```typescript
213
+ * formatNumber(1234567); // "1,234,567"
214
+ * formatNumber(1234.5678, { maximumFractionDigits: 2 }); // "1,234.57"
215
+ * formatNumber(0.001234, { minimumFractionDigits: 6 }); // "0.001234"
216
+ * ```
217
+ */
218
+ export function formatNumber(
219
+ num: number,
220
+ options: NumberFormatOptions = {}
221
+ ): string {
222
+ const {
223
+ minimumFractionDigits,
224
+ maximumFractionDigits = 2,
225
+ useGrouping = true,
226
+ locale = 'en-US',
227
+ } = options;
228
+
229
+ // Handle edge cases
230
+ if (!Number.isFinite(num)) {
231
+ if (Number.isNaN(num)) return 'NaN';
232
+ return num > 0 ? '∞' : '-∞';
233
+ }
234
+
235
+ try {
236
+ return new Intl.NumberFormat(locale, {
237
+ minimumFractionDigits,
238
+ maximumFractionDigits,
239
+ useGrouping,
240
+ }).format(num);
241
+ } catch {
242
+ // Fallback for environments without Intl support
243
+ const absNum = Math.abs(num);
244
+ const sign = num < 0 ? '-' : '';
245
+
246
+ // Handle integer part with grouping
247
+ const integerPart = Math.floor(absNum);
248
+ const fractionPart = absNum - integerPart;
249
+
250
+ let result = useGrouping
251
+ ? integerPart.toLocaleString(locale)
252
+ : integerPart.toString();
253
+
254
+ // Add fraction part if needed
255
+ const minDecimals = minimumFractionDigits ?? 0;
256
+ const maxDecimals = maximumFractionDigits ?? 2;
257
+
258
+ if (fractionPart > 0 || minDecimals > 0) {
259
+ const fractionStr = fractionPart
260
+ .toFixed(maxDecimals)
261
+ .slice(2)
262
+ .padEnd(minDecimals, '0');
263
+ // Trim trailing zeros if no minimum required
264
+ const trimmedFraction =
265
+ minDecimals === 0
266
+ ? fractionStr.replace(/0+$/, '')
267
+ : fractionStr;
268
+ if (trimmedFraction.length > 0) {
269
+ result += '.' + trimmedFraction;
270
+ }
271
+ }
272
+
273
+ return sign + result;
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Formats a number as a percentage
279
+ *
280
+ * @param {number} value - Value to format (0.5 = 50%)
281
+ * @param {number} [decimals=1] - Number of decimal places
282
+ * @returns {string} Formatted percentage string
283
+ * @example
284
+ * ```typescript
285
+ * formatPercentage(0.5); // "50%"
286
+ * formatPercentage(0.1234, 2); // "12.34%"
287
+ * ```
288
+ */
289
+ export function formatPercentage(value: number, decimals: number = 1): string {
290
+ if (!Number.isFinite(value)) {
291
+ return '--%';
292
+ }
293
+ return `${(value * 100).toFixed(decimals)}%`;
294
+ }
295
+
296
+ /**
297
+ * Formats a number as compact notation (1K, 1M, etc.)
298
+ *
299
+ * @param {number} num - Number to format
300
+ * @param {number} [decimals=1] - Number of decimal places
301
+ * @returns {string} Compact formatted string
302
+ * @example
303
+ * ```typescript
304
+ * formatCompact(1234); // "1.2K"
305
+ * formatCompact(1000000); // "1M"
306
+ * formatCompact(1234567890); // "1.2B"
307
+ * ```
308
+ */
309
+ export function formatCompact(num: number, decimals: number = 1): string {
310
+ if (!Number.isFinite(num) || num === 0) {
311
+ return '0';
312
+ }
313
+
314
+ const absNum = Math.abs(num);
315
+ const sign = num < 0 ? '-' : '';
316
+
317
+ const units = ['', 'K', 'M', 'B', 'T'];
318
+ const unitIndex = Math.min(
319
+ Math.floor(Math.log10(absNum) / 3),
320
+ units.length - 1
321
+ );
322
+
323
+ const value = absNum / Math.pow(1000, unitIndex);
324
+ const formatted = value.toFixed(decimals).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1');
325
+
326
+ return `${sign}${formatted}${units[unitIndex]}`;
327
+ }
328
+
329
+ // Default export
330
+ export default {
331
+ formatDuration,
332
+ formatBytes,
333
+ formatNumber,
334
+ formatPercentage,
335
+ formatCompact,
336
+ };