@flanksource/clicky-ui 0.3.46 → 0.3.48

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 (474) hide show
  1. package/NOTICE.md +11 -0
  2. package/README.md +49 -0
  3. package/dist/ai.cjs +13 -8
  4. package/dist/ai.cjs.map +1 -1
  5. package/dist/ai.js +7 -2
  6. package/dist/ai.js.map +1 -1
  7. package/dist/comments/CommentSidePanel.cjs +10 -4
  8. package/dist/comments/CommentSidePanel.cjs.map +1 -1
  9. package/dist/comments/CommentSidePanel.d.ts +2 -0
  10. package/dist/comments/CommentSidePanel.d.ts.map +1 -1
  11. package/dist/comments/CommentSidePanel.js +10 -4
  12. package/dist/comments/CommentSidePanel.js.map +1 -1
  13. package/dist/comments/CommentThread.cjs +2 -0
  14. package/dist/comments/CommentThread.cjs.map +1 -1
  15. package/dist/comments/CommentThread.d.ts +2 -1
  16. package/dist/comments/CommentThread.d.ts.map +1 -1
  17. package/dist/comments/CommentThread.js +2 -0
  18. package/dist/comments/CommentThread.js.map +1 -1
  19. package/dist/comments/CommentThreadList.cjs +3 -0
  20. package/dist/comments/CommentThreadList.cjs.map +1 -1
  21. package/dist/comments/CommentThreadList.d.ts +3 -1
  22. package/dist/comments/CommentThreadList.d.ts.map +1 -1
  23. package/dist/comments/CommentThreadList.js +3 -0
  24. package/dist/comments/CommentThreadList.js.map +1 -1
  25. package/dist/components/ErrorWrapper.cjs +2 -3
  26. package/dist/components/ErrorWrapper.cjs.map +1 -1
  27. package/dist/components/ErrorWrapper.d.ts.map +1 -1
  28. package/dist/components/ErrorWrapper.js +3 -4
  29. package/dist/components/ErrorWrapper.js.map +1 -1
  30. package/dist/data/Clicky.cjs +3 -0
  31. package/dist/data/Clicky.cjs.map +1 -1
  32. package/dist/data/Clicky.js +3 -0
  33. package/dist/data/Clicky.js.map +1 -1
  34. package/dist/data/CodeLine.cjs +4 -2
  35. package/dist/data/CodeLine.cjs.map +1 -1
  36. package/dist/data/CodeLine.d.ts.map +1 -1
  37. package/dist/data/CodeLine.js +4 -2
  38. package/dist/data/CodeLine.js.map +1 -1
  39. package/dist/data/FilterPill.cjs +15 -2
  40. package/dist/data/FilterPill.cjs.map +1 -1
  41. package/dist/data/FilterPill.d.ts +1 -1
  42. package/dist/data/FilterPill.d.ts.map +1 -1
  43. package/dist/data/FilterPill.js +15 -2
  44. package/dist/data/FilterPill.js.map +1 -1
  45. package/dist/data/LogViewer.cjs +27 -8
  46. package/dist/data/LogViewer.cjs.map +1 -1
  47. package/dist/data/LogViewer.d.ts +11 -3
  48. package/dist/data/LogViewer.d.ts.map +1 -1
  49. package/dist/data/LogViewer.js +27 -8
  50. package/dist/data/LogViewer.js.map +1 -1
  51. package/dist/data/Properties.cjs +2 -15
  52. package/dist/data/Properties.cjs.map +1 -1
  53. package/dist/data/Properties.d.ts +4 -0
  54. package/dist/data/Properties.d.ts.map +1 -1
  55. package/dist/data/Properties.js +2 -15
  56. package/dist/data/Properties.js.map +1 -1
  57. package/dist/data/PropertyValue.cjs +37 -0
  58. package/dist/data/PropertyValue.cjs.map +1 -0
  59. package/dist/data/PropertyValue.d.ts +6 -0
  60. package/dist/data/PropertyValue.d.ts.map +1 -0
  61. package/dist/data/PropertyValue.js +37 -0
  62. package/dist/data/PropertyValue.js.map +1 -0
  63. package/dist/data/SymbolBadge.cjs +61 -0
  64. package/dist/data/SymbolBadge.cjs.map +1 -0
  65. package/dist/data/SymbolBadge.d.ts +21 -0
  66. package/dist/data/SymbolBadge.d.ts.map +1 -0
  67. package/dist/data/SymbolBadge.js +61 -0
  68. package/dist/data/SymbolBadge.js.map +1 -0
  69. package/dist/data/Tree.cjs +4 -3
  70. package/dist/data/Tree.cjs.map +1 -1
  71. package/dist/data/Tree.d.ts +3 -1
  72. package/dist/data/Tree.d.ts.map +1 -1
  73. package/dist/data/Tree.js +4 -3
  74. package/dist/data/Tree.js.map +1 -1
  75. package/dist/data/ai/ChatFab.cjs +1 -1
  76. package/dist/data/ai/ChatFab.cjs.map +1 -1
  77. package/dist/data/ai/ChatFab.js +1 -1
  78. package/dist/data/ai/ChatFab.js.map +1 -1
  79. package/dist/data/ai/ChatWindow.cjs +106 -171
  80. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  81. package/dist/data/ai/ChatWindow.d.ts +6 -2
  82. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  83. package/dist/data/ai/ChatWindow.header.cjs +104 -0
  84. package/dist/data/ai/ChatWindow.header.cjs.map +1 -0
  85. package/dist/data/ai/ChatWindow.header.d.ts +29 -0
  86. package/dist/data/ai/ChatWindow.header.d.ts.map +1 -0
  87. package/dist/data/ai/ChatWindow.header.js +104 -0
  88. package/dist/data/ai/ChatWindow.header.js.map +1 -0
  89. package/dist/data/ai/ChatWindow.js +107 -172
  90. package/dist/data/ai/ChatWindow.js.map +1 -1
  91. package/dist/data/ai/ChatWindow.placement.cjs +95 -0
  92. package/dist/data/ai/ChatWindow.placement.cjs.map +1 -0
  93. package/dist/data/ai/ChatWindow.placement.d.ts +32 -0
  94. package/dist/data/ai/ChatWindow.placement.d.ts.map +1 -0
  95. package/dist/data/ai/ChatWindow.placement.js +95 -0
  96. package/dist/data/ai/ChatWindow.placement.js.map +1 -0
  97. package/dist/data/ai/SessionChatComposer.cjs +3 -1
  98. package/dist/data/ai/SessionChatComposer.cjs.map +1 -1
  99. package/dist/data/ai/SessionChatComposer.d.ts +4 -1
  100. package/dist/data/ai/SessionChatComposer.d.ts.map +1 -1
  101. package/dist/data/ai/SessionChatComposer.js +3 -1
  102. package/dist/data/ai/SessionChatComposer.js.map +1 -1
  103. package/dist/data/ai/SessionInspector.cjs +68 -72
  104. package/dist/data/ai/SessionInspector.cjs.map +1 -1
  105. package/dist/data/ai/SessionInspector.composer-slot.cjs +8 -0
  106. package/dist/data/ai/SessionInspector.composer-slot.cjs.map +1 -0
  107. package/dist/data/ai/SessionInspector.composer-slot.d.ts +9 -0
  108. package/dist/data/ai/SessionInspector.composer-slot.d.ts.map +1 -0
  109. package/dist/data/ai/SessionInspector.composer-slot.js +8 -0
  110. package/dist/data/ai/SessionInspector.composer-slot.js.map +1 -0
  111. package/dist/data/ai/SessionInspector.d.ts +6 -1
  112. package/dist/data/ai/SessionInspector.d.ts.map +1 -1
  113. package/dist/data/ai/SessionInspector.js +69 -73
  114. package/dist/data/ai/SessionInspector.js.map +1 -1
  115. package/dist/data/ai/SessionInspector.legacy.cjs +41 -42
  116. package/dist/data/ai/SessionInspector.legacy.cjs.map +1 -1
  117. package/dist/data/ai/SessionInspector.legacy.d.ts.map +1 -1
  118. package/dist/data/ai/SessionInspector.legacy.js +41 -42
  119. package/dist/data/ai/SessionInspector.legacy.js.map +1 -1
  120. package/dist/data/ai/SessionInspector.remote-view.cjs +14 -9
  121. package/dist/data/ai/SessionInspector.remote-view.cjs.map +1 -1
  122. package/dist/data/ai/SessionInspector.remote-view.d.ts +6 -2
  123. package/dist/data/ai/SessionInspector.remote-view.d.ts.map +1 -1
  124. package/dist/data/ai/SessionInspector.remote-view.js +14 -9
  125. package/dist/data/ai/SessionInspector.remote-view.js.map +1 -1
  126. package/dist/data/ai/SessionInspector.remote.cjs +84 -16
  127. package/dist/data/ai/SessionInspector.remote.cjs.map +1 -1
  128. package/dist/data/ai/SessionInspector.remote.d.ts +42 -7
  129. package/dist/data/ai/SessionInspector.remote.d.ts.map +1 -1
  130. package/dist/data/ai/SessionInspector.remote.js +86 -18
  131. package/dist/data/ai/SessionInspector.remote.js.map +1 -1
  132. package/dist/data/ai/SessionInspector.transcript.cjs +5 -0
  133. package/dist/data/ai/SessionInspector.transcript.cjs.map +1 -1
  134. package/dist/data/ai/SessionInspector.transcript.d.ts.map +1 -1
  135. package/dist/data/ai/SessionInspector.transcript.js +5 -0
  136. package/dist/data/ai/SessionInspector.transcript.js.map +1 -1
  137. package/dist/data/ai/SessionTail.cjs +63 -0
  138. package/dist/data/ai/SessionTail.cjs.map +1 -0
  139. package/dist/data/ai/SessionTail.d.ts +14 -0
  140. package/dist/data/ai/SessionTail.d.ts.map +1 -0
  141. package/dist/data/ai/SessionTail.js +63 -0
  142. package/dist/data/ai/SessionTail.js.map +1 -0
  143. package/dist/data/ai/SessionViewer.cjs +10 -3
  144. package/dist/data/ai/SessionViewer.cjs.map +1 -1
  145. package/dist/data/ai/SessionViewer.d.ts +7 -1
  146. package/dist/data/ai/SessionViewer.d.ts.map +1 -1
  147. package/dist/data/ai/SessionViewer.js +10 -3
  148. package/dist/data/ai/SessionViewer.js.map +1 -1
  149. package/dist/data/ai/SessionViewer.unified.d.ts +14 -0
  150. package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
  151. package/dist/data/ai/index.d.ts +4 -3
  152. package/dist/data/ai/index.d.ts.map +1 -1
  153. package/dist/data/ai/use-session-scroll.cjs +30 -6
  154. package/dist/data/ai/use-session-scroll.cjs.map +1 -1
  155. package/dist/data/ai/use-session-scroll.d.ts +7 -0
  156. package/dist/data/ai/use-session-scroll.d.ts.map +1 -1
  157. package/dist/data/ai/use-session-scroll.js +30 -6
  158. package/dist/data/ai/use-session-scroll.js.map +1 -1
  159. package/dist/data/chat/Chat.cjs +1 -1
  160. package/dist/data/chat/Chat.cjs.map +1 -1
  161. package/dist/data/chat/Chat.js +1 -1
  162. package/dist/data/chat/Chat.js.map +1 -1
  163. package/dist/data/chat/ChatRuntimeToolbar.cjs +4 -4
  164. package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
  165. package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
  166. package/dist/data/chat/ChatRuntimeToolbar.js +4 -4
  167. package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
  168. package/dist/data/chat/PromptInput.cjs +4 -2
  169. package/dist/data/chat/PromptInput.cjs.map +1 -1
  170. package/dist/data/chat/PromptInput.d.ts +5 -1
  171. package/dist/data/chat/PromptInput.d.ts.map +1 -1
  172. package/dist/data/chat/PromptInput.js +4 -2
  173. package/dist/data/chat/PromptInput.js.map +1 -1
  174. package/dist/data/diagnostics/ErrorDetails.cjs +13 -2
  175. package/dist/data/diagnostics/ErrorDetails.cjs.map +1 -1
  176. package/dist/data/diagnostics/ErrorDetails.d.ts +3 -1
  177. package/dist/data/diagnostics/ErrorDetails.d.ts.map +1 -1
  178. package/dist/data/diagnostics/ErrorDetails.js +14 -3
  179. package/dist/data/diagnostics/ErrorDetails.js.map +1 -1
  180. package/dist/data/diagnostics/FrameSourceWindow.cjs +3 -2
  181. package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -1
  182. package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -1
  183. package/dist/data/diagnostics/FrameSourceWindow.js +3 -2
  184. package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -1
  185. package/dist/data/diagnostics/error-diagnostics.cjs +5 -0
  186. package/dist/data/diagnostics/error-diagnostics.cjs.map +1 -1
  187. package/dist/data/diagnostics/error-diagnostics.d.ts +1 -0
  188. package/dist/data/diagnostics/error-diagnostics.d.ts.map +1 -1
  189. package/dist/data/diagnostics/error-diagnostics.js +6 -1
  190. package/dist/data/diagnostics/error-diagnostics.js.map +1 -1
  191. package/dist/data/har/HarPanel.cjs +3 -1
  192. package/dist/data/har/HarPanel.cjs.map +1 -1
  193. package/dist/data/har/HarPanel.d.ts +4 -1
  194. package/dist/data/har/HarPanel.d.ts.map +1 -1
  195. package/dist/data/har/HarPanel.js +3 -1
  196. package/dist/data/har/HarPanel.js.map +1 -1
  197. package/dist/data/test-runner/Expectation.cjs +57 -0
  198. package/dist/data/test-runner/Expectation.cjs.map +1 -0
  199. package/dist/data/test-runner/Expectation.d.ts +19 -0
  200. package/dist/data/test-runner/Expectation.d.ts.map +1 -0
  201. package/dist/data/test-runner/Expectation.js +57 -0
  202. package/dist/data/test-runner/Expectation.js.map +1 -0
  203. package/dist/data/test-runner/MarkIcons.cjs +38 -0
  204. package/dist/data/test-runner/MarkIcons.cjs.map +1 -0
  205. package/dist/data/test-runner/MarkIcons.d.ts +15 -0
  206. package/dist/data/test-runner/MarkIcons.d.ts.map +1 -0
  207. package/dist/data/test-runner/MarkIcons.js +38 -0
  208. package/dist/data/test-runner/MarkIcons.js.map +1 -0
  209. package/dist/data/test-runner/TestDetailPanel.cjs +99 -53
  210. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
  211. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
  212. package/dist/data/test-runner/TestDetailPanel.js +101 -55
  213. package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
  214. package/dist/data/test-runner/TestFailureDetail.cjs +23 -27
  215. package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -1
  216. package/dist/data/test-runner/TestFailureDetail.d.ts +2 -2
  217. package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -1
  218. package/dist/data/test-runner/TestFailureDetail.js +23 -27
  219. package/dist/data/test-runner/TestFailureDetail.js.map +1 -1
  220. package/dist/data/test-runner/TestFilterBar.cjs +87 -93
  221. package/dist/data/test-runner/TestFilterBar.cjs.map +1 -1
  222. package/dist/data/test-runner/TestFilterBar.d.ts +3 -2
  223. package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -1
  224. package/dist/data/test-runner/TestFilterBar.js +89 -95
  225. package/dist/data/test-runner/TestFilterBar.js.map +1 -1
  226. package/dist/data/test-runner/TestRunSummary.cjs +35 -21
  227. package/dist/data/test-runner/TestRunSummary.cjs.map +1 -1
  228. package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -1
  229. package/dist/data/test-runner/TestRunSummary.js +35 -21
  230. package/dist/data/test-runner/TestRunSummary.js.map +1 -1
  231. package/dist/data/test-runner/TestRunner.cjs +58 -30
  232. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  233. package/dist/data/test-runner/TestRunner.d.ts +6 -3
  234. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  235. package/dist/data/test-runner/TestRunner.js +58 -30
  236. package/dist/data/test-runner/TestRunner.js.map +1 -1
  237. package/dist/data/test-runner/TestTree.cjs +5 -2
  238. package/dist/data/test-runner/TestTree.cjs.map +1 -1
  239. package/dist/data/test-runner/TestTree.d.ts +5 -2
  240. package/dist/data/test-runner/TestTree.d.ts.map +1 -1
  241. package/dist/data/test-runner/TestTree.js +5 -2
  242. package/dist/data/test-runner/TestTree.js.map +1 -1
  243. package/dist/data/test-runner/TestTreeNode.cjs +28 -19
  244. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
  245. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
  246. package/dist/data/test-runner/TestTreeNode.js +29 -20
  247. package/dist/data/test-runner/TestTreeNode.js.map +1 -1
  248. package/dist/data/test-runner/adapter.cjs +15 -0
  249. package/dist/data/test-runner/adapter.cjs.map +1 -1
  250. package/dist/data/test-runner/adapter.d.ts +27 -7
  251. package/dist/data/test-runner/adapter.d.ts.map +1 -1
  252. package/dist/data/test-runner/adapter.js +15 -0
  253. package/dist/data/test-runner/adapter.js.map +1 -1
  254. package/dist/data/test-runner/context.cjs.map +1 -1
  255. package/dist/data/test-runner/context.d.ts +2 -0
  256. package/dist/data/test-runner/context.d.ts.map +1 -1
  257. package/dist/data/test-runner/context.js.map +1 -1
  258. package/dist/data/test-runner/copy.cjs +114 -0
  259. package/dist/data/test-runner/copy.cjs.map +1 -0
  260. package/dist/data/test-runner/copy.d.ts +47 -0
  261. package/dist/data/test-runner/copy.d.ts.map +1 -0
  262. package/dist/data/test-runner/copy.js +114 -0
  263. package/dist/data/test-runner/copy.js.map +1 -0
  264. package/dist/data/test-runner/index.d.ts +6 -3
  265. package/dist/data/test-runner/index.d.ts.map +1 -1
  266. package/dist/data/test-runner/marks.cjs +184 -0
  267. package/dist/data/test-runner/marks.cjs.map +1 -0
  268. package/dist/data/test-runner/marks.d.ts +40 -0
  269. package/dist/data/test-runner/marks.d.ts.map +1 -0
  270. package/dist/data/test-runner/marks.js +184 -0
  271. package/dist/data/test-runner/marks.js.map +1 -0
  272. package/dist/data/test-runner/status.cjs +27 -44
  273. package/dist/data/test-runner/status.cjs.map +1 -1
  274. package/dist/data/test-runner/status.d.ts +12 -3
  275. package/dist/data/test-runner/status.d.ts.map +1 -1
  276. package/dist/data/test-runner/status.js +27 -44
  277. package/dist/data/test-runner/status.js.map +1 -1
  278. package/dist/data/traces/TerminalCastPlayer.d.ts +21 -0
  279. package/dist/data/traces/TerminalCastPlayer.d.ts.map +1 -0
  280. package/dist/data/traces/TraceHarView.d.ts +13 -0
  281. package/dist/data/traces/TraceHarView.d.ts.map +1 -0
  282. package/dist/data/traces/cast.d.ts +9 -0
  283. package/dist/data/traces/cast.d.ts.map +1 -0
  284. package/dist/data/traces/fixtures.d.ts +8 -0
  285. package/dist/data/traces/fixtures.d.ts.map +1 -0
  286. package/dist/data/traces/har-adapter.d.ts +13 -0
  287. package/dist/data/traces/har-adapter.d.ts.map +1 -0
  288. package/dist/data/traces/terminal-screen.d.ts +45 -0
  289. package/dist/data/traces/terminal-screen.d.ts.map +1 -0
  290. package/dist/data/traces/terminal-timeline.d.ts +26 -0
  291. package/dist/data/traces/terminal-timeline.d.ts.map +1 -0
  292. package/dist/data/traces/types.d.ts +141 -0
  293. package/dist/data/traces/types.d.ts.map +1 -0
  294. package/dist/data/verification/VerificationResults.cjs +4 -0
  295. package/dist/data/verification/VerificationResults.cjs.map +1 -1
  296. package/dist/data/verification/VerificationResults.d.ts +3 -1
  297. package/dist/data/verification/VerificationResults.d.ts.map +1 -1
  298. package/dist/data/verification/VerificationResults.js +4 -0
  299. package/dist/data/verification/VerificationResults.js.map +1 -1
  300. package/dist/data/verification/adapterViews.cjs +28 -44
  301. package/dist/data/verification/adapterViews.cjs.map +1 -1
  302. package/dist/data/verification/adapterViews.d.ts +4 -1
  303. package/dist/data/verification/adapterViews.d.ts.map +1 -1
  304. package/dist/data/verification/adapterViews.js +28 -44
  305. package/dist/data/verification/adapterViews.js.map +1 -1
  306. package/dist/data/verification/adapters.cjs +163 -8
  307. package/dist/data/verification/adapters.cjs.map +1 -1
  308. package/dist/data/verification/adapters.d.ts +7 -1
  309. package/dist/data/verification/adapters.d.ts.map +1 -1
  310. package/dist/data/verification/adapters.js +163 -8
  311. package/dist/data/verification/adapters.js.map +1 -1
  312. package/dist/data/verification/index.d.ts +4 -2
  313. package/dist/data/verification/index.d.ts.map +1 -1
  314. package/dist/data/verification/stepKind.cjs +217 -0
  315. package/dist/data/verification/stepKind.cjs.map +1 -0
  316. package/dist/data/verification/stepKind.d.ts +87 -0
  317. package/dist/data/verification/stepKind.d.ts.map +1 -0
  318. package/dist/data/verification/stepKind.js +217 -0
  319. package/dist/data/verification/stepKind.js.map +1 -0
  320. package/dist/data/verification/stepKindViews.cjs +111 -0
  321. package/dist/data/verification/stepKindViews.cjs.map +1 -0
  322. package/dist/data/verification/stepKindViews.d.ts +18 -0
  323. package/dist/data/verification/stepKindViews.d.ts.map +1 -0
  324. package/dist/data/verification/stepKindViews.js +111 -0
  325. package/dist/data/verification/stepKindViews.js.map +1 -0
  326. package/dist/data/verification/verifyReportTests.cjs +1 -0
  327. package/dist/data/verification/verifyReportTests.cjs.map +1 -1
  328. package/dist/data/verification/verifyReportTests.d.ts +2 -1
  329. package/dist/data/verification/verifyReportTests.d.ts.map +1 -1
  330. package/dist/data/verification/verifyReportTests.js +1 -0
  331. package/dist/data/verification/verifyReportTests.js.map +1 -1
  332. package/dist/data/version-info.cjs +3 -3
  333. package/dist/data/version-info.js +3 -3
  334. package/dist/data.cjs +58 -3
  335. package/dist/data.cjs.map +1 -1
  336. package/dist/data.d.ts +3 -0
  337. package/dist/data.d.ts.map +1 -1
  338. package/dist/data.js +60 -5
  339. package/dist/data.js.map +1 -1
  340. package/dist/icons/components/UiLogoEslint.cjs +10 -0
  341. package/dist/icons/components/UiLogoEslint.cjs.map +1 -0
  342. package/dist/icons/components/UiLogoEslint.d.ts +9 -0
  343. package/dist/icons/components/UiLogoEslint.d.ts.map +1 -0
  344. package/dist/icons/components/UiLogoEslint.js +10 -0
  345. package/dist/icons/components/UiLogoEslint.js.map +1 -0
  346. package/dist/icons/components/UiLogoGo.cjs +10 -0
  347. package/dist/icons/components/UiLogoGo.cjs.map +1 -0
  348. package/dist/icons/components/UiLogoGo.d.ts +9 -0
  349. package/dist/icons/components/UiLogoGo.d.ts.map +1 -0
  350. package/dist/icons/components/UiLogoGo.js +10 -0
  351. package/dist/icons/components/UiLogoGo.js.map +1 -0
  352. package/dist/icons/components/UiLogoJest.cjs +10 -0
  353. package/dist/icons/components/UiLogoJest.cjs.map +1 -0
  354. package/dist/icons/components/UiLogoJest.d.ts +9 -0
  355. package/dist/icons/components/UiLogoJest.d.ts.map +1 -0
  356. package/dist/icons/components/UiLogoJest.js +10 -0
  357. package/dist/icons/components/UiLogoJest.js.map +1 -0
  358. package/dist/icons/components/UiLogoMarkdown.cjs +10 -0
  359. package/dist/icons/components/UiLogoMarkdown.cjs.map +1 -0
  360. package/dist/icons/components/UiLogoMarkdown.d.ts +9 -0
  361. package/dist/icons/components/UiLogoMarkdown.d.ts.map +1 -0
  362. package/dist/icons/components/UiLogoMarkdown.js +10 -0
  363. package/dist/icons/components/UiLogoMarkdown.js.map +1 -0
  364. package/dist/icons/components/UiLogoOxc.cjs +10 -0
  365. package/dist/icons/components/UiLogoOxc.cjs.map +1 -0
  366. package/dist/icons/components/UiLogoOxc.d.ts +9 -0
  367. package/dist/icons/components/UiLogoOxc.d.ts.map +1 -0
  368. package/dist/icons/components/UiLogoOxc.js +10 -0
  369. package/dist/icons/components/UiLogoOxc.js.map +1 -0
  370. package/dist/icons/components/UiLogoPlaywright.cjs +10 -0
  371. package/dist/icons/components/UiLogoPlaywright.cjs.map +1 -0
  372. package/dist/icons/components/UiLogoPlaywright.d.ts +9 -0
  373. package/dist/icons/components/UiLogoPlaywright.d.ts.map +1 -0
  374. package/dist/icons/components/UiLogoPlaywright.js +10 -0
  375. package/dist/icons/components/UiLogoPlaywright.js.map +1 -0
  376. package/dist/icons/components/UiLogoPython.cjs +10 -0
  377. package/dist/icons/components/UiLogoPython.cjs.map +1 -0
  378. package/dist/icons/components/UiLogoPython.d.ts +9 -0
  379. package/dist/icons/components/UiLogoPython.d.ts.map +1 -0
  380. package/dist/icons/components/UiLogoPython.js +10 -0
  381. package/dist/icons/components/UiLogoPython.js.map +1 -0
  382. package/dist/icons/components/UiLogoReact.cjs +10 -0
  383. package/dist/icons/components/UiLogoReact.cjs.map +1 -0
  384. package/dist/icons/components/UiLogoReact.d.ts +9 -0
  385. package/dist/icons/components/UiLogoReact.d.ts.map +1 -0
  386. package/dist/icons/components/UiLogoReact.js +10 -0
  387. package/dist/icons/components/UiLogoReact.js.map +1 -0
  388. package/dist/icons/components/UiLogoRuff.cjs +10 -0
  389. package/dist/icons/components/UiLogoRuff.cjs.map +1 -0
  390. package/dist/icons/components/UiLogoRuff.d.ts +9 -0
  391. package/dist/icons/components/UiLogoRuff.d.ts.map +1 -0
  392. package/dist/icons/components/UiLogoRuff.js +10 -0
  393. package/dist/icons/components/UiLogoRuff.js.map +1 -0
  394. package/dist/icons/components/UiLogoTypescript.cjs +10 -0
  395. package/dist/icons/components/UiLogoTypescript.cjs.map +1 -0
  396. package/dist/icons/components/UiLogoTypescript.d.ts +9 -0
  397. package/dist/icons/components/UiLogoTypescript.d.ts.map +1 -0
  398. package/dist/icons/components/UiLogoTypescript.js +10 -0
  399. package/dist/icons/components/UiLogoTypescript.js.map +1 -0
  400. package/dist/icons/components/UiLogoVitest.cjs +10 -0
  401. package/dist/icons/components/UiLogoVitest.cjs.map +1 -0
  402. package/dist/icons/components/UiLogoVitest.d.ts +9 -0
  403. package/dist/icons/components/UiLogoVitest.d.ts.map +1 -0
  404. package/dist/icons/components/UiLogoVitest.js +10 -0
  405. package/dist/icons/components/UiLogoVitest.js.map +1 -0
  406. package/dist/icons/index.d.ts +11 -0
  407. package/dist/icons/index.d.ts.map +1 -1
  408. package/dist/icons/registry.cjs +22 -0
  409. package/dist/icons/registry.cjs.map +1 -1
  410. package/dist/icons/registry.d.ts.map +1 -1
  411. package/dist/icons/registry.js +22 -0
  412. package/dist/icons/registry.js.map +1 -1
  413. package/dist/icons.cjs +22 -0
  414. package/dist/icons.cjs.map +1 -1
  415. package/dist/icons.js +22 -0
  416. package/dist/icons.js.map +1 -1
  417. package/dist/index.cjs +60 -3
  418. package/dist/index.cjs.map +1 -1
  419. package/dist/index.js +62 -5
  420. package/dist/index.js.map +1 -1
  421. package/dist/overlay/DropdownMenuSubmenu.cjs +4 -4
  422. package/dist/overlay/DropdownMenuSubmenu.cjs.map +1 -1
  423. package/dist/overlay/DropdownMenuSubmenu.js +4 -4
  424. package/dist/overlay/DropdownMenuSubmenu.js.map +1 -1
  425. package/dist/rpc/FilterForm.cjs +9 -0
  426. package/dist/rpc/FilterForm.cjs.map +1 -1
  427. package/dist/rpc/FilterForm.d.ts +5 -1
  428. package/dist/rpc/FilterForm.d.ts.map +1 -1
  429. package/dist/rpc/FilterForm.js +9 -0
  430. package/dist/rpc/FilterForm.js.map +1 -1
  431. package/dist/rpc/InlineError.cjs +12 -43
  432. package/dist/rpc/InlineError.cjs.map +1 -1
  433. package/dist/rpc/InlineError.d.ts.map +1 -1
  434. package/dist/rpc/InlineError.js +13 -44
  435. package/dist/rpc/InlineError.js.map +1 -1
  436. package/dist/rpc/OperationArgsForm.cjs +153 -0
  437. package/dist/rpc/OperationArgsForm.cjs.map +1 -0
  438. package/dist/rpc/OperationArgsForm.d.ts +16 -0
  439. package/dist/rpc/OperationArgsForm.d.ts.map +1 -0
  440. package/dist/rpc/OperationArgsForm.js +153 -0
  441. package/dist/rpc/OperationArgsForm.js.map +1 -0
  442. package/dist/rpc/OperationScheduleDialog.cjs +119 -22
  443. package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
  444. package/dist/rpc/OperationScheduleDialog.d.ts +7 -0
  445. package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
  446. package/dist/rpc/OperationScheduleDialog.js +120 -23
  447. package/dist/rpc/OperationScheduleDialog.js.map +1 -1
  448. package/dist/rpc/OperationSchedules.cjs +17 -2
  449. package/dist/rpc/OperationSchedules.cjs.map +1 -1
  450. package/dist/rpc/OperationSchedules.d.ts.map +1 -1
  451. package/dist/rpc/OperationSchedules.js +17 -2
  452. package/dist/rpc/OperationSchedules.js.map +1 -1
  453. package/dist/rpc/operation-schedule-model.cjs +2 -7
  454. package/dist/rpc/operation-schedule-model.cjs.map +1 -1
  455. package/dist/rpc/operation-schedule-model.d.ts +0 -2
  456. package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
  457. package/dist/rpc/operation-schedule-model.js +2 -7
  458. package/dist/rpc/operation-schedule-model.js.map +1 -1
  459. package/dist/rpc/operation-schedule-types.d.ts +12 -1
  460. package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
  461. package/dist/rpc.cjs +2 -0
  462. package/dist/rpc.cjs.map +1 -1
  463. package/dist/rpc.d.ts +1 -0
  464. package/dist/rpc.d.ts.map +1 -1
  465. package/dist/rpc.js +2 -0
  466. package/dist/rpc.js.map +1 -1
  467. package/dist/styles.css +1 -1
  468. package/package.json +1 -1
  469. package/dist/data/test-runner/frameworkIcon.cjs +0 -27
  470. package/dist/data/test-runner/frameworkIcon.cjs.map +0 -1
  471. package/dist/data/test-runner/frameworkIcon.d.ts +0 -3
  472. package/dist/data/test-runner/frameworkIcon.d.ts.map +0 -1
  473. package/dist/data/test-runner/frameworkIcon.js +0 -27
  474. package/dist/data/test-runner/frameworkIcon.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { useMemo } from "react";
