@truefoundry/trueforge-ui 0.2.4 → 0.3.0-rc.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +55 -23
  3. package/dist/AgentCodeSnippets-NZOFJE57.js +74 -0
  4. package/dist/AgentCodeSnippets-NZOFJE57.js.map +1 -0
  5. package/dist/AgentMetricChart-JPU3MFQ2.js +15 -0
  6. package/dist/AgentOverview-EEBHJC32.js +167 -0
  7. package/dist/AgentOverview-EEBHJC32.js.map +1 -0
  8. package/dist/AgentSessionEventTimeline-26Z6IB3B.js +79 -0
  9. package/dist/AgentSessionEventTimeline-26Z6IB3B.js.map +1 -0
  10. package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js +504 -0
  11. package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js.map +1 -0
  12. package/dist/AgentSessions-3MPDZASK.js +275 -0
  13. package/dist/AgentSessions-3MPDZASK.js.map +1 -0
  14. package/dist/{ConnectorSettings-ODC3LVHN.js → ConnectorSettings-MXFRWWR6.js} +8 -6
  15. package/dist/{ConnectorSettings-ODC3LVHN.js.map → ConnectorSettings-MXFRWWR6.js.map} +1 -1
  16. package/dist/{DockLayout-TEOWEMSV.js → DockLayout-LM4AFMFY.js} +12 -11
  17. package/dist/{DockLayout-TEOWEMSV.js.map → DockLayout-LM4AFMFY.js.map} +1 -1
  18. package/dist/DrawerLayout-JR3O6RRB.js +184 -0
  19. package/dist/DrawerLayout-JR3O6RRB.js.map +1 -0
  20. package/dist/{ModelSettings-CKQ5CVAC.js → ModelSettings-G7IFNC2Y.js} +8 -6
  21. package/dist/{ModelSettings-CKQ5CVAC.js.map → ModelSettings-G7IFNC2Y.js.map} +1 -1
  22. package/dist/OpenUiFenceBlock-2TV5TQ6P.js +9 -0
  23. package/dist/OpenUiFenceBlock-2TV5TQ6P.js.map +1 -0
  24. package/dist/{SandboxSettings-HSCPYUOC.js → SandboxSettings-NXAANMAX.js} +8 -6
  25. package/dist/{SandboxSettings-HSCPYUOC.js.map → SandboxSettings-NXAANMAX.js.map} +1 -1
  26. package/dist/SchedulesPage-KKLUDO6B.js +15 -0
  27. package/dist/SchedulesPage-KKLUDO6B.js.map +1 -0
  28. package/dist/{SettingsBuilder-MYWRFXRI.js → SettingsBuilder-L34IQ56S.js} +26 -22
  29. package/dist/SettingsBuilder-L34IQ56S.js.map +1 -0
  30. package/dist/{ShellRouteSync-T4WLY57Q.js → ShellRouteSync-CEBETGEJ.js} +95 -22
  31. package/dist/ShellRouteSync-CEBETGEJ.js.map +1 -0
  32. package/dist/SidebarLayout-A2HJPPHS.js +308 -0
  33. package/dist/SidebarLayout-A2HJPPHS.js.map +1 -0
  34. package/dist/{SkillSettings-ECH6SHZ7.js → SkillSettings-FGGMSUNO.js} +8 -6
  35. package/dist/{SkillSettings-ECH6SHZ7.js.map → SkillSettings-FGGMSUNO.js.map} +1 -1
  36. package/dist/{TrueForgeUIWithRouter-KMHPOKLN.js → TrueForgeUIWithRouter-MZVJWJMB.js} +15 -11
  37. package/dist/{TrueForgeUIWithRouter-KMHPOKLN.js.map → TrueForgeUIWithRouter-MZVJWJMB.js.map} +1 -1
  38. package/dist/{WidgetLayout-4ZIG3RXV.js → WidgetLayout-6QM45OQQ.js} +19 -15
  39. package/dist/WidgetLayout-6QM45OQQ.js.map +1 -0
  40. package/dist/chunk-2XPPBEYF.js +74 -0
  41. package/dist/chunk-2XPPBEYF.js.map +1 -0
  42. package/dist/chunk-6O6FWUDW.js +1376 -0
  43. package/dist/chunk-6O6FWUDW.js.map +1 -0
  44. package/dist/chunk-7Z2C2K7F.js +578 -0
  45. package/dist/chunk-7Z2C2K7F.js.map +1 -0
  46. package/dist/{chunk-FXCYLEFB.js → chunk-BSIPI3KV.js} +18 -2
  47. package/dist/chunk-BSIPI3KV.js.map +1 -0
  48. package/dist/{chunk-255AE5NI.js → chunk-CYHJDKFB.js} +118 -33
  49. package/dist/chunk-CYHJDKFB.js.map +1 -0
  50. package/dist/chunk-DJUFS2BW.js +27 -0
  51. package/dist/chunk-DJUFS2BW.js.map +1 -0
  52. package/dist/chunk-DMMZSZTN.js +252 -0
  53. package/dist/chunk-DMMZSZTN.js.map +1 -0
  54. package/dist/chunk-FJW36PD7.js +180 -0
  55. package/dist/chunk-FJW36PD7.js.map +1 -0
  56. package/dist/chunk-M62XY4NC.js +555 -0
  57. package/dist/chunk-M62XY4NC.js.map +1 -0
  58. package/dist/{chunk-JRRCAKEP.js → chunk-MPNSO7E6.js} +2 -2
  59. package/dist/{chunk-CFLVPV5M.js → chunk-QC42MLAK.js} +7 -2
  60. package/dist/chunk-QC42MLAK.js.map +1 -0
  61. package/dist/chunk-RDXBMTP5.js +32 -0
  62. package/dist/chunk-RDXBMTP5.js.map +1 -0
  63. package/dist/chunk-SFZNIO6U.js +18 -0
  64. package/dist/chunk-SFZNIO6U.js.map +1 -0
  65. package/dist/chunk-U7KURWW3.js +840 -0
  66. package/dist/chunk-U7KURWW3.js.map +1 -0
  67. package/dist/chunk-WVE3F724.js +200 -0
  68. package/dist/chunk-WVE3F724.js.map +1 -0
  69. package/dist/{chunk-IFLE7OMM.js → chunk-XAU7QFG3.js} +180 -38
  70. package/dist/chunk-XAU7QFG3.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-ZKNUMH2M.js +119 -0
  74. package/dist/chunk-ZKNUMH2M.js.map +1 -0
  75. package/dist/chunk-ZTMKFZQ3.js +13214 -0
  76. package/dist/chunk-ZTMKFZQ3.js.map +1 -0
  77. package/dist/{createTrueFoundryServer-B72bU-J7.d.ts → createTrueFoundryServer-CmZnBh_l.d.ts} +15 -4
  78. package/dist/index.d.ts +1199 -161
  79. package/dist/index.js +699 -69
  80. package/dist/index.js.map +1 -1
  81. package/dist/plugins/trueforge-agent-server-adapter/index.d.ts +552 -12
  82. package/dist/plugins/trueforge-agent-server-adapter/index.js +506 -188
  83. package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -1
  84. package/dist/styles.css +1 -1
  85. package/package.json +11 -10
  86. package/dist/DrawerLayout-2JOQ4EDZ.js +0 -159
  87. package/dist/DrawerLayout-2JOQ4EDZ.js.map +0 -1
  88. package/dist/OpenUiFenceBlock-3EFPY4JW.js +0 -9
  89. package/dist/SettingsBuilder-MYWRFXRI.js.map +0 -1
  90. package/dist/ShellRouteSync-T4WLY57Q.js.map +0 -1
  91. package/dist/SidebarLayout-6MHQYW45.js +0 -239
  92. package/dist/SidebarLayout-6MHQYW45.js.map +0 -1
  93. package/dist/WidgetLayout-4ZIG3RXV.js.map +0 -1
  94. package/dist/chunk-255AE5NI.js.map +0 -1
  95. package/dist/chunk-72LC2M3R.js +0 -567
  96. package/dist/chunk-72LC2M3R.js.map +0 -1
  97. package/dist/chunk-CFLVPV5M.js.map +0 -1
  98. package/dist/chunk-FXCYLEFB.js.map +0 -1
  99. package/dist/chunk-IFLE7OMM.js.map +0 -1
  100. package/dist/chunk-IWPDSPVA.js +0 -2068
  101. package/dist/chunk-IWPDSPVA.js.map +0 -1
  102. package/dist/chunk-OZTNBS6V.js +0 -75
  103. package/dist/chunk-OZTNBS6V.js.map +0 -1
  104. package/dist/chunk-PUFYHFND.js +0 -91
  105. package/dist/chunk-PUFYHFND.js.map +0 -1
  106. package/dist/chunk-WQULF5AT.js +0 -460
  107. package/dist/chunk-WQULF5AT.js.map +0 -1
  108. package/dist/chunk-XRTTKGKN.js +0 -6825
  109. package/dist/chunk-XRTTKGKN.js.map +0 -1
  110. package/dist/chunk-YIWUG6MC.js +0 -101
  111. package/dist/chunk-YIWUG6MC.js.map +0 -1
  112. /package/dist/{OpenUiFenceBlock-3EFPY4JW.js.map → AgentMetricChart-JPU3MFQ2.js.map} +0 -0
  113. /package/dist/{chunk-JRRCAKEP.js.map → chunk-MPNSO7E6.js.map} +0 -0
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import {
3
+ SESSION_EVENT_TYPES,
4
+ useSlot
5
+ } from "./chunk-ZTMKFZQ3.js";
6
+ import "./chunk-2XPPBEYF.js";
7
+ import "./chunk-XAU7QFG3.js";
8
+ import "./chunk-U7KURWW3.js";
9
+ import "./chunk-M6MHLVSO.js";
10
+ import "./chunk-MPNSO7E6.js";
11
+ import {
12
+ cn
13
+ } from "./chunk-7Z2C2K7F.js";
14
+
15
+ // src/atoms/agent-details/AgentSessionEventTimeline.tsx
16
+ import { useCallback, useMemo, useState } from "react";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ function AgentSessionEventTimeline({ turns, segments, onSelectTurn }) {
19
+ const AgentSessionEventTimelineChart = useSlot("AgentSessionEventTimelineChart");
20
+ const [hiddenTypes, setHiddenTypes] = useState(() => /* @__PURE__ */ new Set());
21
+ const availableEventTypes = useMemo(() => {
22
+ const available = new Set(segments.map((segment) => segment.type));
23
+ return SESSION_EVENT_TYPES.filter((eventType) => available.has(eventType.id));
24
+ }, [segments]);
25
+ const handleToggleType = useCallback((type) => {
26
+ setHiddenTypes((previous) => {
27
+ const next = new Set(previous);
28
+ if (next.has(type)) next.delete(type);
29
+ else next.add(type);
30
+ return next;
31
+ });
32
+ }, []);
33
+ if (segments.length === 0) return null;
34
+ return /* @__PURE__ */ jsxs("div", { className: "w-full", "data-slot": "agent-session-event-timeline", children: [
35
+ /* @__PURE__ */ jsxs(
36
+ "div",
37
+ {
38
+ className: "flex flex-wrap items-center gap-2 border-b border-border px-3 py-2",
39
+ "data-slot": "agent-session-event-types",
40
+ children: [
41
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase text-text-secondary", children: "Event types" }),
42
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1", children: availableEventTypes.map((eventType) => {
43
+ const isVisible = !hiddenTypes.has(eventType.id);
44
+ return /* @__PURE__ */ jsxs(
45
+ "button",
46
+ {
47
+ type: "button",
48
+ "aria-pressed": isVisible,
49
+ className: cn(
50
+ "flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium",
51
+ isVisible ? "border-border text-text-primary" : "border-transparent text-text-secondary opacity-80"
52
+ ),
53
+ onClick: () => handleToggleType(eventType.id),
54
+ children: [
55
+ /* @__PURE__ */ jsx("span", { className: "size-2 rounded-sm", style: { backgroundColor: eventType.color } }),
56
+ eventType.label
57
+ ]
58
+ },
59
+ eventType.id
60
+ );
61
+ }) })
62
+ ]
63
+ }
64
+ ),
65
+ /* @__PURE__ */ jsx("div", { className: "w-full py-2", children: /* @__PURE__ */ jsx(
66
+ AgentSessionEventTimelineChart,
67
+ {
68
+ turns,
69
+ segments,
70
+ hiddenTypes,
71
+ onSelectTurn
72
+ }
73
+ ) })
74
+ ] });
75
+ }
76
+ export {
77
+ AgentSessionEventTimeline
78
+ };
79
+ //# sourceMappingURL=AgentSessionEventTimeline-26Z6IB3B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/atoms/agent-details/AgentSessionEventTimeline.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useState, type ComponentType } from 'react';\n\nimport { useSlot } from '../../theme/SlotsProvider.js';\nimport { SESSION_EVENT_TYPES, type SessionEventType } from '../../utils/sessionEventTimeline.js';\nimport { cn } from '../lib/cn.js';\nimport type { AgentSessionEventTimelineProps } from './types.js';\n\nexport function AgentSessionEventTimeline({ turns, segments, onSelectTurn }: AgentSessionEventTimelineProps) {\n const AgentSessionEventTimelineChart = useSlot('AgentSessionEventTimelineChart');\n const [hiddenTypes, setHiddenTypes] = useState<Set<SessionEventType>>(() => new Set());\n\n const availableEventTypes = useMemo(() => {\n const available = new Set(segments.map(segment => segment.type));\n return SESSION_EVENT_TYPES.filter(eventType => available.has(eventType.id));\n }, [segments]);\n\n const handleToggleType = useCallback((type: SessionEventType) => {\n setHiddenTypes(previous => {\n const next = new Set(previous);\n if (next.has(type)) next.delete(type);\n else next.add(type);\n return next;\n });\n }, []);\n\n if (segments.length === 0) return null;\n\n return (\n <div className=\"w-full\" data-slot=\"agent-session-event-timeline\">\n <div\n className=\"flex flex-wrap items-center gap-2 border-b border-border px-3 py-2\"\n data-slot=\"agent-session-event-types\"\n >\n <span className=\"text-xs font-medium uppercase text-text-secondary\">Event types</span>\n <div className=\"flex flex-wrap items-center gap-1\">\n {availableEventTypes.map(eventType => {\n const isVisible = !hiddenTypes.has(eventType.id);\n return (\n <button\n key={eventType.id}\n type=\"button\"\n aria-pressed={isVisible}\n className={cn(\n 'flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium',\n isVisible ? 'border-border text-text-primary' : 'border-transparent text-text-secondary opacity-80',\n )}\n onClick={() => handleToggleType(eventType.id)}\n >\n <span className=\"size-2 rounded-sm\" style={{ backgroundColor: eventType.color }} />\n {eventType.label}\n </button>\n );\n })}\n </div>\n </div>\n <div className=\"w-full py-2\">\n <AgentSessionEventTimelineChart\n turns={turns}\n segments={segments}\n hiddenTypes={hiddenTypes}\n onSelectTurn={onSelectTurn}\n />\n </div>\n </div>\n );\n}\n\ndeclare module '../../theme/SlotsProvider.js' {\n interface AtomSlots {\n AgentSessionEventTimeline: ComponentType<AgentSessionEventTimelineProps>;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAEA,SAAS,aAAa,SAAS,gBAAoC;AAiC3D,cAKM,YALN;AA1BD,SAAS,0BAA0B,EAAE,OAAO,UAAU,aAAa,GAAmC;AAC3G,QAAM,iCAAiC,QAAQ,gCAAgC;AAC/E,QAAM,CAAC,aAAa,cAAc,IAAI,SAAgC,MAAM,oBAAI,IAAI,CAAC;AAErF,QAAM,sBAAsB,QAAQ,MAAM;AACxC,UAAM,YAAY,IAAI,IAAI,SAAS,IAAI,aAAW,QAAQ,IAAI,CAAC;AAC/D,WAAO,oBAAoB,OAAO,eAAa,UAAU,IAAI,UAAU,EAAE,CAAC;AAAA,EAC5E,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,mBAAmB,YAAY,CAAC,SAA2B;AAC/D,mBAAe,cAAY;AACzB,YAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,UAAI,KAAK,IAAI,IAAI,EAAG,MAAK,OAAO,IAAI;AAAA,UAC/B,MAAK,IAAI,IAAI;AAClB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,SACE,qBAAC,SAAI,WAAU,UAAS,aAAU,gCAChC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,aAAU;AAAA,QAEV;AAAA,8BAAC,UAAK,WAAU,qDAAoD,yBAAW;AAAA,UAC/E,oBAAC,SAAI,WAAU,qCACZ,8BAAoB,IAAI,eAAa;AACpC,kBAAM,YAAY,CAAC,YAAY,IAAI,UAAU,EAAE;AAC/C,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,gBAAc;AAAA,gBACd,WAAW;AAAA,kBACT;AAAA,kBACA,YAAY,oCAAoC;AAAA,gBAClD;AAAA,gBACA,SAAS,MAAM,iBAAiB,UAAU,EAAE;AAAA,gBAE5C;AAAA,sCAAC,UAAK,WAAU,qBAAoB,OAAO,EAAE,iBAAiB,UAAU,MAAM,GAAG;AAAA,kBAChF,UAAU;AAAA;AAAA;AAAA,cAVN,UAAU;AAAA,YAWjB;AAAA,UAEJ,CAAC,GACH;AAAA;AAAA;AAAA,IACF;AAAA,IACA,oBAAC,SAAI,WAAU,eACb;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,504 @@
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
+ formatTimelineDuration,
11
+ formatTokenCount,
12
+ getActiveTimelineMs,
13
+ getSessionEventColor,
14
+ getSessionEventHoverColor,
15
+ getSessionEventLabel,
16
+ getSessionEventTooltipHeading,
17
+ getSubAgentHoverGroups,
18
+ getSubAgentLanes,
19
+ getTimelineHoverTargetId,
20
+ getTimelineLayout,
21
+ getTimelineRange,
22
+ groupOverlappingToolCalls,
23
+ pickLongestNonOverlappingSegments,
24
+ useThemeMode
25
+ } from "./chunk-ZTMKFZQ3.js";
26
+ import "./chunk-2XPPBEYF.js";
27
+ import "./chunk-XAU7QFG3.js";
28
+ import "./chunk-U7KURWW3.js";
29
+ import "./chunk-M6MHLVSO.js";
30
+ import "./chunk-MPNSO7E6.js";
31
+ import "./chunk-7Z2C2K7F.js";
32
+
33
+ // src/atoms/agent-details/AgentSessionEventTimelineChart.tsx
34
+ import {
35
+ BarElement,
36
+ Chart as ChartJS,
37
+ LinearScale,
38
+ Tooltip
39
+ } from "chart.js";
40
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
41
+ import { Bar } from "react-chartjs-2";
42
+
43
+ // src/atoms/agent-details/AgentSessionTimelineTooltip.tsx
44
+ import { jsx, jsxs } from "react/jsx-runtime";
45
+ function hasSessionEventTooltip(segment) {
46
+ return segment.type !== "system" || segment.description.trim().length > 0;
47
+ }
48
+ function SessionEventTooltip({
49
+ segment,
50
+ subAgentLabel
51
+ }) {
52
+ const showDuration = !segment.isMarker && !SESSION_EVENT_TOOLTIP_HIDE_DURATION.has(segment.type);
53
+ const content = segment.description.trim();
54
+ if (segment.type === "system") {
55
+ if (content.length === 0) return null;
56
+ return /* @__PURE__ */ jsx("p", { className: "max-w-[25rem] whitespace-normal break-words text-xs font-medium text-text-primary", children: content });
57
+ }
58
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 max-w-[25rem] overflow-auto whitespace-normal break-words text-xs", children: [
59
+ subAgentLabel != null ? /* @__PURE__ */ jsx("div", { className: "border-b border-border py-1.5 text-text-secondary", children: `Sub-Agent: ${subAgentLabel}` }) : null,
60
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 pt-1", children: [
61
+ /* @__PURE__ */ jsx("span", { className: segment.type === "error" ? "font-medium text-failure-bg" : "font-medium text-text-secondary", children: getSessionEventTooltipHeading(segment.type) }),
62
+ showDuration ? /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) }) : null
63
+ ] }),
64
+ content.length > 0 ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 line-clamp-6 font-medium text-text-primary", children: content }) : null
65
+ ] });
66
+ }
67
+ function SessionTurnTooltip({
68
+ turn,
69
+ turnNumber,
70
+ durationMs,
71
+ segments
72
+ }) {
73
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-72 max-w-full overflow-auto text-xs text-text-primary", children: [
74
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
75
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", children: `Turn ${turnNumber}` }),
76
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatDurationMs(durationMs) })
77
+ ] }),
78
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-col text-text-secondary", children: [
79
+ turn.totalCostInUsd != null ? /* @__PURE__ */ jsx("span", { children: `Cost: ${formatCostUsd(turn.totalCostInUsd)}` }) : null,
80
+ turn.totalTokens != null ? /* @__PURE__ */ jsx("span", { children: `Tokens: ${formatTokenCount(turn.totalTokens)}` }) : null
81
+ ] }),
82
+ /* @__PURE__ */ jsx("div", { className: "mt-2 border-t border-border pt-2", children: segments.map((segment) => {
83
+ const label = segment.type === "tool_call" || segment.type === "sub_agent" ? segment.description : segment.title;
84
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
85
+ /* @__PURE__ */ jsx(
86
+ "span",
87
+ {
88
+ className: "size-1.5 shrink-0 rounded-full",
89
+ style: { backgroundColor: getSessionEventColor(segment.type) }
90
+ }
91
+ ),
92
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: label }),
93
+ segment.isMarker ? null : /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) })
94
+ ] }, segment.id);
95
+ }) })
96
+ ] });
97
+ }
98
+ function SessionToolCallGroupTooltip({ group }) {
99
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-80 max-w-full overflow-auto text-xs", children: [
100
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
101
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-text-secondary", children: "Tool calls" }),
102
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatTimelineDuration(group.endMs - group.startMs) })
103
+ ] }),
104
+ /* @__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: [
105
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-text-primary", children: segment.description || segment.title }),
106
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) })
107
+ ] }, segment.id)) })
108
+ ] });
109
+ }
110
+ function SessionSubAgentGroupTooltip({ group }) {
111
+ return /* @__PURE__ */ jsxs("div", { className: "max-h-72 w-80 max-w-full overflow-auto text-xs", children: [
112
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
113
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-text-secondary", children: "Sub-agents" }),
114
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-text-secondary", children: formatTimelineDuration(group.endMs - group.startMs) })
115
+ ] }),
116
+ /* @__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: [
117
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-text-primary", children: `Sub-agent ${index + 1}: ${segment.description || segment.title}` }),
118
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums text-text-secondary", children: formatTimelineDuration(segment.endMs - segment.startMs) })
119
+ ] }, segment.id)) })
120
+ ] });
121
+ }
122
+
123
+ // src/atoms/agent-details/AgentSessionEventTimelineChart.tsx
124
+ import { jsx as jsx2 } from "react/jsx-runtime";
125
+ ChartJS.register(LinearScale, BarElement, Tooltip);
126
+ var MARKER_PX = 4;
127
+ var TURN_GAP_PX = 12;
128
+ var END_PAD_PX = 12;
129
+ var BAR_RADIUS_PX = 2;
130
+ var ROW_GAP_PX = 8;
131
+ var OVERHEAD_PX = 30;
132
+ function rowCenters(heights, gap) {
133
+ const centers = [];
134
+ let offset = 0;
135
+ for (const height of heights) {
136
+ centers.push(offset + height / 2);
137
+ offset += height + gap;
138
+ }
139
+ return { centers, band: Math.max(0, offset - gap) };
140
+ }
141
+ function eventOrder(type) {
142
+ if (type === "sub_agent") return 0;
143
+ if (type === "model") return 2;
144
+ return 1;
145
+ }
146
+ function turnBarLabelPoint(element) {
147
+ if (!("x" in element) || !("y" in element) || !("base" in element)) return null;
148
+ const { x, y, base } = element;
149
+ if (typeof x !== "number" || typeof y !== "number" || typeof base !== "number") return null;
150
+ return { x: (x + base) / 2, y };
151
+ }
152
+ function AgentSessionEventTimelineChart({
153
+ turns,
154
+ segments,
155
+ hiddenTypes,
156
+ onSelectTurn
157
+ }) {
158
+ const mode = useThemeMode();
159
+ const isDark = mode === "dark";
160
+ const axis = isDark ? "#8c8c92" : "#71717a";
161
+ const grid = isDark ? "#27272a" : "#e4e4e7";
162
+ const turnFill = isDark ? "#3f3f46" : "#e4e4e7";
163
+ const turnHover = isDark ? "#52525b" : "#d4d4d8";
164
+ const wrapperRef = useRef(null);
165
+ const [widthPx, setWidthPx] = useState(0);
166
+ const [overheadPx, setOverheadPx] = useState(OVERHEAD_PX);
167
+ const [tooltipTarget, setTooltipTarget] = useState(null);
168
+ const [tooltipAnchor, setTooltipAnchor] = useState(null);
169
+ const tooltipIdRef = useRef("");
170
+ const tooltipCursorXRef = useRef(null);
171
+ useEffect(() => {
172
+ const wrapper = wrapperRef.current;
173
+ if (wrapper == null) return void 0;
174
+ const update = () => setWidthPx(wrapper.clientWidth);
175
+ update();
176
+ const observer = new ResizeObserver(update);
177
+ observer.observe(wrapper);
178
+ return () => observer.disconnect();
179
+ }, []);
180
+ const refreshTooltipAnchor = useCallback(() => {
181
+ const wrapper = wrapperRef.current;
182
+ const left = tooltipCursorXRef.current;
183
+ if (wrapper == null || left == null) return;
184
+ setTooltipAnchor({ left, top: wrapper.getBoundingClientRect().bottom });
185
+ }, []);
186
+ const clearTooltip = useCallback(() => {
187
+ tooltipIdRef.current = "";
188
+ tooltipCursorXRef.current = null;
189
+ setTooltipTarget(null);
190
+ setTooltipAnchor(null);
191
+ }, []);
192
+ useEffect(() => {
193
+ if (tooltipTarget == null) return void 0;
194
+ refreshTooltipAnchor();
195
+ window.addEventListener("scroll", refreshTooltipAnchor, true);
196
+ window.addEventListener("resize", refreshTooltipAnchor);
197
+ return () => {
198
+ window.removeEventListener("scroll", refreshTooltipAnchor, true);
199
+ window.removeEventListener("resize", refreshTooltipAnchor);
200
+ };
201
+ }, [refreshTooltipAnchor, tooltipTarget]);
202
+ const activeTotalMs = Math.max(1, ...segments.map((segment) => segment.endMs));
203
+ const { turnStartsMs, turnOrdinals, turnRanges } = useMemo(() => getTimelineLayout(segments), [segments]);
204
+ const gapCount = Math.max(0, turnStartsMs.length - 1);
205
+ const scaleWidthPx = Math.max(widthPx / 2, widthPx - TURN_GAP_PX * gapCount - END_PAD_PX);
206
+ const msPerPx = activeTotalMs / Math.max(1, scaleWidthPx);
207
+ const turnGapMs = TURN_GAP_PX * msPerPx;
208
+ const totalMs = activeTotalMs + gapCount * turnGapMs;
209
+ const timelineMaxMs = totalMs + END_PAD_PX * msPerPx;
210
+ const timelineGaps = useMemo(
211
+ () => turnStartsMs.slice(1).map((startMs, index) => ({ startMs: startMs + index * turnGapMs, endMs: startMs + (index + 1) * turnGapMs })),
212
+ [turnGapMs, turnStartsMs]
213
+ );
214
+ const visibleSegments = useMemo(
215
+ () => segments.flatMap((segment) => {
216
+ if (hiddenTypes.has(segment.type)) return [];
217
+ const shiftMs = (turnOrdinals.get(segment.turnIndex) ?? 0) * turnGapMs;
218
+ return [{ ...segment, startMs: segment.startMs + shiftMs, endMs: segment.endMs + shiftMs }];
219
+ }),
220
+ [hiddenTypes, segments, turnGapMs, turnOrdinals]
221
+ );
222
+ const turnBars = useMemo(
223
+ () => turnRanges.length < 2 ? [] : turnRanges.map((range) => ({
224
+ ...range,
225
+ id: `turn-${range.turnIndex}`,
226
+ durationMs: range.endMs - range.startMs,
227
+ startMs: range.startMs + range.ordinal * turnGapMs,
228
+ endMs: range.endMs + range.ordinal * turnGapMs + MARKER_PX * msPerPx
229
+ })),
230
+ [msPerPx, turnGapMs, turnRanges]
231
+ );
232
+ const subAgentSegments = useMemo(
233
+ () => visibleSegments.filter((segment) => segment.type === "sub_agent"),
234
+ [visibleSegments]
235
+ );
236
+ const mainSubAgents = useMemo(() => pickLongestNonOverlappingSegments(subAgentSegments), [subAgentSegments]);
237
+ const subAgentGroups = useMemo(
238
+ () => getSubAgentHoverGroups({ bars: mainSubAgents, subAgentSegments }),
239
+ [mainSubAgents, subAgentSegments]
240
+ );
241
+ const subAgentLanes = useMemo(
242
+ () => getSubAgentLanes({ subAgentSegments, threadSegments: visibleSegments, minWidthMs: MARKER_PX * msPerPx }),
243
+ [msPerPx, subAgentSegments, visibleSegments]
244
+ );
245
+ const mainCandidates = useMemo(() => {
246
+ const kept = new Set(mainSubAgents.map((segment) => segment.id));
247
+ return visibleSegments.filter(
248
+ (segment) => segment.threadId === MAIN_THREAD_ID || segment.type === "sub_agent" && kept.has(segment.id)
249
+ );
250
+ }, [mainSubAgents, visibleSegments]);
251
+ const toolCallGroups = useMemo(
252
+ () => groupOverlappingToolCalls(mainCandidates.filter((segment) => segment.type === "tool_call")).filter(
253
+ (group) => group.segments.length > 1
254
+ ),
255
+ [mainCandidates]
256
+ );
257
+ const groupedIds = useMemo(
258
+ () => new Set(toolCallGroups.flatMap((group) => group.segments.map((segment) => segment.id))),
259
+ [toolCallGroups]
260
+ );
261
+ const mainEventSegments = useMemo(
262
+ () => mainCandidates.filter((segment) => !groupedIds.has(segment.id)),
263
+ [groupedIds, mainCandidates]
264
+ );
265
+ const laneCount = subAgentLanes.reduce((count, lane) => Math.max(count, lane.lane + 1), 0);
266
+ const hasTurnRow = turnBars.length > 0;
267
+ const eventRow = hasTurnRow ? 1 : 0;
268
+ const { centers, band } = useMemo(
269
+ () => rowCenters([...hasTurnRow ? [16] : [], 28, ...Array.from({ length: laneCount }, () => 12)], ROW_GAP_PX),
270
+ [hasTurnRow, laneCount]
271
+ );
272
+ const chartTargets = useMemo(
273
+ () => [
274
+ ...turnBars.map((bar) => ({ type: TIMELINE_TYPE.turn, bar })),
275
+ ...subAgentLanes.flatMap(
276
+ (lane) => lane.segments.map((segment) => ({ type: TIMELINE_TYPE.event, segment }))
277
+ ),
278
+ ...mainEventSegments.map((segment) => {
279
+ if (segment.type !== "sub_agent") return { type: TIMELINE_TYPE.event, segment };
280
+ const group = subAgentGroups.find((candidate) => candidate.barId === segment.id);
281
+ return group == null ? { type: TIMELINE_TYPE.event, segment } : { type: TIMELINE_TYPE.subAgentGroup, group };
282
+ }),
283
+ ...toolCallGroups.map((group) => ({ type: TIMELINE_TYPE.toolCallGroup, group }))
284
+ ],
285
+ [mainEventSegments, subAgentGroups, subAgentLanes, toolCallGroups, turnBars]
286
+ );
287
+ const barDataset = ({
288
+ label,
289
+ range,
290
+ y,
291
+ color,
292
+ hover,
293
+ thickness,
294
+ order = 1,
295
+ inflateAmount
296
+ }) => ({
297
+ label,
298
+ data: [{ x: getTimelineRange(range), y }],
299
+ backgroundColor: color,
300
+ hoverBackgroundColor: hover,
301
+ hoverBorderColor: hover,
302
+ borderWidth: 0,
303
+ borderSkipped: false,
304
+ borderRadius: BAR_RADIUS_PX,
305
+ minBarLength: MARKER_PX,
306
+ barThickness: thickness,
307
+ grouped: false,
308
+ order,
309
+ ...inflateAmount == null ? {} : { inflateAmount }
310
+ });
311
+ const chartData = useMemo(
312
+ () => ({
313
+ datasets: [
314
+ ...turnBars.map(
315
+ (bar) => barDataset({
316
+ label: `Turn ${bar.ordinal + 1}`,
317
+ range: bar,
318
+ y: centers[0] ?? 0,
319
+ color: turnFill,
320
+ hover: turnHover,
321
+ thickness: 16
322
+ })
323
+ ),
324
+ ...subAgentLanes.flatMap(
325
+ (lane) => lane.segments.map(
326
+ (segment) => barDataset({
327
+ label: `${getSessionEventLabel(segment.type)}: ${segment.title}`,
328
+ range: segment,
329
+ y: centers[eventRow + 1 + lane.lane] ?? 0,
330
+ color: getSessionEventColor(segment.type),
331
+ hover: getSessionEventHoverColor(segment.type),
332
+ thickness: 12,
333
+ order: eventOrder(segment.type),
334
+ inflateAmount: 0.5
335
+ })
336
+ )
337
+ ),
338
+ ...mainEventSegments.map(
339
+ (segment) => barDataset({
340
+ label: `${getSessionEventLabel(segment.type)}: ${segment.title}`,
341
+ range: segment,
342
+ y: centers[eventRow] ?? 0,
343
+ color: getSessionEventColor(segment.type),
344
+ hover: getSessionEventHoverColor(segment.type),
345
+ thickness: 28,
346
+ order: eventOrder(segment.type),
347
+ inflateAmount: 0.5
348
+ })
349
+ ),
350
+ ...toolCallGroups.map(
351
+ (group) => barDataset({
352
+ label: "Parallel tool calls",
353
+ range: group,
354
+ y: centers[eventRow] ?? 0,
355
+ color: getSessionEventColor("tool_call"),
356
+ hover: getSessionEventHoverColor("tool_call"),
357
+ thickness: 28,
358
+ order: eventOrder("tool_call"),
359
+ inflateAmount: 0.5
360
+ })
361
+ )
362
+ ]
363
+ }),
364
+ [centers, eventRow, mainEventSegments, subAgentLanes, toolCallGroups, turnBars, turnFill, turnHover]
365
+ );
366
+ const turnLabelPlugin = useMemo(
367
+ () => ({
368
+ id: "turnLabels",
369
+ afterDatasetsDraw(chart) {
370
+ const { ctx } = chart;
371
+ ctx.save();
372
+ ctx.fillStyle = axis;
373
+ ctx.font = "500 10px sans-serif";
374
+ ctx.textAlign = "center";
375
+ ctx.textBaseline = "middle";
376
+ turnBars.forEach((bar, index) => {
377
+ const element = chart.getDatasetMeta(index).data[0];
378
+ if (element == null) return;
379
+ const point = turnBarLabelPoint(element);
380
+ if (point) ctx.fillText(`T${bar.ordinal + 1}`, point.x, point.y);
381
+ });
382
+ ctx.restore();
383
+ }
384
+ }),
385
+ [axis, turnBars]
386
+ );
387
+ const overheadPlugin = useMemo(
388
+ () => ({
389
+ id: "timelineOverhead",
390
+ afterLayout(chart) {
391
+ const next = Math.round(chart.height - chart.chartArea.height);
392
+ setOverheadPx((previous) => previous === next ? previous : next);
393
+ }
394
+ }),
395
+ []
396
+ );
397
+ const chartOptions = useMemo(
398
+ () => ({
399
+ indexAxis: "y",
400
+ animation: false,
401
+ responsive: true,
402
+ maintainAspectRatio: false,
403
+ onClick: (_event, elements) => {
404
+ const target = chartTargets[elements[0]?.datasetIndex ?? -1];
405
+ if (target?.type === TIMELINE_TYPE.turn) onSelectTurn?.(target.bar.turnIndex);
406
+ },
407
+ onHover: (event, elements) => {
408
+ const hoveredDatasetIndex = elements[0]?.datasetIndex;
409
+ const next = hoveredDatasetIndex == null ? null : chartTargets[hoveredDatasetIndex] ?? null;
410
+ const native = event.native;
411
+ if (native?.target instanceof HTMLElement) {
412
+ native.target.style.cursor = next?.type === TIMELINE_TYPE.turn ? "pointer" : "default";
413
+ }
414
+ if (native instanceof MouseEvent) {
415
+ tooltipCursorXRef.current = native.clientX;
416
+ refreshTooltipAnchor();
417
+ }
418
+ if (hoveredDatasetIndex == null) return;
419
+ const nextId = getTimelineHoverTargetId(next);
420
+ if (tooltipIdRef.current === nextId) return;
421
+ tooltipIdRef.current = nextId;
422
+ setTooltipTarget(next);
423
+ if (next == null) {
424
+ setTooltipAnchor(null);
425
+ return;
426
+ }
427
+ refreshTooltipAnchor();
428
+ },
429
+ plugins: { legend: { display: false }, tooltip: { enabled: false } },
430
+ scales: {
431
+ x: {
432
+ type: "linear",
433
+ position: "top",
434
+ min: 0,
435
+ max: timelineMaxMs,
436
+ grid: { color: grid },
437
+ border: { color: grid },
438
+ afterBuildTicks: (scale) => {
439
+ scale.ticks = buildTimelineAxisTicks({
440
+ ticks: scale.ticks,
441
+ totalMs,
442
+ timelineMaxMs
443
+ });
444
+ },
445
+ ticks: {
446
+ color: axis,
447
+ autoSkip: false,
448
+ includeBounds: true,
449
+ maxTicksLimit: 9,
450
+ stepSize: timelineMaxMs / 8,
451
+ callback: (value) => {
452
+ const timelineMs = Number(value);
453
+ return timelineMs <= totalMs ? formatTimelineDuration(getActiveTimelineMs(timelineMs, timelineGaps)) : "";
454
+ }
455
+ }
456
+ },
457
+ y: {
458
+ type: "linear",
459
+ display: false,
460
+ reverse: true,
461
+ offset: false,
462
+ min: 0,
463
+ max: band,
464
+ grid: { display: false }
465
+ }
466
+ }
467
+ }),
468
+ [axis, band, chartTargets, grid, onSelectTurn, refreshTooltipAnchor, timelineGaps, timelineMaxMs, totalMs]
469
+ );
470
+ const tooltipTurn = tooltipTarget?.type === TIMELINE_TYPE.turn ? turns[tooltipTarget.bar.turnIndex] : void 0;
471
+ const containerStyle = useMemo(() => ({ height: band + overheadPx }), [band, overheadPx]);
472
+ const tooltipContent = tooltipTarget?.type === TIMELINE_TYPE.turn && tooltipTurn != null ? /* @__PURE__ */ jsx2(
473
+ SessionTurnTooltip,
474
+ {
475
+ turn: tooltipTurn,
476
+ turnNumber: tooltipTarget.bar.ordinal + 1,
477
+ durationMs: tooltipTarget.bar.durationMs,
478
+ segments: segments.filter((segment) => segment.turnIndex === tooltipTarget.bar.turnIndex)
479
+ }
480
+ ) : tooltipTarget?.type === TIMELINE_TYPE.event && hasSessionEventTooltip(tooltipTarget.segment) ? /* @__PURE__ */ jsx2(
481
+ SessionEventTooltip,
482
+ {
483
+ segment: tooltipTarget.segment,
484
+ subAgentLabel: tooltipTarget.segment.type === "sub_agent" ? void 0 : subAgentLanes.find((lane) => lane.threadId === tooltipTarget.segment.threadId)?.track.description
485
+ }
486
+ ) : tooltipTarget?.type === TIMELINE_TYPE.toolCallGroup ? /* @__PURE__ */ jsx2(SessionToolCallGroupTooltip, { group: tooltipTarget.group }) : tooltipTarget?.type === TIMELINE_TYPE.subAgentGroup ? /* @__PURE__ */ jsx2(SessionSubAgentGroupTooltip, { group: tooltipTarget.group }) : null;
487
+ return /* @__PURE__ */ jsx2("div", { ref: wrapperRef, className: "w-full", "data-slot": "agent-session-event-timeline-chart", children: /* @__PURE__ */ jsx2(
488
+ LightTooltip,
489
+ {
490
+ title: tooltipContent,
491
+ side: "bottom",
492
+ triggerClassName: "block w-full",
493
+ followCursor: true,
494
+ open: tooltipTarget != null && tooltipContent != null,
495
+ className: "max-w-[min(25rem,calc(100vw-1rem))] whitespace-normal",
496
+ anchor: tooltipAnchor,
497
+ children: /* @__PURE__ */ jsx2("div", { className: "w-full", style: containerStyle, onMouseLeave: clearTooltip, children: /* @__PURE__ */ jsx2(Bar, { data: chartData, options: chartOptions, plugins: [turnLabelPlugin, overheadPlugin] }) })
498
+ }
499
+ ) });
500
+ }
501
+ export {
502
+ AgentSessionEventTimelineChart
503
+ };
504
+ //# sourceMappingURL=AgentSessionEventTimelineChart-CGCU7WBK.js.map