@mastra/playground-ui 53.0.0-alpha.2 → 53.0.0-alpha.3
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/dist/components/LogsDataList.cjs.js +1 -0
- package/dist/components/LogsDataList.cjs.js.map +1 -1
- package/dist/components/LogsDataList.es.js +1 -0
- package/dist/components/LogsDataList.es.js.map +1 -1
- package/dist/domains/traces/components/span-data-panel-view.cjs.js +4 -3
- package/dist/domains/traces/components/span-data-panel-view.cjs.js.map +1 -1
- package/dist/domains/traces/components/span-data-panel-view.es.js +4 -3
- package/dist/domains/traces/components/span-data-panel-view.es.js.map +1 -1
- package/dist/domains/traces/components/trace-data-panel-view.cjs.js +113 -83
- package/dist/domains/traces/components/trace-data-panel-view.cjs.js.map +1 -1
- package/dist/domains/traces/components/trace-data-panel-view.es.js +113 -83
- package/dist/domains/traces/components/trace-data-panel-view.es.js.map +1 -1
- package/dist/domains/traces/components/trace-timeline.cjs.js +14 -16
- package/dist/domains/traces/components/trace-timeline.cjs.js.map +1 -1
- package/dist/domains/traces/components/trace-timeline.es.js +14 -16
- package/dist/domains/traces/components/trace-timeline.es.js.map +1 -1
- package/dist/domains/traces/components/traces-layout.cjs.js +44 -30
- package/dist/domains/traces/components/traces-layout.cjs.js.map +1 -1
- package/dist/domains/traces/components/traces-layout.es.js +45 -31
- package/dist/domains/traces/components/traces-layout.es.js.map +1 -1
- package/dist/domains/traces/hooks/use-trace-spans.cjs.js +32 -15
- package/dist/domains/traces/hooks/use-trace-spans.cjs.js.map +1 -1
- package/dist/domains/traces/hooks/use-trace-spans.es.js +32 -17
- package/dist/domains/traces/hooks/use-trace-spans.es.js.map +1 -1
- package/dist/domains/traces/hooks/use-trace-url-state.cjs.js +31 -0
- package/dist/domains/traces/hooks/use-trace-url-state.cjs.js.map +1 -1
- package/dist/domains/traces/hooks/use-trace-url-state.es.js +31 -0
- package/dist/domains/traces/hooks/use-trace-url-state.es.js.map +1 -1
- package/dist/ee/signals/components/entity-index-list.cjs.js +1 -0
- package/dist/ee/signals/components/entity-index-list.cjs.js.map +1 -1
- package/dist/ee/signals/components/entity-index-list.es.js +1 -0
- package/dist/ee/signals/components/entity-index-list.es.js.map +1 -1
- package/dist/src/domains/traces/components/trace-data-panel-view.d.ts +11 -8
- package/dist/src/domains/traces/components/trace-timeline.d.ts +1 -1
- package/dist/src/domains/traces/components/traces-layout.d.ts +19 -9
- package/dist/src/domains/traces/hooks/use-trace-spans.d.ts +204 -0
- package/dist/src/domains/traces/hooks/use-trace-url-state.d.ts +4 -0
- package/dist/style.css +29 -0
- package/package.json +7 -7
|
@@ -50,22 +50,20 @@ function TraceTimeline({
|
|
|
50
50
|
]
|
|
51
51
|
}
|
|
52
52
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
})
|
|
68
|
-
] }),
|
|
53
|
+
leadingSlot && /* @__PURE__ */ jsx("div", { className: "px-2 pt-1.5", children: leadingSlot }),
|
|
54
|
+
usedSpanTypes.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-end gap-3 px-2 py-1.5", children: usedSpanTypes.map((type) => {
|
|
55
|
+
const spanUI = getSpanTypeUi(type);
|
|
56
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-ui-sm text-neutral3 flex shrink-0 items-center gap-1", children: [
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
"span",
|
|
59
|
+
{
|
|
60
|
+
className: "inline-block size-1.5 shrink-0 rounded-full",
|
|
61
|
+
style: { backgroundColor: spanUI?.color }
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
spanUI?.label || type
|
|
65
|
+
] }, type);
|
|
66
|
+
}) }),
|
|
69
67
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-[minmax(0,1fr)_auto_auto] content-start items-start gap-y-px overflow-hidden py-1", children: hierarchicalSpans?.map((span) => /* @__PURE__ */ jsx(
|
|
70
68
|
TraceTimelineSpan,
|
|
71
69
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-timeline.es.js","sources":["../../../../src/domains/traces/components/trace-timeline.tsx"],"sourcesContent":["import type { Dispatch, ReactNode, SetStateAction } from 'react';\nimport { useMemo } from 'react';\nimport type { UISpan } from '../types';\nimport { spanTypePrefixes, getSpanTypeUi } from './shared';\nimport { TraceTimelineSpan } from './trace-timeline-span';\nimport { Spinner } from '@/ds/components/Spinner';\nimport { cn } from '@/lib/utils';\n\ntype TraceTimelineProps = {\n hierarchicalSpans: UISpan[];\n onSpanClick: (id: string) => void;\n selectedSpanId?: string;\n isLoading?: boolean;\n fadedTypes?: string[];\n expandedSpanIds?: string[];\n setExpandedSpanIds?: Dispatch<SetStateAction<string[]>>;\n featuredSpanIds?: string[];\n chartWidth?: 'wide' | 'default';\n /** Rendered
|
|
1
|
+
{"version":3,"file":"trace-timeline.es.js","sources":["../../../../src/domains/traces/components/trace-timeline.tsx"],"sourcesContent":["import type { Dispatch, ReactNode, SetStateAction } from 'react';\nimport { useMemo } from 'react';\nimport type { UISpan } from '../types';\nimport { spanTypePrefixes, getSpanTypeUi } from './shared';\nimport { TraceTimelineSpan } from './trace-timeline-span';\nimport { Spinner } from '@/ds/components/Spinner';\nimport { cn } from '@/lib/utils';\n\ntype TraceTimelineProps = {\n hierarchicalSpans: UISpan[];\n onSpanClick: (id: string) => void;\n selectedSpanId?: string;\n isLoading?: boolean;\n fadedTypes?: string[];\n expandedSpanIds?: string[];\n setExpandedSpanIds?: Dispatch<SetStateAction<string[]>>;\n featuredSpanIds?: string[];\n chartWidth?: 'wide' | 'default';\n /** Rendered full-width above the span type legend row. */\n leadingSlot?: ReactNode;\n};\n\nexport function TraceTimeline({\n hierarchicalSpans = [],\n onSpanClick,\n selectedSpanId,\n isLoading,\n fadedTypes,\n expandedSpanIds,\n setExpandedSpanIds,\n featuredSpanIds,\n chartWidth = 'default',\n leadingSlot,\n}: TraceTimelineProps) {\n const overallLatency = hierarchicalSpans?.[0]?.latency || 0;\n const overallStartTime = hierarchicalSpans?.[0]?.startTime || '';\n\n const usedSpanTypes = useMemo(() => {\n const collectTypes = (spans: UISpan[]): Set<string> => {\n const types = new Set<string>();\n for (const span of spans) {\n const prefix = span.type?.toLowerCase().split('_')[0];\n if (prefix) types.add(prefix);\n if (span.spans) {\n for (const t of collectTypes(span.spans)) types.add(t);\n }\n }\n return types;\n };\n const types = collectTypes(hierarchicalSpans);\n const hasOther = [...types].some(t => !spanTypePrefixes.includes(t));\n const known = spanTypePrefixes.filter(p => p !== 'other' && types.has(p));\n if (hasOther) known.push('other');\n return known;\n }, [hierarchicalSpans]);\n\n return (\n <>\n {isLoading ? (\n <div\n className={cn(\n 'flex items-center justify-center gap-3 rounded-md bg-surface3/50 p-3 text-ui-sm text-neutral3',\n '[&_svg]:size-[1.25em] [&_svg]:opacity-50',\n )}\n >\n <Spinner /> Loading Trace Timeline ...\n </div>\n ) : (\n <>\n {leadingSlot && <div className=\"px-2 pt-1.5\">{leadingSlot}</div>}\n {usedSpanTypes.length > 0 && (\n <div className=\"flex flex-wrap items-center justify-end gap-3 px-2 py-1.5\">\n {usedSpanTypes.map(type => {\n const spanUI = getSpanTypeUi(type);\n return (\n <div key={type} className=\"text-ui-sm text-neutral3 flex shrink-0 items-center gap-1\">\n <span\n className=\"inline-block size-1.5 shrink-0 rounded-full\"\n style={{ backgroundColor: spanUI?.color }}\n />\n {spanUI?.label || type}\n </div>\n );\n })}\n </div>\n )}\n <div className=\"grid grid-cols-[minmax(0,1fr)_auto_auto] content-start items-start gap-y-px overflow-hidden py-1\">\n {hierarchicalSpans?.map(span => (\n <TraceTimelineSpan\n key={span.id}\n span={span}\n siblings={hierarchicalSpans}\n onSpanClick={onSpanClick}\n selectedSpanId={selectedSpanId}\n overallLatency={overallLatency}\n overallStartTime={overallStartTime}\n fadedTypes={fadedTypes}\n featuredSpanIds={featuredSpanIds}\n expandedSpanIds={expandedSpanIds}\n setExpandedSpanIds={setExpandedSpanIds}\n chartWidth={chartWidth}\n />\n ))}\n </div>\n </>\n )}\n </>\n );\n}\n"],"names":["types"],"mappings":"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"}
|
|
@@ -5,44 +5,58 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
5
5
|
const jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
const utils_cn = require('../../../cn-DCPcv4xv.cjs');
|
|
7
7
|
|
|
8
|
+
const SIDE_PANEL_WIDTH_CLASS = {
|
|
9
|
+
half: "w-1/2",
|
|
10
|
+
wide: "w-4/5",
|
|
11
|
+
full: "w-full"
|
|
12
|
+
};
|
|
8
13
|
function TracesLayout({
|
|
9
14
|
listSlot,
|
|
10
15
|
tracePanelSlot,
|
|
11
16
|
spanPanelSlot,
|
|
12
17
|
scorePanelSlot,
|
|
13
18
|
traceCollapsed,
|
|
14
|
-
|
|
19
|
+
sidePanelWidth = "half"
|
|
15
20
|
}) {
|
|
16
21
|
const hasSidePanel = !!tracePanelSlot;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
22
|
+
const isHalf = sidePanelWidth === "half";
|
|
23
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
24
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
25
|
+
"div",
|
|
26
|
+
{
|
|
27
|
+
className: utils_cn.cn(
|
|
28
|
+
"grid h-full min-h-0 items-start gap-4 transition-[grid-template-columns] duration-300 ease-in-out",
|
|
29
|
+
hasSidePanel ? isHalf ? "grid-cols-[1fr_1fr]" : "grid-cols-[1fr_4fr]" : "grid-cols-[1fr]"
|
|
30
|
+
),
|
|
31
|
+
children: [
|
|
32
|
+
listSlot,
|
|
33
|
+
hasSidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true })
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
hasSidePanel && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
role: "dialog",
|
|
41
|
+
"aria-label": "Trace details",
|
|
42
|
+
"data-trace-side-panel": true,
|
|
43
|
+
className: utils_cn.cn(
|
|
44
|
+
// z-50 matches the route Header (a z-50 grid sibling in the app frame) so the panel,
|
|
45
|
+
// rendered later in the DOM, paints above it while body-level portals stay on top.
|
|
46
|
+
"absolute inset-y-0 right-0 z-50 min-w-0 p-3",
|
|
47
|
+
"transition-[width] duration-300 ease-in-out",
|
|
48
|
+
SIDE_PANEL_WIDTH_CLASS[sidePanelWidth],
|
|
49
|
+
"grid gap-4 overflow-auto [&>*]:rounded-lg [&>*]:bg-surface3 [&>*]:shadow-lg",
|
|
50
|
+
scorePanelSlot ? traceCollapsed ? "grid-rows-[auto_3fr_3fr]" : "grid-rows-[2fr_3fr_3fr]" : spanPanelSlot ? traceCollapsed ? "grid-rows-[auto_3fr]" : "grid-rows-[2fr_3fr]" : traceCollapsed ? "grid-rows-[auto]" : "grid-rows-[1fr]"
|
|
51
|
+
),
|
|
52
|
+
children: [
|
|
53
|
+
tracePanelSlot,
|
|
54
|
+
spanPanelSlot,
|
|
55
|
+
scorePanelSlot
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
] });
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
exports.TracesLayout = TracesLayout;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traces-layout.cjs.js","sources":["../../../../src/domains/traces/components/traces-layout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface TracesLayoutProps {\n /** The trace list (left column). */\n listSlot: ReactNode;\n /** The trace data panel (
|
|
1
|
+
{"version":3,"file":"traces-layout.cjs.js","sources":["../../../../src/domains/traces/components/traces-layout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface TracesLayoutProps {\n /** The trace list (left column). */\n listSlot: ReactNode;\n /** The trace data panel (side panel, top). When null/undefined, the side panel is not rendered. */\n tracePanelSlot?: ReactNode;\n /** The span data panel (side panel, middle). Only rendered when truthy. */\n spanPanelSlot?: ReactNode;\n /** The score data panel (side panel, bottom). Only rendered when truthy. */\n scorePanelSlot?: ReactNode;\n /** When the trace panel is collapsed, the side panel's grid-rows squash the trace row to `auto`. */\n traceCollapsed?: boolean;\n /**\n * Side panel width, driven by how many columns the trace panel shows:\n * `half` (trace only), `wide` (trace + one side column), `full` (messages + trace + span —\n * covers the whole positioned ancestor, i.e. stops at the main nav).\n */\n sidePanelWidth?: TracesSidePanelWidth;\n}\n\nexport type TracesSidePanelWidth = 'half' | 'wide' | 'full';\n\nconst SIDE_PANEL_WIDTH_CLASS: Record<TracesSidePanelWidth, string> = {\n half: 'w-1/2',\n wide: 'w-4/5',\n full: 'w-full',\n};\n\n/**\n * Layout shell for the traces page. Owns no state and fetches no data — pass slots in.\n *\n * The list lives in a 2-column grid whose right column is an empty spacer; the side panel\n * is rendered as an `absolute inset-y-0 right-0` overlay whose width mirrors the spacer\n * column, so it spans the full height of its nearest positioned ancestor while the list\n * stays interactive on the left. Consumers control the coverage by choosing which ancestor\n * is `relative` (e.g. the app frame, so the overlay also covers the route header).\n */\nexport function TracesLayout({\n listSlot,\n tracePanelSlot,\n spanPanelSlot,\n scorePanelSlot,\n traceCollapsed,\n sidePanelWidth = 'half',\n}: TracesLayoutProps) {\n const hasSidePanel = !!tracePanelSlot;\n const isHalf = sidePanelWidth === 'half';\n\n return (\n <>\n <div\n className={cn(\n 'grid h-full min-h-0 items-start gap-4 transition-[grid-template-columns] duration-300 ease-in-out',\n hasSidePanel ? (isHalf ? 'grid-cols-[1fr_1fr]' : 'grid-cols-[1fr_4fr]') : 'grid-cols-[1fr]',\n )}\n >\n {listSlot}\n {hasSidePanel && <div aria-hidden />}\n </div>\n\n {hasSidePanel && (\n <div\n role=\"dialog\"\n aria-label=\"Trace details\"\n data-trace-side-panel\n className={cn(\n // z-50 matches the route Header (a z-50 grid sibling in the app frame) so the panel,\n // rendered later in the DOM, paints above it while body-level portals stay on top.\n 'absolute inset-y-0 right-0 z-50 min-w-0 p-3',\n 'transition-[width] duration-300 ease-in-out',\n SIDE_PANEL_WIDTH_CLASS[sidePanelWidth],\n 'grid gap-4 overflow-auto [&>*]:rounded-lg [&>*]:bg-surface3 [&>*]:shadow-lg',\n scorePanelSlot\n ? traceCollapsed\n ? 'grid-rows-[auto_3fr_3fr]'\n : 'grid-rows-[2fr_3fr_3fr]'\n : spanPanelSlot\n ? traceCollapsed\n ? 'grid-rows-[auto_3fr]'\n : 'grid-rows-[2fr_3fr]'\n : traceCollapsed\n ? 'grid-rows-[auto]'\n : 'grid-rows-[1fr]',\n )}\n >\n {tracePanelSlot}\n {spanPanelSlot}\n {scorePanelSlot}\n </div>\n )}\n </>\n );\n}\n"],"names":["jsxs","Fragment","cn","jsx"],"mappings":";;;;;;;AAwBA,MAAM,sBAAA,GAA+D;AAAA,EACnE,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM;AACR,CAAA;AAWO,SAAS,YAAA,CAAa;AAAA,EAC3B,QAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA,GAAiB;AACnB,CAAA,EAAsB;AACpB,EAAA,MAAM,YAAA,GAAe,CAAC,CAAC,cAAA;AACvB,EAAA,MAAM,SAAS,cAAA,KAAmB,MAAA;AAElC,EAAA,uBACEA,eAAA,CAAAC,mBAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAAD,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAWE,WAAA;AAAA,UACT,mGAAA;AAAA,UACA,YAAA,GAAgB,MAAA,GAAS,qBAAA,GAAwB,qBAAA,GAAyB;AAAA,SAC5E;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,QAAA;AAAA,UACA,YAAA,oBAAgBC,cAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAW,IAAA,EAAC;AAAA;AAAA;AAAA,KACpC;AAAA,IAEC,YAAA,oBACCH,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,YAAA,EAAW,eAAA;AAAA,QACX,uBAAA,EAAqB,IAAA;AAAA,QACrB,SAAA,EAAWE,WAAA;AAAA;AAAA;AAAA,UAGT,6CAAA;AAAA,UACA,6CAAA;AAAA,UACA,uBAAuB,cAAc,CAAA;AAAA,UACrC,6EAAA;AAAA,UACA,cAAA,GACI,iBACE,0BAAA,GACA,yBAAA,GACF,gBACE,cAAA,GACE,sBAAA,GACA,qBAAA,GACF,cAAA,GACE,kBAAA,GACA;AAAA,SACV;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,cAAA;AAAA,UACA,aAAA;AAAA,UACA;AAAA;AAAA;AAAA;AACH,GAAA,EAEJ,CAAA;AAEJ;;;;"}
|
|
@@ -1,44 +1,58 @@
|
|
|
1
|
-
import { jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { c as cn } from '../../../cn-B4oPhFx9.js';
|
|
3
3
|
|
|
4
|
+
const SIDE_PANEL_WIDTH_CLASS = {
|
|
5
|
+
half: "w-1/2",
|
|
6
|
+
wide: "w-4/5",
|
|
7
|
+
full: "w-full"
|
|
8
|
+
};
|
|
4
9
|
function TracesLayout({
|
|
5
10
|
listSlot,
|
|
6
11
|
tracePanelSlot,
|
|
7
12
|
spanPanelSlot,
|
|
8
13
|
scorePanelSlot,
|
|
9
14
|
traceCollapsed,
|
|
10
|
-
|
|
15
|
+
sidePanelWidth = "half"
|
|
11
16
|
}) {
|
|
12
17
|
const hasSidePanel = !!tracePanelSlot;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
const isHalf = sidePanelWidth === "half";
|
|
19
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
20
|
+
/* @__PURE__ */ jsxs(
|
|
21
|
+
"div",
|
|
22
|
+
{
|
|
23
|
+
className: cn(
|
|
24
|
+
"grid h-full min-h-0 items-start gap-4 transition-[grid-template-columns] duration-300 ease-in-out",
|
|
25
|
+
hasSidePanel ? isHalf ? "grid-cols-[1fr_1fr]" : "grid-cols-[1fr_4fr]" : "grid-cols-[1fr]"
|
|
26
|
+
),
|
|
27
|
+
children: [
|
|
28
|
+
listSlot,
|
|
29
|
+
hasSidePanel && /* @__PURE__ */ jsx("div", { "aria-hidden": true })
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
),
|
|
33
|
+
hasSidePanel && /* @__PURE__ */ jsxs(
|
|
34
|
+
"div",
|
|
35
|
+
{
|
|
36
|
+
role: "dialog",
|
|
37
|
+
"aria-label": "Trace details",
|
|
38
|
+
"data-trace-side-panel": true,
|
|
39
|
+
className: cn(
|
|
40
|
+
// z-50 matches the route Header (a z-50 grid sibling in the app frame) so the panel,
|
|
41
|
+
// rendered later in the DOM, paints above it while body-level portals stay on top.
|
|
42
|
+
"absolute inset-y-0 right-0 z-50 min-w-0 p-3",
|
|
43
|
+
"transition-[width] duration-300 ease-in-out",
|
|
44
|
+
SIDE_PANEL_WIDTH_CLASS[sidePanelWidth],
|
|
45
|
+
"grid gap-4 overflow-auto [&>*]:rounded-lg [&>*]:bg-surface3 [&>*]:shadow-lg",
|
|
46
|
+
scorePanelSlot ? traceCollapsed ? "grid-rows-[auto_3fr_3fr]" : "grid-rows-[2fr_3fr_3fr]" : spanPanelSlot ? traceCollapsed ? "grid-rows-[auto_3fr]" : "grid-rows-[2fr_3fr]" : traceCollapsed ? "grid-rows-[auto]" : "grid-rows-[1fr]"
|
|
47
|
+
),
|
|
48
|
+
children: [
|
|
49
|
+
tracePanelSlot,
|
|
50
|
+
spanPanelSlot,
|
|
51
|
+
scorePanelSlot
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
] });
|
|
42
56
|
}
|
|
43
57
|
|
|
44
58
|
export { TracesLayout };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traces-layout.es.js","sources":["../../../../src/domains/traces/components/traces-layout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface TracesLayoutProps {\n /** The trace list (left column). */\n listSlot: ReactNode;\n /** The trace data panel (
|
|
1
|
+
{"version":3,"file":"traces-layout.es.js","sources":["../../../../src/domains/traces/components/traces-layout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface TracesLayoutProps {\n /** The trace list (left column). */\n listSlot: ReactNode;\n /** The trace data panel (side panel, top). When null/undefined, the side panel is not rendered. */\n tracePanelSlot?: ReactNode;\n /** The span data panel (side panel, middle). Only rendered when truthy. */\n spanPanelSlot?: ReactNode;\n /** The score data panel (side panel, bottom). Only rendered when truthy. */\n scorePanelSlot?: ReactNode;\n /** When the trace panel is collapsed, the side panel's grid-rows squash the trace row to `auto`. */\n traceCollapsed?: boolean;\n /**\n * Side panel width, driven by how many columns the trace panel shows:\n * `half` (trace only), `wide` (trace + one side column), `full` (messages + trace + span —\n * covers the whole positioned ancestor, i.e. stops at the main nav).\n */\n sidePanelWidth?: TracesSidePanelWidth;\n}\n\nexport type TracesSidePanelWidth = 'half' | 'wide' | 'full';\n\nconst SIDE_PANEL_WIDTH_CLASS: Record<TracesSidePanelWidth, string> = {\n half: 'w-1/2',\n wide: 'w-4/5',\n full: 'w-full',\n};\n\n/**\n * Layout shell for the traces page. Owns no state and fetches no data — pass slots in.\n *\n * The list lives in a 2-column grid whose right column is an empty spacer; the side panel\n * is rendered as an `absolute inset-y-0 right-0` overlay whose width mirrors the spacer\n * column, so it spans the full height of its nearest positioned ancestor while the list\n * stays interactive on the left. Consumers control the coverage by choosing which ancestor\n * is `relative` (e.g. the app frame, so the overlay also covers the route header).\n */\nexport function TracesLayout({\n listSlot,\n tracePanelSlot,\n spanPanelSlot,\n scorePanelSlot,\n traceCollapsed,\n sidePanelWidth = 'half',\n}: TracesLayoutProps) {\n const hasSidePanel = !!tracePanelSlot;\n const isHalf = sidePanelWidth === 'half';\n\n return (\n <>\n <div\n className={cn(\n 'grid h-full min-h-0 items-start gap-4 transition-[grid-template-columns] duration-300 ease-in-out',\n hasSidePanel ? (isHalf ? 'grid-cols-[1fr_1fr]' : 'grid-cols-[1fr_4fr]') : 'grid-cols-[1fr]',\n )}\n >\n {listSlot}\n {hasSidePanel && <div aria-hidden />}\n </div>\n\n {hasSidePanel && (\n <div\n role=\"dialog\"\n aria-label=\"Trace details\"\n data-trace-side-panel\n className={cn(\n // z-50 matches the route Header (a z-50 grid sibling in the app frame) so the panel,\n // rendered later in the DOM, paints above it while body-level portals stay on top.\n 'absolute inset-y-0 right-0 z-50 min-w-0 p-3',\n 'transition-[width] duration-300 ease-in-out',\n SIDE_PANEL_WIDTH_CLASS[sidePanelWidth],\n 'grid gap-4 overflow-auto [&>*]:rounded-lg [&>*]:bg-surface3 [&>*]:shadow-lg',\n scorePanelSlot\n ? traceCollapsed\n ? 'grid-rows-[auto_3fr_3fr]'\n : 'grid-rows-[2fr_3fr_3fr]'\n : spanPanelSlot\n ? traceCollapsed\n ? 'grid-rows-[auto_3fr]'\n : 'grid-rows-[2fr_3fr]'\n : traceCollapsed\n ? 'grid-rows-[auto]'\n : 'grid-rows-[1fr]',\n )}\n >\n {tracePanelSlot}\n {spanPanelSlot}\n {scorePanelSlot}\n </div>\n )}\n </>\n );\n}\n"],"names":[],"mappings":";;;AAwBA,MAAM,sBAAA,GAA+D;AAAA,EACnE,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM;AACR,CAAA;AAWO,SAAS,YAAA,CAAa;AAAA,EAC3B,QAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA,GAAiB;AACnB,CAAA,EAAsB;AACpB,EAAA,MAAM,YAAA,GAAe,CAAC,CAAC,cAAA;AACvB,EAAA,MAAM,SAAS,cAAA,KAAmB,MAAA;AAElC,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAA;AAAA,UACT,mGAAA;AAAA,UACA,YAAA,GAAgB,MAAA,GAAS,qBAAA,GAAwB,qBAAA,GAAyB;AAAA,SAC5E;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,QAAA;AAAA,UACA,YAAA,oBAAgB,GAAA,CAAC,KAAA,EAAA,EAAI,aAAA,EAAW,IAAA,EAAC;AAAA;AAAA;AAAA,KACpC;AAAA,IAEC,YAAA,oBACC,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,YAAA,EAAW,eAAA;AAAA,QACX,uBAAA,EAAqB,IAAA;AAAA,QACrB,SAAA,EAAW,EAAA;AAAA;AAAA;AAAA,UAGT,6CAAA;AAAA,UACA,6CAAA;AAAA,UACA,uBAAuB,cAAc,CAAA;AAAA,UACrC,6EAAA;AAAA,UACA,cAAA,GACI,iBACE,0BAAA,GACA,yBAAA,GACF,gBACE,cAAA,GACE,sBAAA,GACA,qBAAA,GACF,cAAA,GACE,kBAAA,GACA;AAAA,SACV;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,cAAA;AAAA,UACA,aAAA;AAAA,UACA;AAAA;AAAA;AAAA;AACH,GAAA,EAEJ,CAAA;AAEJ;;;;"}
|
|
@@ -8,27 +8,44 @@ const domains_traces_utils_toSearchableSpans = require('../utils/to-searchable-s
|
|
|
8
8
|
require('date-fns');
|
|
9
9
|
|
|
10
10
|
const IMMUTABLE_CACHE_TIME = 1e3 * 60 * 60 * 24 * 30;
|
|
11
|
+
const traceSpansQueryOptions = (client, traceId) => reactQuery.queryOptions({
|
|
12
|
+
queryKey: ["trace-spans", traceId],
|
|
13
|
+
queryFn: async () => {
|
|
14
|
+
if (!traceId) {
|
|
15
|
+
throw new Error("Trace ID is required");
|
|
16
|
+
}
|
|
17
|
+
const res = await client.getTrace(traceId);
|
|
18
|
+
return res;
|
|
19
|
+
},
|
|
20
|
+
enabled: !!traceId,
|
|
21
|
+
staleTime: (query) => {
|
|
22
|
+
const data = query.state.data;
|
|
23
|
+
const isFinished = data?.spans.every((span) => Boolean(span.endedAt));
|
|
24
|
+
return isFinished ? IMMUTABLE_CACHE_TIME : 0;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
11
27
|
function useTraceSpans(traceId) {
|
|
12
28
|
const client = react.useMastraClient();
|
|
13
29
|
return reactQuery.useQuery({
|
|
14
|
-
|
|
15
|
-
queryFn: async () => {
|
|
16
|
-
if (!traceId) {
|
|
17
|
-
throw new Error("Trace ID is required");
|
|
18
|
-
}
|
|
19
|
-
const res = await client.getTrace(traceId);
|
|
20
|
-
return res;
|
|
21
|
-
},
|
|
30
|
+
...traceSpansQueryOptions(client, traceId),
|
|
22
31
|
// Builds each span's search haystack once per fetch, cached with the query.
|
|
23
|
-
select: domains_traces_utils_toSearchableSpans.selectSearchableSpans
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
select: domains_traces_utils_toSearchableSpans.selectSearchableSpans
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function useTraceSpansQueries(traceIds, select, fallback) {
|
|
36
|
+
const client = react.useMastraClient();
|
|
37
|
+
return reactQuery.useQueries({
|
|
38
|
+
queries: traceIds.map((traceId) => ({
|
|
39
|
+
...traceSpansQueryOptions(client, traceId),
|
|
40
|
+
select: (data) => select(traceId, data)
|
|
41
|
+
})),
|
|
42
|
+
combine: (results) => results.map(
|
|
43
|
+
(result, index) => result.data === void 0 ? fallback(traceIds[index] ?? "") : result.data
|
|
44
|
+
)
|
|
30
45
|
});
|
|
31
46
|
}
|
|
32
47
|
|
|
48
|
+
exports.traceSpansQueryOptions = traceSpansQueryOptions;
|
|
33
49
|
exports.useTraceSpans = useTraceSpans;
|
|
50
|
+
exports.useTraceSpansQueries = useTraceSpansQueries;
|
|
34
51
|
//# sourceMappingURL=use-trace-spans.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-trace-spans.cjs.js","sources":["../../../../src/domains/traces/hooks/use-trace-spans.ts"],"sourcesContent":["import { useMastraClient } from '@mastra/react';\nimport { useQuery } from '@tanstack/react-query';\nimport type { UseQueryResult } from '@tanstack/react-query';\nimport type { SearchableSpan } from '../types';\nimport { selectSearchableSpans } from '../utils';\n\nconst IMMUTABLE_CACHE_TIME = 1000 * 60 * 60 * 24 * 30; // 30 days, massive cache, span data is immutable\n\n/**\n * Every span of a single trace, with its full payload.\n *\n * The lightweight projection exists to keep blob columns off the read path of a\n * *list*, where the cost is paid once per trace on screen. A trace that is open\n * has already narrowed that to one, and the panel both renders and searches\n * these spans -- `input`, `output` and `attributes` included -- so the\n * projection would only hide content the reader is looking at.\n */\nexport function useTraceSpans(\n traceId: string | null | undefined,\n): UseQueryResult<{ traceId: string; spans: SearchableSpan[] } | null> {\n const client = useMastraClient();\n\n return useQuery({\n
|
|
1
|
+
{"version":3,"file":"use-trace-spans.cjs.js","sources":["../../../../src/domains/traces/hooks/use-trace-spans.ts"],"sourcesContent":["import type { MastraClient } from '@mastra/client-js';\nimport { useMastraClient } from '@mastra/react';\nimport { queryOptions, useQueries, useQuery } from '@tanstack/react-query';\nimport type { UseQueryResult } from '@tanstack/react-query';\nimport type { SearchableSpan } from '../types';\nimport { selectSearchableSpans } from '../utils';\n\nconst IMMUTABLE_CACHE_TIME = 1000 * 60 * 60 * 24 * 30; // 30 days, massive cache, span data is immutable\n\n/**\n * Key, fetcher and stale policy of the `trace-spans` query. Every observer of this key must\n * share these (rather than only the key) so one observer with a stricter `staleTime` does not\n * refetch data another one considers fresh.\n */\nexport const traceSpansQueryOptions = (client: MastraClient, traceId: string | null | undefined) =>\n queryOptions({\n queryKey: ['trace-spans', traceId],\n queryFn: async () => {\n if (!traceId) {\n throw new Error('Trace ID is required');\n }\n const res = await client.getTrace(traceId);\n return res;\n },\n enabled: !!traceId,\n staleTime: query => {\n const data = query.state.data;\n const isFinished = data?.spans.every(span => Boolean(span.endedAt));\n return isFinished ? IMMUTABLE_CACHE_TIME : 0;\n },\n });\n\n/**\n * Every span of a single trace, with its full payload.\n *\n * The lightweight projection exists to keep blob columns off the read path of a\n * *list*, where the cost is paid once per trace on screen. A trace that is open\n * has already narrowed that to one, and the panel both renders and searches\n * these spans -- `input`, `output` and `attributes` included -- so the\n * projection would only hide content the reader is looking at.\n */\nexport function useTraceSpans(\n traceId: string | null | undefined,\n): UseQueryResult<{ traceId: string; spans: SearchableSpan[] } | null> {\n const client = useMastraClient();\n\n return useQuery({\n ...traceSpansQueryOptions(client, traceId),\n // Builds each span's search haystack once per fetch, cached with the query.\n select: selectSearchableSpans,\n });\n}\n\nexport type TraceSpansData = Awaited<ReturnType<MastraClient['getTrace']>>;\n\n/**\n * Observes the `trace-spans` query of several traces at once and projects each one with `select`.\n * Traces still loading (or failed) yield `fallback(traceId)` so the result always lines up with `traceIds`.\n */\nexport function useTraceSpansQueries<T>(\n traceIds: string[],\n select: (traceId: string, data: TraceSpansData) => T,\n fallback: (traceId: string) => T,\n): T[] {\n const client = useMastraClient();\n\n return useQueries({\n queries: traceIds.map(traceId => ({\n ...traceSpansQueryOptions(client, traceId),\n select: (data: TraceSpansData) => select(traceId, data),\n })),\n combine: results =>\n results.map((result, index) =>\n result.data === undefined ? fallback(traceIds[index] ?? '') : (result.data as T),\n ),\n });\n}\n"],"names":["queryOptions","useMastraClient","useQuery","selectSearchableSpans","useQueries"],"mappings":";;;;;;;;;AAOA,MAAM,oBAAA,GAAuB,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAO5C,MAAM,sBAAA,GAAyB,CAAC,MAAA,EAAsB,OAAA,KAC3DA,uBAAA,CAAa;AAAA,EACX,QAAA,EAAU,CAAC,aAAA,EAAe,OAAO,CAAA;AAAA,EACjC,SAAS,YAAY;AACnB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAI,MAAM,sBAAsB,CAAA;AAAA,IACxC;AACA,IAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AACzC,IAAA,OAAO,GAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAA,EAAS,CAAC,CAAC,OAAA;AAAA,EACX,WAAW,CAAA,KAAA,KAAS;AAClB,IAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,IAAA;AACzB,IAAA,MAAM,UAAA,GAAa,MAAM,KAAA,CAAM,KAAA,CAAM,UAAQ,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAC,CAAA;AAClE,IAAA,OAAO,aAAa,oBAAA,GAAuB,CAAA;AAAA,EAC7C;AACF,CAAC;AAWI,SAAS,cACd,OAAA,EACqE;AACrE,EAAA,MAAM,SAASC,qBAAA,EAAgB;AAE/B,EAAA,OAAOC,mBAAA,CAAS;AAAA,IACd,GAAG,sBAAA,CAAuB,MAAA,EAAQ,OAAO,CAAA;AAAA;AAAA,IAEzC,MAAA,EAAQC;AAAA,GACT,CAAA;AACH;AAQO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACK;AACL,EAAA,MAAM,SAASF,qBAAA,EAAgB;AAE/B,EAAA,OAAOG,qBAAA,CAAW;AAAA,IAChB,OAAA,EAAS,QAAA,CAAS,GAAA,CAAI,CAAA,OAAA,MAAY;AAAA,MAChC,GAAG,sBAAA,CAAuB,MAAA,EAAQ,OAAO,CAAA;AAAA,MACzC,MAAA,EAAQ,CAAC,IAAA,KAAyB,MAAA,CAAO,SAAS,IAAI;AAAA,KACxD,CAAE,CAAA;AAAA,IACF,OAAA,EAAS,aACP,OAAA,CAAQ,GAAA;AAAA,MAAI,CAAC,MAAA,EAAQ,KAAA,KACnB,MAAA,CAAO,IAAA,KAAS,MAAA,GAAY,QAAA,CAAS,QAAA,CAAS,KAAK,CAAA,IAAK,EAAE,CAAA,GAAK,MAAA,CAAO;AAAA;AACxE,GACH,CAAA;AACH;;;;;;"}
|
|
@@ -1,30 +1,45 @@
|
|
|
1
1
|
import { useMastraClient } from '@mastra/react';
|
|
2
|
-
import { useQuery } from '@tanstack/react-query';
|
|
2
|
+
import { useQuery, queryOptions, useQueries } from '@tanstack/react-query';
|
|
3
3
|
import { selectSearchableSpans } from '../utils/to-searchable-spans.es.js';
|
|
4
4
|
import 'date-fns';
|
|
5
5
|
|
|
6
6
|
const IMMUTABLE_CACHE_TIME = 1e3 * 60 * 60 * 24 * 30;
|
|
7
|
+
const traceSpansQueryOptions = (client, traceId) => queryOptions({
|
|
8
|
+
queryKey: ["trace-spans", traceId],
|
|
9
|
+
queryFn: async () => {
|
|
10
|
+
if (!traceId) {
|
|
11
|
+
throw new Error("Trace ID is required");
|
|
12
|
+
}
|
|
13
|
+
const res = await client.getTrace(traceId);
|
|
14
|
+
return res;
|
|
15
|
+
},
|
|
16
|
+
enabled: !!traceId,
|
|
17
|
+
staleTime: (query) => {
|
|
18
|
+
const data = query.state.data;
|
|
19
|
+
const isFinished = data?.spans.every((span) => Boolean(span.endedAt));
|
|
20
|
+
return isFinished ? IMMUTABLE_CACHE_TIME : 0;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
7
23
|
function useTraceSpans(traceId) {
|
|
8
24
|
const client = useMastraClient();
|
|
9
25
|
return useQuery({
|
|
10
|
-
|
|
11
|
-
queryFn: async () => {
|
|
12
|
-
if (!traceId) {
|
|
13
|
-
throw new Error("Trace ID is required");
|
|
14
|
-
}
|
|
15
|
-
const res = await client.getTrace(traceId);
|
|
16
|
-
return res;
|
|
17
|
-
},
|
|
26
|
+
...traceSpansQueryOptions(client, traceId),
|
|
18
27
|
// Builds each span's search haystack once per fetch, cached with the query.
|
|
19
|
-
select: selectSearchableSpans
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
select: selectSearchableSpans
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function useTraceSpansQueries(traceIds, select, fallback) {
|
|
32
|
+
const client = useMastraClient();
|
|
33
|
+
return useQueries({
|
|
34
|
+
queries: traceIds.map((traceId) => ({
|
|
35
|
+
...traceSpansQueryOptions(client, traceId),
|
|
36
|
+
select: (data) => select(traceId, data)
|
|
37
|
+
})),
|
|
38
|
+
combine: (results) => results.map(
|
|
39
|
+
(result, index) => result.data === void 0 ? fallback(traceIds[index] ?? "") : result.data
|
|
40
|
+
)
|
|
26
41
|
});
|
|
27
42
|
}
|
|
28
43
|
|
|
29
|
-
export { useTraceSpans };
|
|
44
|
+
export { traceSpansQueryOptions, useTraceSpans, useTraceSpansQueries };
|
|
30
45
|
//# sourceMappingURL=use-trace-spans.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-trace-spans.es.js","sources":["../../../../src/domains/traces/hooks/use-trace-spans.ts"],"sourcesContent":["import { useMastraClient } from '@mastra/react';\nimport { useQuery } from '@tanstack/react-query';\nimport type { UseQueryResult } from '@tanstack/react-query';\nimport type { SearchableSpan } from '../types';\nimport { selectSearchableSpans } from '../utils';\n\nconst IMMUTABLE_CACHE_TIME = 1000 * 60 * 60 * 24 * 30; // 30 days, massive cache, span data is immutable\n\n/**\n * Every span of a single trace, with its full payload.\n *\n * The lightweight projection exists to keep blob columns off the read path of a\n * *list*, where the cost is paid once per trace on screen. A trace that is open\n * has already narrowed that to one, and the panel both renders and searches\n * these spans -- `input`, `output` and `attributes` included -- so the\n * projection would only hide content the reader is looking at.\n */\nexport function useTraceSpans(\n traceId: string | null | undefined,\n): UseQueryResult<{ traceId: string; spans: SearchableSpan[] } | null> {\n const client = useMastraClient();\n\n return useQuery({\n
|
|
1
|
+
{"version":3,"file":"use-trace-spans.es.js","sources":["../../../../src/domains/traces/hooks/use-trace-spans.ts"],"sourcesContent":["import type { MastraClient } from '@mastra/client-js';\nimport { useMastraClient } from '@mastra/react';\nimport { queryOptions, useQueries, useQuery } from '@tanstack/react-query';\nimport type { UseQueryResult } from '@tanstack/react-query';\nimport type { SearchableSpan } from '../types';\nimport { selectSearchableSpans } from '../utils';\n\nconst IMMUTABLE_CACHE_TIME = 1000 * 60 * 60 * 24 * 30; // 30 days, massive cache, span data is immutable\n\n/**\n * Key, fetcher and stale policy of the `trace-spans` query. Every observer of this key must\n * share these (rather than only the key) so one observer with a stricter `staleTime` does not\n * refetch data another one considers fresh.\n */\nexport const traceSpansQueryOptions = (client: MastraClient, traceId: string | null | undefined) =>\n queryOptions({\n queryKey: ['trace-spans', traceId],\n queryFn: async () => {\n if (!traceId) {\n throw new Error('Trace ID is required');\n }\n const res = await client.getTrace(traceId);\n return res;\n },\n enabled: !!traceId,\n staleTime: query => {\n const data = query.state.data;\n const isFinished = data?.spans.every(span => Boolean(span.endedAt));\n return isFinished ? IMMUTABLE_CACHE_TIME : 0;\n },\n });\n\n/**\n * Every span of a single trace, with its full payload.\n *\n * The lightweight projection exists to keep blob columns off the read path of a\n * *list*, where the cost is paid once per trace on screen. A trace that is open\n * has already narrowed that to one, and the panel both renders and searches\n * these spans -- `input`, `output` and `attributes` included -- so the\n * projection would only hide content the reader is looking at.\n */\nexport function useTraceSpans(\n traceId: string | null | undefined,\n): UseQueryResult<{ traceId: string; spans: SearchableSpan[] } | null> {\n const client = useMastraClient();\n\n return useQuery({\n ...traceSpansQueryOptions(client, traceId),\n // Builds each span's search haystack once per fetch, cached with the query.\n select: selectSearchableSpans,\n });\n}\n\nexport type TraceSpansData = Awaited<ReturnType<MastraClient['getTrace']>>;\n\n/**\n * Observes the `trace-spans` query of several traces at once and projects each one with `select`.\n * Traces still loading (or failed) yield `fallback(traceId)` so the result always lines up with `traceIds`.\n */\nexport function useTraceSpansQueries<T>(\n traceIds: string[],\n select: (traceId: string, data: TraceSpansData) => T,\n fallback: (traceId: string) => T,\n): T[] {\n const client = useMastraClient();\n\n return useQueries({\n queries: traceIds.map(traceId => ({\n ...traceSpansQueryOptions(client, traceId),\n select: (data: TraceSpansData) => select(traceId, data),\n })),\n combine: results =>\n results.map((result, index) =>\n result.data === undefined ? fallback(traceIds[index] ?? '') : (result.data as T),\n ),\n });\n}\n"],"names":[],"mappings":";;;;;AAOA,MAAM,oBAAA,GAAuB,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAO5C,MAAM,sBAAA,GAAyB,CAAC,MAAA,EAAsB,OAAA,KAC3D,YAAA,CAAa;AAAA,EACX,QAAA,EAAU,CAAC,aAAA,EAAe,OAAO,CAAA;AAAA,EACjC,SAAS,YAAY;AACnB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAI,MAAM,sBAAsB,CAAA;AAAA,IACxC;AACA,IAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AACzC,IAAA,OAAO,GAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAA,EAAS,CAAC,CAAC,OAAA;AAAA,EACX,WAAW,CAAA,KAAA,KAAS;AAClB,IAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,IAAA;AACzB,IAAA,MAAM,UAAA,GAAa,MAAM,KAAA,CAAM,KAAA,CAAM,UAAQ,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAC,CAAA;AAClE,IAAA,OAAO,aAAa,oBAAA,GAAuB,CAAA;AAAA,EAC7C;AACF,CAAC;AAWI,SAAS,cACd,OAAA,EACqE;AACrE,EAAA,MAAM,SAAS,eAAA,EAAgB;AAE/B,EAAA,OAAO,QAAA,CAAS;AAAA,IACd,GAAG,sBAAA,CAAuB,MAAA,EAAQ,OAAO,CAAA;AAAA;AAAA,IAEzC,MAAA,EAAQ;AAAA,GACT,CAAA;AACH;AAQO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACK;AACL,EAAA,MAAM,SAAS,eAAA,EAAgB;AAE/B,EAAA,OAAO,UAAA,CAAW;AAAA,IAChB,OAAA,EAAS,QAAA,CAAS,GAAA,CAAI,CAAA,OAAA,MAAY;AAAA,MAChC,GAAG,sBAAA,CAAuB,MAAA,EAAQ,OAAO,CAAA;AAAA,MACzC,MAAA,EAAQ,CAAC,IAAA,KAAyB,MAAA,CAAO,SAAS,IAAI;AAAA,KACxD,CAAE,CAAA;AAAA,IACF,OAAA,EAAS,aACP,OAAA,CAAQ,GAAA;AAAA,MAAI,CAAC,MAAA,EAAQ,KAAA,KACnB,MAAA,CAAO,IAAA,KAAS,MAAA,GAAY,QAAA,CAAS,QAAA,CAAS,KAAK,CAAA,IAAK,EAAE,CAAA,GAAK,MAAA,CAAO;AAAA;AACxE,GACH,CAAA;AACH;;;;"}
|
|
@@ -9,6 +9,7 @@ const TRACE_ID_PARAM = "traceId";
|
|
|
9
9
|
const SPAN_ID_PARAM = "spanId";
|
|
10
10
|
const TAB_PARAM = "tab";
|
|
11
11
|
const SCORE_ID_PARAM = "scoreId";
|
|
12
|
+
const HIGHLIGHT_SPAN_IDS_PARAM = "highlightSpanIds";
|
|
12
13
|
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
13
14
|
const PRESET_MS = {
|
|
14
15
|
"last-24h": DAY_MS,
|
|
@@ -23,6 +24,7 @@ function clearSelectionParams(params) {
|
|
|
23
24
|
params.delete(domains_traces_traceFilters.TRACE_ANCHOR_SPAN_ID_PARAM);
|
|
24
25
|
params.delete(TAB_PARAM);
|
|
25
26
|
params.delete(SCORE_ID_PARAM);
|
|
27
|
+
params.delete(HIGHLIGHT_SPAN_IDS_PARAM);
|
|
26
28
|
}
|
|
27
29
|
function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
28
30
|
const { onRemoveAll } = options ?? {};
|
|
@@ -54,6 +56,11 @@ function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
|
54
56
|
const tabParam = searchParams.get(TAB_PARAM);
|
|
55
57
|
const spanTabParam = tabParam === "feedback" ? "feedback" : tabParam === "details" ? "details" : void 0;
|
|
56
58
|
const scoreIdParam = searchParams.get(SCORE_ID_PARAM) || void 0;
|
|
59
|
+
const highlightSpanIdsRaw = searchParams.get(HIGHLIGHT_SPAN_IDS_PARAM);
|
|
60
|
+
const highlightSpanIdsParam = React.useMemo(
|
|
61
|
+
() => highlightSpanIdsRaw ? highlightSpanIdsRaw.split(",").filter(Boolean) : [],
|
|
62
|
+
[highlightSpanIdsRaw]
|
|
63
|
+
);
|
|
57
64
|
const listMode = React.useMemo(() => {
|
|
58
65
|
const value = searchParams.get(domains_traces_traceFilters.TRACE_LIST_MODE_PARAM);
|
|
59
66
|
return value && domains_traces_traceFilters.TRACE_LIST_MODE_VALUES.has(value) ? value : "traces";
|
|
@@ -89,6 +96,7 @@ function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
|
89
96
|
}
|
|
90
97
|
next.delete(TAB_PARAM);
|
|
91
98
|
next.delete(SCORE_ID_PARAM);
|
|
99
|
+
next.delete(HIGHLIGHT_SPAN_IDS_PARAM);
|
|
92
100
|
return next;
|
|
93
101
|
},
|
|
94
102
|
{ replace: true }
|
|
@@ -180,6 +188,27 @@ function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
|
180
188
|
},
|
|
181
189
|
[searchParams, setSearchParams]
|
|
182
190
|
);
|
|
191
|
+
const handleHighlightSpans = React.useCallback(
|
|
192
|
+
(spanIds) => {
|
|
193
|
+
setSearchParams(
|
|
194
|
+
(prev) => {
|
|
195
|
+
const next = new URLSearchParams(prev);
|
|
196
|
+
const [firstSpanId] = spanIds;
|
|
197
|
+
if (!firstSpanId) {
|
|
198
|
+
next.delete(HIGHLIGHT_SPAN_IDS_PARAM);
|
|
199
|
+
return next;
|
|
200
|
+
}
|
|
201
|
+
next.set(HIGHLIGHT_SPAN_IDS_PARAM, spanIds.join(","));
|
|
202
|
+
next.set(SPAN_ID_PARAM, firstSpanId);
|
|
203
|
+
next.delete(TAB_PARAM);
|
|
204
|
+
next.delete(SCORE_ID_PARAM);
|
|
205
|
+
return next;
|
|
206
|
+
},
|
|
207
|
+
{ replace: true }
|
|
208
|
+
);
|
|
209
|
+
},
|
|
210
|
+
[setSearchParams]
|
|
211
|
+
);
|
|
183
212
|
const applyFilterTokens = React.useCallback(
|
|
184
213
|
(tokens) => {
|
|
185
214
|
setSearchParams(
|
|
@@ -287,6 +316,7 @@ function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
|
287
316
|
anchorSpanIdParam,
|
|
288
317
|
spanTabParam,
|
|
289
318
|
scoreIdParam,
|
|
319
|
+
highlightSpanIdsParam,
|
|
290
320
|
listMode,
|
|
291
321
|
selectedEntityOption,
|
|
292
322
|
selectedStatus,
|
|
@@ -298,6 +328,7 @@ function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
|
298
328
|
handleSpanTabChange,
|
|
299
329
|
handleSpanChangeWithTab,
|
|
300
330
|
handleScoreChange,
|
|
331
|
+
handleHighlightSpans,
|
|
301
332
|
handleListModeChange,
|
|
302
333
|
handleFilterTokensChange,
|
|
303
334
|
handleDateChange,
|