3
+ import { EmptyState } from "./SessionInspector.panel-parts.js";
3
4
  import { cn } from "../../lib/utils.js";
4
5
  import { createTurnPalette, shortSessionId } from "./SessionInspector.model.js";
5
6
  import { SessionViewer } from "./SessionViewer.js";
@@ -17,6 +18,7 @@ function SessionTranscript({
17
18
  session,
18
19
  viewerProps
19
20
  }) {
21
+ var _a, _b;
20
22
  const turns = session.turns ?? [];
21
23
  const palette = useMemo(
22
24
  () => createTurnPalette(
@@ -35,6 +37,9 @@ function SessionTranscript({
35
37
  );
36
38
  const viewerClassName = cn("min-h-0 flex-1", viewerProps == null ? void 0 : viewerProps.className);
37
39
  const scrollResetKey = session.id ?? (viewerProps == null ? void 0 : viewerProps.scrollResetKey);
40
+ if (session.detailAvailable === false && !((_a = session.messages) == null ? void 0 : _a.length) && !((_b = session.events) == null ? void 0 : _b.length)) {
41
+ return /* @__PURE__ */ jsx(EmptyState, { children: "Transcript unavailable" });
42
+ }
38
43
  return /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-0 flex-col", children: /* @__PURE__ */ jsx(
39
44
  SessionViewer,
40
45
  {
@@ -1 +1 @@
1
- {"version":3,"file":"SessionInspector.transcript.js","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n createTurnPalette,\n shortSessionId,\n type TurnPalette,\n} from \"./SessionInspector.model\";\nimport {\n SessionViewer,\n type SessionEvent,\n type SessionViewerProps,\n} from \"./SessionViewer\";\nimport type { SessionTurn, UnifiedSessionInput } from \"./SessionViewer.unified\";\n\ntype TurnBadge = {\n id: string;\n label: string;\n color: TurnPalette;\n className: string;\n};\n\nconst TURN_COLOR_CLASS: Record<TurnPalette, string> = {\n sky: \"border-sky-300 bg-sky-100 text-sky-800 [[data-theme=dark]_&]:border-sky-700 [[data-theme=dark]_&]:bg-sky-950/60 [[data-theme=dark]_&]:text-sky-300\",\n violet:\n \"border-violet-300 bg-violet-100 text-violet-800 [[data-theme=dark]_&]:border-violet-700 [[data-theme=dark]_&]:bg-violet-950/60 [[data-theme=dark]_&]:text-violet-300\",\n emerald:\n \"border-emerald-300 bg-emerald-100 text-emerald-800 [[data-theme=dark]_&]:border-emerald-700 [[data-theme=dark]_&]:bg-emerald-950/60 [[data-theme=dark]_&]:text-emerald-300\",\n orange:\n \"border-orange-300 bg-orange-100 text-orange-800 [[data-theme=dark]_&]:border-orange-700 [[data-theme=dark]_&]:bg-orange-950/60 [[data-theme=dark]_&]:text-orange-300\",\n rose: \"border-rose-300 bg-rose-100 text-rose-800 [[data-theme=dark]_&]:border-rose-700 [[data-theme=dark]_&]:bg-rose-950/60 [[data-theme=dark]_&]:text-rose-300\",\n indigo:\n \"border-indigo-300 bg-indigo-100 text-indigo-800 [[data-theme=dark]_&]:border-indigo-700 [[data-theme=dark]_&]:bg-indigo-950/60 [[data-theme=dark]_&]:text-indigo-300\",\n teal: \"border-teal-300 bg-teal-100 text-teal-800 [[data-theme=dark]_&]:border-teal-700 [[data-theme=dark]_&]:bg-teal-950/60 [[data-theme=dark]_&]:text-teal-300\",\n fuchsia:\n \"border-fuchsia-300 bg-fuchsia-100 text-fuchsia-800 [[data-theme=dark]_&]:border-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-950/60 [[data-theme=dark]_&]:text-fuchsia-300\",\n};\n\nexport function SessionTranscript({\n session,\n viewerProps,\n}: {\n session: UnifiedSessionInput;\n viewerProps?: Omit<SessionViewerProps, \"session\">;\n}) {\n const turns = session.turns ?? [];\n const palette = useMemo(\n () =>\n createTurnPalette(\n session.id,\n turns.map((turn) => turn.id),\n ),\n [session.id, turns],\n );\n const turnBadges = useMemo(\n () => buildTurnBadges(turns, palette),\n [palette, turns],\n );\n const turnBadgeById = useMemo(\n () => new Map(turnBadges.map((badge) => [badge.id, badge])),\n [turnBadges],\n );\n const viewerClassName = cn(\"min-h-0 flex-1\", viewerProps?.className);\n const scrollResetKey = session.id ?? viewerProps?.scrollResetKey;\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <SessionViewer\n session={session}\n scrollable\n {...viewerProps}\n windowSize={viewerProps?.windowSize ?? 10}\n batchSize={viewerProps?.batchSize ?? 10}\n {...(scrollResetKey !== undefined\n ? { scrollResetKey }\n : {})}\n className={viewerClassName}\n showHeader={false}\n showContextMeter={false}\n showRowMetadata={false}\n renderMessageBadge={(event: SessionEvent) => {\n const badge = event.turnId\n ? turnBadgeById.get(event.turnId)\n : undefined;\n return badge ? (\n <TranscriptTurnBadge badge={badge} placement=\"message\" />\n ) : null;\n }}\n headerActions={\n <>\n {viewerProps?.headerActions}\n <TurnBadgeLegend badges={turnBadges} />\n </>\n }\n />\n </div>\n );\n}\n\nfunction buildTurnBadges(\n turns: SessionTurn[],\n palette: Record<string, TurnPalette>,\n) {\n return turns.map((turn): TurnBadge => {\n const color = palette[turn.id] ?? \"sky\";\n return {\n id: turn.id,\n label: shortSessionId(turn.id),\n color,\n className: TURN_COLOR_CLASS[color],\n };\n });\n}\n\nfunction TurnBadgeLegend({ badges }: { badges: TurnBadge[] }) {\n return (\n <div className=\"flex max-w-full flex-wrap gap-1\" aria-label=\"Turn colors\">\n {badges.map((badge) => (\n <TranscriptTurnBadge key={badge.id} badge={badge} placement=\"legend\" />\n ))}\n </div>\n );\n}\n\nfunction TranscriptTurnBadge({\n badge,\n placement,\n}: {\n badge: TurnBadge;\n placement: \"legend\" | \"message\";\n}) {\n return (\n <span\n data-testid={\n placement === \"legend\" ? \"transcript-turn-badge\" : \"message-turn-badge\"\n }\n data-turn-color={badge.color}\n title={`Turn ${badge.id}`}\n className={cn(\n \"inline-flex shrink-0 rounded border px-1 py-px font-mono text-[9px] font-semibold leading-none\",\n placement === \"message\" && \"mt-1\",\n badge.className,\n )}\n >\n {badge.label}\n </span>\n );\n}\n"],"names":[],"mappings":";;;;;AAqBA,MAAM,mBAAgD;AAAA,EACpD,KAAK;AAAA,EACL,QACE;AAAA,EACF,SACE;AAAA,EACF,QACE;AAAA,EACF,MAAM;AAAA,EACN,QACE;AAAA,EACF,MAAM;AAAA,EACN,SACE;AACJ;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AACF,GAGG;AACD,QAAM,QAAQ,QAAQ,SAAS,CAAA;AAC/B,QAAM,UAAU;AAAA,IACd,MACE;AAAA,MACE,QAAQ;AAAA,MACR,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,IAAA;AAAA,IAE/B,CAAC,QAAQ,IAAI,KAAK;AAAA,EAAA;AAEpB,QAAM,aAAa;AAAA,IACjB,MAAM,gBAAgB,OAAO,OAAO;AAAA,IACpC,CAAC,SAAS,KAAK;AAAA,EAAA;AAEjB,QAAM,gBAAgB;AAAA,IACpB,MAAM,IAAI,IAAI,WAAW,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AAAA,IAC1D,CAAC,UAAU;AAAA,EAAA;AAEb,QAAM,kBAAkB,GAAG,kBAAkB,2CAAa,SAAS;AACnE,QAAM,iBAAiB,QAAQ,OAAM,2CAAa;AAElD,SACE,oBAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,YAAU;AAAA,MACT,GAAG;AAAA,MACJ,aAAY,2CAAa,eAAc;AAAA,MACvC,YAAW,2CAAa,cAAa;AAAA,MACpC,GAAI,mBAAmB,SACpB,EAAE,eAAA,IACF,CAAA;AAAA,MACJ,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,MACjB,oBAAoB,CAAC,UAAwB;AAC3C,cAAM,QAAQ,MAAM,SAChB,cAAc,IAAI,MAAM,MAAM,IAC9B;AACJ,eAAO,QACL,oBAAC,qBAAA,EAAoB,OAAc,WAAU,WAAU,IACrD;AAAA,MACN;AAAA,MACA,eACE,qBAAA,UAAA,EACG,UAAA;AAAA,QAAA,2CAAa;AAAA,QACd,oBAAC,iBAAA,EAAgB,QAAQ,WAAA,CAAY;AAAA,MAAA,EAAA,CACvC;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ;AAEA,SAAS,gBACP,OACA,SACA;AACA,SAAO,MAAM,IAAI,CAAC,SAAoB;AACpC,UAAM,QAAQ,QAAQ,KAAK,EAAE,KAAK;AAClC,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,OAAO,eAAe,KAAK,EAAE;AAAA,MAC7B;AAAA,MACA,WAAW,iBAAiB,KAAK;AAAA,IAAA;AAAA,EAErC,CAAC;AACH;AAEA,SAAS,gBAAgB,EAAE,UAAmC;AAC5D,6BACG,OAAA,EAAI,WAAU,mCAAkC,cAAW,eACzD,iBAAO,IAAI,CAAC,UACX,oBAAC,uBAAmC,OAAc,WAAU,YAAlC,MAAM,EAAqC,CACtE,GACH;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AACF,GAGG;AACD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eACE,cAAc,WAAW,0BAA0B;AAAA,MAErD,mBAAiB,MAAM;AAAA,MACvB,OAAO,QAAQ,MAAM,EAAE;AAAA,MACvB,WAAW;AAAA,QACT;AAAA,QACA,cAAc,aAAa;AAAA,QAC3B,MAAM;AAAA,MAAA;AAAA,MAGP,UAAA,MAAM;AAAA,IAAA;AAAA,EAAA;AAGb;"}
1
+ {"version":3,"file":"SessionInspector.transcript.js","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { EmptyState } from \"./SessionInspector.panel-parts\";\nimport { cn } from \"../../lib/utils\";\nimport {\n createTurnPalette,\n shortSessionId,\n type TurnPalette,\n} from \"./SessionInspector.model\";\nimport {\n SessionViewer,\n type SessionEvent,\n type SessionViewerProps,\n} from \"./SessionViewer\";\nimport type { SessionTurn, UnifiedSessionInput } from \"./SessionViewer.unified\";\n\ntype TurnBadge = {\n id: string;\n label: string;\n color: TurnPalette;\n className: string;\n};\n\nconst TURN_COLOR_CLASS: Record<TurnPalette, string> = {\n sky: \"border-sky-300 bg-sky-100 text-sky-800 [[data-theme=dark]_&]:border-sky-700 [[data-theme=dark]_&]:bg-sky-950/60 [[data-theme=dark]_&]:text-sky-300\",\n violet:\n \"border-violet-300 bg-violet-100 text-violet-800 [[data-theme=dark]_&]:border-violet-700 [[data-theme=dark]_&]:bg-violet-950/60 [[data-theme=dark]_&]:text-violet-300\",\n emerald:\n \"border-emerald-300 bg-emerald-100 text-emerald-800 [[data-theme=dark]_&]:border-emerald-700 [[data-theme=dark]_&]:bg-emerald-950/60 [[data-theme=dark]_&]:text-emerald-300\",\n orange:\n \"border-orange-300 bg-orange-100 text-orange-800 [[data-theme=dark]_&]:border-orange-700 [[data-theme=dark]_&]:bg-orange-950/60 [[data-theme=dark]_&]:text-orange-300\",\n rose: \"border-rose-300 bg-rose-100 text-rose-800 [[data-theme=dark]_&]:border-rose-700 [[data-theme=dark]_&]:bg-rose-950/60 [[data-theme=dark]_&]:text-rose-300\",\n indigo:\n \"border-indigo-300 bg-indigo-100 text-indigo-800 [[data-theme=dark]_&]:border-indigo-700 [[data-theme=dark]_&]:bg-indigo-950/60 [[data-theme=dark]_&]:text-indigo-300\",\n teal: \"border-teal-300 bg-teal-100 text-teal-800 [[data-theme=dark]_&]:border-teal-700 [[data-theme=dark]_&]:bg-teal-950/60 [[data-theme=dark]_&]:text-teal-300\",\n fuchsia:\n \"border-fuchsia-300 bg-fuchsia-100 text-fuchsia-800 [[data-theme=dark]_&]:border-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-950/60 [[data-theme=dark]_&]:text-fuchsia-300\",\n};\n\nexport function SessionTranscript({\n session,\n viewerProps,\n}: {\n session: UnifiedSessionInput;\n viewerProps?: Omit<SessionViewerProps, \"session\">;\n}) {\n const turns = session.turns ?? [];\n const palette = useMemo(\n () =>\n createTurnPalette(\n session.id,\n turns.map((turn) => turn.id)\n ),\n [session.id, turns]\n );\n const turnBadges = useMemo(\n () => buildTurnBadges(turns, palette),\n [palette, turns]\n );\n const turnBadgeById = useMemo(\n () => new Map(turnBadges.map((badge) => [badge.id, badge])),\n [turnBadges]\n );\n const viewerClassName = cn(\"min-h-0 flex-1\", viewerProps?.className);\n const scrollResetKey = session.id ?? viewerProps?.scrollResetKey;\n\n if (\n session.detailAvailable === false &&\n !session.messages?.length &&\n !session.events?.length\n ) {\n return <EmptyState>Transcript unavailable</EmptyState>;\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <SessionViewer\n session={session}\n scrollable\n {...viewerProps}\n windowSize={viewerProps?.windowSize ?? 10}\n batchSize={viewerProps?.batchSize ?? 10}\n {...(scrollResetKey !== undefined ? { scrollResetKey } : {})}\n className={viewerClassName}\n showHeader={false}\n showContextMeter={false}\n showRowMetadata={false}\n renderMessageBadge={(event: SessionEvent) => {\n const badge = event.turnId\n ? turnBadgeById.get(event.turnId)\n : undefined;\n return badge ? (\n <TranscriptTurnBadge badge={badge} placement=\"message\" />\n ) : null;\n }}\n headerActions={\n <>\n {viewerProps?.headerActions}\n <TurnBadgeLegend badges={turnBadges} />\n </>\n }\n />\n </div>\n );\n}\n\nfunction buildTurnBadges(\n turns: SessionTurn[],\n palette: Record<string, TurnPalette>\n) {\n return turns.map((turn): TurnBadge => {\n const color = palette[turn.id] ?? \"sky\";\n return {\n id: turn.id,\n label: shortSessionId(turn.id),\n color,\n className: TURN_COLOR_CLASS[color],\n };\n });\n}\n\nfunction TurnBadgeLegend({ badges }: { badges: TurnBadge[] }) {\n return (\n <div className=\"flex max-w-full flex-wrap gap-1\" aria-label=\"Turn colors\">\n {badges.map((badge) => (\n <TranscriptTurnBadge key={badge.id} badge={badge} placement=\"legend\" />\n ))}\n </div>\n );\n}\n\nfunction TranscriptTurnBadge({\n badge,\n placement,\n}: {\n badge: TurnBadge;\n placement: \"legend\" | \"message\";\n}) {\n return (\n <span\n data-testid={\n placement === \"legend\" ? \"transcript-turn-badge\" : \"message-turn-badge\"\n }\n data-turn-color={badge.color}\n title={`Turn ${badge.id}`}\n className={cn(\n \"inline-flex shrink-0 rounded border px-1 py-px font-mono text-[9px] font-semibold leading-none\",\n placement === \"message\" && \"mt-1\",\n badge.className\n )}\n >\n {badge.label}\n </span>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const utils = require("../../lib/utils.cjs");
6
+ const Icon = require("../Icon.cjs");
7
+ const sessionCategories = require("./session-categories.cjs");
8
+ const SessionInspector_remote = require("./SessionInspector.remote.cjs");
9
+ const SessionViewer_model = require("./SessionViewer.model.cjs");
10
+ const SessionViewer_input = require("./SessionViewer.input.cjs");
11
+ const UiLoader = require("../../icons/components/UiLoader.cjs");
12
+ const UiSparkles = require("../../icons/components/UiSparkles.cjs");
13
+ const TAIL_PAGE_SIZE = 20;
14
+ function latestTailEvent(events) {
15
+ return events.findLast(
16
+ (event) => event.kind === "tool" || event.kind === "assistant" && Boolean(event.text)
17
+ );
18
+ }
19
+ function ToolLine({ event }) {
20
+ const tool = event.tool ?? "";
21
+ const summary = SessionViewer_input.summarizeToolInput(tool, event.toolInput, event.cwd);
22
+ const category = sessionCategories.classifyToolCategory(tool, event.toolInput);
23
+ return /* @__PURE__ */ jsxRuntime.jsxs(
24
+ "span",
25
+ {
26
+ "data-testid": "session-tail-tool",
27
+ title: summary ? `${tool} ${summary}` : tool,
28
+ className: "flex min-w-0 items-center gap-1.5",
29
+ children: [
30
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: sessionCategories.CATEGORY_ICONS[category], className: "size-3.5 shrink-0" }),
31
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-medium text-foreground", children: tool }),
32
+ summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate font-mono", children: summary })
33
+ ]
34
+ }
35
+ );
36
+ }
37
+ function TextLine({ text }) {
38
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
39
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiSparkles.UiSparkles, className: "size-3.5 shrink-0" }),
40
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "session-tail-text", title: text, className: "min-w-0 truncate", children: text })
41
+ ] });
42
+ }
43
+ function SessionTail({ src, follow = true, className, emptyLabel }) {
44
+ const { session, error } = SessionInspector_remote.useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });
45
+ const latest = react.useMemo(
46
+ () => session ? latestTailEvent(SessionViewer_model.normalizeMessages(session.messages ?? [])) : void 0,
47
+ [session]
48
+ );
49
+ const following = follow && !SessionInspector_remote.isTerminalSessionLifecycle(session == null ? void 0 : session.lifecycleStatus);
50
+ return /* @__PURE__ */ jsxRuntime.jsxs(
51
+ "div",
52
+ {
53
+ "data-session-tail": true,
54
+ className: utils.cn("flex min-w-0 items-center gap-2 text-xs text-muted-foreground", className),
55
+ children: [
56
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", title: error, className: "block truncate text-destructive", children: error }) : (latest == null ? void 0 : latest.kind) === "tool" ? /* @__PURE__ */ jsxRuntime.jsx(ToolLine, { event: latest }) : (latest == null ? void 0 : latest.text) ? /* @__PURE__ */ jsxRuntime.jsx(TextLine, { text: latest.text }) : emptyLabel !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: emptyLabel }) }),
57
+ following && /* @__PURE__ */ jsxRuntime.jsx("span", { role: "status", "aria-label": "Following session", className: "shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiLoader.UiLoader, className: "size-3.5 animate-spin" }) })
58
+ ]
59
+ }
60
+ );
61
+ }
62
+ exports.SessionTail = SessionTail;
63
+ //# sourceMappingURL=SessionTail.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionTail.cjs","sources":["../../../src/data/ai/SessionTail.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiLoader, UiSparkles } from \"../../icons\";\nimport { CATEGORY_ICONS, classifyToolCategory } from \"./session-categories\";\nimport { isTerminalSessionLifecycle, useRemoteSession } from \"./SessionInspector.remote\";\nimport { normalizeMessages, type SessionEvent } from \"./SessionViewer.model\";\nimport { summarizeToolInput } from \"./SessionViewer.input\";\n\n/** Messages read for the first load: enough to reach past a run of reasoning\n * or empty messages to the latest text or tool call. */\nconst TAIL_PAGE_SIZE = 20;\n\nexport interface SessionTailProps {\n /** Captain session URL, e.g. `/api/captain/sessions/{id}`. */\n src: string;\n /** Follow the live stream (default true). When false the session is read\n * once and no spinner is shown. */\n follow?: boolean;\n className?: string;\n /** Shown until the session has a text message or tool call. */\n emptyLabel?: string;\n}\n\n/** The newest assistant text or tool call; reasoning, user, system and error\n * rows are not part of the tail. */\nfunction latestTailEvent(events: SessionEvent[]): SessionEvent | undefined {\n return events.findLast(\n (event) => event.kind === \"tool\" || (event.kind === \"assistant\" && Boolean(event.text)),\n );\n}\n\nfunction ToolLine({ event }: { event: SessionEvent }) {\n const tool = event.tool ?? \"\";\n const summary = summarizeToolInput(tool, event.toolInput, event.cwd);\n const category = classifyToolCategory(tool, event.toolInput);\n return (\n <span\n data-testid=\"session-tail-tool\"\n title={summary ? `${tool} ${summary}` : tool}\n className=\"flex min-w-0 items-center gap-1.5\"\n >\n <Icon icon={CATEGORY_ICONS[category]} className=\"size-3.5 shrink-0\" />\n <span className=\"shrink-0 font-medium text-foreground\">{tool}</span>\n {summary && <span className=\"min-w-0 truncate font-mono\">{summary}</span>}\n </span>\n );\n}\n\nfunction TextLine({ text }: { text: string }) {\n return (\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Icon icon={UiSparkles} className=\"size-3.5 shrink-0\" />\n <span data-testid=\"session-tail-text\" title={text} className=\"min-w-0 truncate\">\n {text}\n </span>\n </span>\n );\n}\n\n/** One line showing only the newest entry of a captain session: the latest\n * assistant text or the latest tool call, with a spinner while following. */\nexport function SessionTail({ src, follow = true, className, emptyLabel }: SessionTailProps) {\n const { session, error } = useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });\n const latest = useMemo(\n () => (session ? latestTailEvent(normalizeMessages(session.messages ?? [])) : undefined),\n [session],\n );\n const following = follow && !isTerminalSessionLifecycle(session?.lifecycleStatus);\n\n return (\n <div\n data-session-tail\n className={cn(\"flex min-w-0 items-center gap-2 text-xs text-muted-foreground\", className)}\n >\n <div className=\"min-w-0 flex-1\">\n {error !== undefined ? (\n <span role=\"alert\" title={error} className=\"block truncate text-destructive\">\n {error}\n </span>\n ) : latest?.kind === \"tool\" ? (\n <ToolLine event={latest} />\n ) : latest?.text ? (\n <TextLine text={latest.text} />\n ) : (\n emptyLabel !== undefined && <span className=\"block truncate\">{emptyLabel}</span>\n )}\n </div>\n {following && (\n <span role=\"status\" aria-label=\"Following session\" className=\"shrink-0\">\n <Icon icon={UiLoader} className=\"size-3.5 animate-spin\" />\n </span>\n )}\n </div>\n );\n}\n"],"names":["summarizeToolInput","classifyToolCategory","jsxs","jsx","Icon","CATEGORY_ICONS","UiSparkles","useRemoteSession","useMemo","normalizeMessages","isTerminalSessionLifecycle","cn","UiLoader"],"mappings":";;;;;;;;;;;;AAWA,MAAM,iBAAiB;AAevB,SAAS,gBAAgB,QAAkD;AACzE,SAAO,OAAO;AAAA,IACZ,CAAC,UAAU,MAAM,SAAS,UAAW,MAAM,SAAS,eAAe,QAAQ,MAAM,IAAI;AAAA,EAAA;AAEzF;AAEA,SAAS,SAAS,EAAE,SAAkC;AACpD,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAUA,oBAAAA,mBAAmB,MAAM,MAAM,WAAW,MAAM,GAAG;AACnE,QAAM,WAAWC,kBAAAA,qBAAqB,MAAM,MAAM,SAAS;AAC3D,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,OAAO,UAAU,GAAG,IAAI,IAAI,OAAO,KAAK;AAAA,MACxC,WAAU;AAAA,MAEV,UAAA;AAAA,QAAAC,+BAACC,KAAAA,QAAK,MAAMC,kBAAAA,eAAe,QAAQ,GAAG,WAAU,qBAAoB;AAAA,QACpEF,2BAAAA,IAAC,QAAA,EAAK,WAAU,wCAAwC,UAAA,MAAK;AAAA,QAC5D,WAAWA,2BAAAA,IAAC,QAAA,EAAK,WAAU,8BAA8B,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGxE;AAEA,SAAS,SAAS,EAAE,QAA0B;AAC5C,SACED,2BAAAA,KAAC,QAAA,EAAK,WAAU,qCACd,UAAA;AAAA,IAAAC,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAME,WAAAA,YAAY,WAAU,qBAAoB;AAAA,IACtDH,2BAAAA,IAAC,UAAK,eAAY,qBAAoB,OAAO,MAAM,WAAU,oBAC1D,UAAA,KAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAIO,SAAS,YAAY,EAAE,KAAK,SAAS,MAAM,WAAW,cAAgC;AAC3F,QAAM,EAAE,SAAS,MAAA,IAAUI,wBAAAA,iBAAiB,KAAK,EAAE,QAAQ,UAAU,gBAAgB;AACrF,QAAM,SAASC,MAAAA;AAAAA,IACb,MAAO,UAAU,gBAAgBC,oBAAAA,kBAAkB,QAAQ,YAAY,CAAA,CAAE,CAAC,IAAI;AAAA,IAC9E,CAAC,OAAO;AAAA,EAAA;AAEV,QAAM,YAAY,UAAU,CAACC,wBAAAA,2BAA2B,mCAAS,eAAe;AAEhF,SACER,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,qBAAiB;AAAA,MACjB,WAAWS,MAAAA,GAAG,iEAAiE,SAAS;AAAA,MAExF,UAAA;AAAA,QAAAR,+BAAC,SAAI,WAAU,kBACZ,UAAA,UAAU,wCACR,QAAA,EAAK,MAAK,SAAQ,OAAO,OAAO,WAAU,mCACxC,iBACH,KACE,iCAAQ,UAAS,SACnBA,2BAAAA,IAAC,UAAA,EAAS,OAAO,QAAQ,KACvB,iCAAQ,QACVA,2BAAAA,IAAC,YAAS,MAAM,OAAO,MAAM,IAE7B,eAAe,UAAaA,2BAAAA,IAAC,UAAK,WAAU,kBAAkB,sBAAW,GAE7E;AAAA,QACC,aACCA,2BAAAA,IAAC,QAAA,EAAK,MAAK,UAAS,cAAW,qBAAoB,WAAU,YAC3D,yCAACC,KAAAA,MAAA,EAAK,MAAMQ,SAAAA,UAAU,WAAU,yBAAwB,EAAA,CAC1D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;;"}
@@ -0,0 +1,14 @@
1
+ export interface SessionTailProps {
2
+ /** Captain session URL, e.g. `/api/captain/sessions/{id}`. */
3
+ src: string;
4
+ /** Follow the live stream (default true). When false the session is read
5
+ * once and no spinner is shown. */
6
+ follow?: boolean;
7
+ className?: string;
8
+ /** Shown until the session has a text message or tool call. */
9
+ emptyLabel?: string;
10
+ }
11
+ /** One line showing only the newest entry of a captain session: the latest
12
+ * assistant text or the latest tool call, with a spinner while following. */
13
+ export declare function SessionTail({ src, follow, className, emptyLabel }: SessionTailProps): import("react").JSX.Element;
14
+ //# sourceMappingURL=SessionTail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionTail.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionTail.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ;wCACoC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsCD;8EAC8E;AAC9E,wBAAgB,WAAW,CAAC,EAAE,GAAG,EAAE,MAAa,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,gBAAgB,+BAiC1F"}
@@ -0,0 +1,63 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { cn } from "../../lib/utils.js";
4
+ import { Icon } from "../Icon.js";
5
+ import { classifyToolCategory, CATEGORY_ICONS } from "./session-categories.js";
6
+ import { useRemoteSession, isTerminalSessionLifecycle } from "./SessionInspector.remote.js";
7
+ import { normalizeMessages } from "./SessionViewer.model.js";
8
+ import { summarizeToolInput } from "./SessionViewer.input.js";
9
+ import { UiLoader } from "../../icons/components/UiLoader.js";
10
+ import { UiSparkles } from "../../icons/components/UiSparkles.js";
11
+ const TAIL_PAGE_SIZE = 20;
12
+ function latestTailEvent(events) {
13
+ return events.findLast(
14
+ (event) => event.kind === "tool" || event.kind === "assistant" && Boolean(event.text)
15
+ );
16
+ }
17
+ function ToolLine({ event }) {
18
+ const tool = event.tool ?? "";
19
+ const summary = summarizeToolInput(tool, event.toolInput, event.cwd);
20
+ const category = classifyToolCategory(tool, event.toolInput);
21
+ return /* @__PURE__ */ jsxs(
22
+ "span",
23
+ {
24
+ "data-testid": "session-tail-tool",
25
+ title: summary ? `${tool} ${summary}` : tool,
26
+ className: "flex min-w-0 items-center gap-1.5",
27
+ children: [
28
+ /* @__PURE__ */ jsx(Icon, { icon: CATEGORY_ICONS[category], className: "size-3.5 shrink-0" }),
29
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 font-medium text-foreground", children: tool }),
30
+ summary && /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-mono", children: summary })
31
+ ]
32
+ }
33
+ );
34
+ }
35
+ function TextLine({ text }) {
36
+ return /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
37
+ /* @__PURE__ */ jsx(Icon, { icon: UiSparkles, className: "size-3.5 shrink-0" }),
38
+ /* @__PURE__ */ jsx("span", { "data-testid": "session-tail-text", title: text, className: "min-w-0 truncate", children: text })
39
+ ] });
40
+ }
41
+ function SessionTail({ src, follow = true, className, emptyLabel }) {
42
+ const { session, error } = useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });
43
+ const latest = useMemo(
44
+ () => session ? latestTailEvent(normalizeMessages(session.messages ?? [])) : void 0,
45
+ [session]
46
+ );
47
+ const following = follow && !isTerminalSessionLifecycle(session == null ? void 0 : session.lifecycleStatus);
48
+ return /* @__PURE__ */ jsxs(
49
+ "div",
50
+ {
51
+ "data-session-tail": true,
52
+ className: cn("flex min-w-0 items-center gap-2 text-xs text-muted-foreground", className),
53
+ children: [
54
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: error !== void 0 ? /* @__PURE__ */ jsx("span", { role: "alert", title: error, className: "block truncate text-destructive", children: error }) : (latest == null ? void 0 : latest.kind) === "tool" ? /* @__PURE__ */ jsx(ToolLine, { event: latest }) : (latest == null ? void 0 : latest.text) ? /* @__PURE__ */ jsx(TextLine, { text: latest.text }) : emptyLabel !== void 0 && /* @__PURE__ */ jsx("span", { className: "block truncate", children: emptyLabel }) }),
55
+ following && /* @__PURE__ */ jsx("span", { role: "status", "aria-label": "Following session", className: "shrink-0", children: /* @__PURE__ */ jsx(Icon, { icon: UiLoader, className: "size-3.5 animate-spin" }) })
56
+ ]
57
+ }
58
+ );
59
+ }
60
+ export {
61
+ SessionTail
62
+ };
63
+ //# sourceMappingURL=SessionTail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionTail.js","sources":["../../../src/data/ai/SessionTail.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiLoader, UiSparkles } from \"../../icons\";\nimport { CATEGORY_ICONS, classifyToolCategory } from \"./session-categories\";\nimport { isTerminalSessionLifecycle, useRemoteSession } from \"./SessionInspector.remote\";\nimport { normalizeMessages, type SessionEvent } from \"./SessionViewer.model\";\nimport { summarizeToolInput } from \"./SessionViewer.input\";\n\n/** Messages read for the first load: enough to reach past a run of reasoning\n * or empty messages to the latest text or tool call. */\nconst TAIL_PAGE_SIZE = 20;\n\nexport interface SessionTailProps {\n /** Captain session URL, e.g. `/api/captain/sessions/{id}`. */\n src: string;\n /** Follow the live stream (default true). When false the session is read\n * once and no spinner is shown. */\n follow?: boolean;\n className?: string;\n /** Shown until the session has a text message or tool call. */\n emptyLabel?: string;\n}\n\n/** The newest assistant text or tool call; reasoning, user, system and error\n * rows are not part of the tail. */\nfunction latestTailEvent(events: SessionEvent[]): SessionEvent | undefined {\n return events.findLast(\n (event) => event.kind === \"tool\" || (event.kind === \"assistant\" && Boolean(event.text)),\n );\n}\n\nfunction ToolLine({ event }: { event: SessionEvent }) {\n const tool = event.tool ?? \"\";\n const summary = summarizeToolInput(tool, event.toolInput, event.cwd);\n const category = classifyToolCategory(tool, event.toolInput);\n return (\n <span\n data-testid=\"session-tail-tool\"\n title={summary ? `${tool} ${summary}` : tool}\n className=\"flex min-w-0 items-center gap-1.5\"\n >\n <Icon icon={CATEGORY_ICONS[category]} className=\"size-3.5 shrink-0\" />\n <span className=\"shrink-0 font-medium text-foreground\">{tool}</span>\n {summary && <span className=\"min-w-0 truncate font-mono\">{summary}</span>}\n </span>\n );\n}\n\nfunction TextLine({ text }: { text: string }) {\n return (\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Icon icon={UiSparkles} className=\"size-3.5 shrink-0\" />\n <span data-testid=\"session-tail-text\" title={text} className=\"min-w-0 truncate\">\n {text}\n </span>\n </span>\n );\n}\n\n/** One line showing only the newest entry of a captain session: the latest\n * assistant text or the latest tool call, with a spinner while following. */\nexport function SessionTail({ src, follow = true, className, emptyLabel }: SessionTailProps) {\n const { session, error } = useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });\n const latest = useMemo(\n () => (session ? latestTailEvent(normalizeMessages(session.messages ?? [])) : undefined),\n [session],\n );\n const following = follow && !isTerminalSessionLifecycle(session?.lifecycleStatus);\n\n return (\n <div\n data-session-tail\n className={cn(\"flex min-w-0 items-center gap-2 text-xs text-muted-foreground\", className)}\n >\n <div className=\"min-w-0 flex-1\">\n {error !== undefined ? (\n <span role=\"alert\" title={error} className=\"block truncate text-destructive\">\n {error}\n </span>\n ) : latest?.kind === \"tool\" ? (\n <ToolLine event={latest} />\n ) : latest?.text ? (\n <TextLine text={latest.text} />\n ) : (\n emptyLabel !== undefined && <span className=\"block truncate\">{emptyLabel}</span>\n )}\n </div>\n {following && (\n <span role=\"status\" aria-label=\"Following session\" className=\"shrink-0\">\n <Icon icon={UiLoader} className=\"size-3.5 animate-spin\" />\n </span>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAWA,MAAM,iBAAiB;AAevB,SAAS,gBAAgB,QAAkD;AACzE,SAAO,OAAO;AAAA,IACZ,CAAC,UAAU,MAAM,SAAS,UAAW,MAAM,SAAS,eAAe,QAAQ,MAAM,IAAI;AAAA,EAAA;AAEzF;AAEA,SAAS,SAAS,EAAE,SAAkC;AACpD,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAU,mBAAmB,MAAM,MAAM,WAAW,MAAM,GAAG;AACnE,QAAM,WAAW,qBAAqB,MAAM,MAAM,SAAS;AAC3D,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,OAAO,UAAU,GAAG,IAAI,IAAI,OAAO,KAAK;AAAA,MACxC,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,oBAAC,QAAK,MAAM,eAAe,QAAQ,GAAG,WAAU,qBAAoB;AAAA,QACpE,oBAAC,QAAA,EAAK,WAAU,wCAAwC,UAAA,MAAK;AAAA,QAC5D,WAAW,oBAAC,QAAA,EAAK,WAAU,8BAA8B,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGxE;AAEA,SAAS,SAAS,EAAE,QAA0B;AAC5C,SACE,qBAAC,QAAA,EAAK,WAAU,qCACd,UAAA;AAAA,IAAA,oBAAC,MAAA,EAAK,MAAM,YAAY,WAAU,qBAAoB;AAAA,IACtD,oBAAC,UAAK,eAAY,qBAAoB,OAAO,MAAM,WAAU,oBAC1D,UAAA,KAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAIO,SAAS,YAAY,EAAE,KAAK,SAAS,MAAM,WAAW,cAAgC;AAC3F,QAAM,EAAE,SAAS,MAAA,IAAU,iBAAiB,KAAK,EAAE,QAAQ,UAAU,gBAAgB;AACrF,QAAM,SAAS;AAAA,IACb,MAAO,UAAU,gBAAgB,kBAAkB,QAAQ,YAAY,CAAA,CAAE,CAAC,IAAI;AAAA,IAC9E,CAAC,OAAO;AAAA,EAAA;AAEV,QAAM,YAAY,UAAU,CAAC,2BAA2B,mCAAS,eAAe;AAEhF,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,qBAAiB;AAAA,MACjB,WAAW,GAAG,iEAAiE,SAAS;AAAA,MAExF,UAAA;AAAA,QAAA,oBAAC,SAAI,WAAU,kBACZ,UAAA,UAAU,6BACR,QAAA,EAAK,MAAK,SAAQ,OAAO,OAAO,WAAU,mCACxC,iBACH,KACE,iCAAQ,UAAS,SACnB,oBAAC,UAAA,EAAS,OAAO,QAAQ,KACvB,iCAAQ,QACV,oBAAC,YAAS,MAAM,OAAO,MAAM,IAE7B,eAAe,UAAa,oBAAC,UAAK,WAAU,kBAAkB,sBAAW,GAE7E;AAAA,QACC,aACC,oBAAC,QAAA,EAAK,MAAK,UAAS,cAAW,qBAAoB,WAAU,YAC3D,8BAAC,MAAA,EAAK,MAAM,UAAU,WAAU,yBAAwB,EAAA,CAC1D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;"}
@@ -41,6 +41,8 @@ function SessionViewer({
41
41
  windowSize = 60,
42
42
  batchSize = 40,
43
43
  scrollResetKey,
44
+ onReachStart,
45
+ loadingOlder = false,
44
46
  showRowMetadata = false,
45
47
  showRaw = false,
46
48
  renderMessageBadge,
@@ -98,7 +100,9 @@ function SessionViewer({
98
100
  enabled: scrollable,
99
101
  windowSize,
100
102
  batchSize,
101
- resetKey
103
+ resetKey,
104
+ ...allEvents[0] ? { firstKey: allEvents[0].id } : {},
105
+ ...onReachStart ? { onReachStart } : {}
102
106
  });
103
107
  if (allEvents.length === 0) {
104
108
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -237,13 +241,16 @@ function SessionViewer({
237
241
  }
238
242
  ),
239
243
  scrollable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-h-0 flex-1", children: [
240
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
244
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
241
245
  "div",
242
246
  {
243
247
  ref: contentRef,
244
248
  "data-session-viewer-content": true,
245
249
  className: "p-density-3 @min-[48rem]/session-viewer:p-density-4",
246
- children: list
250
+ children: [
251
+ loadingOlder ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", className: "pb-density-2 text-center text-xs text-muted-foreground", children: "Loading earlier messages…" }) : null,
252
+ list
253
+ ]
247
254
  }
248
255
  ) }),
