@shohojdhara/atomix 0.5.2 → 0.5.4
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/atomix.config.ts +33 -33
- package/dist/config.d.ts +187 -112
- package/dist/config.js +7 -49
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1958 -900
- package/dist/index.esm.js +2275 -383
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2327 -417
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +1390 -276
- package/dist/theme.js +2129 -621
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/scripts/cli/internal/config-loader.js +30 -20
- package/src/lib/config/index.ts +38 -362
- package/src/lib/config/loader.ts +419 -0
- package/src/lib/config/public-api.ts +43 -0
- package/src/lib/config/types.ts +389 -0
- package/src/lib/config/validator.ts +305 -0
- package/src/lib/theme/adapters/index.ts +1 -1
- package/src/lib/theme/adapters/themeAdapter.ts +358 -229
- package/src/lib/theme/components/ThemeToggle.tsx +276 -0
- package/src/lib/theme/config/configLoader.ts +351 -0
- package/src/lib/theme/config/loader.ts +221 -0
- package/src/lib/theme/core/createTheme.ts +126 -50
- package/src/lib/theme/core/createThemeObject.ts +7 -4
- package/src/lib/theme/hooks/useThemeSwitcher.ts +164 -0
- package/src/lib/theme/index.ts +322 -38
- package/src/lib/theme/runtime/ThemeProvider.tsx +44 -10
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +44 -393
- package/src/lib/theme/runtime/useTheme.ts +1 -0
- package/src/lib/theme/tokens/tokens.ts +101 -1
- package/src/lib/theme/types.ts +91 -0
- package/src/lib/theme/utils/performanceMonitor.ts +315 -0
- package/src/lib/theme/utils/responsive.ts +280 -0
- package/src/lib/theme/utils/themeUtils.ts +531 -117
- package/src/styles/05-objects/_objects.masonry-grid.scss +3 -3
|
@@ -378,6 +378,59 @@ export interface DesignTokens {
|
|
|
378
378
|
'breakpoint-xl': string;
|
|
379
379
|
'breakpoint-xxl': string;
|
|
380
380
|
|
|
381
|
+
// ============================================================================
|
|
382
|
+
// Advanced Features - Interactive Effects (Phase 2)
|
|
383
|
+
// ============================================================================
|
|
384
|
+
'interactive-vortex-enabled': string;
|
|
385
|
+
'interactive-vortex-strength': string;
|
|
386
|
+
'interactive-vortex-radius': string;
|
|
387
|
+
'interactive-vortex-decay': string;
|
|
388
|
+
'interactive-chromatic-enabled': string;
|
|
389
|
+
'interactive-chromatic-mode': string;
|
|
390
|
+
'interactive-chromatic-red-shift': string;
|
|
391
|
+
'interactive-chromatic-green-shift': string;
|
|
392
|
+
'interactive-chromatic-blue-shift': string;
|
|
393
|
+
'interactive-chromatic-edge-only': string;
|
|
394
|
+
'interactive-chromatic-edge-threshold': string;
|
|
395
|
+
'interactive-mouse-sensitivity': string;
|
|
396
|
+
'interactive-mouse-trail-effect': string;
|
|
397
|
+
'interactive-animation-speed-base': string;
|
|
398
|
+
'interactive-animation-speed-multiplier': string;
|
|
399
|
+
|
|
400
|
+
// ============================================================================
|
|
401
|
+
// Advanced Features - Optimization (Phase 3)
|
|
402
|
+
// ============================================================================
|
|
403
|
+
'optimization-breakpoint-mobile': string;
|
|
404
|
+
'optimization-breakpoint-tablet': string;
|
|
405
|
+
'optimization-breakpoint-desktop': string;
|
|
406
|
+
'optimization-breakpoint-wide': string;
|
|
407
|
+
'optimization-device-scaling-mobile': string;
|
|
408
|
+
'optimization-device-scaling-tablet': string;
|
|
409
|
+
'optimization-device-scaling-desktop': string;
|
|
410
|
+
'optimization-performance-fps-target': string;
|
|
411
|
+
'optimization-auto-scaling-enabled': string;
|
|
412
|
+
'optimization-auto-scaling-low-end': string;
|
|
413
|
+
'optimization-auto-scaling-mid-range': string;
|
|
414
|
+
'optimization-auto-scaling-high-end': string;
|
|
415
|
+
|
|
416
|
+
// ============================================================================
|
|
417
|
+
// Advanced Features - Visual Polish (Phase 4)
|
|
418
|
+
// ============================================================================
|
|
419
|
+
'visual-polish-border-iridescent-glow': string;
|
|
420
|
+
'visual-polish-border-shimmer-effect': string;
|
|
421
|
+
'visual-polish-border-beveled-edges': string;
|
|
422
|
+
'visual-polish-border-pulsing-glow': string;
|
|
423
|
+
'visual-polish-content-aware-blur-enabled': string;
|
|
424
|
+
'visual-polish-content-aware-depth-detection': string;
|
|
425
|
+
'visual-polish-content-aware-edge-preservation': string;
|
|
426
|
+
'visual-polish-content-aware-variable-radius': string;
|
|
427
|
+
'visual-polish-holographic-enabled': string;
|
|
428
|
+
'visual-polish-holographic-rainbow-diffraction': string;
|
|
429
|
+
'visual-polish-holographic-scanline-animation': string;
|
|
430
|
+
'visual-polish-holographic-grid-overlay': string;
|
|
431
|
+
'visual-polish-holographic-data-stream': string;
|
|
432
|
+
'visual-polish-holographic-pulse-rings': string;
|
|
433
|
+
|
|
381
434
|
// ============================================================================
|
|
382
435
|
// Custom tokens (allow any additional tokens)
|
|
383
436
|
// ============================================================================
|
|
@@ -701,6 +754,53 @@ export const defaultTokens: DesignTokens = {
|
|
|
701
754
|
'breakpoint-lg': '992px',
|
|
702
755
|
'breakpoint-xl': '1200px',
|
|
703
756
|
'breakpoint-xxl': '1440px',
|
|
757
|
+
|
|
758
|
+
// Advanced Features - Interactive Effects (Phase 2)
|
|
759
|
+
'interactive-vortex-enabled': 'false',
|
|
760
|
+
'interactive-vortex-strength': '0.5',
|
|
761
|
+
'interactive-vortex-radius': '100',
|
|
762
|
+
'interactive-vortex-decay': '0.8',
|
|
763
|
+
'interactive-chromatic-enabled': 'false',
|
|
764
|
+
'interactive-chromatic-mode': 'lateral',
|
|
765
|
+
'interactive-chromatic-red-shift': '0.02',
|
|
766
|
+
'interactive-chromatic-green-shift': '0',
|
|
767
|
+
'interactive-chromatic-blue-shift': '-0.02',
|
|
768
|
+
'interactive-chromatic-edge-only': 'false',
|
|
769
|
+
'interactive-chromatic-edge-threshold': '0.5',
|
|
770
|
+
'interactive-mouse-sensitivity': '1.0',
|
|
771
|
+
'interactive-mouse-trail-effect': 'false',
|
|
772
|
+
'interactive-animation-speed-base': '1.0',
|
|
773
|
+
'interactive-animation-speed-multiplier': '1.0',
|
|
774
|
+
|
|
775
|
+
// Advanced Features - Optimization (Phase 3)
|
|
776
|
+
'optimization-breakpoint-mobile': '0px',
|
|
777
|
+
'optimization-breakpoint-tablet': '768px',
|
|
778
|
+
'optimization-breakpoint-desktop': '1024px',
|
|
779
|
+
'optimization-breakpoint-wide': '1440px',
|
|
780
|
+
'optimization-device-scaling-mobile': '0.5',
|
|
781
|
+
'optimization-device-scaling-tablet': '0.75',
|
|
782
|
+
'optimization-device-scaling-desktop': '1.0',
|
|
783
|
+
'optimization-performance-fps-target': '60',
|
|
784
|
+
'optimization-auto-scaling-enabled': 'false',
|
|
785
|
+
'optimization-auto-scaling-low-end': '0.5',
|
|
786
|
+
'optimization-auto-scaling-mid-range': '0.75',
|
|
787
|
+
'optimization-auto-scaling-high-end': '1.0',
|
|
788
|
+
|
|
789
|
+
// Advanced Features - Visual Polish (Phase 4)
|
|
790
|
+
'visual-polish-border-iridescent-glow': 'false',
|
|
791
|
+
'visual-polish-border-shimmer-effect': 'false',
|
|
792
|
+
'visual-polish-border-beveled-edges': 'false',
|
|
793
|
+
'visual-polish-border-pulsing-glow': 'false',
|
|
794
|
+
'visual-polish-content-aware-blur-enabled': 'false',
|
|
795
|
+
'visual-polish-content-aware-depth-detection': 'false',
|
|
796
|
+
'visual-polish-content-aware-edge-preservation': 'false',
|
|
797
|
+
'visual-polish-content-aware-variable-radius': 'false',
|
|
798
|
+
'visual-polish-holographic-enabled': 'false',
|
|
799
|
+
'visual-polish-holographic-rainbow-diffraction': 'false',
|
|
800
|
+
'visual-polish-holographic-scanline-animation': 'false',
|
|
801
|
+
'visual-polish-holographic-grid-overlay': 'false',
|
|
802
|
+
'visual-polish-holographic-data-stream': 'false',
|
|
803
|
+
'visual-polish-holographic-pulse-rings': 'false',
|
|
704
804
|
};
|
|
705
805
|
|
|
706
806
|
/**
|
|
@@ -719,4 +819,4 @@ export const defaultTokens: DesignTokens = {
|
|
|
719
819
|
*/
|
|
720
820
|
export function createTokens(overrides?: Partial<DesignTokens>): DesignTokens {
|
|
721
821
|
return { ...defaultTokens, ...overrides };
|
|
722
|
-
}
|
|
822
|
+
}
|
package/src/lib/theme/types.ts
CHANGED
|
@@ -141,6 +141,8 @@ export interface UseThemeReturn {
|
|
|
141
141
|
theme: string | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>,
|
|
142
142
|
options?: ThemeLoadOptions
|
|
143
143
|
) => Promise<void>;
|
|
144
|
+
/** Update a specific theme section */
|
|
145
|
+
updateTheme: (section: ThemeSection, values: any) => Promise<void>;
|
|
144
146
|
/** Available themes */
|
|
145
147
|
availableThemes: ThemeMetadata[];
|
|
146
148
|
/** Whether a theme is currently loading */
|
|
@@ -250,6 +252,16 @@ export interface ThemeProviderProps {
|
|
|
250
252
|
onError?: (error: Error, themeName: string) => void;
|
|
251
253
|
}
|
|
252
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Theme configuration (partial tokens)
|
|
257
|
+
*/
|
|
258
|
+
export type ThemeConfig = Partial<import('./tokens').DesignTokens>;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Theme section name
|
|
262
|
+
*/
|
|
263
|
+
export type ThemeSection = keyof import('./tokens').DesignTokens;
|
|
264
|
+
|
|
253
265
|
/**
|
|
254
266
|
* Theme context value
|
|
255
267
|
*/
|
|
@@ -263,6 +275,8 @@ export interface ThemeContextValue {
|
|
|
263
275
|
theme: string | import('./tokens').DesignTokens | Partial<import('./tokens').DesignTokens>,
|
|
264
276
|
options?: ThemeLoadOptions
|
|
265
277
|
) => Promise<void>;
|
|
278
|
+
/** Update a specific theme section */
|
|
279
|
+
updateTheme: (section: ThemeSection, values: any) => Promise<void>;
|
|
266
280
|
/** Available themes */
|
|
267
281
|
availableThemes: ThemeMetadata[];
|
|
268
282
|
/** Loading state */
|
|
@@ -501,6 +515,83 @@ export interface BorderRadiusOptions {
|
|
|
501
515
|
* Users can augment this interface via module augmentation
|
|
502
516
|
*/
|
|
503
517
|
export interface ThemeCustomProperties {
|
|
518
|
+
// Advanced Features Configuration
|
|
519
|
+
interactiveEffects?: {
|
|
520
|
+
vortex?: {
|
|
521
|
+
enabled?: boolean;
|
|
522
|
+
strength?: number;
|
|
523
|
+
radius?: number;
|
|
524
|
+
decay?: number;
|
|
525
|
+
};
|
|
526
|
+
chromaticAberration?: {
|
|
527
|
+
enabled?: boolean;
|
|
528
|
+
mode?: 'longitudinal' | 'lateral' | 'hybrid';
|
|
529
|
+
redShift?: number;
|
|
530
|
+
greenShift?: number;
|
|
531
|
+
blueShift?: number;
|
|
532
|
+
edgeOnly?: boolean;
|
|
533
|
+
edgeThreshold?: number;
|
|
534
|
+
};
|
|
535
|
+
mouseInteraction?: {
|
|
536
|
+
sensitivity?: number;
|
|
537
|
+
trailEffect?: boolean;
|
|
538
|
+
};
|
|
539
|
+
animationSpeed?: {
|
|
540
|
+
base?: number;
|
|
541
|
+
timeMultiplier?: number;
|
|
542
|
+
};
|
|
543
|
+
};
|
|
544
|
+
optimization?: {
|
|
545
|
+
responsive?: {
|
|
546
|
+
breakpoints?: {
|
|
547
|
+
mobile?: string;
|
|
548
|
+
tablet?: string;
|
|
549
|
+
desktop?: string;
|
|
550
|
+
wide?: string;
|
|
551
|
+
};
|
|
552
|
+
deviceScaling?: {
|
|
553
|
+
mobile?: number;
|
|
554
|
+
tablet?: number;
|
|
555
|
+
desktop?: number;
|
|
556
|
+
};
|
|
557
|
+
};
|
|
558
|
+
performance?: {
|
|
559
|
+
fpsTarget?: number;
|
|
560
|
+
autoScaling?: boolean;
|
|
561
|
+
};
|
|
562
|
+
autoScaling?: {
|
|
563
|
+
enabled?: boolean;
|
|
564
|
+
qualityThresholds?: {
|
|
565
|
+
lowEnd?: number;
|
|
566
|
+
midRange?: number;
|
|
567
|
+
highEnd?: number;
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
visualPolish?: {
|
|
573
|
+
borders?: {
|
|
574
|
+
iridescentGlow?: boolean;
|
|
575
|
+
shimmerEffect?: boolean;
|
|
576
|
+
beveledEdges?: boolean;
|
|
577
|
+
pulsingGlow?: boolean;
|
|
578
|
+
};
|
|
579
|
+
contentAwareBlur?: {
|
|
580
|
+
enabled?: boolean;
|
|
581
|
+
depthDetection?: boolean;
|
|
582
|
+
edgePreservation?: boolean;
|
|
583
|
+
variableRadius?: boolean;
|
|
584
|
+
};
|
|
585
|
+
holographicEffects?: {
|
|
586
|
+
enabled?: boolean;
|
|
587
|
+
rainbowDiffraction?: boolean;
|
|
588
|
+
scanlineAnimation?: boolean;
|
|
589
|
+
gridOverlay?: boolean;
|
|
590
|
+
dataStream?: boolean;
|
|
591
|
+
pulseRings?: boolean;
|
|
592
|
+
};
|
|
593
|
+
};
|
|
594
|
+
|
|
504
595
|
[key: string]: any;
|
|
505
596
|
}
|
|
506
597
|
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Monitor Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides performance monitoring and metrics collection for the Atomix theme system.
|
|
5
|
+
* Used to measure and optimize performance of advanced visual effects.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Performance metrics collected by the monitor
|
|
12
|
+
*/
|
|
13
|
+
export interface PerformanceMetrics {
|
|
14
|
+
/** Frames per second */
|
|
15
|
+
fps: number;
|
|
16
|
+
/** Average frame render time in ms */
|
|
17
|
+
frameTime: number;
|
|
18
|
+
/** Peak frame render time in ms */
|
|
19
|
+
peakFrameTime: number;
|
|
20
|
+
/** Memory usage statistics */
|
|
21
|
+
memory?: {
|
|
22
|
+
usedJSHeapSize: number;
|
|
23
|
+
totalJSHeapSize: number;
|
|
24
|
+
jsHeapSizeLimit: number;
|
|
25
|
+
};
|
|
26
|
+
/** Timestamp of measurement */
|
|
27
|
+
timestamp: number;
|
|
28
|
+
/** Whether performance is degrading */
|
|
29
|
+
isDegraded: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Performance monitor configuration
|
|
34
|
+
*/
|
|
35
|
+
export interface PerformanceMonitorConfig {
|
|
36
|
+
/** Target FPS threshold (default: 60) */
|
|
37
|
+
fpsTarget?: number;
|
|
38
|
+
/** How frequently to sample (default: 500ms) */
|
|
39
|
+
sampleInterval?: number;
|
|
40
|
+
/** Callback for performance updates */
|
|
41
|
+
onUpdate?: (metrics: PerformanceMetrics) => void;
|
|
42
|
+
/** Callback when performance degrades */
|
|
43
|
+
onDegraded?: (metrics: PerformanceMetrics) => void;
|
|
44
|
+
/** Enable/disable memory monitoring */
|
|
45
|
+
enableMemoryMonitoring?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Performance monitor class
|
|
50
|
+
*/
|
|
51
|
+
export class PerformanceMonitor {
|
|
52
|
+
public config: Required<PerformanceMonitorConfig>;
|
|
53
|
+
private frameCount: number = 0;
|
|
54
|
+
private lastSampleTime: number = 0;
|
|
55
|
+
private lastFpsUpdate: number = 0;
|
|
56
|
+
private frameTimes: number[] = [];
|
|
57
|
+
private animationFrameId: number | null = null;
|
|
58
|
+
private isActive: boolean = false;
|
|
59
|
+
private startTime: number = 0;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a new performance monitor
|
|
63
|
+
*
|
|
64
|
+
* @param config Configuration options
|
|
65
|
+
*/
|
|
66
|
+
constructor(config?: PerformanceMonitorConfig) {
|
|
67
|
+
this.config = {
|
|
68
|
+
fpsTarget: config?.fpsTarget ?? 60,
|
|
69
|
+
sampleInterval: config?.sampleInterval ?? 500,
|
|
70
|
+
onUpdate: config?.onUpdate ?? (() => {}),
|
|
71
|
+
onDegraded: config?.onDegraded ?? (() => {}),
|
|
72
|
+
enableMemoryMonitoring: config?.enableMemoryMonitoring ?? (typeof window !== 'undefined' &&
|
|
73
|
+
(window as any).performance &&
|
|
74
|
+
(window as any).performance.memory),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Start monitoring performance
|
|
80
|
+
*/
|
|
81
|
+
public start(): void {
|
|
82
|
+
if (this.isActive) return;
|
|
83
|
+
|
|
84
|
+
this.isActive = true;
|
|
85
|
+
this.frameCount = 0;
|
|
86
|
+
this.lastSampleTime = performance.now();
|
|
87
|
+
this.lastFpsUpdate = this.lastSampleTime;
|
|
88
|
+
this.frameTimes = [];
|
|
89
|
+
this.startTime = this.lastSampleTime;
|
|
90
|
+
|
|
91
|
+
this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Stop monitoring performance
|
|
96
|
+
*/
|
|
97
|
+
public stop(): void {
|
|
98
|
+
if (this.animationFrameId) {
|
|
99
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
100
|
+
this.animationFrameId = null;
|
|
101
|
+
}
|
|
102
|
+
this.isActive = false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get current performance metrics
|
|
107
|
+
*/
|
|
108
|
+
public getMetrics(): PerformanceMetrics {
|
|
109
|
+
const now = performance.now();
|
|
110
|
+
const elapsed = now - this.lastFpsUpdate;
|
|
111
|
+
const fps = elapsed > 0 ? Math.round((this.frameCount / elapsed) * 1000) : 0;
|
|
112
|
+
|
|
113
|
+
// Calculate average frame time
|
|
114
|
+
const avgFrameTime = this.frameTimes.length > 0
|
|
115
|
+
? this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length
|
|
116
|
+
: 0;
|
|
117
|
+
|
|
118
|
+
// Peak frame time
|
|
119
|
+
const peakFrameTime = this.frameTimes.length > 0
|
|
120
|
+
? Math.max(...this.frameTimes)
|
|
121
|
+
: 0;
|
|
122
|
+
|
|
123
|
+
// Get memory stats if available
|
|
124
|
+
let memory = undefined;
|
|
125
|
+
if (this.config.enableMemoryMonitoring) {
|
|
126
|
+
const perf = window.performance as any;
|
|
127
|
+
if (perf && perf.memory) {
|
|
128
|
+
memory = {
|
|
129
|
+
usedJSHeapSize: perf.memory.usedJSHeapSize,
|
|
130
|
+
totalJSHeapSize: perf.memory.totalJSHeapSize,
|
|
131
|
+
jsHeapSizeLimit: perf.memory.jsHeapSizeLimit,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
fps,
|
|
138
|
+
frameTime: avgFrameTime,
|
|
139
|
+
peakFrameTime,
|
|
140
|
+
memory,
|
|
141
|
+
timestamp: now,
|
|
142
|
+
isDegraded: fps < (this.config.fpsTarget * 0.7), // Degraded if under 70% of target
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the current FPS
|
|
148
|
+
*/
|
|
149
|
+
public getFps(): number {
|
|
150
|
+
return this.getMetrics().fps;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Check if performance is degraded
|
|
155
|
+
*/
|
|
156
|
+
public isPerformanceDegraded(): boolean {
|
|
157
|
+
return this.getMetrics().isDegraded;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Private method called on each animation frame
|
|
162
|
+
*/
|
|
163
|
+
private onFrame(timestamp: number): void {
|
|
164
|
+
if (!this.isActive) return;
|
|
165
|
+
|
|
166
|
+
// Calculate frame time
|
|
167
|
+
const frameTime = timestamp - this.lastSampleTime;
|
|
168
|
+
this.frameTimes.push(frameTime);
|
|
169
|
+
|
|
170
|
+
// Keep only the last 60 frame times for averaging
|
|
171
|
+
if (this.frameTimes.length > 60) {
|
|
172
|
+
this.frameTimes.shift();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.frameCount++;
|
|
176
|
+
this.lastSampleTime = timestamp;
|
|
177
|
+
|
|
178
|
+
// Check if we need to update metrics
|
|
179
|
+
if (timestamp - this.lastFpsUpdate >= this.config.sampleInterval) {
|
|
180
|
+
const metrics = this.getMetrics();
|
|
181
|
+
|
|
182
|
+
// Call update callback
|
|
183
|
+
this.config.onUpdate(metrics);
|
|
184
|
+
|
|
185
|
+
// Check for degradation
|
|
186
|
+
if (metrics.isDegraded) {
|
|
187
|
+
this.config.onDegraded(metrics);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Reset counters
|
|
191
|
+
this.frameCount = 0;
|
|
192
|
+
this.lastFpsUpdate = timestamp;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.animationFrameId = requestAnimationFrame(this.onFrame.bind(this));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Run a performance test for a specific function
|
|
200
|
+
*
|
|
201
|
+
* @param fn Function to test
|
|
202
|
+
* @param iterations Number of iterations (default: 100)
|
|
203
|
+
* @returns Average execution time in ms
|
|
204
|
+
*/
|
|
205
|
+
public async testFunctionPerformance(
|
|
206
|
+
fn: () => void,
|
|
207
|
+
iterations: number = 100
|
|
208
|
+
): Promise<number> {
|
|
209
|
+
const times: number[] = [];
|
|
210
|
+
|
|
211
|
+
for (let i = 0; i < iterations; i++) {
|
|
212
|
+
const start = performance.now();
|
|
213
|
+
fn();
|
|
214
|
+
const end = performance.now();
|
|
215
|
+
times.push(end - start);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return times.reduce((a, b) => a + b, 0) / times.length;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Create a performance monitor instance
|
|
224
|
+
*
|
|
225
|
+
* @param config Configuration options
|
|
226
|
+
* @returns PerformanceMonitor instance
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```typescript
|
|
230
|
+
* import { createPerformanceMonitor } from '@shohojdhara/atomix/theme';
|
|
231
|
+
*
|
|
232
|
+
* const monitor = createPerformanceMonitor({
|
|
233
|
+
* fpsTarget: 60,
|
|
234
|
+
* onUpdate: (metrics) => console.log('FPS:', metrics.fps),
|
|
235
|
+
* onDegraded: (metrics) => console.warn('Performance degraded!', metrics),
|
|
236
|
+
* });
|
|
237
|
+
*
|
|
238
|
+
* monitor.start();
|
|
239
|
+
*
|
|
240
|
+
* // Later...
|
|
241
|
+
* monitor.stop();
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
export function createPerformanceMonitor(config?: PerformanceMonitorConfig): PerformanceMonitor {
|
|
245
|
+
return new PerformanceMonitor(config);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Hook for React components to monitor performance
|
|
250
|
+
*
|
|
251
|
+
* @param config Configuration options
|
|
252
|
+
* @returns Performance metrics and monitor controls
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* import { usePerformanceMonitor } from '@shohojdhara/atomix/theme';
|
|
257
|
+
*
|
|
258
|
+
* function MyComponent() {
|
|
259
|
+
* const { metrics, start, stop } = usePerformanceMonitor({ fpsTarget: 60 });
|
|
260
|
+
*
|
|
261
|
+
* useEffect(() => {
|
|
262
|
+
* start();
|
|
263
|
+
* return () => stop();
|
|
264
|
+
* }, []);
|
|
265
|
+
*
|
|
266
|
+
* return <div>FPS: {metrics.fps}</div>;
|
|
267
|
+
* }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
export function usePerformanceMonitor(config?: PerformanceMonitorConfig) {
|
|
271
|
+
const [monitor] = React.useState(() => createPerformanceMonitor(config));
|
|
272
|
+
const [metrics, setMetrics] = React.useState<PerformanceMetrics>(() =>
|
|
273
|
+
typeof window !== 'undefined' ? monitor.getMetrics() : {
|
|
274
|
+
fps: 0,
|
|
275
|
+
frameTime: 0,
|
|
276
|
+
peakFrameTime: 0,
|
|
277
|
+
timestamp: 0,
|
|
278
|
+
isDegraded: false,
|
|
279
|
+
} as PerformanceMetrics
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const start = React.useCallback(() => {
|
|
283
|
+
if (typeof window !== 'undefined') {
|
|
284
|
+
monitor.start();
|
|
285
|
+
}
|
|
286
|
+
}, [monitor]);
|
|
287
|
+
|
|
288
|
+
const stop = React.useCallback(() => {
|
|
289
|
+
if (typeof window !== 'undefined') {
|
|
290
|
+
monitor.stop();
|
|
291
|
+
}
|
|
292
|
+
}, [monitor]);
|
|
293
|
+
|
|
294
|
+
React.useEffect(() => {
|
|
295
|
+
if (typeof window === 'undefined') return;
|
|
296
|
+
|
|
297
|
+
// Update metrics when monitor callbacks fire
|
|
298
|
+
const originalOnUpdate = config?.onUpdate;
|
|
299
|
+
monitor.config.onUpdate = (newMetrics: PerformanceMetrics) => {
|
|
300
|
+
setMetrics(newMetrics);
|
|
301
|
+
originalOnUpdate?.(newMetrics);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
return () => {
|
|
305
|
+
monitor.stop();
|
|
306
|
+
};
|
|
307
|
+
}, [monitor, config?.onUpdate]);
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
metrics,
|
|
311
|
+
start,
|
|
312
|
+
stop,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|