@checkstack/ui 1.3.3 → 1.3.5
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.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 286491a: Added automatic FPS detection that enables "Low Power Mode" once for devices running below 50 FPS, ensuring smooth performance even for users unaware of the manual toggle.
|
|
8
|
+
|
|
9
|
+
## 1.3.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 692c717: Increased the brightness and color intensity of the AmbientBackground auroras to ensure high visibility through the 1px grid lines.
|
|
14
|
+
|
|
3
15
|
## 1.3.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -24,35 +24,19 @@ export const AmbientBackground: React.FC<AmbientBackgroundProps> = ({
|
|
|
24
24
|
return (
|
|
25
25
|
<>
|
|
26
26
|
<div
|
|
27
|
-
className="aurora-blob absolute w-[
|
|
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.
|
|
31
|
-
animation: "aurora-float-1
|
|
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-[
|
|
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.
|
|
39
|
-
animation: "aurora-float-2
|
|
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
|
-
|
|
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,9 +1,10 @@
|
|
|
1
1
|
import React, { createContext, useContext, useEffect, useState } from "react";
|
|
2
|
+
import { useToast } from "./ToastProvider";
|
|
2
3
|
|
|
3
4
|
const STORAGE_KEY = "checkstack-low-power";
|
|
4
5
|
|
|
5
6
|
interface PerformanceContextValue {
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
7
8
|
* Whether the application should run in low-power mode.
|
|
8
9
|
* Derived from (manualLowPower || prefersReducedMotion).
|
|
9
10
|
*/
|
|
@@ -38,9 +39,13 @@ interface PerformanceProviderProps {
|
|
|
38
39
|
* Supports manual override (persisted to localStorage) and respects OS-level
|
|
39
40
|
* Reduced Motion preferences.
|
|
40
41
|
*/
|
|
41
|
-
export const PerformanceProvider: React.FC<PerformanceProviderProps> = ({
|
|
42
|
+
export const PerformanceProvider: React.FC<PerformanceProviderProps> = ({
|
|
43
|
+
children,
|
|
44
|
+
}) => {
|
|
45
|
+
const toast = useToast();
|
|
42
46
|
const [manualLowPower, setManualLowPower] = useState<boolean>(false);
|
|
43
|
-
const [prefersReducedMotion, setPrefersReducedMotion] =
|
|
47
|
+
const [prefersReducedMotion, setPrefersReducedMotion] =
|
|
48
|
+
useState<boolean>(false);
|
|
44
49
|
const [isLoaded, setIsLoaded] = useState<boolean>(false);
|
|
45
50
|
|
|
46
51
|
useEffect(() => {
|
|
@@ -51,11 +56,14 @@ export const PerformanceProvider: React.FC<PerformanceProviderProps> = ({ childr
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
// 2. Initialize Reduced Motion Detection
|
|
54
|
-
const mediaQuery = globalThis.matchMedia(
|
|
59
|
+
const mediaQuery = globalThis.matchMedia(
|
|
60
|
+
"(prefers-reduced-motion: reduce)",
|
|
61
|
+
);
|
|
55
62
|
setPrefersReducedMotion(mediaQuery.matches);
|
|
56
63
|
|
|
57
64
|
// 3. Listen for changes in OS-level settings
|
|
58
|
-
const listener = (e: MediaQueryListEvent) =>
|
|
65
|
+
const listener = (e: MediaQueryListEvent) =>
|
|
66
|
+
setPrefersReducedMotion(e.matches);
|
|
59
67
|
mediaQuery.addEventListener("change", listener);
|
|
60
68
|
|
|
61
69
|
setIsLoaded(true);
|
|
@@ -63,6 +71,61 @@ export const PerformanceProvider: React.FC<PerformanceProviderProps> = ({ childr
|
|
|
63
71
|
return () => mediaQuery.removeEventListener("change", listener);
|
|
64
72
|
}, []);
|
|
65
73
|
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
// 4. Automatic FPS Detection (Runs once per device)
|
|
76
|
+
const stored = globalThis.localStorage?.getItem(STORAGE_KEY);
|
|
77
|
+
if (stored !== null) return; // User already has a saved preference, don't auto-detect
|
|
78
|
+
|
|
79
|
+
let rAFId: number;
|
|
80
|
+
let isCancelled = false;
|
|
81
|
+
|
|
82
|
+
// Wait 1 second for initial render and hydration to settle
|
|
83
|
+
const warmupTimer = setTimeout(() => {
|
|
84
|
+
if (isCancelled) return;
|
|
85
|
+
|
|
86
|
+
let frameCount = 0;
|
|
87
|
+
let startTime = performance.now();
|
|
88
|
+
|
|
89
|
+
const measure = (time: DOMHighResTimeStamp) => {
|
|
90
|
+
if (isCancelled) return;
|
|
91
|
+
if (frameCount === 0) startTime = time;
|
|
92
|
+
frameCount++;
|
|
93
|
+
const elapsed = time - startTime;
|
|
94
|
+
|
|
95
|
+
if (elapsed >= 1000) {
|
|
96
|
+
const fps = (frameCount * 1000) / elapsed;
|
|
97
|
+
if (fps < 50) {
|
|
98
|
+
setManualLowPower(true);
|
|
99
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, "true");
|
|
100
|
+
toast.info(
|
|
101
|
+
"Low power mode was automatically activated to improve performance. Hardware acceleration appears to be disabled.",
|
|
102
|
+
8000
|
|
103
|
+
);
|
|
104
|
+
console.warn(
|
|
105
|
+
`[PerformanceProvider] Auto-enabled Low Power Mode. Detected FPS: ${fps.toFixed(1)}`,
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
// Save 'false' to prevent future auto-detection checks
|
|
109
|
+
globalThis.localStorage?.setItem(STORAGE_KEY, "false");
|
|
110
|
+
console.log(
|
|
111
|
+
`[PerformanceProvider] Performance OK. Detected FPS: ${fps.toFixed(1)}`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
rAFId = globalThis.requestAnimationFrame(measure);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
rAFId = globalThis.requestAnimationFrame(measure);
|
|
120
|
+
}, 1000);
|
|
121
|
+
|
|
122
|
+
return () => {
|
|
123
|
+
isCancelled = true;
|
|
124
|
+
clearTimeout(warmupTimer);
|
|
125
|
+
if (rAFId) globalThis.cancelAnimationFrame(rAFId);
|
|
126
|
+
};
|
|
127
|
+
}, [toast]);
|
|
128
|
+
|
|
66
129
|
const toggleManualLowPower = () => {
|
|
67
130
|
setManualLowPower((prev) => {
|
|
68
131
|
const next = !prev;
|