@shohojdhara/atomix 0.5.0 → 0.5.2

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 (168) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +230 -83
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +24 -23
  9. package/dist/charts.js +271 -369
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +3 -2
  15. package/dist/core.js +342 -382
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +4 -6
  18. package/dist/forms.js +233 -334
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +11 -2
  21. package/dist/heavy.js +406 -445
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +109 -65
  24. package/dist/index.esm.js +654 -748
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +621 -717
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +24 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +1 -133
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  55. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
  56. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
  58. package/src/components/AtomixGlass/glass-utils.ts +51 -1
  59. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  60. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  62. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  63. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  64. package/src/components/AtomixGlass/stories/types.ts +3 -3
  65. package/src/components/Button/Button.tsx +114 -57
  66. package/src/components/Callout/Callout.tsx +4 -4
  67. package/src/components/Chart/ChartRenderer.tsx +1 -1
  68. package/src/components/Chart/DonutChart.tsx +11 -8
  69. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  70. package/src/components/Form/Select.tsx +4 -4
  71. package/src/components/List/List.tsx +4 -4
  72. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  73. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  74. package/src/components/ProductReview/ProductReview.tsx +4 -2
  75. package/src/components/Rating/Rating.tsx +4 -2
  76. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  77. package/src/components/Steps/Steps.tsx +1 -1
  78. package/src/components/Tabs/Tabs.tsx +5 -5
  79. package/src/components/Testimonial/Testimonial.tsx +4 -2
  80. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  81. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  82. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  83. package/src/layouts/CssGrid/index.ts +8 -0
  84. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  85. package/src/layouts/CssGrid/scripts/index.js +43 -0
  86. package/src/layouts/Grid/scripts/Container.js +139 -0
  87. package/src/layouts/Grid/scripts/Grid.js +184 -0
  88. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  89. package/src/layouts/Grid/scripts/Row.js +154 -0
  90. package/src/layouts/Grid/scripts/index.js +48 -0
  91. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  92. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  93. package/src/lib/composables/useAccordion.ts +5 -5
  94. package/src/lib/composables/useAtomixGlass.ts +111 -74
  95. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  96. package/src/lib/composables/useBarChart.ts +2 -2
  97. package/src/lib/composables/useChart.ts +3 -2
  98. package/src/lib/composables/useChartToolbar.ts +48 -66
  99. package/src/lib/composables/useDataTable.ts +1 -1
  100. package/src/lib/composables/useDatePicker.ts +2 -2
  101. package/src/lib/composables/useEdgePanel.ts +45 -54
  102. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  103. package/src/lib/composables/usePhotoViewer.ts +2 -3
  104. package/src/lib/composables/usePieChart.ts +1 -1
  105. package/src/lib/composables/usePopover.ts +151 -139
  106. package/src/lib/composables/useSideMenu.ts +28 -41
  107. package/src/lib/composables/useSlider.ts +2 -6
  108. package/src/lib/composables/useTooltip.ts +2 -2
  109. package/src/lib/config/index.ts +39 -0
  110. package/src/lib/constants/components.ts +1 -0
  111. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  112. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  113. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  114. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  115. package/src/lib/types/components.ts +1 -0
  116. package/src/styles/01-settings/_index.scss +1 -0
  117. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  118. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  119. package/src/styles/02-tools/_tools.glass.scss +6 -0
  120. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  121. package/src/styles/06-components/_components.atomix-glass.scss +160 -99
  122. package/scripts/cli/__tests__/README.md +0 -81
  123. package/scripts/cli/__tests__/basic.test.js +0 -116
  124. package/scripts/cli/__tests__/clean.test.js +0 -278
  125. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  126. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  127. package/scripts/cli/__tests__/generator.test.js +0 -613
  128. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  129. package/scripts/cli/__tests__/integration.test.js +0 -938
  130. package/scripts/cli/__tests__/migrate.test.js +0 -74
  131. package/scripts/cli/__tests__/security.test.js +0 -206
  132. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  133. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  134. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  135. package/scripts/cli/__tests__/utils.test.js +0 -165
  136. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  137. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  138. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  139. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  140. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  141. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  142. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  143. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  144. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  145. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  146. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  147. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  148. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  149. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  150. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  151. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  152. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  153. package/src/components/TypedButton/TypedButton.tsx +0 -39
  154. package/src/components/TypedButton/index.ts +0 -2
  155. package/src/lib/composables/useBreadcrumb.ts +0 -81
  156. package/src/lib/composables/useChartInteractions.ts +0 -123
  157. package/src/lib/composables/useChartPerformance.ts +0 -347
  158. package/src/lib/composables/useDropdown.ts +0 -338
  159. package/src/lib/composables/useModal.ts +0 -110
  160. package/src/lib/composables/useTypedButton.ts +0 -66
  161. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  162. package/src/lib/utils/displacement-generator.ts +0 -92
  163. package/src/lib/utils/memoryMonitor.ts +0 -191
  164. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  165. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  166. package/src/styles/06-components/_components.testbutton.scss +0 -212
  167. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  168. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -1,63 +1,50 @@