249
256
  !following && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1 +1 @@
1
- {"version":3,"file":"SessionViewer.cjs","sources":["../../../src/data/ai/SessionViewer.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { useDensityValue, type Density } from \"../../hooks/use-density\";\nimport { DensityValueProvider } from \"../../hooks/density-provider\";\nimport { UiArrowDown, UiRobotAi } from \"../../icons\";\nimport {\n getSessionMetadata,\n normalizeSession,\n summarizeSession,\n type SessionEvent,\n type SessionInput,\n} from \"./SessionViewer.model\";\nimport {\n collapseWaitRuns,\n isSessionEventGroup,\n} from \"./SessionViewer.grouping\";\nimport { SessionRow, WaitGroupRow } from \"./SessionViewer.rows\";\nimport { SessionMetadataBadges } from \"./SessionViewer.header\";\nimport {\n collectSessionFilters,\n isEncryptedReasoning,\n isEventVisible,\n type SessionCategory,\n} from \"./session-categories\";\nimport {\n SessionViewerMenu,\n type SessionThemeOverride,\n} from \"./SessionViewerMenu\";\nimport { useSessionScroll } from \"./use-session-scroll\";\nimport type {\n ApprovalDecisionFields,\n ApprovalKind,\n ApprovalRequest,\n} from \"./approval-request\";\nimport type { SessionTokenSizer } from \"./session-token-sizing\";\nimport { SessionTokenContext, useSessionTokenSizing } from \"./use-session-token-sizing\";\nexport type { SessionTokenSizer, SessionTokenSize, SessionTokenSizingRequest, SessionTokenSizingResult } from \"./session-token-sizing\";\n\nexport type {\n SessionEntry,\n SessionEvent,\n SessionInput,\n SessionActionMeta,\n} from \"./SessionViewer.model\";\nexport type { SessionThemeOverride } from \"./SessionViewerMenu\";\n\nexport interface SessionPendingTool {\n tool: string;\n input?: Record<string, unknown>;\n toolCallId?: string;\n approvalId?: string;\n sessionId?: string;\n /** What kind of approval this is. Absent means an untyped tool approval, which\n * renders as Allow / Reject. */\n kind?: ApprovalKind;\n /** The typed approval request; drives the per-kind controls. */\n request?: ApprovalRequest;\n}\n\n/** A decision on one pending tool. `allow`/`message`/`answers` are the original\n * fields; `interrupt`, `scope`, `grants` and `content` are set only for a typed\n * request that offers them. */\nexport interface SessionToolDecision extends ApprovalDecisionFields {\n event: SessionEvent;\n allow: boolean;\n message?: string;\n}\n\nexport interface SessionViewerProps {\n\n /** Explicit menu actions only; never invoked automatically when a transcript loads. */\n sizeTokens?: SessionTokenSizer;\n /** A captain session: parsed `SessionEntry[]` or raw log text (JSON / JSONL). */\n session: SessionInput;\n className?: string;\n /** Expand each tool call's input/output by default. Defaults to false. */\n defaultExpanded?: boolean;\n /** Render assistant reasoning (\"thinking\") blocks. Defaults to true. */\n showThinking?: boolean;\n /** Show the summary header (model + action counts). Defaults to true. */\n showHeader?: boolean;\n /** Show context usage in the summary header. Defaults to true. */\n showContextMeter?: boolean;\n /** Show the 3-dot menu (density + category/tool/source filters). Defaults to true. */\n showMenu?: boolean;\n /** Controls rendered beside the 3-dot menu in the summary header. */\n headerActions?: ReactNode;\n /** Host controls rendered at the top of the 3-dot menu. */\n menuHeader?: ReactNode;\n /** Portal the 3-dot menu into this element instead of rendering it inline in\n * the summary header — lets a host place the menu in its own toolbar while the\n * filter/density state stays owned by the viewer. Ignored when `showMenu` is\n * false; falls back to inline rendering when null/undefined. */\n menuContainer?: Element | null;\n /** Initial density override; undefined inherits the page/document density. */\n defaultDensity?: Density;\n /** Initial theme override; undefined inherits the page/document `data-theme`. */\n defaultTheme?: SessionThemeOverride;\n /** Own an internal scroll container that fills its parent's height: render only\n * the newest screenful, stay pinned to the bottom (most recent activity), and\n * backfill older rows as the reader scrolls up. Off by default, so inline\n * consumers keep rendering the whole log in normal page flow. Requires a\n * height-bounded parent. */\n scrollable?: boolean;\n /** Rows rendered on first paint in `scrollable` mode. Defaults to 60. */\n windowSize?: number;\n /** Rows added each time older content loads in `scrollable` mode. Defaults to 40. */\n batchSize?: number;\n /** Stable identity used to reset the transcript window when switching sessions. */\n scrollResetKey?: string;\n /** Show per-row timestamp/source/model/turn/agent metadata. Defaults to false. */\n showRowMetadata?: boolean;\n /** Show a per-row raw JSON payload expander when available. Defaults to false. */\n showRaw?: boolean;\n /** Render content before each assistant message body. */\n renderMessageBadge?: (event: SessionEvent) => ReactNode;\n pendingTools?: readonly SessionPendingTool[];\n onPendingToolDecision?:\n | ((decision: SessionToolDecision) => Promise<void> | void)\n | undefined;\n}\n\nfunction toggleInSet<T>(set: ReadonlySet<T>, value: T): Set<T> {\n const next = new Set(set);\n if (next.has(value)) next.delete(value);\n else next.add(value);\n return next;\n}\n\n/**\n * SessionViewer renders a recorded AI coding-agent session (the captain\n * `pkg/ai/history` JSON schema — Claude Code / Codex transcripts) as a vertical\n * action log. Each entry sits on a tone-colored icon disc from the Flanksource\n * \"Agent Action Icons\" set; user prompts/selections are right-aligned like a\n * chat. Shell commands render inline as bash blocks and file ops as their\n * cwd-relative path — the icon carries the verb. Tool responses expand behind\n * a chevron, rendered bare (no panel chrome). The \"3-dot\" menu mirrors\n * `captain history` filtering — toggle density and hide categories, tools or\n * sources.\n */\nexport function SessionViewer({\n session,\n className,\n defaultExpanded = false,\n showThinking = true,\n showHeader = true,\n showContextMeter = true,\n showMenu = true,\n headerActions,\n menuHeader,\n menuContainer,\n defaultDensity,\n defaultTheme,\n scrollable = false,\n windowSize = 60,\n batchSize = 40,\n scrollResetKey,\n showRowMetadata = false,\n showRaw = false,\n renderMessageBadge,\n pendingTools = [],\n onPendingToolDecision,\n sizeTokens,\n}: SessionViewerProps) {\n const allEvents = useMemo(\n () => mergePendingTools(normalizeSession(session), pendingTools),\n [session, pendingTools],\n );\n const displayItems = useMemo(() => collapseWaitRuns(allEvents), [allEvents]);\n const metadata = useMemo(() => getSessionMetadata(session), [session]);\n const tokenSizing = useSessionTokenSizing(session, allEvents, sizeTokens);\n\n const pageDensity = useDensityValue();\n const [densityOverride, setDensityOverride] = useState<Density | undefined>(\n defaultDensity,\n );\n const [themeOverride, setThemeOverride] = useState<\n SessionThemeOverride | undefined\n >(defaultTheme);\n const [hiddenCategories, setHiddenCategories] = useState<\n ReadonlySet<SessionCategory>\n >(() => new Set());\n const [hiddenTools, setHiddenTools] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n const [hiddenSources, setHiddenSources] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n // Undefined = follow the `showThinking` prop; the menu toggle sets an override.\n const [showThinkingOverride, setShowThinkingOverride] = useState<\n boolean | undefined\n >(undefined);\n const effectiveShowThinking = showThinkingOverride ?? showThinking;\n const [showEncryptedReasoning, setShowEncryptedReasoning] = useState(false);\n const [showEstimatedToolCost, setShowEstimatedToolCost] = useState(false);\n\n const filters = useMemo(() => collectSessionFilters(allEvents), [allEvents]);\n const hasThinking = useMemo(\n () => allEvents.some((e) => e.kind === \"thinking\"),\n [allEvents],\n );\n const hasEncryptedReasoning = useMemo(\n () => allEvents.some(isEncryptedReasoning),\n [allEvents],\n );\n\n const visibility = {\n hiddenCategories,\n hiddenTools,\n hiddenSources,\n showThinking: effectiveShowThinking,\n };\n const items = displayItems.filter((item) => {\n const event = isSessionEventGroup(item) ? item.representative : item;\n return (showEncryptedReasoning || !isEncryptedReasoning(event)) && isEventVisible(event, visibility);\n });\n const firstUserEventId = allEvents.find((event) => event.kind === \"user\")?.id;\n\n // Reset the window when the underlying session changes, not on filter toggles —\n // so hiding a category doesn't yank the reader back to the bottom.\n const resetKey = scrollResetKey ?? allEvents[0]?.id ?? \"\";\n const { scrollRef, contentRef, startIndex, following, resumeFollowing } = useSessionScroll({\n total: items.length,\n enabled: scrollable,\n windowSize,\n batchSize,\n resetKey,\n });\n\n if (allEvents.length === 0) {\n return (\n <div\n className={cn(\n \"rounded-md border border-dashed border-border p-density-4 text-center text-sm text-muted-foreground\",\n className,\n )}\n >\n No session activity.\n </div>\n );\n }\n\n const summary = summarizeSession(allEvents);\n const effectiveDensity = densityOverride ?? pageDensity;\n const dataAttrs: Record<string, string> = {\n ...(densityOverride ? { \"data-density\": densityOverride } : {}),\n ...(themeOverride ? { \"data-theme\": themeOverride } : {}),\n };\n\n const menu = showMenu ? (\n <SessionViewerMenu\n menuHeader={menuHeader}\n density={densityOverride}\n onDensityChange={setDensityOverride}\n theme={themeOverride}\n onThemeChange={setThemeOverride}\n filters={filters}\n hiddenCategories={hiddenCategories}\n hiddenTools={hiddenTools}\n hiddenSources={hiddenSources}\n onToggleCategory={(category) =>\n setHiddenCategories((set) => toggleInSet(set, category))\n }\n onToggleTool={(tool) => setHiddenTools((set) => toggleInSet(set, tool))}\n onToggleSource={(source) =>\n setHiddenSources((set) => toggleInSet(set, source))\n }\n showThinking={effectiveShowThinking}\n onToggleThinking={() => setShowThinkingOverride(!effectiveShowThinking)}\n hasThinking={hasThinking}\n hasEncryptedReasoning={hasEncryptedReasoning}\n showEncryptedReasoning={showEncryptedReasoning}\n onToggleEncryptedReasoning={() => setShowEncryptedReasoning((shown) => !shown)}\n showEstimatedToolCost={showEstimatedToolCost}\n onToggleEstimatedToolCost={() => setShowEstimatedToolCost((shown) => !shown)}\n {...(tokenSizing.estimateAll ? { onEstimateTokens: tokenSizing.estimateAll, onCalculateMissingTokens: tokenSizing.calculateMissing, estimatingTokens: tokenSizing.pending } : {})}\n />\n ) : null;\n // When a host supplies `menuContainer`, the menu is portaled into it (e.g. the\n // host's own toolbar) and the inline header collapses to just its summary.\n const inlineMenu = menu && !menuContainer;\n\n const list =\n items.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border p-density-4 text-center text-sm text-muted-foreground\">\n All actions are hidden by the active filters.\n </div>\n ) : (\n // In scrollable mode only the newest `startIndex..` slice is mounted; `last`\n // is measured against the full length so the timeline connector stays right.\n <ol className=\"relative\">\n {items.slice(startIndex).map((item, index) => {\n const last = startIndex + index === items.length - 1;\n return isSessionEventGroup(item) ? (\n <WaitGroupRow\n key={item.id}\n group={item}\n last={last}\n showEstimatedToolCost={showEstimatedToolCost}\n defaultExpanded={defaultExpanded}\n showRowMetadata={showRowMetadata}\n showRaw={showRaw}\n renderMessageBadge={renderMessageBadge}\n onPendingToolDecision={onPendingToolDecision}\n />\n ) : (\n <SessionRow\n key={item.id}\n event={item}\n initialPrompt={item.id === firstUserEventId}\n showEstimatedToolCost={showEstimatedToolCost}\n last={last}\n defaultExpanded={defaultExpanded}\n showRowMetadata={showRowMetadata}\n showRaw={showRaw}\n renderMessageBadge={renderMessageBadge}\n onPendingToolDecision={onPendingToolDecision}\n />\n );\n })}\n </ol>\n );\n\n return (\n <div\n className={cn(\n \"@container/session-viewer min-w-0 bg-background text-sm text-foreground data-[theme=dark]:bg-[var(--fs-bg-subtle)] [[data-theme=dark]_&]:bg-[var(--fs-bg-subtle)]\",\n scrollable && \"flex h-full min-h-0 flex-col\",\n className,\n )}\n {...dataAttrs}\n >\n <SessionTokenContext.Provider value={tokenSizing.context}>\n <DensityValueProvider density={effectiveDensity}>\n {menu && menuContainer && createPortal(menu, menuContainer)}\n {(showHeader || headerActions || inlineMenu) && (\n <div\n data-session-viewer-header\n className={cn(\n \"flex min-w-0 flex-wrap items-center justify-between gap-x-density-3 gap-y-density-2\",\n scrollable\n ? \"shrink-0 border-b border-border px-density-3 py-density-2 @min-[48rem]/session-viewer:px-density-4\"\n : \"mb-density-3\",\n )}\n >\n {showHeader && (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-density-3 gap-y-1 text-xs text-muted-foreground\">\n {summary.model && (\n <span className=\"inline-flex items-center gap-1 font-medium text-foreground\">\n <Icon icon={UiRobotAi} className=\"size-3.5\" />\n {summary.model}\n </span>\n )}\n <span>{summary.toolCount} actions</span>\n <span>{summary.messageCount} messages</span>\n {metadata && (\n <SessionMetadataBadges\n metadata={metadata}\n showContextMeter={showContextMeter}\n />\n )}\n </div>\n )}\n {(headerActions || inlineMenu) && (\n <div\n role=\"group\"\n aria-label=\"Session viewer actions\"\n className=\"ml-auto flex min-w-0 shrink-0 items-center gap-density-2\"\n >\n {headerActions}\n {inlineMenu && menu}\n </div>\n )}\n </div>\n )}\n\n {scrollable ? (\n <div className=\"relative min-h-0 flex-1\">\n <div ref={scrollRef} className=\"h-full overflow-y-auto\">\n <div\n ref={contentRef}\n data-session-viewer-content\n className=\"p-density-3 @min-[48rem]/session-viewer:p-density-4\"\n >\n {list}\n </div>\n </div>\n {!following && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={resumeFollowing}\n className=\"absolute bottom-density-3 left-1/2 -translate-x-1/2 shadow-md\"\n >\n <Icon icon={UiArrowDown} className=\"size-4\" />\n Scroll to bottom\n </Button>\n )}\n </div>\n ) : (\n list\n )}\n </DensityValueProvider>\n </SessionTokenContext.Provider>\n </div>\n );\n}\n\nfunction mergePendingTools(\n events: SessionEvent[],\n pendingTools: readonly SessionPendingTool[],\n): SessionEvent[] {\n const merged = [...events];\n for (const [index, pending] of pendingTools.entries()) {\n const matches = merged.filter(\n (event) =>\n event.kind === \"tool\" &&\n ((pending.toolCallId && event.toolCallId === pending.toolCallId) ||\n (pending.approvalId && event.approvalId === pending.approvalId) ||\n (pending.sessionId &&\n event.sessionId === pending.sessionId &&\n event.tool === pending.tool)),\n );\n const match = matches.length === 1 ? matches[0] : undefined;\n if (match) {\n Object.assign(match, {\n pending: true,\n toolInput: pending.input ?? match.toolInput,\n toolCallId: pending.toolCallId ?? match.toolCallId,\n approvalId: pending.approvalId ?? match.approvalId,\n sessionId: pending.sessionId ?? match.sessionId,\n ...approvalFields(pending),\n });\n } else {\n merged.push({\n id: `pending-${\n pending.approvalId ?? pending.toolCallId ?? `${pending.tool}-${index}`\n }`,\n kind: \"tool\",\n tool: pending.tool,\n ...(pending.input ? { toolInput: pending.input } : {}),\n ...(pending.toolCallId ? { toolCallId: pending.toolCallId } : {}),\n ...(pending.approvalId ? { approvalId: pending.approvalId } : {}),\n ...(pending.sessionId ? { sessionId: pending.sessionId } : {}),\n ...approvalFields(pending),\n pending: true,\n });\n }\n }\n return merged;\n}\n\nfunction approvalFields(\n pending: SessionPendingTool,\n): Pick<SessionEvent, \"approvalKind\" | \"approvalRequest\"> {\n const kind = pending.kind ?? pending.request?.kind;\n return {\n ...(kind ? { approvalKind: kind } : {}),\n ...(pending.request ? { approvalRequest: pending.request } : {}),\n };\n}\n"],"names":["useMemo","normalizeSession","collapseWaitRuns","getSessionMetadata","useSessionTokenSizing","useDensityValue","useState","collectSessionFilters","isEncryptedReasoning","isSessionEventGroup","isEventVisible","useSessionScroll","jsx","cn","summarizeSession","SessionViewerMenu","WaitGroupRow","SessionRow","SessionTokenContext","jsxs","DensityValueProvider","createPortal","Icon","UiRobotAi","SessionMetadataBadges","Button","UiArrowDown"],"mappings":"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"}
1
+ {"version":3,"file":"SessionViewer.cjs","sources":["../../../src/data/ai/SessionViewer.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { Button } from \"../../components/button\";\nimport { useDensityValue, type Density } from \"../../hooks/use-density\";\nimport { DensityValueProvider } from \"../../hooks/density-provider\";\nimport { UiArrowDown, UiRobotAi } from \"../../icons\";\nimport {\n getSessionMetadata,\n normalizeSession,\n summarizeSession,\n type SessionEvent,\n type SessionInput,\n} from \"./SessionViewer.model\";\nimport {\n collapseWaitRuns,\n isSessionEventGroup,\n} from \"./SessionViewer.grouping\";\nimport { SessionRow, WaitGroupRow } from \"./SessionViewer.rows\";\nimport { SessionMetadataBadges } from \"./SessionViewer.header\";\nimport {\n collectSessionFilters,\n isEncryptedReasoning,\n isEventVisible,\n type SessionCategory,\n} from \"./session-categories\";\nimport {\n SessionViewerMenu,\n type SessionThemeOverride,\n} from \"./SessionViewerMenu\";\nimport { useSessionScroll } from \"./use-session-scroll\";\nimport type {\n ApprovalDecisionFields,\n ApprovalKind,\n ApprovalRequest,\n} from \"./approval-request\";\nimport type { SessionTokenSizer } from \"./session-token-sizing\";\nimport { SessionTokenContext, useSessionTokenSizing } from \"./use-session-token-sizing\";\nexport type { SessionTokenSizer, SessionTokenSize, SessionTokenSizingRequest, SessionTokenSizingResult } from \"./session-token-sizing\";\n\nexport type {\n SessionEntry,\n SessionEvent,\n SessionInput,\n SessionActionMeta,\n} from \"./SessionViewer.model\";\nexport type { SessionThemeOverride } from \"./SessionViewerMenu\";\n\nexport interface SessionPendingTool {\n tool: string;\n input?: Record<string, unknown>;\n toolCallId?: string;\n approvalId?: string;\n sessionId?: string;\n /** What kind of approval this is. Absent means an untyped tool approval, which\n * renders as Allow / Reject. */\n kind?: ApprovalKind;\n /** The typed approval request; drives the per-kind controls. */\n request?: ApprovalRequest;\n}\n\n/** A decision on one pending tool. `allow`/`message`/`answers` are the original\n * fields; `interrupt`, `scope`, `grants` and `content` are set only for a typed\n * request that offers them. */\nexport interface SessionToolDecision extends ApprovalDecisionFields {\n event: SessionEvent;\n allow: boolean;\n message?: string;\n}\n\nexport interface SessionViewerProps {\n\n /** Explicit menu actions only; never invoked automatically when a transcript loads. */\n sizeTokens?: SessionTokenSizer;\n /** A captain session: parsed `SessionEntry[]` or raw log text (JSON / JSONL). */\n session: SessionInput;\n className?: string;\n /** Expand each tool call's input/output by default. Defaults to false. */\n defaultExpanded?: boolean;\n /** Render assistant reasoning (\"thinking\") blocks. Defaults to true. */\n showThinking?: boolean;\n /** Show the summary header (model + action counts). Defaults to true. */\n showHeader?: boolean;\n /** Show context usage in the summary header. Defaults to true. */\n showContextMeter?: boolean;\n /** Show the 3-dot menu (density + category/tool/source filters). Defaults to true. */\n showMenu?: boolean;\n /** Controls rendered beside the 3-dot menu in the summary header. */\n headerActions?: ReactNode;\n /** Host controls rendered at the top of the 3-dot menu. */\n menuHeader?: ReactNode;\n /** Portal the 3-dot menu into this element instead of rendering it inline in\n * the summary header — lets a host place the menu in its own toolbar while the\n * filter/density state stays owned by the viewer. Ignored when `showMenu` is\n * false; falls back to inline rendering when null/undefined. */\n menuContainer?: Element | null;\n /** Initial density override; undefined inherits the page/document density. */\n defaultDensity?: Density;\n /** Initial theme override; undefined inherits the page/document `data-theme`. */\n defaultTheme?: SessionThemeOverride;\n /** Own an internal scroll container that fills its parent's height: render only\n * the newest screenful, stay pinned to the bottom (most recent activity), and\n * backfill older rows as the reader scrolls up. Off by default, so inline\n * consumers keep rendering the whole log in normal page flow. Requires a\n * height-bounded parent. */\n scrollable?: boolean;\n /** Rows rendered on first paint in `scrollable` mode. Defaults to 60. */\n windowSize?: number;\n /** Rows added each time older content loads in `scrollable` mode. Defaults to 40. */\n batchSize?: number;\n /** Stable identity used to reset the transcript window when switching sessions. */\n scrollResetKey?: string;\n /** `scrollable` mode: called when the reader scrolls to the top with every\n * row already rendered, to fetch older history. Prepended rows keep the\n * reader's position. */\n onReachStart?: () => void;\n /** Shows a \"Loading earlier messages…\" row above the transcript. */\n loadingOlder?: boolean;\n /** Show per-row timestamp/source/model/turn/agent metadata. Defaults to false. */\n showRowMetadata?: boolean;\n /** Show a per-row raw JSON payload expander when available. Defaults to false. */\n showRaw?: boolean;\n /** Render content before each assistant message body. */\n renderMessageBadge?: (event: SessionEvent) => ReactNode;\n pendingTools?: readonly SessionPendingTool[];\n onPendingToolDecision?:\n | ((decision: SessionToolDecision) => Promise<void> | void)\n | undefined;\n}\n\nfunction toggleInSet<T>(set: ReadonlySet<T>, value: T): Set<T> {\n const next = new Set(set);\n if (next.has(value)) next.delete(value);\n else next.add(value);\n return next;\n}\n\n/**\n * SessionViewer renders a recorded AI coding-agent session (the captain\n * `pkg/ai/history` JSON schema — Claude Code / Codex transcripts) as a vertical\n * action log. Each entry sits on a tone-colored icon disc from the Flanksource\n * \"Agent Action Icons\" set; user prompts/selections are right-aligned like a\n * chat. Shell commands render inline as bash blocks and file ops as their\n * cwd-relative path — the icon carries the verb. Tool responses expand behind\n * a chevron, rendered bare (no panel chrome). The \"3-dot\" menu mirrors\n * `captain history` filtering — toggle density and hide categories, tools or\n * sources.\n */\nexport function SessionViewer({\n session,\n className,\n defaultExpanded = false,\n showThinking = true,\n showHeader = true,\n showContextMeter = true,\n showMenu = true,\n headerActions,\n menuHeader,\n menuContainer,\n defaultDensity,\n defaultTheme,\n scrollable = false,\n windowSize = 60,\n batchSize = 40,\n scrollResetKey,\n onReachStart,\n loadingOlder = false,\n showRowMetadata = false,\n showRaw = false,\n renderMessageBadge,\n pendingTools = [],\n onPendingToolDecision,\n sizeTokens,\n}: SessionViewerProps) {\n const allEvents = useMemo(\n () => mergePendingTools(normalizeSession(session), pendingTools),\n [session, pendingTools],\n );\n const displayItems = useMemo(() => collapseWaitRuns(allEvents), [allEvents]);\n const metadata = useMemo(() => getSessionMetadata(session), [session]);\n const tokenSizing = useSessionTokenSizing(session, allEvents, sizeTokens);\n\n const pageDensity = useDensityValue();\n const [densityOverride, setDensityOverride] = useState<Density | undefined>(\n defaultDensity,\n );\n const [themeOverride, setThemeOverride] = useState<\n SessionThemeOverride | undefined\n >(defaultTheme);\n const [hiddenCategories, setHiddenCategories] = useState<\n ReadonlySet<SessionCategory>\n >(() => new Set());\n const [hiddenTools, setHiddenTools] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n const [hiddenSources, setHiddenSources] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n // Undefined = follow the `showThinking` prop; the menu toggle sets an override.\n const [showThinkingOverride, setShowThinkingOverride] = useState<\n boolean | undefined\n >(undefined);\n const effectiveShowThinking = showThinkingOverride ?? showThinking;\n const [showEncryptedReasoning, setShowEncryptedReasoning] = useState(false);\n const [showEstimatedToolCost, setShowEstimatedToolCost] = useState(false);\n\n const filters = useMemo(() => collectSessionFilters(allEvents), [allEvents]);\n const hasThinking = useMemo(\n () => allEvents.some((e) => e.kind === \"thinking\"),\n [allEvents],\n );\n const hasEncryptedReasoning = useMemo(\n () => allEvents.some(isEncryptedReasoning),\n [allEvents],\n );\n\n const visibility = {\n hiddenCategories,\n hiddenTools,\n hiddenSources,\n showThinking: effectiveShowThinking,\n };\n const items = displayItems.filter((item) => {\n const event = isSessionEventGroup(item) ? item.representative : item;\n return (showEncryptedReasoning || !isEncryptedReasoning(event)) && isEventVisible(event, visibility);\n });\n const firstUserEventId = allEvents.find((event) => event.kind === \"user\")?.id;\n\n // Reset the window when the underlying session changes, not on filter toggles —\n // so hiding a category doesn't yank the reader back to the bottom.\n const resetKey = scrollResetKey ?? allEvents[0]?.id ?? \"\";\n const { scrollRef, contentRef, startIndex, following, resumeFollowing } = useSessionScroll({\n total: items.length,\n enabled: scrollable,\n windowSize,\n batchSize,\n resetKey,\n ...(allEvents[0] ? { firstKey: allEvents[0].id } : {}),\n ...(onReachStart ? { onReachStart } : {}),\n });\n\n if (allEvents.length === 0) {\n return (\n <div\n className={cn(\n \"rounded-md border border-dashed border-border p-density-4 text-center text-sm text-muted-foreground\",\n className,\n )}\n >\n No session activity.\n </div>\n );\n }\n\n const summary = summarizeSession(allEvents);\n const effectiveDensity = densityOverride ?? pageDensity;\n const dataAttrs: Record<string, string> = {\n ...(densityOverride ? { \"data-density\": densityOverride } : {}),\n ...(themeOverride ? { \"data-theme\": themeOverride } : {}),\n };\n\n const menu = showMenu ? (\n <SessionViewerMenu\n menuHeader={menuHeader}\n density={densityOverride}\n onDensityChange={setDensityOverride}\n theme={themeOverride}\n onThemeChange={setThemeOverride}\n filters={filters}\n hiddenCategories={hiddenCategories}\n hiddenTools={hiddenTools}\n hiddenSources={hiddenSources}\n onToggleCategory={(category) =>\n setHiddenCategories((set) => toggleInSet(set, category))\n }\n onToggleTool={(tool) => setHiddenTools((set) => toggleInSet(set, tool))}\n onToggleSource={(source) =>\n setHiddenSources((set) => toggleInSet(set, source))\n }\n showThinking={effectiveShowThinking}\n onToggleThinking={() => setShowThinkingOverride(!effectiveShowThinking)}\n hasThinking={hasThinking}\n hasEncryptedReasoning={hasEncryptedReasoning}\n showEncryptedReasoning={showEncryptedReasoning}\n onToggleEncryptedReasoning={() => setShowEncryptedReasoning((shown) => !shown)}\n showEstimatedToolCost={showEstimatedToolCost}\n onToggleEstimatedToolCost={() => setShowEstimatedToolCost((shown) => !shown)}\n {...(tokenSizing.estimateAll ? { onEstimateTokens: tokenSizing.estimateAll, onCalculateMissingTokens: tokenSizing.calculateMissing, estimatingTokens: tokenSizing.pending } : {})}\n />\n ) : null;\n // When a host supplies `menuContainer`, the menu is portaled into it (e.g. the\n // host's own toolbar) and the inline header collapses to just its summary.\n const inlineMenu = menu && !menuContainer;\n\n const list =\n items.length === 0 ? (\n <div className=\"rounded-md border border-dashed border-border p-density-4 text-center text-sm text-muted-foreground\">\n All actions are hidden by the active filters.\n </div>\n ) : (\n // In scrollable mode only the newest `startIndex..` slice is mounted; `last`\n // is measured against the full length so the timeline connector stays right.\n <ol className=\"relative\">\n {items.slice(startIndex).map((item, index) => {\n const last = startIndex + index === items.length - 1;\n return isSessionEventGroup(item) ? (\n <WaitGroupRow\n key={item.id}\n group={item}\n last={last}\n showEstimatedToolCost={showEstimatedToolCost}\n defaultExpanded={defaultExpanded}\n showRowMetadata={showRowMetadata}\n showRaw={showRaw}\n renderMessageBadge={renderMessageBadge}\n onPendingToolDecision={onPendingToolDecision}\n />\n ) : (\n <SessionRow\n key={item.id}\n event={item}\n initialPrompt={item.id === firstUserEventId}\n showEstimatedToolCost={showEstimatedToolCost}\n last={last}\n defaultExpanded={defaultExpanded}\n showRowMetadata={showRowMetadata}\n showRaw={showRaw}\n renderMessageBadge={renderMessageBadge}\n onPendingToolDecision={onPendingToolDecision}\n />\n );\n })}\n </ol>\n );\n\n return (\n <div\n className={cn(\n \"@container/session-viewer min-w-0 bg-background text-sm text-foreground data-[theme=dark]:bg-[var(--fs-bg-subtle)] [[data-theme=dark]_&]:bg-[var(--fs-bg-subtle)]\",\n scrollable && \"flex h-full min-h-0 flex-col\",\n className,\n )}\n {...dataAttrs}\n >\n <SessionTokenContext.Provider value={tokenSizing.context}>\n <DensityValueProvider density={effectiveDensity}>\n {menu && menuContainer && createPortal(menu, menuContainer)}\n {(showHeader || headerActions || inlineMenu) && (\n <div\n data-session-viewer-header\n className={cn(\n \"flex min-w-0 flex-wrap items-center justify-between gap-x-density-3 gap-y-density-2\",\n scrollable\n ? \"shrink-0 border-b border-border px-density-3 py-density-2 @min-[48rem]/session-viewer:px-density-4\"\n : \"mb-density-3\",\n )}\n >\n {showHeader && (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-density-3 gap-y-1 text-xs text-muted-foreground\">\n {summary.model && (\n <span className=\"inline-flex items-center gap-1 font-medium text-foreground\">\n <Icon icon={UiRobotAi} className=\"size-3.5\" />\n {summary.model}\n </span>\n )}\n <span>{summary.toolCount} actions</span>\n <span>{summary.messageCount} messages</span>\n {metadata && (\n <SessionMetadataBadges\n metadata={metadata}\n showContextMeter={showContextMeter}\n />\n )}\n </div>\n )}\n {(headerActions || inlineMenu) && (\n <div\n role=\"group\"\n aria-label=\"Session viewer actions\"\n className=\"ml-auto flex min-w-0 shrink-0 items-center gap-density-2\"\n >\n {headerActions}\n {inlineMenu && menu}\n </div>\n )}\n </div>\n )}\n\n {scrollable ? (\n <div className=\"relative min-h-0 flex-1\">\n <div ref={scrollRef} className=\"h-full overflow-y-auto\">\n <div\n ref={contentRef}\n data-session-viewer-content\n className=\"p-density-3 @min-[48rem]/session-viewer:p-density-4\"\n >\n {loadingOlder ? (\n <p role=\"status\" className=\"pb-density-2 text-center text-xs text-muted-foreground\">\n Loading earlier messages…\n </p>\n ) : null}\n {list}\n </div>\n </div>\n {!following && (\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={resumeFollowing}\n className=\"absolute bottom-density-3 left-1/2 -translate-x-1/2 shadow-md\"\n >\n <Icon icon={UiArrowDown} className=\"size-4\" />\n Scroll to bottom\n </Button>\n )}\n </div>\n ) : (\n list\n )}\n </DensityValueProvider>\n </SessionTokenContext.Provider>\n </div>\n );\n}\n\nfunction mergePendingTools(\n events: SessionEvent[],\n pendingTools: readonly SessionPendingTool[],\n): SessionEvent[] {\n const merged = [...events];\n for (const [index, pending] of pendingTools.entries()) {\n const matches = merged.filter(\n (event) =>\n event.kind === \"tool\" &&\n ((pending.toolCallId && event.toolCallId === pending.toolCallId) ||\n (pending.approvalId && event.approvalId === pending.approvalId) ||\n (pending.sessionId &&\n event.sessionId === pending.sessionId &&\n event.tool === pending.tool)),\n );\n const match = matches.length === 1 ? matches[0] : undefined;\n if (match) {\n Object.assign(match, {\n pending: true,\n toolInput: pending.input ?? match.toolInput,\n toolCallId: pending.toolCallId ?? match.toolCallId,\n approvalId: pending.approvalId ?? match.approvalId,\n sessionId: pending.sessionId ?? match.sessionId,\n ...approvalFields(pending),\n });\n } else {\n merged.push({\n id: `pending-${\n pending.approvalId ?? pending.toolCallId ?? `${pending.tool}-${index}`\n }`,\n kind: \"tool\",\n tool: pending.tool,\n ...(pending.input ? { toolInput: pending.input } : {}),\n ...(pending.toolCallId ? { toolCallId: pending.toolCallId } : {}),\n ...(pending.approvalId ? { approvalId: pending.approvalId } : {}),\n ...(pending.sessionId ? { sessionId: pending.sessionId } : {}),\n ...approvalFields(pending),\n pending: true,\n });\n }\n }\n return merged;\n}\n\nfunction approvalFields(\n pending: SessionPendingTool,\n): Pick<SessionEvent, \"approvalKind\" | \"approvalRequest\"> {\n const kind = pending.kind ?? pending.request?.kind;\n return {\n ...(kind ? { approvalKind: kind } : {}),\n ...(pending.request ? { approvalRequest: pending.request } : {}),\n };\n}\n"],"names":["useMemo","normalizeSession","collapseWaitRuns","getSessionMetadata","useSessionTokenSizing","useDensityValue","useState","collectSessionFilters","isEncryptedReasoning","isSessionEventGroup","isEventVisible","useSessionScroll","jsx","cn","summarizeSession","SessionViewerMenu","WaitGroupRow","SessionRow","SessionTokenContext","jsxs","DensityValueProvider","createPortal","Icon","UiRobotAi","SessionMetadataBadges","Button","UiArrowDown"],"mappings":"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"}
@@ -68,6 +68,12 @@ export interface SessionViewerProps {
68
68
  batchSize?: number;
69
69
  /** Stable identity used to reset the transcript window when switching sessions. */
70
70
  scrollResetKey?: string;
71
+ /** `scrollable` mode: called when the reader scrolls to the top with every
72
+ * row already rendered, to fetch older history. Prepended rows keep the
73
+ * reader's position. */
74
+ onReachStart?: () => void;
75
+ /** Shows a "Loading earlier messages…" row above the transcript. */
76
+ loadingOlder?: boolean;
71
77
  /** Show per-row timestamp/source/model/turn/agent metadata. Defaults to false. */
72
78
  showRowMetadata?: boolean;
73
79
  /** Show a per-row raw JSON payload expander when available. Defaults to false. */
@@ -88,5 +94,5 @@ export interface SessionViewerProps {
88
94
  * `captain history` filtering — toggle density and hide categories, tools or
89
95
  * sources.
90
96
  */
91
- export declare function SessionViewer({ session, className, defaultExpanded, showThinking, showHeader, showContextMeter, showMenu, headerActions, menuHeader, menuContainer, defaultDensity, defaultTheme, scrollable, windowSize, batchSize, scrollResetKey, showRowMetadata, showRaw, renderMessageBadge, pendingTools, onPendingToolDecision, sizeTokens, }: SessionViewerProps): import("react").JSX.Element;
97
+ export declare function SessionViewer({ session, className, defaultExpanded, showThinking, showHeader, showContextMeter, showMenu, headerActions, menuHeader, menuContainer, defaultDensity, defaultTheme, scrollable, windowSize, batchSize, scrollResetKey, onReachStart, loadingOlder, showRowMetadata, showRaw, renderMessageBadge, pendingTools, onPendingToolDecision, sizeTokens, }: SessionViewerProps): import("react").JSX.Element;
92
98
  //# sourceMappingURL=SessionViewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SessionViewer.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionViewer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SessionViewer.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionViewer.tsx"],"names":[],"mappings":"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"}
@@ -39,6 +39,8 @@ function SessionViewer({
39
39
  windowSize = 60,
40
40
  batchSize = 40,
41
41
  scrollResetKey,
42
+ onReachStart,
43
+ loadingOlder = false,
42
44
  showRowMetadata = false,
43
45
  showRaw = false,
44
46
  renderMessageBadge,
@@ -96,7 +98,9 @@ function SessionViewer({
96
98
  enabled: scrollable,
97
99
  windowSize,
98
100
  batchSize,
99
- resetKey
101
+ resetKey,
102
+ ...allEvents[0] ? { firstKey: allEvents[0].id } : {},
103
+ ...onReachStart ? { onReachStart } : {}
100
104
  });
101
105
  if (allEvents.length === 0) {
102
106
  return /* @__PURE__ */ jsx(
@@ -235,13 +239,16 @@ function SessionViewer({
235
239
  }
236
240
  ),
237
241
  scrollable ? /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1", children: [
238
- /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsx(
242
+ /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsxs(
239
243
  "div",
240
244
  {
241
245
  ref: contentRef,
242
246
  "data-session-viewer-content": true,
243
247
  className: "p-density-3 @min-[48rem]/session-viewer:p-density-4",
244
- children: list
248
+ children: [
249
+ loadingOlder ? /* @__PURE__ */ jsx("p", { role: "status", className: "pb-density-2 text-center text-xs text-muted-foreground", children: "Loading earlier messages…" }) : null,
250
+ list
251
+ ]
245
252
  }
246
253
  ) }),
247
254
  !following && /* @__PURE__ */ jsxs(