@truefoundry/trueforge-ui 0.3.0-rc.0 → 0.3.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +21 -6
  3. package/dist/AgentCodeSnippets-637HQUXD.js +74 -0
  4. package/dist/AgentCodeSnippets-637HQUXD.js.map +1 -0
  5. package/dist/AgentMetricChart-4FYECRIN.js +15 -0
  6. package/dist/AgentOverview-3FJ5EE6N.js +167 -0
  7. package/dist/AgentOverview-3FJ5EE6N.js.map +1 -0
  8. package/dist/AgentSessionEventTimeline-VELWUYPR.js +88 -0
  9. package/dist/AgentSessionEventTimeline-VELWUYPR.js.map +1 -0
  10. package/dist/AgentSessionEventTimelineChart-CUKETHWM.js +591 -0
  11. package/dist/AgentSessionEventTimelineChart-CUKETHWM.js.map +1 -0
  12. package/dist/AgentSessions-T7MOGOTZ.js +275 -0
  13. package/dist/AgentSessions-T7MOGOTZ.js.map +1 -0
  14. package/dist/{ConnectorSettings-FXQ5B7TN.js → ConnectorSettings-ESZ2RSRG.js} +19 -21
  15. package/dist/ConnectorSettings-ESZ2RSRG.js.map +1 -0
  16. package/dist/{DockLayout-LBNQJZNF.js → DockLayout-NLFNU6F2.js} +11 -10
  17. package/dist/{DockLayout-LBNQJZNF.js.map → DockLayout-NLFNU6F2.js.map} +1 -1
  18. package/dist/DrawerLayout-PR3SMKA6.js +184 -0
  19. package/dist/DrawerLayout-PR3SMKA6.js.map +1 -0
  20. package/dist/{ModelSettings-4L4LOQJA.js → ModelSettings-LYW64IRR.js} +20 -24
  21. package/dist/ModelSettings-LYW64IRR.js.map +1 -0
  22. package/dist/OpenUiFenceBlock-JEPNPJ7U.js +9 -0
  23. package/dist/OpenUiFenceBlock-JEPNPJ7U.js.map +1 -0
  24. package/dist/{SandboxSettings-Z4ZG6ZX4.js → SandboxSettings-DF64LHYI.js} +16 -18
  25. package/dist/SandboxSettings-DF64LHYI.js.map +1 -0
  26. package/dist/SchedulesPage-WQU4H25F.js +15 -0
  27. package/dist/SchedulesPage-WQU4H25F.js.map +1 -0
  28. package/dist/{SettingsBuilder-WF7ZWKQW.js → SettingsBuilder-LSXK2VN4.js} +26 -22
  29. package/dist/SettingsBuilder-LSXK2VN4.js.map +1 -0
  30. package/dist/ShellRouteSync-B22IAV2W.js +255 -0
  31. package/dist/ShellRouteSync-B22IAV2W.js.map +1 -0
  32. package/dist/SidebarLayout-XW6LXB7R.js +309 -0
  33. package/dist/SidebarLayout-XW6LXB7R.js.map +1 -0
  34. package/dist/{SkillSettings-3EPPC3IT.js → SkillSettings-64VXUO6J.js} +14 -15
  35. package/dist/SkillSettings-64VXUO6J.js.map +1 -0
  36. package/dist/{TrueForgeUIWithRouter-NZ4FGPE4.js → TrueForgeUIWithRouter-3TSLV22Q.js} +14 -10
  37. package/dist/{TrueForgeUIWithRouter-NZ4FGPE4.js.map → TrueForgeUIWithRouter-3TSLV22Q.js.map} +1 -1
  38. package/dist/{WidgetLayout-3QZ357FL.js → WidgetLayout-Q5P7PYSJ.js} +17 -13
  39. package/dist/WidgetLayout-Q5P7PYSJ.js.map +1 -0
  40. package/dist/chunk-5I2COQNL.js +119 -0
  41. package/dist/chunk-5I2COQNL.js.map +1 -0
  42. package/dist/chunk-BWHOZFIV.js +587 -0
  43. package/dist/chunk-BWHOZFIV.js.map +1 -0
  44. package/dist/chunk-DJUFS2BW.js +27 -0
  45. package/dist/chunk-DJUFS2BW.js.map +1 -0
  46. package/dist/chunk-FQK2XVVE.js +880 -0
  47. package/dist/chunk-FQK2XVVE.js.map +1 -0
  48. package/dist/chunk-G2TUAD65.js +555 -0
  49. package/dist/chunk-G2TUAD65.js.map +1 -0
  50. package/dist/chunk-GXYAOAK5.js +1357 -0
  51. package/dist/chunk-GXYAOAK5.js.map +1 -0
  52. package/dist/{chunk-FJ5RSSMA.js → chunk-H6WTY5PM.js} +196 -32
  53. package/dist/chunk-H6WTY5PM.js.map +1 -0
  54. package/dist/chunk-I3AL54MA.js +180 -0
  55. package/dist/chunk-I3AL54MA.js.map +1 -0
  56. package/dist/{chunk-WREC7D54.js → chunk-LGRHHFHN.js} +120 -32
  57. package/dist/chunk-LGRHHFHN.js.map +1 -0
  58. package/dist/chunk-NHHSZB5N.js +74 -0
  59. package/dist/chunk-NHHSZB5N.js.map +1 -0
  60. package/dist/{chunk-CFLVPV5M.js → chunk-QC42MLAK.js} +7 -2
  61. package/dist/chunk-QC42MLAK.js.map +1 -0
  62. package/dist/chunk-RDXBMTP5.js +32 -0
  63. package/dist/chunk-RDXBMTP5.js.map +1 -0
  64. package/dist/chunk-SFZNIO6U.js +18 -0
  65. package/dist/chunk-SFZNIO6U.js.map +1 -0
  66. package/dist/chunk-SQGT43Z3.js +200 -0
  67. package/dist/chunk-SQGT43Z3.js.map +1 -0
  68. package/dist/{chunk-B52K63AW.js → chunk-VJ7BZGAF.js} +2 -2
  69. package/dist/chunk-VUMGMGKZ.js +252 -0
  70. package/dist/chunk-VUMGMGKZ.js.map +1 -0
  71. package/dist/chunk-Y6S4OVVQ.js +16 -0
  72. package/dist/chunk-Y6S4OVVQ.js.map +1 -0
  73. package/dist/chunk-YQO2NMJ3.js +13341 -0
  74. package/dist/chunk-YQO2NMJ3.js.map +1 -0
  75. package/dist/{createTrueFoundryServer-B72bU-J7.d.ts → createTrueFoundryServer-CmZnBh_l.d.ts} +15 -4
  76. package/dist/index.d.ts +1024 -24
  77. package/dist/index.js +691 -67
  78. package/dist/index.js.map +1 -1
  79. package/dist/plugins/trueforge-agent-server-adapter/index.d.ts +552 -8
  80. package/dist/plugins/trueforge-agent-server-adapter/index.js +504 -188
  81. package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -1
  82. package/dist/styles.css +1 -1
  83. package/package.json +9 -8
  84. package/dist/ConnectorSettings-FXQ5B7TN.js.map +0 -1
  85. package/dist/DrawerLayout-Q6UK4VMI.js +0 -159
  86. package/dist/DrawerLayout-Q6UK4VMI.js.map +0 -1
  87. package/dist/ModelSettings-4L4LOQJA.js.map +0 -1
  88. package/dist/OpenUiFenceBlock-6UIIAVBX.js +0 -9
  89. package/dist/SandboxSettings-Z4ZG6ZX4.js.map +0 -1
  90. package/dist/SettingsBuilder-WF7ZWKQW.js.map +0 -1
  91. package/dist/ShellRouteSync-T4WLY57Q.js +0 -172
  92. package/dist/ShellRouteSync-T4WLY57Q.js.map +0 -1
  93. package/dist/SidebarLayout-3WZ3CVON.js +0 -256
  94. package/dist/SidebarLayout-3WZ3CVON.js.map +0 -1
  95. package/dist/SkillSettings-3EPPC3IT.js.map +0 -1
  96. package/dist/WidgetLayout-3QZ357FL.js.map +0 -1
  97. package/dist/chunk-36KOSC2J.js +0 -567
  98. package/dist/chunk-36KOSC2J.js.map +0 -1
  99. package/dist/chunk-3S6KHO7H.js +0 -2121
  100. package/dist/chunk-3S6KHO7H.js.map +0 -1
  101. package/dist/chunk-7L63OK3O.js +0 -91
  102. package/dist/chunk-7L63OK3O.js.map +0 -1
  103. package/dist/chunk-CFLVPV5M.js.map +0 -1
  104. package/dist/chunk-FJ5RSSMA.js.map +0 -1
  105. package/dist/chunk-OSV3HI5M.js +0 -6891
  106. package/dist/chunk-OSV3HI5M.js.map +0 -1
  107. package/dist/chunk-WQULF5AT.js +0 -460
  108. package/dist/chunk-WQULF5AT.js.map +0 -1
  109. package/dist/chunk-WREC7D54.js.map +0 -1
  110. package/dist/chunk-YIWUG6MC.js +0 -101
  111. package/dist/chunk-YIWUG6MC.js.map +0 -1
  112. package/dist/chunk-ZXOADNF3.js +0 -75
  113. package/dist/chunk-ZXOADNF3.js.map +0 -1
  114. /package/dist/{OpenUiFenceBlock-6UIIAVBX.js.map → AgentMetricChart-4FYECRIN.js.map} +0 -0
  115. /package/dist/{chunk-B52K63AW.js.map → chunk-VJ7BZGAF.js.map} +0 -0
