rails-profiler 0.20.0 → 0.21.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f2b977ebc11075e9d00ae802a9d8f038819907c18b17073cf6b3a0fa437ba29
4
- data.tar.gz: 4565ab7a98d1a8992cfc3bde0646c6dea355b64d79ab66849c99cab2fff024a3
3
+ metadata.gz: 80a34504e3cb67a077d5f46265cbc265cbf7704f89ad06f90e5ab5df22651a70
4
+ data.tar.gz: d23a6d0d9f107611d66dfe34987447efde5f74f2562efd146d43d910fe431c6a
5
5
  SHA512:
6
- metadata.gz: 48c0c092e6d9145ed04651fbc3e924fa7f3e02d79da737cc6a052e0d9d7b41c590651c086a941d76a983eef29358ee0817b1fd078c41d0a7edd750a948a191f2
7
- data.tar.gz: 7de21cc82a590ff838ed5130970e6947c69e4910ff2ebb5c93431f4d4deba93389cdf446e370fe50a9fce7097526b16ffcd442a18bea82ef29716b0638746028
6
+ metadata.gz: 668d228ede43471fc71a5d65d6498bfd00606865f7cad69dd23e03f79a4e84a2ba5f34dbc2d9e07809df4319946055464b7b8e615ee29e14c8cfafdef10ffc4a
7
+ data.tar.gz: 16926d1a141c64cc72110698cd21e4d5c26d6e9022cd17ad6ba5ba3112aa98ce1917db9d005c112fac62190790433c06b24f1d9a85648edabf679664eb89b30d
@@ -68,8 +68,8 @@
68
68
  }
69
69
  }
70
70
  function P(n2, l3, u4, t3, i3, r3, o3, e3, f4, c3, s3) {
71
- var a3, h3, y3, d3, w3, g2, _2, m3 = t3 && t3.__k || v, b = l3.length;
72
- for (f4 = A(u4, l3, m3, f4, b), a3 = 0; a3 < b; a3++) null != (y3 = u4.__k[a3]) && (h3 = -1 != y3.__i && m3[y3.__i] || p, y3.__i = a3, g2 = z(n2, y3, h3, i3, r3, o3, e3, f4, c3, s3), d3 = y3.__e, y3.ref && h3.ref != y3.ref && (h3.ref && D(h3.ref, null, y3), s3.push(y3.ref, y3.__c || d3, y3)), null == w3 && null != d3 && (w3 = d3), (_2 = !!(4 & y3.__u)) || h3.__k === y3.__k ? f4 = H(y3, f4, n2, _2) : "function" == typeof y3.type && void 0 !== g2 ? f4 = g2 : d3 && (f4 = d3.nextSibling), y3.__u &= -7);
71
+ var a3, h3, y3, d3, w3, g2, _3, m3 = t3 && t3.__k || v, b = l3.length;
72
+ for (f4 = A(u4, l3, m3, f4, b), a3 = 0; a3 < b; a3++) null != (y3 = u4.__k[a3]) && (h3 = -1 != y3.__i && m3[y3.__i] || p, y3.__i = a3, g2 = z(n2, y3, h3, i3, r3, o3, e3, f4, c3, s3), d3 = y3.__e, y3.ref && h3.ref != y3.ref && (h3.ref && D(h3.ref, null, y3), s3.push(y3.ref, y3.__c || d3, y3)), null == w3 && null != d3 && (w3 = d3), (_3 = !!(4 & y3.__u)) || h3.__k === y3.__k ? f4 = H(y3, f4, n2, _3) : "function" == typeof y3.type && void 0 !== g2 ? f4 = g2 : d3 && (f4 = d3.nextSibling), y3.__u &= -7);
73
73
  return u4.__e = w3, f4;
74
74
  }
75
75
  function A(n2, l3, u4, t3, i3) {
@@ -130,11 +130,11 @@
130
130
  };
131
131
  }
