@kylincloud/flamegraph 0.35.6

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.
Files changed (267) hide show
  1. package/CHANGELOG.md +1211 -0
  2. package/LICENSE +202 -0
  3. package/README.md +251 -0
  4. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts +2 -0
  5. package/dist/FlameGraph/FlameGraphComponent/CheckIcon.d.ts.map +1 -0
  6. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts +17 -0
  7. package/dist/FlameGraph/FlameGraphComponent/ContextMenu.d.ts.map +1 -0
  8. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts +14 -0
  9. package/dist/FlameGraph/FlameGraphComponent/ContextMenuHighlight.d.ts.map +1 -0
  10. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts +9 -0
  11. package/dist/FlameGraph/FlameGraphComponent/DiffLegend.d.ts.map +1 -0
  12. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts +8 -0
  13. package/dist/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.d.ts.map +1 -0
  14. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts +96 -0
  15. package/dist/FlameGraph/FlameGraphComponent/Flamegraph.d.ts.map +1 -0
  16. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts +27 -0
  17. package/dist/FlameGraph/FlameGraphComponent/Flamegraph_render.d.ts.map +1 -0
  18. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts +7 -0
  19. package/dist/FlameGraph/FlameGraphComponent/GraphVizPane.d.ts.map +1 -0
  20. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts +12 -0
  21. package/dist/FlameGraph/FlameGraphComponent/Header.d.ts.map +1 -0
  22. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts +18 -0
  23. package/dist/FlameGraph/FlameGraphComponent/Highlight.d.ts.map +1 -0
  24. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts +2 -0
  25. package/dist/FlameGraph/FlameGraphComponent/LogoLink.d.ts.map +1 -0
  26. package/dist/FlameGraph/FlameGraphComponent/color.d.ts +20 -0
  27. package/dist/FlameGraph/FlameGraphComponent/color.d.ts.map +1 -0
  28. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts +11 -0
  29. package/dist/FlameGraph/FlameGraphComponent/colorPalette.d.ts.map +1 -0
  30. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts +6 -0
  31. package/dist/FlameGraph/FlameGraphComponent/constants.d.ts.map +1 -0
  32. package/dist/FlameGraph/FlameGraphComponent/index.d.ts +37 -0
  33. package/dist/FlameGraph/FlameGraphComponent/index.d.ts.map +1 -0
  34. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts +2 -0
  35. package/dist/FlameGraph/FlameGraphComponent/murmur3.d.ts.map +1 -0
  36. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts +53 -0
  37. package/dist/FlameGraph/FlameGraphComponent/testData.d.ts.map +1 -0
  38. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts +6 -0
  39. package/dist/FlameGraph/FlameGraphComponent/utils.d.ts.map +1 -0
  40. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts +2 -0
  41. package/dist/FlameGraph/FlameGraphComponent/viewTypes.d.ts.map +1 -0
  42. package/dist/FlameGraph/FlameGraphRenderer.d.ts +86 -0
  43. package/dist/FlameGraph/FlameGraphRenderer.d.ts.map +1 -0
  44. package/dist/FlameGraph/decode.d.ts +27 -0
  45. package/dist/FlameGraph/decode.d.ts.map +1 -0
  46. package/dist/FlameGraph/normalize.d.ts +6 -0
  47. package/dist/FlameGraph/normalize.d.ts.map +1 -0
  48. package/dist/FlameGraph/uniqueness.d.ts +3 -0
  49. package/dist/FlameGraph/uniqueness.d.ts.map +1 -0
  50. package/dist/FlamegraphRenderer.d.ts +19 -0
  51. package/dist/FlamegraphRenderer.d.ts.map +1 -0
  52. package/dist/Icons.d.ts +9 -0
  53. package/dist/Icons.d.ts.map +1 -0
  54. package/dist/ProfilerTable.d.ts +21 -0
  55. package/dist/ProfilerTable.d.ts.map +1 -0
  56. package/dist/SharedQueryInput.d.ts +10 -0
  57. package/dist/SharedQueryInput.d.ts.map +1 -0
  58. package/dist/Toolbar.d.ts +31 -0
  59. package/dist/Toolbar.d.ts.map +1 -0
  60. package/dist/Tooltip/FlamegraphTooltip.d.ts +59 -0
  61. package/dist/Tooltip/FlamegraphTooltip.d.ts.map +1 -0
  62. package/dist/Tooltip/LeftClickIcon.d.ts +2 -0
  63. package/dist/Tooltip/LeftClickIcon.d.ts.map +1 -0
  64. package/dist/Tooltip/RightClickIcon.d.ts +2 -0
  65. package/dist/Tooltip/RightClickIcon.d.ts.map +1 -0
  66. package/dist/Tooltip/TableTooltip.d.ts +12 -0
  67. package/dist/Tooltip/TableTooltip.d.ts.map +1 -0
  68. package/dist/Tooltip/Tooltip.d.ts +29 -0
  69. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  70. package/dist/convert/convertJaegerTraceToProfile.d.ts +3 -0
  71. package/dist/convert/convertJaegerTraceToProfile.d.ts.map +1 -0
  72. package/dist/convert/diffTwoProfiles.d.ts +3 -0
  73. package/dist/convert/diffTwoProfiles.d.ts.map +1 -0
  74. package/dist/convert/flamebearersToTree.d.ts +11 -0
  75. package/dist/convert/flamebearersToTree.d.ts.map +1 -0
  76. package/dist/convert/sandwichViewProfiles.d.ts +14 -0
  77. package/dist/convert/sandwichViewProfiles.d.ts.map +1 -0
  78. package/dist/convert/subtract.d.ts +3 -0
  79. package/dist/convert/subtract.d.ts.map +1 -0
  80. package/dist/convert/testData.d.ts +50 -0
  81. package/dist/convert/testData.d.ts.map +1 -0
  82. package/dist/convert/toGraphviz.d.ts +3 -0
  83. package/dist/convert/toGraphviz.d.ts.map +1 -0
  84. package/dist/fitMode/fitMode.d.ts +42 -0
  85. package/dist/fitMode/fitMode.d.ts.map +1 -0
  86. package/dist/format/format.d.ts +42 -0
  87. package/dist/format/format.d.ts.map +1 -0
  88. package/dist/i18n.d.ts +55 -0
  89. package/dist/i18n.d.ts.map +1 -0
  90. package/dist/index.cjs.css +792 -0
  91. package/dist/index.cjs.js +5087 -0
  92. package/dist/index.d.ts +4 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.esm.css +792 -0
  95. package/dist/index.esm.js +5079 -0
  96. package/dist/index.node.d.ts +9 -0
  97. package/dist/index.node.d.ts.map +1 -0
  98. package/dist/logo-v3-small-T5VXIMRR.svg +32 -0
  99. package/dist/models/decode.d.ts +3 -0
  100. package/dist/models/decode.d.ts.map +1 -0
  101. package/dist/models/flamebearer.d.ts +63 -0
  102. package/dist/models/flamebearer.d.ts.map +1 -0
  103. package/dist/models/groups.d.ts +37 -0
  104. package/dist/models/groups.d.ts.map +1 -0
  105. package/dist/models/index.d.ts +8 -0
  106. package/dist/models/index.d.ts.map +1 -0
  107. package/dist/models/profile.d.ts +152 -0
  108. package/dist/models/profile.d.ts.map +1 -0
  109. package/dist/models/spyName.d.ts +8 -0
  110. package/dist/models/spyName.d.ts.map +1 -0
  111. package/dist/models/trace.d.ts +357 -0
  112. package/dist/models/trace.d.ts.map +1 -0
  113. package/dist/models/units.d.ts +6 -0
  114. package/dist/models/units.d.ts.map +1 -0
  115. package/dist/search.d.ts +2 -0
  116. package/dist/search.d.ts.map +1 -0
  117. package/dist/shims/Box.d.ts +38 -0
  118. package/dist/shims/Box.d.ts.map +1 -0
  119. package/dist/shims/Button.d.ts +26 -0
  120. package/dist/shims/Button.d.ts.map +1 -0
  121. package/dist/shims/Dropdown.d.ts +30 -0
  122. package/dist/shims/Dropdown.d.ts.map +1 -0
  123. package/dist/shims/Input.d.ts +19 -0
  124. package/dist/shims/Input.d.ts.map +1 -0
  125. package/dist/shims/LoadingSpinner.d.ts +7 -0
  126. package/dist/shims/LoadingSpinner.d.ts.map +1 -0
  127. package/dist/shims/Menu.d.ts +4 -0
  128. package/dist/shims/Menu.d.ts.map +1 -0
  129. package/dist/shims/NoData.d.ts +2 -0
  130. package/dist/shims/NoData.d.ts.map +1 -0
  131. package/dist/shims/Table.d.ts +52 -0
  132. package/dist/shims/Table.d.ts.map +1 -0
  133. package/dist/shims/Tooltip.d.ts +9 -0
  134. package/dist/shims/Tooltip.d.ts.map +1 -0
  135. package/package.json +84 -0
  136. package/src/FlameGraph/FlameGraphComponent/CheckIcon.tsx +27 -0
  137. package/src/FlameGraph/FlameGraphComponent/ContextMenu.module.scss +10 -0
  138. package/src/FlameGraph/FlameGraphComponent/ContextMenu.spec.tsx +84 -0
  139. package/src/FlameGraph/FlameGraphComponent/ContextMenu.tsx +86 -0
  140. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.module.css +8 -0
  141. package/src/FlameGraph/FlameGraphComponent/ContextMenuHighlight.tsx +47 -0
  142. package/src/FlameGraph/FlameGraphComponent/DiffLegend.module.css +21 -0
  143. package/src/FlameGraph/FlameGraphComponent/DiffLegend.tsx +52 -0
  144. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.module.css +40 -0
  145. package/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx +129 -0
  146. package/src/FlameGraph/FlameGraphComponent/Flamegraph.spec.ts +552 -0
  147. package/src/FlameGraph/FlameGraphComponent/Flamegraph.ts +446 -0
  148. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.spec.tsx +233 -0
  149. package/src/FlameGraph/FlameGraphComponent/Flamegraph_render.ts +478 -0
  150. package/src/FlameGraph/FlameGraphComponent/GraphVizPane.tsx +56 -0
  151. package/src/FlameGraph/FlameGraphComponent/GraphVizPanel.module.scss +55 -0
  152. package/src/FlameGraph/FlameGraphComponent/Header.module.css +27 -0
  153. package/src/FlameGraph/FlameGraphComponent/Header.tsx +71 -0
  154. package/src/FlameGraph/FlameGraphComponent/Highlight.module.css +7 -0
  155. package/src/FlameGraph/FlameGraphComponent/Highlight.spec.tsx +53 -0
  156. package/src/FlameGraph/FlameGraphComponent/Highlight.tsx +94 -0
  157. package/src/FlameGraph/FlameGraphComponent/LogoLink.module.scss +10 -0
  158. package/src/FlameGraph/FlameGraphComponent/LogoLink.tsx +101 -0
  159. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-collapses-small-blocks-into-one-1-snap.png +0 -0
  160. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-diff-mode-1-snap.png +0 -0
  161. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-highlighted-flamegraph-1-snap.png +0 -0
  162. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-normal-flamegraph-1-snap.png +0 -0
  163. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/canvas-renderer-spec-tsx-canvas-renderer-group-snapshot-works-with-selected-node-1-snap.png +0 -0
  164. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-also-zooms-1-snap.png +0 -0
  165. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-in-the-beginning-1-snap.png +0 -0
  166. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-when-node-is-not-in-the-beginning-1-snap.png +0 -0
  167. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-focused-renders-a-focused-node-zoom-top-level-1-snap.png +0 -0
  168. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-complex-flamegraph-1-snap.png +0 -0
  169. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-double-diff-flamegraph-1-snap.png +0 -0
  170. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-double-flamegraph-1-snap.png +0 -0
  171. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-highlighted-flamegraph-1-snap.png +0 -0
  172. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-flamegraph-1-snap.png +0 -0
  173. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-simple-tree-1-snap.png +0 -0
  174. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-flamegraph-1-snap.png +0 -0
  175. package/src/FlameGraph/FlameGraphComponent/__image_snapshots__/flamegraph-render-spec-tsx-render-group-snapshot-renders-a-zoomed-with-fit-mode-tail-1-snap.png +0 -0
  176. package/src/FlameGraph/FlameGraphComponent/canvas.module.css +6 -0
  177. package/src/FlameGraph/FlameGraphComponent/color.spec.ts +308 -0
  178. package/src/FlameGraph/FlameGraphComponent/color.ts +167 -0
  179. package/src/FlameGraph/FlameGraphComponent/colorPalette.ts +58 -0
  180. package/src/FlameGraph/FlameGraphComponent/constants.ts +5 -0
  181. package/src/FlameGraph/FlameGraphComponent/index.spec.tsx +291 -0
  182. package/src/FlameGraph/FlameGraphComponent/index.tsx +411 -0
  183. package/src/FlameGraph/FlameGraphComponent/murmur3.ts +97 -0
  184. package/src/FlameGraph/FlameGraphComponent/styles.module.scss +10 -0
  185. package/src/FlameGraph/FlameGraphComponent/testData.ts +427 -0
  186. package/src/FlameGraph/FlameGraphComponent/utils.ts +31 -0
  187. package/src/FlameGraph/FlameGraphComponent/viewTypes.ts +6 -0
  188. package/src/FlameGraph/FlameGraphRenderer.tsx +603 -0
  189. package/src/FlameGraph/FlamegraphRenderer.module.scss +93 -0
  190. package/src/FlameGraph/decode.ts +78 -0
  191. package/src/FlameGraph/normalize.spec.ts +76 -0
  192. package/src/FlameGraph/normalize.ts +60 -0
  193. package/src/FlameGraph/testData.json +423 -0
  194. package/src/FlameGraph/uniqueness.spec.ts +16 -0
  195. package/src/FlameGraph/uniqueness.ts +84 -0
  196. package/src/FlamegraphRenderer.tsx +61 -0
  197. package/src/Icons.tsx +74 -0
  198. package/src/ProfilerTable.tsx +527 -0
  199. package/src/SharedQueryInput.module.scss +82 -0
  200. package/src/SharedQueryInput.tsx +127 -0
  201. package/src/Toolbar.module.scss +117 -0
  202. package/src/Toolbar.spec.tsx +217 -0
  203. package/src/Toolbar.tsx +471 -0
  204. package/src/Tooltip/FlamegraphTooltip.spec.tsx +81 -0
  205. package/src/Tooltip/FlamegraphTooltip.tsx +257 -0
  206. package/src/Tooltip/LeftClickIcon.tsx +18 -0
  207. package/src/Tooltip/RightClickIcon.tsx +18 -0
  208. package/src/Tooltip/TableTooltip.spec.tsx +44 -0
  209. package/src/Tooltip/TableTooltip.tsx +145 -0
  210. package/src/Tooltip/Tooltip.module.scss +71 -0
  211. package/src/Tooltip/Tooltip.spec.tsx +395 -0
  212. package/src/Tooltip/Tooltip.tsx +336 -0
  213. package/src/__snapshots__/Toolbar.spec.tsx.snap +297 -0
  214. package/src/convert/convertJaegerTraceToProfile.ts +97 -0
  215. package/src/convert/diffTwoProfiles.ts +81 -0
  216. package/src/convert/flamebearersToTree.ts +78 -0
  217. package/src/convert/sandwichViewProfiles.spec.ts +65 -0
  218. package/src/convert/sandwichViewProfiles.ts +191 -0
  219. package/src/convert/subtract.ts +87 -0
  220. package/src/convert/testData.ts +145 -0
  221. package/src/convert/toGraphviz.ts +485 -0
  222. package/src/fitMode/fitMode.spec.ts +93 -0
  223. package/src/fitMode/fitMode.ts +122 -0
  224. package/src/format/format.spec.ts +291 -0
  225. package/src/format/format.ts +303 -0
  226. package/src/globals.d.ts +13 -0
  227. package/src/i18n.tsx +293 -0
  228. package/src/index.node.ts +19 -0
  229. package/src/index.spec.tsx +383 -0
  230. package/src/index.tsx +10 -0
  231. package/src/logo-v3-small.svg +32 -0
  232. package/src/models/decode.ts +45 -0
  233. package/src/models/flamebearer.ts +86 -0
  234. package/src/models/groups.ts +14 -0
  235. package/src/models/index.ts +7 -0
  236. package/src/models/profile.spec.ts +32 -0
  237. package/src/models/profile.ts +48 -0
  238. package/src/models/spyName.spec.ts +18 -0
  239. package/src/models/spyName.ts +32 -0
  240. package/src/models/trace.ts +45 -0
  241. package/src/models/units.spec.ts +21 -0
  242. package/src/models/units.ts +24 -0
  243. package/src/sass/_common.scss +206 -0
  244. package/src/sass/_css-variables.scss +201 -0
  245. package/src/sass/_mixins.scss +15 -0
  246. package/src/sass/_sanitize.scss +407 -0
  247. package/src/sass/_variables.scss +53 -0
  248. package/src/sass/flamegraph.scss +18 -0
  249. package/src/search.spec.ts +11 -0
  250. package/src/search.ts +4 -0
  251. package/src/shameful-any.d.ts +2 -0
  252. package/src/shims/Box.module.scss +57 -0
  253. package/src/shims/Box.tsx +105 -0
  254. package/src/shims/Button.module.scss +129 -0
  255. package/src/shims/Button.tsx +128 -0
  256. package/src/shims/Dropdown.module.scss +63 -0
  257. package/src/shims/Dropdown.tsx +96 -0
  258. package/src/shims/Input.module.scss +15 -0
  259. package/src/shims/Input.tsx +55 -0
  260. package/src/shims/LoadingSpinner.tsx +19 -0
  261. package/src/shims/Menu.tsx +9 -0
  262. package/src/shims/NoData.module.scss +6 -0
  263. package/src/shims/NoData.tsx +11 -0
  264. package/src/shims/Table.module.scss +82 -0
  265. package/src/shims/Table.spec.tsx +121 -0
  266. package/src/shims/Table.tsx +252 -0
  267. package/src/shims/Tooltip.tsx +51 -0
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@kylincloud/flamegraph",
3
+ "version": "0.35.6",
4
+ "description": "KylinCloud flamegraph renderer (Pyroscope-based)",
5
+ "license": "Apache-2.0",
6
+ "main": "dist/index.cjs.js",
7
+ "module": "dist/index.esm.js",
8
+ "types": "dist/index.d.ts",
9
+ "sideEffects": [
10
+ "*.css",
11
+ "*.scss"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.esm.js",
17
+ "require": "./dist/index.cjs.js"
18
+ },
19
+ "./dist/index.esm.css": "./dist/index.esm.css",
20
+ "./dist/index.cjs.css": "./dist/index.cjs.css"
21
+ },
22
+ "files": [
23
+ "dist/**/*",
24
+ "src/**/*",
25
+ "README.md",
26
+ "CHANGELOG.md",
27
+ "LICENSE",
28
+ "package.json"
29
+ ],
30
+ "scripts": {
31
+ "clean": "rm -rf dist",
32
+ "build": "pnpm run clean && pnpm run build:types && pnpm run build:js",
33
+ "build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly",
34
+ "build:js": "node scripts/build-esbuild.mjs",
35
+ "type-check": "tsc -p tsconfig.build.json --noEmit",
36
+ "export:vendor": "npm run build && node scripts/export-vendor.mjs",
37
+ "lint": "eslint ./ --cache --fix"
38
+ },
39
+ "peerDependencies": {
40
+ "react": ">=16.14.0",
41
+ "react-dom": ">=16.14.0"
42
+ },
43
+ "dependencies": {
44
+ "@fortawesome/free-solid-svg-icons": "^7.1.0",
45
+ "@fortawesome/react-fontawesome": "^3.1.1",
46
+ "@pyroscope/models": "^0.4.7",
47
+ "@react-hook/resize-observer": "^2.0.2",
48
+ "@szhsin/react-menu": "^4.5.1",
49
+ "classnames": "^2.5.1",
50
+ "clsx": "^2.1.1",
51
+ "color": "^5.0.3",
52
+ "d3-scale": "^4.0.2",
53
+ "graphviz-react": "^1.2.5",
54
+ "lodash.debounce": "^4.0.8",
55
+ "lodash.groupby": "^4.6.0",
56
+ "lodash.map": "^4.6.0",
57
+ "react-debounce-input": "^3.3.0",
58
+ "react-svg-spinner": "^1.0.4",
59
+ "true-myth": "^5.1.2",
60
+ "ts-essentials": "^10.1.1",
61
+ "zod": "^3.22.4"
62
+ },
63
+ "devDependencies": {
64
+ "@fortawesome/fontawesome-common-types": "^7.1.0",
65
+ "@types/classnames": "^2.3.4",
66
+ "@types/color": "^4.2.0",
67
+ "@types/d3-scale": "^4.0.9",
68
+ "@types/lodash.debounce": "^4.0.9",
69
+ "@types/lodash.groupby": "^4.6.9",
70
+ "@types/lodash.map": "^4.6.13",
71
+ "@types/node": "^24.10.1",
72
+ "@types/react": "18.2.66",
73
+ "@types/react-dom": "18.2.22",
74
+ "esbuild": "^0.27.1",
75
+ "esbuild-sass-plugin": "^3.3.1",
76
+ "eslint": "^9.0.0",
77
+ "postcss": "^8.5.6",
78
+ "postcss-modules": "^6.0.1",
79
+ "rollup": "^4.53.3",
80
+ "sass": "^1.94.2",
81
+ "tslib": "^2.8.1",
82
+ "typescript": "5.4.5"
83
+ }
84
+ }
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ // https://www.svgrepo.com/vectors/check/
4
+ export default function CheckIcon() {
5
+ return (
6
+ <svg
7
+ version="1.1"
8
+ id="Capa_1"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ x="0px"
11
+ y="0px"
12
+ width="12px"
13
+ height="12px"
14
+ viewBox="0 0 405.272 405.272"
15
+ >
16
+ <g>
17
+ <path
18
+ d="M393.401,124.425L179.603,338.208c-15.832,15.835-41.514,15.835-57.361,0L11.878,227.836
19
+
20
+ c-15.838-15.835-15.838-41.52,0-57.358c15.841-15.841,41.521-15.841,57.355-0.006l81.698,81.699L336.037,67.064
21
+
22
+ c15.841-15.841,41.523-15.829,57.358,0C409.23,82.902,409.23,108.578,393.401,124.425z"
23
+ />
24
+ </g>
25
+ </svg>
26
+ );
27
+ }
@@ -0,0 +1,10 @@
1
+ // a dummy class is required
2
+ // so that the component can import this class
3
+ // and therefore the library style (index.css above) is imported
4
+ .dummy {
5
+ display: block;
6
+
7
+ svg {
8
+ margin-right: 10px;
9
+ }
10
+ }
@@ -0,0 +1,84 @@
1
+ /* eslint-disable react/jsx-props-no-spreading */
2
+ import React from 'react';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { MenuItem } from './shims/Menu';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ import ContextMenu, { ContextMenuProps } from './ContextMenu';
8
+
9
+ const { queryByRole, queryAllByRole, getByRole } = screen;
10
+
11
+ function TestCanvas(props: Omit<ContextMenuProps, 'canvasRef'>) {
12
+ const canvasRef = React.useRef<HTMLCanvasElement>(null);
13
+
14
+ return (
15
+ <>
16
+ <canvas data-testid="canvas" ref={canvasRef} />
17
+ <ContextMenu data-testid="contextmenu" canvasRef={canvasRef} {...props} />
18
+ </>
19
+ );
20
+ }
21
+
22
+ describe('ContextMenu', () => {
23
+ it('works', () => {
24
+ let hasBeenClicked = false;
25
+
26
+ const xyToMenuItems = () => {
27
+ return [
28
+ <MenuItem
29
+ key="test"
30
+ onClick={() => {
31
+ hasBeenClicked = true;
32
+ }}
33
+ >
34
+ Test
35
+ </MenuItem>,
36
+ ];
37
+ };
38
+
39
+ render(
40
+ <TestCanvas
41
+ xyToMenuItems={xyToMenuItems}
42
+ onClose={() => {}}
43
+ onOpen={() => {}}
44
+ />
45
+ );
46
+
47
+ expect(queryByRole('menu')).not.toBeInTheDocument();
48
+
49
+ // trigger a right click
50
+ userEvent.click(screen.getByTestId('canvas'), { buttons: 2 });
51
+
52
+ expect(queryByRole('menu')).toBeVisible();
53
+ expect(queryAllByRole('menuitem')).toHaveLength(1);
54
+
55
+ userEvent.click(getByRole('menuitem'));
56
+ expect(hasBeenClicked).toBe(true);
57
+ });
58
+
59
+ it('shows different items depending on the clicked node', () => {
60
+ const xyToMenuItems = jest.fn();
61
+
62
+ render(
63
+ <TestCanvas
64
+ xyToMenuItems={xyToMenuItems}
65
+ onClose={() => {}}
66
+ onOpen={() => {}}
67
+ />
68
+ );
69
+
70
+ expect(queryByRole('menu')).not.toBeInTheDocument();
71
+
72
+ // trigger a right click
73
+ xyToMenuItems.mockReturnValueOnce([<MenuItem key="1">1</MenuItem>]);
74
+ userEvent.click(screen.getByTestId('canvas'), { buttons: 2 });
75
+ expect(queryAllByRole('menuitem')).toHaveLength(1);
76
+
77
+ xyToMenuItems.mockReturnValueOnce([
78
+ <MenuItem key="1">1</MenuItem>,
79
+ <MenuItem key="2">2</MenuItem>,
80
+ ]);
81
+ userEvent.click(screen.getByTestId('canvas'), { buttons: 2 });
82
+ expect(queryAllByRole('menuitem')).toHaveLength(2);
83
+ });
84
+ });
@@ -0,0 +1,86 @@
1
+ import '@szhsin/react-menu/dist/index.css';
2
+ /* eslint-disable react/jsx-props-no-spreading, import/no-extraneous-dependencies */
3
+ import React from 'react';
4
+ import { ControlledMenu, useMenuState } from '../../shims/Menu';
5
+ import styles from './ContextMenu.module.scss';
6
+
7
+ type xyToMenuItems = (x: number, y: number) => JSX.Element[];
8
+
9
+ export interface ContextMenuProps {
10
+ canvasRef: React.RefObject<HTMLCanvasElement>;
11
+
12
+ /**
13
+ * The menu is built dynamically
14
+ * Based on the cell's contents
15
+ * only MenuItem and SubMenu should be supported
16
+ */
17
+ xyToMenuItems: xyToMenuItems;
18
+
19
+ onClose: () => void;
20
+ onOpen: (x: number, y: number) => void;
21
+ }
22
+
23
+ export default function ContextMenu(props: ContextMenuProps) {
24
+ const [menuProps, toggleMenu] = useMenuState({ transition: true });
25
+ const [anchorPoint, setAnchorPoint] = React.useState({ x: 0, y: 0 });
26
+ const { canvasRef } = props;
27
+ const [menuItems, setMenuItems] = React.useState<JSX.Element[]>([]);
28
+ const {
29
+ xyToMenuItems,
30
+ onClose: onCloseCallback,
31
+ onOpen: onOpenCallback,
32
+ } = props;
33
+
34
+ const onClose = () => {
35
+ toggleMenu(false);
36
+
37
+ onCloseCallback();
38
+ };
39
+
40
+ React.useEffect(() => {
41
+ toggleMenu(false);
42
+
43
+ // use closure to "cache" the current canvas reference
44
+ // so that when cleaning up, it points to a valid canvas
45
+ // (otherwise it would be null)
46
+ const canvasEl = canvasRef.current;
47
+ if (!canvasEl) {
48
+ return () => {};
49
+ }
50
+
51
+ const onContextMenu = (e: MouseEvent) => {
52
+ e.preventDefault();
53
+
54
+ const items = xyToMenuItems(e.offsetX, e.offsetY);
55
+ setMenuItems(items);
56
+
57
+ // TODO
58
+ // if the menu becomes too large, it may overflow to outside the screen
59
+ const x = e.clientX;
60
+ const y = e.clientY + 20;
61
+
62
+ setAnchorPoint({ x, y });
63
+ toggleMenu(true);
64
+
65
+ onOpenCallback(e.offsetX, e.offsetY);
66
+ };
67
+
68
+ // watch for mouse events on the bar
69
+ canvasEl.addEventListener('contextmenu', onContextMenu);
70
+
71
+ return () => {
72
+ canvasEl.removeEventListener('contextmenu', onContextMenu);
73
+ };
74
+ }, [xyToMenuItems]);
75
+
76
+ return (
77
+ <ControlledMenu
78
+ {...menuProps}
79
+ className={styles.dummy}
80
+ anchorPoint={anchorPoint}
81
+ onClose={onClose}
82
+ >
83
+ {menuItems}
84
+ </ControlledMenu>
85
+ );
86
+ }
@@ -0,0 +1,8 @@
1
+ .highlightContextMenu {
2
+ position: absolute;
3
+ pointer-events: none;
4
+ /* make it a bit lighter so that it's easier to distinguish when both highlights are on */
5
+ background: #ffffff8c;
6
+ mix-blend-mode: overlay;
7
+ z-index: 2;
8
+ }
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import { Maybe } from 'true-myth';
3
+ import styles from './ContextMenuHighlight.module.css';
4
+
5
+ export interface HighlightProps {
6
+ // probably the same as the bar height
7
+ barHeight: number;
8
+
9
+ node: Maybe<{ top: number; left: number; width: number }>;
10
+ }
11
+
12
+ const initialSyle: React.CSSProperties = {
13
+ height: '0px',
14
+ visibility: 'hidden',
15
+ };
16
+
17
+ /**
18
+ * Highlight on the node that triggered the context menu
19
+ */
20
+ export default function ContextMenuHighlight(props: HighlightProps) {
21
+ const { node, barHeight } = props;
22
+ const [style, setStyle] = React.useState(initialSyle);
23
+
24
+ React.useEffect(
25
+ () => {
26
+ node.match({
27
+ Nothing: () => setStyle(initialSyle),
28
+ Just: (data) =>
29
+ setStyle({
30
+ visibility: 'visible',
31
+ height: `${barHeight}px`,
32
+ ...data,
33
+ }),
34
+ });
35
+ },
36
+ // refresh callback functions when they change
37
+ [node]
38
+ );
39
+
40
+ return (
41
+ <div
42
+ className={styles.highlightContextMenu}
43
+ style={style}
44
+ data-testid="flamegraph-highlight-contextmenu"
45
+ />
46
+ );
47
+ }
@@ -0,0 +1,21 @@
1
+ .flamegraph-legend {
2
+ display: flex;
3
+ align-items: center;
4
+ font-size: 11px;
5
+ justify-content: center;
6
+ }
7
+
8
+ .flamegraph-legend-list {
9
+ display: flex;
10
+ justify-content: center;
11
+ }
12
+
13
+ .flamegraph-legend-item {
14
+ width: 37px;
15
+ text-align: center;
16
+
17
+ /* if for some reason the contents can't fit
18
+ * don't break into 2 lines */
19
+ white-space: nowrap;
20
+ overflow: hidden;
21
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { NewDiffColor } from './color';
3
+ import { FlamegraphPalette } from './colorPalette';
4
+ import styles from './DiffLegend.module.css';
5
+
6
+ export type sizeMode = 'small' | 'large';
7
+ interface DiffLegendProps {
8
+ palette: FlamegraphPalette;
9
+ showMode: sizeMode;
10
+ }
11
+
12
+ export default function DiffLegend(props: DiffLegendProps) {
13
+ const { palette, showMode } = props;
14
+ const values = decideLegend(showMode);
15
+
16
+ const color = NewDiffColor(palette);
17
+
18
+ return (
19
+ <div
20
+ data-testid="flamegraph-legend"
21
+ className={`${styles['flamegraph-legend']} ${styles['flamegraph-legend-list']}`}
22
+ >
23
+ {values.map((v) => (
24
+ <div
25
+ key={v}
26
+ className={styles['flamegraph-legend-item']}
27
+ style={{
28
+ backgroundColor: color(v).rgb().toString(),
29
+ }}
30
+ >
31
+ {v > 0 ? '+' : ''}
32
+ {v}%
33
+ </div>
34
+ ))}
35
+ </div>
36
+ );
37
+ }
38
+
39
+ function decideLegend(showMode: sizeMode) {
40
+ switch (showMode) {
41
+ case 'large': {
42
+ return [-100, -80, -60, -40, -20, -10, 0, 10, 20, 40, 60, 80, 100];
43
+ }
44
+
45
+ case 'small': {
46
+ return [-100, -40, -20, 0, 20, 40, 100];
47
+ }
48
+
49
+ default:
50
+ throw new Error(`Unsupported ${showMode}`);
51
+ }
52
+ }
@@ -0,0 +1,40 @@
1
+ .diffPaletteDropdown {
2
+ max-width: 510px;
3
+ min-width: 292px;
4
+ width: inherit;
5
+ padding-right: 20px !important;
6
+ }
7
+
8
+ .dropdownWrapper {
9
+ display: flex;
10
+ align-items: center;
11
+ flex-direction: column;
12
+ }
13
+
14
+ .diffPaletteDropdown > div {
15
+ margin-bottom: 0px;
16
+ display: flex;
17
+ }
18
+
19
+ .diffPaletteDropdown::after {
20
+ top: 0px;
21
+ }
22
+
23
+ .dropdownItem {
24
+ width: 100%;
25
+ display: flex;
26
+ }
27
+
28
+ .dropdownItem svg {
29
+ margin-left: 1em;
30
+ fill: var(--ps-neutral-2);
31
+ }
32
+
33
+ .row {
34
+ display: flex;
35
+ justify-content: space-between;
36
+ margin-left: auto;
37
+ margin-right: auto;
38
+ width: 300px;
39
+ height: 45px;
40
+ }
@@ -0,0 +1,129 @@
1
+ // src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx
2
+ import React from 'react';
3
+ import cx from 'classnames';
4
+ import useResizeObserver from '@react-hook/resize-observer';
5
+ import {
6
+ ColorBlindPalette,
7
+ DefaultPalette,
8
+ FlamegraphPalette,
9
+ } from './colorPalette';
10
+ import DiffLegend from './DiffLegend';
11
+ import CheckIcon from './CheckIcon';
12
+ // Until we migrate ui to its own package this should do it
13
+ // eslint-disable-next-line
14
+ import Dropdown, { MenuItem, MenuButton } from '../../shims/Dropdown';
15
+ // eslint-disable-next-line
16
+ import dropdownStyles from '../../shims/Dropdown.module.scss';
17
+
18
+ import styles from './DiffLegendPaletteDropdown.module.css';
19
+ import { useFlamegraphI18n } from '../../i18n';
20
+
21
+ const paletteList = [DefaultPalette, ColorBlindPalette];
22
+
23
+ interface DiffLegendPaletteDropdownProps {
24
+ palette: FlamegraphPalette;
25
+ onChange: (p: FlamegraphPalette) => void;
26
+ }
27
+
28
+ export const DiffLegendPaletteDropdown = (
29
+ props: DiffLegendPaletteDropdownProps
30
+ ) => {
31
+ const { palette = DefaultPalette, onChange } = props;
32
+ const legendRef = React.useRef<HTMLDivElement>(null);
33
+ const showMode = useSizeMode(legendRef);
34
+
35
+ const messages = useFlamegraphI18n();
36
+
37
+ const renderPaletteLabel = (name: string) => {
38
+ if (name === 'Default') {
39
+ return messages.paletteDefaultName;
40
+ }
41
+ if (name === 'Color Blind') {
42
+ return messages.paletteColorBlindName;
43
+ }
44
+ // 兜底:未知名字直接原样展示
45
+ return name;
46
+ };
47
+
48
+ return (
49
+ <>
50
+ <div className={styles.row} role="heading" aria-level={2}>
51
+ <p style={{ color: palette.goodColor.rgb().string() }}>
52
+ {messages.diffLegendRemovedLabel}
53
+ </p>
54
+ <p style={{ color: palette.badColor.rgb().string() }}>
55
+ {messages.diffLegendAddedLabel}
56
+ </p>
57
+ </div>
58
+
59
+ <div ref={legendRef} className={styles.dropdownWrapper}>
60
+ <Dropdown
61
+ label={messages.diffLegendSelectPalette}
62
+ align="end"
63
+ menuButton={
64
+ <MenuButton
65
+ className={cx(
66
+ // eslint-disable-next-line
67
+ dropdownStyles.dropdownMenuButton,
68
+ styles.diffPaletteDropdown
69
+ )}
70
+ >
71
+ <DiffLegend palette={palette} showMode={showMode} />
72
+ </MenuButton>
73
+ }
74
+ onItemClick={(e) => onChange(e.value)}
75
+ >
76
+ {paletteList.map((p) => (
77
+ <MenuItem key={p.name} value={p}>
78
+ <div>
79
+ <label>{renderPaletteLabel(p.name)}</label>
80
+ <div className={styles.dropdownItem}>
81
+ <DiffLegend palette={p} showMode={showMode} />
82
+
83
+ {p === palette ? <CheckIcon /> : null}
84
+ </div>
85
+ </div>
86
+ </MenuItem>
87
+ ))}
88
+ </Dropdown>
89
+ </div>
90
+ </>
91
+ );
92
+ };
93
+
94
+ /**
95
+ * TODO: unify this and toolbar's
96
+ * Custom hook that returns the size ('large' | 'small')
97
+ * that should be displayed
98
+ * based on the toolbar width
99
+ */
100
+ // arbitrary value
101
+ // as a simple heuristic, try to run the comparison view
102
+ // and see when the buttons start to overlap
103
+ const WIDTH_THRESHOLD = 13 * 37;
104
+ const useSizeMode = (target: React.RefObject<HTMLDivElement>) => {
105
+ const [size, setSize] = React.useState<'large' | 'small'>('large');
106
+
107
+ const calcMode = (width: number) => {
108
+ if (width < WIDTH_THRESHOLD) {
109
+ return 'small';
110
+ }
111
+ return 'large';
112
+ };
113
+
114
+ React.useLayoutEffect(() => {
115
+ if (target.current) {
116
+ const { width } = target.current.getBoundingClientRect();
117
+
118
+ setSize(calcMode(width));
119
+ }
120
+ }, [target.current]);
121
+
122
+ useResizeObserver(target, (entry: ResizeObserverEntry) => {
123
+ setSize(calcMode(entry.contentRect.width));
124
+ });
125
+
126
+ return size;
127
+ };
128
+
129
+ export default DiffLegendPaletteDropdown;