@flanksource/clicky-ui 0.3.41 → 0.3.43

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 (487) hide show
  1. package/dist/ai.cjs +4 -0
  2. package/dist/ai.cjs.map +1 -1
  3. package/dist/ai.js +4 -0
  4. package/dist/ai.js.map +1 -1
  5. package/dist/comments/CommentCard.cjs +3 -1
  6. package/dist/comments/CommentCard.cjs.map +1 -1
  7. package/dist/comments/CommentCard.d.ts.map +1 -1
  8. package/dist/comments/CommentCard.js +4 -2
  9. package/dist/comments/CommentCard.js.map +1 -1
  10. package/dist/comments/CommentCardParts.cjs +16 -0
  11. package/dist/comments/CommentCardParts.cjs.map +1 -1
  12. package/dist/comments/CommentCardParts.d.ts +4 -1
  13. package/dist/comments/CommentCardParts.d.ts.map +1 -1
  14. package/dist/comments/CommentCardParts.js +16 -0
  15. package/dist/comments/CommentCardParts.js.map +1 -1
  16. package/dist/comments/comment-types.cjs.map +1 -1
  17. package/dist/comments/comment-types.d.ts +4 -0
  18. package/dist/comments/comment-types.d.ts.map +1 -1
  19. package/dist/comments/comment-types.js.map +1 -1
  20. package/dist/components/Combobox.cjs +54 -8
  21. package/dist/components/Combobox.cjs.map +1 -1
  22. package/dist/components/Combobox.d.ts.map +1 -1
  23. package/dist/components/Combobox.js +56 -10
  24. package/dist/components/Combobox.js.map +1 -1
  25. package/dist/components/ComboboxMenu.cjs +139 -113
  26. package/dist/components/ComboboxMenu.cjs.map +1 -1
  27. package/dist/components/ComboboxMenu.d.ts +3 -1
  28. package/dist/components/ComboboxMenu.d.ts.map +1 -1
  29. package/dist/components/ComboboxMenu.js +139 -113
  30. package/dist/components/ComboboxMenu.js.map +1 -1
  31. package/dist/components/ComboboxMenuTrigger.cjs +87 -0
  32. package/dist/components/ComboboxMenuTrigger.cjs.map +1 -0
  33. package/dist/components/ComboboxMenuTrigger.d.ts +23 -0
  34. package/dist/components/ComboboxMenuTrigger.d.ts.map +1 -0
  35. package/dist/components/ComboboxMenuTrigger.js +87 -0
  36. package/dist/components/ComboboxMenuTrigger.js.map +1 -0
  37. package/dist/components/JsonSchemaForm.cjs +30 -7
  38. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  39. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  40. package/dist/components/JsonSchemaForm.js +30 -7
  41. package/dist/components/JsonSchemaForm.js.map +1 -1
  42. package/dist/components/SegmentedControl.cjs +20 -3
  43. package/dist/components/SegmentedControl.cjs.map +1 -1
  44. package/dist/components/SegmentedControl.d.ts +11 -0
  45. package/dist/components/SegmentedControl.d.ts.map +1 -1
  46. package/dist/components/SegmentedControl.js +21 -4
  47. package/dist/components/SegmentedControl.js.map +1 -1
  48. package/dist/components/combobox-types.d.ts +8 -0
  49. package/dist/components/combobox-types.d.ts.map +1 -1
  50. package/dist/components/json-schema-form-debug-badge.cjs +108 -0
  51. package/dist/components/json-schema-form-debug-badge.cjs.map +1 -0
  52. package/dist/components/json-schema-form-debug-badge.d.ts +10 -0
  53. package/dist/components/json-schema-form-debug-badge.d.ts.map +1 -0
  54. package/dist/components/json-schema-form-debug-badge.js +108 -0
  55. package/dist/components/json-schema-form-debug-badge.js.map +1 -0
  56. package/dist/components/json-schema-form-debug-glyph-map.cjs +129 -0
  57. package/dist/components/json-schema-form-debug-glyph-map.cjs.map +1 -0
  58. package/dist/components/json-schema-form-debug-glyph-map.d.ts +20 -0
  59. package/dist/components/json-schema-form-debug-glyph-map.d.ts.map +1 -0
  60. package/dist/components/json-schema-form-debug-glyph-map.js +129 -0
  61. package/dist/components/json-schema-form-debug-glyph-map.js.map +1 -0
  62. package/dist/components/json-schema-form-debug-glyph.cjs +13 -0
  63. package/dist/components/json-schema-form-debug-glyph.cjs.map +1 -0
  64. package/dist/components/json-schema-form-debug-glyph.d.ts +6 -0
  65. package/dist/components/json-schema-form-debug-glyph.d.ts.map +1 -0
  66. package/dist/components/json-schema-form-debug-glyph.js +13 -0
  67. package/dist/components/json-schema-form-debug-glyph.js.map +1 -0
  68. package/dist/components/json-schema-form-debug-groups.cjs +56 -0
  69. package/dist/components/json-schema-form-debug-groups.cjs.map +1 -0
  70. package/dist/components/json-schema-form-debug-groups.d.ts +8 -0
  71. package/dist/components/json-schema-form-debug-groups.d.ts.map +1 -0
  72. package/dist/components/json-schema-form-debug-groups.js +56 -0
  73. package/dist/components/json-schema-form-debug-groups.js.map +1 -0
  74. package/dist/components/json-schema-form-debug-keywords.cjs +159 -0
  75. package/dist/components/json-schema-form-debug-keywords.cjs.map +1 -0
  76. package/dist/components/json-schema-form-debug-keywords.d.ts +6 -0
  77. package/dist/components/json-schema-form-debug-keywords.d.ts.map +1 -0
  78. package/dist/components/json-schema-form-debug-keywords.js +159 -0
  79. package/dist/components/json-schema-form-debug-keywords.js.map +1 -0
  80. package/dist/components/json-schema-form-debug-listeners.cjs +102 -0
  81. package/dist/components/json-schema-form-debug-listeners.cjs.map +1 -0
  82. package/dist/components/json-schema-form-debug-listeners.d.ts +6 -0
  83. package/dist/components/json-schema-form-debug-listeners.d.ts.map +1 -0
  84. package/dist/components/json-schema-form-debug-listeners.js +102 -0
  85. package/dist/components/json-schema-form-debug-listeners.js.map +1 -0
  86. package/dist/components/json-schema-form-debug.cjs +31 -0
  87. package/dist/components/json-schema-form-debug.cjs.map +1 -0
  88. package/dist/components/json-schema-form-debug.d.ts +11 -0
  89. package/dist/components/json-schema-form-debug.d.ts.map +1 -0
  90. package/dist/components/json-schema-form-debug.js +31 -0
  91. package/dist/components/json-schema-form-debug.js.map +1 -0
  92. package/dist/components/json-schema-form-enum-choice.cjs +114 -0
  93. package/dist/components/json-schema-form-enum-choice.cjs.map +1 -0
  94. package/dist/components/json-schema-form-enum-choice.d.ts +14 -0
  95. package/dist/components/json-schema-form-enum-choice.d.ts.map +1 -0
  96. package/dist/components/json-schema-form-enum-choice.js +114 -0
  97. package/dist/components/json-schema-form-enum-choice.js.map +1 -0
  98. package/dist/components/json-schema-form-fields.cjs +3 -93
  99. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  100. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  101. package/dist/components/json-schema-form-fields.js +1 -91
  102. package/dist/components/json-schema-form-fields.js.map +1 -1
  103. package/dist/components/json-schema-form-listeners.cjs +7 -1
  104. package/dist/components/json-schema-form-listeners.cjs.map +1 -1
  105. package/dist/components/json-schema-form-listeners.d.ts.map +1 -1
  106. package/dist/components/json-schema-form-listeners.js +7 -1
  107. package/dist/components/json-schema-form-listeners.js.map +1 -1
  108. package/dist/components/json-schema-form-preferences.cjs +1 -0
  109. package/dist/components/json-schema-form-preferences.cjs.map +1 -1
  110. package/dist/components/json-schema-form-preferences.d.ts +1 -0
  111. package/dist/components/json-schema-form-preferences.d.ts.map +1 -1
  112. package/dist/components/json-schema-form-preferences.js +1 -0
  113. package/dist/components/json-schema-form-preferences.js.map +1 -1
  114. package/dist/components/json-schema-form-render.cjs +21 -5
  115. package/dist/components/json-schema-form-render.cjs.map +1 -1
  116. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  117. package/dist/components/json-schema-form-render.js +21 -5
  118. package/dist/components/json-schema-form-render.js.map +1 -1
  119. package/dist/components/json-schema-form-runtime-types.d.ts +1 -0
  120. package/dist/components/json-schema-form-runtime-types.d.ts.map +1 -1
  121. package/dist/components/json-schema-form-tone.cjs +12 -0
  122. package/dist/components/json-schema-form-tone.cjs.map +1 -1
  123. package/dist/components/json-schema-form-tone.js +12 -0
  124. package/dist/components/json-schema-form-tone.js.map +1 -1
  125. package/dist/components/json-schema-form-types.d.ts +1 -0
  126. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  127. package/dist/data/GraphDiagram.cjs +125 -138
  128. package/dist/data/GraphDiagram.cjs.map +1 -1
  129. package/dist/data/GraphDiagram.d.ts +30 -23
  130. package/dist/data/GraphDiagram.d.ts.map +1 -1
  131. package/dist/data/GraphDiagram.js +128 -141
  132. package/dist/data/GraphDiagram.js.map +1 -1
  133. package/dist/data/GraphDiagramEdges.cjs +208 -0
  134. package/dist/data/GraphDiagramEdges.cjs.map +1 -0
  135. package/dist/data/GraphDiagramEdges.d.ts +40 -0
  136. package/dist/data/GraphDiagramEdges.d.ts.map +1 -0
  137. package/dist/data/GraphDiagramEdges.js +208 -0
  138. package/dist/data/GraphDiagramEdges.js.map +1 -0
  139. package/dist/data/GraphDiagramNodes.cjs +142 -0
  140. package/dist/data/GraphDiagramNodes.cjs.map +1 -0
  141. package/dist/data/GraphDiagramNodes.d.ts +22 -0
  142. package/dist/data/GraphDiagramNodes.d.ts.map +1 -0
  143. package/dist/data/GraphDiagramNodes.js +142 -0
  144. package/dist/data/GraphDiagramNodes.js.map +1 -0
  145. package/dist/data/ai/PromptRunEditor/RuntimeRows.cjs +8 -3
  146. package/dist/data/ai/PromptRunEditor/RuntimeRows.cjs.map +1 -1
  147. package/dist/data/ai/PromptRunEditor/RuntimeRows.d.ts.map +1 -1
  148. package/dist/data/ai/PromptRunEditor/RuntimeRows.js +8 -3
  149. package/dist/data/ai/PromptRunEditor/RuntimeRows.js.map +1 -1
  150. package/dist/data/ai/PromptRunEditor/index.cjs +37 -43
  151. package/dist/data/ai/PromptRunEditor/index.cjs.map +1 -1
  152. package/dist/data/ai/PromptRunEditor/index.d.ts +3 -1
  153. package/dist/data/ai/PromptRunEditor/index.d.ts.map +1 -1
  154. package/dist/data/ai/PromptRunEditor/index.js +38 -44
  155. package/dist/data/ai/PromptRunEditor/index.js.map +1 -1
  156. package/dist/data/ai/PromptRunEditor/runtimeActions.cjs +16 -5
  157. package/dist/data/ai/PromptRunEditor/runtimeActions.cjs.map +1 -1
  158. package/dist/data/ai/PromptRunEditor/runtimeActions.d.ts +4 -1
  159. package/dist/data/ai/PromptRunEditor/runtimeActions.d.ts.map +1 -1
  160. package/dist/data/ai/PromptRunEditor/runtimeActions.js +17 -6
  161. package/dist/data/ai/PromptRunEditor/runtimeActions.js.map +1 -1
  162. package/dist/data/ai/SessionInspector.approvals-model.cjs +12 -0
  163. package/dist/data/ai/SessionInspector.approvals-model.cjs.map +1 -1
  164. package/dist/data/ai/SessionInspector.approvals-model.d.ts +11 -2
  165. package/dist/data/ai/SessionInspector.approvals-model.d.ts.map +1 -1
  166. package/dist/data/ai/SessionInspector.approvals-model.js +12 -0
  167. package/dist/data/ai/SessionInspector.approvals-model.js.map +1 -1
  168. package/dist/data/ai/SessionInspector.approvals.cjs +25 -1
  169. package/dist/data/ai/SessionInspector.approvals.cjs.map +1 -1
  170. package/dist/data/ai/SessionInspector.approvals.d.ts.map +1 -1
  171. package/dist/data/ai/SessionInspector.approvals.js +26 -2
  172. package/dist/data/ai/SessionInspector.approvals.js.map +1 -1
  173. package/dist/data/ai/SessionInspector.summary.cjs +9 -3
  174. package/dist/data/ai/SessionInspector.summary.cjs.map +1 -1
  175. package/dist/data/ai/SessionInspector.summary.d.ts.map +1 -1
  176. package/dist/data/ai/SessionInspector.summary.js +9 -3
  177. package/dist/data/ai/SessionInspector.summary.js.map +1 -1
  178. package/dist/data/ai/SessionInspector.transcript.cjs +4 -0
  179. package/dist/data/ai/SessionInspector.transcript.cjs.map +1 -1
  180. package/dist/data/ai/SessionInspector.transcript.d.ts.map +1 -1
  181. package/dist/data/ai/SessionInspector.transcript.js +4 -0
  182. package/dist/data/ai/SessionInspector.transcript.js.map +1 -1
  183. package/dist/data/ai/SessionViewer.approval-body.cjs +146 -0
  184. package/dist/data/ai/SessionViewer.approval-body.cjs.map +1 -0
  185. package/dist/data/ai/SessionViewer.approval-body.d.ts +11 -0
  186. package/dist/data/ai/SessionViewer.approval-body.d.ts.map +1 -0
  187. package/dist/data/ai/SessionViewer.approval-body.js +146 -0
  188. package/dist/data/ai/SessionViewer.approval-body.js.map +1 -0
  189. package/dist/data/ai/SessionViewer.approval-forms.cjs +150 -0
  190. package/dist/data/ai/SessionViewer.approval-forms.cjs.map +1 -0
  191. package/dist/data/ai/SessionViewer.approval-forms.d.ts +23 -0
  192. package/dist/data/ai/SessionViewer.approval-forms.d.ts.map +1 -0
  193. package/dist/data/ai/SessionViewer.approval-forms.js +150 -0
  194. package/dist/data/ai/SessionViewer.approval-forms.js.map +1 -0
  195. package/dist/data/ai/SessionViewer.cjs +57 -20
  196. package/dist/data/ai/SessionViewer.cjs.map +1 -1
  197. package/dist/data/ai/SessionViewer.d.ts +13 -3
  198. package/dist/data/ai/SessionViewer.d.ts.map +1 -1
  199. package/dist/data/ai/SessionViewer.decision-controls.cjs +313 -0
  200. package/dist/data/ai/SessionViewer.decision-controls.cjs.map +1 -0
  201. package/dist/data/ai/SessionViewer.decision-controls.d.ts +43 -0
  202. package/dist/data/ai/SessionViewer.decision-controls.d.ts.map +1 -0
  203. package/dist/data/ai/SessionViewer.decision-controls.js +313 -0
  204. package/dist/data/ai/SessionViewer.decision-controls.js.map +1 -0
  205. package/dist/data/ai/SessionViewer.decision.cjs +21 -0
  206. package/dist/data/ai/SessionViewer.decision.cjs.map +1 -0
  207. package/dist/data/ai/SessionViewer.decision.d.ts +12 -0
  208. package/dist/data/ai/SessionViewer.decision.d.ts.map +1 -0
  209. package/dist/data/ai/SessionViewer.decision.js +21 -0
  210. package/dist/data/ai/SessionViewer.decision.js.map +1 -0
  211. package/dist/data/ai/SessionViewer.input.cjs +14 -0
  212. package/dist/data/ai/SessionViewer.input.cjs.map +1 -1
  213. package/dist/data/ai/SessionViewer.input.d.ts +5 -0
  214. package/dist/data/ai/SessionViewer.input.d.ts.map +1 -1
  215. package/dist/data/ai/SessionViewer.input.js +14 -0
  216. package/dist/data/ai/SessionViewer.input.js.map +1 -1
  217. package/dist/data/ai/SessionViewer.js +58 -21
  218. package/dist/data/ai/SessionViewer.js.map +1 -1
  219. package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
  220. package/dist/data/ai/SessionViewer.model.d.ts +4 -0
  221. package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
  222. package/dist/data/ai/SessionViewer.model.js.map +1 -1
  223. package/dist/data/ai/SessionViewer.rows.cjs +6 -3
  224. package/dist/data/ai/SessionViewer.rows.cjs.map +1 -1
  225. package/dist/data/ai/SessionViewer.rows.d.ts +2 -1
  226. package/dist/data/ai/SessionViewer.rows.d.ts.map +1 -1
  227. package/dist/data/ai/SessionViewer.rows.js +6 -3
  228. package/dist/data/ai/SessionViewer.rows.js.map +1 -1
  229. package/dist/data/ai/SessionViewer.tool-row.cjs +29 -237
  230. package/dist/data/ai/SessionViewer.tool-row.cjs.map +1 -1
  231. package/dist/data/ai/SessionViewer.tool-row.d.ts.map +1 -1
  232. package/dist/data/ai/SessionViewer.tool-row.js +27 -235
  233. package/dist/data/ai/SessionViewer.tool-row.js.map +1 -1
  234. package/dist/data/ai/SessionViewer.unified.d.ts +4 -0
  235. package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
  236. package/dist/data/ai/SessionViewerMenu.cjs +8 -2
  237. package/dist/data/ai/SessionViewerMenu.cjs.map +1 -1
  238. package/dist/data/ai/SessionViewerMenu.d.ts +4 -1
  239. package/dist/data/ai/SessionViewerMenu.d.ts.map +1 -1
  240. package/dist/data/ai/SessionViewerMenu.js +8 -2
  241. package/dist/data/ai/SessionViewerMenu.js.map +1 -1
  242. package/dist/data/ai/SpecRuntimeEditor/CommitSection.cjs +28 -17
  243. package/dist/data/ai/SpecRuntimeEditor/CommitSection.cjs.map +1 -1
  244. package/dist/data/ai/SpecRuntimeEditor/CommitSection.d.ts +10 -2
  245. package/dist/data/ai/SpecRuntimeEditor/CommitSection.d.ts.map +1 -1
  246. package/dist/data/ai/SpecRuntimeEditor/CommitSection.js +29 -18
  247. package/dist/data/ai/SpecRuntimeEditor/CommitSection.js.map +1 -1
  248. package/dist/data/ai/SpecRuntimeEditor/index.cjs +9 -1
  249. package/dist/data/ai/SpecRuntimeEditor/index.cjs.map +1 -1
  250. package/dist/data/ai/SpecRuntimeEditor/index.d.ts +9 -2
  251. package/dist/data/ai/SpecRuntimeEditor/index.d.ts.map +1 -1
  252. package/dist/data/ai/SpecRuntimeEditor/index.js +9 -1
  253. package/dist/data/ai/SpecRuntimeEditor/index.js.map +1 -1
  254. package/dist/data/ai/SpecRuntimeEditor/permission-mode-visuals.cjs +1 -1
  255. package/dist/data/ai/SpecRuntimeEditor/permission-mode-visuals.js +1 -1
  256. package/dist/data/ai/SpecRuntimeEditor/summaries.cjs.map +1 -1
  257. package/dist/data/ai/SpecRuntimeEditor/summaries.d.ts +8 -2
  258. package/dist/data/ai/SpecRuntimeEditor/summaries.d.ts.map +1 -1
  259. package/dist/data/ai/SpecRuntimeEditor/summaries.js.map +1 -1
  260. package/dist/data/ai/SpecRuntimeEditor/update.cjs +14 -6
  261. package/dist/data/ai/SpecRuntimeEditor/update.cjs.map +1 -1
  262. package/dist/data/ai/SpecRuntimeEditor/update.d.ts +1 -0
  263. package/dist/data/ai/SpecRuntimeEditor/update.d.ts.map +1 -1
  264. package/dist/data/ai/SpecRuntimeEditor/update.js +14 -6
  265. package/dist/data/ai/SpecRuntimeEditor/update.js.map +1 -1
  266. package/dist/data/ai/approval-request.cjs +75 -0
  267. package/dist/data/ai/approval-request.cjs.map +1 -0
  268. package/dist/data/ai/approval-request.d.ts +155 -0
  269. package/dist/data/ai/approval-request.d.ts.map +1 -0
  270. package/dist/data/ai/approval-request.js +75 -0
  271. package/dist/data/ai/approval-request.js.map +1 -0
  272. package/dist/data/ai/index.d.ts +3 -0
  273. package/dist/data/ai/index.d.ts.map +1 -1
  274. package/dist/data/ai/runtime-preset-menu/SavePresetDialog.cjs +125 -0
  275. package/dist/data/ai/runtime-preset-menu/SavePresetDialog.cjs.map +1 -0
  276. package/dist/data/ai/runtime-preset-menu/SavePresetDialog.d.ts +11 -0
  277. package/dist/data/ai/runtime-preset-menu/SavePresetDialog.d.ts.map +1 -0
  278. package/dist/data/ai/runtime-preset-menu/SavePresetDialog.js +125 -0
  279. package/dist/data/ai/runtime-preset-menu/SavePresetDialog.js.map +1 -0
  280. package/dist/data/ai/runtime-preset-menu/index.d.ts +3 -0
  281. package/dist/data/ai/runtime-preset-menu/index.d.ts.map +1 -0
  282. package/dist/data/ai/runtime-preset-menu/model.cjs +73 -0
  283. package/dist/data/ai/runtime-preset-menu/model.cjs.map +1 -0
  284. package/dist/data/ai/runtime-preset-menu/model.d.ts +13 -0
  285. package/dist/data/ai/runtime-preset-menu/model.d.ts.map +1 -0
  286. package/dist/data/ai/runtime-preset-menu/model.js +73 -0
  287. package/dist/data/ai/runtime-preset-menu/model.js.map +1 -0
  288. package/dist/data/ai/runtime-preset-menu/use-runtime-preset-menu.cjs +149 -0
  289. package/dist/data/ai/runtime-preset-menu/use-runtime-preset-menu.cjs.map +1 -0
  290. package/dist/data/ai/runtime-preset-menu/use-runtime-preset-menu.d.ts +15 -0
  291. package/dist/data/ai/runtime-preset-menu/use-runtime-preset-menu.d.ts.map +1 -0
  292. package/dist/data/ai/runtime-preset-menu/use-runtime-preset-menu.js +149 -0
  293. package/dist/data/ai/runtime-preset-menu/use-runtime-preset-menu.js.map +1 -0
  294. package/dist/data/ai/runtime-spec-fields.cjs +166 -0
  295. package/dist/data/ai/runtime-spec-fields.cjs.map +1 -0
  296. package/dist/data/ai/runtime-spec-fields.d.ts +13 -0
  297. package/dist/data/ai/runtime-spec-fields.d.ts.map +1 -0
  298. package/dist/data/ai/runtime-spec-fields.js +166 -0
  299. package/dist/data/ai/runtime-spec-fields.js.map +1 -0
  300. package/dist/data/ai/session-categories.cjs +4 -0
  301. package/dist/data/ai/session-categories.cjs.map +1 -1
  302. package/dist/data/ai/session-categories.d.ts +1 -0
  303. package/dist/data/ai/session-categories.d.ts.map +1 -1
  304. package/dist/data/ai/session-categories.js +4 -0
  305. package/dist/data/ai/session-categories.js.map +1 -1
  306. package/dist/data/ai/use-session-scroll.cjs +49 -21
  307. package/dist/data/ai/use-session-scroll.cjs.map +1 -1
  308. package/dist/data/ai/use-session-scroll.d.ts +2 -0
  309. package/dist/data/ai/use-session-scroll.d.ts.map +1 -1
  310. package/dist/data/ai/use-session-scroll.js +49 -21
  311. package/dist/data/ai/use-session-scroll.js.map +1 -1
  312. package/dist/data/chat/Chat.runtime.cjs +1 -1
  313. package/dist/data/chat/Chat.runtime.cjs.map +1 -1
  314. package/dist/data/chat/Chat.runtime.d.ts.map +1 -1
  315. package/dist/data/chat/Chat.runtime.js +1 -1
  316. package/dist/data/chat/Chat.runtime.js.map +1 -1
  317. package/dist/data/graph-columns-layout.cjs +216 -0
  318. package/dist/data/graph-columns-layout.cjs.map +1 -0
  319. package/dist/data/graph-columns-layout.d.ts +56 -0
  320. package/dist/data/graph-columns-layout.d.ts.map +1 -0
  321. package/dist/data/graph-columns-layout.js +216 -0
  322. package/dist/data/graph-columns-layout.js.map +1 -0
  323. package/dist/data/graph-diagram-model.cjs +73 -0
  324. package/dist/data/graph-diagram-model.cjs.map +1 -0
  325. package/dist/data/graph-diagram-model.d.ts +90 -0
  326. package/dist/data/graph-diagram-model.d.ts.map +1 -0
  327. package/dist/data/graph-diagram-model.js +73 -0
  328. package/dist/data/graph-diagram-model.js.map +1 -0
  329. package/dist/data/graph-edge-routing.cjs +235 -0
  330. package/dist/data/graph-edge-routing.cjs.map +1 -0
  331. package/dist/data/graph-edge-routing.d.ts +73 -0
  332. package/dist/data/graph-edge-routing.d.ts.map +1 -0
  333. package/dist/data/graph-edge-routing.js +235 -0
  334. package/dist/data/graph-edge-routing.js.map +1 -0
  335. package/dist/data/graph-layout.cjs +0 -82
  336. package/dist/data/graph-layout.cjs.map +1 -1
  337. package/dist/data/graph-layout.d.ts +1 -28
  338. package/dist/data/graph-layout.d.ts.map +1 -1
  339. package/dist/data/graph-layout.js +1 -83
  340. package/dist/data/graph-layout.js.map +1 -1
  341. package/dist/data/har/HarPanel.cjs +129 -33
  342. package/dist/data/har/HarPanel.cjs.map +1 -1
  343. package/dist/data/har/HarPanel.d.ts +13 -1
  344. package/dist/data/har/HarPanel.d.ts.map +1 -1
  345. package/dist/data/har/HarPanel.js +130 -34
  346. package/dist/data/har/HarPanel.js.map +1 -1
  347. package/dist/data/har/fixtures.d.ts +12 -0
  348. package/dist/data/har/fixtures.d.ts.map +1 -1
  349. package/dist/data/har/types.d.ts +12 -0
  350. package/dist/data/har/types.d.ts.map +1 -1
  351. package/dist/data/runtime/RuntimeBar.cjs +67 -84
  352. package/dist/data/runtime/RuntimeBar.cjs.map +1 -1
  353. package/dist/data/runtime/RuntimeBar.d.ts +4 -5
  354. package/dist/data/runtime/RuntimeBar.d.ts.map +1 -1
  355. package/dist/data/runtime/RuntimeBar.fields.cjs +170 -0
  356. package/dist/data/runtime/RuntimeBar.fields.cjs.map +1 -0
  357. package/dist/data/runtime/RuntimeBar.fields.d.ts +16 -0
  358. package/dist/data/runtime/RuntimeBar.fields.d.ts.map +1 -0
  359. package/dist/data/runtime/RuntimeBar.fields.js +170 -0
  360. package/dist/data/runtime/RuntimeBar.fields.js.map +1 -0
  361. package/dist/data/runtime/RuntimeBar.js +70 -87
  362. package/dist/data/runtime/RuntimeBar.js.map +1 -1
  363. package/dist/data/runtime/RuntimeBar.selection.cjs +56 -0
  364. package/dist/data/runtime/RuntimeBar.selection.cjs.map +1 -0
  365. package/dist/data/runtime/RuntimeBar.selection.d.ts +20 -0
  366. package/dist/data/runtime/RuntimeBar.selection.d.ts.map +1 -0
  367. package/dist/data/runtime/RuntimeBar.selection.js +56 -0
  368. package/dist/data/runtime/RuntimeBar.selection.js.map +1 -0
  369. package/dist/data/runtime/RuntimeBarActions.cjs +10 -6
  370. package/dist/data/runtime/RuntimeBarActions.cjs.map +1 -1
  371. package/dist/data/runtime/RuntimeBarActions.d.ts +8 -1
  372. package/dist/data/runtime/RuntimeBarActions.d.ts.map +1 -1
  373. package/dist/data/runtime/RuntimeBarActions.js +10 -6
  374. package/dist/data/runtime/RuntimeBarActions.js.map +1 -1
  375. package/dist/data/runtime/RuntimeBarIdentity.cjs +166 -0
  376. package/dist/data/runtime/RuntimeBarIdentity.cjs.map +1 -0
  377. package/dist/data/runtime/RuntimeBarIdentity.d.ts +25 -0
  378. package/dist/data/runtime/RuntimeBarIdentity.d.ts.map +1 -0
  379. package/dist/data/runtime/RuntimeBarIdentity.js +166 -0
  380. package/dist/data/runtime/RuntimeBarIdentity.js.map +1 -0
  381. package/dist/data/runtime/RuntimeBarLayout.cjs +95 -0
  382. package/dist/data/runtime/RuntimeBarLayout.cjs.map +1 -0
  383. package/dist/data/runtime/RuntimeBarLayout.d.ts +9 -0
  384. package/dist/data/runtime/RuntimeBarLayout.d.ts.map +1 -0
  385. package/dist/data/runtime/RuntimeBarLayout.js +95 -0
  386. package/dist/data/runtime/RuntimeBarLayout.js.map +1 -0
  387. package/dist/data/runtime/RuntimeBarSegment.cjs +25 -14
  388. package/dist/data/runtime/RuntimeBarSegment.cjs.map +1 -1
  389. package/dist/data/runtime/RuntimeBarSegment.d.ts +4 -0
  390. package/dist/data/runtime/RuntimeBarSegment.d.ts.map +1 -1
  391. package/dist/data/runtime/RuntimeBarSegment.js +25 -14
  392. package/dist/data/runtime/RuntimeBarSegment.js.map +1 -1
  393. package/dist/data/runtime/RuntimeLimitInput.cjs +41 -0
  394. package/dist/data/runtime/RuntimeLimitInput.cjs.map +1 -0
  395. package/dist/data/runtime/RuntimeLimitInput.d.ts +9 -0
  396. package/dist/data/runtime/RuntimeLimitInput.d.ts.map +1 -0
  397. package/dist/data/runtime/RuntimeLimitInput.js +41 -0
  398. package/dist/data/runtime/RuntimeLimitInput.js.map +1 -0
  399. package/dist/data/runtime/use-runtime-bar-overflow.cjs +70 -0
  400. package/dist/data/runtime/use-runtime-bar-overflow.cjs.map +1 -0
  401. package/dist/data/runtime/use-runtime-bar-overflow.d.ts +13 -0
  402. package/dist/data/runtime/use-runtime-bar-overflow.d.ts.map +1 -0
  403. package/dist/data/runtime/use-runtime-bar-overflow.js +70 -0
  404. package/dist/data/runtime/use-runtime-bar-overflow.js.map +1 -0
  405. package/dist/data/use-edge-focus.cjs +20 -0
  406. package/dist/data/use-edge-focus.cjs.map +1 -0
  407. package/dist/data/use-edge-focus.d.ts +23 -0
  408. package/dist/data/use-edge-focus.d.ts.map +1 -0
  409. package/dist/data/use-edge-focus.js +20 -0
  410. package/dist/data/use-edge-focus.js.map +1 -0
  411. package/dist/data/use-pan-zoom.cjs +159 -0
  412. package/dist/data/use-pan-zoom.cjs.map +1 -0
  413. package/dist/data/use-pan-zoom.d.ts +79 -0
  414. package/dist/data/use-pan-zoom.d.ts.map +1 -0
  415. package/dist/data/use-pan-zoom.js +159 -0
  416. package/dist/data/use-pan-zoom.js.map +1 -0
  417. package/dist/data/version-info.cjs +3 -3
  418. package/dist/data/version-info.js +3 -3
  419. package/dist/data.cjs +5 -1
  420. package/dist/data.cjs.map +1 -1
  421. package/dist/data.d.ts +4 -3
  422. package/dist/data.d.ts.map +1 -1
  423. package/dist/data.js +6 -2
  424. package/dist/data.js.map +1 -1
  425. package/dist/index.cjs +5 -1
  426. package/dist/index.cjs.map +1 -1
  427. package/dist/index.js +6 -2
  428. package/dist/index.js.map +1 -1
  429. package/dist/overlay/DropdownMenu.cjs +1 -0
  430. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  431. package/dist/overlay/DropdownMenu.d.ts +4 -0
  432. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  433. package/dist/overlay/DropdownMenu.js +1 -0
  434. package/dist/overlay/DropdownMenu.js.map +1 -1
  435. package/dist/overlay/DropdownMenuSubmenu.cjs +31 -15
  436. package/dist/overlay/DropdownMenuSubmenu.cjs.map +1 -1
  437. package/dist/overlay/DropdownMenuSubmenu.d.ts.map +1 -1
  438. package/dist/overlay/DropdownMenuSubmenu.js +31 -15
  439. package/dist/overlay/DropdownMenuSubmenu.js.map +1 -1
  440. package/dist/profiles/fields/profileFieldFilterEditor.cjs +5 -2
  441. package/dist/profiles/fields/profileFieldFilterEditor.cjs.map +1 -1
  442. package/dist/profiles/fields/profileFieldFilterEditor.js +5 -2
  443. package/dist/profiles/fields/profileFieldFilterEditor.js.map +1 -1
  444. package/dist/profiles/fields/profileFieldIcons.cjs +2 -0
  445. package/dist/profiles/fields/profileFieldIcons.cjs.map +1 -1
  446. package/dist/profiles/fields/profileFieldIcons.d.ts.map +1 -1
  447. package/dist/profiles/fields/profileFieldIcons.js +2 -0
  448. package/dist/profiles/fields/profileFieldIcons.js.map +1 -1
  449. package/dist/profiles/wizard/profileWizardModel.cjs +1 -0
  450. package/dist/profiles/wizard/profileWizardModel.cjs.map +1 -1
  451. package/dist/profiles/wizard/profileWizardModel.d.ts +3 -0
  452. package/dist/profiles/wizard/profileWizardModel.d.ts.map +1 -1
  453. package/dist/profiles/wizard/profileWizardModel.js +1 -0
  454. package/dist/profiles/wizard/profileWizardModel.js.map +1 -1
  455. package/dist/rpc/OperationCatalog.cjs +3 -1
  456. package/dist/rpc/OperationCatalog.cjs.map +1 -1
  457. package/dist/rpc/OperationCatalog.d.ts +3 -1
  458. package/dist/rpc/OperationCatalog.d.ts.map +1 -1
  459. package/dist/rpc/OperationCatalog.js +3 -1
  460. package/dist/rpc/OperationCatalog.js.map +1 -1
  461. package/dist/rpc/useOperationPages.cjs +6 -1
  462. package/dist/rpc/useOperationPages.cjs.map +1 -1
  463. package/dist/rpc/useOperationPages.d.ts +3 -1
  464. package/dist/rpc/useOperationPages.d.ts.map +1 -1
  465. package/dist/rpc/useOperationPages.js +6 -1
  466. package/dist/rpc/useOperationPages.js.map +1 -1
  467. package/dist/styles.css +1 -1
  468. package/package.json +1 -1
  469. package/schemas/json-schema-form.schema.json +5 -0
  470. package/dist/data/runtime/RuntimeBarCombo.cjs +0 -423
  471. package/dist/data/runtime/RuntimeBarCombo.cjs.map +0 -1
  472. package/dist/data/runtime/RuntimeBarCombo.d.ts +0 -33
  473. package/dist/data/runtime/RuntimeBarCombo.d.ts.map +0 -1
  474. package/dist/data/runtime/RuntimeBarCombo.js +0 -423
  475. package/dist/data/runtime/RuntimeBarCombo.js.map +0 -1
  476. package/dist/data/runtime/RuntimeBarLimits.cjs +0 -262
  477. package/dist/data/runtime/RuntimeBarLimits.cjs.map +0 -1
  478. package/dist/data/runtime/RuntimeBarLimits.d.ts +0 -17
  479. package/dist/data/runtime/RuntimeBarLimits.d.ts.map +0 -1
  480. package/dist/data/runtime/RuntimeBarLimits.js +0 -262
  481. package/dist/data/runtime/RuntimeBarLimits.js.map +0 -1
  482. package/dist/data/runtime/RuntimeBarSegments.cjs +0 -346
  483. package/dist/data/runtime/RuntimeBarSegments.cjs.map +0 -1
  484. package/dist/data/runtime/RuntimeBarSegments.d.ts +0 -37
  485. package/dist/data/runtime/RuntimeBarSegments.d.ts.map +0 -1
  486. package/dist/data/runtime/RuntimeBarSegments.js +0 -346
  487. package/dist/data/runtime/RuntimeBarSegments.js.map +0 -1
