@dudousxd/nestjs-telescope-ui 2.0.0 → 3.0.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.
Files changed (266) hide show
  1. package/CHANGELOG.md +771 -0
  2. package/README.md +35 -0
  3. package/dist/client/console-url.d.ts +11 -0
  4. package/dist/client/console-url.d.ts.map +1 -0
  5. package/dist/client/console-url.js +21 -0
  6. package/dist/client/console-url.js.map +1 -0
  7. package/dist/client/index.d.ts +3 -2
  8. package/dist/client/index.d.ts.map +1 -1
  9. package/dist/client/index.js +4 -1
  10. package/dist/client/index.js.map +1 -1
  11. package/dist/client/telescope-client.d.ts +82 -11
  12. package/dist/client/telescope-client.d.ts.map +1 -1
  13. package/dist/client/telescope-client.js +101 -54
  14. package/dist/client/telescope-client.js.map +1 -1
  15. package/dist/client/types.d.ts +431 -30
  16. package/dist/client/types.d.ts.map +1 -1
  17. package/dist/react/components/batch-timeline.js +2 -2
  18. package/dist/react/components/batch-timeline.js.map +1 -1
  19. package/dist/react/components/cache-badge.d.ts +14 -6
  20. package/dist/react/components/cache-badge.d.ts.map +1 -1
  21. package/dist/react/components/cache-badge.js +26 -12
  22. package/dist/react/components/cache-badge.js.map +1 -1
  23. package/dist/react/components/charts/area-chart-card.d.ts +11 -3
  24. package/dist/react/components/charts/area-chart-card.d.ts.map +1 -1
  25. package/dist/react/components/charts/area-chart-card.js +33 -6
  26. package/dist/react/components/charts/area-chart-card.js.map +1 -1
  27. package/dist/react/components/charts/bar-chart-card.d.ts +18 -7
  28. package/dist/react/components/charts/bar-chart-card.d.ts.map +1 -1
  29. package/dist/react/components/charts/bar-chart-card.js +18 -9
  30. package/dist/react/components/charts/bar-chart-card.js.map +1 -1
  31. package/dist/react/components/charts/breakdown-card.d.ts +17 -0
  32. package/dist/react/components/charts/breakdown-card.d.ts.map +1 -0
  33. package/dist/react/components/charts/breakdown-card.js +43 -0
  34. package/dist/react/components/charts/breakdown-card.js.map +1 -0
  35. package/dist/react/components/charts/chart-card.d.ts +7 -0
  36. package/dist/react/components/charts/chart-card.d.ts.map +1 -1
  37. package/dist/react/components/charts/chart-card.js +10 -3
  38. package/dist/react/components/charts/chart-card.js.map +1 -1
  39. package/dist/react/components/charts/chart-selection.d.ts +44 -0
  40. package/dist/react/components/charts/chart-selection.d.ts.map +1 -0
  41. package/dist/react/components/charts/chart-selection.js +36 -0
  42. package/dist/react/components/charts/chart-selection.js.map +1 -0
  43. package/dist/react/components/charts/chart-theme.d.ts +62 -19
  44. package/dist/react/components/charts/chart-theme.d.ts.map +1 -1
  45. package/dist/react/components/charts/chart-theme.js +72 -13
  46. package/dist/react/components/charts/chart-theme.js.map +1 -1
  47. package/dist/react/components/charts/distribution-chart-card.d.ts +17 -0
  48. package/dist/react/components/charts/distribution-chart-card.d.ts.map +1 -0
  49. package/dist/react/components/charts/distribution-chart-card.js +46 -0
  50. package/dist/react/components/charts/distribution-chart-card.js.map +1 -0
  51. package/dist/react/components/charts/gauge-card.d.ts +12 -0
  52. package/dist/react/components/charts/gauge-card.d.ts.map +1 -0
  53. package/dist/react/components/charts/gauge-card.js +17 -0
  54. package/dist/react/components/charts/gauge-card.js.map +1 -0
  55. package/dist/react/components/charts/index.d.ts +1 -0
  56. package/dist/react/components/charts/index.d.ts.map +1 -1
  57. package/dist/react/components/charts/index.js +1 -0
  58. package/dist/react/components/charts/index.js.map +1 -1
  59. package/dist/react/components/charts/stacked-area-chart-card.d.ts +17 -3
  60. package/dist/react/components/charts/stacked-area-chart-card.d.ts.map +1 -1
  61. package/dist/react/components/charts/stacked-area-chart-card.js +31 -8
  62. package/dist/react/components/charts/stacked-area-chart-card.js.map +1 -1
  63. package/dist/react/components/entries-table.d.ts.map +1 -1
  64. package/dist/react/components/entries-table.js +17 -3
  65. package/dist/react/components/entries-table.js.map +1 -1
  66. package/dist/react/components/entry-detail.d.ts +2 -0
  67. package/dist/react/components/entry-detail.d.ts.map +1 -1
  68. package/dist/react/components/entry-detail.js +186 -20
  69. package/dist/react/components/entry-detail.js.map +1 -1
  70. package/dist/react/components/entry-insights.d.ts.map +1 -1
  71. package/dist/react/components/entry-insights.js +28 -17
  72. package/dist/react/components/entry-insights.js.map +1 -1
  73. package/dist/react/components/entry-types.d.ts +29 -0
  74. package/dist/react/components/entry-types.d.ts.map +1 -1
  75. package/dist/react/components/entry-types.js +60 -0
  76. package/dist/react/components/entry-types.js.map +1 -1
  77. package/dist/react/components/export-json-toolbar.js +2 -2
  78. package/dist/react/components/export-json-toolbar.js.map +1 -1
  79. package/dist/react/components/extensions/panel-column-menu.d.ts +19 -0
  80. package/dist/react/components/extensions/panel-column-menu.d.ts.map +1 -0
  81. package/dist/react/components/extensions/panel-column-menu.js +47 -0
  82. package/dist/react/components/extensions/panel-column-menu.js.map +1 -0
  83. package/dist/react/components/extensions/panel-renderer.d.ts +46 -0
  84. package/dist/react/components/extensions/panel-renderer.d.ts.map +1 -0
  85. package/dist/react/components/extensions/panel-renderer.js +122 -0
  86. package/dist/react/components/extensions/panel-renderer.js.map +1 -0
  87. package/dist/react/components/extensions/panel-table-header.d.ts +18 -0
  88. package/dist/react/components/extensions/panel-table-header.d.ts.map +1 -0
  89. package/dist/react/components/extensions/panel-table-header.js +82 -0
  90. package/dist/react/components/extensions/panel-table-header.js.map +1 -0
  91. package/dist/react/components/extensions/panel-table.d.ts +27 -0
  92. package/dist/react/components/extensions/panel-table.d.ts.map +1 -0
  93. package/dist/react/components/extensions/panel-table.js +87 -0
  94. package/dist/react/components/extensions/panel-table.js.map +1 -0
  95. package/dist/react/components/extensions/stat-card.d.ts +18 -0
  96. package/dist/react/components/extensions/stat-card.d.ts.map +1 -0
  97. package/dist/react/components/extensions/stat-card.js +44 -0
  98. package/dist/react/components/extensions/stat-card.js.map +1 -0
  99. package/dist/react/components/extensions/table-columns.d.ts +32 -0
  100. package/dist/react/components/extensions/table-columns.d.ts.map +1 -0
  101. package/dist/react/components/extensions/table-columns.js +51 -0
  102. package/dist/react/components/extensions/table-columns.js.map +1 -0
  103. package/dist/react/components/extensions/table-features.d.ts +47 -0
  104. package/dist/react/components/extensions/table-features.d.ts.map +1 -0
  105. package/dist/react/components/extensions/table-features.js +45 -0
  106. package/dist/react/components/extensions/table-features.js.map +1 -0
  107. package/dist/react/components/extensions/table-query.d.ts +53 -0
  108. package/dist/react/components/extensions/table-query.d.ts.map +1 -0
  109. package/dist/react/components/extensions/table-query.js +51 -0
  110. package/dist/react/components/extensions/table-query.js.map +1 -0
  111. package/dist/react/components/flamegraph-view.d.ts +18 -0
  112. package/dist/react/components/flamegraph-view.d.ts.map +1 -0
  113. package/dist/react/components/flamegraph-view.js +48 -0
  114. package/dist/react/components/flamegraph-view.js.map +1 -0
  115. package/dist/react/components/flamegraph.d.ts +34 -0
  116. package/dist/react/components/flamegraph.d.ts.map +1 -0
  117. package/dist/react/components/flamegraph.js +54 -0
  118. package/dist/react/components/flamegraph.js.map +1 -0
  119. package/dist/react/components/inertia-badge.d.ts +33 -0
  120. package/dist/react/components/inertia-badge.d.ts.map +1 -0
  121. package/dist/react/components/inertia-badge.js +72 -0
  122. package/dist/react/components/inertia-badge.js.map +1 -0
  123. package/dist/react/components/inertia-body.d.ts +11 -0
  124. package/dist/react/components/inertia-body.d.ts.map +1 -0
  125. package/dist/react/components/inertia-body.js +127 -0
  126. package/dist/react/components/inertia-body.js.map +1 -0
  127. package/dist/react/components/official-event.d.ts +16 -0
  128. package/dist/react/components/official-event.d.ts.map +1 -0
  129. package/dist/react/components/official-event.js +88 -0
  130. package/dist/react/components/official-event.js.map +1 -0
  131. package/dist/react/components/pulse-panel.d.ts.map +1 -1
  132. package/dist/react/components/pulse-panel.js +6 -6
  133. package/dist/react/components/pulse-panel.js.map +1 -1
  134. package/dist/react/components/queues/EnqueueJobForm.d.ts.map +1 -1
  135. package/dist/react/components/queues/EnqueueJobForm.js +2 -1
  136. package/dist/react/components/queues/EnqueueJobForm.js.map +1 -1
  137. package/dist/react/components/queues/JobActions.d.ts.map +1 -1
  138. package/dist/react/components/queues/JobActions.js +11 -9
  139. package/dist/react/components/queues/JobActions.js.map +1 -1
  140. package/dist/react/components/queues/JobDetailDrawer.d.ts.map +1 -1
  141. package/dist/react/components/queues/JobDetailDrawer.js +8 -13
  142. package/dist/react/components/queues/JobDetailDrawer.js.map +1 -1
  143. package/dist/react/components/queues/JobTable.d.ts.map +1 -1
  144. package/dist/react/components/queues/JobTable.js +0 -0
  145. package/dist/react/components/queues/JobTable.js.map +1 -1
  146. package/dist/react/components/queues/QueueList.d.ts.map +1 -1
  147. package/dist/react/components/queues/QueueList.js +6 -6
  148. package/dist/react/components/queues/QueueList.js.map +1 -1
  149. package/dist/react/components/queues/QueueStateTabs.d.ts.map +1 -1
  150. package/dist/react/components/queues/QueueStateTabs.js +9 -7
  151. package/dist/react/components/queues/QueueStateTabs.js.map +1 -1
  152. package/dist/react/components/queues/queue-format.d.ts +5 -1
  153. package/dist/react/components/queues/queue-format.d.ts.map +1 -1
  154. package/dist/react/components/queues/queue-format.js +11 -7
  155. package/dist/react/components/queues/queue-format.js.map +1 -1
  156. package/dist/react/components/queues-panel.js +1 -1
  157. package/dist/react/components/queues-panel.js.map +1 -1
  158. package/dist/react/components/request-timeline.d.ts.map +1 -1
  159. package/dist/react/components/request-timeline.js +2 -2
  160. package/dist/react/components/request-timeline.js.map +1 -1
  161. package/dist/react/components/retention-indicator.d.ts.map +1 -1
  162. package/dist/react/components/retention-indicator.js +1 -1
  163. package/dist/react/components/retention-indicator.js.map +1 -1
  164. package/dist/react/components/sparkline.d.ts.map +1 -1
  165. package/dist/react/components/sparkline.js +1 -1
  166. package/dist/react/components/sparkline.js.map +1 -1
  167. package/dist/react/components/tag-autocomplete.d.ts +21 -4
  168. package/dist/react/components/tag-autocomplete.d.ts.map +1 -1
  169. package/dist/react/components/tag-autocomplete.js +57 -29
  170. package/dist/react/components/tag-autocomplete.js.map +1 -1
  171. package/dist/react/components/to-csv.d.ts +25 -0
  172. package/dist/react/components/to-csv.d.ts.map +1 -0
  173. package/dist/react/components/to-csv.js +78 -0
  174. package/dist/react/components/to-csv.js.map +1 -0
  175. package/dist/react/components/user-tag.d.ts +15 -0
  176. package/dist/react/components/user-tag.d.ts.map +1 -1
  177. package/dist/react/components/user-tag.js +19 -0
  178. package/dist/react/components/user-tag.js.map +1 -1
  179. package/dist/react/components/waterfall-view.d.ts +13 -0
  180. package/dist/react/components/waterfall-view.d.ts.map +1 -0
  181. package/dist/react/components/waterfall-view.js +58 -0
  182. package/dist/react/components/waterfall-view.js.map +1 -0
  183. package/dist/react/components/window-select.d.ts.map +1 -1
  184. package/dist/react/components/window-select.js +6 -2
  185. package/dist/react/components/window-select.js.map +1 -1
  186. package/dist/react/index.d.ts +7 -1
  187. package/dist/react/index.d.ts.map +1 -1
  188. package/dist/react/index.js +11 -1
  189. package/dist/react/index.js.map +1 -1
  190. package/dist/react/ui/badge.d.ts +16 -0
  191. package/dist/react/ui/badge.d.ts.map +1 -0
  192. package/dist/react/ui/badge.js +28 -0
  193. package/dist/react/ui/badge.js.map +1 -0
  194. package/dist/react/ui/button.d.ts +17 -0
  195. package/dist/react/ui/button.d.ts.map +1 -0
  196. package/dist/react/ui/button.js +35 -0
  197. package/dist/react/ui/button.js.map +1 -0
  198. package/dist/react/ui/chart.d.ts +116 -0
  199. package/dist/react/ui/chart.d.ts.map +1 -0
  200. package/dist/react/ui/chart.js +191 -0
  201. package/dist/react/ui/chart.js.map +1 -0
  202. package/dist/react/ui/cn.d.ts +8 -0
  203. package/dist/react/ui/cn.d.ts.map +1 -0
  204. package/dist/react/ui/cn.js +11 -0
  205. package/dist/react/ui/cn.js.map +1 -0
  206. package/dist/react/ui/dialog.d.ts +26 -0
  207. package/dist/react/ui/dialog.d.ts.map +1 -0
  208. package/dist/react/ui/dialog.js +40 -0
  209. package/dist/react/ui/dialog.js.map +1 -0
  210. package/dist/react/ui/index.d.ts +24 -0
  211. package/dist/react/ui/index.d.ts.map +1 -0
  212. package/dist/react/ui/index.js +24 -0
  213. package/dist/react/ui/index.js.map +1 -0
  214. package/dist/react/ui/input.d.ts +3 -0
  215. package/dist/react/ui/input.d.ts.map +1 -0
  216. package/dist/react/ui/input.js +8 -0
  217. package/dist/react/ui/input.js.map +1 -0
  218. package/dist/react/ui/select.d.ts +20 -0
  219. package/dist/react/ui/select.d.ts.map +1 -0
  220. package/dist/react/ui/select.js +27 -0
  221. package/dist/react/ui/select.js.map +1 -0
  222. package/dist/react/ui/table.d.ts +27 -0
  223. package/dist/react/ui/table.d.ts.map +1 -0
  224. package/dist/react/ui/table.js +22 -0
  225. package/dist/react/ui/table.js.map +1 -0
  226. package/dist/react/ui/tabs.d.ts +12 -0
  227. package/dist/react/ui/tabs.d.ts.map +1 -0
  228. package/dist/react/ui/tabs.js +20 -0
  229. package/dist/react/ui/tabs.js.map +1 -0
  230. package/dist/react/ui/tooltip.d.ts +10 -0
  231. package/dist/react/ui/tooltip.d.ts.map +1 -0
  232. package/dist/react/ui/tooltip.js +12 -0
  233. package/dist/react/ui/tooltip.js.map +1 -0
  234. package/dist/react/use-telescope-queries.d.ts +129 -130
  235. package/dist/react/use-telescope-queries.d.ts.map +1 -1
  236. package/dist/react/use-telescope-queries.js +146 -1
  237. package/dist/react/use-telescope-queries.js.map +1 -1
  238. package/dist/react/use-telescope-stream.d.ts +10 -0
  239. package/dist/react/use-telescope-stream.d.ts.map +1 -0
  240. package/dist/react/use-telescope-stream.js +40 -0
  241. package/dist/react/use-telescope-stream.js.map +1 -0
  242. package/dist/server/index.d.ts +3 -1
  243. package/dist/server/index.d.ts.map +1 -1
  244. package/dist/server/index.js +5 -0
  245. package/dist/server/index.js.map +1 -1
  246. package/dist/server/telescope-ui.controller.d.ts.map +1 -1
  247. package/dist/server/telescope-ui.controller.js +3 -1
  248. package/dist/server/telescope-ui.controller.js.map +1 -1
  249. package/dist/server/telescope-ui.module.d.ts +28 -1
  250. package/dist/server/telescope-ui.module.d.ts.map +1 -1
  251. package/dist/server/telescope-ui.module.js +48 -4
  252. package/dist/server/telescope-ui.module.js.map +1 -1
  253. package/dist/server/telescope-ui.options.d.ts +63 -0
  254. package/dist/server/telescope-ui.options.d.ts.map +1 -1
  255. package/dist/server/telescope-ui.options.js.map +1 -1
  256. package/dist/spa/assets/index-BfyQ_2IN.css +1 -0
  257. package/dist/spa/assets/index-EBJiTdQF.js +159 -0
  258. package/dist/spa/index.html +2 -2
  259. package/package.json +75 -36
  260. package/skills/telescope-ui-dashboard/SKILL.md +203 -0
  261. package/dist/react/components/type-tabs.d.ts +0 -5
  262. package/dist/react/components/type-tabs.d.ts.map +0 -1
  263. package/dist/react/components/type-tabs.js +0 -8
  264. package/dist/react/components/type-tabs.js.map +0 -1
  265. package/dist/spa/assets/index-CCzTpxj3.css +0 -1
  266. package/dist/spa/assets/index-w0XTI5OM.js +0 -135
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from 'react';
3
+ import { Table, TableBody, TableCell, TableRow } from '../../ui/table.js';
4
+ import { PanelColumnMenu } from './panel-column-menu.js';
5
+ import { PanelTableHeader } from './panel-table-header.js';
6
+ import { buildPanelColumns } from './table-columns.js';
7
+ import { flexRender, panelTableFeatures, useTable, } from './table-features.js';
8
+ /**
9
+ * How tall a scrolling (paged) table is allowed to get before its rows scroll
10
+ * under a pinned header. A page of 50 rows is otherwise a ~1,500px card that
11
+ * pushes every panel below it off the screen, and by the time you have scrolled
12
+ * to row 40 the column labels are long gone. A short page never reaches the cap,
13
+ * so it looks exactly as it did.
14
+ */
15
+ const SCROLL_BODY_CLASS = 'max-h-[28rem]';
16
+ function keyForRow(row, columns, index) {
17
+ // The row's own values, as before, with the index appended: two identical rows
18
+ // in one page are perfectly legal in provider output and used to collide into
19
+ // a duplicate React key.
20
+ return `${columns.map((column) => String(row[column.key] ?? '')).join('|')}#${index}`;
21
+ }
22
+ /**
23
+ * A `table` panel, rendered through a TanStack Table v9 instance.
24
+ *
25
+ * The instance is what earns its place: sorting cycles, filter values, and
26
+ * column visibility are all state machines this file would otherwise hand-roll.
27
+ * What it deliberately does NOT do is transform rows — `manualSorting` and
28
+ * `manualFiltering` are on and no row model is registered, so the rows render in
29
+ * exactly the order the provider returned them, and the sort/filter state is
30
+ * handed straight back up to the dashboard page to re-resolve that provider.
31
+ * See `table-features.ts` for why.
32
+ */
33
+ export function PanelTable({ columns, rows, sort, onSortChange, filters, onFiltersChange, scrolls, }) {
34
+ const [columnVisibility, setColumnVisibility] = useState({});
35
+ const columnDefs = useMemo(() => buildPanelColumns(columns), [columns]);
36
+ // The dashboard's own state shapes, projected into TanStack's. Both directions
37
+ // are narrow on purpose: this table sorts by one column and filters with text,
38
+ // which is all the SPI puts on the wire, so a multi-column sort arriving from
39
+ // the library would have nowhere to go.
40
+ const sorting = sort ? [{ id: sort.key, desc: sort.dir === 'desc' }] : [];
41
+ const columnFilters = Object.entries(filters ?? {}).map(([id, value]) => ({
42
+ id,
43
+ value,
44
+ }));
45
+ const table = useTable({
46
+ features: panelTableFeatures,
47
+ columns: columnDefs,
48
+ data: rows,
49
+ state: { sorting, columnFilters, columnVisibility },
50
+ onColumnVisibilityChange: setColumnVisibility,
51
+ onSortingChange: (updater) => {
52
+ const next = typeof updater === 'function' ? updater(sorting) : updater;
53
+ const first = next[0];
54
+ // `enableSortingRemoval` is on by default, so the third click empties the
55
+ // array — that is "unsorted", not "sorted by nothing", and it has to reach
56
+ // the provider as an absent `sort` param or the table would be stuck.
57
+ onSortChange?.(first ? { key: first.id, dir: first.desc ? 'desc' : 'asc' } : undefined);
58
+ },
59
+ onColumnFiltersChange: (updater) => {
60
+ const next = typeof updater === 'function' ? updater(columnFilters) : updater;
61
+ const applied = {};
62
+ for (const filter of next) {
63
+ if (typeof filter.value === 'string' && filter.value !== '')
64
+ applied[filter.id] = filter.value;
65
+ }
66
+ onFiltersChange?.(applied);
67
+ },
68
+ // Rows arrive already sorted, filtered and paginated by the provider.
69
+ manualSorting: true,
70
+ manualFiltering: true,
71
+ getRowId: (row, index) => keyForRow(row, columns, index),
72
+ });
73
+ const leafColumns = table.getAllLeafColumns();
74
+ // Asked of the columns, not of the rendered element: `<PanelColumnMenu/>` is an
75
+ // element whether or not it will return null, so testing the element for null
76
+ // would silently add a trailing column to every table.
77
+ const hasMenu = leafColumns.some((column) => column.getCanHide());
78
+ // The menu occupies its own trailing header cell when present, so the
79
+ // empty-state row has to span it too or the "No data" text sits short.
80
+ const bodyColumnCount = table.getVisibleLeafColumns().length + (hasMenu ? 1 : 0);
81
+ return (_jsxs(Table, { containerClassName: scrolls ? SCROLL_BODY_CLASS : '', children: [_jsx(PanelTableHeader, { table: table, sticky: scrolls === true, ...(hasMenu ? { menu: _jsx(PanelColumnMenu, { columns: leafColumns }) } : {}) }), _jsxs(TableBody, { children: [rows.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: bodyColumnCount, className: "py-6 text-center text-muted-foreground", children: "No data in this window." }) })) : null, table.getRowModel().rows.map((row) => (_jsxs(TableRow, { children: [row.getVisibleCells().map((cell) => (
82
+ // Cells stay on one line: wrapping is what turned durable's "Workers"
83
+ // panel into three-line rows. Anything wider than the card is reachable
84
+ // by scrolling the table now, which is the cheaper trade of the two.
85
+ _jsx(TableCell, { className: "whitespace-nowrap text-foreground", children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))), hasMenu ? _jsx(TableCell, {}) : null] }, row.id)))] })] }));
86
+ }
87
+ //# sourceMappingURL=panel-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel-table.js","sourceRoot":"","sources":["../../../../src/react/components/extensions/panel-table.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAY,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAwB,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAKL,UAAU,EACV,kBAAkB,EAClB,QAAQ,GACT,MAAM,qBAAqB,CAAC;AAG7B;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAE1C,SAAS,SAAS,CAAC,GAAa,EAAE,OAA0B,EAAE,KAAa;IACzE,+EAA+E;IAC/E,8EAA8E;IAC9E,yBAAyB;IACzB,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC;AACxF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,eAAe,EACf,OAAO,GAWR;IACC,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAwB,EAAE,CAAC,CAAC;IACpF,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAExE,+EAA+E;IAC/E,+EAA+E;IAC/E,8EAA8E;IAC9E,wCAAwC;IACxC,MAAM,OAAO,GAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxF,MAAM,aAAa,GAAuB,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,EAAE;QACF,KAAK;KACN,CAAC,CAAC,CAAC;IAEJ,MAAM,KAAK,GAAG,QAAQ,CAAC;QACrB,QAAQ,EAAE,kBAAkB;QAC5B,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE;QACnD,wBAAwB,EAAE,mBAAmB;QAC7C,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE;YAC3B,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACxE,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,0EAA0E;YAC1E,2EAA2E;YAC3E,sEAAsE;YACtE,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC1F,CAAC;QACD,qBAAqB,EAAE,CAAC,OAAO,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9E,MAAM,OAAO,GAAqB,EAAE,CAAC;YACrC,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;gBAC1B,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE;oBACzD,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;YACtC,CAAC;YACD,eAAe,EAAE,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QACD,sEAAsE;QACtE,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,IAAI;QACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC;KACzD,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC9C,gFAAgF;IAChF,8EAA8E;IAC9E,uDAAuD;IACvD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAClE,sEAAsE;IACtE,uEAAuE;IACvE,MAAM,eAAe,GAAG,KAAK,CAAC,qBAAqB,EAAE,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjF,OAAO,CACL,MAAC,KAAK,IAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,aACzD,KAAC,gBAAgB,IACf,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,OAAO,KAAK,IAAI,KACpB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAC,eAAe,IAAC,OAAO,EAAE,WAAW,GAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GACxE,EACF,MAAC,SAAS,eACP,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACnB,KAAC,QAAQ,cACP,KAAC,SAAS,IAAC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAC,wCAAwC,wCAE3E,GACH,CACZ,CAAC,CAAC,CAAC,IAAI,EACP,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACrC,MAAC,QAAQ,eACN,GAAG,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;4BACnC,sEAAsE;4BACtE,wEAAwE;4BACxE,qEAAqE;4BACrE,KAAC,SAAS,IAAe,SAAS,EAAC,mCAAmC,YACnE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,IAD5C,IAAI,CAAC,EAAE,CAEX,CACb,CAAC,EACD,OAAO,CAAC,CAAC,CAAC,KAAC,SAAS,KAAG,CAAC,CAAC,CAAC,IAAI,KATlB,GAAG,CAAC,EAAE,CAUV,CACZ,CAAC,IACQ,IACN,CACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { JSX } from 'react';
2
+ export interface PanelThresholds {
3
+ warn: number;
4
+ bad: number;
5
+ direction: 'up-bad' | 'down-bad';
6
+ }
7
+ export declare function StatCard({ label, value, delta, deltaLabel, spark, thresholds, currentValue, accent, hint, }: {
8
+ label: string;
9
+ value: string | number;
10
+ delta?: number;
11
+ deltaLabel?: string;
12
+ spark?: number[];
13
+ thresholds?: PanelThresholds;
14
+ currentValue?: number;
15
+ accent?: string;
16
+ hint?: string;
17
+ }): JSX.Element;
18
+ //# sourceMappingURL=stat-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../../../src/react/components/extensions/stat-card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAEjC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,QAAQ,GAAG,UAAU,CAAC;CAClC;AAoCD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,UAAU,EACV,YAAY,EACZ,MAA0B,EAC1B,IAAI,GACL,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,GAAG,CAAC,OAAO,CA8Bd"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ function health(value, t) {
3
+ if (value === undefined || !t)
4
+ return null;
5
+ const worseUp = t.direction === 'up-bad';
6
+ if (worseUp ? value >= t.bad : value <= t.bad)
7
+ return 'bad';
8
+ if (worseUp ? value >= t.warn : value <= t.warn)
9
+ return 'warn';
10
+ return 'ok';
11
+ }
12
+ const ACCENT = {
13
+ ok: 'var(--good)',
14
+ warn: 'var(--warn)',
15
+ bad: 'var(--bad)',
16
+ };
17
+ function Sparkline({ points }) {
18
+ if (points.length < 2)
19
+ return null;
20
+ const max = Math.max(...points);
21
+ const min = Math.min(...points);
22
+ const span = max - min || 1;
23
+ const d = points
24
+ .map((p, i) => {
25
+ const x = (i / (points.length - 1)) * 100;
26
+ const y = 20 - ((p - min) / span) * 18 - 1;
27
+ return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`;
28
+ })
29
+ .join(' ');
30
+ return (_jsxs("svg", { className: "mt-2", width: "100%", height: "22", viewBox: "0 0 100 22", preserveAspectRatio: "none", children: [_jsx("title", { children: "Trend sparkline" }), _jsx("path", { d: d, fill: "none", stroke: "var(--muted)", strokeWidth: "1.5" })] }));
31
+ }
32
+ export function StatCard({ label, value, delta, deltaLabel, spark, thresholds, currentValue, accent = 'text-foreground', hint, }) {
33
+ const h = health(currentValue, thresholds);
34
+ // A positive delta is "good" unless higher is bad.
35
+ const deltaGood = delta === undefined || !thresholds
36
+ ? null
37
+ : thresholds.direction === 'up-bad'
38
+ ? delta < 0
39
+ : delta > 0;
40
+ const deltaColor = deltaGood === null ? '#a1a1aa' : deltaGood ? 'var(--good)' : 'var(--bad)';
41
+ const arrow = delta === undefined ? '' : delta > 0 ? '▲' : delta < 0 ? '▼' : '◆';
42
+ return (_jsxs("div", { "data-health": h ?? undefined, className: "rounded-lg border border-line bg-panel/40 px-4 py-3 overflow-hidden", children: [h && _jsx("div", { style: { height: 3, background: ACCENT[h], margin: '-12px -16px 9px' } }), _jsx("p", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: label }), _jsxs("p", { className: `mt-1 text-2xl font-semibold tabular-nums ${accent} flex items-baseline gap-2`, children: [value, delta !== undefined && (_jsxs("span", { className: "text-xs font-semibold", style: { color: deltaColor }, children: [arrow, " ", deltaLabel ?? Math.abs(delta)] }))] }), spark && _jsx(Sparkline, { points: spark }), hint && _jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: hint })] }));
43
+ }
44
+ //# sourceMappingURL=stat-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stat-card.js","sourceRoot":"","sources":["../../../../src/react/components/extensions/stat-card.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { Panel } from '../../../client/types.js';
2
+ import type { PanelColumnDef, PanelRow } from './table-features.js';
3
+ /** The `table` panel variant's column shape, pulled from the discriminated union
4
+ * rather than re-declared, so it can never drift from `Panel`. */
5
+ export type TableColumnSpec = Extract<Panel, {
6
+ kind: 'table';
7
+ }>['columns'][number];
8
+ /**
9
+ * Fills a `{key}` link template from a row.
10
+ *
11
+ * The template is author-controlled but the substituted values come from
12
+ * provider row data — never let a row turn the link into a `javascript:`/`data:`
13
+ * URL.
14
+ */
15
+ export declare function fillTemplate(href: string, row: PanelRow): string;
16
+ /**
17
+ * Translates a panel's declared columns into TanStack column definitions.
18
+ *
19
+ * `accessorFn` rather than `accessorKey`: a key is parsed as a *deep* path, so a
20
+ * provider returning a flat row keyed `'queue.name'` would resolve to
21
+ * `row.queue.name` and render blank. It also has to be present at all — both
22
+ * `getCanSort()` and `getCanFilter()` key off the accessor, not off a
23
+ * registered row model, which is what lets the header show sort and filter
24
+ * affordances for state that is resolved on the server.
25
+ *
26
+ * Each `enable*` is set explicitly from the spec because TanStack defaults all
27
+ * three to `true`: left off, every column of every existing dashboard would
28
+ * suddenly be sortable, filterable and hideable, promising behaviour no
29
+ * provider implements.
30
+ */
31
+ export declare function buildPanelColumns(specs: TableColumnSpec[]): PanelColumnDef[];
32
+ //# sourceMappingURL=table-columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-columns.d.ts","sourceRoot":"","sources":["../../../../src/react/components/extensions/table-columns.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE;mEACmE;AACnE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,MAAM,CAIhE;AAoBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,EAAE,CAY5E"}
@@ -0,0 +1,51 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Fills a `{key}` link template from a row.
4
+ *
5
+ * The template is author-controlled but the substituted values come from
6
+ * provider row data — never let a row turn the link into a `javascript:`/`data:`
7
+ * URL.
8
+ */
9
+ export function fillTemplate(href, row) {
10
+ const filled = href.replace(/\{(\w+)\}/g, (_m, k) => String(row[k] ?? ''));
11
+ if (/^\s*(javascript|data|vbscript):/i.test(filled))
12
+ return '#';
13
+ return filled;
14
+ }
15
+ /** A cell's rendered content: the row's value for this column, as a deep link
16
+ * when the column declares one. A fragment, not an element, so the markup
17
+ * inside the `<td>` is exactly what it was before the table grew a table
18
+ * instance around it. */
19
+ function CellContent({ spec, row }) {
20
+ const text = String(row[spec.key] ?? '');
21
+ if (!spec.link)
22
+ return _jsx(_Fragment, { children: text });
23
+ return (_jsx("a", { className: "text-sky-400 hover:underline", href: fillTemplate(spec.link.href, row), ...(spec.link.external ? { target: '_blank', rel: 'noreferrer' } : {}), children: text }));
24
+ }
25
+ /**
26
+ * Translates a panel's declared columns into TanStack column definitions.
27
+ *
28
+ * `accessorFn` rather than `accessorKey`: a key is parsed as a *deep* path, so a
29
+ * provider returning a flat row keyed `'queue.name'` would resolve to
30
+ * `row.queue.name` and render blank. It also has to be present at all — both
31
+ * `getCanSort()` and `getCanFilter()` key off the accessor, not off a
32
+ * registered row model, which is what lets the header show sort and filter
33
+ * affordances for state that is resolved on the server.
34
+ *
35
+ * Each `enable*` is set explicitly from the spec because TanStack defaults all
36
+ * three to `true`: left off, every column of every existing dashboard would
37
+ * suddenly be sortable, filterable and hideable, promising behaviour no
38
+ * provider implements.
39
+ */
40
+ export function buildPanelColumns(specs) {
41
+ return specs.map((spec) => ({
42
+ id: spec.key,
43
+ accessorFn: (row) => row[spec.key],
44
+ header: spec.label,
45
+ enableSorting: spec.sortable === true,
46
+ enableColumnFilter: spec.filterable === true,
47
+ enableHiding: spec.hideable === true,
48
+ cell: ({ row }) => (_jsx(CellContent, { spec: spec, row: row.original })),
49
+ }));
50
+ }
51
+ //# sourceMappingURL=table-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-columns.js","sourceRoot":"","sources":["../../../../src/react/components/extensions/table-columns.tsx"],"names":[],"mappings":";AAQA;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,GAAa;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACnF,IAAI,kCAAkC,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,GAAG,CAAC;IAChE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;0BAG0B;AAC1B,SAAS,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAA4C;IAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI,CAAC,IAAI;QAAE,OAAO,4BAAG,IAAI,GAAI,CAAC;IACnC,OAAO,CACL,YACE,SAAS,EAAC,8BAA8B,EACxC,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,KACnC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAEtE,IAAI,GACH,CACL,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAwB;IACxD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,EAAE,EAAE,IAAI,CAAC,GAAG;QACZ,UAAU,EAAE,CAAC,GAAa,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5C,MAAM,EAAE,IAAI,CAAC,KAAK;QAClB,aAAa,EAAE,IAAI,CAAC,QAAQ,KAAK,IAAI;QACrC,kBAAkB,EAAE,IAAI,CAAC,UAAU,KAAK,IAAI;QAC5C,YAAY,EAAE,IAAI,CAAC,QAAQ,KAAK,IAAI;QACpC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAmC,EAAE,EAAE,CAAC,CAClD,KAAC,WAAW,IAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,GAAI,CAC/C;KACF,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { type Cell, type Column, type ColumnDef, type ColumnFiltersState, type ColumnVisibilityState, type Header, type Row, type SortingState, type Table, flexRender, useTable } from '@tanstack/react-table';
2
+ /**
3
+ * This package's entire binding to `@tanstack/react-table`.
4
+ *
5
+ * The rule the extension dashboard holds to: **no other module imports the
6
+ * library**. Everything a panel table needs — the hook, `flexRender`, and the
7
+ * pre-bound types — is re-exported from here, so the next major version is a
8
+ * change in one file rather than a sweep over every header, body and menu
9
+ * module. That matters more in v9 than it did in v8, because v9 makes almost
10
+ * every public type generic in the registered feature set: a bare
11
+ * `Column<TData>` no longer type-checks anywhere.
12
+ *
13
+ * v9 installs a feature's state slice and instance methods **only when that
14
+ * feature is registered below**. An unregistered feature does not fail loudly —
15
+ * the method is simply absent, which at the call site is indistinguishable from
16
+ * "this API was removed in v9". `table-features.spec.ts` asserts every method
17
+ * the dashboard actually calls, so deleting an entry here fails a test instead
18
+ * of blanking a panel.
19
+ */
20
+ export declare const panelTableFeatures: {
21
+ rowSortingFeature: import("@tanstack/react-table").TableFeature;
22
+ columnFilteringFeature: import("@tanstack/react-table").TableFeature;
23
+ columnVisibilityFeature: import("@tanstack/react-table").TableFeature;
24
+ };
25
+ /**
26
+ * No client row models are registered above, and that is the point:
27
+ * `createSortedRowModel` / `createFilteredRowModel` / `createPaginatedRowModel`
28
+ * would re-do server-side work over the single page already in hand — sorting
29
+ * 50 rows out of 50,000 and presenting the result as "the top of the list".
30
+ */
31
+ export type PanelTableFeatures = typeof panelTableFeatures;
32
+ /**
33
+ * A dashboard table row. Provider rows are opaque JSON keyed by the panel's
34
+ * declared column keys, so unlike the app-level tables this one is not generic
35
+ * — which is why every alias below can be bound flat instead of staying generic
36
+ * in `TData`.
37
+ */
38
+ export type PanelRow = Record<string, unknown>;
39
+ export type PanelTableInstance = Table<PanelTableFeatures, PanelRow>;
40
+ export type PanelColumn = Column<PanelTableFeatures, PanelRow, unknown>;
41
+ export type PanelColumnDef = ColumnDef<PanelTableFeatures, PanelRow, unknown>;
42
+ export type PanelHeader = Header<PanelTableFeatures, PanelRow, unknown>;
43
+ export type PanelCell = Cell<PanelTableFeatures, PanelRow, unknown>;
44
+ export type PanelTableRow = Row<PanelTableFeatures, PanelRow>;
45
+ export { flexRender, useTable };
46
+ export type { ColumnFiltersState, ColumnVisibilityState, SortingState };
47
+ //# sourceMappingURL=table-features.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-features.d.ts","sourceRoot":"","sources":["../../../../src/react/components/extensions/table-features.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,IAAI,EACT,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,MAAM,EACX,KAAK,GAAG,EACR,KAAK,YAAY,EACjB,KAAK,KAAK,EAGV,UAAU,EAGV,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB;;;;CAuB7B,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,kBAAkB,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AACrE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC9E,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACxE,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACpE,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAE9D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAChC,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { columnFilteringFeature, columnVisibilityFeature, flexRender, rowSortingFeature, tableFeatures, useTable, } from '@tanstack/react-table';
2
+ /**
3
+ * This package's entire binding to `@tanstack/react-table`.
4
+ *
5
+ * The rule the extension dashboard holds to: **no other module imports the
6
+ * library**. Everything a panel table needs — the hook, `flexRender`, and the
7
+ * pre-bound types — is re-exported from here, so the next major version is a
8
+ * change in one file rather than a sweep over every header, body and menu
9
+ * module. That matters more in v9 than it did in v8, because v9 makes almost
10
+ * every public type generic in the registered feature set: a bare
11
+ * `Column<TData>` no longer type-checks anywhere.
12
+ *
13
+ * v9 installs a feature's state slice and instance methods **only when that
14
+ * feature is registered below**. An unregistered feature does not fail loudly —
15
+ * the method is simply absent, which at the call site is indistinguishable from
16
+ * "this API was removed in v9". `table-features.spec.ts` asserts every method
17
+ * the dashboard actually calls, so deleting an entry here fails a test instead
18
+ * of blanking a panel.
19
+ */
20
+ export const panelTableFeatures = tableFeatures({
21
+ // column.getCanSort / getIsSorted / getToggleSortingHandler, state.sorting.
22
+ // Sorting itself is the provider's: `manualSorting` is on and the resolved
23
+ // state is handed back up to the dashboard page, which re-resolves the
24
+ // provider with it.
25
+ rowSortingFeature,
26
+ // column.getCanFilter / getFilterValue / setFilterValue, state.columnFilters.
27
+ // Server-side for the same reason.
28
+ columnFilteringFeature,
29
+ // column.getCanHide / getIsVisible / toggleVisibility, row.getVisibleCells.
30
+ // The one genuinely client-side feature here — hiding a column is a display
31
+ // choice, so it never reaches the provider.
32
+ columnVisibilityFeature,
33
+ // Deliberately NO `rowPaginationFeature`. A paged panel's pager is driven
34
+ // entirely by what the provider returned (`{ total, page, limit }`); nothing
35
+ // calls a TanStack pagination API, so registering the feature would add a
36
+ // `pagination` state slice that has to be kept in sync with the real one and
37
+ // could disagree with it.
38
+ //
39
+ // Deliberately NO `columnSizingFeature` either: it contributes `size: 150`
40
+ // through `getDefaultColumnDef()` to every column, which would pin every
41
+ // dashboard table to fixed 150px columns and leave no way to express "size
42
+ // this from the content" — the layout these tables actually want.
43
+ });
44
+ export { flexRender, useTable };
45
+ //# sourceMappingURL=table-features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-features.js","sourceRoot":"","sources":["../../../../src/react/components/extensions/table-features.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,sBAAsB,EACtB,uBAAuB,EACvB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,QAAQ,GACT,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;IAC9C,4EAA4E;IAC5E,2EAA2E;IAC3E,uEAAuE;IACvE,oBAAoB;IACpB,iBAAiB;IACjB,8EAA8E;IAC9E,mCAAmC;IACnC,sBAAsB;IACtB,4EAA4E;IAC5E,4EAA4E;IAC5E,4CAA4C;IAC5C,uBAAuB;IACvB,0EAA0E;IAC1E,6EAA6E;IAC7E,0EAA0E;IAC1E,6EAA6E;IAC7E,0BAA0B;IAC1B,EAAE;IACF,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,kEAAkE;CACnE,CAAC,CAAC;AAyBH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The client half of the table SPI: turning a panel's live table state into the
3
+ * query its provider is re-resolved with.
4
+ *
5
+ * The param names below are the wire contract, and the server half of it lives
6
+ * in `@dudousxd/nestjs-telescope`'s `readTableQuery`. They are duplicated rather
7
+ * than imported: `src/client` and `src/react` deliberately carry no dependency
8
+ * on the core package (see the `Panel` doc in `../../../client/types.ts`), and a
9
+ * type-only mirror is what keeps a host from installing a NestJS server package
10
+ * to render a table. Change one, change the other.
11
+ */
12
+ /** Query-param key naming the column the table is sorted by. */
13
+ export declare const TABLE_SORT_PARAM = "sort";
14
+ /** Query-param key carrying the sort direction. */
15
+ export declare const TABLE_SORT_DIR_PARAM = "dir";
16
+ /** Prefix that namespaces a per-column filter — `filter.status=failed`. */
17
+ export declare const TABLE_FILTER_PREFIX = "filter.";
18
+ /** Which column a table is sorted by, and which way. `undefined` means unsorted. */
19
+ export interface TableSortState {
20
+ /** The panel `Column.key` of the sorted column. */
21
+ key: string;
22
+ dir: 'asc' | 'desc';
23
+ }
24
+ /** Committed per-column filter text, keyed by panel `Column.key`. */
25
+ export type TableFilterState = Record<string, string>;
26
+ /** Everything a table panel can add to its provider query beyond `data.query`. */
27
+ export interface TableQueryState {
28
+ /** Present only for a `paged: true` panel. */
29
+ paging?: {
30
+ page: number;
31
+ limit: number;
32
+ };
33
+ sort?: TableSortState;
34
+ filters?: TableFilterState;
35
+ }
36
+ /**
37
+ * Merges a table's live state onto the panel's own static `data.query`.
38
+ *
39
+ * Returns `base` **by identity** when there is nothing to merge. That is not a
40
+ * micro-optimisation: the resolved query is part of the React Query cache key,
41
+ * so a table that has never been sorted or filtered has to produce the exact
42
+ * query it produced before this feature existed — otherwise every existing
43
+ * dashboard would quietly start on a fresh cache entry, and a plain table's
44
+ * request would no longer be deduplicated against an identical panel elsewhere
45
+ * on the page.
46
+ *
47
+ * Filters are namespaced with `filter.` for the same reason the server reads
48
+ * them that way: a panel scoped to `{ status: 'running' }` with a filterable
49
+ * `status` column is an ordinary combination, and unprefixed one would silently
50
+ * overwrite the other.
51
+ */
52
+ export declare function buildTableQuery(base: Record<string, unknown> | undefined, state: TableQueryState): Record<string, unknown> | undefined;
53
+ //# sourceMappingURL=table-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-query.d.ts","sourceRoot":"","sources":["../../../../src/react/components/extensions/table-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,SAAS,CAAC;AAEvC,mDAAmD;AACnD,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAE1C,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,KAAK,GAAG,MAAM,CAAC;CACrB;AAED,qEAAqE;AACrE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEtD,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EACzC,KAAK,EAAE,eAAe,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAerC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The client half of the table SPI: turning a panel's live table state into the
3
+ * query its provider is re-resolved with.
4
+ *
5
+ * The param names below are the wire contract, and the server half of it lives
6
+ * in `@dudousxd/nestjs-telescope`'s `readTableQuery`. They are duplicated rather
7
+ * than imported: `src/client` and `src/react` deliberately carry no dependency
8
+ * on the core package (see the `Panel` doc in `../../../client/types.ts`), and a
9
+ * type-only mirror is what keeps a host from installing a NestJS server package
10
+ * to render a table. Change one, change the other.
11
+ */
12
+ /** Query-param key naming the column the table is sorted by. */
13
+ export const TABLE_SORT_PARAM = 'sort';
14
+ /** Query-param key carrying the sort direction. */
15
+ export const TABLE_SORT_DIR_PARAM = 'dir';
16
+ /** Prefix that namespaces a per-column filter — `filter.status=failed`. */
17
+ export const TABLE_FILTER_PREFIX = 'filter.';
18
+ /**
19
+ * Merges a table's live state onto the panel's own static `data.query`.
20
+ *
21
+ * Returns `base` **by identity** when there is nothing to merge. That is not a
22
+ * micro-optimisation: the resolved query is part of the React Query cache key,
23
+ * so a table that has never been sorted or filtered has to produce the exact
24
+ * query it produced before this feature existed — otherwise every existing
25
+ * dashboard would quietly start on a fresh cache entry, and a plain table's
26
+ * request would no longer be deduplicated against an identical panel elsewhere
27
+ * on the page.
28
+ *
29
+ * Filters are namespaced with `filter.` for the same reason the server reads
30
+ * them that way: a panel scoped to `{ status: 'running' }` with a filterable
31
+ * `status` column is an ordinary combination, and unprefixed one would silently
32
+ * overwrite the other.
33
+ */
34
+ export function buildTableQuery(base, state) {
35
+ const filters = Object.entries(state.filters ?? {}).filter(([, value]) => value !== '');
36
+ if (!state.paging && !state.sort && filters.length === 0)
37
+ return base;
38
+ const query = { ...base };
39
+ if (state.paging) {
40
+ query.page = state.paging.page;
41
+ query.limit = state.paging.limit;
42
+ }
43
+ if (state.sort) {
44
+ query[TABLE_SORT_PARAM] = state.sort.key;
45
+ query[TABLE_SORT_DIR_PARAM] = state.sort.dir;
46
+ }
47
+ for (const [key, value] of filters)
48
+ query[`${TABLE_FILTER_PREFIX}${key}`] = value;
49
+ return query;
50
+ }
51
+ //# sourceMappingURL=table-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-query.js","sourceRoot":"","sources":["../../../../src/react/components/extensions/table-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gEAAgE;AAChE,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEvC,mDAAmD;AACnD,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAE1C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAoB7C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAyC,EACzC,KAAsB;IAEtB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACxF,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtE,MAAM,KAAK,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;IACnD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;QAC/B,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;IACnC,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QACzC,KAAK,CAAC,oBAAoB,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO;QAAE,KAAK,CAAC,GAAG,mBAAmB,GAAG,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC;IAClF,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type JSX } from 'react';
2
+ import type { FlameNode } from '../../client/index.js';
3
+ /**
4
+ * A dependency-light flamegraph (icicle layout, root at top). Renders one
5
+ * absolutely-positioned `<div>` per visible frame — no canvas, no charting
6
+ * library — sized as a percentage of the container width so it's responsive and
7
+ * SSR-safe. Clicking a frame "zooms" to make it the new 100%-width root; click
8
+ * the root or the reset control to zoom back out.
9
+ *
10
+ * Why div-based: the frame trees are pre-aggregated and pruned server-side (and
11
+ * again here via `minWidthFraction`), so the visible cell count is small enough
12
+ * that the DOM is the simplest robust renderer. This keeps the UI bundle free of
13
+ * a heavy flamegraph dependency, as the task requires.
14
+ */
15
+ export declare function Flamegraph({ tree }: {
16
+ tree: FlameNode;
17
+ }): JSX.Element;
18
+ //# sourceMappingURL=flamegraph-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flamegraph-view.d.ts","sourceRoot":"","sources":["../../../src/react/components/flamegraph-view.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAqB,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAKvD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CA4ErE"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // packages/ui/src/react/components/flamegraph-view.tsx
3
+ import { useMemo, useState } from 'react';
4
+ import { flameColor, flameDepth, flattenFlame, formatProfileMs } from './flamegraph.js';
5
+ const ROW_HEIGHT = 18;
6
+ /**
7
+ * A dependency-light flamegraph (icicle layout, root at top). Renders one
8
+ * absolutely-positioned `<div>` per visible frame — no canvas, no charting
9
+ * library — sized as a percentage of the container width so it's responsive and
10
+ * SSR-safe. Clicking a frame "zooms" to make it the new 100%-width root; click
11
+ * the root or the reset control to zoom back out.
12
+ *
13
+ * Why div-based: the frame trees are pre-aggregated and pruned server-side (and
14
+ * again here via `minWidthFraction`), so the visible cell count is small enough
15
+ * that the DOM is the simplest robust renderer. This keeps the UI bundle free of
16
+ * a heavy flamegraph dependency, as the task requires.
17
+ */
18
+ export function Flamegraph({ tree }) {
19
+ // Zoom focus: the path key of the frame currently treated as the root.
20
+ const [focusKey, setFocusKey] = useState('0');
21
+ const cells = useMemo(() => flattenFlame(tree), [tree]);
22
+ const depth = flameDepth(cells);
23
+ // Resolve the focused subtree's x/width so we can rescale all cells to it.
24
+ const focus = cells.find((c) => c.key === focusKey) ?? cells[0];
25
+ const focusX = focus?.x ?? 0;
26
+ const focusWidth = focus?.width ?? 1;
27
+ const focusDepth = focus?.depth ?? 0;
28
+ if (tree.totalMs <= 0 || cells.length === 0) {
29
+ return (_jsx("div", { className: "flex h-24 items-center justify-center rounded-lg border border-line bg-panel/40 text-xs text-muted-foreground", children: "No samples \u2014 the capture was empty." }));
30
+ }
31
+ // Only render cells inside the focused subtree (its descendants), rescaled.
32
+ const visible = cells.filter((c) => c.key === focusKey || c.key.startsWith(`${focusKey}.`));
33
+ const rows = depth - focusDepth;
34
+ return (_jsxs("div", { children: [focusKey !== '0' && (_jsx("button", { type: "button", onClick: () => setFocusKey('0'), className: "mb-2 rounded border border-line px-2 py-0.5 text-[10px] text-foreground hover:bg-panel-2", children: "\u2190 Reset zoom" })), _jsx("div", { "data-testid": "flamegraph", className: "relative w-full overflow-hidden rounded-lg border border-line bg-background", style: { height: rows * ROW_HEIGHT + 2 }, children: visible.map((cell) => {
35
+ const left = ((cell.x - focusX) / focusWidth) * 100;
36
+ const width = (cell.width / focusWidth) * 100;
37
+ const top = (cell.depth - focusDepth) * ROW_HEIGHT;
38
+ const pct = ((cell.node.totalMs / tree.totalMs) * 100).toFixed(1);
39
+ return (_jsx("button", { type: "button", onClick: () => setFocusKey(cell.key), title: `${cell.node.name}${cell.node.file ? ` — ${cell.node.file}` : ''}\ntotal ${formatProfileMs(cell.node.totalMs)} (${pct}%) · self ${formatProfileMs(cell.node.selfMs)}`, className: "absolute flex items-center overflow-hidden border border-line-soft/60 px-1 text-left text-[10px] leading-none text-black/80 hover:brightness-110", style: {
40
+ left: `${left}%`,
41
+ width: `${width}%`,
42
+ top,
43
+ height: ROW_HEIGHT - 1,
44
+ background: cell.node.name === '(root)' ? 'var(--line)' : flameColor(cell.node.name),
45
+ }, children: _jsx("span", { className: "truncate", style: cell.node.name === '(root)' ? { color: 'var(--text)' } : {}, children: cell.node.name }) }, cell.key));
46
+ }) })] }));
47
+ }
48
+ //# sourceMappingURL=flamegraph-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flamegraph-view.js","sourceRoot":"","sources":["../../../src/react/components/flamegraph-view.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { FlameNode } from '../../client/index.js';
2
+ /**
3
+ * A flame cell positioned for rendering: a horizontal bar at depth `depth`,
4
+ * starting at `x` (fraction 0–1 of the root width) and spanning `width`
5
+ * (fraction). Produced by {@link flattenFlame}, which is a PURE function so the
6
+ * layout is unit-testable without a DOM.
7
+ */
8
+ export interface FlameCell {
9
+ /** Stable key path (indices from the root), e.g. "0.1.0". */
10
+ key: string;
11
+ node: FlameNode;
12
+ depth: number;
13
+ /** Left edge as a fraction (0–1) of the root's total width. */
14
+ x: number;
15
+ /** Width as a fraction (0–1) of the root's total width. */
16
+ width: number;
17
+ }
18
+ /**
19
+ * Flattens a flame tree into positioned cells using the icicle layout (root at
20
+ * top, children below, each child's width proportional to its `totalMs` share of
21
+ * the parent). Children are laid out left-to-right widest-first so the hot path
22
+ * reads down the left edge — the convention Chrome DevTools / speedscope use.
23
+ *
24
+ * `minWidthFraction` prunes cells narrower than the given fraction of the root,
25
+ * so a 50k-node profile doesn't emit tens of thousands of sub-pixel divs.
26
+ */
27
+ export declare function flattenFlame(root: FlameNode, minWidthFraction?: number): FlameCell[];
28
+ /** Max depth (number of rows) in a flattened cell list. */
29
+ export declare function flameDepth(cells: FlameCell[]): number;
30
+ /** A stable color for a frame, hashed from its name so siblings differ but a
31
+ * frame keeps its hue across renders. Warm palette (flamegraph convention). */
32
+ export declare function flameColor(name: string): string;
33
+ export declare function formatProfileMs(ms: number): string;
34
+ //# sourceMappingURL=flamegraph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flamegraph.d.ts","sourceRoot":"","sources":["../../../src/react/components/flamegraph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,6DAA6D;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,SAAQ,GAAG,SAAS,EAAE,CAoBnF;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAErD;AAED;gFACgF;AAChF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ/C;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAIlD"}