@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.
- package/WORKSPACE_INTEGRATION.md +290 -0
- package/chat-sessions/sessions-index.json +98 -0
- package/package.json +43 -0
- package/package.json.original +31 -0
- package/package.json.publish +47 -0
- package/src/bootstrap/state.d.ts +169 -0
- package/src/bootstrap/state.js +261 -0
- package/src/bootstrap/state.ts +371 -0
- package/src/context/pack-builder.d.ts +24 -0
- package/src/context/pack-builder.js +281 -0
- package/src/context/pack-builder.ts +334 -0
- package/src/hooks/useExitOnCtrlCD.d.ts +86 -0
- package/src/hooks/useExitOnCtrlCD.js +130 -0
- package/src/hooks/useExitOnCtrlCD.ts +202 -0
- package/src/hooks/useKeybinding.d.ts +89 -0
- package/src/hooks/useKeybinding.js +163 -0
- package/src/hooks/useKeybinding.ts +226 -0
- package/src/hooks/useTerminalSize.d.ts +41 -0
- package/src/hooks/useTerminalSize.js +90 -0
- package/src/hooks/useTerminalSize.ts +117 -0
- package/src/index.d.ts +23 -0
- package/src/index.js +24 -0
- package/src/index.ts +132 -0
- package/src/runtime/brand/brand.d.ts +488 -0
- package/src/runtime/brand/brand.js +464 -0
- package/src/runtime/brand/brand.ts +667 -0
- package/src/runtime/services/tokenEstimation.d.ts +187 -0
- package/src/runtime/services/tokenEstimation.js +423 -0
- package/src/runtime/services/tokenEstimation.ts +547 -0
- package/src/runtime/tools/Tool.d.ts +239 -0
- package/src/runtime/tools/Tool.js +285 -0
- package/src/runtime/tools/Tool.ts +524 -0
- package/src/scheduler/TaskScheduler.d.ts +98 -0
- package/src/scheduler/TaskScheduler.js +201 -0
- package/src/scheduler/TaskScheduler.ts +259 -0
- package/src/state/AppStateStore.d.ts +347 -0
- package/src/state/AppStateStore.js +439 -0
- package/src/state/AppStateStore.ts +658 -0
- package/src/state/WorkspaceStateStore.d.ts +43 -0
- package/src/state/WorkspaceStateStore.js +224 -0
- package/src/state/WorkspaceStateStore.ts +297 -0
- package/src/types/message.d.ts +190 -0
- package/src/types/message.js +77 -0
- package/src/types/message.ts +277 -0
- package/src/utils/format.d.ts +126 -0
- package/src/utils/format.js +230 -0
- package/src/utils/format.ts +336 -0
- package/src/utils/fullscreen.d.ts +161 -0
- package/src/utils/fullscreen.js +277 -0
- package/src/utils/fullscreen.ts +382 -0
- package/src/utils/sessionStorage.d.ts +210 -0
- package/src/utils/sessionStorage.js +435 -0
- package/src/utils/sessionStorage.ts +559 -0
- package/src/watcher/WorkspaceWatcher.d.ts +70 -0
- package/src/watcher/WorkspaceWatcher.js +191 -0
- package/src/watcher/WorkspaceWatcher.ts +251 -0
- package/src/workspace/loader.d.ts +30 -0
- package/src/workspace/loader.js +117 -0
- package/src/workspace/loader.ts +171 -0
- package/src/workspace/types.d.ts +91 -0
- package/src/workspace/types.js +7 -0
- package/src/workspace/types.ts +122 -0
- package/threads/thread_1775361175022.json +9 -0
- 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
|
+
};
|