132
132
  function z(n2, u4, t3, i3, r3, o3, e3, f4, c3, s3) {
133
- var a3, h3, p3, y3, _2, m3, b, S2, C3, M2, $2, I2, A3, H2, L, T3 = u4.type;
133
+ var a3, h3, p3, y3, _3, m3, b, S2, C3, M2, $2, I2, A3, H2, L, T3 = u4.type;
134
134
  if (void 0 !== u4.constructor) return null;
135
135
  128 & t3.__u && (c3 = !!(32 & t3.__u), o3 = [f4 = u4.__e = t3.__e]), (a3 = l.__b) && a3(u4);
136
136
  n: if ("function" == typeof T3) try {
137
- if (S2 = u4.props, C3 = T3.prototype && T3.prototype.render, M2 = (a3 = T3.contextType) && i3[a3.__c], $2 = a3 ? M2 ? M2.props.value : a3.__ : i3, t3.__c ? b = (h3 = u4.__c = t3.__c).__ = h3.__E : (C3 ? u4.__c = h3 = new T3(S2, $2) : (u4.__c = h3 = new x(S2, $2), h3.constructor = T3, h3.render = G), M2 && M2.sub(h3), h3.state || (h3.state = {}), h3.__n = i3, p3 = h3.__d = true, h3.__h = [], h3._sb = []), C3 && null == h3.__s && (h3.__s = h3.state), C3 && null != T3.getDerivedStateFromProps && (h3.__s == h3.state && (h3.__s = w({}, h3.__s)), w(h3.__s, T3.getDerivedStateFromProps(S2, h3.__s))), y3 = h3.props, _2 = h3.state, h3.__v = u4, p3) C3 && null == T3.getDerivedStateFromProps && null != h3.componentWillMount && h3.componentWillMount(), C3 && null != h3.componentDidMount && h3.__h.push(h3.componentDidMount);
137
+ if (S2 = u4.props, C3 = T3.prototype && T3.prototype.render, M2 = (a3 = T3.contextType) && i3[a3.__c], $2 = a3 ? M2 ? M2.props.value : a3.__ : i3, t3.__c ? b = (h3 = u4.__c = t3.__c).__ = h3.__E : (C3 ? u4.__c = h3 = new T3(S2, $2) : (u4.__c = h3 = new x(S2, $2), h3.constructor = T3, h3.render = G), M2 && M2.sub(h3), h3.state || (h3.state = {}), h3.__n = i3, p3 = h3.__d = true, h3.__h = [], h3._sb = []), C3 && null == h3.__s && (h3.__s = h3.state), C3 && null != T3.getDerivedStateFromProps && (h3.__s == h3.state && (h3.__s = w({}, h3.__s)), w(h3.__s, T3.getDerivedStateFromProps(S2, h3.__s))), y3 = h3.props, _3 = h3.state, h3.__v = u4, p3) C3 && null == T3.getDerivedStateFromProps && null != h3.componentWillMount && h3.componentWillMount(), C3 && null != h3.componentDidMount && h3.__h.push(h3.componentDidMount);
138
138
  else {
139
139
  if (C3 && null == T3.getDerivedStateFromProps && S2 !== y3 && null != h3.componentWillReceiveProps && h3.componentWillReceiveProps(S2, $2), u4.__v == t3.__v || !h3.__e && null != h3.shouldComponentUpdate && false === h3.shouldComponentUpdate(S2, h3.__s, $2)) {
140
140
  u4.__v != t3.__v && (h3.props = S2, h3.state = h3.__s, h3.__d = false), u4.__e = t3.__e, u4.__k = t3.__k, u4.__k.some(function(n3) {
@@ -143,14 +143,14 @@
143
143
  break n;
144
144
  }
145
145
  null != h3.componentWillUpdate && h3.componentWillUpdate(S2, h3.__s, $2), C3 && null != h3.componentDidUpdate && h3.__h.push(function() {
146
- h3.componentDidUpdate(y3, _2, m3);
146
+ h3.componentDidUpdate(y3, _3, m3);
147
147
  });
148
148
  }
149
149
  if (h3.context = $2, h3.props = S2, h3.__P = n2, h3.__e = false, I2 = l.__r, A3 = 0, C3) h3.state = h3.__s, h3.__d = false, I2 && I2(u4), a3 = h3.render(h3.props, h3.state, h3.context), v.push.apply(h3.__h, h3._sb), h3._sb = [];
150
150
  else do {
151
151
  h3.__d = false, I2 && I2(u4), a3 = h3.render(h3.props, h3.state, h3.context), h3.state = h3.__s;
152
152
  } while (h3.__d && ++A3 < 25);
153
- h3.state = h3.__s, null != h3.getChildContext && (i3 = w(w({}, i3), h3.getChildContext())), C3 && !p3 && null != h3.getSnapshotBeforeUpdate && (m3 = h3.getSnapshotBeforeUpdate(y3, _2)), H2 = null != a3 && a3.type === k && null == a3.key ? q(a3.props.children) : a3, f4 = P(n2, d(H2) ? H2 : [H2], u4, t3, i3, r3, o3, e3, f4, c3, s3), h3.base = u4.__e, u4.__u &= -161, h3.__h.length && e3.push(h3), b && (h3.__E = h3.__ = null);
153
+ h3.state = h3.__s, null != h3.getChildContext && (i3 = w(w({}, i3), h3.getChildContext())), C3 && !p3 && null != h3.getSnapshotBeforeUpdate && (m3 = h3.getSnapshotBeforeUpdate(y3, _3)), H2 = null != a3 && a3.type === k && null == a3.key ? q(a3.props.children) : a3, f4 = P(n2, d(H2) ? H2 : [H2], u4, t3, i3, r3, o3, e3, f4, c3, s3), h3.base = u4.__e, u4.__u &= -161, h3.__h.length && e3.push(h3), b && (h3.__E = h3.__ = null);
154
154
  } catch (n3) {
155
155
  if (u4.__v = null, c3 || null != o3) if (n3.then) {
156
156
  for (u4.__u |= c3 ? 160 : 128; f4 && 8 == f4.nodeType && f4.nextSibling; ) f4 = f4.nextSibling;
@@ -184,7 +184,7 @@
184
184
  return "object" != typeof n2 || null == n2 || n2.__b > 0 ? n2 : d(n2) ? n2.map(q) : w({}, n2);
185
185
  }
186
186
  function B(u4, t3, i3, r3, o3, e3, f4, c3, s3) {
187
- var a3, h3, v3, y3, w3, _2, m3, b = i3.props || p, k3 = t3.props, x2 = t3.type;
187
+ var a3, h3, v3, y3, w3, _3, m3, b = i3.props || p, k3 = t3.props, x2 = t3.type;
188
188
  if ("svg" == x2 ? o3 = "http://www.w3.org/2000/svg" : "math" == x2 ? o3 = "http://www.w3.org/1998/Math/MathML" : o3 || (o3 = "http://www.w3.org/1999/xhtml"), null != e3) {
189
189
  for (a3 = 0; a3 < e3.length; a3++) if ((w3 = e3[a3]) && "setAttribute" in w3 == !!x2 && (x2 ? w3.localName == x2 : 3 == w3.nodeType)) {
190
190
  u4 = w3, e3[a3] = null;
@@ -199,10 +199,10 @@
199
199
  else {
200
200
  if (e3 = e3 && n.call(u4.childNodes), !c3 && null != e3) for (b = {}, a3 = 0; a3 < u4.attributes.length; a3++) b[(w3 = u4.attributes[a3]).name] = w3.value;
201
201
  for (a3 in b) w3 = b[a3], "dangerouslySetInnerHTML" == a3 ? v3 = w3 : "children" == a3 || a3 in k3 || "value" == a3 && "defaultValue" in k3 || "checked" == a3 && "defaultChecked" in k3 || F(u4, a3, null, w3, o3);
202
- for (a3 in k3) w3 = k3[a3], "children" == a3 ? y3 = w3 : "dangerouslySetInnerHTML" == a3 ? h3 = w3 : "value" == a3 ? _2 = w3 : "checked" == a3 ? m3 = w3 : c3 && "function" != typeof w3 || b[a3] === w3 || F(u4, a3, w3, b[a3], o3);
202
+ for (a3 in k3) w3 = k3[a3], "children" == a3 ? y3 = w3 : "dangerouslySetInnerHTML" == a3 ? h3 = w3 : "value" == a3 ? _3 = w3 : "checked" == a3 ? m3 = w3 : c3 && "function" != typeof w3 || b[a3] === w3 || F(u4, a3, w3, b[a3], o3);
203
203
  if (h3) c3 || v3 && (h3.__html == v3.__html || h3.__html == u4.innerHTML) || (u4.innerHTML = h3.__html), t3.__k = [];
204
204
  else if (v3 && (u4.innerHTML = ""), P("template" == t3.type ? u4.content : u4, d(y3) ? y3 : [y3], t3, i3, r3, "foreignObject" == x2 ? "http://www.w3.org/1999/xhtml" : o3, e3, f4, e3 ? e3[0] : i3.__k && S(i3, 0), c3, s3), null != e3) for (a3 = e3.length; a3--; ) g(e3[a3]);
205
- c3 || (a3 = "value", "progress" == x2 && null == _2 ? u4.removeAttribute("value") : null != _2 && (_2 !== u4[a3] || "progress" == x2 && !_2 || "option" == x2 && _2 != b[a3]) && F(u4, a3, _2, b[a3], o3), a3 = "checked", null != m3 && m3 != u4[a3] && F(u4, a3, m3, b[a3], o3));
205
+ c3 || (a3 = "value", "progress" == x2 && null == _3 ? u4.removeAttribute("value") : null != _3 && (_3 !== u4[a3] || "progress" == x2 && !_3 || "option" == x2 && _3 != b[a3]) && F(u4, a3, _3, b[a3], o3), a3 = "checked", null != m3 && m3 != u4[a3] && F(u4, a3, m3, b[a3], o3));
206
206
  }
207
207
  return u4;
208
208
  }
@@ -314,6 +314,10 @@
314
314
  var i3 = p2(t2++, 3);
315
315
  !c2.__s && C2(i3.__H, u4) && (i3.__ = n2, i3.u = u4, r2.__H.__h.push(i3));
316
316
  }
317
+ function _2(n2, u4) {
318
+ var i3 = p2(t2++, 4);
319
+ !c2.__s && C2(i3.__H, u4) && (i3.__ = n2, i3.u = u4, r2.__h.push(i3));
320
+ }
317
321
  function A2(n2) {
318
322
  return o2 = 5, T2(function() {
319
323
  return { current: n2 };
@@ -427,7 +431,7 @@
427
431
  }
428
432
  hideTimer.current = setTimeout(() => setVisible(false), 150);
429
433
  };
430
- y2(() => {
434
+ _2(() => {
431
435
  if (visible && panelRef.current) {
432
436
  const el = panelRef.current;
433
437
  el.style.left = "50%";
@@ -453,8 +457,7 @@
453
457
  "div",
454
458
  {
455
459
  ref: panelRef,
456
- class: `profiler-toolbar-panel${panelLarge ? " profiler-toolbar-panel-large" : ""}`,
457
- style: { display: visible ? "block" : "none" },
460
+ class: `profiler-toolbar-panel${panelLarge ? " profiler-toolbar-panel-large" : ""}${visible ? " is-visible" : ""}`,
458
461
  onMouseEnter: show,
459
462
  onMouseLeave: hide,
460
463
  children: panel
@@ -1027,7 +1030,7 @@
1027
1030
  second: "2-digit"
1028
1031
  });
1029
1032
  }
1030
- function ToolbarApp({ profile, token, currentVersion }) {
1033
+ function ToolbarApp({ profile, token }) {
1031
1034
  const cd = profile.collectors_data || {};
1032
1035
  const requestData = cd["request"];
1033
1036
  const dbData = cd["database"];
@@ -1048,7 +1051,7 @@
1048
1051
  const dbClass = (dbData?.slow_queries ?? 0) > 0 ? "profiler-text--error" : "profiler-text--success";
1049
1052
  const ajaxClass = (ajaxData?.total_requests ?? 0) > 20 ? "profiler-text--error" : "profiler-text--success";
1050
1053
  const cacheClass = (cacheData?.hit_rate ?? 0) > 80 ? "profiler-text--success" : "profiler-text--warning";
1051
- return /* @__PURE__ */ u3("div", { class: "profiler-toolbar-container", children: [
1054
+ return /* @__PURE__ */ u3(k, { children: /* @__PURE__ */ u3("div", { class: "profiler-toolbar-container", children: [
1052
1055
  requestData && /* @__PURE__ */ u3(k, { children: [
1053
1056
  /* @__PURE__ */ u3(
1054
1057
  ToolbarItem,
@@ -1291,83 +1294,84 @@
1291
1294
  ]
1292
1295
  }
1293
1296
  ),
1294
- profile.gem_version && profile.gem_version !== currentVersion ? /* @__PURE__ */ u3(
1295
- "a",
1296
- {
1297
- href: `/_profiler/profiles/${token}`,
1298
- class: "profiler-toolbar-item profiler-text--warning",
1299
- title: `Captur\xE9 avec v${profile.gem_version} \u2014 actuel : v${currentVersion}`,
1300
- children: [
1301
- "\u26A0\uFE0F v",
1302
- profile.gem_version
1303
- ]
1304
- }
1305
- ) : currentVersion ? /* @__PURE__ */ u3("span", { class: "profiler-toolbar-item", style: "cursor:default", children: [
1306
- "v",
1307
- currentVersion
1308
- ] }) : null,
1309
- /* @__PURE__ */ u3("a", { href: "/_profiler", class: "profiler-toolbar-item", children: "\u2B21 Profiler" })
1310
- ] });
1297
+ /* @__PURE__ */ u3("a", { href: "/_profiler", class: "profiler-toolbar-item profiler-toolbar-logo", children: "\u2B21 Profiler" })
1298
+ ] }) });
1311
1299
  }
1312
1300
 
1313
1301
  // app/assets/typescript/profiler/toolbar-bundle.tsx
1314
- function applyTheme(el) {
1302
+ var ANIM_MS = 280;
1303
+ function applyTheme(elements) {
1315
1304
  const stored = localStorage.getItem("profiler-theme");
1316
1305
  const theme = stored === "light" ? "light" : stored === "dark" ? "dark" : window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
1317
- el.setAttribute("data-theme", theme);
1306
+ elements.forEach((el) => el.setAttribute("data-theme", theme));
1307
+ }
1308
+ function ToolbarMount({ token }) {
1309
+ const [profile, setProfile] = d2(null);
1310
+ y2(() => {
1311
+ const load = () => {
1312
+ fetch(`/_profiler/api/toolbar/${token}`).then((res) => res.json()).then((data) => {
1313
+ if (data.profile) setProfile(data.profile);
1314
+ }).catch((err) => console.debug("Profiler toolbar load failed:", err));
1315
+ };
1316
+ window.__PROFILER_REFRESH_TOOLBAR__ = load;
1317
+ load();
1318
+ }, [token]);
1319
+ if (!profile) return null;
1320
+ return /* @__PURE__ */ u3(ToolbarApp, { profile, token });
1318
1321
  }
1319
1322
  function mountToolbar() {
1320
1323
  const el = document.getElementById("profiler-toolbar");
1321
- if (!el) return;
1324
+ const toggleEl = document.getElementById("profiler-toolbar-toggle");
1325
+ if (!el || !toggleEl) return;
1322
1326
  const token = el.dataset.token;
1323
1327
  if (!token) return;
1324
- const currentVersion = el.dataset.version ?? "";
1325
- applyTheme(el);
1328
+ const themeEls = [el, toggleEl];
1329
+ applyTheme(themeEls);
1326
1330
  window.addEventListener("storage", (e3) => {
1327
- if (e3.key === "profiler-theme") applyTheme(el);
1331
+ if (e3.key === "profiler-theme") applyTheme(themeEls);
1328
1332
  });
1329
1333
  window.addEventListener("profiler:theme-change", ((e3) => {
1330
- if (e3.detail?.theme) el.setAttribute("data-theme", e3.detail.theme);
1334
+ if (e3.detail?.theme) themeEls.forEach((elem) => elem.setAttribute("data-theme", e3.detail.theme));
1331
1335
  }));
1332
- const renderToolbar = (profile) => {
1333
- J(/* @__PURE__ */ u3(ToolbarApp, { profile, token, currentVersion }), el);
1334
- applyTheme(el);
1335
- };
1336
- const loadAndRender = () => {
1337
- fetch(`/_profiler/api/toolbar/${token}`).then((res) => res.json()).then((data) => {
1338
- if (data.profile) renderToolbar(data.profile);
1339
- }).catch((err) => console.debug("Profiler toolbar load failed:", err));
1340
- };
1341
- window.__PROFILER_REFRESH_TOOLBAR__ = loadAndRender;
1342
- loadAndRender();
1336
+ let isCollapsed = localStorage.getItem("profiler-toolbar-collapsed") === "true";
1337
+ toggleEl.dataset.collapsed = String(isCollapsed);
1338
+ if (isCollapsed && !el.style.transform) {
1339
+ el.style.animation = "none";
1340
+ el.style.transform = "translateX(calc(100% + 44px))";
1341
+ }
1342
+ if (!isCollapsed) {
1343
+ setTimeout(() => {
1344
+ el.style.animation = "none";
1345
+ }, 400);
1346
+ }
1347
+ function toggleCollapse() {
1348
+ const next = !isCollapsed;
1349
+ isCollapsed = next;
1350
+ localStorage.setItem("profiler-toolbar-collapsed", String(next));
1351
+ toggleEl.dataset.collapsed = String(next);
1352
+ if (next) {
1353
+ el.style.transition = `transform ${ANIM_MS}ms cubic-bezier(0.4,0,0.2,1)`;
1354
+ el.style.transform = "translateX(calc(100% + 44px))";
1355
+ } else {
1356
+ el.style.transition = "none";
1357
+ el.style.transform = "translateX(calc(100% + 44px))";
1358
+ void el.offsetWidth;
1359
+ el.style.transition = `transform ${ANIM_MS}ms cubic-bezier(0.4,0,0.2,1)`;
1360
+ el.style.transform = "translateX(0)";
1361
+ setTimeout(() => {
1362
+ el.style.transform = "";
1363
+ el.style.transition = "";
1364
+ }, ANIM_MS + 50);
1365
+ }
1366
+ }
1367
+ toggleEl.addEventListener("click", toggleCollapse);
1343
1368
  document.addEventListener("keydown", (e3) => {
1344
1369
  if (e3.altKey && e3.key === "p") {
1345
1370
  e3.preventDefault();
1346
- const hidden2 = el.dataset.hidden === "true";
1347
- if (hidden2) {
1348
- el.dataset.hidden = "false";
1349
- el.style.transform = "translateY(0)";
1350
- el.style.opacity = "1";
1351
- localStorage.setItem("profiler-toolbar-hidden", "false");
1352
- } else {
1353
- el.dataset.hidden = "true";
1354
- el.style.transform = "translateY(100%)";
1355
- el.style.opacity = "0";
1356
- localStorage.setItem("profiler-toolbar-hidden", "true");
1357
- }
1358
- }
1359
- if (e3.key === "Escape") {
1360
- el.dataset.hidden = "true";
1361
- el.style.transform = "translateY(100%)";
1362
- el.style.opacity = "0";
1371
+ toggleCollapse();
1363
1372
  }
1364
1373
  });
1365
- const hidden = localStorage.getItem("profiler-toolbar-hidden") === "true";
1366
- if (hidden) {
1367
- el.dataset.hidden = "true";
1368
- el.style.transform = "translateY(100%)";
1369
- el.style.opacity = "0";
1370
- }
1374
+ J(/* @__PURE__ */ u3(ToolbarMount, { token }), el);
1371
1375
  }
1372
1376
  if (document.readyState === "loading") {
1373
1377
  document.addEventListener("DOMContentLoaded", mountToolbar);
@@ -1395,6 +1395,26 @@ tr:hover .btn-row-delete {
1395
1395
  color: var(--profiler-text-muted);
1396
1396
  }
1397
1397
 
1398
+ @keyframes pfPop {
1399
+ 0% {
1400
+ transform: scaleX(1);
1401
+ }
1402
+ 15% {
1403
+ transform: scaleX(1.18);
1404
+ }
1405
+ 40% {
1406
+ transform: scaleX(0.87);
1407
+ }
1408
+ 65% {
1409
+ transform: scaleX(1.08);
1410
+ }
1411
+ 85% {
1412
+ transform: scaleX(0.97);
1413
+ }
1414
+ 100% {
1415
+ transform: scaleX(1);
1416
+ }
1417
+ }
1398
1418
  #profiler-toolbar {
1399
1419
  position: fixed;
1400
1420
  bottom: 0;
@@ -1409,6 +1429,7 @@ tr:hover .btn-row-delete {
1409
1429
  font-family: var(--profiler-font-mono);
1410
1430
  font-size: 11px;
1411
1431
  animation: toolbarIn 300ms cubic-bezier(0.4, 0, 0.2, 1) both;
1432
+ transition: left 280ms cubic-bezier(0.4, 0, 0.2, 1);
1412
1433
  }
1413
1434
  #profiler-toolbar::before {
1414
1435
  content: "";
@@ -1464,12 +1485,10 @@ tr:hover .btn-row-delete {
1464
1485
  background: var(--profiler-accent);
1465
1486
  transform: scaleX(0);
1466
1487
  transform-origin: left;
1467
- transition: transform var(--profiler-transition-base);
1488
+ transition: transform 280ms cubic-bezier(0.34, 1.4, 0.64, 1);
1468
1489
  }
1469
1490
  .profiler-toolbar-item:last-child {
1470
1491
  border-right: none;
1471
- margin-left: auto;
1472
- padding-left: 20px;
1473
1492
  }
1474
1493
  .profiler-toolbar-item:hover {
1475
1494
  color: var(--profiler-text);
@@ -1479,12 +1498,30 @@ tr:hover .btn-row-delete {
1479
1498
  transform: scaleX(1);
1480
1499
  }
1481
1500
 
1501
+ .profiler-toolbar-collapse-btn {
1502
+ margin-left: auto;
1503
+ padding: 0 14px;
1504
+ border-left: 1px solid var(--profiler-border);
1505
+ font-size: 10px;
1506
+ color: var(--profiler-text-muted);
1507
+ cursor: pointer;
1508
+ }
1509
+ .profiler-toolbar-collapse-btn:hover {
1510
+ color: var(--profiler-text);
1511
+ background: var(--profiler-accent-bg);
1512
+ animation: pfPop 380ms ease-out both;
1513
+ }
1514
+
1482
1515
  a.profiler-toolbar-item {
1483
1516
  cursor: pointer;
1484
1517
  font-family: var(--profiler-font-mono);
1485
1518
  font-size: 11px;
1486
1519
  }
1487
1520
 
1521
+ .profiler-toolbar-logo {
1522
+ border-right: 1px solid var(--profiler-border) !important;
1523
+ }
1524
+
1488
1525
  .profiler-text--success {
1489
1526
  color: var(--profiler-success) !important;
1490
1527
  }
@@ -1505,19 +1542,22 @@ a.profiler-toolbar-item {
1505
1542
  color: var(--profiler-accent) !important;
1506
1543
  }
1507
1544
 
1508
- a.profiler-toolbar-item.profiler-text--error::after {
1545
+ .profiler-toolbar-item.profiler-text--error::after {
1509
1546
  background: var(--profiler-error);
1510
1547
  }
1511
1548
 
1512
- a.profiler-toolbar-item.profiler-text--warning::after {
1549
+ .profiler-toolbar-item.profiler-text--warning::after {
1513
1550
  background: var(--profiler-warning);
1514
1551
  }
1515
1552
 
1553
+ .profiler-toolbar-item.profiler-text--success::after {
1554
+ background: var(--profiler-success);
1555
+ }
1556
+
1516
1557
  .profiler-toolbar-hoverable {
1517
1558
  position: relative;
1518
1559
  }
1519
1560
  .profiler-toolbar-hoverable .profiler-toolbar-panel {
1520
- display: none;
1521
1561
  position: absolute;
1522
1562
  bottom: calc(100% + 12px);
1523
1563
  left: 50%;
@@ -1530,6 +1570,16 @@ a.profiler-toolbar-item.profiler-text--warning::after {
1530
1570
  box-shadow: var(--profiler-shadow-xl), 0 0 0 1px var(--profiler-border-accent);
1531
1571
  z-index: var(--profiler-z-panel);
1532
1572
  overflow: hidden;
1573
+ opacity: 0;
1574
+ visibility: hidden;
1575
+ pointer-events: none;
1576
+ transition: opacity 160ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 160ms;
1577
+ }
1578
+ .profiler-toolbar-hoverable .profiler-toolbar-panel.is-visible {
1579
+ opacity: 1;
1580
+ visibility: visible;
1581
+ pointer-events: auto;
1582
+ transition: opacity 160ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s;
1533
1583
  }
1534
1584
  .profiler-toolbar-hoverable .profiler-toolbar-panel::before {
1535
1585
  content: "";
@@ -1674,6 +1724,16 @@ a.profiler-toolbar-item.profiler-text--warning::after {
1674
1724
  margin-top: 6px;
1675
1725
  }
1676
1726
 
1727
+ #profiler-toolbar[data-collapsed=true] .profiler-toolbar-item:not(.profiler-toolbar-collapse-btn) {
1728
+ display: none;
1729
+ }
1730
+ #profiler-toolbar[data-collapsed=true] .profiler-toolbar-collapse-btn {
1731
+ margin-left: 0;
1732
+ }
1733
+ #profiler-toolbar[data-collapsed=true]::before {
1734
+ opacity: 0;
1735
+ }
1736
+
1677
1737
  .container > table {
1678
1738
  width: 100%;
1679
1739
  border-collapse: collapse;
@@ -62,9 +62,11 @@ module Profiler
62
62
  def toolbar_html
63
63
  <<~HTML
64
64
  #{ajax_interceptor_script}
65
- <div id="profiler-toolbar" data-token="#{@token}" data-version="#{Profiler::VERSION}"></div>
66
- <script src="/_profiler/assets/profiler-toolbar.js" defer#{nonce_attr}></script>
67
65
  <style>#{toolbar_styles}</style>
66
+ <div id="profiler-toolbar" class="profiler-root" data-token="#{@token}"></div>
67
+ <button id="profiler-toolbar-toggle" class="profiler-root" title="Toggle profiler (Alt+P)"><span class="profiler-toggle-icon">&#9654;</span></button>
68
+ <script#{nonce_attr}>(function(){var c=localStorage.getItem('profiler-toolbar-collapsed')==='true',t=localStorage.getItem('profiler-theme'),theme=t==='light'?'light':t==='dark'?'dark':(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'),el=document.getElementById('profiler-toolbar'),tog=document.getElementById('profiler-toolbar-toggle');if(c){el.style.cssText='animation:none!important;transform:translateX(calc(100% + 44px))';tog.dataset.collapsed='true';}el.setAttribute('data-theme',theme);tog.setAttribute('data-theme',theme);})();</script>
69
+ <script src="/_profiler/assets/profiler-toolbar.js" defer#{nonce_attr}></script>
68
70
  HTML
69
71
  end
70
72
 
@@ -76,39 +78,28 @@ module Profiler
76
78
  # Variables are defined on #profiler-toolbar to avoid polluting the host app.
77
79
  def toolbar_styles
78
80
  <<~'CSS'
79
- #profiler-toolbar {
81
+ .profiler-root {
80
82
  --pf-bg: #080b10;
81
83
  --pf-surface: #0d1117;
82
84
  --pf-raised: #131920;
83
85
  --pf-text: #eef2f7;
84
- --pf-muted: #5e7080;
86
+ --pf-muted: #4d6170;
85
87
  --pf-amber: #f59e0b;
86
88
  --pf-amber-h: #fbbf24;
87
- --pf-amber-bg: rgba(245,158,11,.1);
88
- --pf-amber-glow: rgba(245,158,11,.25);
89
+ --pf-amber-bg: rgba(245,158,11,.08);
90
+ --pf-amber-glow: rgba(245,158,11,.2);
89
91
  --pf-success: #22c55e;
90
92
  --pf-warning: #fb923c;
91
93
  --pf-error: #f87171;
92
94
  --pf-info: #60a5fa;
93
- --pf-border: rgba(255,255,255,.07);
94
- --pf-border-s: rgba(255,255,255,.13);
95
+ --pf-border: rgba(255,255,255,.06);
96
+ --pf-border-s: rgba(255,255,255,.11);
95
97
  --pf-mono: 'JetBrains Mono','SF Mono','Fira Code',monospace;
96
- --pf-tf: 120ms cubic-bezier(.4,0,.2,1);
97
- --pf-tb: 220ms cubic-bezier(.4,0,.2,1);
98
-
99
- position: fixed;
100
- bottom: 0; left: 0; right: 0;
101
- height: 44px;
102
- background: var(--pf-bg);
103
- border-top: 1px solid var(--pf-border-s);
104
- z-index: 999999;
105
- font-family: var(--pf-mono);
106
- font-size: 11px;
107
- color: var(--pf-muted);
108
- animation: pfIn 300ms cubic-bezier(.4,0,.2,1) both;
98
+ --pf-tf: 140ms cubic-bezier(.4,0,.2,1);
99
+ --pf-tb: 240ms cubic-bezier(.4,0,.2,1);
109
100
  }
110
- /* ── Light theme overrides ───────────────────────────────────────── */
111
- #profiler-toolbar[data-theme="light"] {
101
+ /* ── Light theme ─────────────────────────────────────────────────── */
102
+ .profiler-root[data-theme="light"] {
112
103
  --pf-bg: #f5f3ef;
113
104
  --pf-surface: #edeae3;
114
105
  --pf-raised: #e3ded5;
@@ -116,39 +107,63 @@ module Profiler
116
107
  --pf-muted: #8a7a6e;
117
108
  --pf-amber: #b45309;
118
109
  --pf-amber-h: #92400e;
119
- --pf-amber-bg: rgba(180,83,9,.08);
120
- --pf-amber-glow: rgba(180,83,9,.2);
110
+ --pf-amber-bg: rgba(180,83,9,.07);
111
+ --pf-amber-glow: rgba(180,83,9,.18);
121
112
  --pf-success: #15803d;
122
113
  --pf-warning: #c2410c;
123
114
  --pf-error: #dc2626;
124
115
  --pf-info: #1d4ed8;
125
- --pf-border: rgba(28,20,16,.1);
126
- --pf-border-s: rgba(28,20,16,.2);
116
+ --pf-border: rgba(28,20,16,.08);
117
+ --pf-border-s: rgba(28,20,16,.18);
127
118
  }
128
-
119
+ #profiler-toolbar {
120
+ position: fixed;
121
+ bottom: 0; left: 0; right: 44px;
122
+ height: 44px;
123
+ background: linear-gradient(180deg, var(--pf-surface) 0%, var(--pf-bg) 100%);
124
+ border-top: 1px solid var(--pf-border-s);
125
+ z-index: 999999;
126
+ font-family: var(--pf-mono);
127
+ font-size: 11px;
128
+ color: var(--pf-muted);
129
+ animation: pfIn 320ms cubic-bezier(.4,0,.2,1) both;
130
+ }
131
+ /* ── Slide-in animation ──────────────────────────────────────────── */
129
132
  @keyframes pfIn {
130
133
  from { transform: translateY(100%); opacity: 0; }
131
- to { transform: translateY(0); opacity: 1; }
134
+ to { transform: translateY(0); opacity: 1; }
135
+ }
136
+ @keyframes pfPop {
137
+ 0% { transform: scaleX(1); }
138
+ 15% { transform: scaleX(1.18); }
139
+ 40% { transform: scaleX(0.87); }
140
+ 65% { transform: scaleX(1.08); }
141
+ 85% { transform: scaleX(0.97); }
142
+ 100% { transform: scaleX(1); }
132
143
  }
144
+ /* ── Amber accent line ───────────────────────────────────────────── */
133
145
  #profiler-toolbar::before {
134
146
  content: '';
135
147
  position: absolute;
136
148
  top: -1px; left: 0; right: 0;
137
149
  height: 1px;
138
- background: linear-gradient(90deg,transparent 0%,#f59e0b 20%,#fbbf24 50%,#f59e0b 80%,transparent 100%);
139
- opacity: .6;
150
+ background: linear-gradient(90deg, transparent 0%, #f59e0b 25%, #fbbf24 50%, #f59e0b 75%, transparent 100%);
151
+ opacity: .55;
152
+ transition: opacity 280ms;
140
153
  }
154
+ /* ── Container ───────────────────────────────────────────────────── */
141
155
  #profiler-toolbar .profiler-toolbar-container {
142
156
  display: flex;
143
157
  align-items: stretch;
144
158
  height: 100%;
145
159
  }
160
+ /* ── Items ───────────────────────────────────────────────────────── */
146
161
  #profiler-toolbar .profiler-toolbar-item {
147
162
  position: relative;
148
163
  display: inline-flex;
149
164
  align-items: center;
150
165
  gap: 5px;
151
- padding: 0 14px;
166
+ padding: 0 13px;
152
167
  color: var(--pf-muted);
153
168
  text-decoration: none;
154
169
  white-space: nowrap;
@@ -159,7 +174,9 @@ module Profiler
159
174
  font-family: var(--pf-mono);
160
175
  font-size: 11px;
161
176
  transition: color var(--pf-tf), background var(--pf-tf);
177
+ cursor: default;
162
178
  }
179
+ /* Amber bottom indicator — springy easing */
163
180
  #profiler-toolbar .profiler-toolbar-item::after {
164
181
  content: '';
165
182
  position: absolute;
@@ -168,52 +185,114 @@ module Profiler
168
185
  background: var(--pf-amber);
169
186
  transform: scaleX(0);
170
187
  transform-origin: left;
171
- transition: transform var(--pf-tb);
188
+ transition: transform 280ms cubic-bezier(.34,1.4,.64,1);
172
189
  }
173
- #profiler-toolbar a.profiler-toolbar-item { cursor: pointer; }
174
- #profiler-toolbar a.profiler-toolbar-item:hover {
190
+ /* Hover: all interactive items */
191
+ #profiler-toolbar a.profiler-toolbar-item,
192
+ #profiler-toolbar .profiler-toolbar-hoverable { cursor: pointer; }
193
+
194
+ #profiler-toolbar a.profiler-toolbar-item:hover,
195
+ #profiler-toolbar .profiler-toolbar-hoverable:hover {
175
196
  color: var(--pf-text);
176
197
  background: var(--pf-amber-bg);
177
198
  }
178
- #profiler-toolbar a.profiler-toolbar-item:hover::after { transform: scaleX(1); }
199
+ #profiler-toolbar a.profiler-toolbar-item:hover::after,
200
+ #profiler-toolbar .profiler-toolbar-hoverable:hover::after { transform: scaleX(1); }
201
+
202
+ /* Severity underline colors */
203
+ #profiler-toolbar .profiler-text--error::after { background: var(--pf-error); }
204
+ #profiler-toolbar .profiler-text--warning::after { background: var(--pf-warning); }
205
+ #profiler-toolbar .profiler-text--success::after { background: var(--pf-success); }
206
+
207
+ /* Semantic colors */
179
208
  #profiler-toolbar .profiler-text--success { color: var(--pf-success) !important; }
180
209
  #profiler-toolbar .profiler-text--warning { color: var(--pf-warning) !important; }
181
210
  #profiler-toolbar .profiler-text--error { color: var(--pf-error) !important; }
182
211
  #profiler-toolbar .profiler-text--accent { color: var(--pf-amber) !important; }
183
212
  #profiler-toolbar .profiler-text--muted { color: var(--pf-muted) !important; }
184
- #profiler-toolbar a.profiler-toolbar-item.profiler-text--error::after { background: var(--pf-error); }
185
- #profiler-toolbar a.profiler-toolbar-item.profiler-text--warning::after { background: var(--pf-warning); }
186
- #profiler-toolbar .profiler-toolbar-item:last-child {
187
- border-right: none;
188
- margin-left: auto;
213
+
214
+ /* Logo link */
215
+ #profiler-toolbar .profiler-toolbar-logo {
216
+ border-right: 1px solid var(--pf-border) !important;
189
217
  color: var(--pf-amber);
190
- padding-left: 20px;
191
218
  font-weight: 600;
219
+ letter-spacing: .02em;
220
+ }
221
+ #profiler-toolbar .profiler-toolbar-logo:hover { color: var(--pf-amber-h); }
222
+
223
+ /* ── Toggle button (fixed, always visible) ───────────────────────── */
224
+ #profiler-toolbar-toggle {
225
+ position: fixed;
226
+ bottom: 0; right: 0;
227
+ width: 44px; height: 44px;
228
+ display: flex; align-items: center; justify-content: center;
229
+ cursor: pointer;
230
+ background: linear-gradient(180deg, var(--pf-surface) 0%, var(--pf-bg) 100%);
231
+ border: none;
232
+ border-top: 1px solid var(--pf-border-s);
233
+ border-left: 1px solid var(--pf-border);
234
+ color: var(--pf-muted);
235
+ font-family: var(--pf-mono);
236
+ font-size: 13px;
237
+ z-index: 1000000;
238
+ border-radius: 0;
239
+ transition: color var(--pf-tf), background var(--pf-tf);
240
+ }
241
+ #profiler-toolbar-toggle:hover {
242
+ color: var(--pf-amber);
243
+ animation: pfPop 380ms ease-out both;
244
+ transform: none;
192
245
  }
193
- #profiler-toolbar a.profiler-toolbar-item:last-child:hover { color: var(--pf-amber-h); }
246
+ .profiler-toggle-icon {
247
+ display: inline-block;
248
+ transition: transform 280ms cubic-bezier(.4,0,.2,1);
249
+ }
250
+ #profiler-toolbar-toggle[data-collapsed="true"] .profiler-toggle-icon {
251
+ transform: scaleX(-1);
252
+ }
253
+
254
+ /* ── Hoverable wrapper ───────────────────────────────────────────── */
194
255
  #profiler-toolbar .profiler-toolbar-hoverable { position: relative; }
256
+
257
+ /* ── Panel (CSS-driven visibility) ───────────────────────────────── */
195
258
  #profiler-toolbar .profiler-toolbar-panel {
196
- display: none;
197
259
  position: absolute;
198
- bottom: calc(100% + 10px);
260
+ bottom: calc(100% + 12px);
199
261
  left: 50%;
200
262
  transform: translateX(-50%);
201
263
  min-width: 300px;
202
264
  max-width: 420px;
203
265
  background: var(--pf-surface);
204
266
  border: 1px solid var(--pf-border-s);
205
- border-radius: 8px;
206
- box-shadow: 0 8px 32px rgba(0,0,0,.65), 0 0 0 1px var(--pf-amber-glow);
267
+ border-radius: 10px;
268
+ box-shadow: 0 12px 40px rgba(0,0,0,.7), 0 0 0 1px var(--pf-amber-glow);
207
269
  z-index: 1000000;
208
270
  overflow: hidden;
271
+ /* Hidden state */
272
+ opacity: 0;
273
+ visibility: hidden;
274
+ pointer-events: none;
275
+ transition:
276
+ opacity 160ms cubic-bezier(.4,0,.2,1),
277
+ visibility 0s linear 160ms;
209
278
  }
279
+ #profiler-toolbar .profiler-toolbar-panel.is-visible {
280
+ opacity: 1;
281
+ visibility: visible;
282
+ pointer-events: auto;
283
+ transition:
284
+ opacity 160ms cubic-bezier(.4,0,.2,1),
285
+ visibility 0s;
286
+ }
287
+ /* Amber top bar on panel */
210
288
  #profiler-toolbar .profiler-toolbar-panel::before {
211
289
  content: '';
212
290
  position: absolute;
213
291
  top: 0; left: 0; right: 0;
214
292
  height: 2px;
215
- background: linear-gradient(90deg,#f59e0b,#fbbf24);
293
+ background: linear-gradient(90deg, #f59e0b, #fbbf24);
216
294
  }
295
+ /* Arrow */
217
296
  #profiler-toolbar .profiler-toolbar-panel::after {
218
297
  content: '';
219
298
  position: absolute;
@@ -223,11 +302,13 @@ module Profiler
223
302
  border-top-color: var(--pf-border-s);
224
303
  }
225
304
  #profiler-toolbar .profiler-toolbar-panel-large { min-width: 420px; max-width: 560px; }
305
+
306
+ /* ── Panel header ────────────────────────────────────────────────── */
226
307
  #profiler-toolbar .profiler-toolbar-panel-header {
227
308
  display: flex;
228
309
  align-items: center;
229
310
  justify-content: space-between;
230
- padding: 10px 14px 8px;
311
+ padding: 10px 14px 9px;
231
312
  font-size: 10px;
232
313
  font-weight: 700;
233
314
  letter-spacing: .12em;
@@ -244,6 +325,8 @@ module Profiler
244
325
  letter-spacing: 0;
245
326
  font-size: 10px;
246
327
  }
328
+
329
+ /* ── Panel content ───────────────────────────────────────────────── */
247
330
  #profiler-toolbar .profiler-toolbar-panel-content {
248
331
  padding: 8px 14px 12px;
249
332
  max-height: 380px;
@@ -258,16 +341,22 @@ module Profiler
258
341
  background: var(--pf-border-s);
259
342
  border-radius: 99px;
260
343
  }
344
+
345
+ /* ── Panel rows ──────────────────────────────────────────────────── */
261
346
  #profiler-toolbar .profiler-toolbar-panel-row {
262
347
  display: flex;
263
348
  justify-content: space-between;
264
349
  align-items: baseline;
265
- padding: 5px 0;
350
+ padding: 6px 0;
266
351
  border-bottom: 1px solid var(--pf-border);
267
352
  gap: 12px;
268
353
  }
269
354
  #profiler-toolbar .profiler-toolbar-panel-row:last-child { border-bottom: none; }
270
- #profiler-toolbar .profiler-toolbar-panel-row span { color: var(--pf-muted); font-size: 10px; flex-shrink: 0; }
355
+ #profiler-toolbar .profiler-toolbar-panel-row span {
356
+ color: var(--pf-muted);
357
+ font-size: 10px;
358
+ flex-shrink: 0;
359
+ }
271
360
  #profiler-toolbar .profiler-toolbar-panel-row strong {
272
361
  color: var(--pf-text);
273
362
  font-weight: 600;
@@ -275,6 +364,8 @@ module Profiler
275
364
  font-variant-numeric: tabular-nums;
276
365
  word-break: break-all;
277
366
  }
367
+
368
+ /* ── Section header ──────────────────────────────────────────────── */
278
369
  #profiler-toolbar .profiler-section__header {
279
370
  font-size: 9px;
280
371
  font-weight: 700;
@@ -282,22 +373,26 @@ module Profiler
282
373
  text-transform: uppercase;
283
374
  color: var(--pf-amber);
284
375
  padding: 10px 0 4px;
285
- border-bottom: 1px solid rgba(245,158,11,.2);
376
+ border-bottom: 1px solid rgba(245,158,11,.18);
286
377
  margin-bottom: 6px;
287
378
  font-family: var(--pf-mono);
288
379
  }