@@ -42,87 +42,5 @@ function ringLayout(nodes, options = {}) {
42
42
  positions
43
43
  };
44
44
  }
45
- const CURVE_OFFSET = 22;
46
- function unorderedPairKey(a, b) {
47
- return a < b ? `${a}\0${b}` : `${b}\0${a}`;
48
- }
49
- function requirePosition(positions, id) {
50
- const position = positions[id];
51
- if (!position) {
52
- throw new Error(`routeEdges: no layout position for node "${id}"`);
53
- }
54
- return position;
55
- }
56
- function requireEdge(group, index) {
57
- const edge = group[index];
58
- if (!edge) {
59
- throw new Error(`routeEdges: expected an edge at index ${index}`);
60
- }
61
- return edge;
62
- }
63
- function clipToRect(center, towards, halfW, halfH) {
64
- const dx = towards.x - center.x;
65
- const dy = towards.y - center.y;
66
- if (dx === 0 && dy === 0) return { x: center.x, y: center.y };
67
- const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Number.POSITIVE_INFINITY;
68
- const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Number.POSITIVE_INFINITY;
69
- const scale = Math.min(scaleX, scaleY);
70
- return { x: center.x + dx * scale, y: center.y + dy * scale };
71
- }
72
- function quadraticMidpoint(p0, p1, p2) {
73
- return {
74
- x: 0.25 * p0.x + 0.5 * p1.x + 0.25 * p2.x,
75
- y: 0.25 * p0.y + 0.5 * p1.y + 0.25 * p2.y
76
- };
77
- }
78
- function routeEdges(edges, positions, dimensions) {
79
- const halfW = dimensions.nodeWidth / 2;
80
- const halfH = dimensions.nodeHeight / 2;
81
- const groups = /* @__PURE__ */ new Map();
82
- for (const edge of edges) {
83
- if (!(edge.from in positions)) {
84
- throw new Error(`routeEdges: edge "${edge.id}" references unknown node "${edge.from}"`);
85
- }
86
- if (!(edge.to in positions)) {
87
- throw new Error(`routeEdges: edge "${edge.id}" references unknown node "${edge.to}"`);
88
- }
89
- const key = unorderedPairKey(edge.from, edge.to);
90
- const group = groups.get(key);
91
- if (group) {
92
- group.push(edge);
93
- } else {
94
- groups.set(key, [edge]);
95
- }
96
- }
97
- const routes = {};
98
- for (const group of groups.values()) {
99
- const first = requireEdge(group, 0);
100
- const posA = requirePosition(positions, first.from);
101
- const posB = requirePosition(positions, first.to);
102
- const dx = posB.x - posA.x;
103
- const dy = posB.y - posA.y;
104
- const len = Math.hypot(dx, dy) || 1;
105
- const perpX = -dy / len;
106
- const perpY = dx / len;
107
- const midX = (posA.x + posB.x) / 2;
108
- const midY = (posA.y + posB.y) / 2;
109
- group.forEach((edge, index) => {
110
- const isCurved = group.length > 1;
111
- const sign = index % 2 === 0 ? 1 : -1;
112
- const magnitude = Math.floor(index / 2) + 1;
113
- const offset = isCurved ? sign * magnitude * CURVE_OFFSET : 0;
114
- const control = { x: midX + perpX * offset, y: midY + perpY * offset };
115
- const fromCenter = requirePosition(positions, edge.from);
116
- const toCenter = requirePosition(positions, edge.to);
117
- const start = clipToRect(fromCenter, control, halfW, halfH);
118
- const end = clipToRect(toCenter, control, halfW, halfH);
119
- const path = isCurved ? `M ${start.x} ${start.y} Q ${control.x} ${control.y} ${end.x} ${end.y}` : `M ${start.x} ${start.y} L ${end.x} ${end.y}`;
120
- const label = isCurved ? quadraticMidpoint(start, control, end) : { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
121
- routes[edge.id] = { path, labelX: label.x, labelY: label.y };
122
- });
123
- }
124
- return routes;
125
- }
126
45
  exports.ringLayout = ringLayout;
127
- exports.routeEdges = routeEdges;
128
46
  //# sourceMappingURL=graph-layout.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-layout.cjs","sources":["../../src/data/graph-layout.ts"],"sourcesContent":["// Pure, framework-free layout and edge-routing math for GraphDiagram. Kept\n// free of React so it can be unit tested without rendering, and so a future\n// \"columns\" layout can live alongside `ringLayout` without dragging in DOM\n// concerns.\n\nexport interface GraphLayoutNode {\n id: string;\n}\n\nexport interface RingLayoutOptions {\n /** Node bounding-box width in px. Defaults to 168. */\n nodeWidth?: number;\n /** Node bounding-box height in px. Defaults to 60. */\n nodeHeight?: number;\n /** Minimum gap between adjacent node edges in px. Defaults to 40. */\n gap?: number;\n /** Space reserved around the ring before the viewport edge. Defaults to 24. */\n padding?: number;\n}\n\nexport interface GraphLayoutPosition {\n /** Node centre x, in the same coordinate space as `width`/`height`. */\n x: number;\n /** Node centre y. */\n y: number;\n}\n\nexport interface GraphLayoutResult {\n width: number;\n height: number;\n positions: Record<string, GraphLayoutPosition>;\n}\n\nconst DEFAULT_NODE_WIDTH = 168;\nconst DEFAULT_NODE_HEIGHT = 60;\nconst DEFAULT_GAP = 40;\nconst DEFAULT_PADDING = 24;\nconst MIN_RADIUS = 96;\n\nfunction requireNode<T extends GraphLayoutNode>(nodes: readonly T[], index: number): T {\n const node = nodes[index];\n if (!node) {\n throw new Error(`ringLayout: expected a node at index ${index}`);\n }\n return node;\n}\n\n/**\n * Places nodes on a circle in the given order, starting at 12 o'clock and\n * running clockwise. `n=1` is centred (radius 0); `n=2` is a special case —\n * left/right — because 180° clockwise from 12 o'clock lands at 6 o'clock,\n * which reads poorly for a two-node relationship.\n *\n * The radius is sized so the straight-line (chord) distance between any two\n * angularly-adjacent nodes is at least `nodeWidth + gap`, with a sensible\n * floor (`MIN_RADIUS`) for small rings.\n */\nexport function ringLayout(\n nodes: readonly GraphLayoutNode[],\n options: RingLayoutOptions = {},\n): GraphLayoutResult {\n const nodeWidth = options.nodeWidth ?? DEFAULT_NODE_WIDTH;\n const nodeHeight = options.nodeHeight ?? DEFAULT_NODE_HEIGHT;\n const gap = options.gap ?? DEFAULT_GAP;\n const padding = options.padding ?? DEFAULT_PADDING;\n const n = nodes.length;\n\n const radius = n <= 1 ? 0 : Math.max(MIN_RADIUS, (nodeWidth + gap) / (2 * Math.sin(Math.PI / n)));\n const cx = radius + nodeWidth / 2 + padding;\n const cy = radius + nodeHeight / 2 + padding;\n\n const positions: Record<string, GraphLayoutPosition> = {};\n\n if (n === 1) {\n positions[requireNode(nodes, 0).id] = { x: cx, y: cy };\n } else if (n === 2) {\n positions[requireNode(nodes, 0).id] = { x: cx - radius, y: cy };\n positions[requireNode(nodes, 1).id] = { x: cx + radius, y: cy };\n } else {\n nodes.forEach((node, i) => {\n const theta = -Math.PI / 2 + i * ((2 * Math.PI) / n);\n positions[node.id] = {\n x: cx + radius * Math.cos(theta),\n y: cy + radius * Math.sin(theta),\n };\n });\n }\n\n return {\n width: 2 * cx,\n height: 2 * cy,\n positions,\n };\n}\n\nexport interface GraphLayoutEdge {\n id: string;\n from: string;\n to: string;\n}\n\nexport interface EdgeRoute {\n /** SVG path `d` attribute, clipped to the node rectangle boundaries. */\n path: string;\n /** Midpoint of the rendered path, for placing an edge label. */\n labelX: number;\n labelY: number;\n}\n\nexport interface RouteEdgesDimensions {\n nodeWidth: number;\n nodeHeight: number;\n}\n\nconst CURVE_OFFSET = 22;\n\nfunction unorderedPairKey(a: string, b: string): string {\n return a < b ? `${a}\u0000${b}` : `${b}\u0000${a}`;\n}\n\nfunction requirePosition(\n positions: Record<string, GraphLayoutPosition>,\n id: string,\n): GraphLayoutPosition {\n const position = positions[id];\n if (!position) {\n throw new Error(`routeEdges: no layout position for node \"${id}\"`);\n }\n return position;\n}\n\nfunction requireEdge(group: readonly GraphLayoutEdge[], index: number): GraphLayoutEdge {\n const edge = group[index];\n if (!edge) {\n throw new Error(`routeEdges: expected an edge at index ${index}`);\n }\n return edge;\n}\n\n/** Point on the rectangle (centred at `center`, half-extents `halfW`/`halfH`) where the ray toward `towards` exits. */\nfunction clipToRect(\n center: GraphLayoutPosition,\n towards: GraphLayoutPosition,\n halfW: number,\n halfH: number,\n): GraphLayoutPosition {\n const dx = towards.x - center.x;\n const dy = towards.y - center.y;\n if (dx === 0 && dy === 0) return { x: center.x, y: center.y };\n\n const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Number.POSITIVE_INFINITY;\n const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Number.POSITIVE_INFINITY;\n const scale = Math.min(scaleX, scaleY);\n\n return { x: center.x + dx * scale, y: center.y + dy * scale };\n}\n\n/** Point at t=0.5 on the quadratic Bézier defined by p0 (start), p1 (control), p2 (end). */\nfunction quadraticMidpoint(\n p0: GraphLayoutPosition,\n p1: GraphLayoutPosition,\n p2: GraphLayoutPosition,\n): GraphLayoutPosition {\n return {\n x: 0.25 * p0.x + 0.5 * p1.x + 0.25 * p2.x,\n y: 0.25 * p0.y + 0.5 * p1.y + 0.25 * p2.y,\n };\n}\n\n/**\n * Routes each edge as an SVG path clipped to the source/target node\n * rectangles. Edges sharing an unordered node pair — a reciprocal A→B + B→A,\n * or literal duplicates — are fanned out as quadratic curves with\n * alternating, increasing offsets from the straight line between the two\n * node centres, so they never overlap. A lone edge between a pair is a\n * straight line.\n *\n * Throws when an edge references a node id absent from `positions`, naming\n * the edge id, rather than silently dropping or mis-routing it.\n */\nexport function routeEdges(\n edges: readonly GraphLayoutEdge[],\n positions: Record<string, GraphLayoutPosition>,\n dimensions: RouteEdgesDimensions,\n): Record<string, EdgeRoute> {\n const halfW = dimensions.nodeWidth / 2;\n const halfH = dimensions.nodeHeight / 2;\n\n const groups = new Map<string, GraphLayoutEdge[]>();\n for (const edge of edges) {\n if (!(edge.from in positions)) {\n throw new Error(`routeEdges: edge \"${edge.id}\" references unknown node \"${edge.from}\"`);\n }\n if (!(edge.to in positions)) {\n throw new Error(`routeEdges: edge \"${edge.id}\" references unknown node \"${edge.to}\"`);\n }\n const key = unorderedPairKey(edge.from, edge.to);\n const group = groups.get(key);\n if (group) {\n group.push(edge);\n } else {\n groups.set(key, [edge]);\n }\n }\n\n const routes: Record<string, EdgeRoute> = {};\n\n for (const group of groups.values()) {\n const first = requireEdge(group, 0);\n const posA = requirePosition(positions, first.from);\n const posB = requirePosition(positions, first.to);\n const dx = posB.x - posA.x;\n const dy = posB.y - posA.y;\n const len = Math.hypot(dx, dy) || 1;\n const perpX = -dy / len;\n const perpY = dx / len;\n const midX = (posA.x + posB.x) / 2;\n const midY = (posA.y + posB.y) / 2;\n\n group.forEach((edge, index) => {\n const isCurved = group.length > 1;\n const sign = index % 2 === 0 ? 1 : -1;\n const magnitude = Math.floor(index / 2) + 1;\n const offset = isCurved ? sign * magnitude * CURVE_OFFSET : 0;\n\n const control = { x: midX + perpX * offset, y: midY + perpY * offset };\n const fromCenter = requirePosition(positions, edge.from);\n const toCenter = requirePosition(positions, edge.to);\n const start = clipToRect(fromCenter, control, halfW, halfH);\n const end = clipToRect(toCenter, control, halfW, halfH);\n\n const path = isCurved\n ? `M ${start.x} ${start.y} Q ${control.x} ${control.y} ${end.x} ${end.y}`\n : `M ${start.x} ${start.y} L ${end.x} ${end.y}`;\n\n const label = isCurved\n ? quadraticMidpoint(start, control, end)\n : { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };\n\n routes[edge.id] = { path, labelX: label.x, labelY: label.y };\n });\n }\n\n return routes;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"graph-layout.cjs","sources":["../../src/data/graph-layout.ts"],"sourcesContent":["// Pure, framework-free layout math for GraphDiagram. Kept free of React so it\n// can be unit tested without rendering. `ringLayout` places nodes on a circle;\n// the level-column layout lives in graph-columns-layout.ts, and the edge\n// routing and label placement in graph-edge-routing.ts, re-exported here.\n\nexport {\n routeEdges,\n spreadLabels,\n type EdgeLabelBox,\n type EdgeRoute,\n type GraphLayoutEdge,\n type RouteEdgesDimensions,\n type RouteEdgesOptions,\n} from \"./graph-edge-routing\";\n\nexport interface GraphLayoutNode {\n id: string;\n}\n\nexport interface RingLayoutOptions {\n /** Node bounding-box width in px. Defaults to 168. */\n nodeWidth?: number;\n /** Node bounding-box height in px. Defaults to 60. */\n nodeHeight?: number;\n /** Minimum gap between adjacent node edges in px. Defaults to 40. */\n gap?: number;\n /** Space reserved around the ring before the viewport edge. Defaults to 24. */\n padding?: number;\n}\n\nexport interface GraphLayoutPosition {\n /** Node centre x, in the same coordinate space as `width`/`height`. */\n x: number;\n /** Node centre y. */\n y: number;\n}\n\nexport interface GraphLayoutResult {\n width: number;\n height: number;\n positions: Record<string, GraphLayoutPosition>;\n}\n\nconst DEFAULT_NODE_WIDTH = 168;\nconst DEFAULT_NODE_HEIGHT = 60;\nconst DEFAULT_GAP = 40;\nconst DEFAULT_PADDING = 24;\nconst MIN_RADIUS = 96;\n\nfunction requireNode<T extends GraphLayoutNode>(nodes: readonly T[], index: number): T {\n const node = nodes[index];\n if (!node) {\n throw new Error(`ringLayout: expected a node at index ${index}`);\n }\n return node;\n}\n\n/**\n * Places nodes on a circle in the given order, starting at 12 o'clock and\n * running clockwise. `n=1` is centred (radius 0); `n=2` is a special case —\n * left/right — because 180° clockwise from 12 o'clock lands at 6 o'clock,\n * which reads poorly for a two-node relationship.\n *\n * The radius is sized so the straight-line (chord) distance between any two\n * angularly-adjacent nodes is at least `nodeWidth + gap`, with a sensible\n * floor (`MIN_RADIUS`) for small rings.\n */\nexport function ringLayout(\n nodes: readonly GraphLayoutNode[],\n options: RingLayoutOptions = {},\n): GraphLayoutResult {\n const nodeWidth = options.nodeWidth ?? DEFAULT_NODE_WIDTH;\n const nodeHeight = options.nodeHeight ?? DEFAULT_NODE_HEIGHT;\n const gap = options.gap ?? DEFAULT_GAP;\n const padding = options.padding ?? DEFAULT_PADDING;\n const n = nodes.length;\n\n const radius = n <= 1 ? 0 : Math.max(MIN_RADIUS, (nodeWidth + gap) / (2 * Math.sin(Math.PI / n)));\n const cx = radius + nodeWidth / 2 + padding;\n const cy = radius + nodeHeight / 2 + padding;\n\n const positions: Record<string, GraphLayoutPosition> = {};\n\n if (n === 1) {\n positions[requireNode(nodes, 0).id] = { x: cx, y: cy };\n } else if (n === 2) {\n positions[requireNode(nodes, 0).id] = { x: cx - radius, y: cy };\n positions[requireNode(nodes, 1).id] = { x: cx + radius, y: cy };\n } else {\n nodes.forEach((node, i) => {\n const theta = -Math.PI / 2 + i * ((2 * Math.PI) / n);\n positions[node.id] = {\n x: cx + radius * Math.cos(theta),\n y: cy + radius * Math.sin(theta),\n };\n });\n }\n\n return {\n width: 2 * cx,\n height: 2 * cy,\n positions,\n };\n}\n"],"names":[],"mappings":";;AA2CA,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,aAAa;AAEnB,SAAS,YAAuC,OAAqB,OAAkB;AACrF,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,wCAAwC,KAAK,EAAE;AAAA,EACjE;AACA,SAAO;AACT;AAYO,SAAS,WACd,OACA,UAA6B,IACV;AACnB,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,IAAI,MAAM;AAEhB,QAAM,SAAS,KAAK,IAAI,IAAI,KAAK,IAAI,aAAa,YAAY,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,EAAE;AAChG,QAAM,KAAK,SAAS,YAAY,IAAI;AACpC,QAAM,KAAK,SAAS,aAAa,IAAI;AAErC,QAAM,YAAiD,CAAA;AAEvD,MAAI,MAAM,GAAG;AACX,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,GAAA;AAAA,EACpD,WAAW,MAAM,GAAG;AAClB,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,GAAG,GAAA;AAC3D,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,GAAG,GAAA;AAAA,EAC7D,OAAO;AACL,UAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,YAAM,QAAQ,CAAC,KAAK,KAAK,IAAI,KAAM,IAAI,KAAK,KAAM;AAClD,gBAAU,KAAK,EAAE,IAAI;AAAA,QACnB,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,QAC/B,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,MAAA;AAAA,IAEnC,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI;AAAA,IACZ;AAAA,EAAA;AAEJ;;"}
@@ -1,3 +1,4 @@
1
+ export { routeEdges, spreadLabels, type EdgeLabelBox, type EdgeRoute, type GraphLayoutEdge, type RouteEdgesDimensions, type RouteEdgesOptions, } from './graph-edge-routing.js';
1
2
  export interface GraphLayoutNode {
2
3
  id: string;
3
4
  }
@@ -33,32 +34,4 @@ export interface GraphLayoutResult {
33
34
  * floor (`MIN_RADIUS`) for small rings.
34
35
  */
35
36
  export declare function ringLayout(nodes: readonly GraphLayoutNode[], options?: RingLayoutOptions): GraphLayoutResult;
36
- export interface GraphLayoutEdge {
37
- id: string;
38
- from: string;
39
- to: string;
40
- }
41
- export interface EdgeRoute {
42
- /** SVG path `d` attribute, clipped to the node rectangle boundaries. */
43
- path: string;
44
- /** Midpoint of the rendered path, for placing an edge label. */
45
- labelX: number;
46
- labelY: number;
47
- }
48
- export interface RouteEdgesDimensions {
49
- nodeWidth: number;
50
- nodeHeight: number;
51
- }
52
- /**
53
- * Routes each edge as an SVG path clipped to the source/target node
54
- * rectangles. Edges sharing an unordered node pair — a reciprocal A→B + B→A,
55
- * or literal duplicates — are fanned out as quadratic curves with
56
- * alternating, increasing offsets from the straight line between the two
57
- * node centres, so they never overlap. A lone edge between a pair is a
58
- * straight line.
59
- *
60
- * Throws when an edge references a node id absent from `positions`, naming
61
- * the edge id, rather than silently dropping or mis-routing it.
62
- */
63
- export declare function routeEdges(edges: readonly GraphLayoutEdge[], positions: Record<string, GraphLayoutPosition>, dimensions: RouteEdgesDimensions): Record<string, EdgeRoute>;
64
37
  //# sourceMappingURL=graph-layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-layout.d.ts","sourceRoot":"","sources":["../../src/data/graph-layout.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,CAAC,EAAE,MAAM,CAAC;IACV,qBAAqB;IACrB,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAChD;AAgBD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,OAAO,GAAE,iBAAsB,GAC9B,iBAAiB,CAiCnB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,SAAS;IACxB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAyDD;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC9C,UAAU,EAAE,oBAAoB,GAC/B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CA4D3B"}
1
+ {"version":3,"file":"graph-layout.d.ts","sourceRoot":"","sources":["../../src/data/graph-layout.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,UAAU,EACV,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,CAAC,EAAE,MAAM,CAAC;IACV,qBAAqB;IACrB,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAChD;AAgBD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,OAAO,GAAE,iBAAsB,GAC9B,iBAAiB,CAiCnB"}
@@ -40,89 +40,7 @@ function ringLayout(nodes, options = {}) {
40
40
  positions
41
41
  };
42
42
  }
43
- const CURVE_OFFSET = 22;
44
- function unorderedPairKey(a, b) {
45
- return a < b ? `${a}\0${b}` : `${b}\0${a}`;
46
- }
47
- function requirePosition(positions, id) {
48
- const position = positions[id];
49
- if (!position) {
50
- throw new Error(`routeEdges: no layout position for node "${id}"`);
51
- }
52
- return position;
53
- }
54
- function requireEdge(group, index) {
55
- const edge = group[index];
56
- if (!edge) {
57
- throw new Error(`routeEdges: expected an edge at index ${index}`);
58
- }
59
- return edge;
60
- }
61
- function clipToRect(center, towards, halfW, halfH) {
62
- const dx = towards.x - center.x;
63
- const dy = towards.y - center.y;
64
- if (dx === 0 && dy === 0) return { x: center.x, y: center.y };
65
- const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Number.POSITIVE_INFINITY;
66
- const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Number.POSITIVE_INFINITY;
67
- const scale = Math.min(scaleX, scaleY);
68
- return { x: center.x + dx * scale, y: center.y + dy * scale };
69
- }
70
- function quadraticMidpoint(p0, p1, p2) {
71
- return {
72
- x: 0.25 * p0.x + 0.5 * p1.x + 0.25 * p2.x,
73
- y: 0.25 * p0.y + 0.5 * p1.y + 0.25 * p2.y
74
- };
75
- }
76
- function routeEdges(edges, positions, dimensions) {
77
- const halfW = dimensions.nodeWidth / 2;
78
- const halfH = dimensions.nodeHeight / 2;
79
- const groups = /* @__PURE__ */ new Map();
80
- for (const edge of edges) {
81
- if (!(edge.from in positions)) {
82
- throw new Error(`routeEdges: edge "${edge.id}" references unknown node "${edge.from}"`);
83
- }
84
- if (!(edge.to in positions)) {
85
- throw new Error(`routeEdges: edge "${edge.id}" references unknown node "${edge.to}"`);
86
- }
87
- const key = unorderedPairKey(edge.from, edge.to);
88
- const group = groups.get(key);
89
- if (group) {
90
- group.push(edge);
91
- } else {
92
- groups.set(key, [edge]);
93
- }
94
- }
95
- const routes = {};
96
- for (const group of groups.values()) {
97
- const first = requireEdge(group, 0);
98
- const posA = requirePosition(positions, first.from);
99
- const posB = requirePosition(positions, first.to);
100
- const dx = posB.x - posA.x;
101
- const dy = posB.y - posA.y;
102
- const len = Math.hypot(dx, dy) || 1;
103
- const perpX = -dy / len;
104
- const perpY = dx / len;
105
- const midX = (posA.x + posB.x) / 2;
106
- const midY = (posA.y + posB.y) / 2;
107
- group.forEach((edge, index) => {
108
- const isCurved = group.length > 1;
109
- const sign = index % 2 === 0 ? 1 : -1;
110
- const magnitude = Math.floor(index / 2) + 1;
111
- const offset = isCurved ? sign * magnitude * CURVE_OFFSET : 0;
112
- const control = { x: midX + perpX * offset, y: midY + perpY * offset };
113
- const fromCenter = requirePosition(positions, edge.from);
114
- const toCenter = requirePosition(positions, edge.to);
115
- const start = clipToRect(fromCenter, control, halfW, halfH);
116
- const end = clipToRect(toCenter, control, halfW, halfH);
117
- const path = isCurved ? `M ${start.x} ${start.y} Q ${control.x} ${control.y} ${end.x} ${end.y}` : `M ${start.x} ${start.y} L ${end.x} ${end.y}`;
118
- const label = isCurved ? quadraticMidpoint(start, control, end) : { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
119
- routes[edge.id] = { path, labelX: label.x, labelY: label.y };
120
- });
121
- }
122
- return routes;
123
- }
124
43
  export {
125
- ringLayout,
126
- routeEdges
44
+ ringLayout
127
45
  };
128
46
  //# sourceMappingURL=graph-layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-layout.js","sources":["../../src/data/graph-layout.ts"],"sourcesContent":["// Pure, framework-free layout and edge-routing math for GraphDiagram. Kept\n// free of React so it can be unit tested without rendering, and so a future\n// \"columns\" layout can live alongside `ringLayout` without dragging in DOM\n// concerns.\n\nexport interface GraphLayoutNode {\n id: string;\n}\n\nexport interface RingLayoutOptions {\n /** Node bounding-box width in px. Defaults to 168. */\n nodeWidth?: number;\n /** Node bounding-box height in px. Defaults to 60. */\n nodeHeight?: number;\n /** Minimum gap between adjacent node edges in px. Defaults to 40. */\n gap?: number;\n /** Space reserved around the ring before the viewport edge. Defaults to 24. */\n padding?: number;\n}\n\nexport interface GraphLayoutPosition {\n /** Node centre x, in the same coordinate space as `width`/`height`. */\n x: number;\n /** Node centre y. */\n y: number;\n}\n\nexport interface GraphLayoutResult {\n width: number;\n height: number;\n positions: Record<string, GraphLayoutPosition>;\n}\n\nconst DEFAULT_NODE_WIDTH = 168;\nconst DEFAULT_NODE_HEIGHT = 60;\nconst DEFAULT_GAP = 40;\nconst DEFAULT_PADDING = 24;\nconst MIN_RADIUS = 96;\n\nfunction requireNode<T extends GraphLayoutNode>(nodes: readonly T[], index: number): T {\n const node = nodes[index];\n if (!node) {\n throw new Error(`ringLayout: expected a node at index ${index}`);\n }\n return node;\n}\n\n/**\n * Places nodes on a circle in the given order, starting at 12 o'clock and\n * running clockwise. `n=1` is centred (radius 0); `n=2` is a special case —\n * left/right — because 180° clockwise from 12 o'clock lands at 6 o'clock,\n * which reads poorly for a two-node relationship.\n *\n * The radius is sized so the straight-line (chord) distance between any two\n * angularly-adjacent nodes is at least `nodeWidth + gap`, with a sensible\n * floor (`MIN_RADIUS`) for small rings.\n */\nexport function ringLayout(\n nodes: readonly GraphLayoutNode[],\n options: RingLayoutOptions = {},\n): GraphLayoutResult {\n const nodeWidth = options.nodeWidth ?? DEFAULT_NODE_WIDTH;\n const nodeHeight = options.nodeHeight ?? DEFAULT_NODE_HEIGHT;\n const gap = options.gap ?? DEFAULT_GAP;\n const padding = options.padding ?? DEFAULT_PADDING;\n const n = nodes.length;\n\n const radius = n <= 1 ? 0 : Math.max(MIN_RADIUS, (nodeWidth + gap) / (2 * Math.sin(Math.PI / n)));\n const cx = radius + nodeWidth / 2 + padding;\n const cy = radius + nodeHeight / 2 + padding;\n\n const positions: Record<string, GraphLayoutPosition> = {};\n\n if (n === 1) {\n positions[requireNode(nodes, 0).id] = { x: cx, y: cy };\n } else if (n === 2) {\n positions[requireNode(nodes, 0).id] = { x: cx - radius, y: cy };\n positions[requireNode(nodes, 1).id] = { x: cx + radius, y: cy };\n } else {\n nodes.forEach((node, i) => {\n const theta = -Math.PI / 2 + i * ((2 * Math.PI) / n);\n positions[node.id] = {\n x: cx + radius * Math.cos(theta),\n y: cy + radius * Math.sin(theta),\n };\n });\n }\n\n return {\n width: 2 * cx,\n height: 2 * cy,\n positions,\n };\n}\n\nexport interface GraphLayoutEdge {\n id: string;\n from: string;\n to: string;\n}\n\nexport interface EdgeRoute {\n /** SVG path `d` attribute, clipped to the node rectangle boundaries. */\n path: string;\n /** Midpoint of the rendered path, for placing an edge label. */\n labelX: number;\n labelY: number;\n}\n\nexport interface RouteEdgesDimensions {\n nodeWidth: number;\n nodeHeight: number;\n}\n\nconst CURVE_OFFSET = 22;\n\nfunction unorderedPairKey(a: string, b: string): string {\n return a < b ? `${a}\u0000${b}` : `${b}\u0000${a}`;\n}\n\nfunction requirePosition(\n positions: Record<string, GraphLayoutPosition>,\n id: string,\n): GraphLayoutPosition {\n const position = positions[id];\n if (!position) {\n throw new Error(`routeEdges: no layout position for node \"${id}\"`);\n }\n return position;\n}\n\nfunction requireEdge(group: readonly GraphLayoutEdge[], index: number): GraphLayoutEdge {\n const edge = group[index];\n if (!edge) {\n throw new Error(`routeEdges: expected an edge at index ${index}`);\n }\n return edge;\n}\n\n/** Point on the rectangle (centred at `center`, half-extents `halfW`/`halfH`) where the ray toward `towards` exits. */\nfunction clipToRect(\n center: GraphLayoutPosition,\n towards: GraphLayoutPosition,\n halfW: number,\n halfH: number,\n): GraphLayoutPosition {\n const dx = towards.x - center.x;\n const dy = towards.y - center.y;\n if (dx === 0 && dy === 0) return { x: center.x, y: center.y };\n\n const scaleX = dx !== 0 ? halfW / Math.abs(dx) : Number.POSITIVE_INFINITY;\n const scaleY = dy !== 0 ? halfH / Math.abs(dy) : Number.POSITIVE_INFINITY;\n const scale = Math.min(scaleX, scaleY);\n\n return { x: center.x + dx * scale, y: center.y + dy * scale };\n}\n\n/** Point at t=0.5 on the quadratic Bézier defined by p0 (start), p1 (control), p2 (end). */\nfunction quadraticMidpoint(\n p0: GraphLayoutPosition,\n p1: GraphLayoutPosition,\n p2: GraphLayoutPosition,\n): GraphLayoutPosition {\n return {\n x: 0.25 * p0.x + 0.5 * p1.x + 0.25 * p2.x,\n y: 0.25 * p0.y + 0.5 * p1.y + 0.25 * p2.y,\n };\n}\n\n/**\n * Routes each edge as an SVG path clipped to the source/target node\n * rectangles. Edges sharing an unordered node pair — a reciprocal A→B + B→A,\n * or literal duplicates — are fanned out as quadratic curves with\n * alternating, increasing offsets from the straight line between the two\n * node centres, so they never overlap. A lone edge between a pair is a\n * straight line.\n *\n * Throws when an edge references a node id absent from `positions`, naming\n * the edge id, rather than silently dropping or mis-routing it.\n */\nexport function routeEdges(\n edges: readonly GraphLayoutEdge[],\n positions: Record<string, GraphLayoutPosition>,\n dimensions: RouteEdgesDimensions,\n): Record<string, EdgeRoute> {\n const halfW = dimensions.nodeWidth / 2;\n const halfH = dimensions.nodeHeight / 2;\n\n const groups = new Map<string, GraphLayoutEdge[]>();\n for (const edge of edges) {\n if (!(edge.from in positions)) {\n throw new Error(`routeEdges: edge \"${edge.id}\" references unknown node \"${edge.from}\"`);\n }\n if (!(edge.to in positions)) {\n throw new Error(`routeEdges: edge \"${edge.id}\" references unknown node \"${edge.to}\"`);\n }\n const key = unorderedPairKey(edge.from, edge.to);\n const group = groups.get(key);\n if (group) {\n group.push(edge);\n } else {\n groups.set(key, [edge]);\n }\n }\n\n const routes: Record<string, EdgeRoute> = {};\n\n for (const group of groups.values()) {\n const first = requireEdge(group, 0);\n const posA = requirePosition(positions, first.from);\n const posB = requirePosition(positions, first.to);\n const dx = posB.x - posA.x;\n const dy = posB.y - posA.y;\n const len = Math.hypot(dx, dy) || 1;\n const perpX = -dy / len;\n const perpY = dx / len;\n const midX = (posA.x + posB.x) / 2;\n const midY = (posA.y + posB.y) / 2;\n\n group.forEach((edge, index) => {\n const isCurved = group.length > 1;\n const sign = index % 2 === 0 ? 1 : -1;\n const magnitude = Math.floor(index / 2) + 1;\n const offset = isCurved ? sign * magnitude * CURVE_OFFSET : 0;\n\n const control = { x: midX + perpX * offset, y: midY + perpY * offset };\n const fromCenter = requirePosition(positions, edge.from);\n const toCenter = requirePosition(positions, edge.to);\n const start = clipToRect(fromCenter, control, halfW, halfH);\n const end = clipToRect(toCenter, control, halfW, halfH);\n\n const path = isCurved\n ? `M ${start.x} ${start.y} Q ${control.x} ${control.y} ${end.x} ${end.y}`\n : `M ${start.x} ${start.y} L ${end.x} ${end.y}`;\n\n const label = isCurved\n ? quadraticMidpoint(start, control, end)\n : { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };\n\n routes[edge.id] = { path, labelX: label.x, labelY: label.y };\n });\n }\n\n return routes;\n}\n"],"names":[],"mappings":"AAiCA,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,aAAa;AAEnB,SAAS,YAAuC,OAAqB,OAAkB;AACrF,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,wCAAwC,KAAK,EAAE;AAAA,EACjE;AACA,SAAO;AACT;AAYO,SAAS,WACd,OACA,UAA6B,IACV;AACnB,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,IAAI,MAAM;AAEhB,QAAM,SAAS,KAAK,IAAI,IAAI,KAAK,IAAI,aAAa,YAAY,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,EAAE;AAChG,QAAM,KAAK,SAAS,YAAY,IAAI;AACpC,QAAM,KAAK,SAAS,aAAa,IAAI;AAErC,QAAM,YAAiD,CAAA;AAEvD,MAAI,MAAM,GAAG;AACX,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,GAAA;AAAA,EACpD,WAAW,MAAM,GAAG;AAClB,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,GAAG,GAAA;AAC3D,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,GAAG,GAAA;AAAA,EAC7D,OAAO;AACL,UAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,YAAM,QAAQ,CAAC,KAAK,KAAK,IAAI,KAAM,IAAI,KAAK,KAAM;AAClD,gBAAU,KAAK,EAAE,IAAI;AAAA,QACnB,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,QAC/B,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,MAAA;AAAA,IAEnC,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI;AAAA,IACZ;AAAA,EAAA;AAEJ;AAqBA,MAAM,eAAe;AAErB,SAAS,iBAAiB,GAAW,GAAmB;AACtD,SAAO,IAAI,IAAI,GAAG,CAAC,KAAI,CAAC,KAAK,GAAG,CAAC,KAAI,CAAC;AACxC;AAEA,SAAS,gBACP,WACA,IACqB;AACrB,QAAM,WAAW,UAAU,EAAE;AAC7B,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,4CAA4C,EAAE,GAAG;AAAA,EACnE;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAAmC,OAAgC;AACtF,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,yCAAyC,KAAK,EAAE;AAAA,EAClE;AACA,SAAO;AACT;AAGA,SAAS,WACP,QACA,SACA,OACA,OACqB;AACrB,QAAM,KAAK,QAAQ,IAAI,OAAO;AAC9B,QAAM,KAAK,QAAQ,IAAI,OAAO;AAC9B,MAAI,OAAO,KAAK,OAAO,EAAG,QAAO,EAAE,GAAG,OAAO,GAAG,GAAG,OAAO,EAAA;AAE1D,QAAM,SAAS,OAAO,IAAI,QAAQ,KAAK,IAAI,EAAE,IAAI,OAAO;AACxD,QAAM,SAAS,OAAO,IAAI,QAAQ,KAAK,IAAI,EAAE,IAAI,OAAO;AACxD,QAAM,QAAQ,KAAK,IAAI,QAAQ,MAAM;AAErC,SAAO,EAAE,GAAG,OAAO,IAAI,KAAK,OAAO,GAAG,OAAO,IAAI,KAAK,MAAA;AACxD;AAGA,SAAS,kBACP,IACA,IACA,IACqB;AACrB,SAAO;AAAA,IACL,GAAG,OAAO,GAAG,IAAI,MAAM,GAAG,IAAI,OAAO,GAAG;AAAA,IACxC,GAAG,OAAO,GAAG,IAAI,MAAM,GAAG,IAAI,OAAO,GAAG;AAAA,EAAA;AAE5C;AAaO,SAAS,WACd,OACA,WACA,YAC2B;AAC3B,QAAM,QAAQ,WAAW,YAAY;AACrC,QAAM,QAAQ,WAAW,aAAa;AAEtC,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,OAAO;AACxB,QAAI,EAAE,KAAK,QAAQ,YAAY;AAC7B,YAAM,IAAI,MAAM,qBAAqB,KAAK,EAAE,8BAA8B,KAAK,IAAI,GAAG;AAAA,IACxF;AACA,QAAI,EAAE,KAAK,MAAM,YAAY;AAC3B,YAAM,IAAI,MAAM,qBAAqB,KAAK,EAAE,8BAA8B,KAAK,EAAE,GAAG;AAAA,IACtF;AACA,UAAM,MAAM,iBAAiB,KAAK,MAAM,KAAK,EAAE;AAC/C,UAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,QAAI,OAAO;AACT,YAAM,KAAK,IAAI;AAAA,IACjB,OAAO;AACL,aAAO,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,SAAoC,CAAA;AAE1C,aAAW,SAAS,OAAO,UAAU;AACnC,UAAM,QAAQ,YAAY,OAAO,CAAC;AAClC,UAAM,OAAO,gBAAgB,WAAW,MAAM,IAAI;AAClD,UAAM,OAAO,gBAAgB,WAAW,MAAM,EAAE;AAChD,UAAM,KAAK,KAAK,IAAI,KAAK;AACzB,UAAM,KAAK,KAAK,IAAI,KAAK;AACzB,UAAM,MAAM,KAAK,MAAM,IAAI,EAAE,KAAK;AAClC,UAAM,QAAQ,CAAC,KAAK;AACpB,UAAM,QAAQ,KAAK;AACnB,UAAM,QAAQ,KAAK,IAAI,KAAK,KAAK;AACjC,UAAM,QAAQ,KAAK,IAAI,KAAK,KAAK;AAEjC,UAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,YAAM,WAAW,MAAM,SAAS;AAChC,YAAM,OAAO,QAAQ,MAAM,IAAI,IAAI;AACnC,YAAM,YAAY,KAAK,MAAM,QAAQ,CAAC,IAAI;AAC1C,YAAM,SAAS,WAAW,OAAO,YAAY,eAAe;AAE5D,YAAM,UAAU,EAAE,GAAG,OAAO,QAAQ,QAAQ,GAAG,OAAO,QAAQ,OAAA;AAC9D,YAAM,aAAa,gBAAgB,WAAW,KAAK,IAAI;AACvD,YAAM,WAAW,gBAAgB,WAAW,KAAK,EAAE;AACnD,YAAM,QAAQ,WAAW,YAAY,SAAS,OAAO,KAAK;AAC1D,YAAM,MAAM,WAAW,UAAU,SAAS,OAAO,KAAK;AAEtD,YAAM,OAAO,WACT,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KACrE,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC;AAE/C,YAAM,QAAQ,WACV,kBAAkB,OAAO,SAAS,GAAG,IACrC,EAAE,IAAI,MAAM,IAAI,IAAI,KAAK,GAAG,IAAI,MAAM,IAAI,IAAI,KAAK,EAAA;AAEvD,aAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,MAAM,GAAG,QAAQ,MAAM,EAAA;AAAA,IAC3D,CAAC;AAAA,EACH;AAEA,SAAO;AACT;"}
1
+ {"version":3,"file":"graph-layout.js","sources":["../../src/data/graph-layout.ts"],"sourcesContent":["// Pure, framework-free layout math for GraphDiagram. Kept free of React so it\n// can be unit tested without rendering. `ringLayout` places nodes on a circle;\n// the level-column layout lives in graph-columns-layout.ts, and the edge\n// routing and label placement in graph-edge-routing.ts, re-exported here.\n\nexport {\n routeEdges,\n spreadLabels,\n type EdgeLabelBox,\n type EdgeRoute,\n type GraphLayoutEdge,\n type RouteEdgesDimensions,\n type RouteEdgesOptions,\n} from \"./graph-edge-routing\";\n\nexport interface GraphLayoutNode {\n id: string;\n}\n\nexport interface RingLayoutOptions {\n /** Node bounding-box width in px. Defaults to 168. */\n nodeWidth?: number;\n /** Node bounding-box height in px. Defaults to 60. */\n nodeHeight?: number;\n /** Minimum gap between adjacent node edges in px. Defaults to 40. */\n gap?: number;\n /** Space reserved around the ring before the viewport edge. Defaults to 24. */\n padding?: number;\n}\n\nexport interface GraphLayoutPosition {\n /** Node centre x, in the same coordinate space as `width`/`height`. */\n x: number;\n /** Node centre y. */\n y: number;\n}\n\nexport interface GraphLayoutResult {\n width: number;\n height: number;\n positions: Record<string, GraphLayoutPosition>;\n}\n\nconst DEFAULT_NODE_WIDTH = 168;\nconst DEFAULT_NODE_HEIGHT = 60;\nconst DEFAULT_GAP = 40;\nconst DEFAULT_PADDING = 24;\nconst MIN_RADIUS = 96;\n\nfunction requireNode<T extends GraphLayoutNode>(nodes: readonly T[], index: number): T {\n const node = nodes[index];\n if (!node) {\n throw new Error(`ringLayout: expected a node at index ${index}`);\n }\n return node;\n}\n\n/**\n * Places nodes on a circle in the given order, starting at 12 o'clock and\n * running clockwise. `n=1` is centred (radius 0); `n=2` is a special case —\n * left/right — because 180° clockwise from 12 o'clock lands at 6 o'clock,\n * which reads poorly for a two-node relationship.\n *\n * The radius is sized so the straight-line (chord) distance between any two\n * angularly-adjacent nodes is at least `nodeWidth + gap`, with a sensible\n * floor (`MIN_RADIUS`) for small rings.\n */\nexport function ringLayout(\n nodes: readonly GraphLayoutNode[],\n options: RingLayoutOptions = {},\n): GraphLayoutResult {\n const nodeWidth = options.nodeWidth ?? DEFAULT_NODE_WIDTH;\n const nodeHeight = options.nodeHeight ?? DEFAULT_NODE_HEIGHT;\n const gap = options.gap ?? DEFAULT_GAP;\n const padding = options.padding ?? DEFAULT_PADDING;\n const n = nodes.length;\n\n const radius = n <= 1 ? 0 : Math.max(MIN_RADIUS, (nodeWidth + gap) / (2 * Math.sin(Math.PI / n)));\n const cx = radius + nodeWidth / 2 + padding;\n const cy = radius + nodeHeight / 2 + padding;\n\n const positions: Record<string, GraphLayoutPosition> = {};\n\n if (n === 1) {\n positions[requireNode(nodes, 0).id] = { x: cx, y: cy };\n } else if (n === 2) {\n positions[requireNode(nodes, 0).id] = { x: cx - radius, y: cy };\n positions[requireNode(nodes, 1).id] = { x: cx + radius, y: cy };\n } else {\n nodes.forEach((node, i) => {\n const theta = -Math.PI / 2 + i * ((2 * Math.PI) / n);\n positions[node.id] = {\n x: cx + radius * Math.cos(theta),\n y: cy + radius * Math.sin(theta),\n };\n });\n }\n\n return {\n width: 2 * cx,\n height: 2 * cy,\n positions,\n };\n}\n"],"names":[],"mappings":"AA2CA,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,aAAa;AAEnB,SAAS,YAAuC,OAAqB,OAAkB;AACrF,QAAM,OAAO,MAAM,KAAK;AACxB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,wCAAwC,KAAK,EAAE;AAAA,EACjE;AACA,SAAO;AACT;AAYO,SAAS,WACd,OACA,UAA6B,IACV;AACnB,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,IAAI,MAAM;AAEhB,QAAM,SAAS,KAAK,IAAI,IAAI,KAAK,IAAI,aAAa,YAAY,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,EAAE;AAChG,QAAM,KAAK,SAAS,YAAY,IAAI;AACpC,QAAM,KAAK,SAAS,aAAa,IAAI;AAErC,QAAM,YAAiD,CAAA;AAEvD,MAAI,MAAM,GAAG;AACX,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,GAAA;AAAA,EACpD,WAAW,MAAM,GAAG;AAClB,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,GAAG,GAAA;AAC3D,cAAU,YAAY,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,KAAK,QAAQ,GAAG,GAAA;AAAA,EAC7D,OAAO;AACL,UAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,YAAM,QAAQ,CAAC,KAAK,KAAK,IAAI,KAAM,IAAI,KAAK,KAAM;AAClD,gBAAU,KAAK,EAAE,IAAI;AAAA,QACnB,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,QAC/B,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,MAAA;AAAA,IAEnC,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI;AAAA,IACZ;AAAA,EAAA;AAEJ;"}
@@ -2,8 +2,14 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const react = require("react");
5
+ const useResourceClock = require("../../hooks/use-resource-clock.cjs");
6
+ const format = require("../../lib/format.cjs");
5
7
  const DataTable = require("../DataTable.cjs");
8
+ const Icon = require("../Icon.cjs");
6
9
  const JsonView = require("../JsonView.cjs");
10
+ const UiLoader = require("../../icons/components/UiLoader.cjs");
11
+ const ERROR_PREVIEW_CHARS = 80;
12
+ const HarClockContext = react.createContext(void 0);
7
13
  const COLS = [
8
14
  {
9
15
  key: "request.method",
@@ -14,22 +20,26 @@ const COLS = [
14
20
  key: "request.url",
15
21
  label: "URL",
16
22
  grow: true,
17
- render: (value) => /* @__PURE__ */ jsxRuntime.jsx("span", { title: String(value ?? ""), children: String(value ?? "") })
23
+ render: (value, entry) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-0 items-baseline gap-density-2", children: [
24
+ /* @__PURE__ */ jsxRuntime.jsx("span", { title: String(value ?? ""), children: String(value ?? "") }),
25
+ entry._error ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive text-xs", title: entry._error, children: previewError(entry._error) }) : null
26
+ ] })
18
27
  },
19
28
  {
20
29
  key: "response.status",
21
30
  label: "Status",
22
31
  shrink: true,
23
- render: (value) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: statusColor(Number(value ?? 0)), children: String(value ?? "") }),
24
- sortValue: (value) => Number(value ?? 0)
32
+ render: (value, entry) => /* @__PURE__ */ jsxRuntime.jsx(HarStatus, { status: Number(value ?? 0), entry }),
33
+ sortValue: (value) => Number(value ?? 0),
34
+ filterValue: (value, entry) => harStatusLabel(Number(value ?? 0), entry)
25
35
  },
