@kylincloud/flamegraph 0.35.25 → 0.35.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylincloud/flamegraph",
3
- "version": "0.35.25",
3
+ "version": "0.35.27",
4
4
  "description": "KylinCloud flamegraph renderer (Pyroscope-based)",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.node.cjs.js",
@@ -1,5 +1,5 @@
1
1
  // src/ProfilerTable.tsx
2
- import React, { useRef, RefObject, useCallback, useState } from 'react';
2
+ import React, { useRef, RefObject, useCallback, useState, useEffect } from 'react';
3
3
  import type Color from 'color';
4
4
  import cl from 'classnames';
5
5
  import type { Maybe } from 'true-myth';
@@ -329,7 +329,7 @@ const getTableBody = ({
329
329
  const nameCell = (x: { name: string }) => (
330
330
  <button className="table-item-button">
331
331
  <div className="symbol-name" style={fitIntoTableCell(fitMode)}>
332
- {x.name}
332
+ <bdi dir="ltr">{x.name}</bdi>
333
333
  </div>
334
334
  </button>
335
335
  );
@@ -359,10 +359,13 @@ const getTableBody = ({
359
359
  const getDoubleRow = (
360
360
  x: DoubleCell & { name: string }
361
361
  ): BodyRow => {
362
- const leftPercent = ratioToPercent(x.totalLeft / x.leftTicks);
363
- const rghtPercent = ratioToPercent(x.totalRght / x.rightTicks);
362
+ const leftPercent = ratioToPercent(x.leftTicks > 0 ? x.totalLeft / x.leftTicks : 0);
363
+ const rghtPercent = ratioToPercent(x.rightTicks > 0 ? x.totalRght / x.rightTicks : 0);
364
364
 
365
- const totalDiff = diffPercent(leftPercent, rghtPercent);
365
+ let totalDiff = diffPercent(leftPercent, rghtPercent);
366
+ if (!Number.isFinite(totalDiff)) {
367
+ totalDiff = 0;
368
+ }
366
369
 
367
370
  let diffCellColor = '';
368
371
  if (totalDiff > 0) {
@@ -372,12 +375,14 @@ const getTableBody = ({
372
375
  }
373
376
 
374
377
  let diffValue = '';
375
- if (!x.totalLeft || totalDiff === Infinity) {
378
+ if (x.totalLeft === 0 && x.totalRght > 0) {
376
379
  // this is a new function
377
380
  diffValue = messages.diffNew;
378
- } else if (!x.totalRght) {
381
+ } else if (x.totalRght === 0 && x.totalLeft > 0) {
379
382
  // this function has been removed
380
383
  diffValue = messages.diffRemoved;
384
+ } else if (totalDiff === 0) {
385
+ diffValue = '0%';
381
386
  } else if (totalDiff > 0) {
382
387
  diffValue = `+${totalDiff.toFixed(2)}%`;
383
388
  } else if (totalDiff < 0) {
@@ -664,11 +669,28 @@ const ProfilerTable = React.memo(function ProfilerTable({
664
669
  enableSandwichView,
665
670
  }: Omit<ProfilerTableProps, 'tableBodyRef'>) {
666
671
  const tableBodyRef = useRef<HTMLTableSectionElement>(null);
672
+ const wrapperRef = useRef<HTMLDivElement>(null);
673
+ const [scrollbarSize, setScrollbarSize] = useState(0);
667
674
  const isDoubles = flamebearer.format === 'double';
668
675
 
676
+ useEffect(() => {
677
+ const bodyEl = tableBodyRef.current;
678
+ if (!bodyEl) return;
679
+ const next = bodyEl.offsetWidth - bodyEl.clientWidth;
680
+ if (Number.isFinite(next) && next !== scrollbarSize) {
681
+ setScrollbarSize(next);
682
+ }
683
+ }, [tableBodyRef.current, isDoubles, scrollbarSize]);
684
+
669
685
  return (
670
686
  <div
671
687
  data-testid="table-view"
688
+ ref={wrapperRef}
689
+ style={
690
+ {
691
+ '--kylin-flamegraph-scrollbar-size': `${scrollbarSize}px`,
692
+ } as React.CSSProperties
693
+ }
672
694
  className={
673
695
  isDoubles
674
696
  ? 'flamegraph-table-wrapper flamegraph-table-wrapper--double'
@@ -214,8 +214,8 @@ export function formatDouble(
214
214
  };
215
215
  };
216
216
  } {
217
- const leftRatio = totalLeft / leftTicks;
218
- const rightRatio = totalRight / rightTicks;
217
+ const leftRatio = leftTicks > 0 ? totalLeft / leftTicks : 0;
218
+ const rightRatio = rightTicks > 0 ? totalRight / rightTicks : 0;
219
219
 
220
220
  const leftPercent = ratioToPercent(leftRatio);
221
221
  const rightPercent = ratioToPercent(rightRatio);
@@ -236,7 +236,10 @@ export function formatDouble(
236
236
  tooltipType: 'flamegraph',
237
237
  };
238
238
 
239
- const totalDiff = diffPercent(leftPercent, rightPercent);
239
+ let totalDiff = diffPercent(leftPercent, rightPercent);
240
+ if (!Number.isFinite(totalDiff)) {
241
+ totalDiff = 0;
242
+ }
240
243
 
241
244
  let tooltipDiffColor = '';
242
245
  if (totalDiff > 0) {
@@ -249,12 +252,14 @@ export function formatDouble(
249
252
  // - new / removed 走 i18n,带安全兜底
250
253
  // - 百分比 diff 不再带括号,直接 "+12.34%" / "-5.67%"
251
254
  let tooltipDiffText = '';
252
- if (!totalLeft) {
255
+ if (totalLeft === 0 && totalRight > 0) {
253
256
  // 新函数
254
257
  tooltipDiffText = i18n?.diffNew ?? '新增';
255
- } else if (!totalRight) {
258
+ } else if (totalRight === 0 && totalLeft > 0) {
256
259
  // 被移除的函数
257
260
  tooltipDiffText = i18n?.diffRemoved ?? '移除';
261
+ } else if (totalDiff === 0) {
262
+ tooltipDiffText = '0%';
258
263
  } else if (totalDiff > 0) {
259
264
  tooltipDiffText = `+${totalDiff.toFixed(2)}%`;
260
265
  } else if (totalDiff < 0) {
@@ -28,14 +28,35 @@ pyro-flamegraph {
28
28
  }
29
29
 
30
30
  /* 只在差分火焰图(double)模式下启用滚动限制 */
31
+ /* 把滚动交给 tbody,表头固定在表格上方 */
31
32
  .flamegraph-table-wrapper--double .flamegraph-table-scroll {
32
- /* 最大高度:1000px,可按需调小/调大 */
33
+ overflow: hidden;
34
+ position: relative;
35
+ }
36
+
37
+ .flamegraph-table-wrapper--double .flamegraph-table-scroll table {
38
+ width: 100%;
39
+ table-layout: fixed;
40
+ }
41
+
42
+ .flamegraph-table-wrapper--double .flamegraph-table-scroll thead {
43
+ display: table;
44
+ width: calc(100% - var(--kylin-flamegraph-scrollbar-size, 0px));
45
+ table-layout: fixed;
46
+ }
47
+
48
+ .flamegraph-table-wrapper--double .flamegraph-table-scroll tbody {
49
+ display: block;
33
50
  max-height: var(--kylin-flamegraph-table-max-height, 1000px);
34
- overflow-y: auto;
51
+ overflow-y: scroll;
35
52
  overflow-x: hidden;
53
+ scrollbar-gutter: stable;
54
+ }
36
55
 
37
- /* sticky 参照这个容器 */
38
- position: relative;
56
+ .flamegraph-table-wrapper--double .flamegraph-table-scroll tbody tr {
57
+ display: table;
58
+ width: 100%;
59
+ table-layout: fixed;
39
60
  }
40
61
 
41
62
  /* 表头固定 + 不透明背景,避免内容透出来 */
@@ -47,12 +68,12 @@ pyro-flamegraph {
47
68
 
48
69
  /* 整行都有底色,避免列间缝隙透出内容 */
49
70
  .flamegraph-table-wrapper--double .flamegraph-table-scroll thead tr {
50
- background-color: var(--ps-neutral-9);
71
+ background-color: var(--ps-table-header-bg, var(--ps-ui-foreground, #ffffff));
51
72
  }
52
73
 
53
74
  /* 每个单元格再补一层底色,保证完全不透明 */
54
75
  .flamegraph-table-wrapper--double .flamegraph-table-scroll thead th {
55
- background-color: var(--ps-neutral-9);
76
+ background-color: var(--ps-table-header-bg, var(--ps-ui-foreground, #ffffff));
56
77
  background-clip: padding-box; // 防止边框区域产生透明感
57
78
  }
58
79
 
@@ -79,11 +79,40 @@
79
79
  th {
80
80
  border: 1px solid var(--ps-ui-border);
81
81
  padding: 4px 10px;
82
- width: 10%;
83
82
  }
84
83
  }
85
84
  }
86
85
 
86
+ .table:global(.flamegraph-table-doubles) {
87
+ th:first-child,
88
+ td:first-child {
89
+ width: 55%;
90
+ }
91
+
92
+ th:nth-child(2),
93
+ td:nth-child(2),
94
+ th:nth-child(3),
95
+ td:nth-child(3),
96
+ th:nth-child(4),
97
+ td:nth-child(4) {
98
+ width: 15%;
99
+ }
100
+ }
101
+
102
+ .table:global(.flamegraph-table) {
103
+ th:first-child,
104
+ td:first-child {
105
+ width: 55%;
106
+ }
107
+
108
+ th:nth-child(2),
109
+ td:nth-child(2),
110
+ th:nth-child(3),
111
+ td:nth-child(3) {
112
+ width: 22.5%;
113
+ }
114
+ }
115
+
87
116
  .loadingSpinner {
88
117
  text-align: center;
89
118
  margin-top: 50px;