289
380
  #profiler-toolbar .profiler-section__header:first-child { padding-top: 4px; }
381
+
382
+ /* ── Query cards ─────────────────────────────────────────────────── */
290
383
  #profiler-toolbar .profiler-toolbar-panel-query {
291
384
  padding: 7px 10px;
292
385
  background: var(--pf-raised);
293
386
  border: 1px solid var(--pf-border);
294
- border-radius: 4px;
387
+ border-radius: 5px;
295
388
  margin-bottom: 5px;
389
+ transition: border-color var(--pf-tf);
296
390
  }
297
391
  #profiler-toolbar .profiler-toolbar-panel-query:last-child { margin-bottom: 0; }
392
+ #profiler-toolbar .profiler-toolbar-panel-query:hover { border-color: var(--pf-border-s); }
298
393
  #profiler-toolbar .profiler-toolbar-panel-query-slow {
299
394
  border-left: 2px solid var(--pf-error);
300
- background: rgba(248,113,113,.06);
395
+ background: rgba(248,113,113,.05);
301
396
  }
302
397
  #profiler-toolbar .profiler-toolbar-panel-query code {
303
398
  display: block;
@@ -313,6 +408,8 @@ module Profiler
313
408
  padding: 0;
314
409
  border: none;
315
410
  }
411
+
412
+ /* ── More hint ───────────────────────────────────────────────────── */
316
413
  #profiler-toolbar .profiler-more {
317
414
  text-align: center;
318
415
  padding: 7px;
@@ -322,13 +419,17 @@ module Profiler
322
419
  border-top: 1px dashed var(--pf-border);
323
420
  margin-top: 6px;
324
421
  }
422
+
423
+ /* ── Ajax cards ──────────────────────────────────────────────────── */
325
424
  #profiler-toolbar .profiler-ajax-card {
326
425
  background: var(--pf-raised);
327
426
  border: 1px solid var(--pf-border);
328
- border-radius: 4px;
427
+ border-radius: 5px;
329
428
  padding: 6px 8px;
330
429
  margin-bottom: 4px;
430
+ transition: border-color var(--pf-tf);
331
431
  }
432
+ #profiler-toolbar .profiler-ajax-card:hover { border-color: var(--pf-border-s); }
332
433
  #profiler-toolbar .profiler-ajax-card--success { border-left: 2px solid var(--pf-success); }
333
434
  #profiler-toolbar .profiler-ajax-card--error { border-left: 2px solid var(--pf-error); }
334
435
  #profiler-toolbar .profiler-ajax-card__row {
@@ -337,10 +438,12 @@ module Profiler
337
438
  align-items: center;
