@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 CHANGED
@@ -1,3 +1,17 @@
1
- # Temporary Holding Version
1
+ # React performance overlay
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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,5 @@
1
+ export type PerformanceOverlayProps = {
2
+ enabled?: boolean;
3
+ defaultOpen?: boolean;
4
+ };
5
+ export declare function PerformanceOverlay({ enabled, defaultOpen, }: PerformanceOverlayProps): import("react").JSX.Element | null;
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export type { PerformanceOverlayProps } from "./PerformanceOverlay.js";
2
+ export { PerformanceOverlay } from "./PerformanceOverlay.js";
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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }