@flanksource/clicky-ui 0.3.45 → 0.3.47

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 (505) hide show
  1. package/NOTICE.md +11 -0
  2. package/README.md +22 -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/components/ErrorWrapper.cjs +2 -3
  8. package/dist/components/ErrorWrapper.cjs.map +1 -1
  9. package/dist/components/ErrorWrapper.d.ts.map +1 -1
  10. package/dist/components/ErrorWrapper.js +3 -4
  11. package/dist/components/ErrorWrapper.js.map +1 -1
  12. package/dist/data/AccessMark.cjs +23 -0
  13. package/dist/data/AccessMark.cjs.map +1 -0
  14. package/dist/data/AccessMark.d.ts +11 -0
  15. package/dist/data/AccessMark.d.ts.map +1 -0
  16. package/dist/data/AccessMark.js +23 -0
  17. package/dist/data/AccessMark.js.map +1 -0
  18. package/dist/data/Clicky.cjs +3 -0
  19. package/dist/data/Clicky.cjs.map +1 -1
  20. package/dist/data/Clicky.js +3 -0
  21. package/dist/data/Clicky.js.map +1 -1
  22. package/dist/data/CodeDiff.cjs +116 -23
  23. package/dist/data/CodeDiff.cjs.map +1 -1
  24. package/dist/data/CodeDiff.d.ts +26 -0
  25. package/dist/data/CodeDiff.d.ts.map +1 -1
  26. package/dist/data/CodeDiff.js +116 -23
  27. package/dist/data/CodeDiff.js.map +1 -1
  28. package/dist/data/FilterPill.cjs +15 -2
  29. package/dist/data/FilterPill.cjs.map +1 -1
  30. package/dist/data/FilterPill.d.ts +1 -1
  31. package/dist/data/FilterPill.d.ts.map +1 -1
  32. package/dist/data/FilterPill.js +15 -2
  33. package/dist/data/FilterPill.js.map +1 -1
  34. package/dist/data/GraphDiagram.cjs +65 -13
  35. package/dist/data/GraphDiagram.cjs.map +1 -1
  36. package/dist/data/GraphDiagram.d.ts +9 -2
  37. package/dist/data/GraphDiagram.d.ts.map +1 -1
  38. package/dist/data/GraphDiagram.js +66 -14
  39. package/dist/data/GraphDiagram.js.map +1 -1
  40. package/dist/data/GraphDiagramGroups.cjs +100 -0
  41. package/dist/data/GraphDiagramGroups.cjs.map +1 -0
  42. package/dist/data/GraphDiagramGroups.d.ts +24 -0
  43. package/dist/data/GraphDiagramGroups.d.ts.map +1 -0
  44. package/dist/data/GraphDiagramGroups.js +100 -0
  45. package/dist/data/GraphDiagramGroups.js.map +1 -0
  46. package/dist/data/GraphDiagramNodes.cjs +17 -31
  47. package/dist/data/GraphDiagramNodes.cjs.map +1 -1
  48. package/dist/data/GraphDiagramNodes.d.ts +6 -13
  49. package/dist/data/GraphDiagramNodes.d.ts.map +1 -1
  50. package/dist/data/GraphDiagramNodes.js +17 -31
  51. package/dist/data/GraphDiagramNodes.js.map +1 -1
  52. package/dist/data/LogViewer.cjs +27 -8
  53. package/dist/data/LogViewer.cjs.map +1 -1
  54. package/dist/data/LogViewer.d.ts +11 -3
  55. package/dist/data/LogViewer.d.ts.map +1 -1
  56. package/dist/data/LogViewer.js +27 -8
  57. package/dist/data/LogViewer.js.map +1 -1
  58. package/dist/data/Properties.cjs +2 -15
  59. package/dist/data/Properties.cjs.map +1 -1
  60. package/dist/data/Properties.d.ts +4 -0
  61. package/dist/data/Properties.d.ts.map +1 -1
  62. package/dist/data/Properties.js +2 -15
  63. package/dist/data/Properties.js.map +1 -1
  64. package/dist/data/PropertyValue.cjs +37 -0
  65. package/dist/data/PropertyValue.cjs.map +1 -0
  66. package/dist/data/PropertyValue.d.ts +6 -0
  67. package/dist/data/PropertyValue.d.ts.map +1 -0
  68. package/dist/data/PropertyValue.js +37 -0
  69. package/dist/data/PropertyValue.js.map +1 -0
  70. package/dist/data/Tree.cjs +4 -3
  71. package/dist/data/Tree.cjs.map +1 -1
  72. package/dist/data/Tree.d.ts +3 -1
  73. package/dist/data/Tree.d.ts.map +1 -1
  74. package/dist/data/Tree.js +4 -3
  75. package/dist/data/Tree.js.map +1 -1
  76. package/dist/data/TreeNode.cjs +1 -1
  77. package/dist/data/TreeNode.cjs.map +1 -1
  78. package/dist/data/TreeNode.d.ts.map +1 -1
  79. package/dist/data/TreeNode.js +1 -1
  80. package/dist/data/TreeNode.js.map +1 -1
  81. package/dist/data/ai/ChatFab.cjs +1 -1
  82. package/dist/data/ai/ChatFab.cjs.map +1 -1
  83. package/dist/data/ai/ChatFab.js +1 -1
  84. package/dist/data/ai/ChatFab.js.map +1 -1
  85. package/dist/data/ai/ChatWindow.cjs +106 -171
  86. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  87. package/dist/data/ai/ChatWindow.d.ts +6 -2
  88. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  89. package/dist/data/ai/ChatWindow.header.cjs +104 -0
  90. package/dist/data/ai/ChatWindow.header.cjs.map +1 -0
  91. package/dist/data/ai/ChatWindow.header.d.ts +29 -0
  92. package/dist/data/ai/ChatWindow.header.d.ts.map +1 -0
  93. package/dist/data/ai/ChatWindow.header.js +104 -0
  94. package/dist/data/ai/ChatWindow.header.js.map +1 -0
  95. package/dist/data/ai/ChatWindow.js +107 -172
  96. package/dist/data/ai/ChatWindow.js.map +1 -1
  97. package/dist/data/ai/ChatWindow.placement.cjs +95 -0
  98. package/dist/data/ai/ChatWindow.placement.cjs.map +1 -0
  99. package/dist/data/ai/ChatWindow.placement.d.ts +32 -0
  100. package/dist/data/ai/ChatWindow.placement.d.ts.map +1 -0
  101. package/dist/data/ai/ChatWindow.placement.js +95 -0
  102. package/dist/data/ai/ChatWindow.placement.js.map +1 -0
  103. package/dist/data/ai/SessionChatComposer.cjs +3 -1
  104. package/dist/data/ai/SessionChatComposer.cjs.map +1 -1
  105. package/dist/data/ai/SessionChatComposer.d.ts +4 -1
  106. package/dist/data/ai/SessionChatComposer.d.ts.map +1 -1
  107. package/dist/data/ai/SessionChatComposer.js +3 -1
  108. package/dist/data/ai/SessionChatComposer.js.map +1 -1
  109. package/dist/data/ai/SessionInspector.cjs +68 -72
  110. package/dist/data/ai/SessionInspector.cjs.map +1 -1
  111. package/dist/data/ai/SessionInspector.composer-slot.cjs +8 -0
  112. package/dist/data/ai/SessionInspector.composer-slot.cjs.map +1 -0
  113. package/dist/data/ai/SessionInspector.composer-slot.d.ts +9 -0
  114. package/dist/data/ai/SessionInspector.composer-slot.d.ts.map +1 -0
  115. package/dist/data/ai/SessionInspector.composer-slot.js +8 -0
  116. package/dist/data/ai/SessionInspector.composer-slot.js.map +1 -0
  117. package/dist/data/ai/SessionInspector.d.ts +6 -1
  118. package/dist/data/ai/SessionInspector.d.ts.map +1 -1
  119. package/dist/data/ai/SessionInspector.js +69 -73
  120. package/dist/data/ai/SessionInspector.js.map +1 -1
  121. package/dist/data/ai/SessionInspector.legacy.cjs +41 -42
  122. package/dist/data/ai/SessionInspector.legacy.cjs.map +1 -1
  123. package/dist/data/ai/SessionInspector.legacy.d.ts.map +1 -1
  124. package/dist/data/ai/SessionInspector.legacy.js +41 -42
  125. package/dist/data/ai/SessionInspector.legacy.js.map +1 -1
  126. package/dist/data/ai/SessionInspector.remote-view.cjs +14 -9
  127. package/dist/data/ai/SessionInspector.remote-view.cjs.map +1 -1
  128. package/dist/data/ai/SessionInspector.remote-view.d.ts +6 -2
  129. package/dist/data/ai/SessionInspector.remote-view.d.ts.map +1 -1
  130. package/dist/data/ai/SessionInspector.remote-view.js +14 -9
  131. package/dist/data/ai/SessionInspector.remote-view.js.map +1 -1
  132. package/dist/data/ai/SessionInspector.remote.cjs +84 -16
  133. package/dist/data/ai/SessionInspector.remote.cjs.map +1 -1
  134. package/dist/data/ai/SessionInspector.remote.d.ts +42 -7
  135. package/dist/data/ai/SessionInspector.remote.d.ts.map +1 -1
  136. package/dist/data/ai/SessionInspector.remote.js +86 -18
  137. package/dist/data/ai/SessionInspector.remote.js.map +1 -1
  138. package/dist/data/ai/SessionInspector.transcript.cjs +5 -0
  139. package/dist/data/ai/SessionInspector.transcript.cjs.map +1 -1
  140. package/dist/data/ai/SessionInspector.transcript.d.ts.map +1 -1
  141. package/dist/data/ai/SessionInspector.transcript.js +5 -0
  142. package/dist/data/ai/SessionInspector.transcript.js.map +1 -1
  143. package/dist/data/ai/SessionTail.cjs +63 -0
  144. package/dist/data/ai/SessionTail.cjs.map +1 -0
  145. package/dist/data/ai/SessionTail.d.ts +14 -0
  146. package/dist/data/ai/SessionTail.d.ts.map +1 -0
  147. package/dist/data/ai/SessionTail.js +63 -0
  148. package/dist/data/ai/SessionTail.js.map +1 -0
  149. package/dist/data/ai/SessionViewer.cjs +10 -3
  150. package/dist/data/ai/SessionViewer.cjs.map +1 -1
  151. package/dist/data/ai/SessionViewer.d.ts +7 -1
  152. package/dist/data/ai/SessionViewer.d.ts.map +1 -1
  153. package/dist/data/ai/SessionViewer.js +10 -3
  154. package/dist/data/ai/SessionViewer.js.map +1 -1
  155. package/dist/data/ai/SessionViewer.unified.d.ts +14 -0
  156. package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
  157. package/dist/data/ai/index.d.ts +4 -3
  158. package/dist/data/ai/index.d.ts.map +1 -1
  159. package/dist/data/ai/use-session-scroll.cjs +30 -6
  160. package/dist/data/ai/use-session-scroll.cjs.map +1 -1
  161. package/dist/data/ai/use-session-scroll.d.ts +7 -0
  162. package/dist/data/ai/use-session-scroll.d.ts.map +1 -1
  163. package/dist/data/ai/use-session-scroll.js +30 -6
  164. package/dist/data/ai/use-session-scroll.js.map +1 -1
  165. package/dist/data/call-graph/CallGraph.cjs +50 -31
  166. package/dist/data/call-graph/CallGraph.cjs.map +1 -1
  167. package/dist/data/call-graph/CallGraph.d.ts +10 -1
  168. package/dist/data/call-graph/CallGraph.d.ts.map +1 -1
  169. package/dist/data/call-graph/CallGraph.js +52 -33
  170. package/dist/data/call-graph/CallGraph.js.map +1 -1
  171. package/dist/data/call-graph/CallGraphMembers.cjs +5 -4
  172. package/dist/data/call-graph/CallGraphMembers.cjs.map +1 -1
  173. package/dist/data/call-graph/CallGraphMembers.d.ts +1 -1
  174. package/dist/data/call-graph/CallGraphMembers.d.ts.map +1 -1
  175. package/dist/data/call-graph/CallGraphMembers.js +5 -4
  176. package/dist/data/call-graph/CallGraphMembers.js.map +1 -1
  177. package/dist/data/call-graph/CallGraphToolbar.cjs +17 -5
  178. package/dist/data/call-graph/CallGraphToolbar.cjs.map +1 -1
  179. package/dist/data/call-graph/CallGraphToolbar.d.ts +10 -0
  180. package/dist/data/call-graph/CallGraphToolbar.d.ts.map +1 -1
  181. package/dist/data/call-graph/CallGraphToolbar.js +18 -6
  182. package/dist/data/call-graph/CallGraphToolbar.js.map +1 -1
  183. package/dist/data/call-graph/call-graph-model.cjs +169 -18
  184. package/dist/data/call-graph/call-graph-model.cjs.map +1 -1
  185. package/dist/data/call-graph/call-graph-model.d.ts +40 -4
  186. package/dist/data/call-graph/call-graph-model.d.ts.map +1 -1
  187. package/dist/data/call-graph/call-graph-model.js +170 -19
  188. package/dist/data/call-graph/call-graph-model.js.map +1 -1
  189. package/dist/data/call-graph/call-graph-vocabulary.cjs +19 -1
  190. package/dist/data/call-graph/call-graph-vocabulary.cjs.map +1 -1
  191. package/dist/data/call-graph/call-graph-vocabulary.d.ts +4 -0
  192. package/dist/data/call-graph/call-graph-vocabulary.d.ts.map +1 -1
  193. package/dist/data/call-graph/call-graph-vocabulary.js +19 -1
  194. package/dist/data/call-graph/call-graph-vocabulary.js.map +1 -1
  195. package/dist/data/call-graph/call-graph.fixtures.d.ts.map +1 -1
  196. package/dist/data/call-graph/index.d.ts +2 -2
  197. package/dist/data/call-graph/index.d.ts.map +1 -1
  198. package/dist/data/call-graph/types.cjs.map +1 -1
  199. package/dist/data/call-graph/types.d.ts +7 -2
  200. package/dist/data/call-graph/types.d.ts.map +1 -1
  201. package/dist/data/call-graph/types.js.map +1 -1
  202. package/dist/data/chat/Chat.cjs +1 -1
  203. package/dist/data/chat/Chat.cjs.map +1 -1
  204. package/dist/data/chat/Chat.js +1 -1
  205. package/dist/data/chat/Chat.js.map +1 -1
  206. package/dist/data/chat/ChatRuntimeToolbar.cjs +4 -4
  207. package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
  208. package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
  209. package/dist/data/chat/ChatRuntimeToolbar.js +4 -4
  210. package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
  211. package/dist/data/chat/PromptInput.cjs +4 -2
  212. package/dist/data/chat/PromptInput.cjs.map +1 -1
  213. package/dist/data/chat/PromptInput.d.ts +5 -1
  214. package/dist/data/chat/PromptInput.d.ts.map +1 -1
  215. package/dist/data/chat/PromptInput.js +4 -2
  216. package/dist/data/chat/PromptInput.js.map +1 -1
  217. package/dist/data/data-access.cjs +8 -0
  218. package/dist/data/data-access.cjs.map +1 -0
  219. package/dist/data/data-access.d.ts +5 -0
  220. package/dist/data/data-access.d.ts.map +1 -0
  221. package/dist/data/data-access.js +8 -0
  222. package/dist/data/data-access.js.map +1 -0
  223. package/dist/data/diagnostics/ErrorDetails.cjs +13 -2
  224. package/dist/data/diagnostics/ErrorDetails.cjs.map +1 -1
  225. package/dist/data/diagnostics/ErrorDetails.d.ts +3 -1
  226. package/dist/data/diagnostics/ErrorDetails.d.ts.map +1 -1
  227. package/dist/data/diagnostics/ErrorDetails.js +14 -3
  228. package/dist/data/diagnostics/ErrorDetails.js.map +1 -1
  229. package/dist/data/diagnostics/FrameSourceWindow.cjs +3 -2
  230. package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -1
  231. package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -1
  232. package/dist/data/diagnostics/FrameSourceWindow.js +3 -2
  233. package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -1
  234. package/dist/data/diagnostics/error-diagnostics.cjs +5 -0
  235. package/dist/data/diagnostics/error-diagnostics.cjs.map +1 -1
  236. package/dist/data/diagnostics/error-diagnostics.d.ts +1 -0
  237. package/dist/data/diagnostics/error-diagnostics.d.ts.map +1 -1
  238. package/dist/data/diagnostics/error-diagnostics.js +6 -1
  239. package/dist/data/diagnostics/error-diagnostics.js.map +1 -1
  240. package/dist/data/git/GitChangeList.cjs +6 -2
  241. package/dist/data/git/GitChangeList.cjs.map +1 -1
  242. package/dist/data/git/GitChangeList.d.ts +6 -1
  243. package/dist/data/git/GitChangeList.d.ts.map +1 -1
  244. package/dist/data/git/GitChangeList.js +6 -2
  245. package/dist/data/git/GitChangeList.js.map +1 -1
  246. package/dist/data/graph-columns-layout.cjs +47 -16
  247. package/dist/data/graph-columns-layout.cjs.map +1 -1
  248. package/dist/data/graph-columns-layout.d.ts +34 -0
  249. package/dist/data/graph-columns-layout.d.ts.map +1 -1
  250. package/dist/data/graph-columns-layout.js +47 -16
  251. package/dist/data/graph-columns-layout.js.map +1 -1
  252. package/dist/data/graph-diagram-model.cjs +5 -0
  253. package/dist/data/graph-diagram-model.cjs.map +1 -1
  254. package/dist/data/graph-diagram-model.d.ts +19 -0
  255. package/dist/data/graph-diagram-model.d.ts.map +1 -1
  256. package/dist/data/graph-diagram-model.js +5 -0
  257. package/dist/data/graph-diagram-model.js.map +1 -1
  258. package/dist/data/graph-group-window.cjs +60 -0
  259. package/dist/data/graph-group-window.cjs.map +1 -0
  260. package/dist/data/graph-group-window.d.ts +33 -0
  261. package/dist/data/graph-group-window.d.ts.map +1 -0
  262. package/dist/data/graph-group-window.js +60 -0
  263. package/dist/data/graph-group-window.js.map +1 -0
  264. package/dist/data/har/HarPanel.cjs +3 -1
  265. package/dist/data/har/HarPanel.cjs.map +1 -1
  266. package/dist/data/har/HarPanel.d.ts +4 -1
  267. package/dist/data/har/HarPanel.d.ts.map +1 -1
  268. package/dist/data/har/HarPanel.js +3 -1
  269. package/dist/data/har/HarPanel.js.map +1 -1
  270. package/dist/data/test-runner/Expectation.cjs +57 -0
  271. package/dist/data/test-runner/Expectation.cjs.map +1 -0
  272. package/dist/data/test-runner/Expectation.d.ts +19 -0
  273. package/dist/data/test-runner/Expectation.d.ts.map +1 -0
  274. package/dist/data/test-runner/Expectation.js +57 -0
  275. package/dist/data/test-runner/Expectation.js.map +1 -0
  276. package/dist/data/test-runner/MarkIcons.cjs +38 -0
  277. package/dist/data/test-runner/MarkIcons.cjs.map +1 -0
  278. package/dist/data/test-runner/MarkIcons.d.ts +15 -0
  279. package/dist/data/test-runner/MarkIcons.d.ts.map +1 -0
  280. package/dist/data/test-runner/MarkIcons.js +38 -0
  281. package/dist/data/test-runner/MarkIcons.js.map +1 -0
  282. package/dist/data/test-runner/TestDetailPanel.cjs +74 -42
  283. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
  284. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
  285. package/dist/data/test-runner/TestDetailPanel.js +76 -44
  286. package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
  287. package/dist/data/test-runner/TestFailureDetail.cjs +23 -27
  288. package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -1
  289. package/dist/data/test-runner/TestFailureDetail.d.ts +2 -2
  290. package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -1
  291. package/dist/data/test-runner/TestFailureDetail.js +23 -27
  292. package/dist/data/test-runner/TestFailureDetail.js.map +1 -1
  293. package/dist/data/test-runner/TestFilterBar.cjs +87 -93
  294. package/dist/data/test-runner/TestFilterBar.cjs.map +1 -1
  295. package/dist/data/test-runner/TestFilterBar.d.ts +3 -2
  296. package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -1
  297. package/dist/data/test-runner/TestFilterBar.js +89 -95
  298. package/dist/data/test-runner/TestFilterBar.js.map +1 -1
  299. package/dist/data/test-runner/TestRunSummary.cjs +35 -21
  300. package/dist/data/test-runner/TestRunSummary.cjs.map +1 -1
  301. package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -1
  302. package/dist/data/test-runner/TestRunSummary.js +35 -21
  303. package/dist/data/test-runner/TestRunSummary.js.map +1 -1
  304. package/dist/data/test-runner/TestRunner.cjs +52 -30
  305. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  306. package/dist/data/test-runner/TestRunner.d.ts +3 -2
  307. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  308. package/dist/data/test-runner/TestRunner.js +52 -30
  309. package/dist/data/test-runner/TestRunner.js.map +1 -1
  310. package/dist/data/test-runner/TestTree.cjs +5 -2
  311. package/dist/data/test-runner/TestTree.cjs.map +1 -1
  312. package/dist/data/test-runner/TestTree.d.ts +5 -2
  313. package/dist/data/test-runner/TestTree.d.ts.map +1 -1
  314. package/dist/data/test-runner/TestTree.js +5 -2
  315. package/dist/data/test-runner/TestTree.js.map +1 -1
  316. package/dist/data/test-runner/TestTreeNode.cjs +27 -19
  317. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
  318. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
  319. package/dist/data/test-runner/TestTreeNode.js +28 -20
  320. package/dist/data/test-runner/TestTreeNode.js.map +1 -1
  321. package/dist/data/test-runner/adapter.cjs +11 -0
  322. package/dist/data/test-runner/adapter.cjs.map +1 -1
  323. package/dist/data/test-runner/adapter.d.ts +18 -7
  324. package/dist/data/test-runner/adapter.d.ts.map +1 -1
  325. package/dist/data/test-runner/adapter.js +11 -0
  326. package/dist/data/test-runner/adapter.js.map +1 -1
  327. package/dist/data/test-runner/copy.cjs +114 -0
  328. package/dist/data/test-runner/copy.cjs.map +1 -0
  329. package/dist/data/test-runner/copy.d.ts +47 -0
  330. package/dist/data/test-runner/copy.d.ts.map +1 -0
  331. package/dist/data/test-runner/copy.js +114 -0
  332. package/dist/data/test-runner/copy.js.map +1 -0
  333. package/dist/data/test-runner/index.d.ts +5 -2
  334. package/dist/data/test-runner/index.d.ts.map +1 -1
  335. package/dist/data/test-runner/marks.cjs +143 -0
  336. package/dist/data/test-runner/marks.cjs.map +1 -0
  337. package/dist/data/test-runner/marks.d.ts +30 -0
  338. package/dist/data/test-runner/marks.d.ts.map +1 -0
  339. package/dist/data/test-runner/marks.js +143 -0
  340. package/dist/data/test-runner/marks.js.map +1 -0
  341. package/dist/data/test-runner/status.cjs +27 -44
  342. package/dist/data/test-runner/status.cjs.map +1 -1
  343. package/dist/data/test-runner/status.d.ts +12 -3
  344. package/dist/data/test-runner/status.d.ts.map +1 -1
  345. package/dist/data/test-runner/status.js +27 -44
  346. package/dist/data/test-runner/status.js.map +1 -1
  347. package/dist/data/use-group-windows.cjs +50 -0
  348. package/dist/data/use-group-windows.cjs.map +1 -0
  349. package/dist/data/use-group-windows.d.ts +27 -0
  350. package/dist/data/use-group-windows.d.ts.map +1 -0
  351. package/dist/data/use-group-windows.js +50 -0
  352. package/dist/data/use-group-windows.js.map +1 -0
  353. package/dist/data/verification/adapterViews.cjs +16 -41
  354. package/dist/data/verification/adapterViews.cjs.map +1 -1
  355. package/dist/data/verification/adapterViews.d.ts.map +1 -1
  356. package/dist/data/verification/adapterViews.js +16 -41
  357. package/dist/data/verification/adapterViews.js.map +1 -1
  358. package/dist/data/verification/adapters.cjs +115 -3
  359. package/dist/data/verification/adapters.cjs.map +1 -1
  360. package/dist/data/verification/adapters.d.ts.map +1 -1
  361. package/dist/data/verification/adapters.js +115 -3
  362. package/dist/data/verification/adapters.js.map +1 -1
  363. package/dist/data/verification/verifyReportTests.cjs.map +1 -1
  364. package/dist/data/verification/verifyReportTests.js.map +1 -1
  365. package/dist/data/version-info.cjs +3 -3
  366. package/dist/data/version-info.js +3 -3
  367. package/dist/data.cjs +54 -3
  368. package/dist/data.cjs.map +1 -1
  369. package/dist/data.d.ts +6 -2
  370. package/dist/data.d.ts.map +1 -1
  371. package/dist/data.js +58 -7
  372. package/dist/data.js.map +1 -1
  373. package/dist/hooks/event-hub.cjs +229 -0
  374. package/dist/hooks/event-hub.cjs.map +1 -0
  375. package/dist/hooks/event-hub.d.ts +26 -0
  376. package/dist/hooks/event-hub.d.ts.map +1 -0
  377. package/dist/hooks/event-hub.js +229 -0
  378. package/dist/hooks/event-hub.js.map +1 -0
  379. package/dist/hooks.cjs +2 -0
  380. package/dist/hooks.cjs.map +1 -1
  381. package/dist/hooks.d.ts +1 -0
  382. package/dist/hooks.d.ts.map +1 -1
  383. package/dist/hooks.js +2 -0
  384. package/dist/hooks.js.map +1 -1
  385. package/dist/icons/components/UiLogoEslint.cjs +10 -0
  386. package/dist/icons/components/UiLogoEslint.cjs.map +1 -0
  387. package/dist/icons/components/UiLogoEslint.d.ts +9 -0
  388. package/dist/icons/components/UiLogoEslint.d.ts.map +1 -0
  389. package/dist/icons/components/UiLogoEslint.js +10 -0
  390. package/dist/icons/components/UiLogoEslint.js.map +1 -0
  391. package/dist/icons/components/UiLogoGo.cjs +10 -0
  392. package/dist/icons/components/UiLogoGo.cjs.map +1 -0
  393. package/dist/icons/components/UiLogoGo.d.ts +9 -0
  394. package/dist/icons/components/UiLogoGo.d.ts.map +1 -0
  395. package/dist/icons/components/UiLogoGo.js +10 -0
  396. package/dist/icons/components/UiLogoGo.js.map +1 -0
  397. package/dist/icons/components/UiLogoJest.cjs +10 -0
  398. package/dist/icons/components/UiLogoJest.cjs.map +1 -0
  399. package/dist/icons/components/UiLogoJest.d.ts +9 -0
  400. package/dist/icons/components/UiLogoJest.d.ts.map +1 -0
  401. package/dist/icons/components/UiLogoJest.js +10 -0
  402. package/dist/icons/components/UiLogoJest.js.map +1 -0
  403. package/dist/icons/components/UiLogoMarkdown.cjs +10 -0
  404. package/dist/icons/components/UiLogoMarkdown.cjs.map +1 -0
  405. package/dist/icons/components/UiLogoMarkdown.d.ts +9 -0
  406. package/dist/icons/components/UiLogoMarkdown.d.ts.map +1 -0
  407. package/dist/icons/components/UiLogoMarkdown.js +10 -0
  408. package/dist/icons/components/UiLogoMarkdown.js.map +1 -0
  409. package/dist/icons/components/UiLogoOxc.cjs +10 -0
  410. package/dist/icons/components/UiLogoOxc.cjs.map +1 -0
  411. package/dist/icons/components/UiLogoOxc.d.ts +9 -0
  412. package/dist/icons/components/UiLogoOxc.d.ts.map +1 -0
  413. package/dist/icons/components/UiLogoOxc.js +10 -0
  414. package/dist/icons/components/UiLogoOxc.js.map +1 -0
  415. package/dist/icons/components/UiLogoPlaywright.cjs +10 -0
  416. package/dist/icons/components/UiLogoPlaywright.cjs.map +1 -0
  417. package/dist/icons/components/UiLogoPlaywright.d.ts +9 -0
  418. package/dist/icons/components/UiLogoPlaywright.d.ts.map +1 -0
  419. package/dist/icons/components/UiLogoPlaywright.js +10 -0
  420. package/dist/icons/components/UiLogoPlaywright.js.map +1 -0
  421. package/dist/icons/components/UiLogoPython.cjs +10 -0
  422. package/dist/icons/components/UiLogoPython.cjs.map +1 -0
  423. package/dist/icons/components/UiLogoPython.d.ts +9 -0
  424. package/dist/icons/components/UiLogoPython.d.ts.map +1 -0
  425. package/dist/icons/components/UiLogoPython.js +10 -0
  426. package/dist/icons/components/UiLogoPython.js.map +1 -0
  427. package/dist/icons/components/UiLogoReact.cjs +10 -0
  428. package/dist/icons/components/UiLogoReact.cjs.map +1 -0
  429. package/dist/icons/components/UiLogoReact.d.ts +9 -0
  430. package/dist/icons/components/UiLogoReact.d.ts.map +1 -0
  431. package/dist/icons/components/UiLogoReact.js +10 -0
  432. package/dist/icons/components/UiLogoReact.js.map +1 -0
  433. package/dist/icons/components/UiLogoRuff.cjs +10 -0
  434. package/dist/icons/components/UiLogoRuff.cjs.map +1 -0
  435. package/dist/icons/components/UiLogoRuff.d.ts +9 -0
  436. package/dist/icons/components/UiLogoRuff.d.ts.map +1 -0
  437. package/dist/icons/components/UiLogoRuff.js +10 -0
  438. package/dist/icons/components/UiLogoRuff.js.map +1 -0
  439. package/dist/icons/components/UiLogoTypescript.cjs +10 -0
  440. package/dist/icons/components/UiLogoTypescript.cjs.map +1 -0
  441. package/dist/icons/components/UiLogoTypescript.d.ts +9 -0
  442. package/dist/icons/components/UiLogoTypescript.d.ts.map +1 -0
  443. package/dist/icons/components/UiLogoTypescript.js +10 -0
  444. package/dist/icons/components/UiLogoTypescript.js.map +1 -0
  445. package/dist/icons/components/UiLogoVitest.cjs +10 -0
  446. package/dist/icons/components/UiLogoVitest.cjs.map +1 -0
  447. package/dist/icons/components/UiLogoVitest.d.ts +9 -0
  448. package/dist/icons/components/UiLogoVitest.d.ts.map +1 -0
  449. package/dist/icons/components/UiLogoVitest.js +10 -0
  450. package/dist/icons/components/UiLogoVitest.js.map +1 -0
  451. package/dist/icons/index.d.ts +11 -0
  452. package/dist/icons/index.d.ts.map +1 -1
  453. package/dist/icons/registry.cjs +22 -0
  454. package/dist/icons/registry.cjs.map +1 -1
  455. package/dist/icons/registry.d.ts.map +1 -1
  456. package/dist/icons/registry.js +22 -0
  457. package/dist/icons/registry.js.map +1 -1
  458. package/dist/icons.cjs +22 -0
  459. package/dist/icons.cjs.map +1 -1
  460. package/dist/icons.js +22 -0
  461. package/dist/icons.js.map +1 -1
  462. package/dist/index.cjs +56 -3
  463. package/dist/index.cjs.map +1 -1
  464. package/dist/index.js +60 -7
  465. package/dist/index.js.map +1 -1
  466. package/dist/overlay/DropdownMenuSubmenu.cjs +4 -4
  467. package/dist/overlay/DropdownMenuSubmenu.cjs.map +1 -1
  468. package/dist/overlay/DropdownMenuSubmenu.js +4 -4
  469. package/dist/overlay/DropdownMenuSubmenu.js.map +1 -1
  470. package/dist/overlay/modalStack.cjs +13 -6
  471. package/dist/overlay/modalStack.cjs.map +1 -1
  472. package/dist/overlay/modalStack.d.ts.map +1 -1
  473. package/dist/overlay/modalStack.js +13 -6
  474. package/dist/overlay/modalStack.js.map +1 -1
  475. package/dist/rpc/InlineError.cjs +12 -43
  476. package/dist/rpc/InlineError.cjs.map +1 -1
  477. package/dist/rpc/InlineError.d.ts.map +1 -1
  478. package/dist/rpc/InlineError.js +13 -44
  479. package/dist/rpc/InlineError.js.map +1 -1
  480. package/dist/rpc/OperationScheduleDialog.cjs +63 -10
  481. package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
  482. package/dist/rpc/OperationScheduleDialog.d.ts +4 -0
  483. package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
  484. package/dist/rpc/OperationScheduleDialog.js +63 -10
  485. package/dist/rpc/OperationScheduleDialog.js.map +1 -1
  486. package/dist/rpc/OperationSchedules.cjs +12 -2
  487. package/dist/rpc/OperationSchedules.cjs.map +1 -1
  488. package/dist/rpc/OperationSchedules.d.ts.map +1 -1
  489. package/dist/rpc/OperationSchedules.js +12 -2
  490. package/dist/rpc/OperationSchedules.js.map +1 -1
  491. package/dist/rpc/operation-schedule-model.cjs +2 -0
  492. package/dist/rpc/operation-schedule-model.cjs.map +1 -1
  493. package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
  494. package/dist/rpc/operation-schedule-model.js +2 -0
  495. package/dist/rpc/operation-schedule-model.js.map +1 -1
  496. package/dist/rpc/operation-schedule-types.d.ts +7 -1
  497. package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
  498. package/dist/styles.css +1 -1
  499. package/package.json +1 -1
  500. package/dist/data/test-runner/frameworkIcon.cjs +0 -27
  501. package/dist/data/test-runner/frameworkIcon.cjs.map +0 -1
  502. package/dist/data/test-runner/frameworkIcon.d.ts +0 -3
  503. package/dist/data/test-runner/frameworkIcon.d.ts.map +0 -1
  504. package/dist/data/test-runner/frameworkIcon.js +0 -27
  505. package/dist/data/test-runner/frameworkIcon.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"graph-diagram-model.js","sources":["../../src/data/graph-diagram-model.ts"],"sourcesContent":["import type { KeyboardEvent, ReactNode } from \"react\";\nimport type { BadgeTone } from \"./Badge\";\nimport type { GraphLayoutPosition } from \"./graph-layout\";\n\nexport interface GraphDiagramNode {\n id: string;\n label: ReactNode;\n /** Drawn before the label, on the same line. Give the icon its own `title` so it names itself. */\n icon?: ReactNode;\n detail?: ReactNode;\n badge?: ReactNode;\n tone?: BadgeTone;\n shape?: \"pill\" | \"rect\";\n /** Accessible name for the node, when `label` alone isn't enough. */\n ariaLabel?: string;\n /** Full text for the node, shown as its native tooltip. */\n title?: string;\n /** Column of the node. Required by the \"columns\" layout; may be negative. */\n level?: number;\n /** \"columns\" layout: nodes sharing a group are stacked together and boxed. */\n group?: string;\n /**\n * \"columns\" layout: \"compact\" draws the node on one short line at `compactNodeHeight`, its icon and\n * label only, for a member its group box already names the owner of. Defaults to \"regular\".\n */\n size?: \"regular\" | \"compact\";\n /** Dimmed styling, for a node that leads nowhere further: external, unresolved, not expandable. */\n muted?: boolean;\n /** Neighbours not drawn yet. Renders a `+N` control on the node's outer side. */\n expandCount?: number;\n}\n\nexport interface GraphDiagramEdge {\n id: string;\n from: string;\n to: string;\n /** Short text shown in a pill on the edge. */\n label?: string;\n /** Drawn in the pill before the label. An edge with an icon and no label still gets a pill. */\n icon?: ReactNode;\n /** What `icon` says, in words. It precedes the label in the edge's accessible name. */\n iconLabel?: string;\n /** Full text for the edge, shown as its native tooltip. */\n title?: string;\n tone?: BadgeTone;\n dashed?: boolean;\n}\n\nexport interface GraphDiagramGroup {\n id: string;\n label: ReactNode;\n /** Full text for the group, shown as its caption's native tooltip: what a shortened `label` stands for. */\n title?: string;\n}\n\nexport type GraphDiagramLayout = \"ring\" | \"columns\";\n\n/**\n * Whether edges compete for attention or take turns. With focus on, an edge\n * shows its label pill only while it is in focus — selected, hovered, or\n * attached to the selected or hovered node — and while anything is in focus\n * the other edges fade. \"auto\" turns it on past `DENSE_EDGE_COUNT` edges.\n */\nexport type GraphDiagramEdgeFocus = \"auto\" | \"on\" | \"off\";\n\n/** The most edges a diagram draws with every pill showing under `edgeFocus=\"auto\"`. */\nexport const DENSE_EDGE_COUNT = 32;\n\nexport function edgeFocusOn(mode: GraphDiagramEdgeFocus, edgeCount: number): boolean {\n return mode === \"on\" || (mode === \"auto\" && edgeCount > DENSE_EDGE_COUNT);\n}\n\n/** The edges in focus: the named ones, and every edge attached to a named node. */\nexport function focusedEdgeIds(\n edges: readonly { id: string; from: string; to: string }[],\n focus: { nodes: readonly (string | undefined)[]; edges: readonly (string | undefined)[] },\n): Set<string> {\n return new Set(\n edges\n .filter((edge) => focus.edges.includes(edge.id) || focus.nodes.includes(edge.from) || focus.nodes.includes(edge.to))\n .map((edge) => edge.id),\n );\n}\n\n/** Maps a point in layout coordinates to the CSS offsets of the stage it is drawn on. */\nexport type GraphPlacer = (point: GraphLayoutPosition) => { left: number | string; top: number | string };\n\nexport const TONE_TEXT_CLASS: Record<BadgeTone, string> = {\n neutral: \"text-muted-foreground\",\n success: \"text-emerald-600 dark:text-emerald-400\",\n danger: \"text-red-600 dark:text-red-400\",\n warning: \"text-amber-600 dark:text-amber-400\",\n info: \"text-sky-600 dark:text-sky-400\",\n};\n\nexport const TONE_NODE_CLASS: Record<BadgeTone, string> = {\n neutral: \"border-border bg-card text-foreground\",\n success: \"border-emerald-500/50 bg-emerald-500/10 text-foreground\",\n danger: \"border-red-500/50 bg-red-500/10 text-foreground\",\n warning: \"border-amber-500/50 bg-amber-500/10 text-foreground\",\n info: \"border-sky-500/50 bg-sky-500/10 text-foreground\",\n};\n\n/** The node's accessible name: its `ariaLabel`, else a plain-text label, else its id. */\nexport function nodeName(node: GraphDiagramNode): string {\n if (node.ariaLabel !== undefined) return node.ariaLabel;\n return typeof node.label === \"string\" ? node.label : node.id;\n}\n\n/** A selectable edge's accessible name: `<from> to <to>`, then what its pill shows, in words. */\nexport function edgeName(edge: GraphDiagramEdge, names: Record<string, string>): string {\n const ends = `${requireEntry(names, edge.from, \"node name\")} to ${requireEntry(names, edge.to, \"node name\")}`;\n const pill = [edge.iconLabel, edge.label].filter(Boolean).join(\" \");\n return pill === \"\" ? ends : `${ends}: ${pill}`;\n}\n\n// A pill is not measured: its size is estimated from its content, at the mean\n// glyph advance of its 10px medium label plus its padding and border.\nconst LABEL_CHAR_WIDTH = 5.4;\nconst LABEL_CHROME = 14;\n// The pill's icon slot (`text-sm`, so a 1em icon is 14px) and its `gap-1` to the text.\nconst LABEL_ICON_SIZE = 14;\nconst LABEL_ICON_GAP = 4;\n\n/** Estimated px width of an edge's pill. An icon counts as a fixed width, whatever it draws. */\nexport function edgeLabelWidth(edge: GraphDiagramEdge): number {\n const icon = edge.icon == null ? 0 : LABEL_ICON_SIZE + (edge.label ? LABEL_ICON_GAP : 0);\n return (edge.label?.length ?? 0) * LABEL_CHAR_WIDTH + icon + LABEL_CHROME;\n}\n\nexport function requireEntry<T>(entries: Record<string, T>, id: string, what: string): T {\n const entry = entries[id];\n if (entry === undefined) {\n throw new Error(`GraphDiagram: no ${what} computed for \"${id}\"`);\n }\n return entry;\n}\n\n/** Props that report an element as hovered while the pointer is over it or the keyboard focus is inside it. */\nexport function hoverHandlers(id: string, onHover: (id: string | undefined) => void) {\n const enter = () => onHover(id);\n const leave = () => onHover(undefined);\n return { onMouseEnter: enter, onMouseLeave: leave, onFocus: enter, onBlur: leave };\n}\n\n/** Enter and Space activate a focused `role=\"button\"` element, as they do a native button. */\nexport function activateOnKey(event: KeyboardEvent<Element>, activate: () => void): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n activate();\n }\n}\n"],"names":[],"mappings":"AAkEO,MAAM,mBAAmB;AAEzB,SAAS,YAAY,MAA6B,WAA4B;AACnF,SAAO,SAAS,QAAS,SAAS,UAAU,YAAY;AAC1D;AAGO,SAAS,eACd,OACA,OACa;AACb,SAAO,IAAI;AAAA,IACT,MACG,OAAO,CAAC,SAAS,MAAM,MAAM,SAAS,KAAK,EAAE,KAAK,MAAM,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,MAAM,SAAS,KAAK,EAAE,CAAC,EAClH,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,EAAA;AAE5B;AAKO,MAAM,kBAA6C;AAAA,EACxD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;AAEO,MAAM,kBAA6C;AAAA,EACxD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;AAGO,SAAS,SAAS,MAAgC;AACvD,MAAI,KAAK,cAAc,OAAW,QAAO,KAAK;AAC9C,SAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK;AAC5D;AAGO,SAAS,SAAS,MAAwB,OAAuC;AACtF,QAAM,OAAO,GAAG,aAAa,OAAO,KAAK,MAAM,WAAW,CAAC,OAAO,aAAa,OAAO,KAAK,IAAI,WAAW,CAAC;AAC3G,QAAM,OAAO,CAAC,KAAK,WAAW,KAAK,KAAK,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAClE,SAAO,SAAS,KAAK,OAAO,GAAG,IAAI,KAAK,IAAI;AAC9C;AAIA,MAAM,mBAAmB;AACzB,MAAM,eAAe;AAErB,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AAGhB,SAAS,eAAe,MAAgC;AA3DxD;AA4DL,QAAM,OAAO,KAAK,QAAQ,OAAO,IAAI,mBAAmB,KAAK,QAAQ,iBAAiB;AACtF,YAAQ,UAAK,UAAL,mBAAY,WAAU,KAAK,mBAAmB,OAAO;AAC/D;AAEO,SAAS,aAAgB,SAA4B,IAAY,MAAiB;AACvF,QAAM,QAAQ,QAAQ,EAAE;AACxB,MAAI,UAAU,QAAW;AACvB,UAAM,IAAI,MAAM,oBAAoB,IAAI,kBAAkB,EAAE,GAAG;AAAA,EACjE;AACA,SAAO;AACT;AAGO,SAAS,cAAc,IAAY,SAA2C;AACnF,QAAM,QAAQ,MAAM,QAAQ,EAAE;AAC9B,QAAM,QAAQ,MAAM,QAAQ,MAAS;AACrC,SAAO,EAAE,cAAc,OAAO,cAAc,OAAO,SAAS,OAAO,QAAQ,MAAA;AAC7E;AAGO,SAAS,cAAc,OAA+B,UAA4B;AACvF,MAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,UAAM,eAAA;AACN,aAAA;AAAA,EACF;AACF;"}
