@react-perfscope/ui 0.2.0 → 0.4.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 +4 -6
- package/dist/index.cjs +111 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +111 -23
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -72,6 +72,9 @@ var en = {
|
|
|
72
72
|
sort: "Sort",
|
|
73
73
|
sortChronological: "chronological",
|
|
74
74
|
sortSeverity: "severity (worst first)",
|
|
75
|
+
filterPlaceholder: "Filter\u2026",
|
|
76
|
+
filterAria: "Filter signals",
|
|
77
|
+
filterNoMatches: "No signals match the filter.",
|
|
75
78
|
groupBy: "Group by",
|
|
76
79
|
groupChronological: "chronological",
|
|
77
80
|
groupComponent: "component",
|
|
@@ -99,6 +102,8 @@ var en = {
|
|
|
99
102
|
blocking: "blocking",
|
|
100
103
|
sourceCount: (n) => `${n} source(s)`,
|
|
101
104
|
moreItems: (n) => `+ ${n} more`,
|
|
105
|
+
coalescedReads: (n) => `${n} reads`,
|
|
106
|
+
cascadeComponents: (n) => `${n} components`,
|
|
102
107
|
startRecording: "Start recording",
|
|
103
108
|
stopRecording: "Stop recording",
|
|
104
109
|
rec: "rec",
|
|
@@ -160,6 +165,9 @@ var ko = {
|
|
|
160
165
|
sort: "\uC815\uB82C",
|
|
161
166
|
sortChronological: "\uC2DC\uAC04\uC21C",
|
|
162
167
|
sortSeverity: "\uC2EC\uAC01\uB3C4\uC21C (\uB192\uC740 \uAC83\uBD80\uD130)",
|
|
168
|
+
filterPlaceholder: "\uD544\uD130\u2026",
|
|
169
|
+
filterAria: "\uC2DC\uADF8\uB110 \uD544\uD130",
|
|
170
|
+
filterNoMatches: "\uD544\uD130\uC640 \uC77C\uCE58\uD558\uB294 \uC2DC\uADF8\uB110\uC774 \uC5C6\uC5B4\uC694.",
|
|
163
171
|
groupBy: "\uADF8\uB8F9\uD654",
|
|
164
172
|
groupChronological: "\uC2DC\uAC04\uC21C",
|
|
165
173
|
groupComponent: "\uCEF4\uD3EC\uB10C\uD2B8",
|
|
@@ -187,6 +195,8 @@ var ko = {
|
|
|
187
195
|
blocking: "\uCC28\uB2E8",
|
|
188
196
|
sourceCount: (n) => `\uC18C\uC2A4 ${n}\uAC1C`,
|
|
189
197
|
moreItems: (n) => `\uC678 ${n}\uAC1C \uB354`,
|
|
198
|
+
coalescedReads: (n) => `\uC77D\uAE30 ${n}\uD68C`,
|
|
199
|
+
cascadeComponents: (n) => `\uCEF4\uD3EC\uB10C\uD2B8 ${n}\uAC1C`,
|
|
190
200
|
startRecording: "\uB179\uD654 \uC2DC\uC791",
|
|
191
201
|
stopRecording: "\uB179\uD654 \uC885\uB8CC",
|
|
192
202
|
rec: "\uB179\uD654",
|
|
@@ -795,8 +805,10 @@ function renderBreakdown(members) {
|
|
|
795
805
|
const counts = /* @__PURE__ */ new Map();
|
|
796
806
|
for (const s of members) {
|
|
797
807
|
if (s.kind !== "render") continue;
|
|
798
|
-
|
|
799
|
-
|
|
808
|
+
for (const m of s.members ?? [s]) {
|
|
809
|
+
if (!counts.has(m.component)) order.push(m.component);
|
|
810
|
+
counts.set(m.component, (counts.get(m.component) ?? 0) + 1);
|
|
811
|
+
}
|
|
800
812
|
}
|
|
801
813
|
return order.map((c) => ({ component: c, count: counts.get(c) }));
|
|
802
814
|
}
|
|
@@ -1698,6 +1710,7 @@ function TooltipContent({ s, startedAt }) {
|
|
|
1698
1710
|
s.duration.toFixed(2),
|
|
1699
1711
|
"ms"
|
|
1700
1712
|
] }),
|
|
1713
|
+
(s.count ?? 1) > 1 ? ` \xD7${s.count}` : "",
|
|
1701
1714
|
" ",
|
|
1702
1715
|
at
|
|
1703
1716
|
] });
|
|
@@ -1847,6 +1860,45 @@ function RenderInsights({ signals, onSelect }) {
|
|
|
1847
1860
|
);
|
|
1848
1861
|
}
|
|
1849
1862
|
|
|
1863
|
+
// src/filter.ts
|
|
1864
|
+
function signalSearchText(signal) {
|
|
1865
|
+
switch (signal.kind) {
|
|
1866
|
+
case "render": {
|
|
1867
|
+
const parts = [signal.component];
|
|
1868
|
+
if (signal.changedProps) parts.push(...signal.changedProps);
|
|
1869
|
+
if (signal.members) for (const m of signal.members) parts.push(m.component);
|
|
1870
|
+
return parts.join(" ").toLowerCase();
|
|
1871
|
+
}
|
|
1872
|
+
case "network":
|
|
1873
|
+
return signal.url.toLowerCase();
|
|
1874
|
+
case "web-vital":
|
|
1875
|
+
return signal.name.toLowerCase();
|
|
1876
|
+
case "interaction":
|
|
1877
|
+
return [signal.eventType, signal.target ?? ""].join(" ").toLowerCase();
|
|
1878
|
+
case "forced-reflow": {
|
|
1879
|
+
const f = signal.stack[0];
|
|
1880
|
+
return f ? [f.fnName ?? "", f.file].join(" ").toLowerCase() : "";
|
|
1881
|
+
}
|
|
1882
|
+
case "long-task": {
|
|
1883
|
+
const parts = [];
|
|
1884
|
+
if (signal.scripts)
|
|
1885
|
+
for (const s of signal.scripts) parts.push(s.sourceFunctionName, s.invoker, s.sourceURL);
|
|
1886
|
+
if (signal.attribution)
|
|
1887
|
+
for (const a of signal.attribution) parts.push(a.frame.fnName ?? "", a.frame.file);
|
|
1888
|
+
return parts.join(" ").toLowerCase();
|
|
1889
|
+
}
|
|
1890
|
+
case "layout-shift":
|
|
1891
|
+
return "layout-shift";
|
|
1892
|
+
default:
|
|
1893
|
+
return "";
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
function signalMatchesFilter(signal, query) {
|
|
1897
|
+
const q = query.trim().toLowerCase();
|
|
1898
|
+
if (!q) return true;
|
|
1899
|
+
return signalSearchText(signal).includes(q);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1850
1902
|
// src/panel.tsx
|
|
1851
1903
|
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
1852
1904
|
var RENDER_REASON_COLOR = {
|
|
@@ -1975,7 +2027,7 @@ function summary(s) {
|
|
|
1975
2027
|
case "web-vital":
|
|
1976
2028
|
return `${s.name}: ${s.value.toFixed(2)}`;
|
|
1977
2029
|
case "render":
|
|
1978
|
-
return `${s.component} \u2022 ${s.reason} \u2022 ${s.duration.toFixed(2)}ms`;
|
|
2030
|
+
return `${s.component} \u2022 ${s.reason} \u2022 ${s.duration.toFixed(2)}ms${s.count && s.count > 1 ? ` \u2022 ${s.count} components` : ""}`;
|
|
1979
2031
|
}
|
|
1980
2032
|
}
|
|
1981
2033
|
var detailLabelStyle = { color: "#888", marginRight: "6px" };
|
|
@@ -2060,6 +2112,25 @@ function WebVitalDetail({ s }) {
|
|
|
2060
2112
|
}
|
|
2061
2113
|
function RenderDetail({ s }) {
|
|
2062
2114
|
const { t } = useI18n();
|
|
2115
|
+
if (s.members && s.members.length > 0) {
|
|
2116
|
+
return /* @__PURE__ */ jsxs5("div", { style: { paddingLeft: "12px" }, children: [
|
|
2117
|
+
/* @__PURE__ */ jsxs5("div", { style: detailRowStyle, children: [
|
|
2118
|
+
/* @__PURE__ */ jsx5("span", { style: detailLabelStyle, children: t.duration }),
|
|
2119
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
2120
|
+
s.duration.toFixed(3),
|
|
2121
|
+
"ms"
|
|
2122
|
+
] })
|
|
2123
|
+
] }),
|
|
2124
|
+
/* @__PURE__ */ jsxs5("div", { style: detailRowStyle, children: [
|
|
2125
|
+
/* @__PURE__ */ jsx5("span", { style: detailLabelStyle, children: t.at }),
|
|
2126
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
2127
|
+
s.at.toFixed(2),
|
|
2128
|
+
"ms"
|
|
2129
|
+
] })
|
|
2130
|
+
] }),
|
|
2131
|
+
/* @__PURE__ */ jsx5("div", { style: { marginTop: "6px" }, children: /* @__PURE__ */ jsx5(CascadeMembers, { members: s.members }) })
|
|
2132
|
+
] });
|
|
2133
|
+
}
|
|
2063
2134
|
return /* @__PURE__ */ jsxs5("div", { style: { paddingLeft: "12px" }, children: [
|
|
2064
2135
|
/* @__PURE__ */ jsxs5("div", { style: detailRowStyle, children: [
|
|
2065
2136
|
/* @__PURE__ */ jsx5("span", { style: detailLabelStyle, children: t.component }),
|
|
@@ -2350,6 +2421,7 @@ function SummaryLine({ signal }) {
|
|
|
2350
2421
|
] });
|
|
2351
2422
|
}
|
|
2352
2423
|
if (signal.kind === "forced-reflow") {
|
|
2424
|
+
const count = signal.count ?? 1;
|
|
2353
2425
|
return /* @__PURE__ */ jsxs5("span", { children: [
|
|
2354
2426
|
"@ ",
|
|
2355
2427
|
signal.at.toFixed(1),
|
|
@@ -2358,6 +2430,10 @@ function SummaryLine({ signal }) {
|
|
|
2358
2430
|
/* @__PURE__ */ jsxs5("span", { style: { color }, children: [
|
|
2359
2431
|
signal.duration.toFixed(2),
|
|
2360
2432
|
"ms"
|
|
2433
|
+
] }),
|
|
2434
|
+
count > 1 && /* @__PURE__ */ jsxs5("span", { style: { color: "#888" }, children: [
|
|
2435
|
+
" \u2022 ",
|
|
2436
|
+
t.coalescedReads(count)
|
|
2361
2437
|
] })
|
|
2362
2438
|
] });
|
|
2363
2439
|
}
|
|
@@ -2379,7 +2455,8 @@ function SummaryLine({ signal }) {
|
|
|
2379
2455
|
/* @__PURE__ */ jsxs5("span", { style: { color }, children: [
|
|
2380
2456
|
signal.duration.toFixed(2),
|
|
2381
2457
|
"ms"
|
|
2382
|
-
] })
|
|
2458
|
+
] }),
|
|
2459
|
+
signal.count && signal.count > 1 && /* @__PURE__ */ jsx5("span", { style: { color: "#888" }, children: t.cascadeComponents(signal.count) })
|
|
2383
2460
|
] });
|
|
2384
2461
|
}
|
|
2385
2462
|
if (signal.kind === "network") {
|
|
@@ -2399,9 +2476,6 @@ function SummaryLine({ signal }) {
|
|
|
2399
2476
|
}
|
|
2400
2477
|
return /* @__PURE__ */ jsx5("span", { children: summary(signal) });
|
|
2401
2478
|
}
|
|
2402
|
-
function cascadeRootOf(list) {
|
|
2403
|
-
return list.find((s) => s.reason === "state") ?? list.find((s) => s.reason === "mount") ?? [...list].sort((a, b) => a.depth - b.depth)[0];
|
|
2404
|
-
}
|
|
2405
2479
|
function tabSupportsGrouping(kind) {
|
|
2406
2480
|
return kind === "render" || kind === "forced-reflow";
|
|
2407
2481
|
}
|
|
@@ -2445,22 +2519,17 @@ function groupSignals(signals, mode, kind) {
|
|
|
2445
2519
|
const byName = /* @__PURE__ */ new Map();
|
|
2446
2520
|
for (const s of signals) {
|
|
2447
2521
|
if (s.kind !== "render") continue;
|
|
2448
|
-
const
|
|
2449
|
-
|
|
2450
|
-
|
|
2522
|
+
for (const m of s.members ?? [s]) {
|
|
2523
|
+
if (!byName.has(m.component)) byName.set(m.component, []);
|
|
2524
|
+
byName.get(m.component).push(m);
|
|
2525
|
+
}
|
|
2451
2526
|
}
|
|
2452
2527
|
return Array.from(byName.entries()).sort((a, b) => b[1].length - a[1].length).map(([label, list]) => ({ label, count: list.length, signals: list }));
|
|
2453
2528
|
}
|
|
2454
2529
|
if (mode === "commit" && kind === "render") {
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
if (!byCommit.has(s.commitId)) byCommit.set(s.commitId, []);
|
|
2459
|
-
byCommit.get(s.commitId).push(s);
|
|
2460
|
-
}
|
|
2461
|
-
return Array.from(byCommit.entries()).sort((a, b) => a[0] - b[0]).map(([id, list]) => {
|
|
2462
|
-
const root = cascadeRootOf(list);
|
|
2463
|
-
return { label: root?.component ?? `commit ${id}`, count: list.length, signals: list };
|
|
2530
|
+
return signals.filter((s) => s.kind === "render").sort((a, b) => a.commitId - b.commitId).map((s) => {
|
|
2531
|
+
const list = s.members ?? [s];
|
|
2532
|
+
return { label: s.component, count: list.length, signals: list };
|
|
2464
2533
|
});
|
|
2465
2534
|
}
|
|
2466
2535
|
if (mode === "source" && kind === "forced-reflow") {
|
|
@@ -2595,6 +2664,7 @@ function Panel(props) {
|
|
|
2595
2664
|
const [expandedKey, setExpandedKey] = useState3(null);
|
|
2596
2665
|
const [groupMode, setGroupMode] = useState3({});
|
|
2597
2666
|
const [sortMode, setSortMode] = useState3({});
|
|
2667
|
+
const [filterText, setFilterText] = useState3({});
|
|
2598
2668
|
const activeOverlayCount = useRef2(0);
|
|
2599
2669
|
useEffect(() => () => hideAllOverlays(), []);
|
|
2600
2670
|
function handleHover(signal) {
|
|
@@ -2801,7 +2871,21 @@ function Panel(props) {
|
|
|
2801
2871
|
]
|
|
2802
2872
|
}
|
|
2803
2873
|
)
|
|
2804
|
-
] })
|
|
2874
|
+
] }),
|
|
2875
|
+
/* @__PURE__ */ jsx5(
|
|
2876
|
+
"input",
|
|
2877
|
+
{
|
|
2878
|
+
type: "text",
|
|
2879
|
+
"aria-label": t.filterAria,
|
|
2880
|
+
placeholder: t.filterPlaceholder,
|
|
2881
|
+
value: filterText[activeKind] ?? "",
|
|
2882
|
+
onInput: (e) => {
|
|
2883
|
+
setFilterText({ ...filterText, [activeKind]: e.target.value });
|
|
2884
|
+
setExpandedKey(null);
|
|
2885
|
+
},
|
|
2886
|
+
style: { marginLeft: "auto", background: "#1a1a1a", color: "#e6e6e6", border: "1px solid #2a2a2a", borderRadius: "4px", padding: "2px 6px", fontSize: "11px", width: "120px" }
|
|
2887
|
+
}
|
|
2888
|
+
)
|
|
2805
2889
|
] }),
|
|
2806
2890
|
activeTab === "timeline" && /* @__PURE__ */ jsx5("div", { style: { flexGrow: 1, overflowY: "auto", paddingTop: "4px", paddingBottom: "24px" }, children: /* @__PURE__ */ jsx5(
|
|
2807
2891
|
Timeline,
|
|
@@ -2822,7 +2906,7 @@ function Panel(props) {
|
|
|
2822
2906
|
activeKind === "render" && activeTab !== "timeline" && /* @__PURE__ */ jsx5(
|
|
2823
2907
|
RenderInsights,
|
|
2824
2908
|
{
|
|
2825
|
-
signals: grouped.render.
|
|
2909
|
+
signals: grouped.render.flatMap((s) => s.kind === "render" ? s.members ?? [s] : []).filter((s) => signalMatchesFilter(s, filterText.render ?? "")),
|
|
2826
2910
|
onSelect: () => {
|
|
2827
2911
|
setGroupMode({ ...groupMode, render: "component" });
|
|
2828
2912
|
setExpandedKey(null);
|
|
@@ -2845,7 +2929,10 @@ function Panel(props) {
|
|
|
2845
2929
|
}
|
|
2846
2930
|
),
|
|
2847
2931
|
/* @__PURE__ */ jsx5("ul", { style: { listStyle: "none", margin: 0, padding: 0, overflowY: "auto", flexGrow: 1, display: activeTab === "timeline" ? "none" : void 0 }, children: activeKind && (() => {
|
|
2848
|
-
const
|
|
2932
|
+
const filtered = grouped[activeKind].filter(
|
|
2933
|
+
(s) => signalMatchesFilter(s, filterText[activeKind] ?? "")
|
|
2934
|
+
);
|
|
2935
|
+
const baseSignals = (sortMode[activeKind] ?? "chronological") === "severity" ? sortSignalsBySeverity(filtered) : filtered;
|
|
2849
2936
|
return groupSignals(baseSignals, groupMode[activeKind] ?? "chronological", activeKind);
|
|
2850
2937
|
})().map((g, gi) => {
|
|
2851
2938
|
const currentMode = groupMode[activeKind] ?? "chronological";
|
|
@@ -2903,7 +2990,8 @@ function Panel(props) {
|
|
|
2903
2990
|
},
|
|
2904
2991
|
key
|
|
2905
2992
|
);
|
|
2906
|
-
}) })
|
|
2993
|
+
}) }),
|
|
2994
|
+
activeKind && activeTab !== "timeline" && (filterText[activeKind] ?? "").trim() !== "" && grouped[activeKind].length > 0 && grouped[activeKind].every((s) => !signalMatchesFilter(s, filterText[activeKind] ?? "")) && /* @__PURE__ */ jsx5("div", { style: { padding: "12px 8px", fontSize: "11px", color: "#888" }, children: t.filterNoMatches })
|
|
2907
2995
|
] })
|
|
2908
2996
|
] });
|
|
2909
2997
|
}
|