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.cjs
CHANGED
|
@@ -27,9 +27,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
27
|
// src/DevProfiler.tsx
|
|
28
28
|
var import_react4 = require("react");
|
|
29
29
|
|
|
30
|
-
// src/DevProfiler.module.css
|
|
31
|
-
var DevProfiler_default = {};
|
|
32
|
-
|
|
33
30
|
// src/types.ts
|
|
34
31
|
var HISTORY_SIZE = 60;
|
|
35
32
|
var INITIAL_PROFILER = {
|
|
@@ -63,32 +60,228 @@ var __DEV__ = typeof process !== "undefined" ? process.env.NODE_ENV !== "product
|
|
|
63
60
|
|
|
64
61
|
// src/hooks.ts
|
|
65
62
|
var import_react = require("react");
|
|
63
|
+
|
|
64
|
+
// src/styles.ts
|
|
65
|
+
var s = {
|
|
66
|
+
wrapper: {
|
|
67
|
+
display: "contents"
|
|
68
|
+
},
|
|
69
|
+
panel: {
|
|
70
|
+
position: "fixed",
|
|
71
|
+
zIndex: 99999,
|
|
72
|
+
background: "rgba(8, 8, 8, 0.95)",
|
|
73
|
+
border: "1px solid #222",
|
|
74
|
+
borderRadius: 10,
|
|
75
|
+
padding: "10px 14px",
|
|
76
|
+
minWidth: 220,
|
|
77
|
+
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.7)",
|
|
78
|
+
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
|
79
|
+
backdropFilter: "blur(12px)",
|
|
80
|
+
userSelect: "none",
|
|
81
|
+
color: "#ccc",
|
|
82
|
+
fontSize: 11,
|
|
83
|
+
cursor: "grab",
|
|
84
|
+
touchAction: "none"
|
|
85
|
+
},
|
|
86
|
+
panelHeader: {
|
|
87
|
+
display: "flex",
|
|
88
|
+
justifyContent: "space-between",
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
marginBottom: 8
|
|
91
|
+
},
|
|
92
|
+
panelTitle: {
|
|
93
|
+
color: "#666",
|
|
94
|
+
fontSize: 9,
|
|
95
|
+
fontWeight: 700,
|
|
96
|
+
letterSpacing: 1.5,
|
|
97
|
+
textTransform: "uppercase",
|
|
98
|
+
display: "flex",
|
|
99
|
+
alignItems: "center",
|
|
100
|
+
gap: 6
|
|
101
|
+
},
|
|
102
|
+
instanceBadge: {
|
|
103
|
+
background: "#222",
|
|
104
|
+
color: "#888",
|
|
105
|
+
fontSize: 8,
|
|
106
|
+
padding: "1px 5px",
|
|
107
|
+
borderRadius: 4,
|
|
108
|
+
letterSpacing: 0.5,
|
|
109
|
+
textTransform: "none"
|
|
110
|
+
},
|
|
111
|
+
headerActions: {
|
|
112
|
+
display: "flex",
|
|
113
|
+
alignItems: "center",
|
|
114
|
+
gap: 8
|
|
115
|
+
},
|
|
116
|
+
iconBtn: {
|
|
117
|
+
background: "none",
|
|
118
|
+
border: "none",
|
|
119
|
+
color: "#444",
|
|
120
|
+
cursor: "pointer",
|
|
121
|
+
padding: 4,
|
|
122
|
+
margin: -4,
|
|
123
|
+
lineHeight: 1,
|
|
124
|
+
display: "flex",
|
|
125
|
+
alignItems: "center",
|
|
126
|
+
transition: "color 0.15s"
|
|
127
|
+
},
|
|
128
|
+
iconBtnActive: {
|
|
129
|
+
color: "#4ade80"
|
|
130
|
+
},
|
|
131
|
+
closeBtn: {
|
|
132
|
+
background: "none",
|
|
133
|
+
border: "none",
|
|
134
|
+
color: "#444",
|
|
135
|
+
cursor: "pointer",
|
|
136
|
+
fontSize: 13,
|
|
137
|
+
padding: 4,
|
|
138
|
+
margin: -4,
|
|
139
|
+
lineHeight: 1
|
|
140
|
+
},
|
|
141
|
+
body: {
|
|
142
|
+
display: "flex",
|
|
143
|
+
flexDirection: "column",
|
|
144
|
+
gap: 5
|
|
145
|
+
},
|
|
146
|
+
section: {
|
|
147
|
+
color: "#444",
|
|
148
|
+
fontSize: 8,
|
|
149
|
+
fontWeight: 600,
|
|
150
|
+
letterSpacing: 1,
|
|
151
|
+
textTransform: "uppercase",
|
|
152
|
+
marginTop: 2
|
|
153
|
+
},
|
|
154
|
+
separator: {
|
|
155
|
+
height: 1,
|
|
156
|
+
background: "#1a1a1a",
|
|
157
|
+
margin: "2px 0"
|
|
158
|
+
},
|
|
159
|
+
row: {
|
|
160
|
+
display: "flex",
|
|
161
|
+
justifyContent: "space-between",
|
|
162
|
+
alignItems: "center",
|
|
163
|
+
padding: "1px 0"
|
|
164
|
+
},
|
|
165
|
+
rowLabel: {
|
|
166
|
+
color: "#555",
|
|
167
|
+
fontSize: 10
|
|
168
|
+
},
|
|
169
|
+
rowValue: {
|
|
170
|
+
fontSize: 11,
|
|
171
|
+
fontWeight: 600
|
|
172
|
+
},
|
|
173
|
+
miniRow: {
|
|
174
|
+
display: "flex",
|
|
175
|
+
justifyContent: "space-between",
|
|
176
|
+
color: "#444",
|
|
177
|
+
fontSize: 9,
|
|
178
|
+
marginTop: -2,
|
|
179
|
+
marginBottom: 2
|
|
180
|
+
},
|
|
181
|
+
graphWrap: {
|
|
182
|
+
marginTop: 4
|
|
183
|
+
},
|
|
184
|
+
toggleBtn: {
|
|
185
|
+
position: "fixed",
|
|
186
|
+
zIndex: 99998,
|
|
187
|
+
height: 26,
|
|
188
|
+
borderRadius: 13,
|
|
189
|
+
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
190
|
+
background: "rgba(28, 28, 30, 0.92)",
|
|
191
|
+
cursor: "pointer",
|
|
192
|
+
display: "flex",
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
gap: 6,
|
|
195
|
+
padding: "0 10px 0 8px",
|
|
196
|
+
backdropFilter: "blur(20px)",
|
|
197
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
198
|
+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.4), inset 0 0.5px 0 rgba(255, 255, 255, 0.06)"
|
|
199
|
+
},
|
|
200
|
+
toggleDot: {
|
|
201
|
+
width: 6,
|
|
202
|
+
height: 6,
|
|
203
|
+
borderRadius: "50%",
|
|
204
|
+
flexShrink: 0
|
|
205
|
+
},
|
|
206
|
+
toggleFps: {
|
|
207
|
+
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
|
208
|
+
fontSize: 11,
|
|
209
|
+
fontWeight: 600,
|
|
210
|
+
color: "rgba(255, 255, 255, 0.85)",
|
|
211
|
+
letterSpacing: -0.3,
|
|
212
|
+
lineHeight: 1
|
|
213
|
+
},
|
|
214
|
+
toggleLabel: {
|
|
215
|
+
fontFamily: "'SF Mono', 'Fira Code', monospace",
|
|
216
|
+
fontSize: 9,
|
|
217
|
+
fontWeight: 500,
|
|
218
|
+
color: "rgba(255, 255, 255, 0.35)",
|
|
219
|
+
letterSpacing: 0,
|
|
220
|
+
textTransform: "lowercase"
|
|
221
|
+
},
|
|
222
|
+
footer: {
|
|
223
|
+
marginTop: 8,
|
|
224
|
+
color: "#333",
|
|
225
|
+
fontSize: 8,
|
|
226
|
+
textAlign: "center"
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var FLASH_OUTLINE = "2px solid rgba(99, 102, 241, 0.8)";
|
|
230
|
+
|
|
231
|
+
// src/hooks.ts
|
|
232
|
+
function getEffectiveRect(el) {
|
|
233
|
+
const rect = el.getBoundingClientRect();
|
|
234
|
+
if (rect.width > 0 || rect.height > 0) return rect;
|
|
235
|
+
const children = el.children;
|
|
236
|
+
if (children.length === 0) return rect;
|
|
237
|
+
let top = Infinity, left = Infinity, bottom = -Infinity, right = -Infinity;
|
|
238
|
+
for (let i = 0; i < children.length; i++) {
|
|
239
|
+
const cr = children[i].getBoundingClientRect();
|
|
240
|
+
if (cr.width === 0 && cr.height === 0) continue;
|
|
241
|
+
top = Math.min(top, cr.top);
|
|
242
|
+
left = Math.min(left, cr.left);
|
|
243
|
+
bottom = Math.max(bottom, cr.bottom);
|
|
244
|
+
right = Math.max(right, cr.right);
|
|
245
|
+
}
|
|
246
|
+
if (top === Infinity) return rect;
|
|
247
|
+
return new DOMRect(left, top, right - left, bottom - top);
|
|
248
|
+
}
|
|
249
|
+
function getObservableChildren(el) {
|
|
250
|
+
const result = [];
|
|
251
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
252
|
+
const child = el.children[i];
|
|
253
|
+
const r = child.getBoundingClientRect();
|
|
254
|
+
if (r.width > 0 || r.height > 0) result.push(child);
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
66
258
|
function useAnchorPosition(ref, position = "bottom-left") {
|
|
67
259
|
const [pos, setPos] = (0, import_react.useState)({ top: 0, left: 0 });
|
|
68
260
|
(0, import_react.useEffect)(() => {
|
|
69
261
|
if (!ref.current) return;
|
|
262
|
+
const el = ref.current;
|
|
70
263
|
const update = () => {
|
|
71
|
-
|
|
72
|
-
const rect = ref.current.getBoundingClientRect();
|
|
73
|
-
const margin = 8;
|
|
264
|
+
const rect = getEffectiveRect(el);
|
|
74
265
|
switch (position) {
|
|
75
266
|
case "top-left":
|
|
76
|
-
setPos({ top: rect.top
|
|
267
|
+
setPos({ top: rect.top, left: rect.left });
|
|
77
268
|
break;
|
|
78
269
|
case "top-right":
|
|
79
|
-
setPos({ top: rect.top
|
|
270
|
+
setPos({ top: rect.top, left: rect.right });
|
|
80
271
|
break;
|
|
81
272
|
case "bottom-right":
|
|
82
|
-
setPos({ top: rect.bottom
|
|
273
|
+
setPos({ top: rect.bottom, left: rect.right });
|
|
83
274
|
break;
|
|
84
275
|
case "bottom-left":
|
|
85
276
|
default:
|
|
86
|
-
setPos({ top: rect.bottom
|
|
277
|
+
setPos({ top: rect.bottom, left: rect.left });
|
|
87
278
|
break;
|
|
88
279
|
}
|
|
89
280
|
};
|
|
281
|
+
update();
|
|
90
282
|
const observer = new ResizeObserver(update);
|
|
91
|
-
observer.observe(
|
|
283
|
+
observer.observe(el);
|
|
284
|
+
for (const child of getObservableChildren(el)) observer.observe(child);
|
|
92
285
|
observer.observe(document.documentElement);
|
|
93
286
|
return () => observer.disconnect();
|
|
94
287
|
}, [ref, position]);
|
|
@@ -99,9 +292,10 @@ function useDraggable() {
|
|
|
99
292
|
const dragging = (0, import_react.useRef)(false);
|
|
100
293
|
const start = (0, import_react.useRef)({ x: 0, y: 0 });
|
|
101
294
|
const onPointerDown = (0, import_react.useCallback)((e) => {
|
|
295
|
+
if (e.target.closest("button, a, [data-no-drag]")) return;
|
|
102
296
|
dragging.current = true;
|
|
103
297
|
start.current = { x: e.clientX - offset.x, y: e.clientY - offset.y };
|
|
104
|
-
e.
|
|
298
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
105
299
|
}, [offset]);
|
|
106
300
|
const onPointerMove = (0, import_react.useCallback)((e) => {
|
|
107
301
|
if (!dragging.current) return;
|
|
@@ -121,21 +315,13 @@ function useDomTracker(wrapperRef, enabled) {
|
|
|
121
315
|
const dirty = (0, import_react.useRef)(true);
|
|
122
316
|
(0, import_react.useEffect)(() => {
|
|
123
317
|
if (!enabled || !wrapperRef.current) return;
|
|
124
|
-
const observer = new MutationObserver((
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (record.type === "attributes" && record.attributeName === "class") continue;
|
|
128
|
-
realMutation = true;
|
|
129
|
-
}
|
|
130
|
-
if (realMutation) {
|
|
131
|
-
mutations.current++;
|
|
132
|
-
dirty.current = true;
|
|
133
|
-
}
|
|
318
|
+
const observer = new MutationObserver(() => {
|
|
319
|
+
mutations.current++;
|
|
320
|
+
dirty.current = true;
|
|
134
321
|
});
|
|
135
322
|
observer.observe(wrapperRef.current, {
|
|
136
323
|
childList: true,
|
|
137
324
|
subtree: true,
|
|
138
|
-
attributes: true,
|
|
139
325
|
attributeFilter: ["style"]
|
|
140
326
|
});
|
|
141
327
|
return () => observer.disconnect();
|
|
@@ -176,14 +362,19 @@ function useRenderFlash(wrapperRef, open) {
|
|
|
176
362
|
const mutationCount = (0, import_react.useRef)(0);
|
|
177
363
|
(0, import_react.useEffect)(() => {
|
|
178
364
|
if (!open || !wrapperRef.current) return;
|
|
365
|
+
const wrapper = wrapperRef.current;
|
|
179
366
|
const observer = new MutationObserver(() => {
|
|
180
367
|
mutationCount.current++;
|
|
181
|
-
if (
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
368
|
+
if (mutationCount.current <= 1) return;
|
|
369
|
+
const target = getObservableChildren(wrapper)[0] ?? wrapper;
|
|
370
|
+
target.style.outline = FLASH_OUTLINE;
|
|
371
|
+
target.style.outlineOffset = "-2px";
|
|
372
|
+
setTimeout(() => {
|
|
373
|
+
target.style.outline = "";
|
|
374
|
+
target.style.outlineOffset = "";
|
|
375
|
+
}, 150);
|
|
185
376
|
});
|
|
186
|
-
observer.observe(
|
|
377
|
+
observer.observe(wrapper, { childList: true, subtree: true });
|
|
187
378
|
return () => observer.disconnect();
|
|
188
379
|
}, [wrapperRef, open]);
|
|
189
380
|
}
|
|
@@ -215,7 +406,7 @@ function FrameTimeGraph({ history }) {
|
|
|
215
406
|
const w = 140;
|
|
216
407
|
const h = 32;
|
|
217
408
|
const barW = Math.max(1, w / HISTORY_SIZE - 0.5);
|
|
218
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
409
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.graphWrap, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: w, height: h, style: { display: "block" }, children: [
|
|
219
410
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: w, height: h, rx: 3, fill: "#111" }),
|
|
220
411
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
221
412
|
"line",
|
|
@@ -248,27 +439,29 @@ function FrameTimeGraph({ history }) {
|
|
|
248
439
|
] }) });
|
|
249
440
|
}
|
|
250
441
|
function StatRow({ label, value, sub, color = "#4ade80" }) {
|
|
251
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
252
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.row, children: [
|
|
443
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.rowLabel, children: label }),
|
|
253
444
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
254
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
445
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...s.rowValue, color }, children: value }),
|
|
255
446
|
sub && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#444", fontSize: 9, marginLeft: 4 }, children: sub })
|
|
256
447
|
] })
|
|
257
448
|
] });
|
|
258
449
|
}
|
|
450
|
+
var GAP = 8;
|
|
259
451
|
function getPanelStyle(pos, offset, position) {
|
|
260
452
|
const style = {
|
|
453
|
+
...s.panel,
|
|
261
454
|
transform: `translate(${offset.x}px, ${offset.y}px)`
|
|
262
455
|
};
|
|
263
456
|
if (position.startsWith("bottom")) {
|
|
264
|
-
style.bottom = window.innerHeight - pos.top;
|
|
457
|
+
style.bottom = window.innerHeight - pos.top + GAP;
|
|
265
458
|
} else {
|
|
266
|
-
style.top = pos.top;
|
|
459
|
+
style.top = pos.top + GAP;
|
|
267
460
|
}
|
|
268
461
|
if (position.endsWith("right")) {
|
|
269
|
-
style.right = window.innerWidth - pos.left;
|
|
462
|
+
style.right = window.innerWidth - pos.left + GAP;
|
|
270
463
|
} else {
|
|
271
|
-
style.left = pos.left;
|
|
464
|
+
style.left = pos.left + GAP;
|
|
272
465
|
}
|
|
273
466
|
return style;
|
|
274
467
|
}
|
|
@@ -310,7 +503,8 @@ function DevStatsPanel({
|
|
|
310
503
|
allFrameTimes.current.push(avgFrameTime);
|
|
311
504
|
const sorted = [...allFrameTimes.current].sort((a, b) => a - b);
|
|
312
505
|
const el = targetRef.current;
|
|
313
|
-
const
|
|
506
|
+
const r = el ? getEffectiveRect(el) : null;
|
|
507
|
+
const dims = r ? `${Math.round(r.width)} x ${Math.round(r.height)}` : "\u2013";
|
|
314
508
|
const perf = performance;
|
|
315
509
|
const mem = perf.memory ? Math.round(perf.memory.usedJSHeapSize / 1024 / 1024) : 0;
|
|
316
510
|
setStats({
|
|
@@ -345,23 +539,17 @@ function DevStatsPanel({
|
|
|
345
539
|
const handleExport = (0, import_react2.useCallback)(() => {
|
|
346
540
|
const payload = { timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...stats };
|
|
347
541
|
const json = JSON.stringify(payload, null, 2);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
ta.select();
|
|
360
|
-
document.execCommand("copy");
|
|
361
|
-
document.body.removeChild(ta);
|
|
362
|
-
setExported(true);
|
|
363
|
-
setTimeout(() => setExported(false), 1200);
|
|
364
|
-
}
|
|
542
|
+
const blob = new Blob([json], { type: "application/json" });
|
|
543
|
+
const url = URL.createObjectURL(blob);
|
|
544
|
+
const a = document.createElement("a");
|
|
545
|
+
a.href = url;
|
|
546
|
+
a.download = `devprofiler-${Date.now()}.json`;
|
|
547
|
+
document.body.appendChild(a);
|
|
548
|
+
a.click();
|
|
549
|
+
document.body.removeChild(a);
|
|
550
|
+
URL.revokeObjectURL(url);
|
|
551
|
+
setExported(true);
|
|
552
|
+
setTimeout(() => setExported(false), 1200);
|
|
365
553
|
}, [stats]);
|
|
366
554
|
const ftColor = stats.frameTime > 33 ? "#ef4444" : stats.frameTime > 16.67 ? "#f59e0b" : "#4ade80";
|
|
367
555
|
const rpsColor = stats.rendersPerSecond > 30 ? "#ef4444" : stats.rendersPerSecond > 10 ? "#f59e0b" : "#4ade80";
|
|
@@ -369,42 +557,33 @@ function DevStatsPanel({
|
|
|
369
557
|
const fps = stats.frameTime > 0 ? Math.round(1e3 / stats.frameTime) : 0;
|
|
370
558
|
const memoGain = stats.profiler.baseDuration > 0 ? Math.round((1 - stats.profiler.actualDuration / stats.profiler.baseDuration) * 100) : 0;
|
|
371
559
|
const p99Color = stats.frameTimeP99 > 33 ? "#ef4444" : stats.frameTimeP99 > 16.67 ? "#f59e0b" : "#4ade80";
|
|
560
|
+
const exportStyle = exported ? { ...s.iconBtn, ...s.iconBtnActive } : s.iconBtn;
|
|
372
561
|
return (0, import_react_dom.createPortal)(
|
|
373
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
374
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
375
|
-
"
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
),
|
|
397
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: DevProfiler_default.resetBtn, onClick: handleReset, title: "Reset counters", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime.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" }) }) }),
|
|
398
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: DevProfiler_default.closeBtn, onClick: onClose, children: "\u2715" })
|
|
399
|
-
] })
|
|
400
|
-
]
|
|
401
|
-
}
|
|
402
|
-
),
|
|
403
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: DevProfiler_default.body, children: [
|
|
404
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: DevProfiler_default.section, children: "Rendering" }),
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: getPanelStyle(pos, offset, position), ...dragHandlers, children: [
|
|
563
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.panelHeader, children: [
|
|
564
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: s.panelTitle, children: [
|
|
565
|
+
"Dev Profiler",
|
|
566
|
+
instanceCount > 1 && instanceId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.instanceBadge, children: instanceId })
|
|
567
|
+
] }),
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.headerActions, children: [
|
|
569
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
570
|
+
"button",
|
|
571
|
+
{
|
|
572
|
+
style: exportStyle,
|
|
573
|
+
onClick: handleExport,
|
|
574
|
+
title: exported ? "Exported!" : "Export stats as JSON",
|
|
575
|
+
children: exported ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3.5 8.5 6.5 11.5 12.5 4.5" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2v8M4 7l4 4 4-4M2 14h12" }) })
|
|
576
|
+
}
|
|
577
|
+
),
|
|
578
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { style: s.iconBtn, onClick: handleReset, title: "Reset counters", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime.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" }) }) }),
|
|
579
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { style: s.closeBtn, onClick: onClose, children: "\u2715" })
|
|
580
|
+
] })
|
|
581
|
+
] }),
|
|
582
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.body, children: [
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.section, children: "Rendering" }),
|
|
405
584
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Frame time", value: `${stats.frameTime.toFixed(1)}ms`, sub: `${fps} fps`, color: ftColor }),
|
|
406
585
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FrameTimeGraph, { history: stats.frameTimeHistory }),
|
|
407
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
586
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.miniRow, children: [
|
|
408
587
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
409
588
|
"min ",
|
|
410
589
|
stats.frameTimeMin.toFixed(1)
|
|
@@ -420,23 +599,23 @@ function DevStatsPanel({
|
|
|
420
599
|
] }),
|
|
421
600
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Renders/s", value: String(stats.rendersPerSecond), color: rpsColor }),
|
|
422
601
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Long tasks", value: String(stats.longTasks), color: stats.longTasks > 0 ? "#f59e0b" : "#4ade80" }),
|
|
423
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
424
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
602
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.separator }),
|
|
603
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.section, children: "React Profiler" }),
|
|
425
604
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Phase", value: stats.profiler.phase, color: "#888" }),
|
|
426
605
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Render", value: `${stats.profiler.actualDuration.toFixed(2)}ms`, color: actualColor }),
|
|
427
606
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Base (no memo)", value: `${stats.profiler.baseDuration.toFixed(2)}ms`, color: "#888" }),
|
|
428
607
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Memo gain", value: `${memoGain}%`, color: memoGain > 50 ? "#4ade80" : memoGain > 20 ? "#f59e0b" : "#ef4444" }),
|
|
429
608
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Commits", value: String(stats.profiler.commitCount) }),
|
|
430
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
431
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
609
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.separator }),
|
|
610
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.section, children: "DOM" }),
|
|
432
611
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Nodes", value: stats.domNodes.toLocaleString() }),
|
|
433
612
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Mutations", value: String(stats.domMutations) }),
|
|
434
613
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Size", value: stats.dimensions, color: "#888" }),
|
|
435
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
436
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
614
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.separator }),
|
|
615
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.section, children: "Memory" }),
|
|
437
616
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "JS Heap", value: stats.memory > 0 ? `${stats.memory} MB` : "N/A" })
|
|
438
617
|
] }),
|
|
439
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
618
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.footer, children: "Ctrl+I to toggle" })
|
|
440
619
|
] }),
|
|
441
620
|
document.body
|
|
442
621
|
);
|
|
@@ -446,35 +625,35 @@ function DevStatsPanel({
|
|
|
446
625
|
var import_react3 = require("react");
|
|
447
626
|
var import_react_dom2 = require("react-dom");
|
|
448
627
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
628
|
+
var GAP2 = 8;
|
|
449
629
|
function getButtonStyle(pos, position) {
|
|
450
|
-
const style = {};
|
|
630
|
+
const style = { ...s.toggleBtn };
|
|
451
631
|
if (position.startsWith("bottom")) {
|
|
452
|
-
style.bottom = window.innerHeight - pos.top +
|
|
632
|
+
style.bottom = window.innerHeight - pos.top + GAP2;
|
|
453
633
|
} else {
|
|
454
|
-
style.top = pos.top +
|
|
634
|
+
style.top = pos.top + GAP2;
|
|
455
635
|
}
|
|
456
636
|
if (position.endsWith("right")) {
|
|
457
|
-
style.right = window.innerWidth - pos.left;
|
|
637
|
+
style.right = window.innerWidth - pos.left + GAP2;
|
|
458
638
|
} else {
|
|
459
|
-
style.left = pos.left;
|
|
639
|
+
style.left = pos.left + GAP2;
|
|
460
640
|
}
|
|
461
641
|
return style;
|
|
462
642
|
}
|
|
463
643
|
function ToggleButton({
|
|
464
644
|
targetRef,
|
|
465
645
|
onClick,
|
|
466
|
-
position = "bottom-left"
|
|
646
|
+
position = "bottom-left",
|
|
647
|
+
accentColor = "#6366f1"
|
|
467
648
|
}) {
|
|
468
649
|
const pos = useAnchorPosition(targetRef, position);
|
|
469
650
|
const [fps, setFps] = (0, import_react3.useState)(0);
|
|
470
|
-
const lastFrame = (0, import_react3.useRef)(performance.now());
|
|
471
651
|
(0, import_react3.useEffect)(() => {
|
|
472
652
|
let animId;
|
|
473
653
|
let count = 0;
|
|
474
654
|
let lastSecond = performance.now();
|
|
475
655
|
const tick = () => {
|
|
476
656
|
const now = performance.now();
|
|
477
|
-
lastFrame.current = now;
|
|
478
657
|
count++;
|
|
479
658
|
if (now - lastSecond >= 1e3) {
|
|
480
659
|
setFps(count);
|
|
@@ -486,16 +665,18 @@ function ToggleButton({
|
|
|
486
665
|
animId = requestAnimationFrame(tick);
|
|
487
666
|
return () => cancelAnimationFrame(animId);
|
|
488
667
|
}, []);
|
|
489
|
-
const fpsColor = fps < 30 ? "#ef4444" : fps < 55 ? "#f59e0b" : "#4ade80";
|
|
490
668
|
return (0, import_react_dom2.createPortal)(
|
|
491
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
669
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
492
670
|
"button",
|
|
493
671
|
{
|
|
494
|
-
className: DevProfiler_default.toggleBtn,
|
|
495
672
|
onClick,
|
|
496
673
|
title: "Dev Profiler (Ctrl+I)",
|
|
497
674
|
style: getButtonStyle(pos, position),
|
|
498
|
-
children:
|
|
675
|
+
children: [
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { ...s.toggleDot, background: accentColor, boxShadow: `0 0 4px ${accentColor}` } }),
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: s.toggleFps, children: fps }),
|
|
678
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: s.toggleLabel, children: "fps" })
|
|
679
|
+
]
|
|
499
680
|
}
|
|
500
681
|
),
|
|
501
682
|
document.body
|
|
@@ -520,7 +701,8 @@ var activeInstances = /* @__PURE__ */ new Set();
|
|
|
520
701
|
function DevProfiler({
|
|
521
702
|
children,
|
|
522
703
|
position = "bottom-left",
|
|
523
|
-
id
|
|
704
|
+
id,
|
|
705
|
+
accentColor = "#6366f1"
|
|
524
706
|
}) {
|
|
525
707
|
const wrapperRef = (0, import_react4.useRef)(null);
|
|
526
708
|
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
@@ -558,9 +740,9 @@ function DevProfiler({
|
|
|
558
740
|
return () => window.removeEventListener(TOGGLE_EVENT, handler);
|
|
559
741
|
}, []);
|
|
560
742
|
if (!__DEV__) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
|
|
561
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: wrapperRef,
|
|
743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: wrapperRef, style: { display: "contents" }, children: [
|
|
562
744
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Profiler, { id: "DevProfiler", onRender, children }),
|
|
563
|
-
!open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToggleButton, { targetRef: wrapperRef, onClick: toggle, position }),
|
|
745
|
+
!open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToggleButton, { targetRef: wrapperRef, onClick: toggle, position, accentColor }),
|
|
564
746
|
open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
565
747
|
DevStatsPanel,
|
|
566
748
|
{
|
|
@@ -594,6 +776,12 @@ function DevProfiler({
|
|
|
594
776
|
* @author Frederic Denis (billywild87) — https://github.com/billywild87
|
|
595
777
|
* @license MIT
|
|
596
778
|
*/
|
|
779
|
+
/**
|
|
780
|
+
* @module react-dev-profiler
|
|
781
|
+
* @description Inline styles for the profiler UI — no external CSS needed.
|
|
782
|
+
* @author Frederic Denis (billywild87) — https://github.com/billywild87
|
|
783
|
+
* @license MIT
|
|
784
|
+
*/
|
|
597
785
|
/**
|
|
598
786
|
* @module react-dev-profiler
|
|
599
787
|
* @description Custom hooks that power the profiler's data collection.
|