react-dev-profiler 1.1.0 → 1.2.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
@@ -88,11 +88,12 @@ All observers are created lazily (only when the panel is open) and cleaned up on
88
88
 
89
89
  ### `<DevProfiler>`
90
90
 
91
- | Prop | Type | Default | Description |
92
- | ---------- | --------------- | --------------- | ---------------------------------------- |
93
- | `children` | `ReactNode` | — | The subtree to profile |
94
- | `position` | `PanelPosition` | `'bottom-left'` | Where to anchor the panel |
95
- | `id` | `string` | auto-generated | Instance label (shown with multi-panels) |
91
+ | Prop | Type | Default | Description |
92
+ | ------------- | --------------- | --------------- | ------------------------------------------ |
93
+ | `children` | `ReactNode` | — | The subtree to profile |
94
+ | `position` | `PanelPosition` | `'bottom-left'` | Where to anchor the panel |
95
+ | `id` | `string` | auto-generated | Instance label (shown with multi-panels) |
96
+ | `accentColor` | `string` | `'#6366f1'` | Accent color for the toggle button's glow |
96
97
 
97
98
  ### `PanelPosition`
98
99
 
package/dist/index.cjs CHANGED
@@ -55,6 +55,25 @@ function percentile(sorted, p) {
55
55
  return sorted[Math.max(0, idx)];
56
56
  }
57
57
 
58
+ // src/constants.ts
59
+ var COLOR_GREEN = "#4ade80";
60
+ var COLOR_AMBER = "#f59e0b";
61
+ var COLOR_RED = "#ef4444";
62
+ var COLOR_MUTED = "#888";
63
+ var COLOR_DIM = "#444";
64
+ var COLOR_ACCENT = "#6366f1";
65
+ var FPS_60_MS = 16.67;
66
+ var FPS_30_MS = 33;
67
+ var GRAPH_BG = "#111";
68
+ var GRAPH_60FPS_LINE = "#1a3a1a";
69
+ var GRAPH_30FPS_LINE = "#3a1a1a";
70
+ var PANEL_GAP = 8;
71
+ function ftColor(ms) {
72
+ if (ms > FPS_30_MS) return COLOR_RED;
73
+ if (ms > FPS_60_MS) return COLOR_AMBER;
74
+ return COLOR_GREEN;
75
+ }
76
+
58
77
  // src/env.ts
59
78
  var __DEV__ = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
60
79
 
@@ -101,7 +120,7 @@ var s = {
101
120
  },
