rails-profiler 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/app/assets/builds/profiler.css +82 -0
- data/app/assets/builds/profiler.js +174 -8
- data/lib/profiler/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c1f16fad7d5792dda0491401519802bd402be3b6c952b898cc0930e675a0d49
|
|
4
|
+
data.tar.gz: 6ea6e3ca98b297f89b32da94a56cf73ff38788e8d31a9570dd58c072bfb58930
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e487bc2fc57bd9a1dee6eabdc45db629dd752e0df2ce95e74fc31701583ab5d8de72fa57b7b19dc81d6c412c6bec2b4a814768af84ab80da5af6c61b3fb063a
|
|
7
|
+
data.tar.gz: 0e3572af0b7f7bca2d1bc132da45173ad9dd2f29010d5685a987d247a89c114d5c76846781a5383aa7bee2fdef038059aa66ac8c9e8bf7ed1245c8a6eaf50354
|
|
@@ -1896,6 +1896,61 @@ a.profiler-toolbar-item.profiler-text--warning::after {
|
|
|
1896
1896
|
cursor: pointer;
|
|
1897
1897
|
}
|
|
1898
1898
|
|
|
1899
|
+
.profiler-preset-btn {
|
|
1900
|
+
height: 28px;
|
|
1901
|
+
padding: 0 10px;
|
|
1902
|
+
background: transparent;
|
|
1903
|
+
border: 1px solid var(--profiler-border);
|
|
1904
|
+
border-radius: var(--profiler-radius-full);
|
|
1905
|
+
color: var(--profiler-text-muted);
|
|
1906
|
+
font-family: var(--profiler-font-sans);
|
|
1907
|
+
font-size: var(--profiler-text-xs);
|
|
1908
|
+
font-weight: 500;
|
|
1909
|
+
cursor: pointer;
|
|
1910
|
+
transition: all var(--profiler-transition-base);
|
|
1911
|
+
white-space: nowrap;
|
|
1912
|
+
}
|
|
1913
|
+
.profiler-preset-btn:hover {
|
|
1914
|
+
border-color: var(--profiler-accent);
|
|
1915
|
+
color: var(--profiler-accent);
|
|
1916
|
+
background: var(--profiler-accent-bg);
|
|
1917
|
+
}
|
|
1918
|
+
.profiler-preset-btn--active {
|
|
1919
|
+
border-color: var(--profiler-accent);
|
|
1920
|
+
background: var(--profiler-accent);
|
|
1921
|
+
color: var(--profiler-text-on-accent, #fff);
|
|
1922
|
+
}
|
|
1923
|
+
.profiler-preset-btn--active:hover {
|
|
1924
|
+
opacity: 0.85;
|
|
1925
|
+
color: var(--profiler-text-on-accent, #fff);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
.sortable {
|
|
1929
|
+
cursor: pointer;
|
|
1930
|
+
user-select: none;
|
|
1931
|
+
white-space: nowrap;
|
|
1932
|
+
transition: color var(--profiler-transition-base);
|
|
1933
|
+
}
|
|
1934
|
+
.sortable:hover {
|
|
1935
|
+
color: var(--profiler-text) !important;
|
|
1936
|
+
}
|
|
1937
|
+
.sortable--active {
|
|
1938
|
+
color: var(--profiler-text) !important;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
.sort-icon {
|
|
1942
|
+
font-size: 10px;
|
|
1943
|
+
margin-left: 4px;
|
|
1944
|
+
vertical-align: middle;
|
|
1945
|
+
}
|
|
1946
|
+
.sort-icon--idle {
|
|
1947
|
+
opacity: 0.35;
|
|
1948
|
+
}
|
|
1949
|
+
.sort-icon--active {
|
|
1950
|
+
color: var(--profiler-accent);
|
|
1951
|
+
opacity: 1;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1899
1954
|
.profiler-timeline {
|
|
1900
1955
|
margin: 24px 0;
|
|
1901
1956
|
padding: 24px;
|
|
@@ -2321,6 +2376,33 @@ a.profiler-toolbar-item.profiler-text--warning::after {
|
|
|
2321
2376
|
color: var(--profiler-text-subtle);
|
|
2322
2377
|
font-family: var(--profiler-font-sans);
|
|
2323
2378
|
}
|
|
2379
|
+
.profiler-flamegraph__search {
|
|
2380
|
+
display: flex;
|
|
2381
|
+
align-items: center;
|
|
2382
|
+
gap: 8px;
|
|
2383
|
+
}
|
|
2384
|
+
.profiler-flamegraph__search-input {
|
|
2385
|
+
height: 28px;
|
|
2386
|
+
padding: 0 8px;
|
|
2387
|
+
background: var(--profiler-bg-lighter);
|
|
2388
|
+
border: 1px solid var(--profiler-border);
|
|
2389
|
+
border-radius: var(--profiler-radius-sm);
|
|
2390
|
+
color: var(--profiler-text);
|
|
2391
|
+
font-family: var(--profiler-font-mono);
|
|
2392
|
+
font-size: var(--profiler-text-xs);
|
|
2393
|
+
width: 200px;
|
|
2394
|
+
transition: border-color var(--profiler-transition-base);
|
|
2395
|
+
}
|
|
2396
|
+
.profiler-flamegraph__search-input:focus {
|
|
2397
|
+
outline: none;
|
|
2398
|
+
border-color: var(--profiler-accent);
|
|
2399
|
+
}
|
|
2400
|
+
.profiler-flamegraph__match-count {
|
|
2401
|
+
font-size: var(--profiler-text-xs);
|
|
2402
|
+
font-family: var(--profiler-font-mono);
|
|
2403
|
+
color: var(--profiler-text-muted);
|
|
2404
|
+
white-space: nowrap;
|
|
2405
|
+
}
|
|
2324
2406
|
.profiler-flamegraph__canvas-container {
|
|
2325
2407
|
position: relative;
|
|
2326
2408
|
border: 1px solid var(--profiler-border);
|
|
@@ -718,11 +718,26 @@
|
|
|
718
718
|
if (!bytes) return "-";
|
|
719
719
|
return (bytes / 1024 / 1024).toFixed(2) + " MB";
|
|
720
720
|
}
|
|
721
|
+
var PRESETS = [
|
|
722
|
+
{ key: "slow", label: "Slow" },
|
|
723
|
+
{ key: "many_queries", label: "Many queries" },
|
|
724
|
+
{ key: "errors", label: "Errors" },
|
|
725
|
+
{ key: "has_exception", label: "Has exception" }
|
|
726
|
+
];
|
|
721
727
|
function ProfileList() {
|
|
728
|
+
const params = new URLSearchParams(window.location.search);
|
|
722
729
|
const initialSection = () => {
|
|
723
|
-
const s3 =
|
|
730
|
+
const s3 = params.get("section");
|
|
724
731
|
return s3 === "http" || s3 === "jobs" || s3 === "outbound" ? s3 : "http";
|
|
725
732
|
};
|
|
733
|
+
const initialSort = () => {
|
|
734
|
+
const col = params.get("sort");
|
|
735
|
+
const dir = params.get("dir");
|
|
736
|
+
return {
|
|
737
|
+
col: col === "duration" || col === "memory" || col === "status" || col === "queries" ? col : null,
|
|
738
|
+
dir: dir === "desc" ? "desc" : "asc"
|
|
739
|
+
};
|
|
740
|
+
};
|
|
726
741
|
const [section, setSection] = d2(initialSection);
|
|
727
742
|
const [profiles, setProfiles] = d2([]);
|
|
728
743
|
const [jobs, setJobs] = d2([]);
|
|
@@ -740,6 +755,11 @@
|
|
|
740
755
|
const [httpMethod, setHttpMethod] = d2("");
|
|
741
756
|
const [httpStatus, setHttpStatus] = d2("");
|
|
742
757
|
const [httpDuration, setHttpDuration] = d2("");
|
|
758
|
+
const [httpPreset, setHttpPreset] = d2(() => {
|
|
759
|
+
const p3 = params.get("preset");
|
|
760
|
+
return PRESETS.some((pr) => pr.key === p3) ? p3 : "";
|
|
761
|
+
});
|
|
762
|
+
const [httpSort, setHttpSort] = d2(initialSort);
|
|
743
763
|
const [jobSearch, setJobSearch] = d2("");
|
|
744
764
|
const [jobStatus, setJobStatus] = d2("");
|
|
745
765
|
const [jobDuration, setJobDuration] = d2("");
|
|
@@ -759,6 +779,30 @@
|
|
|
759
779
|
if (section === "jobs") loadJobs();
|
|
760
780
|
if (section === "outbound") loadOutbound();
|
|
761
781
|
}, []);
|
|
782
|
+
y2(() => {
|
|
783
|
+
const url = new URL(window.location.href);
|
|
784
|
+
if (httpSort.col) {
|
|
785
|
+
url.searchParams.set("sort", httpSort.col);
|
|
786
|
+
url.searchParams.set("dir", httpSort.dir);
|
|
787
|
+
} else {
|
|
788
|
+
url.searchParams.delete("sort");
|
|
789
|
+
url.searchParams.delete("dir");
|
|
790
|
+
}
|
|
791
|
+
if (httpPreset) {
|
|
792
|
+
url.searchParams.set("preset", httpPreset);
|
|
793
|
+
} else {
|
|
794
|
+
url.searchParams.delete("preset");
|
|
795
|
+
}
|
|
796
|
+
history.replaceState(null, "", url.toString());
|
|
797
|
+
}, [httpSort, httpPreset]);
|
|
798
|
+
const toggleHttpSort = (col) => {
|
|
799
|
+
setHttpSort(
|
|
800
|
+
(prev) => prev.col === col ? { col, dir: prev.dir === "asc" ? "desc" : "asc" } : { col, dir: "asc" }
|
|
801
|
+
);
|
|
802
|
+
};
|
|
803
|
+
const togglePreset = (key) => {
|
|
804
|
+
setHttpPreset((prev) => prev === key ? "" : key);
|
|
805
|
+
};
|
|
762
806
|
const loadJobs = () => {
|
|
763
807
|
if (jobsLoaded) return;
|
|
764
808
|
setLoadingJobs(true);
|
|
@@ -796,6 +840,8 @@
|
|
|
796
840
|
setHttpMethod("");
|
|
797
841
|
setHttpStatus("");
|
|
798
842
|
setHttpDuration("");
|
|
843
|
+
setHttpPreset("");
|
|
844
|
+
setHttpSort({ col: null, dir: "asc" });
|
|
799
845
|
setJobSearch("");
|
|
800
846
|
setJobStatus("");
|
|
801
847
|
setJobDuration("");
|
|
@@ -870,8 +916,36 @@
|
|
|
870
916
|
if (httpDuration) {
|
|
871
917
|
if (httpDuration === "lt100" ? p3.duration >= 100 : p3.duration < parseInt(httpDuration)) return false;
|
|
872
918
|
}
|
|
919
|
+
if (httpPreset === "slow" && p3.duration < 500) return false;
|
|
920
|
+
if (httpPreset === "many_queries" && (p3.collectors_data?.database?.total_queries ?? 0) <= 20) return false;
|
|
921
|
+
if (httpPreset === "errors" && p3.status < 500) return false;
|
|
922
|
+
if (httpPreset === "has_exception" && !p3.collectors_data?.exception) return false;
|
|
873
923
|
return true;
|
|
874
924
|
});
|
|
925
|
+
const sortedProfiles = httpSort.col ? [...filteredProfiles].sort((a3, b) => {
|
|
926
|
+
let av, bv;
|
|
927
|
+
switch (httpSort.col) {
|
|
928
|
+
case "duration":
|
|
929
|
+
av = a3.duration;
|
|
930
|
+
bv = b.duration;
|
|
931
|
+
break;
|
|
932
|
+
case "memory":
|
|
933
|
+
av = a3.memory ?? 0;
|
|
934
|
+
bv = b.memory ?? 0;
|
|
935
|
+
break;
|
|
936
|
+
case "status":
|
|
937
|
+
av = a3.status;
|
|
938
|
+
bv = b.status;
|
|
939
|
+
break;
|
|
940
|
+
case "queries":
|
|
941
|
+
av = a3.collectors_data?.database?.total_queries ?? 0;
|
|
942
|
+
bv = b.collectors_data?.database?.total_queries ?? 0;
|
|
943
|
+
break;
|
|
944
|
+
default:
|
|
945
|
+
return 0;
|
|
946
|
+
}
|
|
947
|
+
return httpSort.dir === "asc" ? av - bv : bv - av;
|
|
948
|
+
}) : filteredProfiles;
|
|
875
949
|
const filteredJobs = jobs.filter((p3) => {
|
|
876
950
|
if (jobSearch && !p3.path.toLowerCase().includes(jobSearch.toLowerCase())) return false;
|
|
877
951
|
if (jobStatus === "failed" && p3.status !== 500) return false;
|
|
@@ -894,9 +968,13 @@
|
|
|
894
968
|
}
|
|
895
969
|
return true;
|
|
896
970
|
});
|
|
897
|
-
const httpFiltersActive = !!(httpSearch || httpMethod || httpStatus || httpDuration);
|
|
971
|
+
const httpFiltersActive = !!(httpSearch || httpMethod || httpStatus || httpDuration || httpPreset);
|
|
898
972
|
const jobFiltersActive = !!(jobSearch || jobStatus || jobDuration);
|
|
899
973
|
const outboundFiltersActive = !!(outboundSearch || outboundMethod || outboundStatus);
|
|
974
|
+
const sortIcon = (col) => {
|
|
975
|
+
if (httpSort.col !== col) return /* @__PURE__ */ u3("span", { class: "sort-icon sort-icon--idle", children: "\u21C5" });
|
|
976
|
+
return /* @__PURE__ */ u3("span", { class: "sort-icon sort-icon--active", children: httpSort.dir === "asc" ? "\u25B2" : "\u25BC" });
|
|
977
|
+
};
|
|
900
978
|
return /* @__PURE__ */ u3("div", { class: "container", children: [
|
|
901
979
|
/* @__PURE__ */ u3("div", { class: "header", children: [
|
|
902
980
|
/* @__PURE__ */ u3("h1", { children: [
|
|
@@ -925,6 +1003,15 @@
|
|
|
925
1003
|
/* @__PURE__ */ u3("div", { class: "profiler-empty__title", children: "No profiles found" }),
|
|
926
1004
|
/* @__PURE__ */ u3("p", { class: "profiler-empty__description", children: "Make some requests to your application to see profiling data" })
|
|
927
1005
|
] }) : /* @__PURE__ */ u3(k, { children: [
|
|
1006
|
+
/* @__PURE__ */ u3("div", { class: "profiler-action-bar profiler-mb-2", children: /* @__PURE__ */ u3("div", { class: "profiler-filter-group", children: PRESETS.map((preset) => /* @__PURE__ */ u3(
|
|
1007
|
+
"button",
|
|
1008
|
+
{
|
|
1009
|
+
class: `profiler-preset-btn${httpPreset === preset.key ? " profiler-preset-btn--active" : ""}`,
|
|
1010
|
+
onClick: () => togglePreset(preset.key),
|
|
1011
|
+
children: preset.label
|
|
1012
|
+
},
|
|
1013
|
+
preset.key
|
|
1014
|
+
)) }) }),
|
|
928
1015
|
/* @__PURE__ */ u3("div", { class: "profiler-action-bar profiler-mb-3", children: [
|
|
929
1016
|
/* @__PURE__ */ u3("div", { class: "profiler-filter-group", children: [
|
|
930
1017
|
/* @__PURE__ */ u3(
|
|
@@ -974,13 +1061,26 @@
|
|
|
974
1061
|
/* @__PURE__ */ u3("th", { children: "Time" }),
|
|
975
1062
|
/* @__PURE__ */ u3("th", { children: "Method" }),
|
|
976
1063
|
/* @__PURE__ */ u3("th", { children: "Path" }),
|
|
977
|
-
/* @__PURE__ */ u3("th", {
|
|
978
|
-
|
|
979
|
-
|
|
1064
|
+
/* @__PURE__ */ u3("th", { class: `sortable${httpSort.col === "duration" ? " sortable--active" : ""}`, onClick: () => toggleHttpSort("duration"), children: [
|
|
1065
|
+
"Duration ",
|
|
1066
|
+
sortIcon("duration")
|
|
1067
|
+
] }),
|
|
1068
|
+
/* @__PURE__ */ u3("th", { class: `sortable${httpSort.col === "queries" ? " sortable--active" : ""}`, onClick: () => toggleHttpSort("queries"), children: [
|
|
1069
|
+
"Queries ",
|
|
1070
|
+
sortIcon("queries")
|
|
1071
|
+
] }),
|
|
1072
|
+
/* @__PURE__ */ u3("th", { class: `sortable${httpSort.col === "memory" ? " sortable--active" : ""}`, onClick: () => toggleHttpSort("memory"), children: [
|
|
1073
|
+
"Memory ",
|
|
1074
|
+
sortIcon("memory")
|
|
1075
|
+
] }),
|
|
1076
|
+
/* @__PURE__ */ u3("th", { class: `sortable${httpSort.col === "status" ? " sortable--active" : ""}`, onClick: () => toggleHttpSort("status"), children: [
|
|
1077
|
+
"Status ",
|
|
1078
|
+
sortIcon("status")
|
|
1079
|
+
] }),
|
|
980
1080
|
/* @__PURE__ */ u3("th", { children: "Token" }),
|
|
981
1081
|
/* @__PURE__ */ u3("th", {})
|
|
982
1082
|
] }) }),
|
|
983
|
-
/* @__PURE__ */ u3("tbody", { children:
|
|
1083
|
+
/* @__PURE__ */ u3("tbody", { children: sortedProfiles.map((p3) => /* @__PURE__ */ u3("tr", { children: [
|
|
984
1084
|
/* @__PURE__ */ u3("td", { children: formatTime(p3.started_at) }),
|
|
985
1085
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: methodClass(p3.method), children: p3.method }) }),
|
|
986
1086
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("a", { href: `${BASE}/profiles/${p3.token}`, children: p3.path }) }),
|
|
@@ -988,6 +1088,7 @@
|
|
|
988
1088
|
p3.duration.toFixed(2),
|
|
989
1089
|
" ms"
|
|
990
1090
|
] }) }),
|
|
1091
|
+
/* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "\u2014" }),
|
|
991
1092
|
/* @__PURE__ */ u3("td", { children: formatMemory(p3.memory) }),
|
|
992
1093
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: statusClass(p3.status), children: p3.status }) }),
|
|
993
1094
|
/* @__PURE__ */ u3("td", { class: "profiler-text--xs profiler-text--mono profiler-text--muted", children: /* @__PURE__ */ u3("button", { class: "token-copy", onClick: () => copyToken(p3.token), title: "Copy full token", children: copiedToken === p3.token ? "\u2713" : p3.token.substring(0, 8) + "\u2026" }) }),
|
|
@@ -1567,6 +1668,7 @@
|
|
|
1567
1668
|
this.dpr = 1;
|
|
1568
1669
|
this.hoveredFrame = null;
|
|
1569
1670
|
this.zoomStack = [];
|
|
1671
|
+
this.searchQuery = "";
|
|
1570
1672
|
this.isPanning = false;
|
|
1571
1673
|
this.panStartX = 0;
|
|
1572
1674
|
this.panStartViewport = { start: 0, end: 0 };
|
|
@@ -1789,6 +1891,10 @@
|
|
|
1789
1891
|
this.render();
|
|
1790
1892
|
}
|
|
1791
1893
|
}
|
|
1894
|
+
setSearchQuery(query) {
|
|
1895
|
+
this.searchQuery = query;
|
|
1896
|
+
this.render();
|
|
1897
|
+
}
|
|
1792
1898
|
render() {
|
|
1793
1899
|
const ctx = this.ctx;
|
|
1794
1900
|
const w3 = this.canvas.width / this.dpr;
|
|
@@ -1798,6 +1904,15 @@
|
|
|
1798
1904
|
const style = getComputedStyle(this.canvas);
|
|
1799
1905
|
const textColor = style.getPropertyValue("--profiler-text").trim() || "#eef2f7";
|
|
1800
1906
|
const textMuted = style.getPropertyValue("--profiler-text-muted").trim() || "#5e7080";
|
|
1907
|
+
const searchLower = this.searchQuery.toLowerCase();
|
|
1908
|
+
const hasSearch = searchLower.length > 0;
|
|
1909
|
+
if (hasSearch && this.callbacks.onSearchResults) {
|
|
1910
|
+
let matchCount = 0;
|
|
1911
|
+
for (const f4 of this.frames) {
|
|
1912
|
+
if (f4.node.name.toLowerCase().includes(searchLower)) matchCount++;
|
|
1913
|
+
}
|
|
1914
|
+
this.callbacks.onSearchResults(matchCount, this.frames.length);
|
|
1915
|
+
}
|
|
1801
1916
|
for (const frame of this.frames) {
|
|
1802
1917
|
const x2 = (frame.absStart - this.viewport.start) / vpRange * w3;
|
|
1803
1918
|
const fw = (frame.absEnd - frame.absStart) / vpRange * w3;
|
|
@@ -1805,8 +1920,9 @@
|
|
|
1805
1920
|
if (x2 + fw < 0 || x2 > w3 || fw < 0.5) continue;
|
|
1806
1921
|
const color = CATEGORY_COLORS[frame.node.category] || "#a78bfa";
|
|
1807
1922
|
const isHovered = frame === this.hoveredFrame;
|
|
1923
|
+
const isMatch = !hasSearch || frame.node.name.toLowerCase().includes(searchLower);
|
|
1808
1924
|
ctx.fillStyle = isHovered ? this.lightenColor(color, 0.2) : color;
|
|
1809
|
-
ctx.globalAlpha = isHovered ? 1 : 0.85;
|
|
1925
|
+
ctx.globalAlpha = hasSearch && !isMatch ? 0.2 : isHovered ? 1 : 0.85;
|
|
1810
1926
|
this.roundRect(ctx, x2, y3, fw, FRAME_HEIGHT, 3);
|
|
1811
1927
|
ctx.fill();
|
|
1812
1928
|
ctx.globalAlpha = 1;
|
|
@@ -1815,8 +1931,15 @@
|
|
|
1815
1931
|
ctx.lineWidth = 1.5;
|
|
1816
1932
|
this.roundRect(ctx, x2, y3, fw, FRAME_HEIGHT, 3);
|
|
1817
1933
|
ctx.stroke();
|
|
1934
|
+
} else if (hasSearch && isMatch) {
|
|
1935
|
+
ctx.strokeStyle = "#ffffff";
|
|
1936
|
+
ctx.lineWidth = 1;
|
|
1937
|
+
ctx.globalAlpha = 0.5;
|
|
1938
|
+
this.roundRect(ctx, x2, y3, fw, FRAME_HEIGHT, 3);
|
|
1939
|
+
ctx.stroke();
|
|
1940
|
+
ctx.globalAlpha = 1;
|
|
1818
1941
|
}
|
|
1819
|
-
if (fw > MIN_TEXT_WIDTH) {
|
|
1942
|
+
if (fw > MIN_TEXT_WIDTH && isMatch) {
|
|
1820
1943
|
ctx.fillStyle = this.getTextColor(color);
|
|
1821
1944
|
ctx.font = '11px "JetBrains Mono", monospace';
|
|
1822
1945
|
ctx.textBaseline = "middle";
|
|
@@ -2041,7 +2164,11 @@
|
|
|
2041
2164
|
const rendererRef = A2(null);
|
|
2042
2165
|
const tooltipRef = A2(null);
|
|
2043
2166
|
const breadcrumbsRef = A2(null);
|
|
2167
|
+
const searchInputRef = A2(null);
|
|
2044
2168
|
const [isZoomed, setIsZoomed] = d2(false);
|
|
2169
|
+
const [searchQuery, setSearchQuery] = d2("");
|
|
2170
|
+
const [matchCount, setMatchCount] = d2(0);
|
|
2171
|
+
const [totalCount, setTotalCount] = d2(0);
|
|
2045
2172
|
const data = flamegraphData;
|
|
2046
2173
|
y2(() => {
|
|
2047
2174
|
if (!data?.root_events?.length || !canvasRef.current || !containerRef.current) return;
|
|
@@ -2073,6 +2200,10 @@
|
|
|
2073
2200
|
onZoomChange: (ancestors) => {
|
|
2074
2201
|
breadcrumbs.update(ancestors);
|
|
2075
2202
|
setIsZoomed(ancestors.length > 0);
|
|
2203
|
+
},
|
|
2204
|
+
onSearchResults: (match, total) => {
|
|
2205
|
+
setMatchCount(match);
|
|
2206
|
+
setTotalCount(total);
|
|
2076
2207
|
}
|
|
2077
2208
|
});
|
|
2078
2209
|
rendererRef.current = renderer;
|
|
@@ -2091,6 +2222,23 @@
|
|
|
2091
2222
|
breadcrumbsRef.current = null;
|
|
2092
2223
|
};
|
|
2093
2224
|
}, [data]);
|
|
2225
|
+
y2(() => {
|
|
2226
|
+
rendererRef.current?.setSearchQuery(searchQuery);
|
|
2227
|
+
if (!searchQuery) {
|
|
2228
|
+
setMatchCount(0);
|
|
2229
|
+
setTotalCount(0);
|
|
2230
|
+
}
|
|
2231
|
+
}, [searchQuery]);
|
|
2232
|
+
y2(() => {
|
|
2233
|
+
const onKeyDown = (e3) => {
|
|
2234
|
+
if ((e3.ctrlKey || e3.metaKey) && e3.key === "f") {
|
|
2235
|
+
e3.preventDefault();
|
|
2236
|
+
searchInputRef.current?.focus();
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
document.addEventListener("keydown", onKeyDown, { capture: true });
|
|
2240
|
+
return () => document.removeEventListener("keydown", onKeyDown, { capture: true });
|
|
2241
|
+
}, []);
|
|
2094
2242
|
if (!data?.root_events?.length) {
|
|
2095
2243
|
if (!perfData?.events?.length) {
|
|
2096
2244
|
return /* @__PURE__ */ u3("div", { class: "profiler-empty", children: /* @__PURE__ */ u3("p", { class: "profiler-empty__description", children: "No performance events recorded" }) });
|
|
@@ -2161,6 +2309,24 @@
|
|
|
2161
2309
|
] }, cat)) }),
|
|
2162
2310
|
/* @__PURE__ */ u3("div", { class: "profiler-flamegraph__controls", children: [
|
|
2163
2311
|
isZoomed && /* @__PURE__ */ u3("button", { class: "profiler-flamegraph__reset", onClick: handleReset, children: "Reset Zoom" }),
|
|
2312
|
+
/* @__PURE__ */ u3("div", { class: "profiler-flamegraph__search", children: [
|
|
2313
|
+
/* @__PURE__ */ u3(
|
|
2314
|
+
"input",
|
|
2315
|
+
{
|
|
2316
|
+
ref: searchInputRef,
|
|
2317
|
+
type: "text",
|
|
2318
|
+
class: "profiler-flamegraph__search-input",
|
|
2319
|
+
placeholder: "Search events\u2026 (Ctrl+F)",
|
|
2320
|
+
value: searchQuery,
|
|
2321
|
+
onInput: (e3) => setSearchQuery(e3.target.value)
|
|
2322
|
+
}
|
|
2323
|
+
),
|
|
2324
|
+
searchQuery && /* @__PURE__ */ u3("span", { class: "profiler-flamegraph__match-count", children: [
|
|
2325
|
+
matchCount,
|
|
2326
|
+
" / ",
|
|
2327
|
+
totalCount
|
|
2328
|
+
] })
|
|
2329
|
+
] }),
|
|
2164
2330
|
/* @__PURE__ */ u3("span", { class: "profiler-flamegraph__hint", children: "Click to zoom, scroll to zoom in/out, drag to pan" })
|
|
2165
2331
|
] }),
|
|
2166
2332
|
/* @__PURE__ */ u3("div", { class: "profiler-flamegraph__canvas-container", ref: containerRef, children: /* @__PURE__ */ u3(
|
data/lib/profiler/version.rb
CHANGED