1
+ {"version":3,"file":"graph-diagram-model.js","sources":["../../src/data/graph-diagram-model.ts"],"sourcesContent":["import type { KeyboardEvent, ReactNode } from \"react\";\nimport type { BadgeTone } from \"./Badge\";\nimport type { GraphLayoutPosition } from \"./graph-layout\";\n\nexport interface GraphDiagramNode {\n id: string;\n label: ReactNode;\n /** Drawn before the label, on the same line. Give the icon its own `title` so it names itself. */\n icon?: ReactNode;\n /** Right-aligned content on a record row, such as a column type. */\n aside?: ReactNode;\n /**\n * A small mark at the end of the node's line, such as how it is accessed (see `AccessMark`). It is\n * hidden from assistive technology, so the node's accessible name stays its own: give it a title.\n */\n mark?: ReactNode;\n detail?: ReactNode;\n badge?: ReactNode;\n tone?: BadgeTone;\n shape?: \"pill\" | \"rect\";\n /** Accessible name for the node, when `label` alone isn't enough. */\n ariaLabel?: string;\n /** Full text for the node, shown as its native tooltip. */\n title?: string;\n /** Column of the node. Required by the \"columns\" layout; may be negative. */\n level?: number;\n /** \"columns\" layout: nodes sharing a group are stacked together and boxed. */\n group?: string;\n /**\n * \"columns\" layout: \"compact\" draws the node on one short line at `compactNodeHeight`, its icon and\n * label only, for a member its group box already names the owner of. Defaults to \"regular\".\n */\n size?: \"regular\" | \"compact\";\n /** Dimmed styling, for a node that leads nowhere further: external, unresolved, not expandable. */\n muted?: boolean;\n /** Neighbours not drawn yet. Renders a `+N` control on the node's outer side. */\n expandCount?: number;\n}\n\nexport interface GraphDiagramEdge {\n id: string;\n from: string;\n to: string;\n /** Short text shown in a pill on the edge. */\n label?: string;\n /** Drawn in the pill before the label. An edge with an icon and no label still gets a pill. */\n icon?: ReactNode;\n /** What `icon` says, in words. It precedes the label in the edge's accessible name. */\n iconLabel?: string;\n /** Full text for the edge, shown as its native tooltip. */\n title?: string;\n tone?: BadgeTone;\n dashed?: boolean;\n}\n\nexport interface GraphDiagramGroup {\n id: string;\n label: ReactNode;\n /** A record draws a header and flush compact member rows. Defaults to a box. */\n variant?: \"box\" | \"record\";\n /** Full text for the group, shown as its caption's native tooltip: what a shortened `label` stands for. */\n title?: string;\n /**\n * \"columns\" layout: whether the group is drawn as its header alone. Set (true or false) to make it\n * collapsible: its header then shows a chevron calling `onGroupToggle`. A collapsed group holds one\n * node, the stand-in its edges attach to, which is not drawn: its header stands for it.\n */\n collapsed?: boolean;\n /** Right-aligned content in the group's header, such as what a collapsed group holds. */\n aside?: ReactNode;\n}\n\n/** The group's name in words: its title, else a plain-text label, else its id. */\nexport function groupName(group: GraphDiagramGroup | undefined, id: string): string {\n if (group?.title !== undefined) return group.title;\n return typeof group?.label === \"string\" ? group.label : id;\n}\n\nexport type GraphDiagramLayout = \"ring\" | \"columns\";\n\n/**\n * Whether edges compete for attention or take turns. With focus on, an edge\n * shows its label pill only while it is in focus — selected, hovered, or\n * attached to the selected or hovered node — and while anything is in focus\n * the other edges fade. \"auto\" turns it on past `DENSE_EDGE_COUNT` edges.\n */\nexport type GraphDiagramEdgeFocus = \"auto\" | \"on\" | \"off\";\n\n/** The most edges a diagram draws with every pill showing under `edgeFocus=\"auto\"`. */\nexport const DENSE_EDGE_COUNT = 32;\n\nexport function edgeFocusOn(mode: GraphDiagramEdgeFocus, edgeCount: number): boolean {\n return mode === \"on\" || (mode === \"auto\" && edgeCount > DENSE_EDGE_COUNT);\n}\n\n/** The edges in focus: the named ones, and every edge attached to a named node. */\nexport function focusedEdgeIds(\n edges: readonly { id: string; from: string; to: string }[],\n focus: { nodes: readonly (string | undefined)[]; edges: readonly (string | undefined)[] },\n): Set<string> {\n return new Set(\n edges\n .filter((edge) => focus.edges.includes(edge.id) || focus.nodes.includes(edge.from) || focus.nodes.includes(edge.to))\n .map((edge) => edge.id),\n );\n}\n\n/** Maps a point in layout coordinates to the CSS offsets of the stage it is drawn on. */\nexport type GraphPlacer = (point: GraphLayoutPosition) => { left: number | string; top: number | string };\n\nexport const TONE_TEXT_CLASS: Record<BadgeTone, string> = {\n neutral: \"text-muted-foreground\",\n success: \"text-emerald-600 dark:text-emerald-400\",\n danger: \"text-red-600 dark:text-red-400\",\n warning: \"text-amber-600 dark:text-amber-400\",\n info: \"text-sky-600 dark:text-sky-400\",\n};\n\nexport const TONE_NODE_CLASS: Record<BadgeTone, string> = {\n neutral: \"border-border bg-card text-foreground\",\n success: \"border-emerald-500/50 bg-emerald-500/10 text-foreground\",\n danger: \"border-red-500/50 bg-red-500/10 text-foreground\",\n warning: \"border-amber-500/50 bg-amber-500/10 text-foreground\",\n info: \"border-sky-500/50 bg-sky-500/10 text-foreground\",\n};\n\n/** The node's accessible name: its `ariaLabel`, else a plain-text label, else its id. */\nexport function nodeName(node: GraphDiagramNode): string {\n if (node.ariaLabel !== undefined) return node.ariaLabel;\n return typeof node.label === \"string\" ? node.label : node.id;\n}\n\n/** A selectable edge's accessible name: `<from> to <to>`, then what its pill shows, in words. */\nexport function edgeName(edge: GraphDiagramEdge, names: Record<string, string>): string {\n const ends = `${requireEntry(names, edge.from, \"node name\")} to ${requireEntry(names, edge.to, \"node name\")}`;\n const pill = [edge.iconLabel, edge.label].filter(Boolean).join(\" \");\n return pill === \"\" ? ends : `${ends}: ${pill}`;\n}\n\n// A pill is not measured: its size is estimated from its content, at the mean\n// glyph advance of its 10px medium label plus its padding and border.\nconst LABEL_CHAR_WIDTH = 5.4;\nconst LABEL_CHROME = 14;\n// The pill's icon slot (`text-sm`, so a 1em icon is 14px) and its `gap-1` to the text.\nconst LABEL_ICON_SIZE = 14;\nconst LABEL_ICON_GAP = 4;\n\n/** Estimated px width of an edge's pill. An icon counts as a fixed width, whatever it draws. */\nexport function edgeLabelWidth(edge: GraphDiagramEdge): number {\n const icon = edge.icon == null ? 0 : LABEL_ICON_SIZE + (edge.label ? LABEL_ICON_GAP : 0);\n return (edge.label?.length ?? 0) * LABEL_CHAR_WIDTH + icon + LABEL_CHROME;\n}\n\nexport function requireEntry<T>(entries: Record<string, T>, id: string, what: string): T {\n const entry = entries[id];\n if (entry === undefined) {\n throw new Error(`GraphDiagram: no ${what} computed for \"${id}\"`);\n }\n return entry;\n}\n\n/** Props that report an element as hovered while the pointer is over it or the keyboard focus is inside it. */\nexport function hoverHandlers(id: string, onHover: (id: string | undefined) => void) {\n const enter = () => onHover(id);\n const leave = () => onHover(undefined);\n return { onMouseEnter: enter, onMouseLeave: leave, onFocus: enter, onBlur: leave };\n}\n\n/** Enter and Space activate a focused `role=\"button\"` element, as they do a native button. */\nexport function activateOnKey(event: KeyboardEvent<Element>, activate: () => void): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n activate();\n }\n}\n"],"names":[],"mappings":"AAyEO,SAAS,UAAU,OAAsC,IAAoB;AAClF,OAAI,+BAAO,WAAU,OAAW,QAAO,MAAM;AAC7C,SAAO,QAAO,+BAAO,WAAU,WAAW,MAAM,QAAQ;AAC1D;AAaO,MAAM,mBAAmB;AAEzB,SAAS,YAAY,MAA6B,WAA4B;AACnF,SAAO,SAAS,QAAS,SAAS,UAAU,YAAY;AAC1D;AAGO,SAAS,eACd,OACA,OACa;AACb,SAAO,IAAI;AAAA,IACT,MACG,OAAO,CAAC,SAAS,MAAM,MAAM,SAAS,KAAK,EAAE,KAAK,MAAM,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,MAAM,SAAS,KAAK,EAAE,CAAC,EAClH,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,EAAA;AAE5B;AAKO,MAAM,kBAA6C;AAAA,EACxD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;AAEO,MAAM,kBAA6C;AAAA,EACxD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;AAGO,SAAS,SAAS,MAAgC;AACvD,MAAI,KAAK,cAAc,OAAW,QAAO,KAAK;AAC9C,SAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAK;AAC5D;AAGO,SAAS,SAAS,MAAwB,OAAuC;AACtF,QAAM,OAAO,GAAG,aAAa,OAAO,KAAK,MAAM,WAAW,CAAC,OAAO,aAAa,OAAO,KAAK,IAAI,WAAW,CAAC;AAC3G,QAAM,OAAO,CAAC,KAAK,WAAW,KAAK,KAAK,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAClE,SAAO,SAAS,KAAK,OAAO,GAAG,IAAI,KAAK,IAAI;AAC9C;AAIA,MAAM,mBAAmB;AACzB,MAAM,eAAe;AAErB,MAAM,kBAAkB;AACxB,MAAM,iBAAiB;AAGhB,SAAS,eAAe,MAAgC;AA3ExD;AA4EL,QAAM,OAAO,KAAK,QAAQ,OAAO,IAAI,mBAAmB,KAAK,QAAQ,iBAAiB;AACtF,YAAQ,UAAK,UAAL,mBAAY,WAAU,KAAK,mBAAmB,OAAO;AAC/D;AAEO,SAAS,aAAgB,SAA4B,IAAY,MAAiB;AACvF,QAAM,QAAQ,QAAQ,EAAE;AACxB,MAAI,UAAU,QAAW;AACvB,UAAM,IAAI,MAAM,oBAAoB,IAAI,kBAAkB,EAAE,GAAG;AAAA,EACjE;AACA,SAAO;AACT;AAGO,SAAS,cAAc,IAAY,SAA2C;AACnF,QAAM,QAAQ,MAAM,QAAQ,EAAE;AAC9B,QAAM,QAAQ,MAAM,QAAQ,MAAS;AACrC,SAAO,EAAE,cAAc,OAAO,cAAc,OAAO,SAAS,OAAO,QAAQ,MAAA;AAC7E;AAGO,SAAS,cAAc,OAA+B,UAA4B;AACvF,MAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,UAAM,eAAA;AACN,aAAA;AAAA,EACF;AACF;"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function clampWindowStart(memberWindow, start) {
4
+ return Math.min(Math.max(0, memberWindow.members.length - memberWindow.rows), Math.max(0, Math.round(start)));
5
+ }
6
+ function revealWindowStart(memberWindow, start, id) {
7
+ const index = memberWindow.members.indexOf(id);
8
+ const current = clampWindowStart(memberWindow, start);
9
+ if (index < 0) return current;
10
+ if (index < current) return index;
11
+ return index >= current + memberWindow.rows ? clampWindowStart(memberWindow, index - memberWindow.rows + 1) : current;
12
+ }
13
+ function revealMember(boxes, starts, id) {
14
+ if (id === void 0) return starts;
15
+ const box = boxes.find((candidate) => {
16
+ var _a;
17
+ return (_a = candidate.window) == null ? void 0 : _a.members.includes(id);
18
+ });
19
+ if (!(box == null ? void 0 : box.window)) return starts;
20
+ const current = starts[box.id] ?? 0;
21
+ const next = revealWindowStart(box.window, current, id);
22
+ return next === clampWindowStart(box.window, current) ? starts : { ...starts, [box.id]: next };
23
+ }
24
+ function windowedPositions(positions, boxes, starts) {
25
+ const moved = { ...positions };
26
+ const hidden = /* @__PURE__ */ new Set();
27
+ for (const box of boxes) {
28
+ const memberWindow = box.window;
29
+ if (!memberWindow) continue;
30
+ const start = clampWindowStart(memberWindow, starts[box.id] ?? 0);
31
+ const shift = memberWindow.tops[start] ?? 0;
32
+ memberWindow.members.forEach((id, index) => {
33
+ const at = positions[id];
34
+ if (!at) throw new Error(`GraphDiagram: windowed member "${id}" of "${box.id}" has no layout position`);
35
+ if (index < start || index >= start + memberWindow.rows) {
36
+ hidden.add(id);
37
+ moved[id] = { x: at.x, y: index < start ? memberWindow.top : memberWindow.top + memberWindow.height };
38
+ } else {
39
+ moved[id] = { x: at.x, y: at.y - shift };
40
+ }
41
+ });
42
+ }
43
+ return { positions: moved, hidden };
44
+ }
45
+ function windowRows(memberWindow, start) {
46
+ const first = clampWindowStart(memberWindow, start);
47
+ return { first: first + 1, last: Math.min(memberWindow.members.length, first + memberWindow.rows), total: memberWindow.members.length };
48
+ }
49
+ function wheelRows(carry, deltaPx, rowPx) {
50
+ const total = carry + deltaPx;
51
+ const rows = Math.trunc(total / rowPx);
52
+ return { rows, carry: total - rows * rowPx };
53
+ }
54
+ exports.clampWindowStart = clampWindowStart;
55
+ exports.revealMember = revealMember;
56
+ exports.revealWindowStart = revealWindowStart;
57
+ exports.wheelRows = wheelRows;
58
+ exports.windowRows = windowRows;
59
+ exports.windowedPositions = windowedPositions;
60
+ //# sourceMappingURL=graph-group-window.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-group-window.cjs","sources":["../../src/data/graph-group-window.ts"],"sourcesContent":["// Scrolling the members of a windowed group (see `GroupMemberWindow`): which rows show, where shown\n// members are drawn, and where the edges of hidden ones attach. Pure math, no React.\n\nimport type { GraphLayoutGroupBox, GroupMemberWindow } from \"./graph-columns-layout\";\nimport type { GraphLayoutPosition } from \"./graph-layout\";\n\n/** Window starts, in rows, by group box id. A box without one starts at its first member. */\nexport type WindowStarts = Readonly<Record<string, number>>;\n\n/** The start kept between the first row and the last full window. */\nexport function clampWindowStart(memberWindow: GroupMemberWindow, start: number): number {\n return Math.min(Math.max(0, memberWindow.members.length - memberWindow.rows), Math.max(0, Math.round(start)));\n}\n\n/** The start that brings member `id` into view, moving no further than needed; unchanged for a shown member or a stranger. */\nexport function revealWindowStart(memberWindow: GroupMemberWindow, start: number, id: string): number {\n const index = memberWindow.members.indexOf(id);\n const current = clampWindowStart(memberWindow, start);\n if (index < 0) return current;\n if (index < current) return index;\n return index >= current + memberWindow.rows ? clampWindowStart(memberWindow, index - memberWindow.rows + 1) : current;\n}\n\n/** The starts after revealing `id` in the windowed box holding it: the same object when nothing moves. */\nexport function revealMember(boxes: readonly GraphLayoutGroupBox[], starts: WindowStarts, id: string | undefined): WindowStarts {\n if (id === undefined) return starts;\n const box = boxes.find((candidate) => candidate.window?.members.includes(id));\n if (!box?.window) return starts;\n const current = starts[box.id] ?? 0;\n const next = revealWindowStart(box.window, current, id);\n return next === clampWindowStart(box.window, current) ? starts : { ...starts, [box.id]: next };\n}\n\nexport interface WindowedPositions {\n positions: Record<string, GraphLayoutPosition>;\n /** Members scrolled out of view: not drawn, their edges attached at the window's edge. */\n hidden: Set<string>;\n}\n\n/**\n * Where each node is drawn once windows scroll: a shown member moves up by the rows scrolled past, a\n * member above the window sits on its top edge and one below on its bottom edge, so its edges still\n * reach the group. Every other node keeps its layout position.\n */\nexport function windowedPositions(\n positions: Readonly<Record<string, GraphLayoutPosition>>,\n boxes: readonly GraphLayoutGroupBox[],\n starts: WindowStarts,\n): WindowedPositions {\n const moved: Record<string, GraphLayoutPosition> = { ...positions };\n const hidden = new Set<string>();\n for (const box of boxes) {\n const memberWindow = box.window;\n if (!memberWindow) continue;\n const start = clampWindowStart(memberWindow, starts[box.id] ?? 0);\n const shift = memberWindow.tops[start] ?? 0;\n memberWindow.members.forEach((id, index) => {\n const at = positions[id];\n if (!at) throw new Error(`GraphDiagram: windowed member \"${id}\" of \"${box.id}\" has no layout position`);\n if (index < start || index >= start + memberWindow.rows) {\n hidden.add(id);\n moved[id] = { x: at.x, y: index < start ? memberWindow.top : memberWindow.top + memberWindow.height };\n } else {\n moved[id] = { x: at.x, y: at.y - shift };\n }\n });\n }\n return { positions: moved, hidden };\n}\n\n/** The rows shown, counted from one, as the footer says them. */\nexport function windowRows(memberWindow: GroupMemberWindow, start: number): { first: number; last: number; total: number } {\n const first = clampWindowStart(memberWindow, start);\n return { first: first + 1, last: Math.min(memberWindow.members.length, first + memberWindow.rows), total: memberWindow.members.length };\n}\n\n/** Whole rows of wheel travel, `rowPx` each, the remainder carried to the next event so small deltas add up. */\nexport function wheelRows(carry: number, deltaPx: number, rowPx: number): { rows: number; carry: number } {\n const total = carry + deltaPx;\n const rows = Math.trunc(total / rowPx);\n return { rows, carry: total - rows * rowPx };\n}\n"],"names":[],"mappings":";;AAUO,SAAS,iBAAiB,cAAiC,OAAuB;AACvF,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,aAAa,QAAQ,SAAS,aAAa,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,CAAC;AAC9G;AAGO,SAAS,kBAAkB,cAAiC,OAAe,IAAoB;AACpG,QAAM,QAAQ,aAAa,QAAQ,QAAQ,EAAE;AAC7C,QAAM,UAAU,iBAAiB,cAAc,KAAK;AACpD,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,QAAS,QAAO;AAC5B,SAAO,SAAS,UAAU,aAAa,OAAO,iBAAiB,cAAc,QAAQ,aAAa,OAAO,CAAC,IAAI;AAChH;AAGO,SAAS,aAAa,OAAuC,QAAsB,IAAsC;AAC9H,MAAI,OAAO,OAAW,QAAO;AAC7B,QAAM,MAAM,MAAM,KAAK,CAAC,cAAA;;AAAc,2BAAU,WAAV,mBAAkB,QAAQ,SAAS;AAAA,GAAG;AAC5E,MAAI,EAAC,2BAAK,QAAQ,QAAO;AACzB,QAAM,UAAU,OAAO,IAAI,EAAE,KAAK;AAClC,QAAM,OAAO,kBAAkB,IAAI,QAAQ,SAAS,EAAE;AACtD,SAAO,SAAS,iBAAiB,IAAI,QAAQ,OAAO,IAAI,SAAS,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,KAAA;AAC1F;AAaO,SAAS,kBACd,WACA,OACA,QACmB;AACnB,QAAM,QAA6C,EAAE,GAAG,UAAA;AACxD,QAAM,6BAAa,IAAA;AACnB,aAAW,OAAO,OAAO;AACvB,UAAM,eAAe,IAAI;AACzB,QAAI,CAAC,aAAc;AACnB,UAAM,QAAQ,iBAAiB,cAAc,OAAO,IAAI,EAAE,KAAK,CAAC;AAChE,UAAM,QAAQ,aAAa,KAAK,KAAK,KAAK;AAC1C,iBAAa,QAAQ,QAAQ,CAAC,IAAI,UAAU;AAC1C,YAAM,KAAK,UAAU,EAAE;AACvB,UAAI,CAAC,GAAI,OAAM,IAAI,MAAM,kCAAkC,EAAE,SAAS,IAAI,EAAE,0BAA0B;AACtG,UAAI,QAAQ,SAAS,SAAS,QAAQ,aAAa,MAAM;AACvD,eAAO,IAAI,EAAE;AACb,cAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,QAAQ,QAAQ,aAAa,MAAM,aAAa,MAAM,aAAa,OAAA;AAAA,MAC/F,OAAO;AACL,cAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,MAAA;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,EAAE,WAAW,OAAO,OAAA;AAC7B;AAGO,SAAS,WAAW,cAAiC,OAA+D;AACzH,QAAM,QAAQ,iBAAiB,cAAc,KAAK;AAClD,SAAO,EAAE,OAAO,QAAQ,GAAG,MAAM,KAAK,IAAI,aAAa,QAAQ,QAAQ,QAAQ,aAAa,IAAI,GAAG,OAAO,aAAa,QAAQ,OAAA;AACjI;AAGO,SAAS,UAAU,OAAe,SAAiB,OAAgD;AACxG,QAAM,QAAQ,QAAQ;AACtB,QAAM,OAAO,KAAK,MAAM,QAAQ,KAAK;AACrC,SAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,MAAA;AACvC;;;;;;;"}
@@ -0,0 +1,33 @@
1
+ import { GraphLayoutGroupBox, GroupMemberWindow } from './graph-columns-layout.js';
2
+ import { GraphLayoutPosition } from './graph-layout.js';
3
+ /** Window starts, in rows, by group box id. A box without one starts at its first member. */
4
+ export type WindowStarts = Readonly<Record<string, number>>;
5
+ /** The start kept between the first row and the last full window. */
6
+ export declare function clampWindowStart(memberWindow: GroupMemberWindow, start: number): number;
7
+ /** The start that brings member `id` into view, moving no further than needed; unchanged for a shown member or a stranger. */
8
+ export declare function revealWindowStart(memberWindow: GroupMemberWindow, start: number, id: string): number;
9
+ /** The starts after revealing `id` in the windowed box holding it: the same object when nothing moves. */
10
+ export declare function revealMember(boxes: readonly GraphLayoutGroupBox[], starts: WindowStarts, id: string | undefined): WindowStarts;
11
+ export interface WindowedPositions {
12
+ positions: Record<string, GraphLayoutPosition>;
13
+ /** Members scrolled out of view: not drawn, their edges attached at the window's edge. */
14
+ hidden: Set<string>;
15
+ }
16
+ /**
17
+ * Where each node is drawn once windows scroll: a shown member moves up by the rows scrolled past, a
18
+ * member above the window sits on its top edge and one below on its bottom edge, so its edges still
19
+ * reach the group. Every other node keeps its layout position.
20
+ */
21
+ export declare function windowedPositions(positions: Readonly<Record<string, GraphLayoutPosition>>, boxes: readonly GraphLayoutGroupBox[], starts: WindowStarts): WindowedPositions;
22
+ /** The rows shown, counted from one, as the footer says them. */
23
+ export declare function windowRows(memberWindow: GroupMemberWindow, start: number): {
24
+ first: number;
25
+ last: number;
26
+ total: number;
27
+ };
28
+ /** Whole rows of wheel travel, `rowPx` each, the remainder carried to the next event so small deltas add up. */
29
+ export declare function wheelRows(carry: number, deltaPx: number, rowPx: number): {
30
+ rows: number;
31
+ carry: number;
32
+ };
33
+ //# sourceMappingURL=graph-group-window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-group-window.d.ts","sourceRoot":"","sources":["../../src/data/graph-group-window.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,6FAA6F;AAC7F,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED,8HAA8H;AAC9H,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMpG;AAED,0GAA0G;AAC1G,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAO9H;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC/C,0FAA0F;IAC1F,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,EACxD,KAAK,EAAE,SAAS,mBAAmB,EAAE,EACrC,MAAM,EAAE,YAAY,GACnB,iBAAiB,CAoBnB;AAED,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,YAAY,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAGzH;AAED,gHAAgH;AAChH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAIxG"}
@@ -0,0 +1,60 @@
1
+ function clampWindowStart(memberWindow, start) {
2
+ return Math.min(Math.max(0, memberWindow.members.length - memberWindow.rows), Math.max(0, Math.round(start)));
3
+ }
4
+ function revealWindowStart(memberWindow, start, id) {
5
+ const index = memberWindow.members.indexOf(id);
6
+ const current = clampWindowStart(memberWindow, start);
7
+ if (index < 0) return current;
8
+ if (index < current) return index;
9
+ return index >= current + memberWindow.rows ? clampWindowStart(memberWindow, index - memberWindow.rows + 1) : current;
10
+ }
11
+ function revealMember(boxes, starts, id) {
12
+ if (id === void 0) return starts;
13
+ const box = boxes.find((candidate) => {
14
+ var _a;
15
+ return (_a = candidate.window) == null ? void 0 : _a.members.includes(id);
16
+ });
17
+ if (!(box == null ? void 0 : box.window)) return starts;
18
+ const current = starts[box.id] ?? 0;
19
+ const next = revealWindowStart(box.window, current, id);
20
+ return next === clampWindowStart(box.window, current) ? starts : { ...starts, [box.id]: next };
21
+ }
22
+ function windowedPositions(positions, boxes, starts) {
23
+ const moved = { ...positions };
24
+ const hidden = /* @__PURE__ */ new Set();
25
+ for (const box of boxes) {
26
+ const memberWindow = box.window;
27
+ if (!memberWindow) continue;
28
+ const start = clampWindowStart(memberWindow, starts[box.id] ?? 0);
29
+ const shift = memberWindow.tops[start] ?? 0;
30
+ memberWindow.members.forEach((id, index) => {
31
+ const at = positions[id];
32
+ if (!at) throw new Error(`GraphDiagram: windowed member "${id}" of "${box.id}" has no layout position`);
33
+ if (index < start || index >= start + memberWindow.rows) {
34
+ hidden.add(id);
35
+ moved[id] = { x: at.x, y: index < start ? memberWindow.top : memberWindow.top + memberWindow.height };
36
+ } else {
37
+ moved[id] = { x: at.x, y: at.y - shift };
38
+ }
39
+ });
40
+ }
41
+ return { positions: moved, hidden };
42
+ }
43
+ function windowRows(memberWindow, start) {
44
+ const first = clampWindowStart(memberWindow, start);
45
+ return { first: first + 1, last: Math.min(memberWindow.members.length, first + memberWindow.rows), total: memberWindow.members.length };
46
+ }
47
+ function wheelRows(carry, deltaPx, rowPx) {
48
+ const total = carry + deltaPx;
49
+ const rows = Math.trunc(total / rowPx);
50
+ return { rows, carry: total - rows * rowPx };
51
+ }
52
+ export {
53
+ clampWindowStart,
54
+ revealMember,
55
+ revealWindowStart,
56
+ wheelRows,
57
+ windowRows,
58
+ windowedPositions
59
+ };
60
+ //# sourceMappingURL=graph-group-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-group-window.js","sources":["../../src/data/graph-group-window.ts"],"sourcesContent":["// Scrolling the members of a windowed group (see `GroupMemberWindow`): which rows show, where shown\n// members are drawn, and where the edges of hidden ones attach. Pure math, no React.\n\nimport type { GraphLayoutGroupBox, GroupMemberWindow } from \"./graph-columns-layout\";\nimport type { GraphLayoutPosition } from \"./graph-layout\";\n\n/** Window starts, in rows, by group box id. A box without one starts at its first member. */\nexport type WindowStarts = Readonly<Record<string, number>>;\n\n/** The start kept between the first row and the last full window. */\nexport function clampWindowStart(memberWindow: GroupMemberWindow, start: number): number {\n return Math.min(Math.max(0, memberWindow.members.length - memberWindow.rows), Math.max(0, Math.round(start)));\n}\n\n/** The start that brings member `id` into view, moving no further than needed; unchanged for a shown member or a stranger. */\nexport function revealWindowStart(memberWindow: GroupMemberWindow, start: number, id: string): number {\n const index = memberWindow.members.indexOf(id);\n const current = clampWindowStart(memberWindow, start);\n if (index < 0) return current;\n if (index < current) return index;\n return index >= current + memberWindow.rows ? clampWindowStart(memberWindow, index - memberWindow.rows + 1) : current;\n}\n\n/** The starts after revealing `id` in the windowed box holding it: the same object when nothing moves. */\nexport function revealMember(boxes: readonly GraphLayoutGroupBox[], starts: WindowStarts, id: string | undefined): WindowStarts {\n if (id === undefined) return starts;\n const box = boxes.find((candidate) => candidate.window?.members.includes(id));\n if (!box?.window) return starts;\n const current = starts[box.id] ?? 0;\n const next = revealWindowStart(box.window, current, id);\n return next === clampWindowStart(box.window, current) ? starts : { ...starts, [box.id]: next };\n}\n\nexport interface WindowedPositions {\n positions: Record<string, GraphLayoutPosition>;\n /** Members scrolled out of view: not drawn, their edges attached at the window's edge. */\n hidden: Set<string>;\n}\n\n/**\n * Where each node is drawn once windows scroll: a shown member moves up by the rows scrolled past, a\n * member above the window sits on its top edge and one below on its bottom edge, so its edges still\n * reach the group. Every other node keeps its layout position.\n */\nexport function windowedPositions(\n positions: Readonly<Record<string, GraphLayoutPosition>>,\n boxes: readonly GraphLayoutGroupBox[],\n starts: WindowStarts,\n): WindowedPositions {\n const moved: Record<string, GraphLayoutPosition> = { ...positions };\n const hidden = new Set<string>();\n for (const box of boxes) {\n const memberWindow = box.window;\n if (!memberWindow) continue;\n const start = clampWindowStart(memberWindow, starts[box.id] ?? 0);\n const shift = memberWindow.tops[start] ?? 0;\n memberWindow.members.forEach((id, index) => {\n const at = positions[id];\n if (!at) throw new Error(`GraphDiagram: windowed member \"${id}\" of \"${box.id}\" has no layout position`);\n if (index < start || index >= start + memberWindow.rows) {\n hidden.add(id);\n moved[id] = { x: at.x, y: index < start ? memberWindow.top : memberWindow.top + memberWindow.height };\n } else {\n moved[id] = { x: at.x, y: at.y - shift };\n }\n });\n }\n return { positions: moved, hidden };\n}\n\n/** The rows shown, counted from one, as the footer says them. */\nexport function windowRows(memberWindow: GroupMemberWindow, start: number): { first: number; last: number; total: number } {\n const first = clampWindowStart(memberWindow, start);\n return { first: first + 1, last: Math.min(memberWindow.members.length, first + memberWindow.rows), total: memberWindow.members.length };\n}\n\n/** Whole rows of wheel travel, `rowPx` each, the remainder carried to the next event so small deltas add up. */\nexport function wheelRows(carry: number, deltaPx: number, rowPx: number): { rows: number; carry: number } {\n const total = carry + deltaPx;\n const rows = Math.trunc(total / rowPx);\n return { rows, carry: total - rows * rowPx };\n}\n"],"names":[],"mappings":"AAUO,SAAS,iBAAiB,cAAiC,OAAuB;AACvF,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,aAAa,QAAQ,SAAS,aAAa,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,CAAC;AAC9G;AAGO,SAAS,kBAAkB,cAAiC,OAAe,IAAoB;AACpG,QAAM,QAAQ,aAAa,QAAQ,QAAQ,EAAE;AAC7C,QAAM,UAAU,iBAAiB,cAAc,KAAK;AACpD,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,QAAS,QAAO;AAC5B,SAAO,SAAS,UAAU,aAAa,OAAO,iBAAiB,cAAc,QAAQ,aAAa,OAAO,CAAC,IAAI;AAChH;AAGO,SAAS,aAAa,OAAuC,QAAsB,IAAsC;AAC9H,MAAI,OAAO,OAAW,QAAO;AAC7B,QAAM,MAAM,MAAM,KAAK,CAAC,cAAA;AAhBnB;AAgBiC,2BAAU,WAAV,mBAAkB,QAAQ,SAAS;AAAA,GAAG;AAC5E,MAAI,EAAC,2BAAK,QAAQ,QAAO;AACzB,QAAM,UAAU,OAAO,IAAI,EAAE,KAAK;AAClC,QAAM,OAAO,kBAAkB,IAAI,QAAQ,SAAS,EAAE;AACtD,SAAO,SAAS,iBAAiB,IAAI,QAAQ,OAAO,IAAI,SAAS,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,KAAA;AAC1F;AAaO,SAAS,kBACd,WACA,OACA,QACmB;AACnB,QAAM,QAA6C,EAAE,GAAG,UAAA;AACxD,QAAM,6BAAa,IAAA;AACnB,aAAW,OAAO,OAAO;AACvB,UAAM,eAAe,IAAI;AACzB,QAAI,CAAC,aAAc;AACnB,UAAM,QAAQ,iBAAiB,cAAc,OAAO,IAAI,EAAE,KAAK,CAAC;AAChE,UAAM,QAAQ,aAAa,KAAK,KAAK,KAAK;AAC1C,iBAAa,QAAQ,QAAQ,CAAC,IAAI,UAAU;AAC1C,YAAM,KAAK,UAAU,EAAE;AACvB,UAAI,CAAC,GAAI,OAAM,IAAI,MAAM,kCAAkC,EAAE,SAAS,IAAI,EAAE,0BAA0B;AACtG,UAAI,QAAQ,SAAS,SAAS,QAAQ,aAAa,MAAM;AACvD,eAAO,IAAI,EAAE;AACb,cAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,QAAQ,QAAQ,aAAa,MAAM,aAAa,MAAM,aAAa,OAAA;AAAA,MAC/F,OAAO;AACL,cAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,MAAA;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO,EAAE,WAAW,OAAO,OAAA;AAC7B;AAGO,SAAS,WAAW,cAAiC,OAA+D;AACzH,QAAM,QAAQ,iBAAiB,cAAc,KAAK;AAClD,SAAO,EAAE,OAAO,QAAQ,GAAG,MAAM,KAAK,IAAI,aAAa,QAAQ,QAAQ,QAAQ,aAAa,IAAI,GAAG,OAAO,aAAa,QAAQ,OAAA;AACjI;AAGO,SAAS,UAAU,OAAe,SAAiB,OAAgD;AACxG,QAAM,QAAQ,QAAQ;AACtB,QAAM,OAAO,KAAK,MAAM,QAAQ,KAAK;AACrC,SAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,MAAA;AACvC;"}
@@ -60,7 +60,8 @@ function HarPanel({
60
60
  search,
61
61
  emptyLabel = "No HTTP traffic captured",
62
62
  className,
63
- bodySource
63
+ bodySource,
64
+ menuActions
64
65
  }) {
65
66
  const filteredEntries = react.useMemo(() => {
66
67
  if (search === void 0 || search.trim() === "") return entries;
@@ -86,6 +87,7 @@ function HarPanel({
86
87
  globalFilterPlaceholder: "Filter URL, method, or body…",
87
88
  defaultSort: { key: "time", dir: "asc" },
88
89
  emptyMessage: emptyLabel,
90
+ ...menuActions ? { menuActions } : {},
89
91
  ...runningCount > 0 ? {
90
92
  footer: ({ visibleRowCount, totalRowCount }) => `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? "" : "s"} · ${runningCount} running`
91
93
  } : {},
@@ -1 +1 @@
1
- {"version":3,"file":"HarPanel.cjs","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport { useResourceClock } from \"../../hooks/use-resource-clock\";\nimport { UiLoader } from \"../../icons\";\nimport { formatDuration } from \"../../lib/format\";\nimport { DataTable, type DataTableColumn, type DataTableFooterContext } from \"../DataTable\";\nimport { Icon } from \"../Icon\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\n\nconst ERROR_PREVIEW_CHARS = 80;\n\n/** Wall-clock ms shared by every in-flight row, ticking only while one exists. */\nconst HarClockContext = createContext<number | undefined>(undefined);\n\nexport type HarPanelProps = {\n /** HAR entries to render. */\n entries: HAREntry[];\n /** Optional external search term. When omitted, the internal DataTable search is shown. */\n search?: string;\n /** Empty-state label for no entries or no matches. */\n emptyLabel?: string;\n /** Classes applied to the panel root. */\n className?: string;\n /** Optional private body store used only when an expanded row needs a preview. */\n bodySource?: HarPanelBodySource;\n};\n\nexport type HarPanelBodySource = {\n hasBody: (entry: HAREntry, part: \"request\" | \"response\") => boolean;\n preview: (requestId: string, part: \"request\" | \"response\") => Promise<string>;\n downloadHref: (entry: HAREntry, part: \"request\" | \"response\") => string;\n};\n\nexport type HarEntryDetailsProps = { entry: HAREntry; bodySource?: HarPanelBodySource | undefined };\n\nconst COLS: DataTableColumn<HAREntry>[] = [\n {\n key: \"request.method\",\n label: \"Method\",\n shrink: true,\n },\n {\n key: \"request.url\",\n label: \"URL\",\n grow: true,\n render: (value, entry) => (\n <span className=\"inline-flex min-w-0 items-baseline gap-density-2\">\n <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>\n {entry._error ? (\n <span className=\"text-destructive text-xs\" title={entry._error}>\n {previewError(entry._error)}\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value, entry) => <HarStatus status={Number(value ?? 0)} entry={entry} />,\n sortValue: (value) => Number(value ?? 0),\n filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value, entry) =>\n entry._pending ? <PendingElapsed entry={entry} /> : `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value, entry) =>\n entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0),\n },\n {\n key: \"response.bodySize\",\n label: \"Size\",\n align: \"right\",\n shrink: true,\n render: (value) => formatBytes(Number(value ?? 0)),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"response.content.mimeType\",\n label: \"Type\",\n shrink: true,\n },\n];\n\nexport function HarPanel({\n entries,\n search,\n emptyLabel = \"No HTTP traffic captured\",\n className,\n bodySource,\n}: HarPanelProps) {\n const filteredEntries = useMemo(() => {\n if (search === undefined || search.trim() === \"\") return entries;\n return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));\n }, [entries, search]);\n const runningCount = useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);\n const now = useResourceClock(runningCount > 0);\n\n if (!entries || entries.length === 0) {\n return (\n <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>\n );\n }\n\n return (\n // A flex column, not a plain block: DataTable sizes itself as `flex-1\n // min-h-0` so its own scroll region can be bounded, and in a block parent\n // that resolves to content height — the table grows past the panel and\n // nothing scrolls.\n <div className={`flex h-full min-h-0 flex-col ${className ?? \"\"}`}>\n <HarClockContext.Provider value={now}>\n <DataTable\n data={filteredEntries}\n columns={COLS}\n autoFilter\n showGlobalFilter={search === undefined}\n globalFilterPlaceholder=\"Filter URL, method, or body…\"\n defaultSort={{ key: \"time\", dir: \"asc\" }}\n emptyMessage={emptyLabel}\n {...(runningCount > 0\n ? {\n footer: ({ visibleRowCount, totalRowCount }: DataTableFooterContext) =>\n `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? \"\" : \"s\"} · ${runningCount} running`,\n }\n : {})}\n getRowId={(entry, index) =>\n // `_id` is the same on a request's pending and completed snapshots,\n // so an expanded row survives the transition. Without it, leave out\n // an in-flight entry's `time`, which grows with every snapshot.\n entry._id ??\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? \"-pending\" : `-${entry.time}`}`\n }\n renderExpandedRow={(entry) => <HarEntryDetails entry={entry} bodySource={bodySource} />}\n />\n </HarClockContext.Provider>\n </div>\n );\n}\n\nfunction HarStatus({ status, entry }: { status: number; entry: HAREntry }) {\n if (entry._pending) {\n return (\n <span className=\"inline-flex items-center gap-1 text-muted-foreground\">\n <Icon icon={UiLoader} className=\"animate-spin\" />\n running\n </span>\n );\n }\n if (entry._error) return <span className=\"font-semibold text-destructive\">ERR</span>;\n return <span className={statusColor(status)}>{String(status)}</span>;\n}\n\nfunction PendingElapsed({ entry }: { entry: HAREntry }) {\n const now = useContext(HarClockContext);\n if (now === undefined) throw new Error(\"PendingElapsed must render inside HarPanel's clock\");\n return <span className=\"tabular-nums\">{formatDuration(pendingElapsedMs(entry, now))}</span>;\n}\n\n/**\n * Elapsed time of an in-flight request, measured from `startedDateTime` so it\n * keeps ticking between snapshots. The snapshot's `time` is only used when the\n * start timestamp cannot be parsed.\n */\nfunction pendingElapsedMs(entry: HAREntry, now: number): number {\n const started = Date.parse(entry.startedDateTime ?? \"\");\n if (Number.isNaN(started)) return entry.time;\n // Server and browser clocks can disagree by a little; a negative elapsed is noise.\n return Math.max(0, now - started);\n}\n\nfunction harStatusLabel(status: number, entry: HAREntry): string {\n if (entry._pending) return \"running\";\n if (entry._error) return \"ERR\";\n return String(status);\n}\n\nfunction previewError(error: string): string {\n return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;\n}\n\nexport function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps) {\n const parentNow = useContext(HarClockContext);\n const ownNow = useResourceClock(Boolean(entry._pending) && parentNow === undefined);\n return <HarClockContext.Provider value={parentNow ?? ownNow}><HarRowDetails entry={entry} bodySource={bodySource} /></HarClockContext.Provider>;\n}\n\nfunction HarRowDetails({ entry, bodySource }: HarEntryDetailsProps) {\n return (\n <>\n {entry._error && (\n <div className=\"mb-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Transport Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{entry._error}</pre>\n </div>\n )}\n {entry._pending && (\n <div className=\"mb-density-3 text-muted-foreground\">\n Waiting for response — in flight for <PendingElapsed entry={entry} />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-density-4\">\n <div>\n <HeaderList\n title=\"Request Headers\"\n {...(entry.request.headers ? { headers: entry.request.headers } : {})}\n />\n {(entry.request.postData?.text || bodySource?.hasBody(entry, \"request\")) && (\n <BodySection entry={entry} part=\"request\" text={entry.request.postData?.text}\n mimeType={entry.request.postData?.mimeType} bodySource={bodySource} />\n )}\n </div>\n <div>\n <HeaderList\n title=\"Response Headers\"\n {...(entry.response.headers ? { headers: entry.response.headers } : {})}\n />\n </div>\n </div>\n {(entry.response.content?.text || bodySource?.hasBody(entry, \"response\")) && (\n <BodySection entry={entry} part=\"response\" text={entry.response.content?.text}\n mimeType={entry.response.content?.mimeType} bodySource={bodySource} />\n )}\n </>\n );\n}\n\nfunction BodySection({ entry, part, text, mimeType, bodySource }: {\n entry: HAREntry;\n part: \"request\" | \"response\";\n text?: string | undefined;\n mimeType?: string | undefined;\n bodySource?: HarPanelBodySource | undefined;\n}) {\n const remote = !text && bodySource?.hasBody(entry, part);\n const requestId = entry._id;\n if (remote && !requestId) throw new Error(\"Private HAR body requires a request ID\");\n const [preview, setPreview] = useState<string | undefined>();\n const [error, setError] = useState<Error | undefined>();\n useEffect(() => {\n if (!remote || !bodySource || !requestId) return;\n let active = true;\n setPreview(undefined);\n setError(undefined);\n bodySource.preview(requestId, part).then(\n (value) => { if (active) setPreview(value); },\n (reason: unknown) => { if (active) setError(reason instanceof Error ? reason : new Error(String(reason))); },\n );\n return () => { active = false; };\n }, [bodySource, requestId, part, remote]);\n return (\n <div className=\"mt-density-3\">\n <div className=\"mb-1 flex items-center gap-2 font-semibold text-foreground\">\n {part === \"request\" ? \"Request Body\" : \"Response Body\"}\n {remote && bodySource && <a className=\"text-sm font-normal text-primary underline\" href={bodySource.downloadHref(entry, part)} download>Download full body</a>}\n </div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n {error ? <span role=\"alert\" className=\"text-destructive\">{error.message}</span>\n : remote && preview === undefined ? <span className=\"text-muted-foreground\">Loading preview…</span>\n : <BodyView text={text || preview || \"\"} {...(mimeType ? { mimeType } : {})} />}\n </div>\n {remote && <p className=\"mt-1 text-xs text-muted-foreground\">Preview shows at most the first 64 KiB.</p>}\n </div>\n );\n}\n\nfunction HeaderList({\n title,\n headers,\n}: {\n title: string;\n headers?: { name: string; value: string }[];\n}) {\n return (\n <>\n <div className=\"font-semibold text-foreground mb-1\">{title}</div>\n <div className=\"space-y-0.5\">\n {headers?.map((h, i) => (\n <div key={i} className=\"whitespace-nowrap\">\n <span className=\"text-purple-600 dark:text-purple-400\">{h.name}:</span> {h.value}\n </div>\n ))}\n </div>\n </>\n );\n}\n\nfunction BodyView({ text, mimeType }: { text: string; mimeType?: string }) {\n if (isJsonType(mimeType)) {\n const parsed = tryParseJson(text);\n if (parsed !== null) return <JsonView data={parsed} />;\n }\n return <pre className=\"whitespace-pre-wrap break-all\">{text}</pre>;\n}\n\nfunction tryParseJson(text: string): unknown | null {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction isJsonType(mime?: string): boolean {\n return !!mime && (mime.includes(\"json\") || mime.includes(\"javascript\"));\n}\n\nfunction matchesSearch(needle: string, entry: HAREntry): boolean {\n return [\n entry.request.method,\n entry.request.url,\n entry.request.postData?.text,\n entry.response.content?.text,\n entry._error,\n ].some((value) => !!value && value.toLowerCase().includes(needle));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)}MB`;\n}\n\nfunction statusColor(status: number): string {\n if (status >= 500) return \"text-red-600\";\n if (status >= 400) return \"text-amber-600\";\n if (status >= 300) return \"text-blue-600\";\n if (status >= 200) return \"text-green-600\";\n return \"text-muted-foreground\";\n}\n"],"names":["createContext","jsxs","jsx","useMemo","useResourceClock","DataTable","Icon","UiLoader","useContext","Fragment","useState","useEffect","JsonView"],"mappings":"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"}
1
+ {"version":3,"file":"HarPanel.cjs","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport { useResourceClock } from \"../../hooks/use-resource-clock\";\nimport { UiLoader } from \"../../icons\";\nimport { formatDuration } from \"../../lib/format\";\nimport {\n DataTable,\n type DataTableColumn,\n type DataTableFooterContext,\n type DataTableMenuAction,\n} from \"../DataTable\";\nimport { Icon } from \"../Icon\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\n\nconst ERROR_PREVIEW_CHARS = 80;\n\n/** Wall-clock ms shared by every in-flight row, ticking only while one exists. */\nconst HarClockContext = createContext<number | undefined>(undefined);\n\nexport type HarPanelProps = {\n /** HAR entries to render. */\n entries: HAREntry[];\n /** Optional external search term. When omitted, the internal DataTable search is shown. */\n search?: string;\n /** Empty-state label for no entries or no matches. */\n emptyLabel?: string;\n /** Classes applied to the panel root. */\n className?: string;\n /** Optional private body store used only when an expanded row needs a preview. */\n bodySource?: HarPanelBodySource;\n /** Entries for the table's overflow menu, e.g. a `useClickyExport` action. */\n menuActions?: DataTableMenuAction[];\n};\n\nexport type HarPanelBodySource = {\n hasBody: (entry: HAREntry, part: \"request\" | \"response\") => boolean;\n preview: (requestId: string, part: \"request\" | \"response\") => Promise<string>;\n downloadHref: (entry: HAREntry, part: \"request\" | \"response\") => string;\n};\n\nexport type HarEntryDetailsProps = { entry: HAREntry; bodySource?: HarPanelBodySource | undefined };\n\nconst COLS: DataTableColumn<HAREntry>[] = [\n {\n key: \"request.method\",\n label: \"Method\",\n shrink: true,\n },\n {\n key: \"request.url\",\n label: \"URL\",\n grow: true,\n render: (value, entry) => (\n <span className=\"inline-flex min-w-0 items-baseline gap-density-2\">\n <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>\n {entry._error ? (\n <span className=\"text-destructive text-xs\" title={entry._error}>\n {previewError(entry._error)}\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value, entry) => <HarStatus status={Number(value ?? 0)} entry={entry} />,\n sortValue: (value) => Number(value ?? 0),\n filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value, entry) =>\n entry._pending ? <PendingElapsed entry={entry} /> : `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value, entry) =>\n entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0),\n },\n {\n key: \"response.bodySize\",\n label: \"Size\",\n align: \"right\",\n shrink: true,\n render: (value) => formatBytes(Number(value ?? 0)),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"response.content.mimeType\",\n label: \"Type\",\n shrink: true,\n },\n];\n\nexport function HarPanel({\n entries,\n search,\n emptyLabel = \"No HTTP traffic captured\",\n className,\n bodySource,\n menuActions,\n}: HarPanelProps) {\n const filteredEntries = useMemo(() => {\n if (search === undefined || search.trim() === \"\") return entries;\n return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));\n }, [entries, search]);\n const runningCount = useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);\n const now = useResourceClock(runningCount > 0);\n\n if (!entries || entries.length === 0) {\n return (\n <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>\n );\n }\n\n return (\n // A flex column, not a plain block: DataTable sizes itself as `flex-1\n // min-h-0` so its own scroll region can be bounded, and in a block parent\n // that resolves to content height — the table grows past the panel and\n // nothing scrolls.\n <div className={`flex h-full min-h-0 flex-col ${className ?? \"\"}`}>\n <HarClockContext.Provider value={now}>\n <DataTable\n data={filteredEntries}\n columns={COLS}\n autoFilter\n showGlobalFilter={search === undefined}\n globalFilterPlaceholder=\"Filter URL, method, or body…\"\n defaultSort={{ key: \"time\", dir: \"asc\" }}\n emptyMessage={emptyLabel}\n {...(menuActions ? { menuActions } : {})}\n {...(runningCount > 0\n ? {\n footer: ({ visibleRowCount, totalRowCount }: DataTableFooterContext) =>\n `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? \"\" : \"s\"} · ${runningCount} running`,\n }\n : {})}\n getRowId={(entry, index) =>\n // `_id` is the same on a request's pending and completed snapshots,\n // so an expanded row survives the transition. Without it, leave out\n // an in-flight entry's `time`, which grows with every snapshot.\n entry._id ??\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? \"-pending\" : `-${entry.time}`}`\n }\n renderExpandedRow={(entry) => <HarEntryDetails entry={entry} bodySource={bodySource} />}\n />\n </HarClockContext.Provider>\n </div>\n );\n}\n\nfunction HarStatus({ status, entry }: { status: number; entry: HAREntry }) {\n if (entry._pending) {\n return (\n <span className=\"inline-flex items-center gap-1 text-muted-foreground\">\n <Icon icon={UiLoader} className=\"animate-spin\" />\n running\n </span>\n );\n }\n if (entry._error) return <span className=\"font-semibold text-destructive\">ERR</span>;\n return <span className={statusColor(status)}>{String(status)}</span>;\n}\n\nfunction PendingElapsed({ entry }: { entry: HAREntry }) {\n const now = useContext(HarClockContext);\n if (now === undefined) throw new Error(\"PendingElapsed must render inside HarPanel's clock\");\n return <span className=\"tabular-nums\">{formatDuration(pendingElapsedMs(entry, now))}</span>;\n}\n\n/**\n * Elapsed time of an in-flight request, measured from `startedDateTime` so it\n * keeps ticking between snapshots. The snapshot's `time` is only used when the\n * start timestamp cannot be parsed.\n */\nfunction pendingElapsedMs(entry: HAREntry, now: number): number {\n const started = Date.parse(entry.startedDateTime ?? \"\");\n if (Number.isNaN(started)) return entry.time;\n // Server and browser clocks can disagree by a little; a negative elapsed is noise.\n return Math.max(0, now - started);\n}\n\nfunction harStatusLabel(status: number, entry: HAREntry): string {\n if (entry._pending) return \"running\";\n if (entry._error) return \"ERR\";\n return String(status);\n}\n\nfunction previewError(error: string): string {\n return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;\n}\n\nexport function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps) {\n const parentNow = useContext(HarClockContext);\n const ownNow = useResourceClock(Boolean(entry._pending) && parentNow === undefined);\n return <HarClockContext.Provider value={parentNow ?? ownNow}><HarRowDetails entry={entry} bodySource={bodySource} /></HarClockContext.Provider>;\n}\n\nfunction HarRowDetails({ entry, bodySource }: HarEntryDetailsProps) {\n return (\n <>\n {entry._error && (\n <div className=\"mb-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Transport Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{entry._error}</pre>\n </div>\n )}\n {entry._pending && (\n <div className=\"mb-density-3 text-muted-foreground\">\n Waiting for response — in flight for <PendingElapsed entry={entry} />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-density-4\">\n <div>\n <HeaderList\n title=\"Request Headers\"\n {...(entry.request.headers ? { headers: entry.request.headers } : {})}\n />\n {(entry.request.postData?.text || bodySource?.hasBody(entry, \"request\")) && (\n <BodySection entry={entry} part=\"request\" text={entry.request.postData?.text}\n mimeType={entry.request.postData?.mimeType} bodySource={bodySource} />\n )}\n </div>\n <div>\n <HeaderList\n title=\"Response Headers\"\n {...(entry.response.headers ? { headers: entry.response.headers } : {})}\n />\n </div>\n </div>\n {(entry.response.content?.text || bodySource?.hasBody(entry, \"response\")) && (\n <BodySection entry={entry} part=\"response\" text={entry.response.content?.text}\n mimeType={entry.response.content?.mimeType} bodySource={bodySource} />\n )}\n </>\n );\n}\n\nfunction BodySection({ entry, part, text, mimeType, bodySource }: {\n entry: HAREntry;\n part: \"request\" | \"response\";\n text?: string | undefined;\n mimeType?: string | undefined;\n bodySource?: HarPanelBodySource | undefined;\n}) {\n const remote = !text && bodySource?.hasBody(entry, part);\n const requestId = entry._id;\n if (remote && !requestId) throw new Error(\"Private HAR body requires a request ID\");\n const [preview, setPreview] = useState<string | undefined>();\n const [error, setError] = useState<Error | undefined>();\n useEffect(() => {\n if (!remote || !bodySource || !requestId) return;\n let active = true;\n setPreview(undefined);\n setError(undefined);\n bodySource.preview(requestId, part).then(\n (value) => { if (active) setPreview(value); },\n (reason: unknown) => { if (active) setError(reason instanceof Error ? reason : new Error(String(reason))); },\n );\n return () => { active = false; };\n }, [bodySource, requestId, part, remote]);\n return (\n <div className=\"mt-density-3\">\n <div className=\"mb-1 flex items-center gap-2 font-semibold text-foreground\">\n {part === \"request\" ? \"Request Body\" : \"Response Body\"}\n {remote && bodySource && <a className=\"text-sm font-normal text-primary underline\" href={bodySource.downloadHref(entry, part)} download>Download full body</a>}\n </div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n {error ? <span role=\"alert\" className=\"text-destructive\">{error.message}</span>\n : remote && preview === undefined ? <span className=\"text-muted-foreground\">Loading preview…</span>\n : <BodyView text={text || preview || \"\"} {...(mimeType ? { mimeType } : {})} />}\n </div>\n {remote && <p className=\"mt-1 text-xs text-muted-foreground\">Preview shows at most the first 64 KiB.</p>}\n </div>\n );\n}\n\nfunction HeaderList({\n title,\n headers,\n}: {\n title: string;\n headers?: { name: string; value: string }[];\n}) {\n return (\n <>\n <div className=\"font-semibold text-foreground mb-1\">{title}</div>\n <div className=\"space-y-0.5\">\n {headers?.map((h, i) => (\n <div key={i} className=\"whitespace-nowrap\">\n <span className=\"text-purple-600 dark:text-purple-400\">{h.name}:</span> {h.value}\n </div>\n ))}\n </div>\n </>\n );\n}\n\nfunction BodyView({ text, mimeType }: { text: string; mimeType?: string }) {\n if (isJsonType(mimeType)) {\n const parsed = tryParseJson(text);\n if (parsed !== null) return <JsonView data={parsed} />;\n }\n return <pre className=\"whitespace-pre-wrap break-all\">{text}</pre>;\n}\n\nfunction tryParseJson(text: string): unknown | null {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction isJsonType(mime?: string): boolean {\n return !!mime && (mime.includes(\"json\") || mime.includes(\"javascript\"));\n}\n\nfunction matchesSearch(needle: string, entry: HAREntry): boolean {\n return [\n entry.request.method,\n entry.request.url,\n entry.request.postData?.text,\n entry.response.content?.text,\n entry._error,\n ].some((value) => !!value && value.toLowerCase().includes(needle));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)}MB`;\n}\n\nfunction statusColor(status: number): string {\n if (status >= 500) return \"text-red-600\";\n if (status >= 400) return \"text-amber-600\";\n if (status >= 300) return \"text-blue-600\";\n if (status >= 200) return \"text-green-600\";\n return \"text-muted-foreground\";\n}\n"],"names":["createContext","jsxs","jsx","useMemo","useResourceClock","DataTable","Icon","UiLoader","useContext","Fragment","useState","useEffect","JsonView"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { DataTableMenuAction } from '../DataTable.js';
1
2
  import { HAREntry } from './types.js';
2
3
  export type HarPanelProps = {
3
4
  /** HAR entries to render. */
@@ -10,6 +11,8 @@ export type HarPanelProps = {
10
11
  className?: string;
11
12
  /** Optional private body store used only when an expanded row needs a preview. */
12
13
  bodySource?: HarPanelBodySource;
14
+ /** Entries for the table's overflow menu, e.g. a `useClickyExport` action. */
15
+ menuActions?: DataTableMenuAction[];
13
16
  };
14
17
  export type HarPanelBodySource = {
15
18
  hasBody: (entry: HAREntry, part: "request" | "response") => boolean;
@@ -20,6 +23,6 @@ export type HarEntryDetailsProps = {
20
23
  entry: HAREntry;
21
24
  bodySource?: HarPanelBodySource | undefined;
22
25
  };
23
- export declare function HarPanel({ entries, search, emptyLabel, className, bodySource, }: HarPanelProps): import("react").JSX.Element;
26
+ export declare function HarPanel({ entries, search, emptyLabel, className, bodySource, menuActions, }: HarPanelProps): import("react").JSX.Element;
24
27
  export declare function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps): import("react").JSX.Element;
25
28
  //# sourceMappingURL=HarPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HarPanel.d.ts","sourceRoot":"","sources":["../../../src/data/har/HarPanel.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAOxC,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,KAAK,OAAO,CAAC;IACpE,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,YAAY,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,KAAK,MAAM,CAAC;CACzE,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;CAAE,CAAC;AAwDpG,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,MAAM,EACN,UAAuC,EACvC,SAAS,EACT,UAAU,GACX,EAAE,aAAa,+BA+Cf;AA2CD,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,oBAAoB,+BAI1E"}
1
+ {"version":3,"file":"HarPanel.d.ts","sourceRoot":"","sources":["../../../src/data/har/HarPanel.tsx"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAOxC,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,8EAA8E;IAC9E,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,KAAK,OAAO,CAAC;IACpE,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9E,YAAY,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,UAAU,KAAK,MAAM,CAAC;CACzE,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAAE,KAAK,EAAE,QAAQ,CAAC;IAAC,UAAU,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;CAAE,CAAC;AAwDpG,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,MAAM,EACN,UAAuC,EACvC,SAAS,EACT,UAAU,EACV,WAAW,GACZ,EAAE,aAAa,+BAgDf;AA2CD,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,oBAAoB,+BAI1E"}
@@ -58,7 +58,8 @@ function HarPanel({
58
58
  search,
59
59
  emptyLabel = "No HTTP traffic captured",
60
60
  className,
61
- bodySource
61
+ bodySource,
62
+ menuActions
62
63
  }) {
63
64
  const filteredEntries = useMemo(() => {
64
65
  if (search === void 0 || search.trim() === "") return entries;
@@ -84,6 +85,7 @@ function HarPanel({
84
85
  globalFilterPlaceholder: "Filter URL, method, or body…",
85
86
  defaultSort: { key: "time", dir: "asc" },
86
87
  emptyMessage: emptyLabel,
88
+ ...menuActions ? { menuActions } : {},
87
89
  ...runningCount > 0 ? {
88
90
  footer: ({ visibleRowCount, totalRowCount }) => `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? "" : "s"} · ${runningCount} running`
89
91
  } : {},
@@ -1 +1 @@
1
- {"version":3,"file":"HarPanel.js","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport { useResourceClock } from \"../../hooks/use-resource-clock\";\nimport { UiLoader } from \"../../icons\";\nimport { formatDuration } from \"../../lib/format\";\nimport { DataTable, type DataTableColumn, type DataTableFooterContext } from \"../DataTable\";\nimport { Icon } from \"../Icon\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\n\nconst ERROR_PREVIEW_CHARS = 80;\n\n/** Wall-clock ms shared by every in-flight row, ticking only while one exists. */\nconst HarClockContext = createContext<number | undefined>(undefined);\n\nexport type HarPanelProps = {\n /** HAR entries to render. */\n entries: HAREntry[];\n /** Optional external search term. When omitted, the internal DataTable search is shown. */\n search?: string;\n /** Empty-state label for no entries or no matches. */\n emptyLabel?: string;\n /** Classes applied to the panel root. */\n className?: string;\n /** Optional private body store used only when an expanded row needs a preview. */\n bodySource?: HarPanelBodySource;\n};\n\nexport type HarPanelBodySource = {\n hasBody: (entry: HAREntry, part: \"request\" | \"response\") => boolean;\n preview: (requestId: string, part: \"request\" | \"response\") => Promise<string>;\n downloadHref: (entry: HAREntry, part: \"request\" | \"response\") => string;\n};\n\nexport type HarEntryDetailsProps = { entry: HAREntry; bodySource?: HarPanelBodySource | undefined };\n\nconst COLS: DataTableColumn<HAREntry>[] = [\n {\n key: \"request.method\",\n label: \"Method\",\n shrink: true,\n },\n {\n key: \"request.url\",\n label: \"URL\",\n grow: true,\n render: (value, entry) => (\n <span className=\"inline-flex min-w-0 items-baseline gap-density-2\">\n <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>\n {entry._error ? (\n <span className=\"text-destructive text-xs\" title={entry._error}>\n {previewError(entry._error)}\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value, entry) => <HarStatus status={Number(value ?? 0)} entry={entry} />,\n sortValue: (value) => Number(value ?? 0),\n filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value, entry) =>\n entry._pending ? <PendingElapsed entry={entry} /> : `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value, entry) =>\n entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0),\n },\n {\n key: \"response.bodySize\",\n label: \"Size\",\n align: \"right\",\n shrink: true,\n render: (value) => formatBytes(Number(value ?? 0)),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"response.content.mimeType\",\n label: \"Type\",\n shrink: true,\n },\n];\n\nexport function HarPanel({\n entries,\n search,\n emptyLabel = \"No HTTP traffic captured\",\n className,\n bodySource,\n}: HarPanelProps) {\n const filteredEntries = useMemo(() => {\n if (search === undefined || search.trim() === \"\") return entries;\n return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));\n }, [entries, search]);\n const runningCount = useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);\n const now = useResourceClock(runningCount > 0);\n\n if (!entries || entries.length === 0) {\n return (\n <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>\n );\n }\n\n return (\n // A flex column, not a plain block: DataTable sizes itself as `flex-1\n // min-h-0` so its own scroll region can be bounded, and in a block parent\n // that resolves to content height — the table grows past the panel and\n // nothing scrolls.\n <div className={`flex h-full min-h-0 flex-col ${className ?? \"\"}`}>\n <HarClockContext.Provider value={now}>\n <DataTable\n data={filteredEntries}\n columns={COLS}\n autoFilter\n showGlobalFilter={search === undefined}\n globalFilterPlaceholder=\"Filter URL, method, or body…\"\n defaultSort={{ key: \"time\", dir: \"asc\" }}\n emptyMessage={emptyLabel}\n {...(runningCount > 0\n ? {\n footer: ({ visibleRowCount, totalRowCount }: DataTableFooterContext) =>\n `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? \"\" : \"s\"} · ${runningCount} running`,\n }\n : {})}\n getRowId={(entry, index) =>\n // `_id` is the same on a request's pending and completed snapshots,\n // so an expanded row survives the transition. Without it, leave out\n // an in-flight entry's `time`, which grows with every snapshot.\n entry._id ??\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? \"-pending\" : `-${entry.time}`}`\n }\n renderExpandedRow={(entry) => <HarEntryDetails entry={entry} bodySource={bodySource} />}\n />\n </HarClockContext.Provider>\n </div>\n );\n}\n\nfunction HarStatus({ status, entry }: { status: number; entry: HAREntry }) {\n if (entry._pending) {\n return (\n <span className=\"inline-flex items-center gap-1 text-muted-foreground\">\n <Icon icon={UiLoader} className=\"animate-spin\" />\n running\n </span>\n );\n }\n if (entry._error) return <span className=\"font-semibold text-destructive\">ERR</span>;\n return <span className={statusColor(status)}>{String(status)}</span>;\n}\n\nfunction PendingElapsed({ entry }: { entry: HAREntry }) {\n const now = useContext(HarClockContext);\n if (now === undefined) throw new Error(\"PendingElapsed must render inside HarPanel's clock\");\n return <span className=\"tabular-nums\">{formatDuration(pendingElapsedMs(entry, now))}</span>;\n}\n\n/**\n * Elapsed time of an in-flight request, measured from `startedDateTime` so it\n * keeps ticking between snapshots. The snapshot's `time` is only used when the\n * start timestamp cannot be parsed.\n */\nfunction pendingElapsedMs(entry: HAREntry, now: number): number {\n const started = Date.parse(entry.startedDateTime ?? \"\");\n if (Number.isNaN(started)) return entry.time;\n // Server and browser clocks can disagree by a little; a negative elapsed is noise.\n return Math.max(0, now - started);\n}\n\nfunction harStatusLabel(status: number, entry: HAREntry): string {\n if (entry._pending) return \"running\";\n if (entry._error) return \"ERR\";\n return String(status);\n}\n\nfunction previewError(error: string): string {\n return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;\n}\n\nexport function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps) {\n const parentNow = useContext(HarClockContext);\n const ownNow = useResourceClock(Boolean(entry._pending) && parentNow === undefined);\n return <HarClockContext.Provider value={parentNow ?? ownNow}><HarRowDetails entry={entry} bodySource={bodySource} /></HarClockContext.Provider>;\n}\n\nfunction HarRowDetails({ entry, bodySource }: HarEntryDetailsProps) {\n return (\n <>\n {entry._error && (\n <div className=\"mb-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Transport Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{entry._error}</pre>\n </div>\n )}\n {entry._pending && (\n <div className=\"mb-density-3 text-muted-foreground\">\n Waiting for response — in flight for <PendingElapsed entry={entry} />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-density-4\">\n <div>\n <HeaderList\n title=\"Request Headers\"\n {...(entry.request.headers ? { headers: entry.request.headers } : {})}\n />\n {(entry.request.postData?.text || bodySource?.hasBody(entry, \"request\")) && (\n <BodySection entry={entry} part=\"request\" text={entry.request.postData?.text}\n mimeType={entry.request.postData?.mimeType} bodySource={bodySource} />\n )}\n </div>\n <div>\n <HeaderList\n title=\"Response Headers\"\n {...(entry.response.headers ? { headers: entry.response.headers } : {})}\n />\n </div>\n </div>\n {(entry.response.content?.text || bodySource?.hasBody(entry, \"response\")) && (\n <BodySection entry={entry} part=\"response\" text={entry.response.content?.text}\n mimeType={entry.response.content?.mimeType} bodySource={bodySource} />\n )}\n </>\n );\n}\n\nfunction BodySection({ entry, part, text, mimeType, bodySource }: {\n entry: HAREntry;\n part: \"request\" | \"response\";\n text?: string | undefined;\n mimeType?: string | undefined;\n bodySource?: HarPanelBodySource | undefined;\n}) {\n const remote = !text && bodySource?.hasBody(entry, part);\n const requestId = entry._id;\n if (remote && !requestId) throw new Error(\"Private HAR body requires a request ID\");\n const [preview, setPreview] = useState<string | undefined>();\n const [error, setError] = useState<Error | undefined>();\n useEffect(() => {\n if (!remote || !bodySource || !requestId) return;\n let active = true;\n setPreview(undefined);\n setError(undefined);\n bodySource.preview(requestId, part).then(\n (value) => { if (active) setPreview(value); },\n (reason: unknown) => { if (active) setError(reason instanceof Error ? reason : new Error(String(reason))); },\n );\n return () => { active = false; };\n }, [bodySource, requestId, part, remote]);\n return (\n <div className=\"mt-density-3\">\n <div className=\"mb-1 flex items-center gap-2 font-semibold text-foreground\">\n {part === \"request\" ? \"Request Body\" : \"Response Body\"}\n {remote && bodySource && <a className=\"text-sm font-normal text-primary underline\" href={bodySource.downloadHref(entry, part)} download>Download full body</a>}\n </div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n {error ? <span role=\"alert\" className=\"text-destructive\">{error.message}</span>\n : remote && preview === undefined ? <span className=\"text-muted-foreground\">Loading preview…</span>\n : <BodyView text={text || preview || \"\"} {...(mimeType ? { mimeType } : {})} />}\n </div>\n {remote && <p className=\"mt-1 text-xs text-muted-foreground\">Preview shows at most the first 64 KiB.</p>}\n </div>\n );\n}\n\nfunction HeaderList({\n title,\n headers,\n}: {\n title: string;\n headers?: { name: string; value: string }[];\n}) {\n return (\n <>\n <div className=\"font-semibold text-foreground mb-1\">{title}</div>\n <div className=\"space-y-0.5\">\n {headers?.map((h, i) => (\n <div key={i} className=\"whitespace-nowrap\">\n <span className=\"text-purple-600 dark:text-purple-400\">{h.name}:</span> {h.value}\n </div>\n ))}\n </div>\n </>\n );\n}\n\nfunction BodyView({ text, mimeType }: { text: string; mimeType?: string }) {\n if (isJsonType(mimeType)) {\n const parsed = tryParseJson(text);\n if (parsed !== null) return <JsonView data={parsed} />;\n }\n return <pre className=\"whitespace-pre-wrap break-all\">{text}</pre>;\n}\n\nfunction tryParseJson(text: string): unknown | null {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction isJsonType(mime?: string): boolean {\n return !!mime && (mime.includes(\"json\") || mime.includes(\"javascript\"));\n}\n\nfunction matchesSearch(needle: string, entry: HAREntry): boolean {\n return [\n entry.request.method,\n entry.request.url,\n entry.request.postData?.text,\n entry.response.content?.text,\n entry._error,\n ].some((value) => !!value && value.toLowerCase().includes(needle));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)}MB`;\n}\n\nfunction statusColor(status: number): string {\n if (status >= 500) return \"text-red-600\";\n if (status >= 400) return \"text-amber-600\";\n if (status >= 300) return \"text-blue-600\";\n if (status >= 200) return \"text-green-600\";\n return \"text-muted-foreground\";\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HarPanel.js","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useState } from \"react\";\nimport { useResourceClock } from \"../../hooks/use-resource-clock\";\nimport { UiLoader } from \"../../icons\";\nimport { formatDuration } from \"../../lib/format\";\nimport {\n DataTable,\n type DataTableColumn,\n type DataTableFooterContext,\n type DataTableMenuAction,\n} from \"../DataTable\";\nimport { Icon } from \"../Icon\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\n\nconst ERROR_PREVIEW_CHARS = 80;\n\n/** Wall-clock ms shared by every in-flight row, ticking only while one exists. */\nconst HarClockContext = createContext<number | undefined>(undefined);\n\nexport type HarPanelProps = {\n /** HAR entries to render. */\n entries: HAREntry[];\n /** Optional external search term. When omitted, the internal DataTable search is shown. */\n search?: string;\n /** Empty-state label for no entries or no matches. */\n emptyLabel?: string;\n /** Classes applied to the panel root. */\n className?: string;\n /** Optional private body store used only when an expanded row needs a preview. */\n bodySource?: HarPanelBodySource;\n /** Entries for the table's overflow menu, e.g. a `useClickyExport` action. */\n menuActions?: DataTableMenuAction[];\n};\n\nexport type HarPanelBodySource = {\n hasBody: (entry: HAREntry, part: \"request\" | \"response\") => boolean;\n preview: (requestId: string, part: \"request\" | \"response\") => Promise<string>;\n downloadHref: (entry: HAREntry, part: \"request\" | \"response\") => string;\n};\n\nexport type HarEntryDetailsProps = { entry: HAREntry; bodySource?: HarPanelBodySource | undefined };\n\nconst COLS: DataTableColumn<HAREntry>[] = [\n {\n key: \"request.method\",\n label: \"Method\",\n shrink: true,\n },\n {\n key: \"request.url\",\n label: \"URL\",\n grow: true,\n render: (value, entry) => (\n <span className=\"inline-flex min-w-0 items-baseline gap-density-2\">\n <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>\n {entry._error ? (\n <span className=\"text-destructive text-xs\" title={entry._error}>\n {previewError(entry._error)}\n </span>\n ) : null}\n </span>\n ),\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value, entry) => <HarStatus status={Number(value ?? 0)} entry={entry} />,\n sortValue: (value) => Number(value ?? 0),\n filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value, entry) =>\n entry._pending ? <PendingElapsed entry={entry} /> : `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value, entry) =>\n entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0),\n },\n {\n key: \"response.bodySize\",\n label: \"Size\",\n align: \"right\",\n shrink: true,\n render: (value) => formatBytes(Number(value ?? 0)),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"response.content.mimeType\",\n label: \"Type\",\n shrink: true,\n },\n];\n\nexport function HarPanel({\n entries,\n search,\n emptyLabel = \"No HTTP traffic captured\",\n className,\n bodySource,\n menuActions,\n}: HarPanelProps) {\n const filteredEntries = useMemo(() => {\n if (search === undefined || search.trim() === \"\") return entries;\n return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));\n }, [entries, search]);\n const runningCount = useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);\n const now = useResourceClock(runningCount > 0);\n\n if (!entries || entries.length === 0) {\n return (\n <div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>\n );\n }\n\n return (\n // A flex column, not a plain block: DataTable sizes itself as `flex-1\n // min-h-0` so its own scroll region can be bounded, and in a block parent\n // that resolves to content height — the table grows past the panel and\n // nothing scrolls.\n <div className={`flex h-full min-h-0 flex-col ${className ?? \"\"}`}>\n <HarClockContext.Provider value={now}>\n <DataTable\n data={filteredEntries}\n columns={COLS}\n autoFilter\n showGlobalFilter={search === undefined}\n globalFilterPlaceholder=\"Filter URL, method, or body…\"\n defaultSort={{ key: \"time\", dir: \"asc\" }}\n emptyMessage={emptyLabel}\n {...(menuActions ? { menuActions } : {})}\n {...(runningCount > 0\n ? {\n footer: ({ visibleRowCount, totalRowCount }: DataTableFooterContext) =>\n `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? \"\" : \"s\"} · ${runningCount} running`,\n }\n : {})}\n getRowId={(entry, index) =>\n // `_id` is the same on a request's pending and completed snapshots,\n // so an expanded row survives the transition. Without it, leave out\n // an in-flight entry's `time`, which grows with every snapshot.\n entry._id ??\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? \"-pending\" : `-${entry.time}`}`\n }\n renderExpandedRow={(entry) => <HarEntryDetails entry={entry} bodySource={bodySource} />}\n />\n </HarClockContext.Provider>\n </div>\n );\n}\n\nfunction HarStatus({ status, entry }: { status: number; entry: HAREntry }) {\n if (entry._pending) {\n return (\n <span className=\"inline-flex items-center gap-1 text-muted-foreground\">\n <Icon icon={UiLoader} className=\"animate-spin\" />\n running\n </span>\n );\n }\n if (entry._error) return <span className=\"font-semibold text-destructive\">ERR</span>;\n return <span className={statusColor(status)}>{String(status)}</span>;\n}\n\nfunction PendingElapsed({ entry }: { entry: HAREntry }) {\n const now = useContext(HarClockContext);\n if (now === undefined) throw new Error(\"PendingElapsed must render inside HarPanel's clock\");\n return <span className=\"tabular-nums\">{formatDuration(pendingElapsedMs(entry, now))}</span>;\n}\n\n/**\n * Elapsed time of an in-flight request, measured from `startedDateTime` so it\n * keeps ticking between snapshots. The snapshot's `time` is only used when the\n * start timestamp cannot be parsed.\n */\nfunction pendingElapsedMs(entry: HAREntry, now: number): number {\n const started = Date.parse(entry.startedDateTime ?? \"\");\n if (Number.isNaN(started)) return entry.time;\n // Server and browser clocks can disagree by a little; a negative elapsed is noise.\n return Math.max(0, now - started);\n}\n\nfunction harStatusLabel(status: number, entry: HAREntry): string {\n if (entry._pending) return \"running\";\n if (entry._error) return \"ERR\";\n return String(status);\n}\n\nfunction previewError(error: string): string {\n return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;\n}\n\nexport function HarEntryDetails({ entry, bodySource }: HarEntryDetailsProps) {\n const parentNow = useContext(HarClockContext);\n const ownNow = useResourceClock(Boolean(entry._pending) && parentNow === undefined);\n return <HarClockContext.Provider value={parentNow ?? ownNow}><HarRowDetails entry={entry} bodySource={bodySource} /></HarClockContext.Provider>;\n}\n\nfunction HarRowDetails({ entry, bodySource }: HarEntryDetailsProps) {\n return (\n <>\n {entry._error && (\n <div className=\"mb-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Transport Error</div>\n <pre className=\"whitespace-pre-wrap break-all text-destructive\">{entry._error}</pre>\n </div>\n )}\n {entry._pending && (\n <div className=\"mb-density-3 text-muted-foreground\">\n Waiting for response — in flight for <PendingElapsed entry={entry} />\n </div>\n )}\n <div className=\"grid grid-cols-2 gap-density-4\">\n <div>\n <HeaderList\n title=\"Request Headers\"\n {...(entry.request.headers ? { headers: entry.request.headers } : {})}\n />\n {(entry.request.postData?.text || bodySource?.hasBody(entry, \"request\")) && (\n <BodySection entry={entry} part=\"request\" text={entry.request.postData?.text}\n mimeType={entry.request.postData?.mimeType} bodySource={bodySource} />\n )}\n </div>\n <div>\n <HeaderList\n title=\"Response Headers\"\n {...(entry.response.headers ? { headers: entry.response.headers } : {})}\n />\n </div>\n </div>\n {(entry.response.content?.text || bodySource?.hasBody(entry, \"response\")) && (\n <BodySection entry={entry} part=\"response\" text={entry.response.content?.text}\n mimeType={entry.response.content?.mimeType} bodySource={bodySource} />\n )}\n </>\n );\n}\n\nfunction BodySection({ entry, part, text, mimeType, bodySource }: {\n entry: HAREntry;\n part: \"request\" | \"response\";\n text?: string | undefined;\n mimeType?: string | undefined;\n bodySource?: HarPanelBodySource | undefined;\n}) {\n const remote = !text && bodySource?.hasBody(entry, part);\n const requestId = entry._id;\n if (remote && !requestId) throw new Error(\"Private HAR body requires a request ID\");\n const [preview, setPreview] = useState<string | undefined>();\n const [error, setError] = useState<Error | undefined>();\n useEffect(() => {\n if (!remote || !bodySource || !requestId) return;\n let active = true;\n setPreview(undefined);\n setError(undefined);\n bodySource.preview(requestId, part).then(\n (value) => { if (active) setPreview(value); },\n (reason: unknown) => { if (active) setError(reason instanceof Error ? reason : new Error(String(reason))); },\n );\n return () => { active = false; };\n }, [bodySource, requestId, part, remote]);\n return (\n <div className=\"mt-density-3\">\n <div className=\"mb-1 flex items-center gap-2 font-semibold text-foreground\">\n {part === \"request\" ? \"Request Body\" : \"Response Body\"}\n {remote && bodySource && <a className=\"text-sm font-normal text-primary underline\" href={bodySource.downloadHref(entry, part)} download>Download full body</a>}\n </div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n {error ? <span role=\"alert\" className=\"text-destructive\">{error.message}</span>\n : remote && preview === undefined ? <span className=\"text-muted-foreground\">Loading preview…</span>\n : <BodyView text={text || preview || \"\"} {...(mimeType ? { mimeType } : {})} />}\n </div>\n {remote && <p className=\"mt-1 text-xs text-muted-foreground\">Preview shows at most the first 64 KiB.</p>}\n </div>\n );\n}\n\nfunction HeaderList({\n title,\n headers,\n}: {\n title: string;\n headers?: { name: string; value: string }[];\n}) {\n return (\n <>\n <div className=\"font-semibold text-foreground mb-1\">{title}</div>\n <div className=\"space-y-0.5\">\n {headers?.map((h, i) => (\n <div key={i} className=\"whitespace-nowrap\">\n <span className=\"text-purple-600 dark:text-purple-400\">{h.name}:</span> {h.value}\n </div>\n ))}\n </div>\n </>\n );\n}\n\nfunction BodyView({ text, mimeType }: { text: string; mimeType?: string }) {\n if (isJsonType(mimeType)) {\n const parsed = tryParseJson(text);\n if (parsed !== null) return <JsonView data={parsed} />;\n }\n return <pre className=\"whitespace-pre-wrap break-all\">{text}</pre>;\n}\n\nfunction tryParseJson(text: string): unknown | null {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nfunction isJsonType(mime?: string): boolean {\n return !!mime && (mime.includes(\"json\") || mime.includes(\"javascript\"));\n}\n\nfunction matchesSearch(needle: string, entry: HAREntry): boolean {\n return [\n entry.request.method,\n entry.request.url,\n entry.request.postData?.text,\n entry.response.content?.text,\n entry._error,\n ].some((value) => !!value && value.toLowerCase().includes(needle));\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)}MB`;\n}\n\nfunction statusColor(status: number): string {\n if (status >= 500) return \"text-red-600\";\n if (status >= 400) return \"text-amber-600\";\n if (status >= 300) return \"text-blue-600\";\n if (status >= 200) return \"text-green-600\";\n return \"text-muted-foreground\";\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const JsonView = require("../JsonView.cjs");
5
+ const utils = require("../../lib/utils.cjs");
6
+ function formatValue(value) {
7
+ if (typeof value === "string") return value;
8
+ return JSON.stringify(value);
9
+ }
10
+ function isStructured(value) {
11
+ if (Array.isArray(value)) return value.some((item) => item !== null && typeof item === "object");
12
+ return value !== null && typeof value === "object";
13
+ }
14
+ function Value({
15
+ value,
16
+ tree = false,
17
+ className
18
+ }) {
19
+ if (tree && isStructured(value)) return /* @__PURE__ */ jsxRuntime.jsx(JsonView.JsonView, { data: value });
20
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: utils.cn("whitespace-pre-wrap break-words font-mono", className), children: formatValue(value) });
21
+ }
22
+ function Row({ label, children }) {
23
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
24
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: label }),
25
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "min-w-0", children })
26
+ ] });
27
+ }
28
+ function Expectation({
29
+ check,
30
+ checkLabel = "matcher",
31
+ expected,
32
+ actual,
33
+ vars,
34
+ location,
35
+ className
36
+ }) {
37
+ const varEntries = Object.entries(vars ?? {});
38
+ return /* @__PURE__ */ jsxRuntime.jsxs(
39
+ "dl",
40
+ {
41
+ className: utils.cn(
42
+ "grid grid-cols-[auto_minmax(0,1fr)] items-baseline gap-x-3 gap-y-0.5 rounded border border-border bg-muted/30 px-2 py-1.5 text-[11px]",
43
+ className
44
+ ),
45
+ "data-expectation": "",
46
+ children: [
47
+ check && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: checkLabel, children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: check }) }),
48
+ varEntries.map(([name, value]) => /* @__PURE__ */ jsxRuntime.jsx(Row, { label: name, children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value, tree: true }) }, name)),
49
+ expected !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "expected", children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: expected, className: "text-emerald-700 dark:text-emerald-400" }) }),
50
+ actual !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "actual", children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: actual, className: "text-red-700 dark:text-red-400" }) }),
51
+ location && /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "at", children: /* @__PURE__ */ jsxRuntime.jsx(Value, { value: location, className: "text-muted-foreground" }) })
52
+ ]
53
+ }
54
+ );
55
+ }
56
+ exports.Expectation = Expectation;
57
+ //# sourceMappingURL=Expectation.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Expectation.cjs","sources":["../../../src/data/test-runner/Expectation.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { JsonView } from \"../JsonView\";\nimport { cn } from \"../../lib/utils\";\n\nexport type ExpectationProps = {\n /** What decided the verdict: a matcher name (`Equal`) or a CEL expression. */\n check?: string | undefined;\n /** Label for `check`; defaults to \"matcher\". */\n checkLabel?: string | undefined;\n expected?: unknown;\n actual?: unknown;\n /** Variables the check saw (CEL `cel_vars`). */\n vars?: Record<string, unknown> | undefined;\n location?: string | undefined;\n className?: string | undefined;\n};\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") return value;\n return JSON.stringify(value);\n}\n\n// A recorder summary or a list of rows nests too deep to read as one line.\nfunction isStructured(value: unknown): boolean {\n if (Array.isArray(value)) return value.some((item) => item !== null && typeof item === \"object\");\n return value !== null && typeof value === \"object\";\n}\n\nfunction Value({\n value,\n tree = false,\n className,\n}: {\n value: unknown;\n /** Expand a structured value as a tree instead of one JSON line. */\n tree?: boolean;\n className?: string;\n}) {\n if (tree && isStructured(value)) return <JsonView data={value} />;\n return (\n <code className={cn(\"whitespace-pre-wrap break-words font-mono\", className)}>\n {formatValue(value)}\n </code>\n );\n}\n\nfunction Row({ label, children }: { label: string; children: ReactNode }) {\n return (\n <>\n <dt className=\"text-muted-foreground\">{label}</dt>\n <dd className=\"min-w-0\">{children}</dd>\n </>\n );\n}\n\n/**\n * One compact label/value grid for what a check expected and what it got —\n * shared by test failures (matcher, expected, actual, location) and CEL\n * assertions (expression, variables, expected, actual).\n */\nexport function Expectation({\n check,\n checkLabel = \"matcher\",\n expected,\n actual,\n vars,\n location,\n className,\n}: ExpectationProps) {\n const varEntries = Object.entries(vars ?? {});\n return (\n <dl\n className={cn(\n \"grid grid-cols-[auto_minmax(0,1fr)] items-baseline gap-x-3 gap-y-0.5 rounded border border-border bg-muted/30 px-2 py-1.5 text-[11px]\",\n className,\n )}\n data-expectation=\"\"\n >\n {check && (\n <Row label={checkLabel}>\n <Value value={check} />\n </Row>\n )}\n {varEntries.map(([name, value]) => (\n <Row key={name} label={name}>\n <Value value={value} tree />\n </Row>\n ))}\n {expected !== undefined && (\n <Row label=\"expected\">\n <Value value={expected} className=\"text-emerald-700 dark:text-emerald-400\" />\n </Row>\n )}\n {actual !== undefined && (\n <Row label=\"actual\">\n <Value value={actual} className=\"text-red-700 dark:text-red-400\" />\n </Row>\n )}\n {location && (\n <Row label=\"at\">\n <Value value={location} className=\"text-muted-foreground\" />\n </Row>\n )}\n </dl>\n );\n}\n"],"names":["jsx","JsonView","cn","jsxs","Fragment"],"mappings":";;;;;AAiBA,SAAS,YAAY,OAAwB;AAC3C,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO,KAAK,UAAU,KAAK;AAC7B;AAGA,SAAS,aAAa,OAAyB;AAC7C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,CAAC,SAAS,SAAS,QAAQ,OAAO,SAAS,QAAQ;AAC/F,SAAO,UAAU,QAAQ,OAAO,UAAU;AAC5C;AAEA,SAAS,MAAM;AAAA,EACb;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAKG;AACD,MAAI,QAAQ,aAAa,KAAK,UAAUA,2BAAAA,IAACC,mBAAA,EAAS,MAAM,OAAO;AAC/D,SACED,+BAAC,UAAK,WAAWE,MAAAA,GAAG,6CAA6C,SAAS,GACvE,UAAA,YAAY,KAAK,EAAA,CACpB;AAEJ;AAEA,SAAS,IAAI,EAAE,OAAO,YAAoD;AACxE,SACEC,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,IAAAJ,2BAAAA,IAAC,MAAA,EAAG,WAAU,yBAAyB,UAAA,OAAM;AAAA,IAC7CA,2BAAAA,IAAC,MAAA,EAAG,WAAU,WAAW,SAAA,CAAS;AAAA,EAAA,GACpC;AAEJ;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,aAAa,OAAO,QAAQ,QAAQ,CAAA,CAAE;AAC5C,SACEG,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWD,MAAAA;AAAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,oBAAiB;AAAA,MAEhB,UAAA;AAAA,QAAA,SACCF,2BAAAA,IAAC,OAAI,OAAO,YACV,yCAAC,OAAA,EAAM,OAAO,OAAO,EAAA,CACvB;AAAA,QAED,WAAW,IAAI,CAAC,CAAC,MAAM,KAAK,MAC3BA,2BAAAA,IAAC,KAAA,EAAe,OAAO,MACrB,yCAAC,OAAA,EAAM,OAAc,MAAI,KAAA,CAAC,KADlB,IAEV,CACD;AAAA,QACA,aAAa,UACZA,2BAAAA,IAAC,KAAA,EAAI,OAAM,YACT,UAAAA,2BAAAA,IAAC,OAAA,EAAM,OAAO,UAAU,WAAU,yCAAA,CAAyC,GAC7E;AAAA,QAED,WAAW,UACVA,2BAAAA,IAAC,KAAA,EAAI,OAAM,UACT,UAAAA,2BAAAA,IAAC,OAAA,EAAM,OAAO,QAAQ,WAAU,iCAAA,CAAiC,GACnE;AAAA,QAED,YACCA,2BAAAA,IAAC,KAAA,EAAI,OAAM,MACT,UAAAA,2BAAAA,IAAC,OAAA,EAAM,OAAO,UAAU,WAAU,wBAAA,CAAwB,EAAA,CAC5D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;;"}