@dudousxd/nestjs-telescope-ui 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +748 -0
- package/README.md +31 -0
- package/dist/client/console-url.d.ts +11 -0
- package/dist/client/console-url.d.ts.map +1 -0
- package/dist/client/console-url.js +21 -0
- package/dist/client/console-url.js.map +1 -0
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/telescope-client.d.ts +64 -11
- package/dist/client/telescope-client.d.ts.map +1 -1
- package/dist/client/telescope-client.js +59 -54
- package/dist/client/telescope-client.js.map +1 -1
- package/dist/client/types.d.ts +431 -30
- package/dist/client/types.d.ts.map +1 -1
- package/dist/react/components/batch-timeline.js +2 -2
- package/dist/react/components/batch-timeline.js.map +1 -1
- package/dist/react/components/cache-badge.d.ts +14 -6
- package/dist/react/components/cache-badge.d.ts.map +1 -1
- package/dist/react/components/cache-badge.js +26 -12
- package/dist/react/components/cache-badge.js.map +1 -1
- package/dist/react/components/charts/area-chart-card.d.ts +11 -3
- package/dist/react/components/charts/area-chart-card.d.ts.map +1 -1
- package/dist/react/components/charts/area-chart-card.js +33 -6
- package/dist/react/components/charts/area-chart-card.js.map +1 -1
- package/dist/react/components/charts/bar-chart-card.d.ts +18 -7
- package/dist/react/components/charts/bar-chart-card.d.ts.map +1 -1
- package/dist/react/components/charts/bar-chart-card.js +18 -9
- package/dist/react/components/charts/bar-chart-card.js.map +1 -1
- package/dist/react/components/charts/breakdown-card.d.ts +17 -0
- package/dist/react/components/charts/breakdown-card.d.ts.map +1 -0
- package/dist/react/components/charts/breakdown-card.js +43 -0
- package/dist/react/components/charts/breakdown-card.js.map +1 -0
- package/dist/react/components/charts/chart-card.d.ts +7 -0
- package/dist/react/components/charts/chart-card.d.ts.map +1 -1
- package/dist/react/components/charts/chart-card.js +10 -3
- package/dist/react/components/charts/chart-card.js.map +1 -1
- package/dist/react/components/charts/chart-selection.d.ts +44 -0
- package/dist/react/components/charts/chart-selection.d.ts.map +1 -0
- package/dist/react/components/charts/chart-selection.js +36 -0
- package/dist/react/components/charts/chart-selection.js.map +1 -0
- package/dist/react/components/charts/chart-theme.d.ts +62 -19
- package/dist/react/components/charts/chart-theme.d.ts.map +1 -1
- package/dist/react/components/charts/chart-theme.js +72 -13
- package/dist/react/components/charts/chart-theme.js.map +1 -1
- package/dist/react/components/charts/distribution-chart-card.d.ts +17 -0
- package/dist/react/components/charts/distribution-chart-card.d.ts.map +1 -0
- package/dist/react/components/charts/distribution-chart-card.js +46 -0
- package/dist/react/components/charts/distribution-chart-card.js.map +1 -0
- package/dist/react/components/charts/gauge-card.d.ts +12 -0
- package/dist/react/components/charts/gauge-card.d.ts.map +1 -0
- package/dist/react/components/charts/gauge-card.js +17 -0
- package/dist/react/components/charts/gauge-card.js.map +1 -0
- package/dist/react/components/charts/index.d.ts +1 -0
- package/dist/react/components/charts/index.d.ts.map +1 -1
- package/dist/react/components/charts/index.js +1 -0
- package/dist/react/components/charts/index.js.map +1 -1
- package/dist/react/components/charts/stacked-area-chart-card.d.ts +17 -3
- package/dist/react/components/charts/stacked-area-chart-card.d.ts.map +1 -1
- package/dist/react/components/charts/stacked-area-chart-card.js +31 -8
- package/dist/react/components/charts/stacked-area-chart-card.js.map +1 -1
- package/dist/react/components/entries-table.d.ts.map +1 -1
- package/dist/react/components/entries-table.js +17 -3
- package/dist/react/components/entries-table.js.map +1 -1
- package/dist/react/components/entry-detail.d.ts +2 -0
- package/dist/react/components/entry-detail.d.ts.map +1 -1
- package/dist/react/components/entry-detail.js +186 -20
- package/dist/react/components/entry-detail.js.map +1 -1
- package/dist/react/components/entry-insights.d.ts.map +1 -1
- package/dist/react/components/entry-insights.js +28 -17
- package/dist/react/components/entry-insights.js.map +1 -1
- package/dist/react/components/entry-types.d.ts +29 -0
- package/dist/react/components/entry-types.d.ts.map +1 -1
- package/dist/react/components/entry-types.js +60 -0
- package/dist/react/components/entry-types.js.map +1 -1
- package/dist/react/components/export-json-toolbar.js +2 -2
- package/dist/react/components/export-json-toolbar.js.map +1 -1
- package/dist/react/components/extensions/panel-column-menu.d.ts +19 -0
- package/dist/react/components/extensions/panel-column-menu.d.ts.map +1 -0
- package/dist/react/components/extensions/panel-column-menu.js +47 -0
- package/dist/react/components/extensions/panel-column-menu.js.map +1 -0
- package/dist/react/components/extensions/panel-renderer.d.ts +46 -0
- package/dist/react/components/extensions/panel-renderer.d.ts.map +1 -0
- package/dist/react/components/extensions/panel-renderer.js +122 -0
- package/dist/react/components/extensions/panel-renderer.js.map +1 -0
- package/dist/react/components/extensions/panel-table-header.d.ts +18 -0
- package/dist/react/components/extensions/panel-table-header.d.ts.map +1 -0
- package/dist/react/components/extensions/panel-table-header.js +82 -0
- package/dist/react/components/extensions/panel-table-header.js.map +1 -0
- package/dist/react/components/extensions/panel-table.d.ts +27 -0
- package/dist/react/components/extensions/panel-table.d.ts.map +1 -0
- package/dist/react/components/extensions/panel-table.js +87 -0
- package/dist/react/components/extensions/panel-table.js.map +1 -0
- package/dist/react/components/extensions/stat-card.d.ts +18 -0
- package/dist/react/components/extensions/stat-card.d.ts.map +1 -0
- package/dist/react/components/extensions/stat-card.js +44 -0
- package/dist/react/components/extensions/stat-card.js.map +1 -0
- package/dist/react/components/extensions/table-columns.d.ts +32 -0
- package/dist/react/components/extensions/table-columns.d.ts.map +1 -0
- package/dist/react/components/extensions/table-columns.js +51 -0
- package/dist/react/components/extensions/table-columns.js.map +1 -0
- package/dist/react/components/extensions/table-features.d.ts +47 -0
- package/dist/react/components/extensions/table-features.d.ts.map +1 -0
- package/dist/react/components/extensions/table-features.js +45 -0
- package/dist/react/components/extensions/table-features.js.map +1 -0
- package/dist/react/components/extensions/table-query.d.ts +53 -0
- package/dist/react/components/extensions/table-query.d.ts.map +1 -0
- package/dist/react/components/extensions/table-query.js +51 -0
- package/dist/react/components/extensions/table-query.js.map +1 -0
- package/dist/react/components/flamegraph-view.d.ts +18 -0
- package/dist/react/components/flamegraph-view.d.ts.map +1 -0
- package/dist/react/components/flamegraph-view.js +48 -0
- package/dist/react/components/flamegraph-view.js.map +1 -0
- package/dist/react/components/flamegraph.d.ts +34 -0
- package/dist/react/components/flamegraph.d.ts.map +1 -0
- package/dist/react/components/flamegraph.js +54 -0
- package/dist/react/components/flamegraph.js.map +1 -0
- package/dist/react/components/inertia-badge.d.ts +33 -0
- package/dist/react/components/inertia-badge.d.ts.map +1 -0
- package/dist/react/components/inertia-badge.js +72 -0
- package/dist/react/components/inertia-badge.js.map +1 -0
- package/dist/react/components/inertia-body.d.ts +11 -0
- package/dist/react/components/inertia-body.d.ts.map +1 -0
- package/dist/react/components/inertia-body.js +127 -0
- package/dist/react/components/inertia-body.js.map +1 -0
- package/dist/react/components/official-event.d.ts +16 -0
- package/dist/react/components/official-event.d.ts.map +1 -0
- package/dist/react/components/official-event.js +88 -0
- package/dist/react/components/official-event.js.map +1 -0
- package/dist/react/components/pulse-panel.d.ts.map +1 -1
- package/dist/react/components/pulse-panel.js +6 -6
- package/dist/react/components/pulse-panel.js.map +1 -1
- package/dist/react/components/queues/EnqueueJobForm.d.ts.map +1 -1
- package/dist/react/components/queues/EnqueueJobForm.js +2 -1
- package/dist/react/components/queues/EnqueueJobForm.js.map +1 -1
- package/dist/react/components/queues/JobActions.d.ts.map +1 -1
- package/dist/react/components/queues/JobActions.js +11 -9
- package/dist/react/components/queues/JobActions.js.map +1 -1
- package/dist/react/components/queues/JobDetailDrawer.d.ts.map +1 -1
- package/dist/react/components/queues/JobDetailDrawer.js +8 -13
- package/dist/react/components/queues/JobDetailDrawer.js.map +1 -1
- package/dist/react/components/queues/JobTable.d.ts.map +1 -1
- package/dist/react/components/queues/JobTable.js +0 -0
- package/dist/react/components/queues/JobTable.js.map +1 -1
- package/dist/react/components/queues/QueueList.d.ts.map +1 -1
- package/dist/react/components/queues/QueueList.js +6 -6
- package/dist/react/components/queues/QueueList.js.map +1 -1
- package/dist/react/components/queues/QueueStateTabs.d.ts.map +1 -1
- package/dist/react/components/queues/QueueStateTabs.js +9 -7
- package/dist/react/components/queues/QueueStateTabs.js.map +1 -1
- package/dist/react/components/queues/queue-format.d.ts +5 -1
- package/dist/react/components/queues/queue-format.d.ts.map +1 -1
- package/dist/react/components/queues/queue-format.js +11 -7
- package/dist/react/components/queues/queue-format.js.map +1 -1
- package/dist/react/components/queues-panel.js +1 -1
- package/dist/react/components/queues-panel.js.map +1 -1
- package/dist/react/components/request-timeline.d.ts.map +1 -1
- package/dist/react/components/request-timeline.js +2 -2
- package/dist/react/components/request-timeline.js.map +1 -1
- package/dist/react/components/retention-indicator.d.ts.map +1 -1
- package/dist/react/components/retention-indicator.js +1 -1
- package/dist/react/components/retention-indicator.js.map +1 -1
- package/dist/react/components/sparkline.d.ts.map +1 -1
- package/dist/react/components/sparkline.js +1 -1
- package/dist/react/components/sparkline.js.map +1 -1
- package/dist/react/components/tag-autocomplete.d.ts +21 -4
- package/dist/react/components/tag-autocomplete.d.ts.map +1 -1
- package/dist/react/components/tag-autocomplete.js +57 -29
- package/dist/react/components/tag-autocomplete.js.map +1 -1
- package/dist/react/components/to-csv.d.ts +25 -0
- package/dist/react/components/to-csv.d.ts.map +1 -0
- package/dist/react/components/to-csv.js +78 -0
- package/dist/react/components/to-csv.js.map +1 -0
- package/dist/react/components/user-tag.d.ts +15 -0
- package/dist/react/components/user-tag.d.ts.map +1 -1
- package/dist/react/components/user-tag.js +19 -0
- package/dist/react/components/user-tag.js.map +1 -1
- package/dist/react/components/waterfall-view.d.ts +13 -0
- package/dist/react/components/waterfall-view.d.ts.map +1 -0
- package/dist/react/components/waterfall-view.js +58 -0
- package/dist/react/components/waterfall-view.js.map +1 -0
- package/dist/react/components/window-select.d.ts.map +1 -1
- package/dist/react/components/window-select.js +6 -2
- package/dist/react/components/window-select.js.map +1 -1
- package/dist/react/index.d.ts +7 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +11 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/ui/badge.d.ts +16 -0
- package/dist/react/ui/badge.d.ts.map +1 -0
- package/dist/react/ui/badge.js +28 -0
- package/dist/react/ui/badge.js.map +1 -0
- package/dist/react/ui/button.d.ts +17 -0
- package/dist/react/ui/button.d.ts.map +1 -0
- package/dist/react/ui/button.js +35 -0
- package/dist/react/ui/button.js.map +1 -0
- package/dist/react/ui/chart.d.ts +116 -0
- package/dist/react/ui/chart.d.ts.map +1 -0
- package/dist/react/ui/chart.js +191 -0
- package/dist/react/ui/chart.js.map +1 -0
- package/dist/react/ui/cn.d.ts +8 -0
- package/dist/react/ui/cn.d.ts.map +1 -0
- package/dist/react/ui/cn.js +11 -0
- package/dist/react/ui/cn.js.map +1 -0
- package/dist/react/ui/dialog.d.ts +26 -0
- package/dist/react/ui/dialog.d.ts.map +1 -0
- package/dist/react/ui/dialog.js +40 -0
- package/dist/react/ui/dialog.js.map +1 -0
- package/dist/react/ui/index.d.ts +24 -0
- package/dist/react/ui/index.d.ts.map +1 -0
- package/dist/react/ui/index.js +24 -0
- package/dist/react/ui/index.js.map +1 -0
- package/dist/react/ui/input.d.ts +3 -0
- package/dist/react/ui/input.d.ts.map +1 -0
- package/dist/react/ui/input.js +8 -0
- package/dist/react/ui/input.js.map +1 -0
- package/dist/react/ui/select.d.ts +20 -0
- package/dist/react/ui/select.d.ts.map +1 -0
- package/dist/react/ui/select.js +27 -0
- package/dist/react/ui/select.js.map +1 -0
- package/dist/react/ui/table.d.ts +27 -0
- package/dist/react/ui/table.d.ts.map +1 -0
- package/dist/react/ui/table.js +22 -0
- package/dist/react/ui/table.js.map +1 -0
- package/dist/react/ui/tabs.d.ts +12 -0
- package/dist/react/ui/tabs.d.ts.map +1 -0
- package/dist/react/ui/tabs.js +20 -0
- package/dist/react/ui/tabs.js.map +1 -0
- package/dist/react/ui/tooltip.d.ts +10 -0
- package/dist/react/ui/tooltip.d.ts.map +1 -0
- package/dist/react/ui/tooltip.js +12 -0
- package/dist/react/ui/tooltip.js.map +1 -0
- package/dist/react/use-telescope-queries.d.ts +129 -130
- package/dist/react/use-telescope-queries.d.ts.map +1 -1
- package/dist/react/use-telescope-queries.js +146 -1
- package/dist/react/use-telescope-queries.js.map +1 -1
- package/dist/react/use-telescope-stream.d.ts +10 -0
- package/dist/react/use-telescope-stream.d.ts.map +1 -0
- package/dist/react/use-telescope-stream.js +40 -0
- package/dist/react/use-telescope-stream.js.map +1 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/telescope-ui.module.d.ts +28 -1
- package/dist/server/telescope-ui.module.d.ts.map +1 -1
- package/dist/server/telescope-ui.module.js +42 -4
- package/dist/server/telescope-ui.module.js.map +1 -1
- package/dist/server/telescope-ui.options.d.ts +47 -0
- package/dist/server/telescope-ui.options.d.ts.map +1 -1
- package/dist/server/telescope-ui.options.js.map +1 -1
- package/dist/spa/assets/index-BPSlup7m.js +159 -0
- package/dist/spa/assets/index-DmsmJrlA.css +1 -0
- package/dist/spa/index.html +2 -2
- package/package.json +75 -36
- package/skills/telescope-ui-dashboard/SKILL.md +198 -0
- package/dist/react/components/type-tabs.d.ts +0 -5
- package/dist/react/components/type-tabs.d.ts.map +0 -1
- package/dist/react/components/type-tabs.js +0 -8
- package/dist/react/components/type-tabs.js.map +0 -1
- package/dist/spa/assets/index-CCzTpxj3.css +0 -1
- 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":"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"}
|
|
@@ -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"}
|