@@ -0,0 +1,591 @@
1
+ "use client";
2
+ import {
3
+ LightTooltip,
4
+ MAIN_THREAD_ID,
5
+ SESSION_EVENT_TOOLTIP_HIDE_DURATION,
6
+ TIMELINE_TYPE,
7
+ buildTimelineAxisTicks,
8
+ formatCostUsd,
9
+ formatDurationMs,
10
+ formatTimelineAxisDuration,
11
+ formatTimelineDuration,
12
+ formatTokenCount,
13
+ getActiveTimelineMs,
14
+ getSessionEventColor,
15
+ getSessionEventHoverColor,
16
+ getSessionEventLabel,
17
+ getSessionEventTooltipHeading,
18
+ getSubAgentHoverGroups,
19
+ getSubAgentLanes,
20
+ getTimelineHoverTargetId,
21
+ getTimelineLayout,
22
+ getTimelineRange,
23
+ groupCoincidentTimelineMarkers,
24
+ groupOverlappingToolCalls,
25
+ pickLongestNonOverlappingSegments,
26
+ roundDurationMsToSecond,
27
+ useThemeMode
28
+ } from "./chunk-YQO2NMJ3.js";
29
+ import "./chunk-NHHSZB5N.js";
30
+ import "./chunk-H6WTY5PM.js";
31
+ import "./chunk-FQK2XVVE.js";
32
+ import "./chunk-M6MHLVSO.js";
33
+ import "./chunk-VJ7BZGAF.js";
34
+ import "./chunk-BWHOZFIV.js";
35
+
36
+ // src/atoms/agent-details/AgentSessionEventTimelineChart.tsx
37
+ import {
38
+ BarController,
39
+ BarElement,
40
+ Chart as ChartJS,
41
+ LinearScale,
42
+ PointElement,
43
+ ScatterController,
44
+ Tooltip
45
+ } from "chart.js";
46
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
47
+ import { Chart } from "react-chartjs-2";
48
+
49
+ // src/atoms/agent-details/AgentSessionTimelineTooltip.tsx
50
+ import { jsx, jsxs } from "react/jsx-runtime";
51
+ function hasSessionEventTooltip(segment) {
52
+ return segment.type !== "system" || segment.description.trim().length > 0;
53
+ }
54
+ function SessionEventTooltip({
55
+ segment,
56
+ subAgentLabel
57
+ }) {
58
+ const showDuration = !segment.isMarker && !SESSION_EVENT_TOOLTIP_HIDE_DURATION.has(segment.type);
59
+ const content = segment.description.trim();
60
+ if (segment.type === "system") {
61
+ if (content.length === 0) return null;
62
+ return /* @__PURE__ */ jsx("p", { className: "max-w-[25rem] whitespace-normal break-words text-xs font-medium text-text-primary", children: content });
63
+ }
64
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 max-w-[25rem] overflow-auto whitespace-normal break-words text-xs", children: [
65
+ subAgentLabel != null ? /* @__PURE__ */ jsx("div", { className: "border-b border-border py-1.5 text-text-secondary", children: `Sub-Agent: ${subAgentLabel}` }) : null,
66
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 pt-1", children: [
67
+ /* @__PURE__ */ jsx("span", { className: segment.type === "error" ? "font-medium text-failure-bg" : "font-medium text-text-secondary", children: getSessionEventTooltipHeading(segment.type) }),
68
+ showDuration ? /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) }) : null
69
+ ] }),
70
+ content.length > 0 ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 line-clamp-6 font-medium text-text-primary", children: content }) : null
71
+ ] });
72
+ }
73
+ function SessionTurnTooltip({
74
+ turn,
75
+ turnNumber,
76
+ durationMs,
77
+ segments
78
+ }) {
79
+ const isDark = useThemeMode() === "dark";
80
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-72 max-w-full overflow-auto text-xs text-text-primary", children: [
81
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
82
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: `Turn ${turnNumber}` }),
83
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatDurationMs(durationMs) })
84
+ ] }),
85
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-col text-text-secondary", children: [
86
+ turn.totalCostInUsd != null ? /* @__PURE__ */ jsx("span", { children: `Cost: ${formatCostUsd(turn.totalCostInUsd)}` }) : null,
87
+ turn.totalTokens != null ? /* @__PURE__ */ jsx("span", { children: `Tokens: ${formatTokenCount(turn.totalTokens)}` }) : null
88
+ ] }),
89
+ /* @__PURE__ */ jsx("div", { className: "mt-2 border-t border-border pt-2", children: segments.map((segment) => {
90
+ const label = segment.type === "tool_call" || segment.type === "sub_agent" ? segment.description : segment.title;
91
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
92
+ /* @__PURE__ */ jsx(
93
+ "span",
94
+ {
95
+ className: "size-1.5 shrink-0 rounded-full",
96
+ style: { backgroundColor: getSessionEventColor(segment.type, isDark) }
97
+ }
98
+ ),
99
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: label }),
100
+ segment.isMarker ? null : /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) })
101
+ ] }, segment.id);
102
+ }) })
103
+ ] });
104
+ }
105
+ function SessionToolCallGroupTooltip({ group }) {
106
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-80 max-w-full overflow-auto text-xs", children: [
107
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
108
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-text-secondary", children: "Tool calls" }),
109
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatTimelineDuration(group.endMs - group.startMs) })
110
+ ] }),
111
+ /* @__PURE__ */ jsx("div", { className: "mt-1.5 border-t border-border pt-1.5", children: group.segments.map((segment) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 py-0.5", children: [
112
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-text-primary", children: segment.description || segment.title }),
113
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) })
114
+ ] }, segment.id)) })
115
+ ] });
116
+ }
117
+ function SessionMarkerGroupTooltip({ group }) {
118
+ const isDark = useThemeMode() === "dark";
119
+ const singleSegment = group.segments.length === 1 ? group.segments[0] : void 0;
120
+ if (singleSegment != null) return /* @__PURE__ */ jsx(SessionEventTooltip, { segment: singleSegment });
121
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-80 max-w-full overflow-auto text-xs", children: [
122
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-text-secondary", children: "Events" }),
123
+ /* @__PURE__ */ jsx("div", { className: "mt-1.5 border-t border-border pt-1.5", children: group.segments.map((segment) => /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-1.5 py-0.5", children: [
124
+ /* @__PURE__ */ jsx(
125
+ "span",
126
+ {
127
+ className: "mt-1 size-1.5 shrink-0 rounded-full",
128
+ style: { backgroundColor: getSessionEventColor(segment.type, isDark) }
129
+ }
130
+ ),
131
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
132
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-text-secondary", children: getSessionEventTooltipHeading(segment.type) }),
133
+ /* @__PURE__ */ jsx("div", { className: "wrap-break-word font-medium text-text-primary", children: segment.description.trim() || segment.title })
134
+ ] })
135
+ ] }, segment.id)) })
136
+ ] });
137
+ }
138
+ function SessionSubAgentGroupTooltip({ group }) {
139
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-80 max-w-full overflow-auto text-xs", children: [
140
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
141
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-text-secondary", children: "Sub-agents" }),
142
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatTimelineDuration(group.endMs - group.startMs) })
143
+ ] }),
144
+ /* @__PURE__ */ jsx("div", { className: "mt-1.5 border-t border-border pt-1.5", children: group.segments.map((segment, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 py-0.5", children: [
145
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-text-primary", children: group.segments.length === 1 ? segment.description || segment.title : `Sub-agent ${index + 1}: ${segment.description || segment.title}` }),
146
+ group.segments.length === 1 ? null : /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) })
147
+ ] }, segment.id)) })
148
+ ] });
149
+ }
150
+
151
+ // src/atoms/agent-details/AgentSessionEventTimelineChart.tsx
152
+ import { jsx as jsx2 } from "react/jsx-runtime";
153
+ ChartJS.register(BarController, BarElement, LinearScale, PointElement, ScatterController, Tooltip);
154
+ var MARKER_PX = 4;
155
+ var TURN_GAP_PX = 12;
156
+ var END_PAD_PX = 12;
157
+ var BAR_RADIUS_PX = 2;
158
+ var ROW_GAP_PX = 8;
159
+ var MARKER_EVENT_GAP_PX = 2;
160
+ var OVERHEAD_PX = 30;
161
+ function rowCenters({ heights, gaps }) {
162
+ const centers = [];
163
+ let offset = 0;
164
+ heights.forEach((height, index) => {
165
+ centers.push(offset + height / 2);
166
+ offset += height + (gaps[index] ?? 0);
167
+ });
168
+ return { centers, band: offset };
169
+ }
170
+ function eventOrder(type) {
171
+ if (type === "sub_agent") return 0;
172
+ if (type === "model") return 2;
173
+ return 1;
174
+ }
175
+ function turnBarLabelPoint(element) {
176
+ if (!("x" in element) || !("y" in element) || !("base" in element)) return null;
177
+ const { x, y, base } = element;
178
+ if (typeof x !== "number" || typeof y !== "number" || typeof base !== "number") return null;
179
+ return { x: (x + base) / 2, y };
180
+ }
181
+ function AgentSessionEventTimelineChart({
182
+ turns,
183
+ segments,
184
+ hiddenTypes,
185
+ onSelectTurn
186
+ }) {
187
+ const mode = useThemeMode();
188
+ const isDark = mode === "dark";
189
+ const axis = isDark ? "#8c8c92" : "#71717a";
190
+ const grid = isDark ? "#27272a" : "#e4e4e7";
191
+ const turnFill = isDark ? "#3f3f46" : "#E0ECFD";
192
+ const turnHover = isDark ? "#52525b" : "#E0ECFD";
193
+ const wrapperRef = useRef(null);
194
+ const [widthPx, setWidthPx] = useState(0);
195
+ const [overheadPx, setOverheadPx] = useState(OVERHEAD_PX);
196
+ const [tooltipTarget, setTooltipTarget] = useState(null);
197
+ const [tooltipAnchor, setTooltipAnchor] = useState(null);
198
+ const tooltipIdRef = useRef("");
199
+ const tooltipCursorXRef = useRef(null);
200
+ useEffect(() => {
201
+ const wrapper = wrapperRef.current;
202
+ if (wrapper == null) return void 0;
203
+ const update = () => setWidthPx(wrapper.clientWidth);
204
+ update();
205
+ const observer = new ResizeObserver(update);
206
+ observer.observe(wrapper);
207
+ return () => observer.disconnect();
208
+ }, []);
209
+ const refreshTooltipAnchor = useCallback(() => {
210
+ const wrapper = wrapperRef.current;
211
+ const left = tooltipCursorXRef.current;
212
+ if (wrapper == null || left == null) return;
213
+ setTooltipAnchor({ left, top: wrapper.getBoundingClientRect().bottom });
214
+ }, []);
215
+ const clearTooltip = useCallback(() => {
216
+ tooltipIdRef.current = "";
217
+ tooltipCursorXRef.current = null;
218
+ setTooltipTarget(null);
219
+ setTooltipAnchor(null);
220
+ }, []);
221
+ useEffect(() => {
222
+ if (tooltipTarget == null) return void 0;
223
+ refreshTooltipAnchor();
224
+ window.addEventListener("scroll", refreshTooltipAnchor, true);
225
+ window.addEventListener("resize", refreshTooltipAnchor);
226
+ return () => {
227
+ window.removeEventListener("scroll", refreshTooltipAnchor, true);
228
+ window.removeEventListener("resize", refreshTooltipAnchor);
229
+ };
230
+ }, [refreshTooltipAnchor, tooltipTarget]);
231
+ const segmentTotalMs = Math.max(0, ...segments.map((segment) => segment.endMs));
232
+ const activeTotalMs = Math.max(1, roundDurationMsToSecond(segmentTotalMs));
233
+ const { turnStartsMs, turnOrdinals, turnRanges } = useMemo(() => getTimelineLayout(segments), [segments]);
234
+ const activeTurnStartsMs = useMemo(
235
+ () => turnStartsMs.filter((startMs, index) => index === 0 || startMs <= activeTotalMs),
236
+ [activeTotalMs, turnStartsMs]
237
+ );
238
+ const gapCount = Math.max(0, activeTurnStartsMs.length - 1);
239
+ const scaleWidthPx = Math.max(widthPx / 2, widthPx - TURN_GAP_PX * gapCount - END_PAD_PX);
240
+ const msPerPx = activeTotalMs / Math.max(1, scaleWidthPx);
241
+ const turnGapMs = TURN_GAP_PX * msPerPx;
242
+ const totalMs = activeTotalMs + gapCount * turnGapMs;
243
+ const timelineMaxMs = totalMs + END_PAD_PX * msPerPx;
244
+ const timelineGaps = useMemo(
245
+ () => activeTurnStartsMs.slice(1).map((startMs, index) => ({ startMs: startMs + index * turnGapMs, endMs: startMs + (index + 1) * turnGapMs })),
246
+ [activeTurnStartsMs, turnGapMs]
247
+ );
248
+ const visibleSegments = useMemo(
249
+ () => segments.flatMap((segment) => {
250
+ if (hiddenTypes.has(segment.type)) return [];
251
+ const shiftMs = (turnOrdinals.get(segment.turnIndex) ?? 0) * turnGapMs;
252
+ return [{ ...segment, startMs: segment.startMs + shiftMs, endMs: segment.endMs + shiftMs }];
253
+ }),
254
+ [hiddenTypes, segments, turnGapMs, turnOrdinals]
255
+ );
256
+ const markerGroups = useMemo(() => groupCoincidentTimelineMarkers(visibleSegments), [visibleSegments]);
257
+ const durationSegments = useMemo(() => visibleSegments.filter((segment) => !segment.isMarker), [visibleSegments]);
258
+ const turnBars = useMemo(
259
+ () => turnRanges.length < 2 ? [] : turnRanges.map((range) => ({
260
+ ...range,
261
+ id: `turn-${range.turnIndex}`,
262
+ durationMs: range.endMs - range.startMs,
263
+ startMs: range.startMs + range.ordinal * turnGapMs,
264
+ endMs: range.endMs + range.ordinal * turnGapMs + MARKER_PX * msPerPx
265
+ })),
266
+ [msPerPx, turnGapMs, turnRanges]
267
+ );
268
+ const subAgentSegments = useMemo(
269
+ () => durationSegments.filter((segment) => segment.type === "sub_agent"),
270
+ [durationSegments]
271
+ );
272
+ const mainSubAgents = useMemo(() => pickLongestNonOverlappingSegments(subAgentSegments), [subAgentSegments]);
273
+ const subAgentGroups = useMemo(
274
+ () => getSubAgentHoverGroups({ bars: mainSubAgents, subAgentSegments }),
275
+ [mainSubAgents, subAgentSegments]
276
+ );
277
+ const subAgentLanes = useMemo(
278
+ () => getSubAgentLanes({ subAgentSegments, threadSegments: durationSegments, minWidthMs: MARKER_PX * msPerPx }),
279
+ [durationSegments, msPerPx, subAgentSegments]
280
+ );
281
+ const mainCandidates = useMemo(() => {
282
+ const kept = new Set(mainSubAgents.map((segment) => segment.id));
283
+ return durationSegments.filter(
284
+ (segment) => segment.threadId === MAIN_THREAD_ID || segment.type === "sub_agent" && kept.has(segment.id)
285
+ );
286
+ }, [durationSegments, mainSubAgents]);
287
+ const toolCallGroups = useMemo(
288
+ () => groupOverlappingToolCalls(mainCandidates.filter((segment) => segment.type === "tool_call")).filter(
289
+ (group) => group.segments.length > 1
290
+ ),
291
+ [mainCandidates]
292
+ );
293
+ const groupedIds = useMemo(
294
+ () => new Set(toolCallGroups.flatMap((group) => group.segments.map((segment) => segment.id))),
295
+ [toolCallGroups]
296
+ );
297
+ const mainEventSegments = useMemo(
298
+ () => mainCandidates.filter((segment) => !groupedIds.has(segment.id)),
299
+ [groupedIds, mainCandidates]
300
+ );
301
+ const laneCount = subAgentLanes.reduce((count, lane) => Math.max(count, lane.lane + 1), 0);
302
+ const hasTurnRow = turnBars.length > 0;
303
+ const markerRow = hasTurnRow ? 1 : 0;
304
+ const eventRow = markerRow + 1;
305
+ const { centers, band } = useMemo(() => {
306
+ const heights = [...hasTurnRow ? [16] : [], 10, 28, ...Array.from({ length: laneCount }, () => 12)];
307
+ const gaps = heights.map((_, index) => {
308
+ if (index === heights.length - 1) return 0;
309
+ return index === markerRow ? MARKER_EVENT_GAP_PX : ROW_GAP_PX;
310
+ });
311
+ return rowCenters({ heights, gaps });
312
+ }, [hasTurnRow, laneCount, markerRow]);
313
+ const chartTargets = useMemo(
314
+ () => [
315
+ ...turnBars.map((bar) => ({ type: TIMELINE_TYPE.turn, bar })),
316
+ ...markerGroups.map((group) => ({ type: TIMELINE_TYPE.markerGroup, group })),
317
+ ...subAgentLanes.flatMap(
318
+ (lane) => lane.segments.map((segment) => ({ type: TIMELINE_TYPE.event, segment }))
319
+ ),
320
+ ...mainEventSegments.map((segment) => {
321
+ if (segment.type !== "sub_agent") return { type: TIMELINE_TYPE.event, segment };
322
+ const group = subAgentGroups.find((candidate) => candidate.barId === segment.id);
323
+ return group == null ? { type: TIMELINE_TYPE.event, segment } : { type: TIMELINE_TYPE.subAgentGroup, group };
324
+ }),
325
+ ...toolCallGroups.map((group) => ({ type: TIMELINE_TYPE.toolCallGroup, group }))
326
+ ],
327
+ [mainEventSegments, markerGroups, subAgentGroups, subAgentLanes, toolCallGroups, turnBars]
328
+ );
329
+ const barDataset = ({
330
+ label,
331
+ range,
332
+ y,
333
+ color,
334
+ hover,
335
+ thickness,
336
+ order = 1,
337
+ inflateAmount
338
+ }) => ({
339
+ label,
340
+ data: [{ x: getTimelineRange(range), y }],
341
+ backgroundColor: color,
342
+ hoverBackgroundColor: hover,
343
+ hoverBorderColor: hover,
344
+ borderWidth: 0,
345
+ borderSkipped: false,
346
+ borderRadius: BAR_RADIUS_PX,
347
+ minBarLength: MARKER_PX,
348
+ barThickness: thickness,
349
+ grouped: false,
350
+ order,
351
+ ...inflateAmount == null ? {} : { inflateAmount }
352
+ });
353
+ const chartData = useMemo(
354
+ () => ({
355
+ datasets: [
356
+ ...turnBars.map(
357
+ (bar) => barDataset({
358
+ label: `Turn ${bar.ordinal + 1}`,
359
+ range: bar,
360
+ y: centers[0] ?? 0,
361
+ color: turnFill,
362
+ hover: turnHover,
363
+ thickness: 16
364
+ })
365
+ ),
366
+ ...markerGroups.map((group) => {
367
+ const type = group.segments.length === 1 ? group.segments[0]?.type : "system";
368
+ const color = getSessionEventColor(type ?? "system", isDark);
369
+ return {
370
+ type: "scatter",
371
+ label: group.segments.map((segment) => getSessionEventLabel(segment.type)).join(", "),
372
+ data: [{ x: group.startMs, y: centers[markerRow] ?? 0 }],
373
+ pointStyle: "rectRot",
374
+ pointRadius: 5,
375
+ pointHoverRadius: 6,
376
+ clip: false,
377
+ backgroundColor: color,
378
+ hoverBackgroundColor: getSessionEventHoverColor(type ?? "system", isDark),
379
+ borderWidth: 0,
380
+ order: 0
381
+ };
382
+ }),
383
+ ...subAgentLanes.flatMap(
384
+ (lane) => lane.segments.map(
385
+ (segment) => barDataset({
386
+ label: `${getSessionEventLabel(segment.type)}: ${segment.title}`,
387
+ range: segment,
388
+ y: centers[eventRow + 1 + lane.lane] ?? 0,
389
+ color: getSessionEventColor(segment.type, isDark),
390
+ hover: getSessionEventHoverColor(segment.type, isDark),
391
+ thickness: 12,
392
+ order: eventOrder(segment.type),
393
+ inflateAmount: 0.5
394
+ })
395
+ )
396
+ ),
397
+ ...mainEventSegments.map(
398
+ (segment) => barDataset({
399
+ label: `${getSessionEventLabel(segment.type)}: ${segment.title}`,
400
+ range: segment,
401
+ y: centers[eventRow] ?? 0,
402
+ color: getSessionEventColor(segment.type, isDark),
403
+ hover: getSessionEventHoverColor(segment.type, isDark),
404
+ thickness: 28,
405
+ order: eventOrder(segment.type),
406
+ inflateAmount: 0.5
407
+ })
408
+ ),
409
+ ...toolCallGroups.map(
410
+ (group) => barDataset({
411
+ label: "Parallel tool calls",
412
+ range: group,
413
+ y: centers[eventRow] ?? 0,
414
+ color: getSessionEventColor("tool_call", isDark),
415
+ hover: getSessionEventHoverColor("tool_call", isDark),
416
+ thickness: 28,
417
+ order: eventOrder("tool_call"),
418
+ inflateAmount: 0.5
419
+ })
420
+ )
421
+ ]
422
+ }),
423
+ [
424
+ centers,
425
+ eventRow,
426
+ isDark,
427
+ mainEventSegments,
428
+ markerGroups,
429
+ markerRow,
430
+ subAgentLanes,
431
+ toolCallGroups,
432
+ turnBars,
433
+ turnFill,
434
+ turnHover
435
+ ]
436
+ );
437
+ const turnLabelPlugin = useMemo(
438
+ () => ({
439
+ id: "turnLabels",
440
+ afterDatasetsDraw(chart) {
441
+ const { ctx } = chart;
442
+ ctx.save();
443
+ ctx.fillStyle = axis;
444
+ ctx.font = "500 10px sans-serif";
445
+ ctx.textAlign = "center";
446
+ ctx.textBaseline = "middle";
447
+ turnBars.forEach((bar, index) => {
448
+ const element = chart.getDatasetMeta(index).data[0];
449
+ if (element == null) return;
450
+ const point = turnBarLabelPoint(element);
451
+ if (point) ctx.fillText(`T${bar.ordinal + 1}`, point.x, point.y);
452
+ });
453
+ ctx.restore();
454
+ }
455
+ }),
456
+ [axis, turnBars]
457
+ );
458
+ const overheadPlugin = useMemo(
459
+ () => ({
460
+ id: "timelineOverhead",
461
+ afterLayout(chart) {
462
+ const next = Math.round(chart.height - chart.chartArea.height);
463
+ setOverheadPx((previous) => previous === next ? previous : next);
464
+ }
465
+ }),
466
+ []
467
+ );
468
+ const chartOptions = useMemo(
469
+ () => ({
470
+ indexAxis: "y",
471
+ animation: false,
472
+ responsive: true,
473
+ maintainAspectRatio: false,
474
+ onClick: (_event, elements) => {
475
+ const target = chartTargets[elements[0]?.datasetIndex ?? -1];
476
+ if (target?.type === TIMELINE_TYPE.turn) onSelectTurn?.(target.bar.turnIndex);
477
+ },
478
+ onHover: (event, elements) => {
479
+ const hoveredDatasetIndex = elements[0]?.datasetIndex;
480
+ const next = hoveredDatasetIndex == null ? null : chartTargets[hoveredDatasetIndex] ?? null;
481
+ const native = event.native;
482
+ if (native?.target instanceof HTMLElement) {
483
+ native.target.style.cursor = next?.type === TIMELINE_TYPE.turn ? "pointer" : "default";
484
+ }
485
+ if (native instanceof MouseEvent) {
486
+ tooltipCursorXRef.current = native.clientX;
487
+ refreshTooltipAnchor();
488
+ }
489
+ if (hoveredDatasetIndex == null) return;
490
+ const nextId = getTimelineHoverTargetId(next);
491
+ if (tooltipIdRef.current === nextId) return;
492
+ tooltipIdRef.current = nextId;
493
+ setTooltipTarget(next);
494
+ if (next == null) {
495
+ setTooltipAnchor(null);
496
+ return;
497
+ }
498
+ refreshTooltipAnchor();
499
+ },
500
+ plugins: { legend: { display: false }, tooltip: { enabled: false } },
501
+ scales: {
502
+ x: {
503
+ type: "linear",
504
+ position: "top",
505
+ min: 0,
506
+ max: timelineMaxMs,
507
+ grid: { color: grid },
508
+ border: { color: grid },
509
+ afterBuildTicks: (scale) => {
510
+ scale.ticks = buildTimelineAxisTicks({ activeTotalMs, turnStartsMs: activeTurnStartsMs, turnGapMs });
511
+ },
512
+ ticks: {
513
+ color: axis,
514
+ autoSkip: false,
515
+ includeBounds: false,
516
+ maxTicksLimit: 9,
517
+ callback: (value) => {
518
+ const timelineMs = Number(value);
519
+ return timelineMs <= totalMs ? formatTimelineAxisDuration(getActiveTimelineMs(timelineMs, timelineGaps)) : "";
520
+ }
521
+ }
522
+ },
523
+ y: {
524
+ type: "linear",
525
+ display: false,
526
+ reverse: true,
527
+ offset: false,
528
+ min: 0,
529
+ max: band,
530
+ grid: { display: false }
531
+ }
532
+ }
533
+ }),
534
+ [
535
+ activeTotalMs,
536
+ activeTurnStartsMs,
537
+ axis,
538
+ band,
539
+ chartTargets,
540
+ grid,
541
+ onSelectTurn,
542
+ refreshTooltipAnchor,
543
+ timelineGaps,
544
+ timelineMaxMs,
545
+ totalMs,
546
+ turnGapMs
547
+ ]
548
+ );
549
+ const tooltipTurn = tooltipTarget?.type === TIMELINE_TYPE.turn ? turns[tooltipTarget.bar.turnIndex] : void 0;
550
+ const containerStyle = useMemo(() => ({ height: band + overheadPx }), [band, overheadPx]);
551
+ const tooltipContent = tooltipTarget?.type === TIMELINE_TYPE.turn && tooltipTurn != null ? /* @__PURE__ */ jsx2(
552
+ SessionTurnTooltip,
553
+ {
554
+ turn: tooltipTurn,
555
+ turnNumber: tooltipTarget.bar.ordinal + 1,
556
+ durationMs: tooltipTarget.bar.durationMs,
557
+ segments: segments.filter((segment) => segment.turnIndex === tooltipTarget.bar.turnIndex)
558
+ }
559
+ ) : tooltipTarget?.type === TIMELINE_TYPE.event && hasSessionEventTooltip(tooltipTarget.segment) ? /* @__PURE__ */ jsx2(
560
+ SessionEventTooltip,
561
+ {
562
+ segment: tooltipTarget.segment,
563
+ subAgentLabel: tooltipTarget.segment.type === "sub_agent" ? void 0 : subAgentLanes.find((lane) => lane.threadId === tooltipTarget.segment.threadId)?.track.description
564
+ }
565
+ ) : tooltipTarget?.type === TIMELINE_TYPE.toolCallGroup ? /* @__PURE__ */ jsx2(SessionToolCallGroupTooltip, { group: tooltipTarget.group }) : tooltipTarget?.type === TIMELINE_TYPE.markerGroup ? /* @__PURE__ */ jsx2(SessionMarkerGroupTooltip, { group: tooltipTarget.group }) : tooltipTarget?.type === TIMELINE_TYPE.subAgentGroup ? /* @__PURE__ */ jsx2(SessionSubAgentGroupTooltip, { group: tooltipTarget.group }) : null;
566
+ return /* @__PURE__ */ jsx2("div", { ref: wrapperRef, className: "w-full", "data-slot": "agent-session-event-timeline-chart", children: /* @__PURE__ */ jsx2(
567
+ LightTooltip,
568
+ {
569
+ title: tooltipContent,
570
+ side: "bottom",
571
+ triggerClassName: "block w-full",
572
+ followCursor: true,
573
+ open: tooltipTarget != null && tooltipContent != null,
574
+ className: "max-w-[min(25rem,calc(100vw-1rem))] whitespace-normal",
575
+ anchor: tooltipAnchor,
576
+ children: /* @__PURE__ */ jsx2("div", { className: "w-full", style: containerStyle, onMouseLeave: clearTooltip, children: /* @__PURE__ */ jsx2(
577
+ Chart,
578
+ {
579
+ type: "bar",
580
+ data: chartData,
581
+ options: chartOptions,
582
+ plugins: [turnLabelPlugin, overheadPlugin]
583
+ }
584
+ ) })
585
+ }
586
+ ) });
587
+ }
588
+ export {
589
+ AgentSessionEventTimelineChart
590
+ };
591
+ //# sourceMappingURL=AgentSessionEventTimelineChart-CUKETHWM.js.map