102
121
  instanceBadge: {
103
122
  background: "#222",
104
- color: "#888",
123
+ color: COLOR_MUTED,
105
124
  fontSize: 8,
106
125
  padding: "1px 5px",
107
126
  borderRadius: 4,
@@ -116,7 +135,7 @@ var s = {
116
135
  iconBtn: {
117
136
  background: "none",
118
137
  border: "none",
119
- color: "#444",
138
+ color: COLOR_DIM,
120
139
  cursor: "pointer",
121
140
  padding: 4,
122
141
  margin: -4,
@@ -126,12 +145,12 @@ var s = {
126
145
  transition: "color 0.15s"
127
146
  },
128
147
  iconBtnActive: {
129
- color: "#4ade80"
148
+ color: COLOR_GREEN
130
149
  },
131
150
  closeBtn: {
132
151
  background: "none",
133
152
  border: "none",
134
- color: "#444",
153
+ color: COLOR_DIM,
135
154
  cursor: "pointer",
136
155
  fontSize: 13,
137
156
  padding: 4,
@@ -144,7 +163,7 @@ var s = {
144
163
  gap: 5
145
164
  },
146
165
  section: {
147
- color: "#444",
166
+ color: COLOR_DIM,
148
167
  fontSize: 8,
149
168
  fontWeight: 600,
150
169
  letterSpacing: 1,
@@ -173,7 +192,7 @@ var s = {
173
192
  miniRow: {
174
193
  display: "flex",
175
194
  justifyContent: "space-between",
176
- color: "#444",
195
+ color: COLOR_DIM,
177
196
  fontSize: 9,
178
197
  marginTop: -2,
179
198
  marginBottom: 2
@@ -226,9 +245,9 @@ var s = {
226
245
  textAlign: "center"
227
246
  }
228
247
  };
229
- var FLASH_OUTLINE = "2px solid rgba(99, 102, 241, 0.8)";
248
+ var FLASH_OUTLINE = `2px solid ${COLOR_ACCENT}cc`;
230
249
 
231
- // src/hooks.ts
250
+ // src/utils.ts
232
251
  function getEffectiveRect(el) {
233
252
  const rect = el.getBoundingClientRect();
234
253
  if (rect.width > 0 || rect.height > 0) return rect;
@@ -255,6 +274,8 @@ function getObservableChildren(el) {
255
274
  }
256
275
  return result;
257
276
  }
277
+
278
+ // src/hooks.ts
258
279
  function useAnchorPosition(ref, position = "bottom-left") {
259
280
  const [pos, setPos] = (0, import_react.useState)({ top: 0, left: 0 });
260
281
  (0, import_react.useEffect)(() => {
@@ -400,22 +421,24 @@ function useLongTasks(enabled) {
400
421
  // src/DevStatsPanel.tsx
401
422
  var import_react2 = require("react");
402
423
  var import_react_dom = require("react-dom");
424
+
425
+ // src/FrameTimeGraph.tsx
403
426
  var import_jsx_runtime = require("react/jsx-runtime");
404
427
  function FrameTimeGraph({ history }) {
405
- const max = Math.max(33, ...history);
428
+ const max = Math.max(FPS_30_MS, ...history);
406
429
  const w = 140;
407
430
  const h = 32;
408
431
  const barW = Math.max(1, w / HISTORY_SIZE - 0.5);
409
432
  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: [
410
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: w, height: h, rx: 3, fill: "#111" }),
433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: w, height: h, rx: 3, fill: GRAPH_BG }),
411
434
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
412
435
  "line",
413
436
  {
414
437
  x1: 0,
415
- y1: h - 16.67 / max * h,
438
+ y1: h - FPS_60_MS / max * h,
416
439
  x2: w,
417
- y2: h - 16.67 / max * h,
418
- stroke: "#1a3a1a",
440
+ y2: h - FPS_60_MS / max * h,
441
+ stroke: GRAPH_60FPS_LINE,
419
442
  strokeWidth: 1
420
443
  }
421
444
  ),
@@ -423,31 +446,36 @@ function FrameTimeGraph({ history }) {
423
446
  "line",
424
447
  {
425
448
  x1: 0,
426
- y1: h - 33 / max * h,
449
+ y1: h - FPS_30_MS / max * h,
427
450
  x2: w,
428
- y2: h - 33 / max * h,
429
- stroke: "#3a1a1a",
451
+ y2: h - FPS_30_MS / max * h,
452
+ stroke: GRAPH_30FPS_LINE,
430
453
  strokeWidth: 1
431
454
  }
432
455
  ),
433
456
  history.map((ms, i) => {
434
457
  const x = i / HISTORY_SIZE * w;
435
458
  const barH = Math.min(ms / max * h, h);
436
- const color = ms > 33 ? "#ef4444" : ms > 16.67 ? "#f59e0b" : "#4ade80";
437
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x, y: h - barH, width: barW, height: barH, fill: color, opacity: 0.8, rx: 0.5 }, i);
459
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x, y: h - barH, width: barW, height: barH, fill: ftColor(ms), opacity: 0.8, rx: 0.5 }, i);
438
460
  })
439
461
  ] }) });
440
462
  }
441
- function StatRow({ label, value, sub, color = "#4ade80" }) {
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 }),
444
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
445
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...s.rowValue, color }, children: value }),
446
- sub && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#444", fontSize: 9, marginLeft: 4 }, children: sub })
463
+
464
+ // src/StatRow.tsx
465
+ var import_jsx_runtime2 = require("react/jsx-runtime");
466
+ function StatRow({ label, value, sub, color = COLOR_GREEN }) {
467
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: s.row, children: [
468
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: s.rowLabel, children: label }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
470
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { ...s.rowValue, color }, children: value }),
471
+ sub && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: COLOR_DIM, fontSize: 9, marginLeft: 4 }, children: sub })
447
472
  ] })
448
473
  ] });
449
474
  }