1
- import React, { forwardRef, useRef, useState, useEffect, useMemo } from 'react';
1
+ import React, { forwardRef, useId, useRef, useState, useEffect, useMemo } from 'react';
2
2
  import type { CSSProperties } from 'react';
3
- import type { DisplacementMode, MousePosition, GlassSize, AtomixGlassProps } from '../../lib/types/components';
4
- import type { FragmentShaderType } from './shader-utils';
3
+ import type {
4
+ DisplacementMode,
5
+ MousePosition,
6
+ GlassSize,
7
+ AtomixGlassProps,
8
+ } from '../../lib/types/components';
9
+ import type { FragmentShaderType, ShaderOptions, Vec2 } from './shader-utils';
5
10
  import { GlassFilter } from './GlassFilter';
6
- import { calculateMouseInfluence, clampBlur, validateGlassSize } from './glass-utils';
11
+ import {
12
+ calculateMouseInfluence,
13
+ clampBlur,
14
+ validateGlassSize,
15
+ getCachedShader,
16
+ setCachedShader,
17
+ } from './glass-utils';
7
18
  import { ATOMIX_GLASS } from '../../lib/constants/components';
8
19
 
9
20
  const { CONSTANTS } = ATOMIX_GLASS;
10
21
 
11
- // Module-level counter for deterministic ID generation
12
- let idCounter = 0;
13
-
14
- // Module-level shared shader cache with LRU eviction
15
- const MAX_CACHE_SIZE = 15;
16
- interface ShaderCacheEntry {
17
- url: string;
18
- timestamp: number;
22
+ // ─── Blur multiplier constants (module-level, never change at runtime) ────────
23
+ const EDGE_BLUR_MULTIPLIER = 1.25;
24
+ const CENTER_BLUR_MULTIPLIER = 1.1;
25
+ const FLOW_BLUR_MULTIPLIER = 1.2;
26
+ const MOUSE_INFLUENCE_BLUR_FACTOR = 0.15;
27
+ const EDGE_INTENSITY_MULTIPLIER = 1.5;
28
+ const EDGE_INTENSITY_MOUSE_FACTOR = 0.15;
29
+ const CENTER_INTENSITY_DISTANCE_FACTOR = 0.3;
30
+ const CENTER_INTENSITY_MOUSE_FACTOR = 0.1;
31
+ /** Maximum blur multiplier relative to base — prevents runaway blur. */
32
+ const MAX_BLUR_RELATIVE = 2;
33
+
34
+ // ─── Shader utility types ─────────────────────────────────────────────────────
35
+
36
+ interface ShaderGenerator {
37
+ updateShader(): string;
38
+ destroy(): void;
19
39
  }
20
- const sharedShaderCache = new Map<string, ShaderCacheEntry>();
21
-
22
- /**
23
- * Get cached shader URL, updating access timestamp
24
- */
25
- const getCachedShader = (key: string): string | null => {
26
- const entry = sharedShaderCache.get(key);
27
- if (entry) {
28
- // Update access timestamp for LRU
29
- entry.timestamp = Date.now();
30
- return entry.url;
31
- }
32
- return null;
33
- };
34
40
 
35
- /**
36
- * Set cached shader URL with LRU eviction
37
- */
38
- const setCachedShader = (key: string, url: string): void => {
39
- // Evict oldest entries if at capacity
40
- if (sharedShaderCache.size >= MAX_CACHE_SIZE) {
41
- const entries = Array.from(sharedShaderCache.entries());
42
- // Sort by timestamp (oldest first)
43
- entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
44
- // Remove oldest entry
45
- const oldestEntry = entries[0];
46
- if (oldestEntry) {
47
- sharedShaderCache.delete(oldestEntry[0]);
48
- }
49
- }
50
- sharedShaderCache.set(key, { url, timestamp: Date.now() });
41
+ /** Fragment shader function — signature matches shader-utils.ts */
42
+ type FragmentShaderFn = (uv: Vec2, mousePosition?: Vec2) => Vec2;
51
43
 
52
- // Development mode: log cache size
53
- if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'production') {
54
- if (sharedShaderCache.size >= MAX_CACHE_SIZE * 0.8) {
55
- console.log(
56
- `AtomixGlass: Shader cache size: ${String(sharedShaderCache.size).replace(/[\r\n]/g, '')}/${String(MAX_CACHE_SIZE).replace(/[\r\n]/g, '')}`
57
- );
58
- }
59
- }
60
- };
44
+ interface ShaderUtilsModule {
45
+ ShaderDisplacementGenerator: new (opts: ShaderOptions) => ShaderGenerator;
46
+ fragmentShaders: Record<string, FragmentShaderFn>;
47
+ }
61
48
 