26
36
  {
27
37
  key: "time",
28
38
  label: "Time",
29
39
  align: "right",
30
40
  shrink: true,
31
- render: (value) => `${Number(value ?? 0).toFixed(0)}ms`,
32
- sortValue: (value) => Number(value ?? 0)
41
+ render: (value, entry) => entry._pending ? /* @__PURE__ */ jsxRuntime.jsx(PendingElapsed, { entry }) : `${Number(value ?? 0).toFixed(0)}ms`,
42
+ sortValue: (value, entry) => entry._pending ? pendingElapsedMs(entry, Date.now()) : Number(value ?? 0)
33
43
  },
34
44
  {
35
45
  key: "response.bodySize",
@@ -49,12 +59,15 @@ function HarPanel({
49
59
  entries,
50
60
  search,
51
61
  emptyLabel = "No HTTP traffic captured",
52
- className
62
+ className,
63
+ bodySource
53
64
  }) {
54
65
  const filteredEntries = react.useMemo(() => {
55
66
  if (search === void 0 || search.trim() === "") return entries;
56
67
  return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry));
57
68
  }, [entries, search]);
69
+ const runningCount = react.useMemo(() => entries.filter((entry) => entry._pending).length, [entries]);
70
+ const now = useResourceClock.useResourceClock(runningCount > 0);
58
71
  if (!entries || entries.length === 0) {
59
72
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel });
60
73
  }
