@kylincloud/flamegraph 0.36.0 → 0.36.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/shims/Table.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/shims/Table.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAGZ,SAAS,EACT,aAAa,EACb,SAAS,EAGV,MAAM,OAAO,CAAC;AAUf,UAAU,UAAU;IAClB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;CACvH;AAED,MAAM,WAAW,IAAK,SAAQ,UAAU;IACtC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,UAAU,QAAS,SAAQ,UAAU;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GACrB;IACA,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACC;IACA,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,GACC;IACA,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACtD,CAAC;AAEJ,KAAK,KAAK,GAAG,aAAa,GAAG;IAC3B,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB,CAAC;AAEF,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,eAAe,EAAE,MAAM,GAAG,KAAK,CAAC;CACjC;AAED,eAAO,MAAM,YAAY,YAAa,QAAQ,EAAE,KAAG,cAsBlD,CAAC;AAEF,UAAU,UAAU;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,KAAK,EAAE,KAAK,CAAC;IACb,YAAY,CAAC,EAAE,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IAEnC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA0CD,iBAAS,KAAK,CAAC,EACb,eAAe,EACf,MAAM,EACN,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,SAAS,EACT,SAAS,EACT,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,kBAAsB,EACtB,SAAS,EACT,WAAW,GACZ,EAAE,UAAU,2CAqPZ;AA2DD,eAAe,KAAK,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylincloud/flamegraph",
3
- "version": "0.36.0",
3
+ "version": "0.36.2",
4
4
  "description": "KylinCloud flamegraph renderer (Pyroscope-based)",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.node.cjs.js",
@@ -1,10 +1,46 @@
1
1
  // a dummy class is required
2
2
  // so that the component can import this class
3
3
  // and therefore the library style (index.css above) is imported
