@piyawat37/performance-overlay 0.0.0-stage → 0.1.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/README.md +16 -2
- package/dist/PerformanceOverlay.d.ts +5 -0
- package/dist/PerformanceOverlay.js +53 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/style.css +47 -0
- package/package.json +29 -4
package/README.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React performance overlay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A small, opt-in browser performance overlay. React 18 or 19 is a peer dependency.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { PerformanceOverlay } from "@env-platform/performance-overlay";
|
|
7
|
+
import "@env-platform/performance-overlay/style.css";
|
|
8
|
+
|
|
9
|
+
// Vite example: the host app decides when the overlay is available.
|
|
10
|
+
<PerformanceOverlay enabled={import.meta.env.DEV} />
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`enabled` defaults to `false`; when false, the component renders nothing and collects no measurements. Pass any boolean expression, such as a feature flag or an environment check supplied by the host bundler. `defaultOpen` defaults to `false` and sets the initial expanded state. The user can expand or collapse it; collection stops while collapsed.
|
|
14
|
+
|
|
15
|
+
The overlay samples frame rate, JavaScript heap, DOM element count, and long tasks. Heap is a non-standard browser API, and long tasks are not available in every browser. FPS depends on display refresh rate and may be throttled in background tabs. These are diagnostic estimates, not CPU utilization. Measurements stay in the browser and include the overlay's own overhead. This package does not send data anywhere.
|
|
16
|
+
|
|
17
|
+
Build with `npm install && npm run build` in this directory. Publish only after choosing and verifying the package name, scope, and registry.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
export function PerformanceOverlay({ enabled = false, defaultOpen = false, }) {
|
|
4
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
5
|
+
const [sample, setSample] = useState(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (!enabled || !open)
|
|
8
|
+
return;
|
|
9
|
+
let frames = 0;
|
|
10
|
+
let frameId = 0;
|
|
11
|
+
let last = performance.now();
|
|
12
|
+
let longTasks = 0;
|
|
13
|
+
const tick = () => {
|
|
14
|
+
frames += 1;
|
|
15
|
+
frameId = requestAnimationFrame(tick);
|
|
16
|
+
};
|
|
17
|
+
frameId = requestAnimationFrame(tick);
|
|
18
|
+
let observer;
|
|
19
|
+
if (typeof PerformanceObserver !== "undefined" &&
|
|
20
|
+
PerformanceObserver.supportedEntryTypes?.includes("longtask")) {
|
|
21
|
+
observer = new PerformanceObserver((list) => {
|
|
22
|
+
longTasks += list.getEntries().length;
|
|
23
|
+
});
|
|
24
|
+
observer.observe({ entryTypes: ["longtask"] });
|
|
25
|
+
}
|
|
26
|
+
const timer = window.setInterval(() => {
|
|
27
|
+
const now = performance.now();
|
|
28
|
+
const memory = performance.memory;
|
|
29
|
+
setSample({
|
|
30
|
+
fps: Math.round((frames * 1000) / (now - last)),
|
|
31
|
+
heap: memory ? `${(memory.usedJSHeapSize / 1048576).toFixed(0)} MB` : "Unavailable",
|
|
32
|
+
nodes: document.getElementsByTagName("*").length,
|
|
33
|
+
longTasks,
|
|
34
|
+
});
|
|
35
|
+
frames = 0;
|
|
36
|
+
longTasks = 0;
|
|
37
|
+
last = now;
|
|
38
|
+
}, 1000);
|
|
39
|
+
return () => {
|
|
40
|
+
cancelAnimationFrame(frameId);
|
|
41
|
+
clearInterval(timer);
|
|
42
|
+
observer?.disconnect();
|
|
43
|
+
};
|
|
44
|
+
}, [enabled, open]);
|
|
45
|
+
if (!enabled)
|
|
46
|
+
return null;
|
|
47
|
+
return (_jsxs("aside", { className: "dev-performance", "aria-label": "Development performance monitor", children: [_jsx("button", { type: "button", className: "dev-performance__toggle", "aria-expanded": open, onClick: () => setOpen(!open), children: open ? "Hide performance" : "Performance" }), open && (_jsxs("div", { className: "dev-performance__body", children: [_jsxs("div", { children: [_jsx("span", { children: "Frame rate" }), _jsx("strong", { children: sample ? `${sample.fps} FPS` : "—" })] }), _jsxs("div", { children: [_jsx("span", { children: "JS heap" }), _jsx("strong", { children: sample?.heap ?? "—" })] }), _jsxs("div", { children: [_jsx("span", { children: "DOM elements" }), _jsx("strong", { children: sample?.nodes.toLocaleString() ?? "—" })] }), _jsxs("div", { children: [_jsx("span", { children: "Long tasks / sec" }), _jsx("strong", { children: sample
|
|
48
|
+
? typeof PerformanceObserver !== "undefined" &&
|
|
49
|
+
PerformanceObserver.supportedEntryTypes?.includes("longtask")
|
|
50
|
+
? sample.longTasks
|
|
51
|
+
: "Unavailable"
|
|
52
|
+
: "—" })] }), _jsx("small", { children: "Heap and long tasks depend on browser support" })] }))] }));
|
|
53
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PerformanceOverlay } from "./PerformanceOverlay.js";
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.dev-performance {
|
|
2
|
+
position: fixed;
|
|
3
|
+
z-index: 2147483647;
|
|
4
|
+
right: 12px;
|
|
5
|
+
top: 12px;
|
|
6
|
+
width: min(230px, calc(100vw - 24px));
|
|
7
|
+
color: #f5f7fa;
|
|
8
|
+
background: #17202b;
|
|
9
|
+
border-radius: 8px;
|
|
10
|
+
box-shadow: 0 4px 16px #0004;
|
|
11
|
+
font:
|
|
12
|
+
12px / 1.5 system-ui,
|
|
13
|
+
sans-serif;
|
|
14
|
+
}
|
|
15
|
+
.dev-performance__toggle {
|
|
16
|
+
width: 100%;
|
|
17
|
+
padding: 8px 12px;
|
|
18
|
+
color: inherit;
|
|
19
|
+
background: transparent;
|
|
20
|
+
border: 0;
|
|
21
|
+
border-radius: inherit;
|
|
22
|
+
text-align: left;
|
|
23
|
+
font: inherit;
|
|
24
|
+
font-weight: 700;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
.dev-performance__toggle:focus-visible {
|
|
28
|
+
outline: 2px solid #8bc6ff;
|
|
29
|
+
outline-offset: 2px;
|
|
30
|
+
}
|
|
31
|
+
.dev-performance__body {
|
|
32
|
+
padding: 0 12px 10px;
|
|
33
|
+
}
|
|
34
|
+
.dev-performance__body div {
|
|
35
|
+
display: flex;
|
|
36
|
+
justify-content: space-between;
|
|
37
|
+
gap: 12px;
|
|
38
|
+
padding: 2px 0;
|
|
39
|
+
}
|
|
40
|
+
.dev-performance__body strong {
|
|
41
|
+
font-variant-numeric: tabular-nums;
|
|
42
|
+
}
|
|
43
|
+
.dev-performance__body small {
|
|
44
|
+
display: block;
|
|
45
|
+
margin-top: 8px;
|
|
46
|
+
color: #c5d1dc;
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piyawat37/performance-overlay",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A configurable client-side performance overlay for React applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./style.css": "./dist/style.css"
|
|
16
|
+
},
|
|
17
|
+
"sideEffects": [
|
|
18
|
+
"**/*.css"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json && cp src/performance-overlay.css dist/style.css",
|
|
22
|
+
"prepare": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": ">=18 <20"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
}
|
|
31
|
+
}
|