@@ -63,7 +76,7 @@ function HarPanel({
63
76
  // min-h-0` so its own scroll region can be bounded, and in a block parent
64
77
  // that resolves to content height — the table grows past the panel and
65
78
  // nothing scrolls.
66
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex h-full min-h-0 flex-col ${className ?? ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(
79
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex h-full min-h-0 flex-col ${className ?? ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(HarClockContext.Provider, { value: now, children: /* @__PURE__ */ jsxRuntime.jsx(
67
80
  DataTable.DataTable,
68
81
  {
69
82
  data: filteredEntries,
@@ -73,15 +86,64 @@ function HarPanel({
73
86
  globalFilterPlaceholder: "Filter URL, method, or body…",
74
87
  defaultSort: { key: "time", dir: "asc" },
75
88
  emptyMessage: emptyLabel,
76
- getRowId: (entry, index) => `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}-${entry.time}`,
77
- renderExpandedRow: (entry) => /* @__PURE__ */ jsxRuntime.jsx(HarRowDetails, { entry })
89
+ ...runningCount > 0 ? {
90
+ footer: ({ visibleRowCount, totalRowCount }) => `${visibleRowCount} of ${totalRowCount} row${totalRowCount === 1 ? "" : "s"} · ${runningCount} running`
91
+ } : {},
92
+ getRowId: (entry, index) => (
93
+ // `_id` is the same on a request's pending and completed snapshots,
94
+ // so an expanded row survives the transition. Without it, leave out
95
+ // an in-flight entry's `time`, which grows with every snapshot.
96
+ entry._id ?? `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}${entry._pending ? "-pending" : `-${entry.time}`}`
97
+ ),
98
+ renderExpandedRow: (entry) => /* @__PURE__ */ jsxRuntime.jsx(HarEntryDetails, { entry, bodySource })
78
99
  }
79
- ) })
100
+ ) }) })
80
101
  );
81
102
  }
