react-dev-profiler 1.0.1 → 1.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/dist/index.cjs +297 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +304 -116
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/index.css +0 -179
- package/dist/index.css.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
// src/DevProfiler.tsx
|
|
2
|
-
import { Profiler, useRef as
|
|
3
|
-
|
|
4
|
-
// src/DevProfiler.module.css
|
|
5
|
-
var DevProfiler_default = {};
|
|
2
|
+
import { Profiler, useRef as useRef3, useState as useState4, useEffect as useEffect4, useCallback as useCallback3 } from "react";
|
|
6
3
|
|
|
7
4
|
// src/types.ts
|
|
8
5
|
var HISTORY_SIZE = 60;
|
|
@@ -37,32 +34,228 @@ var __DEV__ = typeof process !== "undefined" ? process.env.NODE_ENV !== "product
|
|
|
37
34
|
|
|
38
35
|
// src/hooks.ts
|
|
39
36
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
37
|
+
|
|
38
|
+
// src/styles.ts
|
|
39
|
+
var s = {
|
|
40
|
+
wrapper: {
|
|
41
|
+
display: "contents"
|
|
42
|
+
},
|
|
43
|
+
panel: {
|
|
44
|
+
position: "fixed",
|
|
45
|
+
zIndex: 99999,
|
|
46
|
+
background: "rgba(8, 8, 8, 0.95)",
|
|
47
|
+
border: "1px solid #222",
|
|
48
|
+
borderRadius: 10,
|
|
49
|
+
padding: "10px 14px",
|
|
50
|
+
minWidth: 220,
|
|
51
|
+
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.7)",
|
|
52
|
+
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
|
53
|
+
backdropFilter: "blur(12px)",
|
|
54
|
+
userSelect: "none",
|
|
55
|
+
color: "#ccc",
|
|
56
|
+
fontSize: 11,
|
|
57
|
+
cursor: "grab",
|
|
58
|
+
touchAction: "none"
|
|
59
|
+
},
|
|
60
|
+
panelHeader: {
|
|
61
|
+
display: "flex",
|
|
62
|
+
justifyContent: "space-between",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
marginBottom: 8
|
|
65
|
+
},
|
|
66
|
+
panelTitle: {
|
|
67
|
+
color: "#666",
|
|
68
|
+
fontSize: 9,
|
|
69
|
+
fontWeight: 700,
|
|
70
|
+
letterSpacing: 1.5,
|
|
71
|
+
textTransform: "uppercase",
|
|
72
|
+
display: "flex",
|
|
73
|
+
alignItems: "center",
|
|
74
|
+
gap: 6
|
|
75
|
+
},
|
|
76
|
+
instanceBadge: {
|
|
77
|
+
background: "#222",
|
|
78
|
+
color: "#888",
|
|
79
|
+
fontSize: 8,
|
|
80
|
+
padding: "1px 5px",
|
|
81
|
+
borderRadius: 4,
|
|
82
|
+
letterSpacing: 0.5,
|
|
83
|
+
textTransform: "none"
|
|
84
|
+
},
|
|
85
|
+
headerActions: {
|
|
86
|
+
display: "flex",
|
|
87
|
+
alignItems: "center",
|
|
88
|
+
gap: 8
|
|
89
|
+
},
|
|
90
|
+
iconBtn: {
|
|
91
|
+
background: "none",
|
|
92
|
+
border: "none",
|
|
93
|
+
color: "#444",
|
|
94
|
+
cursor: "pointer",
|
|
95
|
+
padding: 4,
|
|
96
|
+
margin: -4,
|
|
97
|
+
lineHeight: 1,
|
|
98
|
+
display: "flex",
|
|
99
|
+
alignItems: "center",
|
|
100
|
+
transition: "color 0.15s"
|
|
101
|
+
},
|
|
102
|
+
iconBtnActive: {
|
|
103
|
+
color: "#4ade80"
|
|
104
|
+
},
|
|
105
|
+
closeBtn: {
|
|
106
|
+
background: "none",
|
|
107
|
+
border: "none",
|
|
108
|
+
color: "#444",
|
|
109
|
+
cursor: "pointer",
|
|
110
|
+
fontSize: 13,
|
|
111
|
+
padding: 4,
|
|
112
|
+
margin: -4,
|
|
113
|
+
lineHeight: 1
|
|
114
|
+
},
|
|
115
|
+
body: {
|
|
116
|
+
display: "flex",
|
|
117
|
+
flexDirection: "column",
|
|
118
|
+
gap: 5
|
|
119
|
+
},
|
|
120
|
+
section: {
|
|
121
|
+
color: "#444",
|
|
122
|
+
fontSize: 8,
|
|
123
|
+
fontWeight: 600,
|
|
124
|
+
letterSpacing: 1,
|
|
125
|
+
textTransform: "uppercase",
|
|
126
|
+
marginTop: 2
|
|
127
|
+
},
|
|
128
|
+
separator: {
|
|
129
|
+
height: 1,
|
|
130
|
+
background: "#1a1a1a",
|
|
131
|
+
margin: "2px 0"
|
|
132
|
+
},
|
|
133
|
+
row: {
|
|
134
|
+
display: "flex",
|
|
135
|
+
justifyContent: "space-between",
|
|
136
|
+
alignItems: "center",
|
|
137
|
+
padding: "1px 0"
|
|
138
|
+
},
|
|
139
|
+
rowLabel: {
|
|
140
|
+
color: "#555",
|
|
141
|
+
fontSize: 10
|
|
142
|
+
},
|
|
143
|
+
rowValue: {
|
|
144
|
+
fontSize: 11,
|
|
145
|
+
fontWeight: 600
|
|
146
|
+
},
|
|
147
|
+
miniRow: {
|
|
148
|
+
display: "flex",
|
|
149
|
+
justifyContent: "space-between",
|
|
150
|
+
color: "#444",
|
|
151
|
+
fontSize: 9,
|
|
152
|
+
marginTop: -2,
|
|
153
|
+
marginBottom: 2
|
|
154
|
+
},
|
|
155
|
+
graphWrap: {
|
|
156
|
+
marginTop: 4
|
|
157
|
+
},
|
|
158
|
+
toggleBtn: {
|
|
159
|
+
position: "fixed",
|
|
160
|
+
zIndex: 99998,
|
|
161
|
+
height: 26,
|
|
162
|
+
borderRadius: 13,
|
|
163
|
+
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
164
|
+
background: "rgba(28, 28, 30, 0.92)",
|
|
165
|
+
cursor: "pointer",
|
|
166
|
+
display: "flex",
|
|
167
|
+
alignItems: "center",
|
|
168
|
+
gap: 6,
|
|
169
|
+
padding: "0 10px 0 8px",
|
|
170
|
+
backdropFilter: "blur(20px)",
|
|
171
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
172
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.4), inset 0 0.5px 0 rgba(255, 255, 255, 0.06)"
|
|
173
|
+
},
|
|
174
|
+
toggleDot: {
|
|
175
|
+
width: 6,
|
|
176
|
+
height: 6,
|
|
177
|
+
borderRadius: "50%",
|
|
178
|
+
flexShrink: 0
|
|
179
|
+
},
|
|
180
|
+
toggleFps: {
|
|
181
|
+
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
|
182
|
+
fontSize: 11,
|
|
183
|
+
fontWeight: 600,
|
|
184
|
+
color: "rgba(255, 255, 255, 0.85)",
|
|
185
|
+
letterSpacing: -0.3,
|
|
186
|
+
lineHeight: 1
|
|
187
|
+
},
|
|
188
|
+
toggleLabel: {
|
|
189
|
+
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
|
190
|
+
fontSize: 9,
|
|
191
|
+
fontWeight: 500,
|
|
192
|
+
color: "rgba(255, 255, 255, 0.35)",
|
|
193
|
+
letterSpacing: 0,
|
|
194
|
+
textTransform: "lowercase"
|
|
195
|
+
},
|
|
196
|
+
footer: {
|
|
197
|
+
marginTop: 8,
|
|
198
|
+
color: "#333",
|
|
199
|
+
fontSize: 8,
|
|
200
|
+
textAlign: "center"
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var FLASH_OUTLINE = "2px solid rgba(99, 102, 241, 0.8)";
|
|
204
|
+
|
|
205
|
+
// src/hooks.ts
|
|
206
|
+
function getEffectiveRect(el) {
|
|
207
|
+
const rect = el.getBoundingClientRect();
|
|
208
|
+
if (rect.width > 0 || rect.height > 0) return rect;
|
|
209
|
+
const children = el.children;
|
|
210
|
+
if (children.length === 0) return rect;
|
|
211
|
+
let top = Infinity, left = Infinity, bottom = -Infinity, right = -Infinity;
|
|
212
|
+
for (let i = 0; i < children.length; i++) {
|
|
213
|
+
const cr = children[i].getBoundingClientRect();
|
|
214
|
+
if (cr.width === 0 && cr.height === 0) continue;
|
|
215
|
+
top = Math.min(top, cr.top);
|
|
216
|
+
left = Math.min(left, cr.left);
|
|
217
|
+
bottom = Math.max(bottom, cr.bottom);
|
|
218
|
+
right = Math.max(right, cr.right);
|
|
219
|
+
}
|
|
220
|
+
if (top === Infinity) return rect;
|
|
221
|
+
return new DOMRect(left, top, right - left, bottom - top);
|
|
222
|
+
}
|
|
223
|
+
function getObservableChildren(el) {
|
|
224
|
+
const result = [];
|
|
225
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
226
|
+
const child = el.children[i];
|
|
227
|
+
const r = child.getBoundingClientRect();
|
|
228
|
+
if (r.width > 0 || r.height > 0) result.push(child);
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
40
232
|
function useAnchorPosition(ref, position = "bottom-left") {
|
|
41
233
|
const [pos, setPos] = useState({ top: 0, left: 0 });
|
|
42
234
|
useEffect(() => {
|
|
43
235
|
if (!ref.current) return;
|
|
236
|
+
const el = ref.current;
|
|
44
237
|
const update = () => {
|
|
45
|
-
|
|
46
|
-
const rect = ref.current.getBoundingClientRect();
|
|
47
|
-
const margin = 8;
|
|
238
|
+
const rect = getEffectiveRect(el);
|
|
48
239
|
switch (position) {
|
|
49
240
|
case "top-left":
|
|
50
|
-
setPos({ top: rect.top
|
|
241
|
+
setPos({ top: rect.top, left: rect.left });
|
|
51
242
|
break;
|
|
52
243
|
case "top-right":
|
|
53
|
-
setPos({ top: rect.top
|
|
244
|
+
setPos({ top: rect.top, left: rect.right });
|
|
54
245
|
break;
|
|
55
246
|
case "bottom-right":
|
|
56
|
-
setPos({ top: rect.bottom
|
|
247
|
+
setPos({ top: rect.bottom, left: rect.right });
|
|
57
248
|
break;
|
|
58
249
|
case "bottom-left":
|
|
59
250
|
default:
|
|
60
|
-
setPos({ top: rect.bottom
|
|
251
|
+
setPos({ top: rect.bottom, left: rect.left });
|
|
61
252
|
break;
|
|
62
253
|
}
|
|
63
254
|
};
|
|
255
|
+
update();
|
|
64
256
|
const observer = new ResizeObserver(update);
|
|
65
|
-
observer.observe(
|
|
257
|
+
observer.observe(el);
|
|
258
|
+
for (const child of getObservableChildren(el)) observer.observe(child);
|
|
66
259
|
observer.observe(document.documentElement);
|
|
67
260
|
return () => observer.disconnect();
|
|
68
261
|
}, [ref, position]);
|
|
@@ -73,9 +266,10 @@ function useDraggable() {
|
|
|
73
266
|
const dragging = useRef(false);
|
|
74
267
|
const start = useRef({ x: 0, y: 0 });
|
|
75
268
|
const onPointerDown = useCallback((e) => {
|
|
269
|
+
if (e.target.closest("button, a, [data-no-drag]")) return;
|
|
76
270
|
dragging.current = true;
|
|
77
271
|
start.current = { x: e.clientX - offset.x, y: e.clientY - offset.y };
|
|
78
|
-
e.
|
|
272
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
79
273
|
}, [offset]);
|
|
80
274
|
const onPointerMove = useCallback((e) => {
|
|
81
275
|
if (!dragging.current) return;
|
|
@@ -95,21 +289,13 @@ function useDomTracker(wrapperRef, enabled) {
|
|
|
95
289
|
const dirty = useRef(true);
|
|
96
290
|
useEffect(() => {
|
|
97
291
|
if (!enabled || !wrapperRef.current) return;
|
|
98
|
-
const observer = new MutationObserver((
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (record.type === "attributes" && record.attributeName === "class") continue;
|
|
102
|
-
realMutation = true;
|
|
103
|
-
}
|
|
104
|
-
if (realMutation) {
|
|
105
|
-
mutations.current++;
|
|
106
|
-
dirty.current = true;
|
|
107
|
-
}
|
|
292
|
+
const observer = new MutationObserver(() => {
|
|
293
|
+
mutations.current++;
|
|
294
|
+
dirty.current = true;
|
|
108
295
|
});
|
|
109
296
|
observer.observe(wrapperRef.current, {
|
|
110
297
|
childList: true,
|
|
111
298
|
subtree: true,
|
|
112
|
-
attributes: true,
|
|
113
299
|
attributeFilter: ["style"]
|
|
114
300
|
});
|
|
115
301
|
return () => observer.disconnect();
|
|
@@ -150,14 +336,19 @@ function useRenderFlash(wrapperRef, open) {
|
|
|
150
336
|
const mutationCount = useRef(0);
|
|
151
337
|
useEffect(() => {
|
|
152
338
|
if (!open || !wrapperRef.current) return;
|
|
339
|
+
const wrapper = wrapperRef.current;
|
|
153
340
|
const observer = new MutationObserver(() => {
|
|
154
341
|
mutationCount.current++;
|
|
155
|
-
if (
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
342
|
+
if (mutationCount.current <= 1) return;
|
|
343
|
+
const target = getObservableChildren(wrapper)[0] ?? wrapper;
|
|
344
|
+
target.style.outline = FLASH_OUTLINE;
|
|
345
|
+
target.style.outlineOffset = "-2px";
|
|
346
|
+
setTimeout(() => {
|
|
347
|
+
target.style.outline = "";
|
|
348
|
+
target.style.outlineOffset = "";
|
|
349
|
+
}, 150);
|
|
159
350
|
});
|
|
160
|
-
observer.observe(
|
|
351
|
+
observer.observe(wrapper, { childList: true, subtree: true });
|
|
161
352
|
return () => observer.disconnect();
|
|
162
353
|
}, [wrapperRef, open]);
|
|
163
354
|
}
|
|
@@ -189,7 +380,7 @@ function FrameTimeGraph({ history }) {
|
|
|
189
380
|
const w = 140;
|
|
190
381
|
const h = 32;
|
|
191
382
|
const barW = Math.max(1, w / HISTORY_SIZE - 0.5);
|
|
192
|
-
return /* @__PURE__ */ jsx("div", {
|
|
383
|
+
return /* @__PURE__ */ jsx("div", { style: s.graphWrap, children: /* @__PURE__ */ jsxs("svg", { width: w, height: h, style: { display: "block" }, children: [
|
|
193
384
|
/* @__PURE__ */ jsx("rect", { width: w, height: h, rx: 3, fill: "#111" }),
|
|
194
385
|
/* @__PURE__ */ jsx(
|
|
195
386
|
"line",
|
|
@@ -222,27 +413,29 @@ function FrameTimeGraph({ history }) {
|
|
|
222
413
|
] }) });
|
|
223
414
|
}
|
|
224
415
|
function StatRow({ label, value, sub, color = "#4ade80" }) {
|
|
225
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
226
|
-
/* @__PURE__ */ jsx("span", {
|
|
416
|
+
return /* @__PURE__ */ jsxs("div", { style: s.row, children: [
|
|
417
|
+
/* @__PURE__ */ jsx("span", { style: s.rowLabel, children: label }),
|
|
227
418
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
228
|
-
/* @__PURE__ */ jsx("span", {
|
|
419
|
+
/* @__PURE__ */ jsx("span", { style: { ...s.rowValue, color }, children: value }),
|
|
229
420
|
sub && /* @__PURE__ */ jsx("span", { style: { color: "#444", fontSize: 9, marginLeft: 4 }, children: sub })
|
|
230
421
|
] })
|
|
231
422
|
] });
|
|
232
423
|
}
|
|
424
|
+
var GAP = 8;
|
|
233
425
|
function getPanelStyle(pos, offset, position) {
|
|
234
426
|
const style = {
|
|
427
|
+
...s.panel,
|
|
235
428
|
transform: `translate(${offset.x}px, ${offset.y}px)`
|
|
236
429
|
};
|
|
237
430
|
if (position.startsWith("bottom")) {
|
|
238
|
-
style.bottom = window.innerHeight - pos.top;
|
|
431
|
+
style.bottom = window.innerHeight - pos.top + GAP;
|
|
239
432
|
} else {
|
|
240
|
-
style.top = pos.top;
|
|
433
|
+
style.top = pos.top + GAP;
|
|
241
434
|
}
|
|
242
435
|
if (position.endsWith("right")) {
|
|
243
|
-
style.right = window.innerWidth - pos.left;
|
|
436
|
+
style.right = window.innerWidth - pos.left + GAP;
|
|
244
437
|
} else {
|
|
245
|
-
style.left = pos.left;
|
|
438
|
+
style.left = pos.left + GAP;
|
|
246
439
|
}
|
|
247
440
|
return style;
|
|
248
441
|
}
|
|
@@ -284,7 +477,8 @@ function DevStatsPanel({
|
|
|
284
477
|
allFrameTimes.current.push(avgFrameTime);
|
|
285
478
|
const sorted = [...allFrameTimes.current].sort((a, b) => a - b);
|
|
286
479
|
const el = targetRef.current;
|
|
287
|
-
const
|
|
480
|
+
const r = el ? getEffectiveRect(el) : null;
|
|
481
|
+
const dims = r ? `${Math.round(r.width)} x ${Math.round(r.height)}` : "\u2013";
|
|
288
482
|
const perf = performance;
|
|
289
483
|
const mem = perf.memory ? Math.round(perf.memory.usedJSHeapSize / 1024 / 1024) : 0;
|
|
290
484
|
setStats({
|
|
@@ -319,23 +513,17 @@ function DevStatsPanel({
|
|
|
319
513
|
const handleExport = useCallback2(() => {
|
|
320
514
|
const payload = { timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...stats };
|
|
321
515
|
const json = JSON.stringify(payload, null, 2);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
ta.select();
|
|
334
|
-
document.execCommand("copy");
|
|
335
|
-
document.body.removeChild(ta);
|
|
336
|
-
setExported(true);
|
|
337
|
-
setTimeout(() => setExported(false), 1200);
|
|
338
|
-
}
|
|
516
|
+
const blob = new Blob([json], { type: "application/json" });
|
|
517
|
+
const url = URL.createObjectURL(blob);
|
|
518
|
+
const a = document.createElement("a");
|
|
519
|
+
a.href = url;
|
|
520
|
+
a.download = `devprofiler-${Date.now()}.json`;
|
|
521
|
+
document.body.appendChild(a);
|
|
522
|
+
a.click();
|
|
523
|
+
document.body.removeChild(a);
|
|
524
|
+
URL.revokeObjectURL(url);
|
|
525
|
+
setExported(true);
|
|
526
|
+
setTimeout(() => setExported(false), 1200);
|
|
339
527
|
}, [stats]);
|
|
340
528
|
const ftColor = stats.frameTime > 33 ? "#ef4444" : stats.frameTime > 16.67 ? "#f59e0b" : "#4ade80";
|
|
341
529
|
const rpsColor = stats.rendersPerSecond > 30 ? "#ef4444" : stats.rendersPerSecond > 10 ? "#f59e0b" : "#4ade80";
|
|
@@ -343,42 +531,33 @@ function DevStatsPanel({
|
|
|
343
531
|
const fps = stats.frameTime > 0 ? Math.round(1e3 / stats.frameTime) : 0;
|
|
344
532
|
const memoGain = stats.profiler.baseDuration > 0 ? Math.round((1 - stats.profiler.actualDuration / stats.profiler.baseDuration) * 100) : 0;
|
|
345
533
|
const p99Color = stats.frameTimeP99 > 33 ? "#ef4444" : stats.frameTimeP99 > 16.67 ? "#f59e0b" : "#4ade80";
|
|
534
|
+
const exportStyle = exported ? { ...s.iconBtn, ...s.iconBtnActive } : s.iconBtn;
|
|
346
535
|
return createPortal(
|
|
347
|
-
/* @__PURE__ */ jsxs("div", {
|
|
348
|
-
/* @__PURE__ */ jsxs(
|
|
349
|
-
"
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
),
|
|
371
|
-
/* @__PURE__ */ jsx("button", { className: DevProfiler_default.resetBtn, onClick: handleReset, title: "Reset counters", children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M14 1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4a1 1 0 0 1 0-2h1.6A6 6 0 0 0 2.07 7.5a1 1 0 1 1-1.97-.36A8 8 0 0 1 13 3.35V2a1 1 0 0 1 1-1zM2 15a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h4a1 1 0 0 1 0 2H4.4A6 6 0 0 0 13.93 8.5a1 1 0 1 1 1.97.36A8 8 0 0 1 3 12.65V14a1 1 0 0 1-1 1z" }) }) }),
|
|
372
|
-
/* @__PURE__ */ jsx("button", { className: DevProfiler_default.closeBtn, onClick: onClose, children: "\u2715" })
|
|
373
|
-
] })
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
),
|
|
377
|
-
/* @__PURE__ */ jsxs("div", { className: DevProfiler_default.body, children: [
|
|
378
|
-
/* @__PURE__ */ jsx("span", { className: DevProfiler_default.section, children: "Rendering" }),
|
|
536
|
+
/* @__PURE__ */ jsxs("div", { style: getPanelStyle(pos, offset, position), ...dragHandlers, children: [
|
|
537
|
+
/* @__PURE__ */ jsxs("div", { style: s.panelHeader, children: [
|
|
538
|
+
/* @__PURE__ */ jsxs("span", { style: s.panelTitle, children: [
|
|
539
|
+
"Dev Profiler",
|
|
540
|
+
instanceCount > 1 && instanceId && /* @__PURE__ */ jsx("span", { style: s.instanceBadge, children: instanceId })
|
|
541
|
+
] }),
|
|
542
|
+
/* @__PURE__ */ jsxs("div", { style: s.headerActions, children: [
|
|
543
|
+
/* @__PURE__ */ jsx(
|
|
544
|
+
"button",
|
|
545
|
+
{
|
|
546
|
+
style: exportStyle,
|
|
547
|
+
onClick: handleExport,
|
|
548
|
+
title: exported ? "Exported!" : "Export stats as JSON",
|
|
549
|
+
children: exported ? /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "3.5 8.5 6.5 11.5 12.5 4.5" }) }) : /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M8 2v8M4 7l4 4 4-4M2 14h12" }) })
|
|
550
|
+
}
|
|
551
|
+
),
|
|
552
|
+
/* @__PURE__ */ jsx("button", { style: s.iconBtn, onClick: handleReset, title: "Reset counters", children: /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M14 1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4a1 1 0 0 1 0-2h1.6A6 6 0 0 0 2.07 7.5a1 1 0 1 1-1.97-.36A8 8 0 0 1 13 3.35V2a1 1 0 0 1 1-1zM2 15a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h4a1 1 0 0 1 0 2H4.4A6 6 0 0 0 13.93 8.5a1 1 0 1 1 1.97.36A8 8 0 0 1 3 12.65V14a1 1 0 0 1-1 1z" }) }) }),
|
|
553
|
+
/* @__PURE__ */ jsx("button", { style: s.closeBtn, onClick: onClose, children: "\u2715" })
|
|
554
|
+
] })
|
|
555
|
+
] }),
|
|
556
|
+
/* @__PURE__ */ jsxs("div", { style: s.body, children: [
|
|
557
|
+
/* @__PURE__ */ jsx("span", { style: s.section, children: "Rendering" }),
|
|
379
558
|
/* @__PURE__ */ jsx(StatRow, { label: "Frame time", value: `${stats.frameTime.toFixed(1)}ms`, sub: `${fps} fps`, color: ftColor }),
|
|
380
559
|
/* @__PURE__ */ jsx(FrameTimeGraph, { history: stats.frameTimeHistory }),
|
|
381
|
-
/* @__PURE__ */ jsxs("div", {
|
|
560
|
+
/* @__PURE__ */ jsxs("div", { style: s.miniRow, children: [
|
|
382
561
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
383
562
|
"min ",
|
|
384
563
|
stats.frameTimeMin.toFixed(1)
|
|
@@ -394,61 +573,61 @@ function DevStatsPanel({
|
|
|
394
573
|
] }),
|
|
395
574
|
/* @__PURE__ */ jsx(StatRow, { label: "Renders/s", value: String(stats.rendersPerSecond), color: rpsColor }),
|
|
396
575
|
/* @__PURE__ */ jsx(StatRow, { label: "Long tasks", value: String(stats.longTasks), color: stats.longTasks > 0 ? "#f59e0b" : "#4ade80" }),
|
|
397
|
-
/* @__PURE__ */ jsx("div", {
|
|
398
|
-
/* @__PURE__ */ jsx("span", {
|
|
576
|
+
/* @__PURE__ */ jsx("div", { style: s.separator }),
|
|
577
|
+
/* @__PURE__ */ jsx("span", { style: s.section, children: "React Profiler" }),
|
|
399
578
|
/* @__PURE__ */ jsx(StatRow, { label: "Phase", value: stats.profiler.phase, color: "#888" }),
|
|
400
579
|
/* @__PURE__ */ jsx(StatRow, { label: "Render", value: `${stats.profiler.actualDuration.toFixed(2)}ms`, color: actualColor }),
|
|
401
580
|
/* @__PURE__ */ jsx(StatRow, { label: "Base (no memo)", value: `${stats.profiler.baseDuration.toFixed(2)}ms`, color: "#888" }),
|
|
402
581
|
/* @__PURE__ */ jsx(StatRow, { label: "Memo gain", value: `${memoGain}%`, color: memoGain > 50 ? "#4ade80" : memoGain > 20 ? "#f59e0b" : "#ef4444" }),
|
|
403
582
|
/* @__PURE__ */ jsx(StatRow, { label: "Commits", value: String(stats.profiler.commitCount) }),
|
|
404
|
-
/* @__PURE__ */ jsx("div", {
|
|
405
|
-
/* @__PURE__ */ jsx("span", {
|
|
583
|
+
/* @__PURE__ */ jsx("div", { style: s.separator }),
|
|
584
|
+
/* @__PURE__ */ jsx("span", { style: s.section, children: "DOM" }),
|
|
406
585
|
/* @__PURE__ */ jsx(StatRow, { label: "Nodes", value: stats.domNodes.toLocaleString() }),
|
|
407
586
|
/* @__PURE__ */ jsx(StatRow, { label: "Mutations", value: String(stats.domMutations) }),
|
|
408
587
|
/* @__PURE__ */ jsx(StatRow, { label: "Size", value: stats.dimensions, color: "#888" }),
|
|
409
|
-
/* @__PURE__ */ jsx("div", {
|
|
410
|
-
/* @__PURE__ */ jsx("span", {
|
|
588
|
+
/* @__PURE__ */ jsx("div", { style: s.separator }),
|
|
589
|
+
/* @__PURE__ */ jsx("span", { style: s.section, children: "Memory" }),
|
|
411
590
|
/* @__PURE__ */ jsx(StatRow, { label: "JS Heap", value: stats.memory > 0 ? `${stats.memory} MB` : "N/A" })
|
|
412
591
|
] }),
|
|
413
|
-
/* @__PURE__ */ jsx("div", {
|
|
592
|
+
/* @__PURE__ */ jsx("div", { style: s.footer, children: "Ctrl+I to toggle" })
|
|
414
593
|
] }),
|
|
415
594
|
document.body
|
|
416
595
|
);
|
|
417
596
|
}
|
|
418
597
|
|
|
419
598
|
// src/ToggleButton.tsx
|
|
420
|
-
import { useState as useState3, useEffect as useEffect3
|
|
599
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
421
600
|
import { createPortal as createPortal2 } from "react-dom";
|
|
422
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
601
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
602
|
+
var GAP2 = 8;
|
|
423
603
|
function getButtonStyle(pos, position) {
|
|
424
|
-
const style = {};
|
|
604
|
+
const style = { ...s.toggleBtn };
|
|
425
605
|
if (position.startsWith("bottom")) {
|
|
426
|
-
style.bottom = window.innerHeight - pos.top +
|
|
606
|
+
style.bottom = window.innerHeight - pos.top + GAP2;
|
|
427
607
|
} else {
|
|
428
|
-
style.top = pos.top +
|
|
608
|
+
style.top = pos.top + GAP2;
|
|
429
609
|
}
|
|
430
610
|
if (position.endsWith("right")) {
|
|
431
|
-
style.right = window.innerWidth - pos.left;
|
|
611
|
+
style.right = window.innerWidth - pos.left + GAP2;
|
|
432
612
|
} else {
|
|
433
|
-
style.left = pos.left;
|
|
613
|
+
style.left = pos.left + GAP2;
|
|
434
614
|
}
|
|
435
615
|
return style;
|
|
436
616
|
}
|
|
437
617
|
function ToggleButton({
|
|
438
618
|
targetRef,
|
|
439
619
|
onClick,
|
|
440
|
-
position = "bottom-left"
|
|
620
|
+
position = "bottom-left",
|
|
621
|
+
accentColor = "#6366f1"
|
|
441
622
|
}) {
|
|
442
623
|
const pos = useAnchorPosition(targetRef, position);
|
|
443
624
|
const [fps, setFps] = useState3(0);
|
|
444
|
-
const lastFrame = useRef3(performance.now());
|
|
445
625
|
useEffect3(() => {
|
|
446
626
|
let animId;
|
|
447
627
|
let count = 0;
|
|
448
628
|
let lastSecond = performance.now();
|
|
449
629
|
const tick = () => {
|
|
450
630
|
const now = performance.now();
|
|
451
|
-
lastFrame.current = now;
|
|
452
631
|
count++;
|
|
453
632
|
if (now - lastSecond >= 1e3) {
|
|
454
633
|
setFps(count);
|
|
@@ -460,16 +639,18 @@ function ToggleButton({
|
|
|
460
639
|
animId = requestAnimationFrame(tick);
|
|
461
640
|
return () => cancelAnimationFrame(animId);
|
|
462
641
|
}, []);
|
|
463
|
-
const fpsColor = fps < 30 ? "#ef4444" : fps < 55 ? "#f59e0b" : "#4ade80";
|
|
464
642
|
return createPortal2(
|
|
465
|
-
/* @__PURE__ */
|
|
643
|
+
/* @__PURE__ */ jsxs2(
|
|
466
644
|
"button",
|
|
467
645
|
{
|
|
468
|
-
className: DevProfiler_default.toggleBtn,
|
|
469
646
|
onClick,
|
|
470
647
|
title: "Dev Profiler (Ctrl+I)",
|
|
471
648
|
style: getButtonStyle(pos, position),
|
|
472
|
-
children:
|
|
649
|
+
children: [
|
|
650
|
+
/* @__PURE__ */ jsx2("span", { style: { ...s.toggleDot, background: accentColor, boxShadow: `0 0 4px ${accentColor}` } }),
|
|
651
|
+
/* @__PURE__ */ jsx2("span", { style: s.toggleFps, children: fps }),
|
|
652
|
+
/* @__PURE__ */ jsx2("span", { style: s.toggleLabel, children: "fps" })
|
|
653
|
+
]
|
|
473
654
|
}
|
|
474
655
|
),
|
|
475
656
|
document.body
|
|
@@ -477,7 +658,7 @@ function ToggleButton({
|
|
|
477
658
|
}
|
|
478
659
|
|
|
479
660
|
// src/DevProfiler.tsx
|
|
480
|
-
import { Fragment, jsx as jsx3, jsxs as
|
|
661
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
481
662
|
var TOGGLE_EVENT = "devprofiler:toggle";
|
|
482
663
|
var BOUND_KEY = "__devprofiler_bound";
|
|
483
664
|
if (typeof window !== "undefined" && __DEV__ && !window[BOUND_KEY]) {
|
|
@@ -494,12 +675,13 @@ var activeInstances = /* @__PURE__ */ new Set();
|
|
|
494
675
|
function DevProfiler({
|
|
495
676
|
children,
|
|
496
677
|
position = "bottom-left",
|
|
497
|
-
id
|
|
678
|
+
id,
|
|
679
|
+
accentColor = "#6366f1"
|
|
498
680
|
}) {
|
|
499
|
-
const wrapperRef =
|
|
681
|
+
const wrapperRef = useRef3(null);
|
|
500
682
|
const [open, setOpen] = useState4(false);
|
|
501
683
|
const toggle = useCallback3(() => setOpen((prev) => !prev), []);
|
|
502
|
-
const instanceId =
|
|
684
|
+
const instanceId = useRef3(id ?? `profiler-${++instanceCounter}`).current;
|
|
503
685
|
useEffect4(() => {
|
|
504
686
|
activeInstances.add(instanceId);
|
|
505
687
|
return () => {
|
|
@@ -510,7 +692,7 @@ function DevProfiler({
|
|
|
510
692
|
const renderRate = useRenderRate();
|
|
511
693
|
const longTasks = useLongTasks(open);
|
|
512
694
|
useRenderFlash(wrapperRef, open);
|
|
513
|
-
const profilerData =
|
|
695
|
+
const profilerData = useRef3({ ...INITIAL_PROFILER });
|
|
514
696
|
const onRender = useCallback3((_id, phase, actualDuration, baseDuration) => {
|
|
515
697
|
profilerData.current = {
|
|
516
698
|
phase,
|
|
@@ -532,9 +714,9 @@ function DevProfiler({
|
|
|
532
714
|
return () => window.removeEventListener(TOGGLE_EVENT, handler);
|
|
533
715
|
}, []);
|
|
534
716
|
if (!__DEV__) return /* @__PURE__ */ jsx3(Fragment, { children });
|
|
535
|
-
return /* @__PURE__ */
|
|
717
|
+
return /* @__PURE__ */ jsxs3("div", { ref: wrapperRef, style: { display: "contents" }, children: [
|
|
536
718
|
/* @__PURE__ */ jsx3(Profiler, { id: "DevProfiler", onRender, children }),
|
|
537
|
-
!open && /* @__PURE__ */ jsx3(ToggleButton, { targetRef: wrapperRef, onClick: toggle, position }),
|
|
719
|
+
!open && /* @__PURE__ */ jsx3(ToggleButton, { targetRef: wrapperRef, onClick: toggle, position, accentColor }),
|
|
538
720
|
open && /* @__PURE__ */ jsx3(
|
|
539
721
|
DevStatsPanel,
|
|
540
722
|
{
|
|
@@ -567,6 +749,12 @@ export {
|
|
|
567
749
|
* @author Frederic Denis (billywild87) — https://github.com/billywild87
|
|
568
750
|
* @license MIT
|
|
569
751
|
*/
|
|
752
|
+
/**
|
|
753
|
+
* @module react-dev-profiler
|
|
754
|
+
* @description Inline styles for the profiler UI — no external CSS needed.
|
|
755
|
+
* @author Frederic Denis (billywild87) — https://github.com/billywild87
|
|
756
|
+
* @license MIT
|
|
757
|
+
*/
|
|
570
758
|
/**
|
|
571
759
|
* @module react-dev-profiler
|
|
572
760
|
* @description Custom hooks that power the profiler's data collection.
|