@checkstack/ui 1.3.2 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @checkstack/ui
2
2
 
3
+ ## 1.3.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 692c717: Increased the brightness and color intensity of the AmbientBackground auroras to ensure high visibility through the 1px grid lines.
8
+
9
+ ## 1.3.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 594eecc: Implemented a manual "Low Power Mode" toggle in the user menu, allowing users to explicitly disable expensive visual effects. This replaces the previous automatic performance diagnostics with a more predictable, user-controlled system that persists to localStorage while still respecting OS-level "Reduced Motion" settings.
14
+
3
15
  ## 1.3.2
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/ui",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "dependencies": {
@@ -24,35 +24,19 @@ export const AmbientBackground: React.FC<AmbientBackgroundProps> = ({
24
24
  return (
25
25
  <>
26
26
  <div
27
- className="aurora-blob absolute w-[50%] h-[50%] -top-[10%] -left-[10%]"
27
+ className="aurora-blob absolute w-[100%] h-[100%] -top-[40%] -left-[20%]"
28
28
  style={{
29
29
  background:
30
- "radial-gradient(circle at center, hsl(var(--primary) / 0.8), transparent 60%)",
31
- animation: "aurora-float-1 25s ease-in-out infinite",
30
+ "radial-gradient(circle at center, hsl(var(--primary) / 0.9) 0%, hsl(var(--primary) / 0.3) 50%, transparent 90%)",
31
+ animation: "aurora-float-1 60s ease-in-out infinite",
32
32
  }}
33
33
  />
34
34
  <div
35
- className="aurora-blob absolute w-[40%] h-[40%] bottom-[10%] right-[10%]"
35
+ className="aurora-blob absolute w-[90%] h-[90%] -bottom-[30%] -right-[20%]"
36
36
  style={{
37
37
  background:
38
- "radial-gradient(circle at center, hsl(var(--chart-2) / 0.7), transparent 60%)",
39
- animation: "aurora-float-2 20s ease-in-out infinite",
40
- }}
41
- />
42
- <div
43
- className="aurora-blob absolute w-[35%] h-[35%] top-[30%] left-[40%]"
44
- style={{
45
- background:
46
- "radial-gradient(circle at center, hsl(var(--primary) / 0.6), transparent 60%)",
47
- animation: "aurora-float-3 30s ease-in-out infinite",
48
- }}
49
- />
50
- <div
51
- className="aurora-blob absolute w-[45%] h-[45%] bottom-[20%] left-[10%]"
52
- style={{
53
- background:
54
- "radial-gradient(circle at center, hsl(var(--chart-1) / 0.5), transparent 60%)",
55
- animation: "aurora-float-4 35s ease-in-out infinite",
38
+ "radial-gradient(circle at center, hsl(var(--chart-2) / 0.8) 0%, hsl(var(--chart-2) / 0.2) 50%, transparent 90%)",
39
+ animation: "aurora-float-2 50s ease-in-out infinite",
56
40
  }}
57
41
  />
58
42
  </>
@@ -67,8 +51,10 @@ export const AmbientBackground: React.FC<AmbientBackgroundProps> = ({
67
51
  )}
68
52
  >
69
53
  <div className="pointer-events-none fixed inset-0 overflow-hidden">
70
- {/* Layer 1: Aurora Blobs (Bottom) */}
71
- {auroraBlobs}
54
+ {/* Layer 1: Aurora Blobs (Bottom) - Centered in content area */}
55
+ <div className="max-w-7xl mx-auto h-full relative">
56
+ {auroraBlobs}
57
+ </div>
72
58
 
73
59
  {/* Layer 2: Grid Mask - Switches mode based on performance capability */}
74
60
  <div
@@ -1,18 +1,26 @@
1
1
  import React, { createContext, useContext, useEffect, useState } from "react";
2
2
 
3
+ const STORAGE_KEY = "checkstack-low-power";
4
+
3
5
  interface PerformanceContextValue {
4
6
  /**
5
- * Whether the device is considered low-power or lacks hardware acceleration.
6
- * If true, expensive animations, blurs, and transitions should be disabled.
7
+ * Whether the application should run in low-power mode.
8
+ * Derived from (manualLowPower || prefersReducedMotion).
7
9
  */
8
10
  isLowPower: boolean;
9
- /** Whether the performance detection has completed */
11
+ /** Whether the performance state has been initialized from storage */
10
12
  isLoaded: boolean;
13
+ /** The current state of the manual toggle */
14
+ manualLowPower: boolean;
15
+ /** Toggle the manual low power setting */
16
+ toggleManualLowPower: () => void;
11
17
  }
12
18
 
13
19
  const PerformanceContext = createContext<PerformanceContextValue>({
14
20
  isLowPower: false,
15
21
  isLoaded: false,
22
+ manualLowPower: false,
23
+ toggleManualLowPower: () => {},
16
24
  });
17
25
 
18
26
  /**
@@ -26,99 +34,54 @@ interface PerformanceProviderProps {
26
34
  }
27
35
 
28
36
  /**
29
- * PerformanceProvider - Centralizes detection of hardware capabilities and user preferences.
30
- * Runs a suite of heuristics (Media Queries, WebGL Audit, and Canvas Benchmarks)
31
- * once on mount and provides the result to the entire application.
37
+ * PerformanceProvider - Centralizes management of the application's performance tier.
38
+ * Supports manual override (persisted to localStorage) and respects OS-level
39
+ * Reduced Motion preferences.
32
40
  */
33
41
  export const PerformanceProvider: React.FC<PerformanceProviderProps> = ({ children }) => {
34
- const [state, setState] = useState<PerformanceContextValue>({
35
- isLowPower: false,
36
- isLoaded: false,
37
- });
42
+ const [manualLowPower, setManualLowPower] = useState<boolean>(false);
43
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState<boolean>(false);
44
+ const [isLoaded, setIsLoaded] = useState<boolean>(false);
38
45
 
39
46
  useEffect(() => {
40
- const runPerformanceChecks = () => {
41
- // 1. Accessibility Override (Reduced Motion)
42
- const prefersReducedMotion = globalThis.matchMedia(
43
- "(prefers-reduced-motion: reduce)"
44
- ).matches;
47
+ // 1. Initialize Manual Toggle from localStorage
48
+ const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
49
+ if (stored === "true") {
50
+ setManualLowPower(true);
51
+ }
52
+
53
+ // 2. Initialize Reduced Motion Detection
54
+ const mediaQuery = globalThis.matchMedia("(prefers-reduced-motion: reduce)");
55
+ setPrefersReducedMotion(mediaQuery.matches);
45
56
 
46
- // 2. Hardware Hint Check (Low RAM or CPU Cores)
47
- const nav = globalThis.navigator as Navigator & { deviceMemory?: number };
48
- const isLowEndHardware =
49
- (nav.deviceMemory !== undefined && nav.deviceMemory < 4) ||
50
- nav.hardwareConcurrency <= 2;
57
+ // 3. Listen for changes in OS-level settings
58
+ const listener = (e: MediaQueryListEvent) => setPrefersReducedMotion(e.matches);
59
+ mediaQuery.addEventListener("change", listener);
51
60
 
52
- // 3. Renderer Audit (Detecting Software Rasterizers)
53
- let isSoftwareRenderer = false;
54
- try {
55
- const canvas = document.createElement("canvas");
56
- const gl =
57
- canvas.getContext("webgl") ||
58
- canvas.getContext("experimental-webgl");
59
-
60
- if (gl instanceof WebGLRenderingContext) {
61
- const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
62
- const renderer = (
63
- debugInfo
64
- ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
65
- : gl.getParameter(gl.RENDERER)
66
- ).toLowerCase();
61
+ setIsLoaded(true);
67
62
 
68
- isSoftwareRenderer = [
69
- "software",
70
- "swiftshader",
71
- "llvmpipe",
72
- "softpipe",
73
- "swrast",
74
- "osmesa",
75
- "mesa off-screen",
76
- "basic render",
77
- "warp",
78
- ].some((id) => renderer.includes(id));
79
- } else {
80
- // No WebGL support at all usually implies old/stripped browser
81
- isSoftwareRenderer = true;
82
- }
83
- } catch {
84
- isSoftwareRenderer = true;
85
- }
63
+ return () => mediaQuery.removeEventListener("change", listener);
64
+ }, []);
86
65
 
87
- // 4. Empirical Benchmark (Stress Test)
88
- let isSlow = false;
89
- try {
90
- const benchCanvas = document.createElement("canvas");
91
- benchCanvas.width = 100;
92
- benchCanvas.height = 100;
93
- const ctx = benchCanvas.getContext("2d");
94
- if (ctx) {
95
- const t0 = globalThis.performance.now();
96
- ctx.filter = "blur(20px)";
97
- for (let i = 0; i < 50; i++) {
98
- ctx.fillRect(i, i, 5, 5);
99
- }
100
- // Force pipeline sync to measure actual drawing time
101
- ctx.getImageData(0, 0, 1, 1);
102
- const t1 = globalThis.performance.now();
103
- isSlow = t1 - t0 > 4; // Threshold for CPU-based rasterization
104
- }
105
- } catch {
106
- isSlow = true;
107
- }
66
+ const toggleManualLowPower = () => {
67
+ setManualLowPower((prev) => {
68
+ const next = !prev;
69
+ globalThis.localStorage?.setItem(STORAGE_KEY, String(next));
70
+ return next;
71
+ });
72
+ };
108
73
 
109
- const isLowPowerVerdict = prefersReducedMotion || isLowEndHardware || isSoftwareRenderer || isSlow;
110
-
111
- setState({
112
- isLowPower: isLowPowerVerdict,
113
- isLoaded: true,
114
- });
115
- };
74
+ const isLowPower = manualLowPower || prefersReducedMotion;
116
75
 
117
- runPerformanceChecks();
118
- }, []);
76
+ const value = {
77
+ isLowPower,
78
+ isLoaded,
79
+ manualLowPower,
80
+ toggleManualLowPower,
81
+ };
119
82
 
120
83
  return (
121
- <PerformanceContext.Provider value={state}>
84
+ <PerformanceContext.Provider value={value}>
122
85
  {children}
123
86
  </PerformanceContext.Provider>
124
87
  );