82
- function HarRowDetails({ entry }) {
83
- var _a, _b;
103
+ function HarStatus({ status, entry }) {
104
+ if (entry._pending) {
105
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 text-muted-foreground", children: [
106
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiLoader.UiLoader, className: "animate-spin" }),
107
+ "running"
108
+ ] });
109
+ }
110
+ if (entry._error) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-destructive", children: "ERR" });
111
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: statusColor(status), children: String(status) });
112
+ }
113
+ function PendingElapsed({ entry }) {
114
+ const now = react.useContext(HarClockContext);
115
+ if (now === void 0) throw new Error("PendingElapsed must render inside HarPanel's clock");
116
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: format.formatDuration(pendingElapsedMs(entry, now)) });
117
+ }
118
+ function pendingElapsedMs(entry, now) {
119
+ const started = Date.parse(entry.startedDateTime ?? "");
120
+ if (Number.isNaN(started)) return entry.time;
121
+ return Math.max(0, now - started);
122
+ }
123
+ function harStatusLabel(status, entry) {
124
+ if (entry._pending) return "running";
125
+ if (entry._error) return "ERR";
126
+ return String(status);
127
+ }
128
+ function previewError(error) {
129
+ return error.length > ERROR_PREVIEW_CHARS ? `${error.slice(0, ERROR_PREVIEW_CHARS)}…` : error;
130
+ }
131
+ function HarEntryDetails({ entry, bodySource }) {
132
+ const parentNow = react.useContext(HarClockContext);
133
+ const ownNow = useResourceClock.useResourceClock(Boolean(entry._pending) && parentNow === void 0);
134
+ return /* @__PURE__ */ jsxRuntime.jsx(HarClockContext.Provider, { value: parentNow ?? ownNow, children: /* @__PURE__ */ jsxRuntime.jsx(HarRowDetails, { entry, bodySource }) });
135
+ }
136
+ function HarRowDetails({ entry, bodySource }) {
137
+ var _a, _b, _c, _d, _e, _f;
84
138
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
139
+ entry._error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-density-3", children: [
140
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-foreground mb-1", children: "Transport Error" }),
141
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap break-all text-destructive", children: entry._error })
142
+ ] }),
143
+ entry._pending && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-density-3 text-muted-foreground", children: [
144
+ "Waiting for response — in flight for ",
145
+ /* @__PURE__ */ jsxRuntime.jsx(PendingElapsed, { entry })
146
+ ] }),
85
147
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-density-4", children: [
86
148
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
87
149
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -91,16 +153,16 @@ function HarRowDetails({ entry }) {
91
153
  ...entry.request.headers ? { headers: entry.request.headers } : {}
92
154
  }