450
- var GAP = 8;
475
+
476
+ // src/DevStatsPanel.tsx
477
+ var import_jsx_runtime3 = require("react/jsx-runtime");
478
+ var GAP = PANEL_GAP;
451
479
  function getPanelStyle(pos, offset, position) {
452
480
  const style = {
453
481
  ...s.panel,
@@ -551,71 +579,71 @@ function DevStatsPanel({
551
579
  setExported(true);
552
580
  setTimeout(() => setExported(false), 1200);
553
581
  }, [stats]);
554
- const ftColor = stats.frameTime > 33 ? "#ef4444" : stats.frameTime > 16.67 ? "#f59e0b" : "#4ade80";
555
- const rpsColor = stats.rendersPerSecond > 30 ? "#ef4444" : stats.rendersPerSecond > 10 ? "#f59e0b" : "#4ade80";
556
- const actualColor = stats.profiler.actualDuration > 16 ? "#ef4444" : stats.profiler.actualDuration > 8 ? "#f59e0b" : "#4ade80";
582
+ const frameTColor = ftColor(stats.frameTime);
583
+ const rpsColor = stats.rendersPerSecond > 30 ? COLOR_RED : stats.rendersPerSecond > 10 ? COLOR_AMBER : COLOR_GREEN;
584
+ const actualColor = stats.profiler.actualDuration > 16 ? COLOR_RED : stats.profiler.actualDuration > 8 ? COLOR_AMBER : COLOR_GREEN;
557
585
  const fps = stats.frameTime > 0 ? Math.round(1e3 / stats.frameTime) : 0;
558
586
  const memoGain = stats.profiler.baseDuration > 0 ? Math.round((1 - stats.profiler.actualDuration / stats.profiler.baseDuration) * 100) : 0;
559
- const p99Color = stats.frameTimeP99 > 33 ? "#ef4444" : stats.frameTimeP99 > 16.67 ? "#f59e0b" : "#4ade80";
587
+ const p99Color = ftColor(stats.frameTimeP99);
560
588
  const exportStyle = exported ? { ...s.iconBtn, ...s.iconBtnActive } : s.iconBtn;
561
589
  return (0, import_react_dom.createPortal)(
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: [
590
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: getPanelStyle(pos, offset, position), ...dragHandlers, children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: s.panelHeader, children: [
592
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: s.panelTitle, children: [
565
593
  "Dev Profiler",
566
- instanceCount > 1 && instanceId && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.instanceBadge, children: instanceId })
594
+ instanceCount > 1 && instanceId && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: s.instanceBadge, children: instanceId })
567
595
  ] }),
568
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.headerActions, children: [
569
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
596
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: s.headerActions, children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
570
598
  "button",
571
599
  {
572
600
  style: exportStyle,
573
601
  onClick: handleExport,
574
602
  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" }) })
603
+ children: exported ? /* @__PURE__ */ (0, import_jsx_runtime3.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_runtime3.jsx)("polyline", { points: "3.5 8.5 6.5 11.5 12.5 4.5" }) }) : /* @__PURE__ */ (0, import_jsx_runtime3.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_runtime3.jsx)("path", { d: "M8 2v8M4 7l4 4 4-4M2 14h12" }) })
576
604
  }
577
605
  ),
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" })
606
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { style: s.iconBtn, onClick: handleReset, title: "Reset counters", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime3.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" }) }) }),
607
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { style: s.closeBtn, onClick: onClose, children: "\u2715" })
580
608
  ] })
581
609
  ] }),
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" }),
584
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Frame time", value: `${stats.frameTime.toFixed(1)}ms`, sub: `${fps} fps`, color: ftColor }),
585
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FrameTimeGraph, { history: stats.frameTimeHistory }),
586
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: s.miniRow, children: [
587
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
610
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: s.body, children: [
611
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: s.section, children: "Rendering" }),
612
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Frame time", value: `${stats.frameTime.toFixed(1)}ms`, sub: `${fps} fps`, color: frameTColor }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FrameTimeGraph, { history: stats.frameTimeHistory }),
614
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: s.miniRow, children: [
615
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
588
616
  "min ",
589
617
  stats.frameTimeMin.toFixed(1)
590
618
  ] }),
591
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
619
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
592
620
  "max ",
593
621
  stats.frameTimeMax.toFixed(1)
594
622
  ] }),