62
49
  interface AtomixGlassContainerProps
63
50
  extends Pick<
@@ -101,11 +88,12 @@ interface AtomixGlassContainerProps
101
88
  effectiveReducedMotion?: boolean;
102
89
  shaderVariant?: FragmentShaderType;
103
90
  withLiquidBlur?: boolean;
91
+ isFixedOrSticky?: boolean;
104
92
 
105
93
  // Phase 1: Animation System props
106
94
  shaderTime?: number;
107
95
 
108
- contentRef?: React.RefObject<HTMLDivElement>;
96
+ contentRef?: React.RefObject<HTMLDivElement | null>;
109
97
  children?: React.ReactNode;
110
98
  }
111
99
 
@@ -140,6 +128,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
140
128
  effectiveReducedMotion = false,
141
129
  shaderVariant = 'liquidGlass',
142
130
  withLiquidBlur = false,
131
+ isFixedOrSticky = false,
143
132
 
144
133
  // Phase 1: Animation System props
145
134
  shaderTime,
@@ -155,23 +144,17 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
155
144
  },
156
145
  ref
157
146
  ) => {
158
- // Generate a stable, deterministic ID for SSR compatibility
159
- // Use a module-level counter that's consistent across server and client
160
- const filterId = useMemo(() => {
161
- return `atomix-glass-filter-${++idCounter}`;
162
- }, []);
147
+ // React 18 useId — stable, unique, and SSR-safe (no module-level counter)
148
+ const rawId = useId();
149
+ const filterId = useMemo(() => `atomix-glass-filter-${rawId.replace(/:/g, '')}`, [rawId]);
163
150
 
164
151
  const [shaderMapUrl, setShaderMapUrl] = useState<string>('');
165
- const shaderGeneratorRef = useRef<any>(null);
166
- const shaderUtilsRef = useRef<{
167
- ShaderDisplacementGenerator: any;
168
- fragmentShaders: any;
169
- } | null>(null);
152
+ const shaderGeneratorRef = useRef<ShaderGenerator | null>(null);
153
+ const shaderUtilsRef = useRef<ShaderUtilsModule | null>(null);
170
154
 
171
- // Use shared module-level cache (no per-instance cache needed)
172
155
  const shaderDebounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
173
156
  const shaderUpdateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
174
-
157
+
175
158
  // Phase 1: Animation frame ref for continuous shader updates
176
159
  const animationFrameRef = useRef<number | null>(null);
177
160
 
@@ -228,7 +211,8 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
228
211
  try {
229
212
  const { ShaderDisplacementGenerator, fragmentShaders } = shaderUtilsRef.current;
230
213
  shaderGeneratorRef.current?.destroy();
231
- const selectedShader = fragmentShaders[shaderVariant] || fragmentShaders.liquidGlass;
214
+ const selectedShader = (fragmentShaders[shaderVariant] ??
215
+ fragmentShaders.liquidGlass) as FragmentShaderFn;
232
216
  shaderGeneratorRef.current = new ShaderDisplacementGenerator({
233
217
  width: glassSize.width,
234
218
  height: glassSize.height,
@@ -236,7 +220,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
236
220
  });
237
221
 
238
222
  shaderUpdateTimeoutRef.current = setTimeout(() => {
239
- const url = shaderGeneratorRef.current?.updateShader() || '';
223
+ const url = shaderGeneratorRef.current?.updateShader() ?? '';
240
224
  if (url) {
241
225
  setCachedShader(cacheKey, url);
242
226
  }
@@ -301,16 +285,18 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
301
285
  ? 24
302
286
  : 20;
303
287
  const effectiveSpeed = Math.max(0.5, Math.min(2, animationSpeed || 1));
304
- const complexity =
305
- withMultiLayerDistortion
306
- ? Math.max(
307
- 1,
308
- (distortionOctaves || 3) / 3 +
309
- Math.max(0, (distortionLacunarity || 2) - 2) * 0.25 +
310
- Math.max(0, (distortionGain || 0.5) - 0.5)
311
- )
312
- : 1;
313
- const targetFps = Math.max(12, Math.min(60, Math.round((baseFps * effectiveSpeed) / complexity)));
288
+ const complexity = withMultiLayerDistortion
289
+ ? Math.max(
290
+ 1,
291
+ (distortionOctaves || 3) / 3 +
292
+ Math.max(0, (distortionLacunarity || 2) - 2) * 0.25 +
293
+ Math.max(0, (distortionGain || 0.5) - 0.5)
294
+ )
295
+ : 1;
296
+ const targetFps = Math.max(
297
+ 12,
298
+ Math.min(60, Math.round((baseFps * effectiveSpeed) / complexity))
299
+ );
314
300
  const frameInterval = 1000 / targetFps;
315
301
  let lastUpdate = 0;
316
302
  let isCancelled = false;
@@ -380,18 +366,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
380
366
  return undefined;
381
367
  }, [ref, glassSize]);
382
368
 
383
- // Pre-calculate static multipliers outside useMemo
384
- const EDGE_BLUR_MULTIPLIER = 1.25;
385
- const CENTER_BLUR_MULTIPLIER = 1.1;
386
- const FLOW_BLUR_MULTIPLIER = 1.2;
387
- const MOUSE_INFLUENCE_BLUR_FACTOR = 0.15;
388
- const EDGE_INTENSITY_MULTIPLIER = 1.5;
389
- const EDGE_INTENSITY_MOUSE_FACTOR = 0.15;
390
- const CENTER_INTENSITY_DISTANCE_FACTOR = 0.3;
391
- const CENTER_INTENSITY_MOUSE_FACTOR = 0.1;
392
- // Maximum blur multiplier relative to base — prevents runaway blur
393
- const MAX_BLUR_RELATIVE = 2;
394
-
395
369
  const liquidBlur = useMemo(() => {
396
370
  const defaultBlur = {
397
371
  baseBlur: blurAmount,
@@ -562,7 +536,7 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
562
536
  } as React.CSSProperties;
563
537
  }
564
538
  }, [
565
- borderRadius,
539
+ borderRadius,
566
540
  backdropStyle,
567
541
  mouseOffset,
568
542
  overLight,
@@ -573,8 +547,6 @@ export const AtomixGlassContainer = forwardRef<HTMLDivElement, AtomixGlassContai
573
547
  return (
574
548
  <div
575
549
  ref={el => {
576
- // Apply force no-transition
577
-
578
550
  // Handle forwarded ref
579
551
  if (typeof ref === 'function') {
580
552
  ref(el);
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { memo } from 'react';
2
2
  import type { PerformanceMetrics } from '../../lib/composables/usePerformanceMonitor';
3
3
 
4
4
  interface PerformanceDashboardProps {
@@ -7,213 +7,165 @@ interface PerformanceDashboardProps {
7
7
  onClose?: () => void;
8
8
  }
9
9
 
10
+ /** Map an FPS value to a semantic color token string. */
11
+ const getFpsColor = (fps: number): string => {
12
+ if (fps >= 58) return 'var(--atomix-color-success, #4ade80)';
13
+ if (fps >= 45) return 'var(--atomix-color-warning, #fbbf24)';
14
+ return 'var(--atomix-color-danger, #ef4444)';
15
+ };
16
+
17
+ /** Map a quality level string to a semantic color token string. */
18
+ const getQualityColor = (quality: string): string => {
19
+ switch (quality) {
20
+ case 'high': return 'var(--atomix-color-success, #4ade80)';
21
+ case 'medium': return 'var(--atomix-color-warning, #fbbf24)';
22
+ case 'low': return 'var(--atomix-color-danger, #ef4444)';
23
+ default: return '#9ca3af';
24
+ }
25
+ };
26
+
27
+ const getFpsLabel = (fps: number): string => {
28
+ if (fps >= 58) return 'Optimal';
29
+ if (fps >= 45) return 'Warning';
30
+ return 'Critical';
31
+ };
32
+
33
+ // Keyframes for pulse animation (injected via style tag)
34
+ const keyframesStyle = `
35
+ @keyframes perf-dashboard-pulse {
36
+ 0%, 100% { opacity: 1; }
37
+ 50% { opacity: 0.5; }
38
+ }
39
+ `;
40
+
41
+ // Inject keyframes once
42
+ if (typeof document !== 'undefined') {
43
+ const styleId = 'perf-dashboard-keyframes';
44
+ if (!document.getElementById(styleId)) {
45
+ const styleEl = document.createElement('style');
46
+ styleEl.id = styleId;
47
+ styleEl.textContent = keyframesStyle;
48
+ document.head.appendChild(styleEl);
49
+ }
50
+ }
51
+
10
52
  /**
11
- * PerformanceDashboard - Real-time performance monitoring overlay
12
- *
13
- * Displays:
14
- * - Current FPS with color coding
15
- * - Frame time statistics
16
- * - Quality level indicator
17
- * - GPU memory usage (if available)
18
- * - Auto-scaling status
53
+ * PerformanceDashboard - Real-time performance monitoring overlay.
54
+ *
55
+ * Displays FPS, frame time, quality level, GPU memory, and auto-scaling status.
56
+ * Rendered only when `debugPerformance={true}` on the parent `AtomixGlass`.
19
57
  */
20
- export const PerformanceDashboard: React.FC<PerformanceDashboardProps> = ({
21
- metrics,
22
- isVisible = true,
23
- onClose
24
- }) => {
25
- // Get color for FPS display
26
- const getFpsColor = (fps: number): string => {
27
- if (fps >= 58) return '#4ade80'; // Green - good
28
- if (fps >= 45) return '#fbbf24'; // Yellow - warning
29
- return '#ef4444'; // Red - critical
30
- };
31
-
32
- // Get quality level badge color
33
- const getQualityColor = (quality: string): string => {
34
- switch (quality) {
35
- case 'high': return '#4ade80';
36
- case 'medium': return '#fbbf24';
37
- case 'low': return '#ef4444';
38
- default: return '#9ca3af';
39
- }
40
- };
41
-
42
- // Dashboard styles
43
- const dashboardStyle: React.CSSProperties = useMemo(() => ({
44
- position: 'fixed',
45
- top: '16px',
46
- right: '16px',
47
- padding: '12px 16px',
48
- backgroundColor: 'rgba(17, 24, 39, 0.95)',
49
- borderRadius: '8px',
50
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
51
- fontFamily: 'monospace',
52
- fontSize: '12px',
53
- color: '#fff',
54
- zIndex: 9999,
55
- minWidth: '200px',
56
- backdropFilter: 'blur(8px)',
57
- border: '1px solid rgba(255, 255, 255, 0.1)',
58
- transition: 'opacity 0.3s ease',
59
- opacity: isVisible ? 1 : 0,
60
- pointerEvents: isVisible ? 'auto' : 'none',
61
- }), [isVisible]);
62
-
63
- const headerStyle: React.CSSProperties = useMemo(() => ({
64
- display: 'flex',
65
- justifyContent: 'space-between',
66
- alignItems: 'center',
67
- marginBottom: '8px',
68
- paddingBottom: '8px',
69
- borderBottom: '1px solid rgba(255, 255, 255, 0.1)',
70
- }), []);
71
-
72
- const titleStyle: React.CSSProperties = useMemo(() => ({
73
- fontWeight: 'bold',
74
- fontSize: '13px',
75
- color: '#fff',
76
- }), []);
77
-
78
- const closeButtonStyle: React.CSSProperties = useMemo(() => ({
79
- background: 'transparent',
80
- border: 'none',
81
- color: '#9ca3af',
82
- cursor: 'pointer',
83
- fontSize: '16px',
84
- padding: '0',
85
- lineHeight: '1',
86
- }), []);
87
-
88
- const metricRowStyle: React.CSSProperties = useMemo(() => ({
89
- display: 'flex',
90
- justifyContent: 'space-between',
91
- alignItems: 'center',
92
- marginBottom: '6px',
93
- }), []);
94
-
95
- const labelStyle: React.CSSProperties = useMemo(() => ({
96
- color: '#9ca3af',
97
- marginRight: '12px',
98
- }), []);
99
-
100
- const valueStyle: React.CSSProperties = useMemo(() => ({
101
- fontWeight: 'bold',
102
- }), []);
103
-
104
- if (!isVisible) return null;
105
-
106
- return (
107
- <div style={dashboardStyle}>
108
- {/* Header */}
109
- <div style={headerStyle}>
110
- <span style={titleStyle}>Performance Monitor</span>
111
- {onClose && (
112
- <button
113
- style={closeButtonStyle}
114
- onClick={onClose}
115
- aria-label="Close performance dashboard"
116
- >
117
- ×
118
- </button>
119
- )}
120
- </div>
121
-
122
- {/* FPS Display */}
123
- <div style={metricRowStyle}>
124
- <span style={labelStyle}>FPS</span>
125
- <span
126
- style={{
127
- ...valueStyle,
128
- color: getFpsColor(metrics.fps)
129
- }}
130
- >
131
- {Math.round(metrics.fps)}
132
- </span>
133
- </div>
58
+ export const PerformanceDashboard: React.FC<PerformanceDashboardProps> = memo(
59
+ ({ metrics, isVisible = true, onClose }) => {
60
+ if (!isVisible) return null;
61
+
62
+ const fpsColor = getFpsColor(metrics.fps);
63
+ const isCritical = metrics.fps < 45;
64
+
65
+ return (
66
+ <div
67
+ className="c-perf-dashboard u-position-fixed u-top-4 u-end-4 u-p-3 u-px-4 u-text-xs u-font-mono u-text-white u-rounded-md u-border u-border-white-alpha-10 u-shadow-lg"
68
+ style={{
69
+ zIndex: 9999,
70
+ minWidth: '12.5rem', // 200px
71
+ backgroundColor: 'rgba(17, 24, 39, 0.95)',
72
+ backdropFilter: 'blur(8px)',
73
+ transition: 'opacity 0.3s ease',
74
+ }}
75
+ >
76
+ {/* Header */}
77
+ <div className="u-flex u-items-center u-justify-between u-mb-2 u-pb-2 u-border-b u-border-white-alpha-10">
78
+ <span className="u-text-sm u-font-bold u-text-white">Performance Monitor</span>
79
+ {onClose && (
80
+ <button
81
+ className="u-bg-transparent u-border-none u-p-0 u-line-height-1 u-text-base u-text-gray-400 u-cursor-pointer hover:u-text-white"
82
+ onClick={onClose}
83
+ aria-label="Close performance dashboard"
84
+ style={{ transition: 'color 0.2s ease' }}
85
+ >
86
+ ×
87
+ </button>
88
+ )}
89
+ </div>
134
90
 
135
- {/* Frame Time */}
136
- <div style={metricRowStyle}>
137
- <span style={labelStyle}>Frame Time</span>
138
- <span style={valueStyle}>
139
- {metrics.frameTime.toFixed(2)}ms
140
- </span>
141
- </div>
91
+ {/* FPS */}
92
+ <div className="u-flex u-items-center u-justify-between u-mb-1-5">
93
+ <span className="u-text-gray-400 u-me-3">FPS</span>
94
+ <span className="u-font-bold" style={{ color: fpsColor }}>
95
+ {Math.round(metrics.fps)}
96
+ </span>
97
+ </div>
142
98
 
143
- {/* Quality Level */}
144
- <div style={metricRowStyle}>
145
- <span style={labelStyle}>Quality</span>
146
- <span
147
- style={{
148
- ...valueStyle,
149
- color: getQualityColor(metrics.qualityLevel),
150
- textTransform: 'uppercase',
151
- fontSize: '11px',
152
- }}
153
- >
154
- {metrics.qualityLevel}
155
- </span>
156
- </div>
99
+ {/* Frame Time */}
100
+ <div className="u-flex u-items-center u-justify-between u-mb-1-5">
101
+ <span className="u-text-gray-400 u-me-3">Frame Time</span>
102
+ <span className="u-font-bold">
103
+ {metrics.frameTime.toFixed(2)}ms
104
+ </span>
105
+ </div>
157
106
 
158
- {/* GPU Memory (if available) */}
159
- {metrics.gpuMemory && (
160
- <div style={metricRowStyle}>
161
- <span style={labelStyle}>GPU Memory</span>
162
- <span style={valueStyle}>
163
- ~{Math.round(metrics.gpuMemory / 1024)}MB
107
+ {/* Quality Level */}
108
+ <div className="u-flex u-items-center u-justify-between u-mb-1-5">
109
+ <span className="u-text-gray-400 u-me-3">Quality</span>
110
+ <span
111
+ className="u-font-bold u-text-uppercase"
112
+ style={{
113
+ fontSize: '0.6875rem', // 11px
114
+ color: getQualityColor(metrics.qualityLevel)
115
+ }}
116
+ >
117
+ {metrics.qualityLevel}
164
118
  </span>
165
119
  </div>
166
- )}
167
-
168
- {/* Auto-scaling Status */}
169
- {metrics.isAutoScaling && (
170
- <div style={{
171
- marginTop: '8px',
172
- paddingTop: '8px',
173
- borderTop: '1px solid rgba(255, 255, 255, 0.1)',
174
- fontSize: '10px',
175
- color: '#6b7280',
176
- textAlign: 'center',
177
- }}>
178
- Auto-scaling active
120
+
121
+ {/* GPU Memory (if available) */}
122
+ {metrics.gpuMemory && (
123
+ <div className="u-flex u-items-center u-justify-between u-mb-1-5">
124
+ <span className="u-text-gray-400 u-me-3">GPU Memory</span>
125
+ <span className="u-font-bold">
126
+ ~{Math.round(metrics.gpuMemory / 1024)}MB
127
+ </span>
128
+ </div>
129
+ )}
130
+
131
+ {/* Auto-scaling notice */}
132
+ {metrics.isAutoScaling && (
133
+ <div
134
+ className="u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10 u-text-center"
135
+ style={{
136
+ fontSize: '0.625rem', // 10px
137
+ color: '#6b7280',
138
+ }}
139
+ >
140
+ Auto-scaling active
141
+ </div>
142
+ )}
143
+
144
+ {/* Status indicator */}
145
+ <div className="u-flex u-items-center u-gap-2 u-mt-2 u-pt-2 u-border-t u-border-white-alpha-10">
146
+ <div
147
+ className="u-rounded-full"
148
+ style={{
149
+ width: '0.5rem',
150
+ height: '0.5rem',
151
+ flexShrink: 0,
152
+ backgroundColor: fpsColor,
153
+ ...(isCritical && { animation: 'perf-dashboard-pulse 1s infinite' }),
154
+ }}
155
+ />
156
+ <span
157
+ className="u-text-xs"
158
+ style={{
159
+ fontSize: '0.625rem', // 10px
160
+ color: fpsColor
161
+ }}
162
+ >
163
+ {getFpsLabel(metrics.fps)}
164
+ </span>
179
165
  </div>
180
- )}
181
-
182
- {/* Performance Status Indicator */}
183
- <div style={{
184
- marginTop: '8px',
185
- paddingTop: '8px',
186
- borderTop: '1px solid rgba(255, 255, 255, 0.1)',
187
- display: 'flex',
188
- alignItems: 'center',
189
- gap: '6px',
190
- }}>
191
- <div style={{
192
- width: '8px',
193
- height: '8px',
194
- borderRadius: '50%',
195
- backgroundColor: getFpsColor(metrics.fps),
196
- animation: metrics.fps < 45 ? 'pulse 1s infinite' : 'none',
197
- }} />
198
- <span style={{
199
- fontSize: '10px',
200
- color: metrics.fps >= 58 ? '#4ade80' : metrics.fps >= 45 ? '#fbbf24' : '#ef4444',
201
- }}>
202
- {metrics.fps >= 58 ? 'Optimal' : metrics.fps >= 45 ? 'Warning' : 'Critical'}
203
- </span>
204
166
  </div>
205
- </div>
206
- );
207
- };
167
+ );
168
+ }
169
+ );
208
170
 
209
- // Add pulse animation for critical FPS
210
- if (typeof document !== 'undefined') {
211
- const styleSheet = document.createElement('style');
212
- styleSheet.textContent = `
213
- @keyframes pulse {
214
- 0%, 100% { opacity: 1; }
215
- 50% { opacity: 0.5; }
216
- }
217
- `;
218
- document.head.appendChild(styleSheet);
219
- }
171
+ PerformanceDashboard.displayName = 'PerformanceDashboard';