93
155
  ),
94
- ((_a = entry.request.postData) == null ? void 0 : _a.text) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-density-2", children: [
95
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-foreground mb-1", children: "Request Body" }),
96
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background p-density-2 rounded border border-border overflow-auto max-h-48", children: /* @__PURE__ */ jsxRuntime.jsx(
97
- BodyView,
98
- {
99
- text: entry.request.postData.text,
100
- ...entry.request.postData.mimeType ? { mimeType: entry.request.postData.mimeType } : {}
101
- }
102
- ) })
103
- ] })
156
+ (((_a = entry.request.postData) == null ? void 0 : _a.text) || (bodySource == null ? void 0 : bodySource.hasBody(entry, "request"))) && /* @__PURE__ */ jsxRuntime.jsx(
157
+ BodySection,
158
+ {
159
+ entry,
160
+ part: "request",
161
+ text: (_b = entry.request.postData) == null ? void 0 : _b.text,
162
+ mimeType: (_c = entry.request.postData) == null ? void 0 : _c.mimeType,
163
+ bodySource
164
+ }
165
+ )
104
166
  ] }),
105
167
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
106
168
  HeaderList,
@@ -110,16 +172,48 @@ function HarRowDetails({ entry }) {
110
172
  }
111
173
  ) })
112
174
  ] }),