595
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { color: p99Color }, children: [
623
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { color: p99Color }, children: [
596
624
  "p99 ",
597
625
  stats.frameTimeP99.toFixed(1)
598
626
  ] })
599
627
  ] }),
600
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Renders/s", value: String(stats.rendersPerSecond), color: rpsColor }),
601
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Long tasks", value: String(stats.longTasks), color: stats.longTasks > 0 ? "#f59e0b" : "#4ade80" }),
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" }),
604
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Phase", value: stats.profiler.phase, color: "#888" }),
605
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Render", value: `${stats.profiler.actualDuration.toFixed(2)}ms`, color: actualColor }),
606
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Base (no memo)", value: `${stats.profiler.baseDuration.toFixed(2)}ms`, color: "#888" }),
607
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Memo gain", value: `${memoGain}%`, color: memoGain > 50 ? "#4ade80" : memoGain > 20 ? "#f59e0b" : "#ef4444" }),
608
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Commits", value: String(stats.profiler.commitCount) }),
609
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.separator }),
610
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.section, children: "DOM" }),
611
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Nodes", value: stats.domNodes.toLocaleString() }),
612
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Mutations", value: String(stats.domMutations) }),
613
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "Size", value: stats.dimensions, color: "#888" }),
614
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.separator }),
615
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: s.section, children: "Memory" }),
616
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatRow, { label: "JS Heap", value: stats.memory > 0 ? `${stats.memory} MB` : "N/A" })
628
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Renders/s", value: String(stats.rendersPerSecond), color: rpsColor }),
629
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Long tasks", value: String(stats.longTasks), color: stats.longTasks > 0 ? COLOR_AMBER : COLOR_GREEN }),
630
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: s.separator }),
631
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: s.section, children: "React Profiler" }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Phase", value: stats.profiler.phase, color: COLOR_MUTED }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Render", value: `${stats.profiler.actualDuration.toFixed(2)}ms`, color: actualColor }),
634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Base (no memo)", value: `${stats.profiler.baseDuration.toFixed(2)}ms`, color: COLOR_MUTED }),
635
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Memo gain", value: `${memoGain}%`, color: memoGain > 50 ? COLOR_GREEN : memoGain > 20 ? COLOR_AMBER : COLOR_RED }),
636
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Commits", value: String(stats.profiler.commitCount) }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: s.separator }),
638
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: s.section, children: "DOM" }),
639
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Nodes", value: stats.domNodes.toLocaleString() }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Mutations", value: String(stats.domMutations) }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "Size", value: stats.dimensions, color: COLOR_MUTED }),
642
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: s.separator }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: s.section, children: "Memory" }),
644
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatRow, { label: "JS Heap", value: stats.memory > 0 ? `${stats.memory} MB` : "N/A" })
617
645
  ] }),
618
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: s.footer, children: "Ctrl+I to toggle" })
646
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: s.footer, children: "Ctrl+I to toggle" })
619
647
  ] }),
620
648
  document.body
621
649
  );
@@ -624,8 +652,8 @@ function DevStatsPanel({
624
652
  // src/ToggleButton.tsx
625
653
  var import_react3 = require("react");
626
654
  var import_react_dom2 = require("react-dom");
627
- var import_jsx_runtime2 = require("react/jsx-runtime");
628
- var GAP2 = 8;
655
+ var import_jsx_runtime4 = require("react/jsx-runtime");
656
+ var GAP2 = PANEL_GAP;
629
657
  function getButtonStyle(pos, position) {
630
658
  const style = { ...s.toggleBtn };
631
659
  if (position.startsWith("bottom")) {
@@ -644,7 +672,7 @@ function ToggleButton({
644
672
  targetRef,
645
673
  onClick,
646
674
  position = "bottom-left",
647
- accentColor = "#6366f1"
675
+ accentColor = COLOR_ACCENT
648
676
  }) {
649
677
  const pos = useAnchorPosition(targetRef, position);
650
678
  const [fps, setFps] = (0, import_react3.useState)(0);
@@ -666,16 +694,16 @@ function ToggleButton({
666
694
  return () => cancelAnimationFrame(animId);
667
695
  }, []);
668
696
  return (0, import_react_dom2.createPortal)(
669
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
697
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
670
698
  "button",
671
699
  {
672
700
  onClick,
673
701
  title: "Dev Profiler (Ctrl+I)",
674
702
  style: getButtonStyle(pos, position),
675
703
  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" })
704
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { ...s.toggleDot, background: accentColor, boxShadow: `0 0 4px ${accentColor}` } }),
705
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: s.toggleFps, children: fps }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: s.toggleLabel, children: "fps" })
679
707
  ]
680
708
  }