4
- .dummy {
4
+ .menu {
5
5
  display: block;
6
6
 
7
7
  svg {
8
8
  margin-right: 10px;
9
9
  }
10
+
11
+ :global(.szh-menu) {
12
+ background-color: var(--ps-dropdown-background, #fff);
13
+ border: 1px solid var(--ps-ui-border, #d9d9d9);
14
+ border-radius: 6px;
15
+ box-shadow:
16
+ 0 6px 16px 0 rgba(0, 0, 0, 0.08),
17
+ 0 3px 6px -4px rgba(0, 0, 0, 0.12),
18
+ 0 9px 28px 8px rgba(0, 0, 0, 0.05);
19
+ padding: 4px 0;
20
+ min-width: 180px;
21
+ font-size: 13px;
22
+ color: var(--ps-ui-foreground-text, #000);
23
+ }
24
+
25
+ :global(.szh-menu__item) {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 8px;
29
+ padding: 6px 12px;
30
+ height: 32px;
31
+ cursor: pointer;
32
+ color: inherit;
33
+ transition: background-color 0.15s;
34
+
35
+ &:hover {
36
+ background-color: var(--ps-ui-element-bg-highlight, #f5f5f5);
37
+ }
38
+
39
+ svg {
40
+ width: 14px;
41
+ height: 14px;
42
+ flex-shrink: 0;
43
+ margin-right: 0;
44
+ }
45
+ }
10
46
  }
@@ -76,7 +76,7 @@ export default function ContextMenu(props: ContextMenuProps) {
76
76
  return (
77
77
  <ControlledMenu
78
78
  {...menuProps}
79
- className={styles.dummy}
79
+ className={styles.menu}
80
80
  anchorPoint={anchorPoint}
81
81
  onClose={onClose}
82
82
  >
@@ -5,7 +5,7 @@
5
5
  .fitModeItem svg,
6
6
  .sandwichItem svg {
7
7
  width: 1em;
8
- margin-right: 10px;
8
+ margin-right: 0;
9
9
  }
10
10
  }
11
11
 
@@ -3,27 +3,32 @@
3
3
  .tableContextMenu {
4
4
  // 继承 react-menu 的基础样式
5
5
  :global(.szh-menu) {
6
- background-color: var(--ps-ui-background, #fff);
7
- border: 1px solid var(--ps-ui-border, #ccc);
8
- border-radius: 4px;
9
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
6
+ background-color: var(--ps-dropdown-background, #fff);
7
+ border: 1px solid var(--ps-ui-border, #d9d9d9);
8
+ border-radius: 6px;
9
+ box-shadow:
10
+ 0 6px 16px 0 rgba(0, 0, 0, 0.08),
11
+ 0 3px 6px -4px rgba(0, 0, 0, 0.12),
12
+ 0 9px 28px 8px rgba(0, 0, 0, 0.05);
10
13
  padding: 4px 0;
11
14
  min-width: 180px;
12
- z-index: 1000;
15
+ font-size: 13px;
16
+ z-index: 3000;
13
17
  }
14
18
 
15
19
  :global(.szh-menu__item) {
16
20
  display: flex;
17
21
  align-items: center;
18
22
  gap: 8px;
19
- padding: 8px 12px;
23
+ padding: 6px 12px;
24
+ height: 32px;
20
25
  cursor: pointer;
21
- color: var(--ps-ui-foreground-text, #333);
22
- font-size: 13px;
26
+ color: var(--ps-ui-foreground-text, #000);
27
+ font-size: inherit;
23
28
  transition: background-color 0.15s;
24
29
 
25
30
  &:hover {
26
- background-color: var(--ps-ui-element-bg-highlight, #f0f0f0);
31
+ background-color: var(--ps-ui-element-bg-highlight, #f5f5f5);
27
32
  }
28
33
 
29
34
  svg {
@@ -501,12 +501,14 @@ function Table({
501
501
  virtualizeRowHeight,
502
502
  virtualizeOverscan,
503
503
  scrollRef,
504
+ menuPortalTarget,
504
505
  }: ProfilerTableProps & {
505
506
  isDoubles: boolean;
506
507
  virtualize?: boolean;
507
508
  virtualizeRowHeight?: number;
508
509
  virtualizeOverscan?: number;
509
510
  scrollRef?: RefObject<HTMLElement>;
511
+ menuPortalTarget?: HTMLElement | null;
510
512
  }) {
511
513
  const i18n = useFlamegraphI18n();
512
514
  const [menuProps, toggleMenu] = useMenuState({ transition: true });
@@ -744,8 +746,8 @@ function Table({
744
746
  },
745
747
  } satisfies ProfilerTableWorkerRequest);
746
748
  } else {
747
- setWorkerReady(false);
748
- setVisibleRowData([]);
749
+ // Keep current table visible while worker recomputes filtered/sorted rows.
750
+ // This avoids flicker on every keystroke in the search box.
749
751
  worker.postMessage({
750
752
  type: 'update',
751
753
  payload,
@@ -1013,6 +1015,7 @@ function Table({
1013
1015
  anchorPoint={anchorPoint}
1014
1016
  onClose={handleMenuClose}
1015
1017
  className={styles.tableContextMenu}
1018
+ portal={menuPortalTarget ? { target: menuPortalTarget } : true}
1016
1019
  >
1017
1020
  {onFocusOnNode && (
1018
1021
  <MenuItem onClick={handleFocusClick}>
@@ -1054,6 +1057,9 @@ const ProfilerTable = React.memo(function ProfilerTable({
1054
1057
  const wrapperRef = useRef<HTMLDivElement>(null);
1055
1058
  const scrollRef = useRef<HTMLDivElement>(null);
1056
1059
  const [scrollbarSize, setScrollbarSize] = useState(0);
1060
+ const [menuPortalTarget, setMenuPortalTarget] = useState<HTMLElement | null>(
1061
+ null
1062
+ );
1057
1063
  const isDoubles = flamebearer.format === 'double';
1058
1064
  const shouldVirtualize = true;
1059
1065
 
@@ -1066,6 +1072,26 @@ const ProfilerTable = React.memo(function ProfilerTable({
1066
1072
  }
1067
1073
  }, [tableBodyRef.current, isDoubles, scrollbarSize]);
1068
1074
 
1075
+ useEffect(() => {
1076
+ const host = wrapperRef.current;
1077
+ if (!host || typeof document === 'undefined') return;
1078
+
1079
+ const themeHost = host.closest(
1080
+ "[data-theme], [data-flamegraph-color-mode]"
1081
+ ) as HTMLElement | null;
1082
+ const targetParent = themeHost || host;
1083
+ const portalTarget = document.createElement('div');
1084
+ portalTarget.className = 'flamegraph-menu-portal';
1085
+ targetParent.appendChild(portalTarget);
1086
+ setMenuPortalTarget(portalTarget);
1087
+
1088
+ return () => {
1089
+ if (portalTarget.parentElement) {
1090
+ portalTarget.parentElement.removeChild(portalTarget);
1091
+ }
1092
+ };
1093
+ }, []);
1094
+
1069
1095
  return (
1070
1096
  <div
1071
1097
  data-testid="table-view"
@@ -1096,9 +1122,9 @@ const ProfilerTable = React.memo(function ProfilerTable({
1096
1122
  updateView={updateView}
1097
1123
  enableSandwichView={enableSandwichView}
1098
1124
  virtualize={shouldVirtualize}
1099
- virtualizeRowHeight={26}
1100
1125
  virtualizeOverscan={8}
1101
1126
  scrollRef={scrollRef}
1127
+ menuPortalTarget={menuPortalTarget}
1102
1128
  />
1103
1129
  </div>
1104
1130
 
@@ -46,6 +46,8 @@ pyro-flamegraph {
46
46
  overflow-y: auto;
47
47
  position: relative;
48
48
  max-height: var(--kylin-flamegraph-table-max-height, 1000px);
49
+ /* Avoid browser scroll anchoring jumps during async virtual row updates */
50
+ overflow-anchor: none;
49
51
  }
50
52
 
51
53
  .flamegraph-table-wrapper--double .flamegraph-table-scroll table {
@@ -62,6 +64,11 @@ pyro-flamegraph {
62
64
 
63
65
  .flamegraph-table-wrapper--double .flamegraph-table-scroll tbody {
64
66
  display: table-row-group;
67
+ overflow-anchor: none;
68
+ }
69
+
70
+ .flamegraph-table-wrapper--double .flamegraph-table-scroll tr {
71
+ overflow-anchor: none;
65
72
  }
66
73
 
67
74
  /* 整行都有底色,避免列间缝隙透出内容 */
@@ -177,6 +177,7 @@ function Table({
177
177
  const [measuredRowHeight, setMeasuredRowHeight] = useState<number | null>(null);
178
178
  const [scrollEl, setScrollEl] = useState<HTMLElement | null>(null);
179
179
  const rafRef = useRef<number | null>(null);
180
+ const lastVirtualRowsRef = useRef<BodyRow[] | null>(null);
180
181
 
181
182
  useLayoutEffect(() => {
182
183
  if (scrollRef?.current && scrollRef.current !== scrollEl) {
@@ -231,7 +232,8 @@ function Table({
231
232
  ? bodyRows.length
232
233
  : 0;
233
234
 
234
- const rowHeight = virtualizeRowHeight || measuredRowHeight || 0;
235
+ // Fallback row height prevents empty render when no measurement yet.
236
+ const rowHeight = virtualizeRowHeight || measuredRowHeight || 26;
235
237
  const canVirtualize =
236
238
  !!virtualize &&
237
239
  !!scrollEl &&
@@ -296,13 +298,22 @@ function Table({
296
298
  }))
297
299
  : [];
298
300
 
299
- const renderVirtualRows =
301
+ const shouldUseFreshRows =
300
302
  table.type === 'virtual' &&
301
303
  virtualState &&
302
304
  table.range &&
303
305
  table.range.start === virtualState.startIndex &&
304
- table.range.end === virtualState.endIndex
305
- ? table.rows
306
+ table.range.end === virtualState.endIndex;
307
+
308
+ if (table.type === 'virtual' && shouldUseFreshRows) {
309
+ lastVirtualRowsRef.current = table.rows;
310
+ }
311
+
312
+ const renderVirtualRows =
313
+ table.type === 'virtual'
314
+ ? (shouldUseFreshRows
315
+ ? table.rows
316
+ : lastVirtualRowsRef.current || placeholderRows)
306
317
  : placeholderRows;
307
318
 
308
319
  return isLoading ? (