113
- ((_b = entry.response.content) == null ? void 0 : _b.text) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-density-3", children: [
114
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-foreground mb-1", children: "Response Body" }),
115
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background p-density-2 rounded border border-border overflow-auto max-h-64", children: /* @__PURE__ */ jsxRuntime.jsx(
116
- BodyView,
117
- {
118
- text: entry.response.content.text,
119
- ...entry.response.content.mimeType ? { mimeType: entry.response.content.mimeType } : {}
120
- }
121
- ) })
122
- ] })
175
+ (((_d = entry.response.content) == null ? void 0 : _d.text) || (bodySource == null ? void 0 : bodySource.hasBody(entry, "response"))) && /* @__PURE__ */ jsxRuntime.jsx(
176
+ BodySection,
177
+ {
178
+ entry,
179
+ part: "response",
180
+ text: (_e = entry.response.content) == null ? void 0 : _e.text,
181
+ mimeType: (_f = entry.response.content) == null ? void 0 : _f.mimeType,
182
+ bodySource
183
+ }
184
+ )
185
+ ] });
186
+ }
187
+ function BodySection({ entry, part, text, mimeType, bodySource }) {
188
+ const remote = !text && (bodySource == null ? void 0 : bodySource.hasBody(entry, part));
189
+ const requestId = entry._id;
190
+ if (remote && !requestId) throw new Error("Private HAR body requires a request ID");
191
+ const [preview, setPreview] = react.useState();
192
+ const [error, setError] = react.useState();
193
+ react.useEffect(() => {
194
+ if (!remote || !bodySource || !requestId) return;
195
+ let active = true;
196
+ setPreview(void 0);
197
+ setError(void 0);
198
+ bodySource.preview(requestId, part).then(
199
+ (value) => {
200
+ if (active) setPreview(value);
201
+ },
202
+ (reason) => {
203
+ if (active) setError(reason instanceof Error ? reason : new Error(String(reason)));
204
+ }
205
+ );
206
+ return () => {
207
+ active = false;
208
+ };
209
+ }, [bodySource, requestId, part, remote]);
210
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-density-3", children: [
211
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center gap-2 font-semibold text-foreground", children: [
212
+ part === "request" ? "Request Body" : "Response Body",
213
+ remote && bodySource && /* @__PURE__ */ jsxRuntime.jsx("a", { className: "text-sm font-normal text-primary underline", href: bodySource.downloadHref(entry, part), download: true, children: "Download full body" })
214
+ ] }),
215
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background p-density-2 rounded border border-border overflow-auto max-h-64", children: error ? /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", className: "text-destructive", children: error.message }) : remote && preview === void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Loading preview…" }) : /* @__PURE__ */ jsxRuntime.jsx(BodyView, { text: text || preview || "", ...mimeType ? { mimeType } : {} }) }),
216
+ remote && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Preview shows at most the first 64 KiB." })
123
217
  ] });