338
439
  gap: 8px;
339
440
  }
441
+
442
+ /* ── Dump cards ──────────────────────────────────────────────────── */
340
443
  #profiler-toolbar .profiler-dump-card {
341
444
  background: var(--pf-raised);
342
445
  border: 1px solid var(--pf-border);
343
- border-radius: 4px;
446
+ border-radius: 5px;
344
447
  padding: 6px 8px;
345
448
  margin-bottom: 4px;
346
449
  }
@@ -349,11 +452,13 @@ module Profiler
349
452
  justify-content: space-between;
350
453
  margin-bottom: 4px;
351
454
  }
455
+
456
+ /* ── Badges ──────────────────────────────────────────────────────── */
352
457
  #profiler-toolbar .badge {
353
458
  display: inline-flex;
354
459
  align-items: center;
355
- padding: 1px 5px;
356
- border-radius: 3px;
460
+ padding: 1px 6px;
461
+ border-radius: 4px;
357
462
  font-size: 9px;
358
463
  font-weight: 700;
359
464
  letter-spacing: .05em;
@@ -361,10 +466,12 @@ module Profiler
361
466
  background: var(--pf-raised);
362
467
  color: var(--pf-muted);
363
468
  }
364
- #profiler-toolbar .badge-info { background: rgba(96,165,250,.12); color: var(--pf-info); }
365
- #profiler-toolbar .badge-success { background: rgba(34,197,94,.12); color: var(--pf-success); }
366
- #profiler-toolbar .badge-warning { background: rgba(251,146,60,.12); color: var(--pf-warning); }
367
- #profiler-toolbar .badge-error { background: rgba(248,113,113,.12); color: var(--pf-error); }
469
+ #profiler-toolbar .badge-info { background: rgba(96,165,250,.1); color: var(--pf-info); }
470
+ #profiler-toolbar .badge-success { background: rgba(34,197,94,.1); color: var(--pf-success); }
471
+ #profiler-toolbar .badge-warning { background: rgba(251,146,60,.1); color: var(--pf-warning); }
472
+ #profiler-toolbar .badge-error { background: rgba(248,113,113,.1); color: var(--pf-error); }
473
+
474
+ /* ── Utilities ───────────────────────────────────────────────────── */
368
475
  #profiler-toolbar .profiler-flex { display: flex; }
369
476
  #profiler-toolbar .profiler-flex--between { justify-content: space-between; }
370
477
  #profiler-toolbar .profiler-flex--gap-2 { gap: 8px; }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Profiler
4
- VERSION = "0.20.0"
4
+ VERSION = "0.21.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sébastien Duplessy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-01 00:00:00.000000000 Z
11
+ date: 2026-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails