@shohojdhara/atomix 0.4.8 → 0.5.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/atomix.config.ts +58 -1
- package/dist/atomix.css +148 -120
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +33 -0
- package/dist/charts.js +1227 -122
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +33 -10
- package/dist/core.js +1052 -41
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +33 -0
- package/dist/forms.js +2086 -1035
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +42 -1
- package/dist/heavy.js +1620 -600
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +441 -270
- package/dist/index.esm.js +1900 -638
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1935 -670
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +6 -3
- package/scripts/atomix-cli.js +148 -4
- package/scripts/cli/__tests__/basic.test.js +3 -2
- package/scripts/cli/__tests__/clean.test.js +278 -0
- package/scripts/cli/__tests__/component-validator.test.js +433 -0
- package/scripts/cli/__tests__/generator.test.js +613 -0
- package/scripts/cli/__tests__/glass-motion.test.js +256 -0
- package/scripts/cli/__tests__/integration.test.js +719 -108
- package/scripts/cli/__tests__/migrate.test.js +74 -0
- package/scripts/cli/__tests__/security.test.js +206 -0
- package/scripts/cli/__tests__/test-setup.js +3 -1
- package/scripts/cli/__tests__/theme-bridge.test.js +507 -0
- package/scripts/cli/__tests__/token-provider.test.js +361 -0
- package/scripts/cli/__tests__/utils.test.js +5 -5
- package/scripts/cli/commands/benchmark.js +105 -0
- package/scripts/cli/commands/build-theme.js +4 -1
- package/scripts/cli/commands/clean.js +109 -0
- package/scripts/cli/commands/doctor.js +88 -0
- package/scripts/cli/commands/generate.js +135 -14
- package/scripts/cli/commands/init.js +45 -18
- package/scripts/cli/commands/migrate.js +106 -0
- package/scripts/cli/commands/sync-tokens.js +206 -0
- package/scripts/cli/commands/theme-bridge.js +248 -0
- package/scripts/cli/commands/tokens.js +157 -0
- package/scripts/cli/commands/validate.js +194 -0
- package/scripts/cli/internal/ai-engine.js +156 -0
- package/scripts/cli/internal/component-validator.js +443 -0
- package/scripts/cli/internal/config-loader.js +162 -0
- package/scripts/cli/internal/filesystem.js +102 -2
- package/scripts/cli/internal/generator.js +359 -39
- package/scripts/cli/internal/glass-generator.js +398 -0
- package/scripts/cli/internal/hook-generator.js +369 -0
- package/scripts/cli/internal/hooks.js +61 -0
- package/scripts/cli/internal/itcss-generator.js +565 -0
- package/scripts/cli/internal/motion-generator.js +679 -0
- package/scripts/cli/internal/template-engine.js +301 -0
- package/scripts/cli/internal/theme-bridge.js +664 -0
- package/scripts/cli/internal/tokens/engine.js +122 -0
- package/scripts/cli/internal/tokens/provider.js +34 -0
- package/scripts/cli/internal/tokens/providers/figma.js +50 -0
- package/scripts/cli/internal/tokens/providers/style-dictionary.js +48 -0
- package/scripts/cli/internal/tokens/providers/w3c.js +48 -0
- package/scripts/cli/internal/tokens/token-provider.js +443 -0
- package/scripts/cli/internal/tokens/token-validator.js +513 -0
- package/scripts/cli/internal/validator.js +276 -0
- package/scripts/cli/internal/wizard.js +60 -6
- package/scripts/cli/mappings.js +23 -0
- package/scripts/cli/migration-tools.js +164 -94
- package/scripts/cli/plugins/style-dictionary.js +46 -0
- package/scripts/cli/templates/README.md +525 -95
- package/scripts/cli/templates/common-templates.js +40 -14
- package/scripts/cli/templates/components/react-component.ts +282 -0
- package/scripts/cli/templates/config/project-config.ts +112 -0
- package/scripts/cli/templates/hooks/use-component.ts +477 -0
- package/scripts/cli/templates/index.js +19 -4
- package/scripts/cli/templates/index.ts +171 -0
- package/scripts/cli/templates/next-templates.js +72 -0
- package/scripts/cli/templates/react-templates.js +70 -126
- package/scripts/cli/templates/scss-templates.js +35 -35
- package/scripts/cli/templates/stories/storybook-story.ts +241 -0
- package/scripts/cli/templates/styles/scss-component.ts +255 -0
- package/scripts/cli/templates/tests/vitest-test.ts +229 -0
- package/scripts/cli/templates/token-templates.js +337 -1
- package/scripts/cli/templates/tokens/token-generators.ts +1088 -0
- package/scripts/cli/templates/types/component-types.ts +145 -0
- package/scripts/cli/templates/utils/testing-utils.ts +144 -0
- package/scripts/cli/templates/vanilla-templates.js +39 -0
- package/scripts/cli/token-manager.js +8 -2
- package/scripts/cli/utils/cache-manager.js +240 -0
- package/scripts/cli/utils/detector.js +46 -0
- package/scripts/cli/utils/diagnostics.js +289 -0
- package/scripts/cli/utils/error.js +45 -3
- package/scripts/cli/utils/helpers.js +24 -0
- package/scripts/cli/utils/logger.js +1 -1
- package/scripts/cli/utils/security.js +302 -0
- package/scripts/cli/utils/telemetry.js +115 -0
- package/scripts/cli/utils/validation.js +4 -38
- package/scripts/cli/utils.js +46 -0
- package/src/components/Accordion/Accordion.stories.tsx +0 -18
- package/src/components/Accordion/Accordion.test.tsx +0 -17
- package/src/components/Accordion/Accordion.tsx +0 -4
- package/src/components/AtomixGlass/AtomixGlass.tsx +102 -2
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +125 -12
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +219 -0
- package/src/components/AtomixGlass/README.md +25 -10
- package/src/components/AtomixGlass/animation-system.ts +578 -0
- package/src/components/AtomixGlass/shader-utils.ts +3 -0
- package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +653 -0
- package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +95 -0
- package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +212 -0
- package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +348 -0
- package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +410 -0
- package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +436 -0
- package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +264 -0
- package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +247 -0
- package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +418 -0
- package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +402 -0
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +157 -6
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +658 -93
- package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +335 -0
- package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +441 -0
- package/src/components/AtomixGlass/stories/argTypes.ts +384 -0
- package/src/components/AtomixGlass/stories/shared-components.tsx +91 -1
- package/src/components/AtomixGlass/stories/types.ts +127 -0
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.stories.disabled-link.tsx +10 -0
- package/src/components/Button/Button.stories.tsx +10 -0
- package/src/components/Button/Button.test.tsx +16 -11
- package/src/components/Button/Button.tsx +4 -4
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +12 -12
- package/src/components/Form/Select.tsx +62 -3
- package/src/components/Modal/Modal.tsx +14 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +44 -0
- package/src/components/Slider/Slider.stories.tsx +3 -3
- package/src/components/Slider/Slider.tsx +38 -0
- package/src/components/Steps/Steps.tsx +3 -3
- package/src/components/Tabs/Tabs.tsx +77 -8
- package/src/components/Testimonial/Testimonial.tsx +1 -1
- package/src/components/TypedButton/TypedButton.stories.tsx +59 -0
- package/src/components/TypedButton/TypedButton.tsx +39 -0
- package/src/components/TypedButton/index.ts +2 -0
- package/src/components/VideoPlayer/VideoPlayer.tsx +11 -4
- package/src/lib/composables/index.ts +4 -7
- package/src/lib/composables/types.ts +45 -0
- package/src/lib/composables/useAccordion.ts +0 -7
- package/src/lib/composables/useAtomixGlass.ts +144 -5
- package/src/lib/composables/useChartExport.ts +3 -13
- package/src/lib/composables/useDropdown.ts +66 -0
- package/src/lib/composables/useFocusTrap.ts +80 -0
- package/src/lib/composables/usePerformanceMonitor.ts +448 -0
- package/src/lib/composables/useResponsiveGlass.presets.ts +192 -0
- package/src/lib/composables/useResponsiveGlass.ts +441 -0
- package/src/lib/composables/useTooltip.ts +16 -0
- package/src/lib/composables/useTypedButton.ts +66 -0
- package/src/lib/config/index.ts +62 -5
- package/src/lib/constants/components.ts +55 -0
- package/src/lib/theme/devtools/__tests__/useHistory.test.tsx +150 -0
- package/src/lib/theme/tokens/centralized-tokens.ts +120 -0
- package/src/lib/theme/utils/__tests__/domUtils.test.ts +101 -0
- package/src/lib/types/components.ts +37 -11
- package/src/lib/types/glass.ts +35 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/utils/displacement-generator.ts +1 -1
- package/src/styles/01-settings/_settings.testtypecheck.scss +53 -0
- package/src/styles/01-settings/_settings.typedbutton.scss +53 -0
- package/src/styles/06-components/_components.testbutton.scss +212 -0
- package/src/styles/06-components/_components.testtypecheck.scss +212 -0
- package/src/styles/06-components/_components.typedbutton.scss +212 -0
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.text.scss +1 -1
- package/src/styles/99-utilities/_utilities.touch-target.scss +36 -0
- package/src/styles/06-components/old.chart.styles.scss +0 -2788
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Performance metrics collected by the monitor
|
|
5
|
+
*/
|
|
6
|
+
export interface PerformanceMetrics {
|
|
7
|
+
/** Frames per second (target: 60) */
|
|
8
|
+
fps: number;
|
|
9
|
+
|
|
10
|
+
/** Time to render last frame in milliseconds (target: <16ms) */
|
|
11
|
+
frameTime: number;
|
|
12
|
+
|
|
13
|
+
/** GPU memory usage in MB (if available, target: <50MB) */
|
|
14
|
+
gpuMemory: number | null;
|
|
15
|
+
|
|
16
|
+
/** Current quality level based on performance */
|
|
17
|
+
qualityLevel: 'low' | 'medium' | 'high';
|
|
18
|
+
|
|
19
|
+
/** Timestamp of last measurement */
|
|
20
|
+
timestamp: number;
|
|
21
|
+
|
|
22
|
+
/** Whether auto-scaling is active */
|
|
23
|
+
isAutoScaling: boolean;
|
|
24
|
+
|
|
25
|
+
/** Number of consecutive low-FPS frames */
|
|
26
|
+
lowFpsCount: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configuration for performance monitor
|
|
31
|
+
*/
|
|
32
|
+
export interface PerformanceMonitorConfig {
|
|
33
|
+
/** Enable/disable monitoring */
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
|
|
36
|
+
/** Target FPS for auto-scaling (default: 60) */
|
|
37
|
+
targetFps?: number;
|
|
38
|
+
|
|
39
|
+
/** Minimum acceptable FPS before scaling down (default: 45) */
|
|
40
|
+
minFps?: number;
|
|
41
|
+
|
|
42
|
+
/** FPS threshold to scale up (default: 58) */
|
|
43
|
+
scaleUpThreshold?: number;
|
|
44
|
+
|
|
45
|
+
/** Consecutive low-FPS frames before scaling down (default: 3) */
|
|
46
|
+
lowFpsFrames?: number;
|
|
47
|
+
|
|
48
|
+
/** Consecutive high-FPS frames before scaling up (default: 10) */
|
|
49
|
+
highFpsFrames?: number;
|
|
50
|
+
|
|
51
|
+
/** Enable debug logging */
|
|
52
|
+
debug?: boolean;
|
|
53
|
+
|
|
54
|
+
/** Show debug overlay on screen */
|
|
55
|
+
showOverlay?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Return value from performance monitor hook
|
|
60
|
+
*/
|
|
61
|
+
export interface UsePerformanceMonitorReturn {
|
|
62
|
+
/** Current performance metrics */
|
|
63
|
+
metrics: PerformanceMetrics;
|
|
64
|
+
|
|
65
|
+
/** Recommended quality level based on performance */
|
|
66
|
+
recommendedQuality: 'low' | 'medium' | 'high';
|
|
67
|
+
|
|
68
|
+
/** Whether performance is below target */
|
|
69
|
+
isUnderperforming: boolean;
|
|
70
|
+
|
|
71
|
+
/** Manually set quality level */
|
|
72
|
+
setQualityLevel: (level: 'low' | 'medium' | 'high') => void;
|
|
73
|
+
|
|
74
|
+
/** Reset auto-scaling state */
|
|
75
|
+
resetAutoScaling: () => void;
|
|
76
|
+
|
|
77
|
+
/** Toggle monitoring on/off */
|
|
78
|
+
toggleMonitoring: () => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get GPU memory info if available (Chrome DevTools only)
|
|
83
|
+
*/
|
|
84
|
+
const getGpuMemoryInfo = (): Promise<number | null> => {
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
// Check for WebGL debug renderer info
|
|
87
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
88
|
+
try {
|
|
89
|
+
const canvas = document.createElement('canvas');
|
|
90
|
+
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
|
91
|
+
|
|
92
|
+
if (gl) {
|
|
93
|
+
const debugInfo = (gl as any).getExtension('WEBGL_debug_renderer_info');
|
|
94
|
+
if (debugInfo) {
|
|
95
|
+
// Note: Actual memory info is not directly available via WebGL
|
|
96
|
+
// We estimate based on renderer
|
|
97
|
+
const glContext = gl as WebGLRenderingContext;
|
|
98
|
+
const renderer = glContext.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
|
99
|
+
|
|
100
|
+
// Rough estimation based on renderer type
|
|
101
|
+
if (renderer?.includes('Integrated')) {
|
|
102
|
+
resolve(256); // Integrated graphics typically share system RAM
|
|
103
|
+
} else if (renderer?.includes('AMD') || renderer?.includes('NVIDIA')) {
|
|
104
|
+
resolve(512); // Dedicated GPU
|
|
105
|
+
} else {
|
|
106
|
+
resolve(null); // Unknown
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
// WebGL not available or error occurred
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
resolve(null);
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Calculate optimal quality level based on FPS
|
|
121
|
+
*/
|
|
122
|
+
const calculateQualityLevel = (fps: number, currentQuality: 'low' | 'medium' | 'high'): 'low' | 'medium' | 'high' => {
|
|
123
|
+
if (fps >= 58) {
|
|
124
|
+
return 'high';
|
|
125
|
+
} else if (fps >= 45) {
|
|
126
|
+
return currentQuality === 'high' ? 'high' : 'medium';
|
|
127
|
+
} else {
|
|
128
|
+
return 'low';
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Performance Monitor Hook
|
|
134
|
+
*
|
|
135
|
+
* Real-time performance tracking with automatic quality scaling.
|
|
136
|
+
* Monitors FPS, frame time, and GPU memory to optimize glass effects.
|
|
137
|
+
*
|
|
138
|
+
* Features:
|
|
139
|
+
* - Real-time FPS measurement
|
|
140
|
+
* - Frame timing analysis
|
|
141
|
+
* - Automatic quality scaling
|
|
142
|
+
* - Debug overlay option
|
|
143
|
+
* - Manual override capability
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const { metrics, recommendedQuality, setQualityLevel } = usePerformanceMonitor({
|
|
148
|
+
* targetFps: 60,
|
|
149
|
+
* minFps: 45,
|
|
150
|
+
* debug: true,
|
|
151
|
+
* });
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @param config Monitor configuration
|
|
155
|
+
* @returns Performance metrics and controls
|
|
156
|
+
*/
|
|
157
|
+
export function usePerformanceMonitor(
|
|
158
|
+
config: PerformanceMonitorConfig = {}
|
|
159
|
+
): UsePerformanceMonitorReturn {
|
|
160
|
+
const {
|
|
161
|
+
enabled = true,
|
|
162
|
+
targetFps = 60,
|
|
163
|
+
minFps = 45,
|
|
164
|
+
scaleUpThreshold = 58,
|
|
165
|
+
lowFpsFrames = 3,
|
|
166
|
+
highFpsFrames = 10,
|
|
167
|
+
debug = false,
|
|
168
|
+
showOverlay = false,
|
|
169
|
+
} = config;
|
|
170
|
+
|
|
171
|
+
const [metrics, setMetrics] = useState<PerformanceMetrics>({
|
|
172
|
+
fps: 0,
|
|
173
|
+
frameTime: 0,
|
|
174
|
+
gpuMemory: null,
|
|
175
|
+
qualityLevel: 'medium',
|
|
176
|
+
timestamp: 0,
|
|
177
|
+
isAutoScaling: true,
|
|
178
|
+
lowFpsCount: 0,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const [manualOverride, setManualOverride] = useState(false);
|
|
182
|
+
const [isEnabled, setIsEnabled] = useState(enabled);
|
|
183
|
+
|
|
184
|
+
// Refs for frame tracking
|
|
185
|
+
const frameCountRef = useRef(0);
|
|
186
|
+
const lastFpsUpdateRef = useRef(0);
|
|
187
|
+
const lastFrameTimeRef = useRef(0);
|
|
188
|
+
const animationFrameRef = useRef<number | null>(null);
|
|
189
|
+
const lowFpsCountRef = useRef(0);
|
|
190
|
+
const highFpsCountRef = useRef(0);
|
|
191
|
+
const qualityLevelRef = useRef<'low' | 'medium' | 'high'>('medium');
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Update metrics state
|
|
195
|
+
*/
|
|
196
|
+
const updateMetrics = useCallback((newMetrics: Partial<PerformanceMetrics>) => {
|
|
197
|
+
setMetrics(prev => ({
|
|
198
|
+
...prev,
|
|
199
|
+
...newMetrics,
|
|
200
|
+
timestamp: performance.now(),
|
|
201
|
+
}));
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Auto-scaling logic
|
|
206
|
+
*/
|
|
207
|
+
const applyAutoScaling = useCallback((currentFps: number) => {
|
|
208
|
+
if (manualOverride) return;
|
|
209
|
+
|
|
210
|
+
const currentQuality = qualityLevelRef.current;
|
|
211
|
+
|
|
212
|
+
// Check for low FPS
|
|
213
|
+
if (currentFps < minFps) {
|
|
214
|
+
lowFpsCountRef.current++;
|
|
215
|
+
highFpsCountRef.current = 0;
|
|
216
|
+
|
|
217
|
+
// Scale down after N consecutive low-FPS frames
|
|
218
|
+
if (lowFpsCountRef.current >= lowFpsFrames && currentQuality !== 'low') {
|
|
219
|
+
qualityLevelRef.current = 'low';
|
|
220
|
+
updateMetrics({ qualityLevel: 'low', lowFpsCount: lowFpsCountRef.current });
|
|
221
|
+
|
|
222
|
+
if (debug) {
|
|
223
|
+
console.log('[PerformanceMonitor] Scaling down to LOW quality. FPS:', currentFps);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Check for high FPS
|
|
228
|
+
else if (currentFps >= scaleUpThreshold) {
|
|
229
|
+
highFpsCountRef.current++;
|
|
230
|
+
lowFpsCountRef.current = 0;
|
|
231
|
+
|
|
232
|
+
// Scale up after N consecutive high-FPS frames
|
|
233
|
+
if (highFpsCountRef.current >= highFpsFrames) {
|
|
234
|
+
const newQuality = currentQuality === 'low' ? 'medium' : 'high';
|
|
235
|
+
qualityLevelRef.current = newQuality;
|
|
236
|
+
updateMetrics({ qualityLevel: newQuality, lowFpsCount: 0 });
|
|
237
|
+
|
|
238
|
+
if (debug) {
|
|
239
|
+
console.log('[PerformanceMonitor] Scaling up to', newQuality.toUpperCase(), 'quality. FPS:', currentFps);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
highFpsCountRef.current = 0;
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
// FPS in normal range, reset counters
|
|
246
|
+
lowFpsCountRef.current = 0;
|
|
247
|
+
highFpsCountRef.current = 0;
|
|
248
|
+
}
|
|
249
|
+
}, [manualOverride, minFps, scaleUpThreshold, lowFpsFrames, highFpsFrames, debug, updateMetrics]);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Animation frame handler - measures performance
|
|
253
|
+
*/
|
|
254
|
+
const measureFrame = useCallback((currentTime: number) => {
|
|
255
|
+
if (!isEnabled) return;
|
|
256
|
+
|
|
257
|
+
frameCountRef.current++;
|
|
258
|
+
|
|
259
|
+
// Calculate frame time
|
|
260
|
+
const frameTime = currentTime - lastFrameTimeRef.current;
|
|
261
|
+
lastFrameTimeRef.current = currentTime;
|
|
262
|
+
|
|
263
|
+
// Update FPS every 100ms for responsiveness
|
|
264
|
+
if (currentTime - lastFpsUpdateRef.current >= 100) {
|
|
265
|
+
const elapsed = currentTime - lastFpsUpdateRef.current;
|
|
266
|
+
const fps = Math.round((frameCountRef.current * 1000) / elapsed);
|
|
267
|
+
|
|
268
|
+
// Apply auto-scaling
|
|
269
|
+
applyAutoScaling(fps);
|
|
270
|
+
|
|
271
|
+
updateMetrics({
|
|
272
|
+
fps,
|
|
273
|
+
frameTime,
|
|
274
|
+
qualityLevel: qualityLevelRef.current,
|
|
275
|
+
lowFpsCount: lowFpsCountRef.current,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Reset for next measurement period
|
|
279
|
+
frameCountRef.current = 0;
|
|
280
|
+
lastFpsUpdateRef.current = currentTime;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Continue measurement loop
|
|
284
|
+
animationFrameRef.current = requestAnimationFrame(measureFrame);
|
|
285
|
+
}, [isEnabled, applyAutoScaling, updateMetrics]);
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Initialize GPU memory tracking
|
|
289
|
+
*/
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
if (!isEnabled || typeof window === 'undefined') return;
|
|
292
|
+
|
|
293
|
+
let mounted = true;
|
|
294
|
+
|
|
295
|
+
const initGpuMemory = async () => {
|
|
296
|
+
const memory = await getGpuMemoryInfo();
|
|
297
|
+
if (mounted) {
|
|
298
|
+
updateMetrics({ gpuMemory: memory });
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
initGpuMemory();
|
|
303
|
+
|
|
304
|
+
return () => {
|
|
305
|
+
mounted = false;
|
|
306
|
+
};
|
|
307
|
+
}, [isEnabled, updateMetrics]);
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Start/stop monitoring based on enabled state
|
|
311
|
+
*/
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
if (!isEnabled) {
|
|
314
|
+
if (animationFrameRef.current !== null) {
|
|
315
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
316
|
+
animationFrameRef.current = null;
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Initialize
|
|
322
|
+
lastFpsUpdateRef.current = performance.now();
|
|
323
|
+
lastFrameTimeRef.current = performance.now();
|
|
324
|
+
animationFrameRef.current = requestAnimationFrame(measureFrame);
|
|
325
|
+
|
|
326
|
+
// Cleanup
|
|
327
|
+
return () => {
|
|
328
|
+
if (animationFrameRef.current !== null) {
|
|
329
|
+
cancelAnimationFrame(animationFrameRef.current);
|
|
330
|
+
animationFrameRef.current = null;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
334
|
+
}, [isEnabled]); // measureFrame is stable via useCallback, avoid re-creating RAF loop
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Manually set quality level (disables auto-scaling)
|
|
338
|
+
*/
|
|
339
|
+
const setQualityLevel = useCallback((level: 'low' | 'medium' | 'high') => {
|
|
340
|
+
setManualOverride(true);
|
|
341
|
+
qualityLevelRef.current = level;
|
|
342
|
+
updateMetrics({ qualityLevel: level, isAutoScaling: false });
|
|
343
|
+
|
|
344
|
+
if (debug) {
|
|
345
|
+
console.log('[PerformanceMonitor] Manual quality override:', level);
|
|
346
|
+
}
|
|
347
|
+
}, [updateMetrics, debug]);
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Reset to auto-scaling mode
|
|
351
|
+
*/
|
|
352
|
+
const resetAutoScaling = useCallback(() => {
|
|
353
|
+
setManualOverride(false);
|
|
354
|
+
lowFpsCountRef.current = 0;
|
|
355
|
+
highFpsCountRef.current = 0;
|
|
356
|
+
updateMetrics({ isAutoScaling: true, lowFpsCount: 0 });
|
|
357
|
+
|
|
358
|
+
if (debug) {
|
|
359
|
+
console.log('[PerformanceMonitor] Auto-scaling reset');
|
|
360
|
+
}
|
|
361
|
+
}, [updateMetrics, debug]);
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Toggle monitoring on/off
|
|
365
|
+
*/
|
|
366
|
+
const toggleMonitoring = useCallback(() => {
|
|
367
|
+
setIsEnabled(prev => !prev);
|
|
368
|
+
}, []);
|
|
369
|
+
|
|
370
|
+
// Calculate derived values
|
|
371
|
+
const recommendedQuality = calculateQualityLevel(metrics.fps, metrics.qualityLevel);
|
|
372
|
+
const isUnderperforming = metrics.fps < minFps;
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
metrics,
|
|
376
|
+
recommendedQuality,
|
|
377
|
+
isUnderperforming,
|
|
378
|
+
setQualityLevel,
|
|
379
|
+
resetAutoScaling,
|
|
380
|
+
toggleMonitoring,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Debug Overlay Component (Optional)
|
|
386
|
+
*
|
|
387
|
+
* Shows real-time performance metrics on screen.
|
|
388
|
+
* Only rendered when showOverlay is enabled.
|
|
389
|
+
*/
|
|
390
|
+
export function PerformanceOverlay({ metrics }: { metrics: PerformanceMetrics }) {
|
|
391
|
+
if (typeof window === 'undefined') return null;
|
|
392
|
+
|
|
393
|
+
const overlayStyle: React.CSSProperties = {
|
|
394
|
+
position: 'fixed',
|
|
395
|
+
top: '10px',
|
|
396
|
+
right: '10px',
|
|
397
|
+
padding: '10px',
|
|
398
|
+
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
399
|
+
color: '#fff',
|
|
400
|
+
fontFamily: 'monospace',
|
|
401
|
+
fontSize: '12px',
|
|
402
|
+
borderRadius: '4px',
|
|
403
|
+
zIndex: 9999,
|
|
404
|
+
pointerEvents: 'none',
|
|
405
|
+
minWidth: '150px',
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const getFpsColor = (fps: number) => {
|
|
409
|
+
if (fps >= 58) return '#4ade80'; // Green
|
|
410
|
+
if (fps >= 45) return '#fbbf24'; // Yellow
|
|
411
|
+
return '#ef4444'; // Red
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Performance overlay removed - will be implemented as separate component
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Utility to get quality multipliers for glass parameters
|
|
420
|
+
*/
|
|
421
|
+
export function getQualityMultipliers(quality: 'low' | 'medium' | 'high') {
|
|
422
|
+
switch (quality) {
|
|
423
|
+
case 'low':
|
|
424
|
+
return {
|
|
425
|
+
distortionOctaves: 2,
|
|
426
|
+
displacementScale: 0.6,
|
|
427
|
+
blurAmount: 0.7,
|
|
428
|
+
animationSpeed: 0.8,
|
|
429
|
+
chromaticIntensity: 0.5,
|
|
430
|
+
};
|
|
431
|
+
case 'medium':
|
|
432
|
+
return {
|
|
433
|
+
distortionOctaves: 4,
|
|
434
|
+
displacementScale: 0.85,
|
|
435
|
+
blurAmount: 0.9,
|
|
436
|
+
animationSpeed: 0.95,
|
|
437
|
+
chromaticIntensity: 0.75,
|
|
438
|
+
};
|
|
439
|
+
case 'high':
|
|
440
|
+
return {
|
|
441
|
+
distortionOctaves: 5,
|
|
442
|
+
displacementScale: 1.0,
|
|
443
|
+
blurAmount: 1.0,
|
|
444
|
+
animationSpeed: 1.0,
|
|
445
|
+
chromaticIntensity: 1.0,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { GlassParams, ResponsiveBreakpoint } from '../../lib/types/glass';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mobile optimization presets
|
|
5
|
+
*
|
|
6
|
+
* These presets adjust glass effect parameters based on device performance tier
|
|
7
|
+
* to ensure smooth animations and responsive interactions.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Performance preset - Maximum FPS, reduced quality
|
|
12
|
+
* Best for low-end devices or when battery saving is priority
|
|
13
|
+
*/
|
|
14
|
+
export const PERFORMANCE_PRESET: GlassParams = {
|
|
15
|
+
distortionOctaves: 2, // Minimal FBM layers
|
|
16
|
+
displacementScale: 50, // Subtle displacement
|
|
17
|
+
blurAmount: 5, // Light blur
|
|
18
|
+
saturation: 80, // Reduced saturation
|
|
19
|
+
aberrationIntensity: 0.3, // Minimal chromatic aberration
|
|
20
|
+
animationSpeed: 0.8, // Slightly slower for performance
|
|
21
|
+
chromaticIntensity: 0.3, // Low chromatic effect
|
|
22
|
+
distortionLacunarity: 1.5, // Simpler noise pattern
|
|
23
|
+
distortionGain: 0.3, // Lower gain for smoother effect
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Balanced preset - Good quality with reasonable performance
|
|
28
|
+
* Default preset for most mobile devices
|
|
29
|
+
*/
|
|
30
|
+
export const BALANCED_PRESET: GlassParams = {
|
|
31
|
+
distortionOctaves: 3, // Moderate FBM layers
|
|
32
|
+
displacementScale: 75, // Medium displacement
|
|
33
|
+
blurAmount: 8, // Moderate blur
|
|
34
|
+
saturation: 90, // Near-full saturation
|
|
35
|
+
aberrationIntensity: 0.5, // Moderate chromatic aberration
|
|
36
|
+
animationSpeed: 1.0, // Normal speed
|
|
37
|
+
chromaticIntensity: 0.5, // Moderate chromatic effect
|
|
38
|
+
distortionLacunarity: 2.0, // Standard noise pattern
|
|
39
|
+
distortionGain: 0.4, // Balanced gain
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Quality preset - Maximum visual fidelity
|
|
44
|
+
* For high-end devices with powerful GPUs
|
|
45
|
+
*/
|
|
46
|
+
export const QUALITY_PRESET: GlassParams = {
|
|
47
|
+
distortionOctaves: 4, // More FBM layers for detail
|
|
48
|
+
displacementScale: 100, // Stronger displacement
|
|
49
|
+
blurAmount: 12, // Smoother blur
|
|
50
|
+
saturation: 100, // Full saturation
|
|
51
|
+
aberrationIntensity: 0.7, // Pronounced chromatic aberration
|
|
52
|
+
animationSpeed: 1.2, // Slightly faster for drama
|
|
53
|
+
chromaticIntensity: 0.7, // Strong chromatic effect
|
|
54
|
+
distortionLacunarity: 2.2, // Richer noise pattern
|
|
55
|
+
distortionGain: 0.5, // Higher gain for more contrast
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Get preset by name
|
|
60
|
+
*/
|
|
61
|
+
export function getDevicePreset(presetName: 'performance' | 'balanced' | 'quality'): GlassParams {
|
|
62
|
+
switch (presetName) {
|
|
63
|
+
case 'performance':
|
|
64
|
+
return PERFORMANCE_PRESET;
|
|
65
|
+
case 'balanced':
|
|
66
|
+
return BALANCED_PRESET;
|
|
67
|
+
case 'quality':
|
|
68
|
+
return QUALITY_PRESET;
|
|
69
|
+
default:
|
|
70
|
+
return BALANCED_PRESET;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Mobile-optimized responsive breakpoints
|
|
76
|
+
* Automatically applies appropriate presets based on viewport size
|
|
77
|
+
*/
|
|
78
|
+
export const MOBILE_OPTIMIZED_BREAKPOINTS: Record<string, ResponsiveBreakpoint> = {
|
|
79
|
+
/** Desktop - Full quality */
|
|
80
|
+
desktop: {
|
|
81
|
+
minWidth: 1024,
|
|
82
|
+
params: {
|
|
83
|
+
distortionOctaves: 6,
|
|
84
|
+
displacementScale: 150,
|
|
85
|
+
blurAmount: 15,
|
|
86
|
+
saturation: 100,
|
|
87
|
+
aberrationIntensity: 1.0,
|
|
88
|
+
animationSpeed: 1.0,
|
|
89
|
+
chromaticIntensity: 1.0,
|
|
90
|
+
distortionLacunarity: 2.5,
|
|
91
|
+
distortionGain: 0.6,
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
/** Laptop - High quality */
|
|
96
|
+
laptop: {
|
|
97
|
+
minWidth: 768,
|
|
98
|
+
params: {
|
|
99
|
+
...QUALITY_PRESET,
|
|
100
|
+
distortionOctaves: 5,
|
|
101
|
+
displacementScale: 120,
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/** Tablet - Balanced quality */
|
|
106
|
+
tablet: {
|
|
107
|
+
minWidth: 640,
|
|
108
|
+
params: {
|
|
109
|
+
...BALANCED_PRESET,
|
|
110
|
+
distortionOctaves: 4,
|
|
111
|
+
displacementScale: 90,
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/** Mobile - Performance optimized */
|
|
116
|
+
mobile: {
|
|
117
|
+
maxWidth: 639,
|
|
118
|
+
params: {
|
|
119
|
+
...PERFORMANCE_PRESET,
|
|
120
|
+
distortionOctaves: 3,
|
|
121
|
+
displacementScale: 75,
|
|
122
|
+
blurAmount: 6,
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/** Small mobile - Maximum performance */
|
|
127
|
+
mobileSmall: {
|
|
128
|
+
maxWidth: 375,
|
|
129
|
+
params: {
|
|
130
|
+
...PERFORMANCE_PRESET,
|
|
131
|
+
distortionOctaves: 2,
|
|
132
|
+
displacementScale: 50,
|
|
133
|
+
blurAmount: 4,
|
|
134
|
+
saturation: 70,
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get mobile-optimized parameters for current viewport
|
|
141
|
+
* Can be used standalone or with useResponsiveGlass hook
|
|
142
|
+
*/
|
|
143
|
+
export function getMobileOptimizedParams(viewportWidth: number): GlassParams {
|
|
144
|
+
if (viewportWidth >= 1024) {
|
|
145
|
+
return MOBILE_OPTIMIZED_BREAKPOINTS.desktop?.params || BALANCED_PRESET;
|
|
146
|
+
} else if (viewportWidth >= 768) {
|
|
147
|
+
return MOBILE_OPTIMIZED_BREAKPOINTS.laptop?.params || BALANCED_PRESET;
|
|
148
|
+
} else if (viewportWidth >= 640) {
|
|
149
|
+
return MOBILE_OPTIMIZED_BREAKPOINTS.tablet?.params || BALANCED_PRESET;
|
|
150
|
+
} else if (viewportWidth >= 375) {
|
|
151
|
+
return MOBILE_OPTIMIZED_BREAKPOINTS.mobile?.params || PERFORMANCE_PRESET;
|
|
152
|
+
} else {
|
|
153
|
+
return MOBILE_OPTIMIZED_BREAKPOINTS.mobileSmall?.params || PERFORMANCE_PRESET;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Device detection utilities
|
|
159
|
+
*/
|
|
160
|
+
export const DeviceDetector = {
|
|
161
|
+
/** Check if device is mobile */
|
|
162
|
+
isMobile(): boolean {
|
|
163
|
+
if (typeof window === 'undefined') return false;
|
|
164
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
/** Check if device is tablet */
|
|
168
|
+
isTablet(): boolean {
|
|
169
|
+
if (typeof window === 'undefined') return false;
|
|
170
|
+
const width = window.innerWidth;
|
|
171
|
+
return width >= 640 && width < 1024 && this.isMobile();
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/** Get recommended preset based on device type */
|
|
175
|
+
getRecommendedPreset(): 'performance' | 'balanced' | 'quality' {
|
|
176
|
+
if (!this.isMobile()) return 'quality';
|
|
177
|
+
if (this.isTablet()) return 'balanced';
|
|
178
|
+
return 'performance';
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
/** Get device pixel ratio */
|
|
182
|
+
getPixelRatio(): number {
|
|
183
|
+
if (typeof window === 'undefined') return 1;
|
|
184
|
+
return window.devicePixelRatio || 1;
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
/** Check if device has touch support */
|
|
188
|
+
hasTouchSupport(): boolean {
|
|
189
|
+
if (typeof window === 'undefined') return false;
|
|
190
|
+
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
191
|
+
},
|
|
192
|
+
};
|