124
218
  }
125
219
  function HeaderList({
@@ -161,7 +255,8 @@ function matchesSearch(needle, entry) {
161
255
  entry.request.method,
162
256
  entry.request.url,
163
257
  (_a = entry.request.postData) == null ? void 0 : _a.text,
164
- (_b = entry.response.content) == null ? void 0 : _b.text
258
+ (_b = entry.response.content) == null ? void 0 : _b.text,
259
+ entry._error
165
260
  ].some((value) => !!value && value.toLowerCase().includes(needle));
166
261
  }
167
262
  function formatBytes(bytes) {
@@ -177,5 +272,6 @@ function statusColor(status) {
177
272
  if (status >= 200) return "text-green-600";
178
273
  return "text-muted-foreground";
179
274
  }
275
+ exports.HarEntryDetails = HarEntryDetails;
180
276
  exports.HarPanel = HarPanel;
181
277
  //# sourceMappingURL=HarPanel.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"HarPanel.cjs","sources":["../../../src/data/har/HarPanel.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { DataTable, type DataTableColumn } from \"../DataTable\";\nimport { JsonView } from \"../JsonView\";\nimport type { HAREntry } from \"./types\";\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};\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) => <span title={String(value ?? \"\")}>{String(value ?? \"\")}</span>,\n },\n {\n key: \"response.status\",\n label: \"Status\",\n shrink: true,\n render: (value) => (\n <span className={statusColor(Number(value ?? 0))}>{String(value ?? \"\")}</span>\n ),\n sortValue: (value) => Number(value ?? 0),\n },\n {\n key: \"time\",\n label: \"Time\",\n align: \"right\",\n shrink: true,\n render: (value) => `${Number(value ?? 0).toFixed(0)}ms`,\n sortValue: (value) => 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}: 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\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 <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 getRowId={(entry, index) =>\n `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}-${entry.time}`\n }\n renderExpandedRow={(entry) => <HarRowDetails entry={entry} />}\n />\n </div>\n );\n}\n\nfunction HarRowDetails({ entry }: { entry: HAREntry }) {\n return (\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 && (\n <div className=\"mt-density-2\">\n <div className=\"font-semibold text-foreground mb-1\">Request Body</div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-48\">\n <BodyView\n text={entry.request.postData.text}\n {...(entry.request.postData.mimeType\n ? { mimeType: entry.request.postData.mimeType }\n : {})}\n />\n </div>\n </div>\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 && (\n <div className=\"mt-density-3\">\n <div className=\"font-semibold text-foreground mb-1\">Response Body</div>\n <div className=\"bg-background p-density-2 rounded border border-border overflow-auto max-h-64\">\n <BodyView\n text={entry.response.content.text}\n {...(entry.response.content.mimeType\n ? { mimeType: entry.response.content.mimeType }\n : {})}\n />\n </div>\n </div>\n )}\n </>\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 ].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":["jsx","useMemo","DataTable","jsxs","Fragment","JsonView"],"mappings":";;;;;;AAgBA,MAAM,OAAoC;AAAA,EACxC;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EAAA;AAAA,EAEV;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,CAAC,UAAUA,2BAAAA,IAAC,QAAA,EAAK,OAAO,OAAO,SAAS,EAAE,GAAI,UAAA,OAAO,SAAS,EAAE,EAAA,CAAE;AAAA,EAAA;AAAA,EAE5E;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,CAAC,UACPA,2BAAAA,IAAC,UAAK,WAAW,YAAY,OAAO,SAAS,CAAC,CAAC,GAAI,UAAA,OAAO,SAAS,EAAE,GAAE;AAAA,IAEzE,WAAW,CAAC,UAAU,OAAO,SAAS,CAAC;AAAA,EAAA;AAAA,EAEzC;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,CAAC,UAAU,GAAG,OAAO,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IACnD,WAAW,CAAC,UAAU,OAAO,SAAS,CAAC;AAAA,EAAA;AAAA,EAEzC;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,CAAC,UAAU,YAAY,OAAO,SAAS,CAAC,CAAC;AAAA,IACjD,WAAW,CAAC,UAAU,OAAO,SAAS,CAAC;AAAA,EAAA;AAAA,EAEzC;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EAAA;AAEZ;AAEO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAAkB;AAChB,QAAM,kBAAkBC,MAAAA,QAAQ,MAAM;AACpC,QAAI,WAAW,UAAa,OAAO,KAAA,MAAW,GAAI,QAAO;AACzD,WAAO,QAAQ,OAAO,CAAC,UAAU,cAAc,OAAO,OAAO,eAAe,KAAK,CAAC;AAAA,EACpF,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,MAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AACpC,WACED,2BAAAA,IAAC,OAAA,EAAI,WAAU,yDAAyD,UAAA,YAAW;AAAA,EAEvF;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA,mCAKG,OAAA,EAAI,WAAW,gCAAgC,aAAa,EAAE,IAC7D,UAAAA,2BAAAA;AAAAA,MAACE,UAAAA;AAAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAU;AAAA,QACV,kBAAkB,WAAW;AAAA,QAC7B,yBAAwB;AAAA,QACxB,aAAa,EAAE,KAAK,QAAQ,KAAK,MAAA;AAAA,QACjC,cAAc;AAAA,QACd,UAAU,CAAC,OAAO,UAChB,GAAG,MAAM,mBAAmB,KAAK,IAAI,MAAM,QAAQ,MAAM,IAAI,MAAM,QAAQ,GAAG,IAAI,MAAM,IAAI;AAAA,QAE9F,mBAAmB,CAAC,UAAUF,2BAAAA,IAAC,iBAAc,MAAA,CAAc;AAAA,MAAA;AAAA,IAAA,EAC7D,CACF;AAAA;AAEJ;AAEA,SAAS,cAAc,EAAE,SAA8B;;AACrD,SACEG,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,IAAAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,MAAAA,gCAAC,OAAA,EACC,UAAA;AAAA,QAAAH,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAM;AAAA,YACL,GAAI,MAAM,QAAQ,UAAU,EAAE,SAAS,MAAM,QAAQ,YAAY,CAAA;AAAA,UAAC;AAAA,QAAA;AAAA,UAEpE,WAAM,QAAQ,aAAd,mBAAwB,SACvBG,2BAAAA,KAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,UAAAH,2BAAAA,IAAC,OAAA,EAAI,WAAU,sCAAqC,UAAA,gBAAY;AAAA,UAChEA,2BAAAA,IAAC,OAAA,EAAI,WAAU,iFACb,UAAAA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAM,MAAM,QAAQ,SAAS;AAAA,cAC5B,GAAI,MAAM,QAAQ,SAAS,WACxB,EAAE,UAAU,MAAM,QAAQ,SAAS,aACnC,CAAA;AAAA,YAAC;AAAA,UAAA,EACP,CACF;AAAA,QAAA,EAAA,CACF;AAAA,MAAA,GAEJ;AAAA,qCACC,OAAA,EACC,UAAAA,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACL,GAAI,MAAM,SAAS,UAAU,EAAE,SAAS,MAAM,SAAS,YAAY,CAAA;AAAA,QAAC;AAAA,MAAA,EACvE,CACF;AAAA,IAAA,GACF;AAAA,MACC,WAAM,SAAS,YAAf,mBAAwB,SACvBG,2BAAAA,KAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,MAAAH,2BAAAA,IAAC,OAAA,EAAI,WAAU,sCAAqC,UAAA,iBAAa;AAAA,MACjEA,2BAAAA,IAAC,OAAA,EAAI,WAAU,iFACb,UAAAA,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAM,MAAM,SAAS,QAAQ;AAAA,UAC5B,GAAI,MAAM,SAAS,QAAQ,WACxB,EAAE,UAAU,MAAM,SAAS,QAAQ,aACnC,CAAA;AAAA,QAAC;AAAA,MAAA,EACP,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AACF,GAGG;AACD,SACEG,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,IAAAJ,2BAAAA,IAAC,OAAA,EAAI,WAAU,sCAAsC,UAAA,OAAM;AAAA,IAC3DA,2BAAAA,IAAC,OAAA,EAAI,WAAU,eACZ,UAAA,mCAAS,IAAI,CAAC,GAAG,MAChBG,2BAAAA,KAAC,OAAA,EAAY,WAAU,qBACrB,UAAA;AAAA,MAAAA,2BAAAA,KAAC,QAAA,EAAK,WAAU,wCAAwC,UAAA;AAAA,QAAA,EAAE;AAAA,QAAK;AAAA,MAAA,GAAC;AAAA,MAAO;AAAA,MAAE,EAAE;AAAA,IAAA,EAAA,GADnE,CAEV,GACD,CACH;AAAA,EAAA,GACF;AAEJ;AAEA,SAAS,SAAS,EAAE,MAAM,YAAiD;AACzE,MAAI,WAAW,QAAQ,GAAG;AACxB,UAAM,SAAS,aAAa,IAAI;AAChC,QAAI,WAAW,KAAM,QAAOH,2BAAAA,IAACK,SAAAA,UAAA,EAAS,MAAM,QAAQ;AAAA,EACtD;AACA,SAAOL,2BAAAA,IAAC,OAAA,EAAI,WAAU,iCAAiC,UAAA,MAAK;AAC9D;AAEA,SAAS,aAAa,MAA8B;AAClD,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,MAAwB;AAC1C,SAAO,CAAC,CAAC,SAAS,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,YAAY;AACvE;AAEA,SAAS,cAAc,QAAgB,OAA0B;;AAC/D,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,KACd,WAAM,QAAQ,aAAd,mBAAwB;AAAA,KACxB,WAAM,SAAS,YAAf,mBAAwB;AAAA,EAAA,EACxB,KAAK,CAAC,UAAU,CAAC,CAAC,SAAS,MAAM,YAAA,EAAc,SAAS,MAAM,CAAC;AACnE;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC5D,SAAO,IAAI,QAAQ,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC5C;AAEA,SAAS,YAAY,QAAwB;AAC3C,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,UAAU,IAAK,QAAO;AAC1B,SAAO;AACT;;"}
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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"}