681
709
  ),
@@ -684,7 +712,7 @@ function ToggleButton({
684
712
  }
685
713
 
686
714
  // src/DevProfiler.tsx
687
- var import_jsx_runtime3 = require("react/jsx-runtime");
715
+ var import_jsx_runtime5 = require("react/jsx-runtime");
688
716
  var TOGGLE_EVENT = "devprofiler:toggle";
689
717
  var BOUND_KEY = "__devprofiler_bound";
690
718
  if (typeof window !== "undefined" && __DEV__ && !window[BOUND_KEY]) {
@@ -702,7 +730,7 @@ function DevProfiler({
702
730
  children,
703
731
  position = "bottom-left",
704
732
  id,
705
- accentColor = "#6366f1"
733
+ accentColor = COLOR_ACCENT
706
734
  }) {
707
735
  const wrapperRef = (0, import_react4.useRef)(null);
708
736
  const [open, setOpen] = (0, import_react4.useState)(false);
@@ -739,11 +767,11 @@ function DevProfiler({
739
767
  window.addEventListener(TOGGLE_EVENT, handler);
740
768
  return () => window.removeEventListener(TOGGLE_EVENT, handler);
741
769
  }, []);
742
- if (!__DEV__) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
743
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: wrapperRef, style: { display: "contents" }, children: [
744
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Profiler, { id: "DevProfiler", onRender, children }),
745
- !open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToggleButton, { targetRef: wrapperRef, onClick: toggle, position, accentColor }),
746
- open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
770
+ if (!__DEV__) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
771
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: wrapperRef, style: { display: "contents" }, children: [
772
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react4.Profiler, { id: "DevProfiler", onRender, children }),
773
+ !open && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ToggleButton, { targetRef: wrapperRef, onClick: toggle, position, accentColor }),
774
+ open && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
747
775
  DevStatsPanel,
748
776
  {
749
777
  targetRef: wrapperRef,
@@ -770,6 +798,12 @@ function DevProfiler({
770
798
  * @author Frederic Denis (billywild87) — https://github.com/billywild87
771
799
  * @license MIT
772
800
  */
801
+ /**
802
+ * @module react-dev-profiler
803
+ * @description Shared colors and thresholds used across the profiler UI.
804
+ * @author Frederic Denis (billywild87) — https://github.com/billywild87
805
+ * @license MIT
806
+ */
773
807
  /**
774
808
  * @module react-dev-profiler
775
809
  * @description Environment detection — works with Vite, webpack, Next.js, and any bundler.
@@ -782,12 +816,30 @@ function DevProfiler({
782
816
  * @author Frederic Denis (billywild87) — https://github.com/billywild87
783
817
  * @license MIT
784
818
  */
819
+ /**
820
+ * @module react-dev-profiler
821
+ * @description Pure utility functions for DOM measurement.
822
+ * @author Frederic Denis (billywild87) — https://github.com/billywild87
823
+ * @license MIT
824
+ */
785
825
  /**
786
826
  * @module react-dev-profiler
787
827
  * @description Custom hooks that power the profiler's data collection.
788
828
  * @author Frederic Denis (billywild87) — https://github.com/billywild87
789
829
  * @license MIT
790
830
  */
831
+ /**
832
+ * @module react-dev-profiler
833
+ * @description Rolling bar chart of frame times (last 60 samples).
834
+ * @author Frederic Denis (billywild87) — https://github.com/billywild87
835
+ * @license MIT
836
+ */
837
+ /**
838
+ * @module react-dev-profiler
839
+ * @description Single label/value row used throughout the profiler panel.
840
+ * @author Frederic Denis (billywild87) — https://github.com/billywild87
841
+ * @license MIT
842
+ */
791
843
  /**
792
844
  * @module react-dev-profiler
793
845
  * @description The main stats panel — renders all performance metrics in a floating overlay.