@contraagent/ui 0.1.0 → 0.2.0

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 (389) hide show
  1. package/README.md +9 -2
  2. package/custom-elements.json +3300 -3098
  3. package/dist/adapters/check-rows.d.ts +2 -0
  4. package/dist/adapters/check-rows.d.ts.map +1 -1
  5. package/dist/adapters/check-rows.js +2 -0
  6. package/dist/adapters/check-rows.js.map +1 -1
  7. package/dist/adapters/panel-body.d.ts +15 -1
  8. package/dist/adapters/panel-body.d.ts.map +1 -1
  9. package/dist/adapters/panel-body.js +27 -0
  10. package/dist/adapters/panel-body.js.map +1 -1
  11. package/dist/contraagent-ui.js +684 -641
  12. package/dist/elements/lists/ca-panel-body.d.ts +16 -3
  13. package/dist/elements/lists/ca-panel-body.d.ts.map +1 -1
  14. package/dist/elements/lists/ca-panel-body.js +74 -8
  15. package/dist/elements/lists/ca-panel-body.js.map +1 -1
  16. package/dist/elements/overview/ca-overview.d.ts +2 -1
  17. package/dist/elements/overview/ca-overview.d.ts.map +1 -1
  18. package/dist/elements/overview/ca-overview.js +8 -1
  19. package/dist/elements/overview/ca-overview.js.map +1 -1
  20. package/dist/elements/overview/ca-services.d.ts +25 -0
  21. package/dist/elements/overview/ca-services.d.ts.map +1 -0
  22. package/dist/elements/overview/ca-services.js +77 -0
  23. package/dist/elements/overview/ca-services.js.map +1 -0
  24. package/dist/elements/overview/index.d.ts +1 -0
  25. package/dist/elements/overview/index.d.ts.map +1 -1
  26. package/dist/elements/overview/index.js +1 -0
  27. package/dist/elements/overview/index.js.map +1 -1
  28. package/dist/elements/shared/contract-parts.d.ts.map +1 -1
  29. package/dist/elements/shared/contract-parts.js +18 -2
  30. package/dist/elements/shared/contract-parts.js.map +1 -1
  31. package/dist/elements/shell/ca-monitor.d.ts +4 -2
  32. package/dist/elements/shell/ca-monitor.d.ts.map +1 -1
  33. package/dist/elements/shell/ca-monitor.js +4 -2
  34. package/dist/elements/shell/ca-monitor.js.map +1 -1
  35. package/dist/elements/task/header.d.ts.map +1 -1
  36. package/dist/elements/task/header.js +7 -3
  37. package/dist/elements/task/header.js.map +1 -1
  38. package/dist/locales/ja.d.ts +24 -0
  39. package/dist/locales/ja.d.ts.map +1 -1
  40. package/dist/locales/ja.js +24 -0
  41. package/dist/locales/ja.js.map +1 -1
  42. package/dist/words/changes.d.ts +2 -2
  43. package/dist/words/changes.d.ts.map +1 -1
  44. package/dist/words/changes.js +2 -0
  45. package/dist/words/changes.js.map +1 -1
  46. package/dist/words/index.d.ts +1 -0
  47. package/dist/words/index.d.ts.map +1 -1
  48. package/dist/words/index.js +1 -0
  49. package/dist/words/index.js.map +1 -1
  50. package/dist/words/panels.d.ts +6 -0
  51. package/dist/words/panels.d.ts.map +1 -1
  52. package/dist/words/panels.js +13 -0
  53. package/dist/words/panels.js.map +1 -1
  54. package/dist/words/services.d.ts +16 -0
  55. package/dist/words/services.d.ts.map +1 -0
  56. package/dist/words/services.js +45 -0
  57. package/dist/words/services.js.map +1 -0
  58. package/docs/embed.md +4 -1
  59. package/package.json +3 -3
  60. package/src/adapters/check-rows.ts +3 -0
  61. package/src/adapters/panel-body.ts +40 -2
  62. package/src/elements/lists/ca-panel-body.ts +70 -9
  63. package/src/elements/overview/ca-overview.ts +8 -1
  64. package/src/elements/overview/ca-services.ts +66 -0
  65. package/src/elements/overview/index.ts +1 -0
  66. package/src/elements/shared/contract-parts.ts +18 -2
  67. package/src/elements/shell/ca-monitor.ts +4 -2
  68. package/src/elements/task/header.ts +7 -3
  69. package/src/locales/ja.ts +24 -0
  70. package/src/words/changes.ts +4 -2
  71. package/src/words/index.ts +1 -0
  72. package/src/words/panels.ts +18 -0
  73. package/src/words/services.ts +57 -0
  74. package/dist/core/clock.d.ts +0 -20
  75. package/dist/core/clock.d.ts.map +0 -1
  76. package/dist/core/clock.js +0 -45
  77. package/dist/core/clock.js.map +0 -1
  78. package/dist/core/route.d.ts +0 -62
  79. package/dist/core/route.d.ts.map +0 -1
  80. package/dist/core/route.js +0 -143
  81. package/dist/core/route.js.map +0 -1
  82. package/dist/core/router.d.ts +0 -27
  83. package/dist/core/router.d.ts.map +0 -1
  84. package/dist/core/router.js +0 -57
  85. package/dist/core/router.js.map +0 -1
  86. package/dist/elements/lists/filters.d.ts +0 -28
  87. package/dist/elements/lists/filters.d.ts.map +0 -1
  88. package/dist/elements/lists/filters.js +0 -71
  89. package/dist/elements/lists/filters.js.map +0 -1
  90. package/dist/elements/overview/ca-pipeline-progress.d.ts +0 -17
  91. package/dist/elements/overview/ca-pipeline-progress.d.ts.map +0 -1
  92. package/dist/elements/overview/ca-pipeline-progress.js +0 -119
  93. package/dist/elements/overview/ca-pipeline-progress.js.map +0 -1
  94. package/dist/elements/overview/ca-plan-matrix.d.ts +0 -27
  95. package/dist/elements/overview/ca-plan-matrix.d.ts.map +0 -1
  96. package/dist/elements/overview/ca-plan-matrix.js +0 -113
  97. package/dist/elements/overview/ca-plan-matrix.js.map +0 -1
  98. package/dist/elements/overview/flow-types.d.ts +0 -2
  99. package/dist/elements/overview/flow-types.d.ts.map +0 -1
  100. package/dist/elements/overview/flow-types.js +0 -2
  101. package/dist/elements/overview/flow-types.js.map +0 -1
  102. package/dist/elements/overview/graph/arrange.d.ts +0 -16
  103. package/dist/elements/overview/graph/arrange.d.ts.map +0 -1
  104. package/dist/elements/overview/graph/arrange.js +0 -152
  105. package/dist/elements/overview/graph/arrange.js.map +0 -1
  106. package/dist/elements/overview/graph/card.d.ts +0 -64
  107. package/dist/elements/overview/graph/card.d.ts.map +0 -1
  108. package/dist/elements/overview/graph/card.js +0 -160
  109. package/dist/elements/overview/graph/card.js.map +0 -1
  110. package/dist/elements/overview/graph/flow-index.d.ts +0 -52
  111. package/dist/elements/overview/graph/flow-index.d.ts.map +0 -1
  112. package/dist/elements/overview/graph/flow-index.js +0 -121
  113. package/dist/elements/overview/graph/flow-index.js.map +0 -1
  114. package/dist/elements/overview/graph/highlight.d.ts +0 -14
  115. package/dist/elements/overview/graph/highlight.d.ts.map +0 -1
  116. package/dist/elements/overview/graph/highlight.js +0 -33
  117. package/dist/elements/overview/graph/highlight.js.map +0 -1
  118. package/dist/elements/overview/graph/layer.d.ts +0 -32
  119. package/dist/elements/overview/graph/layer.d.ts.map +0 -1
  120. package/dist/elements/overview/graph/layer.js +0 -92
  121. package/dist/elements/overview/graph/layer.js.map +0 -1
  122. package/dist/elements/overview/graph/layout.d.ts +0 -80
  123. package/dist/elements/overview/graph/layout.d.ts.map +0 -1
  124. package/dist/elements/overview/graph/layout.js +0 -261
  125. package/dist/elements/overview/graph/layout.js.map +0 -1
  126. package/dist/elements/overview/graph/marks.d.ts +0 -18
  127. package/dist/elements/overview/graph/marks.d.ts.map +0 -1
  128. package/dist/elements/overview/graph/marks.js +0 -17
  129. package/dist/elements/overview/graph/marks.js.map +0 -1
  130. package/dist/elements/overview/graph/matrix.d.ts +0 -58
  131. package/dist/elements/overview/graph/matrix.d.ts.map +0 -1
  132. package/dist/elements/overview/graph/matrix.js +0 -93
  133. package/dist/elements/overview/graph/matrix.js.map +0 -1
  134. package/dist/elements/overview/graph/nav.d.ts +0 -15
  135. package/dist/elements/overview/graph/nav.d.ts.map +0 -1
  136. package/dist/elements/overview/graph/nav.js +0 -80
  137. package/dist/elements/overview/graph/nav.js.map +0 -1
  138. package/dist/elements/overview/graph/pan-zoom.d.ts +0 -83
  139. package/dist/elements/overview/graph/pan-zoom.d.ts.map +0 -1
  140. package/dist/elements/overview/graph/pan-zoom.js +0 -189
  141. package/dist/elements/overview/graph/pan-zoom.js.map +0 -1
  142. package/dist/elements/overview/graph/paths.d.ts +0 -9
  143. package/dist/elements/overview/graph/paths.d.ts.map +0 -1
  144. package/dist/elements/overview/graph/paths.js +0 -44
  145. package/dist/elements/overview/graph/paths.js.map +0 -1
  146. package/dist/elements/overview/graph/place-card.d.ts +0 -36
  147. package/dist/elements/overview/graph/place-card.d.ts.map +0 -1
  148. package/dist/elements/overview/graph/place-card.js +0 -68
  149. package/dist/elements/overview/graph/place-card.js.map +0 -1
  150. package/dist/elements/overview/graph/project.d.ts +0 -31
  151. package/dist/elements/overview/graph/project.d.ts.map +0 -1
  152. package/dist/elements/overview/graph/project.js +0 -455
  153. package/dist/elements/overview/graph/project.js.map +0 -1
  154. package/dist/elements/overview/graph/selection.d.ts +0 -31
  155. package/dist/elements/overview/graph/selection.d.ts.map +0 -1
  156. package/dist/elements/overview/graph/selection.js +0 -44
  157. package/dist/elements/overview/graph/selection.js.map +0 -1
  158. package/dist/elements/overview/graph/states.d.ts +0 -6
  159. package/dist/elements/overview/graph/states.d.ts.map +0 -1
  160. package/dist/elements/overview/graph/states.js +0 -5
  161. package/dist/elements/overview/graph/states.js.map +0 -1
  162. package/dist/elements/overview/graph/types.d.ts +0 -124
  163. package/dist/elements/overview/graph/types.d.ts.map +0 -1
  164. package/dist/elements/overview/graph/types.js +0 -3
  165. package/dist/elements/overview/graph/types.js.map +0 -1
  166. package/dist/elements/overview/graph/view.d.ts +0 -16
  167. package/dist/elements/overview/graph/view.d.ts.map +0 -1
  168. package/dist/elements/overview/graph/view.js +0 -20
  169. package/dist/elements/overview/graph/view.js.map +0 -1
  170. package/dist/elements/overview/kpis-model.d.ts +0 -31
  171. package/dist/elements/overview/kpis-model.d.ts.map +0 -1
  172. package/dist/elements/overview/kpis-model.js +0 -30
  173. package/dist/elements/overview/kpis-model.js.map +0 -1
  174. package/dist/elements/overview/logic.d.ts +0 -442
  175. package/dist/elements/overview/logic.d.ts.map +0 -1
  176. package/dist/elements/overview/logic.js +0 -1431
  177. package/dist/elements/overview/logic.js.map +0 -1
  178. package/dist/elements/overview/pipeline-graph/dag.d.ts +0 -4
  179. package/dist/elements/overview/pipeline-graph/dag.d.ts.map +0 -1
  180. package/dist/elements/overview/pipeline-graph/dag.js +0 -259
  181. package/dist/elements/overview/pipeline-graph/dag.js.map +0 -1
  182. package/dist/elements/overview/pipeline-graph/icons.d.ts +0 -5
  183. package/dist/elements/overview/pipeline-graph/icons.d.ts.map +0 -1
  184. package/dist/elements/overview/pipeline-graph/icons.js +0 -13
  185. package/dist/elements/overview/pipeline-graph/icons.js.map +0 -1
  186. package/dist/elements/overview/pipeline-graph/legend.d.ts +0 -9
  187. package/dist/elements/overview/pipeline-graph/legend.d.ts.map +0 -1
  188. package/dist/elements/overview/pipeline-graph/legend.js +0 -59
  189. package/dist/elements/overview/pipeline-graph/legend.js.map +0 -1
  190. package/dist/elements/overview/pipeline-graph/matrix.d.ts +0 -4
  191. package/dist/elements/overview/pipeline-graph/matrix.d.ts.map +0 -1
  192. package/dist/elements/overview/pipeline-graph/matrix.js +0 -193
  193. package/dist/elements/overview/pipeline-graph/matrix.js.map +0 -1
  194. package/dist/elements/overview/pipeline-graph/table.d.ts +0 -11
  195. package/dist/elements/overview/pipeline-graph/table.d.ts.map +0 -1
  196. package/dist/elements/overview/pipeline-graph/table.js +0 -74
  197. package/dist/elements/overview/pipeline-graph/table.js.map +0 -1
  198. package/dist/elements/overview/pipeline-graph/tools.d.ts +0 -5
  199. package/dist/elements/overview/pipeline-graph/tools.d.ts.map +0 -1
  200. package/dist/elements/overview/pipeline-graph/tools.js +0 -48
  201. package/dist/elements/overview/pipeline-graph/tools.js.map +0 -1
  202. package/dist/elements/overview/pipeline-graph/words.d.ts +0 -45
  203. package/dist/elements/overview/pipeline-graph/words.d.ts.map +0 -1
  204. package/dist/elements/overview/pipeline-graph/words.js +0 -108
  205. package/dist/elements/overview/pipeline-graph/words.js.map +0 -1
  206. package/dist/elements/overview/place-card.d.ts +0 -36
  207. package/dist/elements/overview/place-card.d.ts.map +0 -1
  208. package/dist/elements/overview/place-card.js +0 -68
  209. package/dist/elements/overview/place-card.js.map +0 -1
  210. package/dist/elements/overview/timeline-model.d.ts +0 -44
  211. package/dist/elements/overview/timeline-model.d.ts.map +0 -1
  212. package/dist/elements/overview/timeline-model.js +0 -53
  213. package/dist/elements/overview/timeline-model.js.map +0 -1
  214. package/dist/elements/shared/ca-count.d.ts +0 -17
  215. package/dist/elements/shared/ca-count.d.ts.map +0 -1
  216. package/dist/elements/shared/ca-count.js +0 -59
  217. package/dist/elements/shared/ca-count.js.map +0 -1
  218. package/dist/elements/shared/ca-elapsed.d.ts +0 -14
  219. package/dist/elements/shared/ca-elapsed.d.ts.map +0 -1
  220. package/dist/elements/shared/ca-elapsed.js +0 -33
  221. package/dist/elements/shared/ca-elapsed.js.map +0 -1
  222. package/dist/elements/shared/ca-json-tree.d.ts +0 -16
  223. package/dist/elements/shared/ca-json-tree.d.ts.map +0 -1
  224. package/dist/elements/shared/ca-json-tree.js +0 -52
  225. package/dist/elements/shared/ca-json-tree.js.map +0 -1
  226. package/dist/elements/shared/ca-markdown.d.ts +0 -19
  227. package/dist/elements/shared/ca-markdown.d.ts.map +0 -1
  228. package/dist/elements/shared/ca-markdown.js +0 -59
  229. package/dist/elements/shared/ca-markdown.js.map +0 -1
  230. package/dist/elements/shared/fact-line.d.ts +0 -4
  231. package/dist/elements/shared/fact-line.d.ts.map +0 -1
  232. package/dist/elements/shared/fact-line.js +0 -8
  233. package/dist/elements/shared/fact-line.js.map +0 -1
  234. package/dist/elements/task/rounds.d.ts +0 -14
  235. package/dist/elements/task/rounds.d.ts.map +0 -1
  236. package/dist/elements/task/rounds.js +0 -59
  237. package/dist/elements/task/rounds.js.map +0 -1
  238. package/dist/elements/task/transcript-model.d.ts +0 -106
  239. package/dist/elements/task/transcript-model.d.ts.map +0 -1
  240. package/dist/elements/task/transcript-model.js +0 -280
  241. package/dist/elements/task/transcript-model.js.map +0 -1
  242. package/dist/elements/task/who.d.ts +0 -5
  243. package/dist/elements/task/who.d.ts.map +0 -1
  244. package/dist/elements/task/who.js +0 -30
  245. package/dist/elements/task/who.js.map +0 -1
  246. package/dist/events.d.ts +0 -107
  247. package/dist/events.d.ts.map +0 -1
  248. package/dist/events.js +0 -5
  249. package/dist/events.js.map +0 -1
  250. package/dist/i18n.d.ts +0 -16
  251. package/dist/i18n.d.ts.map +0 -1
  252. package/dist/i18n.js +0 -48
  253. package/dist/i18n.js.map +0 -1
  254. package/dist/lib/clock.d.ts +0 -20
  255. package/dist/lib/clock.d.ts.map +0 -1
  256. package/dist/lib/clock.js +0 -45
  257. package/dist/lib/clock.js.map +0 -1
  258. package/dist/lib/format.d.ts +0 -35
  259. package/dist/lib/format.d.ts.map +0 -1
  260. package/dist/lib/format.js +0 -117
  261. package/dist/lib/format.js.map +0 -1
  262. package/dist/lib/highlight.d.ts +0 -5
  263. package/dist/lib/highlight.d.ts.map +0 -1
  264. package/dist/lib/highlight.js +0 -86
  265. package/dist/lib/highlight.js.map +0 -1
  266. package/dist/lib/labels.d.ts +0 -103
  267. package/dist/lib/labels.d.ts.map +0 -1
  268. package/dist/lib/labels.js +0 -249
  269. package/dist/lib/labels.js.map +0 -1
  270. package/dist/lib/markdown.d.ts +0 -13
  271. package/dist/lib/markdown.d.ts.map +0 -1
  272. package/dist/lib/markdown.js +0 -118
  273. package/dist/lib/markdown.js.map +0 -1
  274. package/dist/lib/memory.d.ts +0 -27
  275. package/dist/lib/memory.d.ts.map +0 -1
  276. package/dist/lib/memory.js +0 -117
  277. package/dist/lib/memory.js.map +0 -1
  278. package/dist/lib/quota-format.d.ts +0 -28
  279. package/dist/lib/quota-format.d.ts.map +0 -1
  280. package/dist/lib/quota-format.js +0 -67
  281. package/dist/lib/quota-format.js.map +0 -1
  282. package/dist/lib/scroll.d.ts +0 -16
  283. package/dist/lib/scroll.d.ts.map +0 -1
  284. package/dist/lib/scroll.js +0 -70
  285. package/dist/lib/scroll.js.map +0 -1
  286. package/dist/lib/status.d.ts +0 -25
  287. package/dist/lib/status.d.ts.map +0 -1
  288. package/dist/lib/status.js +0 -40
  289. package/dist/lib/status.js.map +0 -1
  290. package/dist/route.d.ts +0 -46
  291. package/dist/route.d.ts.map +0 -1
  292. package/dist/route.js +0 -102
  293. package/dist/route.js.map +0 -1
  294. package/dist/router.d.ts +0 -23
  295. package/dist/router.d.ts.map +0 -1
  296. package/dist/router.js +0 -55
  297. package/dist/router.js.map +0 -1
  298. package/dist/styles/base.d.ts +0 -11
  299. package/dist/styles/base.d.ts.map +0 -1
  300. package/dist/styles/base.js +0 -241
  301. package/dist/styles/base.js.map +0 -1
  302. package/dist/styles/chips.d.ts +0 -2
  303. package/dist/styles/chips.d.ts.map +0 -1
  304. package/dist/styles/chips.js +0 -18
  305. package/dist/styles/chips.js.map +0 -1
  306. package/dist/styles/executor-icon.d.ts +0 -3
  307. package/dist/styles/executor-icon.d.ts.map +0 -1
  308. package/dist/styles/executor-icon.js +0 -9
  309. package/dist/styles/executor-icon.js.map +0 -1
  310. package/dist/styles/facts.d.ts +0 -2
  311. package/dist/styles/facts.d.ts.map +0 -1
  312. package/dist/styles/facts.js +0 -7
  313. package/dist/styles/facts.js.map +0 -1
  314. package/dist/styles/index.d.ts +0 -17
  315. package/dist/styles/index.d.ts.map +0 -1
  316. package/dist/styles/index.js +0 -16
  317. package/dist/styles/index.js.map +0 -1
  318. package/dist/styles/layout.d.ts +0 -2
  319. package/dist/styles/layout.d.ts.map +0 -1
  320. package/dist/styles/layout.js +0 -30
  321. package/dist/styles/layout.js.map +0 -1
  322. package/dist/styles/legend.d.ts +0 -2
  323. package/dist/styles/legend.d.ts.map +0 -1
  324. package/dist/styles/legend.js +0 -8
  325. package/dist/styles/legend.js.map +0 -1
  326. package/dist/styles/meter.d.ts +0 -3
  327. package/dist/styles/meter.d.ts.map +0 -1
  328. package/dist/styles/meter.js +0 -9
  329. package/dist/styles/meter.js.map +0 -1
  330. package/dist/styles/motion.d.ts +0 -2
  331. package/dist/styles/motion.d.ts.map +0 -1
  332. package/dist/styles/motion.js +0 -27
  333. package/dist/styles/motion.js.map +0 -1
  334. package/dist/styles/prose.d.ts +0 -3
  335. package/dist/styles/prose.d.ts.map +0 -1
  336. package/dist/styles/prose.js +0 -59
  337. package/dist/styles/prose.js.map +0 -1
  338. package/dist/styles/reset.d.ts +0 -2
  339. package/dist/styles/reset.d.ts.map +0 -1
  340. package/dist/styles/reset.js +0 -13
  341. package/dist/styles/reset.js.map +0 -1
  342. package/dist/styles/status.d.ts +0 -2
  343. package/dist/styles/status.d.ts.map +0 -1
  344. package/dist/styles/status.js +0 -52
  345. package/dist/styles/status.js.map +0 -1
  346. package/dist/styles/text.d.ts +0 -2
  347. package/dist/styles/text.d.ts.map +0 -1
  348. package/dist/styles/text.js +0 -21
  349. package/dist/styles/text.js.map +0 -1
  350. package/dist/styles/theme-sheet.d.ts +0 -12
  351. package/dist/styles/theme-sheet.d.ts.map +0 -1
  352. package/dist/styles/theme-sheet.js +0 -24
  353. package/dist/styles/theme-sheet.js.map +0 -1
  354. package/dist/styles/themes.d.ts +0 -75
  355. package/dist/styles/themes.d.ts.map +0 -1
  356. package/dist/styles/themes.js +0 -452
  357. package/dist/styles/themes.js.map +0 -1
  358. package/dist/styles/tokens.d.ts +0 -8
  359. package/dist/styles/tokens.d.ts.map +0 -1
  360. package/dist/styles/tokens.js +0 -32
  361. package/dist/styles/tokens.js.map +0 -1
  362. package/dist/themes/boot.d.ts +0 -7
  363. package/dist/themes/boot.d.ts.map +0 -1
  364. package/dist/themes/boot.js +0 -14
  365. package/dist/themes/boot.js.map +0 -1
  366. package/dist/themes/choice.d.ts +0 -16
  367. package/dist/themes/choice.d.ts.map +0 -1
  368. package/dist/themes/choice.js +0 -45
  369. package/dist/themes/choice.js.map +0 -1
  370. package/dist/themes/data.d.ts +0 -42
  371. package/dist/themes/data.d.ts.map +0 -1
  372. package/dist/themes/data.js +0 -356
  373. package/dist/themes/data.js.map +0 -1
  374. package/dist/themes/index.d.ts +0 -5
  375. package/dist/themes/index.d.ts.map +0 -1
  376. package/dist/themes/index.js +0 -8
  377. package/dist/themes/index.js.map +0 -1
  378. package/dist/themes/sheet.d.ts +0 -12
  379. package/dist/themes/sheet.d.ts.map +0 -1
  380. package/dist/themes/sheet.js +0 -24
  381. package/dist/themes/sheet.js.map +0 -1
  382. package/dist/themes/tokens-list.d.ts +0 -15
  383. package/dist/themes/tokens-list.d.ts.map +0 -1
  384. package/dist/themes/tokens-list.js +0 -62
  385. package/dist/themes/tokens-list.js.map +0 -1
  386. package/dist/words/units.d.ts +0 -9
  387. package/dist/words/units.d.ts.map +0 -1
  388. package/dist/words/units.js +0 -12
  389. package/dist/words/units.js.map +0 -1
@@ -1,1431 +0,0 @@
1
- // The overview's arithmetic, apart from its templates: the pipeline graph by
2
- // layer, the KPI counts and the timeline layout. Pure.
3
- import { isLaunched, isPython } from '@contraagent/api';
4
- // --- the pipeline graph, by layer ---------------------------------------------------
5
- //
6
- // The run as a graph of what its flow's code did, one layer at a time. A layer is
7
- // a prefix of the flow path (the names of the functions a task was created in,
8
- // outermost first). At a layer, a task created in the layer's own function is
9
- // shown in a kind node (its plan group); a task created deeper is shown in the
10
- // node of the function the layer's function called on the way (a function node).
11
- // The same rule applies at every depth. Nodes whose call sites sit in the same
12
- // loop of the layer's function are drawn in one loop box, a nested loop's box
13
- // inside it; each box counts its own loop's passes (`flow[].loops[].pass`). Edges
14
- // are the tasks' `after` (data) and `created_from` (created after those results),
15
- // projected onto the layer's nodes; an edge from a task outside the layer is an
16
- // incoming stub. Inside each node, one mark per unit, in the same unit order
17
- // everywhere in the layer.
18
- //
19
- // The drawing is a layered (Sugiyama) layout of the compound graph, by container
20
- // (the layer's top level, then each loop box, a box being one item of its parent):
21
- // layers by longest path, with the edges that go back to a loop's next pass left
22
- // out; a point (`~...`) on every layer a long edge crosses, so no edge runs over a
23
- // node; crossings reduced by barycentre sweeps; then coordinates from the measured
24
- // sizes (`layoutGraph`), every edge routed through those points and bending only in
25
- // the gaps between layers. The order depends on structure only, never on states,
26
- // and keeps a previous order: nodes that appear later are appended to their layer.
27
- const SEVERITY = { failed: 5, running: 4, blocked: 3, pending: 2, skipped: 1, done: 0 };
28
- /** The state a mark of several tasks shows: the most severe of them. */
29
- export const worstState = (nodes) => nodes.reduce((a, n) => (SEVERITY[n.state] > SEVERITY[a] ? n.state : a), 'done');
30
- const worstOf = (states) => states.reduce((a, s) => (SEVERITY[s] > SEVERITY[a] ? s : a), 'done');
31
- /** A layer segment that enters a loop box: `loop:<line>`, `loop:<outer>.<inner>` for a nested one. */
32
- export const LOOP_SEGMENT = 'loop:';
33
- const isStr = (x) => typeof x === 'string';
34
- function readFrames(n) {
35
- const f = n.flow;
36
- if (!Array.isArray(f))
37
- return [];
38
- const out = [];
39
- for (const x of f) {
40
- const r = x;
41
- if (!r || typeof r !== 'object' || !isStr(r.function))
42
- break;
43
- const read = (Array.isArray(r.loops) ? r.loops : []).filter((l) => !!l && typeof l.line === 'number');
44
- out.push({
45
- function: r.function,
46
- file: isStr(r.file) ? r.file : '',
47
- line: typeof r.line === 'number' ? r.line : 0,
48
- loops: read.map((l) => l.line),
49
- // A pass not counted (null) reads as the first.
50
- passes: read.map((l) => (typeof l.pass === 'number' && l.pass >= 1 ? l.pass : 1)),
51
- });
52
- }
53
- return out;
54
- }
55
- function readCalls(plan) {
56
- const cs = plan.flow_calls;
57
- if (!Array.isArray(cs) || cs.length === 0)
58
- return null; // no flow call recorded
59
- return new Map(cs.filter((c) => c && isStr(c.id) && isStr(c.function)).map((c) => [c.id, c]));
60
- }
61
- const indexes = new WeakMap();
62
- function indexOf(plan) {
63
- const had = indexes.get(plan);
64
- if (had)
65
- return had;
66
- const tasks = plan.nodes.map((n) => {
67
- const frames = readFrames(n);
68
- const from = n.created_from;
69
- return {
70
- n,
71
- frames,
72
- path: frames.map((f) => f.function),
73
- from: Array.isArray(from) ? from.filter(isStr) : [],
74
- call: isStr(n.flow_call) ? n.flow_call : '',
75
- };
76
- });
77
- const unitRank = new Map();
78
- for (const n of plan.nodes)
79
- if (n.unit && !unitRank.has(n.unit))
80
- unitRank.set(n.unit, unitRank.size);
81
- const ix = {
82
- tasks,
83
- byKey: new Map(tasks.map((t) => [t.n.key, t])),
84
- calls: readCalls(plan),
85
- unitRank,
86
- labels: new Map(plan.groups.map((g) => [g.id, g.label])),
87
- };
88
- indexes.set(plan, ix);
89
- return ix;
90
- }
91
- const startsWith = (path, prefix) => prefix.every((p, i) => path[i] === p);
92
- /** Who runs a kind node's tasks: a coding agent, Python, or both (`mixed`). */
93
- function executorOf(ns) {
94
- const py = ns.filter(isPython).length;
95
- return py === 0 ? 'agent' : py === ns.length ? 'python' : 'mixed';
96
- }
97
- /** A function's name without its module. */
98
- export const shortName = (fn) => fn.slice(fn.lastIndexOf('.') + 1) || fn;
99
- /** The loops around a task's call site at depth `d`, outermost first ([] outside any). */
100
- const loopsAt = (t, d) => (d > 0 ? (t.frames[d - 1]?.loops ?? []) : []);
101
- /** The pass of each of those loops, outermost first: the task's place in them. */
102
- const passesAt = (t, d) => (d > 0 ? (t.frames[d - 1]?.passes ?? []) : []);
103
- /** Two pass lists in the order the passes ran: the outer loop's first. */
104
- function cmpPasses(a, b) {
105
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
106
- const d = (a[i] ?? 0) - (b[i] ?? 0);
107
- if (d)
108
- return d;
109
- }
110
- return 0;
111
- }
112
- /** The loop box ids for a loop chain, outermost first: `loop:7`, then `loop:7.9`, ... */
113
- export const boxChain = (loops) => loops.map((_, i) => `${LOOP_SEGMENT}${loops.slice(0, i + 1).join('.')}`);
114
- /** Where a task is shown at depth `d`: its kind node, or the node of the function it was created under. */
115
- function placeOf(t, d) {
116
- const loops = loopsAt(t, d);
117
- const at = loops.length ? `@${loops.join('.')}` : '';
118
- if (t.path.length <= d)
119
- return { id: `kind:${t.n.group}${at}`, type: 'kind', name: t.n.group, loops };
120
- const name = t.path[d];
121
- return { id: `flow:${name}${at}`, type: 'flow', name, loops };
122
- }
123
- /**
124
- * The flow call a function node stands for, for a task at depth `d`: the call that
125
- * started at frame `d` (`own`), else the call the frame runs in (`yield from`). With
126
- * no flow calls in the plan, each frame of the task's call chain is taken to start one.
127
- */
128
- function instanceOf(t, d, calls) {
129
- if (!t.call)
130
- return { id: `>${d}`, own: false };
131
- const parts = t.call.split('.');
132
- const chain = parts.map((_, i) => parts.slice(0, i + 1).join('.'));
133
- let owner = 0;
134
- let start = 0;
135
- if (calls) {
136
- for (let j = 1; j < chain.length; j++) {
137
- const fn = calls.get(chain[j])?.function;
138
- const idx = fn === undefined ? -1 : t.frames.findIndex((f, i) => i > start && f.function === fn);
139
- if (idx < 0 || idx > d)
140
- break;
141
- owner = j;
142
- start = idx;
143
- }
144
- }
145
- else {
146
- owner = Math.min(d, chain.length - 1);
147
- start = owner;
148
- }
149
- const id = chain[owner];
150
- return start === d ? { id, own: true } : { id: `${id}>${d}`, own: false };
151
- }
152
- const CALL_STATE = { running: 'running', returned: 'done', raised: 'failed', closed: 'failed' };
153
- /** A flow call stopped with an error: it raised, or it was closed. */
154
- const callFailed = (c) => c?.state === 'raised' || c?.state === 'closed';
155
- function taskMark(ts, unit) {
156
- const ns = ts.map((t) => t.n);
157
- const key = (ns.find((n) => n.state === 'running') ?? ns[ns.length - 1] ?? { key: '' }).key;
158
- return { key, unit, state: worstState(ns), count: ns.length, keys: ns.map((n) => n.key) };
159
- }
160
- const push = (m, k, v) => {
161
- const xs = m.get(k);
162
- if (xs)
163
- xs.push(v);
164
- else
165
- m.set(k, [v]);
166
- };
167
- /** A kind node's marks: one per unit, and one per task without a unit. */
168
- function kindMarks(ts) {
169
- const byUnit = new Map();
170
- const single = [];
171
- for (const t of ts) {
172
- if (t.n.unit)
173
- push(byUnit, t.n.unit, t);
174
- else
175
- single.push(taskMark([t], ''));
176
- }
177
- return { byUnit: new Map([...byUnit].map(([u, xs]) => [u, taskMark(xs, u)])), single };
178
- }
179
- function instances(ts, d, calls) {
180
- const by = new Map();
181
- for (const t of ts) {
182
- const { id, own } = instanceOf(t, d, calls);
183
- const x = by.get(id) ?? by.set(id, { id, own, tasks: [], unit: '' }).get(id);
184
- x?.tasks.push(t);
185
- }
186
- for (const x of by.values()) {
187
- const us = new Set(x.tasks.map((t) => t.n.unit || ''));
188
- x.unit = us.size === 1 ? [...us][0] : '';
189
- }
190
- return [...by.values()];
191
- }
192
- /**
193
- * A function node's marks: its flow calls, one mark per unit when a call's tasks share one unit, else one per call.
194
- * A call's state is the most severe of its own (raised or closed: failed) and its tasks' (those of the calls it
195
- * started included), so a call that caught a task's failure and returned still shows it.
196
- */
197
- function flowMarks(ts, d, name, calls) {
198
- const markOf = (xs, unit) => {
199
- const own = xs.map((x) => (x.own ? calls?.get(x.id) : undefined));
200
- const states = xs.map((x, i) => {
201
- const c = own[i];
202
- const tasks = worstState(x.tasks.map((t) => t.n));
203
- return c && CALL_STATE[c.state] ? worstOf([CALL_STATE[c.state], tasks]) : tasks;
204
- });
205
- const tasks = xs.flatMap((x) => x.tasks);
206
- const keys = tasks.map((t) => t.n.key);
207
- const last = xs.find((_, i) => states[i] === 'running') ?? xs[xs.length - 1];
208
- const failed = own.find(callFailed);
209
- const mark = { key: last?.id ?? '', unit, state: worstOf(states), count: keys.length, keys, flow: name, counts: taskCounts(tasks) };
210
- return failed ? { ...mark, error: failed.error ?? '' } : mark;
211
- };
212
- const byUnit = new Map();
213
- const single = [];
214
- const xs = instances(ts, d, calls);
215
- for (const x of xs) {
216
- if (x.unit)
217
- push(byUnit, x.unit, x);
218
- else
219
- single.push(markOf([x], ''));
220
- }
221
- const errors = calls ? xs.filter((x) => x.own && callFailed(calls.get(x.id))).length : 0;
222
- return { byUnit: new Map([...byUnit].map(([u, ys]) => [u, markOf(ys, u)])), single, calls: xs.length, errors };
223
- }
224
- /** Tasks by state. */
225
- function taskCounts(ts) {
226
- const c = { total: ts.length, done: 0, failed: 0, running: 0, pending: 0, skipped: 0, blocked: 0 };
227
- for (const t of ts)
228
- c[t.n.state] += 1;
229
- return c;
230
- }
231
- /** The tasks of each unit's latest pass, by the passes of the loops around them at depth `d` (tasks without a unit count as one unit). */
232
- function latestPass(ts, d) {
233
- const most = new Map();
234
- for (const t of ts) {
235
- const u = t.n.unit || '';
236
- const had = most.get(u);
237
- if (!had || cmpPasses(passesAt(t, d), had) > 0)
238
- most.set(u, passesAt(t, d));
239
- }
240
- return ts.filter((t) => cmpPasses(passesAt(t, d), most.get(t.n.unit || '') ?? []) === 0);
241
- }
242
- /** How many passes each unit's tasks in a node ran in (distinct pass lists at depth `d`). */
243
- function passesByUnit(ts, d) {
244
- const seen = new Map();
245
- for (const t of ts) {
246
- const u = t.n.unit || '';
247
- (seen.get(u) ?? seen.set(u, new Set()).get(u))?.add(passesAt(t, d).join('.'));
248
- }
249
- return new Map([...seen].map(([u, s]) => [u, s.size]));
250
- }
251
- /** A mark with its pass count, when it is more than 1. */
252
- const withPasses = (m, n) => (n && n > 1 ? { ...m, passes: n } : m);
253
- // ---- the order: layers, long-edge points, crossing reduction ----
254
- const pairKey = (a, b) => `${a}\u0000${b}`;
255
- /**
256
- * Each id's layer by longest path. Edges that close a cycle (found depth first, in
257
- * `ids` order) are left out; `fixed` ids keep their layer.
258
- */
259
- function rankLayers(ids, edges, fixed) {
260
- const known = new Set(ids);
261
- const all = new Map(ids.map((id) => [id, []]));
262
- for (const [a, b] of edges)
263
- if (a !== b && known.has(a) && known.has(b) && !all.get(a)?.includes(b))
264
- all.get(a)?.push(b);
265
- const back = new Set();
266
- const seen = new Map();
267
- for (const root of ids) {
268
- if (seen.has(root))
269
- continue;
270
- seen.set(root, 1);
271
- const stack = [[root, 0]];
272
- while (stack.length) {
273
- const top = stack[stack.length - 1];
274
- const ss = all.get(top[0]) ?? [];
275
- if (top[1] < ss.length) {
276
- const s = ss[top[1]++];
277
- const st = seen.get(s);
278
- if (st === 1)
279
- back.add(pairKey(top[0], s));
280
- else if (!st) {
281
- seen.set(s, 1);
282
- stack.push([s, 0]);
283
- }
284
- }
285
- else {
286
- seen.set(top[0], 2);
287
- stack.pop();
288
- }
289
- }
290
- }
291
- const succ = new Map(ids.map((id) => [id, []]));
292
- const pred = new Map(ids.map((id) => [id, []]));
293
- for (const [a, ss] of all) {
294
- for (const b of ss) {
295
- if (back.has(pairKey(a, b)))
296
- continue;
297
- succ.get(a)?.push(b);
298
- pred.get(b)?.push(a);
299
- }
300
- }
301
- // Longest path, in topological order (Kahn's).
302
- const indeg = new Map(ids.map((id) => [id, pred.get(id)?.length ?? 0]));
303
- const queue = ids.filter((id) => !indeg.get(id));
304
- const layer = new Map();
305
- for (let q = queue.shift(); q !== undefined; q = queue.shift()) {
306
- layer.set(q, fixed.get(q) ?? Math.max(0, ...(pred.get(q) ?? []).map((p) => (layer.get(p) ?? 0) + 1)));
307
- for (const s of succ.get(q) ?? []) {
308
- indeg.set(s, (indeg.get(s) ?? 0) - 1);
309
- if (!indeg.get(s))
310
- queue.push(s);
311
- }
312
- }
313
- return layer;
314
- }
315
- /** Pairs (a, b) between adjacent layers that cross, for the order `pos`. */
316
- function crossings(rows, links, pos, layerOf) {
317
- let n = 0;
318
- const by = new Map();
319
- for (const [a, b] of links) {
320
- const l = layerOf.get(a);
321
- if (l === undefined)
322
- continue;
323
- push(by, l, [pos.get(a) ?? 0, pos.get(b) ?? 0]);
324
- }
325
- for (const ls of by.values())
326
- for (let i = 0; i < ls.length; i++)
327
- for (let j = i + 1; j < ls.length; j++) {
328
- const [a1, b1] = ls[i];
329
- const [a2, b2] = ls[j];
330
- if ((a1 - a2) * (b1 - b2) < 0)
331
- n++;
332
- }
333
- return rows.length ? n : 0;
334
- }
335
- /** True for a point where an edge crosses a layer. */
336
- export const isPoint = (id) => id.startsWith('~');
337
- /**
338
- * The order within each layer that reduces crossings: barycentre sweeps, down and
339
- * up, four times, keeping the best. `links` join a slot to one in the next layer.
340
- * With `keep`, those ids keep their order (a previous layout's, new ones after
341
- * them) and only the other slots (the points) move between them.
342
- */
343
- function orderRows(rows, links, keep) {
344
- const layerOf = new Map();
345
- rows.forEach((row, i) => row.forEach((id) => layerOf.set(id, i)));
346
- const pred = new Map();
347
- const succ = new Map();
348
- for (const [a, b] of links) {
349
- push(succ, a, b);
350
- push(pred, b, a);
351
- }
352
- const pos = new Map();
353
- const fix = (row) => row.forEach((id, i) => pos.set(id, i));
354
- const cur = rows.map((r) => [...r]);
355
- cur.forEach(fix);
356
- const score = () => crossings(cur, links, pos, layerOf);
357
- let best = cur.map((r) => [...r]);
358
- let bestN = score();
359
- const sortRow = (row, near) => {
360
- const bc = new Map();
361
- for (const id of row) {
362
- const ps = (near.get(id) ?? []).map((p) => pos.get(p)).filter((x) => x !== undefined);
363
- bc.set(id, ps.length ? ps.reduce((a, b) => a + b, 0) / ps.length : (pos.get(id) ?? 0));
364
- }
365
- const at = (id) => bc.get(id) ?? 0;
366
- if (!keep)
367
- return [...row].sort((a, b) => at(a) - at(b) || (pos.get(a) ?? 0) - (pos.get(b) ?? 0));
368
- // The kept ids stay in their order; each point goes before the first kept id it is left of.
369
- const fixed = row.filter((id) => !isPoint(id));
370
- const points = row.filter(isPoint).sort((a, b) => at(a) - at(b) || (pos.get(a) ?? 0) - (pos.get(b) ?? 0));
371
- const out = [];
372
- let p = 0;
373
- for (const id of fixed) {
374
- while (p < points.length && at(points[p]) < at(id))
375
- out.push(points[p++]);
376
- out.push(id);
377
- }
378
- return [...out, ...points.slice(p)];
379
- };
380
- for (let sweep = 0; sweep < 4; sweep++) {
381
- const down = sweep % 2 === 0;
382
- const order = down ? cur.map((_, i) => i).slice(1) : cur.map((_, i) => i).reverse().slice(1);
383
- for (const i of order) {
384
- const row = sortRow(cur[i], down ? pred : succ);
385
- cur[i] = row;
386
- fix(row);
387
- }
388
- const n = score();
389
- if (n < bestN) {
390
- bestN = n;
391
- best = cur.map((r) => [...r]);
392
- }
393
- }
394
- return score() <= bestN ? cur : best;
395
- }
396
- function project(plan, path, prev, down = false) {
397
- const ix = indexOf(plan);
398
- const d = path.length;
399
- const inside = ix.tasks.filter((t) => startsWith(t.path, path));
400
- const drafts = new Map();
401
- const nodeOfKey = new Map();
402
- for (const t of inside) {
403
- const p = placeOf(t, d);
404
- const x = drafts.get(p.id) ?? drafts.set(p.id, { ...p, tasks: [] }).get(p.id);
405
- x?.tasks.push(t);
406
- nodeOfKey.set(t.n.key, p.id);
407
- }
408
- const marks = new Map();
409
- const shownOf = new Map(); // node id -> the tasks it counts
410
- const boxOf = new Map(); // node id -> its innermost loop box
411
- const boxParent = new Map(); // loop box -> the box it nests in
412
- for (const x of drafts.values()) {
413
- const chain = boxChain(x.loops);
414
- const inner = chain[chain.length - 1] ?? null;
415
- chain.forEach((box, i) => boxParent.set(box, chain[i - 1] ?? null));
416
- if (inner)
417
- boxOf.set(x.id, inner);
418
- // In a loop box, a unit's mark is its latest pass, and counts the passes it ran in.
419
- const shown = inner ? latestPass(x.tasks, d) : x.tasks;
420
- shownOf.set(x.id, shown);
421
- const m = x.type === 'kind' ? kindMarks(shown) : flowMarks(shown, d, x.name, ix.calls);
422
- if (inner) {
423
- const per = passesByUnit(x.tasks, d);
424
- m.byUnit = new Map([...m.byUnit].map(([u, mk]) => [u, withPasses(mk, per.get(u))]));
425
- }
426
- marks.set(x.id, m);
427
- }
428
- const units = [...new Set([...marks.values()].flatMap((m) => [...m.byUnit.keys()]))].sort((a, b) => (ix.unitRank.get(a) ?? 0) - (ix.unitRank.get(b) ?? 0));
429
- // Each box's own loop: its last pass, and the passes each unit ran of it (its
430
- // distinct passes, those of the loops around it included).
431
- const boxLast = new Map();
432
- const boxRan = new Map();
433
- for (const x of drafts.values()) {
434
- const chain = boxChain(x.loops);
435
- for (const t of x.tasks) {
436
- const ps = passesAt(t, d);
437
- chain.forEach((box, i) => {
438
- boxLast.set(box, Math.max(boxLast.get(box) ?? 1, ps[i] ?? 1));
439
- const per = boxRan.get(box) ?? boxRan.set(box, new Map()).get(box);
440
- const u = t.n.unit || '';
441
- (per?.get(u) ?? per?.set(u, new Set()).get(u))?.add(ps.slice(0, i + 1).join('.'));
442
- });
443
- }
444
- }
445
- // The loop boxes an id sits in, innermost first (for a box id, itself first).
446
- const boxPath = (id) => {
447
- const out = [];
448
- let b = id.startsWith(LOOP_SEGMENT) ? id : (boxOf.get(id) ?? null);
449
- while (b) {
450
- out.push(b);
451
- b = boxParent.get(b) ?? null;
452
- }
453
- return out;
454
- };
455
- // The direct child of `container` (a box, or '' for the layer's top level) that holds a node `id`.
456
- const itemIn = (id, container) => {
457
- const bp = boxPath(id);
458
- if (container === '')
459
- return bp[bp.length - 1] ?? id;
460
- const i = bp.indexOf(container);
461
- return i < 0 ? undefined : i === 0 ? id : bp[i - 1];
462
- };
463
- // The deepest loop box holding both ends of an edge (the layer's top level when none).
464
- const commonBox = (a, b) => {
465
- const bp = boxPath(a);
466
- return bp.find((x) => boxPath(b).includes(x)) ?? '';
467
- };
468
- // The container a slot is in: a stub's is the top level.
469
- const containerOf = (id) => (id.startsWith('stub:') ? '' : (boxOf.get(id) ?? ''));
470
- // Edges, projected onto the layer's nodes; from outside it, stubs. An edge whose
471
- // every task joins a later pass of a loop around both ends goes back to the next pass.
472
- const inOrder = new Set();
473
- const pairs = new Map();
474
- const stubs = new Map();
475
- const outside = (o) => {
476
- let c = 0;
477
- while (c < d && o.path[c] === path[c])
478
- c++;
479
- const p = placeOf(o, c);
480
- return { id: `stub:${c}:${p.id}`, label: p.type === 'kind' ? (ix.labels.get(p.name) ?? p.name) : shortName(p.name) };
481
- };
482
- for (const t of inside) {
483
- const to = nodeOfKey.get(t.n.key);
484
- const link = (dep, type) => {
485
- const from = nodeOfKey.get(dep);
486
- if (from === to) {
487
- if (type === 'after')
488
- inOrder.add(to);
489
- return;
490
- }
491
- if (from !== undefined) {
492
- const o = ix.byKey.get(dep);
493
- const a = loopsAt(o, d);
494
- const b = loopsAt(t, d);
495
- let k = 0;
496
- while (k < a.length && a[k] === b[k])
497
- k++;
498
- const same = cmpPasses(passesAt(o, d).slice(0, k), passesAt(t, d).slice(0, k)) === 0;
499
- const key = `${from}\u0000${to}\u0000${type}`;
500
- const e = pairs.get(key) ?? pairs.set(key, { from, to, type, tasks: new Set(), samePass: false }).get(key);
501
- e?.tasks.add(t.n.key);
502
- if (e && same)
503
- e.samePass = true;
504
- return;
505
- }
506
- const o = ix.byKey.get(dep);
507
- if (!o)
508
- return; // not in the plan yet
509
- const s = outside(o);
510
- const k = `${s.id}\u0000${to}\u0000${type}`;
511
- (stubs.get(k) ?? stubs.set(k, { ...s, to, type, tasks: new Set() }).get(k))?.tasks.add(t.n.key);
512
- };
513
- for (const dep of t.n.after ?? [])
514
- link(dep, 'after');
515
- for (const dep of t.from)
516
- link(dep, 'created');
517
- }
518
- // A created_from edge is drawn only where no after edge joins the same two.
519
- const all = [...pairs.values()].filter((e) => e.type === 'after' || !pairs.has(`${e.from}\u0000${e.to}\u0000after`));
520
- const stubList = [...stubs.values()].filter((s) => s.type === 'after' || !stubs.has(`${s.id}\u0000${s.to}\u0000after`));
521
- // Layers, by container: the layer's top level, then each loop box (its own nodes
522
- // and the loop boxes nested in it). Each box is one item of its parent.
523
- const containers = ['', ...boxParent.keys()];
524
- const kidsOf = new Map(containers.map((c) => [c, []]));
525
- for (const id of drafts.keys()) {
526
- for (const c of containers) {
527
- const it = itemIn(id, c);
528
- if (it !== undefined && !kidsOf.get(c)?.includes(it))
529
- kidsOf.get(c)?.push(it);
530
- }
531
- }
532
- const lay = new Map();
533
- for (const c of containers) {
534
- const kids = kidsOf.get(c) ?? [];
535
- const prevRows = c === '' ? prev?.layers : prev?.boxes[c];
536
- const fixed = new Map();
537
- prevRows?.forEach((row, i) => row.forEach((id) => kids.includes(id) && fixed.set(id, i)));
538
- // An edge back to a loop's next pass does not set the layers.
539
- const es = all
540
- .filter((e) => e.samePass && commonBox(e.from, e.to) === c)
541
- .map((e) => [itemIn(e.from, c), itemIn(e.to, c)]);
542
- lay.set(c, rankLayers(kids, es, fixed));
543
- }
544
- // Stubs, in the top level: in the layer before the first they lead to (a new first layer when that is the first).
545
- const top = lay.get('');
546
- const stubIds = [...new Set(stubList.map((s) => s.id))];
547
- const prevTop = new Map();
548
- prev?.layers.forEach((row, i) => row.forEach((id) => prevTop.set(id, i)));
549
- const stubAt = new Map(stubIds.map((id) => [id, prevTop.get(id) ?? Math.min(...stubList.filter((s) => s.id === id).map((s) => top.get(itemIn(s.to, '')) ?? 0)) - 1]));
550
- if ([...stubAt.values()].some((l) => l < 0)) {
551
- for (const [id, l] of top)
552
- top.set(id, l + 1);
553
- for (const [id, l] of stubAt)
554
- stubAt.set(id, l + 1);
555
- }
556
- for (const [id, l] of stubAt)
557
- top.set(id, l);
558
- const layerOf = (c, id) => lay.get(c)?.get(id) ?? 0;
559
- const lastLayer = (c) => Math.max(0, ...(lay.get(c)?.values() ?? []));
560
- // Every edge whose ends are not in a later layer than its start goes back: not drawn.
561
- const isBack = (from, to) => {
562
- const c = commonBox(from, to);
563
- return layerOf(c, itemIn(from, c)) >= layerOf(c, itemIn(to, c));
564
- };
565
- const edges = all.map((e) => ({ from: e.from, to: e.to, tasks: e.tasks.size, type: e.type, back: isBack(e.from, e.to) }));
566
- // Each container's rows: its items by layer, a previous order kept, new items after it.
567
- const rows = new Map();
568
- for (const c of containers) {
569
- const prevRows = c === '' ? prev?.layers : prev?.boxes[c];
570
- const kept = (prevRows ?? []).flat();
571
- const ids = [...(c === '' ? [...stubIds] : []), ...(kidsOf.get(c) ?? [])];
572
- ids.sort((a, b) => {
573
- const ka = kept.indexOf(a);
574
- const kb = kept.indexOf(b);
575
- return (ka < 0 ? Infinity : ka) - (kb < 0 ? Infinity : kb);
576
- });
577
- const r = Array.from({ length: lastLayer(c) + 1 }, () => []);
578
- for (const id of ids)
579
- r[layerOf(c, id)]?.push(id);
580
- rows.set(c, r);
581
- }
582
- // Routes: the drawn edges through points on the layers they cross. The points of
583
- // edges from one place (of one type) are shared, so their lines run as one.
584
- const routes = [];
585
- const points = new Set();
586
- const point = (c, origin, type, l) => {
587
- const id = `~${c}|${origin}|${type}|${l}`;
588
- if (!points.has(id)) {
589
- points.add(id);
590
- rows.get(c)?.[l]?.push(id);
591
- }
592
- return { id, in: c };
593
- };
594
- // A point beside a box, in its layer of the container around it: an edge that leaves or
595
- // enters the box through its side, between two of its layers, passes there.
596
- const byPoint = (c, box, origin, type) => {
597
- const l = layerOf(c, box);
598
- const id = `~${c}|by:${box}|${origin}|${type}|${l}`;
599
- if (!points.has(id)) {
600
- points.add(id);
601
- rows.get(c)?.[l]?.push(id);
602
- }
603
- return { id, in: c };
604
- };
605
- // An edge enters a box at its near side only to reach the box's first layer (and,
606
- // top to bottom, not even then: the box's label is on that side); else through its
607
- // side, next to the layer it goes to. It leaves at the far side only from the box's
608
- // last layer, else through its side. So it runs inside a box only where it starts or ends.
609
- const route = (from, to, type) => {
610
- const c = from.startsWith('stub:') ? '' : commonBox(from, to);
611
- let curIn = containerOf(from);
612
- let cur = from;
613
- const steps = [{ id: from, in: curIn }];
614
- // Out of the boxes around the source, up to the common container.
615
- while (curIn !== c) {
616
- const up = boxParent.get(curIn) ?? '';
617
- steps.push({ id: curIn, in: up });
618
- if (layerOf(curIn, cur) < lastLayer(curIn))
619
- steps.push(byPoint(up, curIn, cur, type));
620
- cur = curIn;
621
- curIn = up;
622
- }
623
- // Across the common container, then into the boxes around the target.
624
- const target = itemIn(to, c);
625
- for (let l = layerOf(c, cur) + 1; l < layerOf(c, target); l++)
626
- steps.push(point(c, cur, type, l));
627
- const bp = boxPath(to);
628
- const inward = bp.slice(0, c === '' ? bp.length : bp.indexOf(c)).reverse();
629
- const origin = `>${c}:${cur}`;
630
- let k = c;
631
- for (const box of inward) {
632
- const n = itemIn(to, box);
633
- if (layerOf(box, n) > 0 || down)
634
- steps.push(byPoint(k, box, origin, type));
635
- steps.push({ id: box, in: k });
636
- k = box;
637
- }
638
- steps.push({ id: to, in: k });
639
- return steps;
640
- };
641
- for (const e of edges)
642
- if (!e.back)
643
- routes.push({ from: e.from, to: e.to, type: e.type, steps: route(e.from, e.to, e.type) });
644
- for (const s of stubList)
645
- routes.push({ from: s.id, to: s.to, type: s.type, steps: route(s.id, s.to, s.type) });
646
- // The order within each layer.
647
- const slots = {};
648
- for (const c of containers) {
649
- const links = [];
650
- const lv = new Map((rows.get(c) ?? []).flatMap((r, i) => r.map((id) => [id, i])));
651
- for (const r of routes)
652
- for (let i = 1; i < r.steps.length; i++) {
653
- const a = r.steps[i - 1];
654
- const b = r.steps[i];
655
- if (a.in === c && b.in === c && lv.get(a.id) !== lv.get(b.id))
656
- links.push([a.id, b.id]);
657
- }
658
- const hasPrev = c === '' ? !!prev : !!prev?.boxes[c];
659
- // A point beside a box is right after it.
660
- slots[c] = orderRows(rows.get(c) ?? [], links, hasPrev).map((row) => {
661
- const by = row.filter((id) => id.includes('|by:'));
662
- const rest = row.filter((id) => !id.includes('|by:'));
663
- return rest.flatMap((id) => [id, ...by.filter((p) => p.includes(`|by:${id}|`))]);
664
- });
665
- }
666
- const real = (r) => r.map((row) => row.filter((id) => !isPoint(id)));
667
- const boxes = [...boxParent.keys()].map((id) => {
668
- const spread = new Map();
669
- for (const [u, ran] of boxRan.get(id) ?? [])
670
- if (u)
671
- spread.set(ran.size, (spread.get(ran.size) ?? 0) + 1);
672
- return {
673
- id,
674
- loop: Number(id.slice(LOOP_SEGMENT.length).split('.').pop()),
675
- file: inside[0]?.frames[d - 1]?.file ?? '',
676
- parent: boxParent.get(id) ?? null,
677
- passes: boxLast.get(id) ?? 1,
678
- spread: [...spread].sort((a, b) => a[0] - b[0]),
679
- layers: real(slots[id] ?? []).filter((r) => r.length),
680
- };
681
- });
682
- const outerRows = real(slots[''] ?? []).map((row) => row.filter((id) => !id.startsWith('stub:')));
683
- const nodeOf = (x) => {
684
- const m = marks.get(x.id);
685
- const hasUnits = m.byUnit.size > 0;
686
- return {
687
- id: x.id,
688
- type: x.type,
689
- name: x.name,
690
- label: x.type === 'kind' ? (ix.labels.get(x.name) ?? x.name) : shortName(x.name),
691
- kinds: [...new Set(x.tasks.map((t) => t.n.kind))],
692
- executor: x.type === 'flow' ? null : executorOf(x.tasks.map((t) => t.n)),
693
- box: boxOf.get(x.id) ?? null,
694
- units: hasUnits ? units.map((u) => m.byUnit.get(u) ?? null) : [],
695
- single: m.single,
696
- inOrder: inOrder.has(x.id),
697
- counts: taskCounts(shownOf.get(x.id) ?? []),
698
- calls: m.calls ?? 0,
699
- errors: m.errors ?? 0,
700
- };
701
- };
702
- const byBox = new Map(boxes.map((b) => [b.id, b]));
703
- // Every node id, in drawing order: expand each loop box to the nodes nested in it.
704
- const expand = (ids) => ids.flatMap((id) => (id.startsWith(LOOP_SEGMENT) ? expand((byBox.get(id)?.layers ?? []).flat()) : [id]));
705
- return {
706
- graph: {
707
- path: [...path],
708
- nodes: expand(outerRows.flat()).map((id) => nodeOf(drafts.get(id))),
709
- layers: outerRows.filter((r) => r.length),
710
- boxes,
711
- edges,
712
- stubs: stubList.map((s) => ({ id: s.id, label: s.label, to: s.to, tasks: s.tasks.size, type: s.type })),
713
- units,
714
- slots,
715
- routes,
716
- order: { layers: real(slots[''] ?? []), boxes: Object.fromEntries(boxes.map((b) => [b.id, real(slots[b.id] ?? [])])) },
717
- },
718
- drafts,
719
- ix,
720
- };
721
- }
722
- /**
723
- * The layer `path` of the plan's flow. Pass the `order` of the last graph of this layer as
724
- * `prev` to keep its nodes in place; `down` routes the edges for a drawing top to bottom
725
- * (a box's label is then on the side its edges come in).
726
- */
727
- export function layerGraph(plan, path = [], prev, down = false) {
728
- return project(plan, path, prev, down).graph;
729
- }
730
- /**
731
- * The layer the graph opens on: from the top, down while a layer has a single
732
- * node and it is a function's. It stops at a single function node whose flow call
733
- * raised or was closed, so a run that failed inside a flow opens on the node that
734
- * shows it, not inside it. A failed or not started task does not stop it: the layer
735
- * below shows that task's own mark. A static flow lands on its kinds.
736
- */
737
- export function autoLayer(plan) {
738
- const ix = indexOf(plan);
739
- const path = [];
740
- // The failure sites: flow calls that raised or were closed with no raised descendant.
741
- // The graph stops on the node that shows one, not inside it. A plan with no flow calls
742
- // cannot tell its failure sites, so it descends as if there were none.
743
- const raised = new Set();
744
- if (ix.calls)
745
- for (const c of ix.calls.values())
746
- if (c.state === 'raised' || c.state === 'closed')
747
- raised.add(c.id);
748
- const isSite = (id) => raised.has(id) && ![...raised].some((r) => r !== id && r.startsWith(`${id}.`));
749
- for (;;) {
750
- let only = null;
751
- let one = true;
752
- const mine = [];
753
- for (const t of ix.tasks) {
754
- if (!startsWith(t.path, path))
755
- continue;
756
- const p = placeOf(t, path.length);
757
- if (p.type === 'kind' || (only && only.id !== p.id)) {
758
- one = false;
759
- break;
760
- }
761
- only = p;
762
- mine.push(t);
763
- }
764
- // Stop before entering a single function node that is itself a failure site.
765
- const atSite = mine.some((t) => isSite(instanceOf(t, path.length, ix.calls).id));
766
- if (!one || !only || atSite)
767
- return path;
768
- path.push(only.name);
769
- }
770
- }
771
- /** A layer's segments: its path, and the loop box entered at its end (null when none). */
772
- export function splitLayer(segments) {
773
- const last = segments[segments.length - 1];
774
- return last?.startsWith(LOOP_SEGMENT) ? { path: segments.slice(0, -1), loop: last } : { path: [...segments], loop: null };
775
- }
776
- /**
777
- * The layer `segments` names, as the plan has it: the segments themselves when the
778
- * plan has that layer (a prefix of a task's flow path, and the loop box after it);
779
- * else, when the last function is named by its `module.qualname` (or, failing that,
780
- * its name) and the plan reaches it by one flow path only, that flow path, with the
781
- * loop box when the layer there has it. Null when the plan has no such layer.
782
- */
783
- export function resolveLayer(plan, segments) {
784
- const { path, loop } = splitLayer(segments);
785
- const ix = indexOf(plan);
786
- const hasLoop = (p) => !loop || layerGraph(plan, p).boxes.some((b) => b.id === loop);
787
- if (ix.tasks.some((t) => startsWith(t.path, path)))
788
- return hasLoop(path) ? [...segments] : null;
789
- const name = path[path.length - 1];
790
- if (name === undefined)
791
- return null;
792
- const found = (match) => {
793
- const by = new Map();
794
- for (const t of ix.tasks)
795
- t.path.forEach((fn, i) => match(fn) && by.set(t.path.slice(0, i + 1).join('/'), t.path.slice(0, i + 1)));
796
- return [...by.values()];
797
- };
798
- const exact = found((fn) => fn === name);
799
- const named = exact.length ? exact : found((fn) => shortName(fn) === name);
800
- const only = named.length === 1 ? named[0] : null;
801
- return only && hasLoop(only) ? (loop ? [...only, loop] : only) : null;
802
- }
803
- /** One level up from `segments`: null at the top (the layer the graph opens on, `auto`), [] for that layer. */
804
- export function parentLayer(segments, auto) {
805
- if (segments.length <= auto.length)
806
- return null;
807
- const up = segments.slice(0, -1);
808
- return up.length <= auto.length ? [] : up;
809
- }
810
- /**
811
- * The loop box `box` of layer `path` with its passes unrolled; null when the layer has
812
- * no such box. Each task is in the column of its node and of its passes of the box's
813
- * loop and of every loop nested in it, so an inner loop's passes are counted within
814
- * each pass of the outer one. A column no task reached is left out.
815
- */
816
- export function passMatrix(plan, path, box, prev) {
817
- const { graph, drafts, ix } = project(plan, path, prev);
818
- const b = graph.boxes.find((x) => x.id === box);
819
- if (!b)
820
- return null;
821
- const d = path.length;
822
- const i0 = b.id.slice(LOOP_SEGMENT.length).split('.').length - 1;
823
- const byBox = new Map(graph.boxes.map((x) => [x.id, x]));
824
- const byNode = new Map(graph.nodes.map((n) => [n.id, n]));
825
- const rel = (t) => passesAt(t, d).slice(i0);
826
- const leaves = (x) => x.layers.flat().flatMap((id) => (byBox.has(id) ? leaves(byBox.get(id)) : [id]));
827
- const tasksOf = (x) => leaves(x).flatMap((id) => drafts.get(id)?.tasks ?? []);
828
- const same = (a, p) => a.length === p.length && cmpPasses(a, p) === 0;
829
- const build = (x, prefix) => {
830
- const k = prefix.length;
831
- const passes = [...new Set(tasksOf(x).map(rel).filter((v) => v.length > k && cmpPasses(v.slice(0, k), prefix) === 0).map((v) => v[k]))].sort((a, c) => a - c);
832
- return passes.flatMap((p) => {
833
- const at = [...prefix, p];
834
- const kids = x.layers.flat().flatMap((id) => {
835
- const inner = byBox.get(id);
836
- if (inner) {
837
- const sub = build(inner, at);
838
- return sub.length ? [{ type: 'loop', box: inner, kids: sub }] : [];
839
- }
840
- const node = byNode.get(id);
841
- return node && drafts.get(id)?.tasks.some((t) => same(rel(t), at)) ? [{ type: 'node', node, passes: at }] : [];
842
- });
843
- return kids.length ? [{ type: 'pass', box: x, pass: p, kids }] : [];
844
- });
845
- };
846
- const tree = build(b, []);
847
- const columns = [];
848
- const width = (t) => (t.type === 'node' ? 1 : t.kids.reduce((a, k) => a + width(k), 0));
849
- const depthOf = (t) => (t.type === 'node' ? 1 : 1 + Math.max(0, ...t.kids.map(depthOf)));
850
- const rowsN = Math.max(1, ...tree.map(depthOf));
851
- const head = Array.from({ length: rowsN }, () => []);
852
- const walk = (t, level) => {
853
- const row = head[level];
854
- if (t.type === 'node') {
855
- row.push({ type: 'node', node: t.node, passes: t.passes, span: 1, rows: rowsN - level });
856
- columns.push({ node: t.node, passes: t.passes });
857
- return;
858
- }
859
- row.push(t.type === 'pass' ? { type: 'pass', box: t.box, pass: t.pass, span: width(t), rows: 1 } : { type: 'loop', box: t.box, span: width(t), rows: 1 });
860
- for (const k of t.kids)
861
- walk(k, level + 1);
862
- };
863
- for (const t of tree)
864
- walk(t, 0);
865
- const mine = tasksOf(b);
866
- const unitsIn = new Set(mine.map((t) => t.n.unit || ''));
867
- const rowUnits = [...graph.units.filter((u) => unitsIn.has(u)), ...(unitsIn.has('') ? [''] : [])];
868
- const cellTasks = new Map(); // `${column}\0${unit}`
869
- columns.forEach((c, ci) => {
870
- for (const t of drafts.get(c.node.id)?.tasks ?? [])
871
- if (same(rel(t), c.passes))
872
- push(cellTasks, `${ci}\u0000${t.n.unit || ''}`, t);
873
- });
874
- const markOf = (c, ts, unit) => {
875
- const x = drafts.get(c.node.id);
876
- if (x.type === 'kind')
877
- return taskMark(ts, unit);
878
- const m = flowMarks(ts, d, x.name, ix.calls);
879
- return unit ? (m.byUnit.get(unit) ?? m.single[0] ?? null) : (m.single[0] ?? null);
880
- };
881
- const rows = rowUnits.map((unit) => {
882
- const cells = columns.map((c, ci) => {
883
- const ts = cellTasks.get(`${ci}\u0000${unit}`);
884
- return ts?.length ? markOf(c, ts, unit) : null;
885
- });
886
- const passes = new Set(mine.filter((t) => (t.n.unit || '') === unit).map((t) => passesAt(t, d).slice(0, i0 + 1).join('.'))).size;
887
- return { unit, passes, state: worstOf(cells.filter((m) => m !== null).map((m) => m.state)), cells };
888
- });
889
- return { box: b, columns, head, rows };
890
- }
891
- /** `rows` sorted by `by`; a tie keeps the scheduled order. */
892
- export function sortRows(rows, by) {
893
- const ix = new Map(rows.map((r, i) => [r, i]));
894
- const key = (r) => (by === 'passes' ? -r.passes : by === 'state' ? -SEVERITY[r.state] : 0);
895
- return [...rows].sort((a, b) => key(a) - key(b) || (ix.get(a) ?? 0) - (ix.get(b) ?? 0));
896
- }
897
- /**
898
- * Where a key moves in a grid of marks (`has[r][c]`: a mark there), from (`r`, `c`):
899
- * left and right to the next mark in the row, up and down to the nearest row with a
900
- * mark in the column, Home and End to the row's first and last, with Ctrl to the
901
- * first and last row's, PageUp and PageDown `page` rows. Null when it does not move.
902
- */
903
- export function gridStep(has, r, c, key, ctrl = false, page = 10) {
904
- const at = (i, j) => has[i]?.[j] === true;
905
- const first = (i) => (has[i] ?? []).findIndex(Boolean);
906
- const last = (i) => (has[i] ?? []).lastIndexOf(true);
907
- const rowWith = (from, step, col) => {
908
- for (let i = from; i >= 0 && i < has.length; i += step)
909
- if (at(i, col))
910
- return i;
911
- return -1;
912
- };
913
- const near = (i) => {
914
- // The mark of row i nearest to column c.
915
- const row = has[i] ?? [];
916
- let best = -1;
917
- row.forEach((x, j) => x && (best < 0 || Math.abs(j - c) < Math.abs(best - c)) && (best = j));
918
- return best < 0 ? null : [i, best];
919
- };
920
- switch (key) {
921
- case 'ArrowRight':
922
- case 'ArrowLeft': {
923
- const s = key === 'ArrowRight' ? 1 : -1;
924
- for (let j = c + s; j >= 0 && j < (has[r]?.length ?? 0); j += s)
925
- if (at(r, j))
926
- return [r, j];
927
- return null;
928
- }
929
- case 'ArrowDown':
930
- case 'ArrowUp': {
931
- const s = key === 'ArrowDown' ? 1 : -1;
932
- const i = rowWith(r + s, s, c);
933
- if (i >= 0)
934
- return [i, c];
935
- for (let k = r + s; k >= 0 && k < has.length; k += s)
936
- if (first(k) >= 0)
937
- return near(k);
938
- return null;
939
- }
940
- case 'Home': {
941
- const i = ctrl ? has.findIndex((row) => row.some(Boolean)) : r;
942
- return i < 0 || first(i) < 0 ? null : [i, first(i)];
943
- }
944
- case 'End': {
945
- let i = r;
946
- if (ctrl)
947
- for (i = has.length - 1; i >= 0 && last(i) < 0; i--)
948
- ;
949
- return i < 0 || last(i) < 0 ? null : [i, last(i)];
950
- }
951
- case 'PageDown':
952
- case 'PageUp': {
953
- const s = key === 'PageDown' ? 1 : -1;
954
- const target = Math.max(0, Math.min(has.length - 1, r + s * page));
955
- for (let k = target; k !== r; k -= s)
956
- if (first(k) >= 0)
957
- return near(k);
958
- return null;
959
- }
960
- default:
961
- return null;
962
- }
963
- }
964
- // ---- what is selected ----
965
- /**
966
- * A mark's selection id: its node, the passes of its column in a loop's passes
967
- * (`|1.2`), and its unit, or its key for a mark without one (`kind:review@7#gamma`).
968
- */
969
- export const markSel = (node, m, passes = []) => `${node}${passes.length ? `|${passes.join('.')}` : ''}#${m.unit || m.key}`;
970
- /**
971
- * What `sel` names in a layer's graph, or, with `matrix`, in a loop's passes (a mark
972
- * there names its column's passes); null when it names nothing there.
973
- */
974
- export function selected(g, matrix, sel) {
975
- if (!sel)
976
- return null;
977
- const hash = sel.indexOf('#');
978
- if (hash < 0) {
979
- const node = g.nodes.find((n) => n.id === sel);
980
- if (node)
981
- return { type: 'node', id: sel, node };
982
- const box = g.boxes.find((b) => b.id === sel);
983
- return box ? { type: 'box', id: sel, box } : null;
984
- }
985
- const [nodeId = '', pass] = sel.slice(0, hash).split('|');
986
- const who = sel.slice(hash + 1);
987
- const is = (m) => !!m && (m.unit ? m.unit === who : m.key === who);
988
- if (pass !== undefined) {
989
- if (!matrix)
990
- return null;
991
- const ci = matrix.columns.findIndex((c) => c.node.id === nodeId && c.passes.join('.') === pass);
992
- const col = matrix.columns[ci];
993
- const mark = matrix.rows.map((r) => r.cells[ci]).find(is);
994
- return col && mark ? { type: 'mark', id: sel, node: col.node, mark, passes: col.passes } : null;
995
- }
996
- const node = g.nodes.find((n) => n.id === nodeId);
997
- const mark = node ? [...node.units, ...node.single].find(is) : undefined;
998
- return node && mark ? { type: 'mark', id: sel, node, mark, passes: [] } : null;
999
- }
1000
- /** The tasks of a layer's nodes by state, added up. */
1001
- export function layerCounts(nodes) {
1002
- const c = { total: 0, done: 0, failed: 0, running: 0, pending: 0, skipped: 0, blocked: 0 };
1003
- for (const n of nodes)
1004
- for (const k of Object.keys(c))
1005
- c[k] += n.counts[k];
1006
- return c;
1007
- }
1008
- // ---- marks ----
1009
- /** How a node lays out its unit marks: as many columns as units up to 12, then 10, then 20; smaller marks as they grow. */
1010
- export function markGrid(units) {
1011
- if (units <= 12)
1012
- return { cols: Math.max(1, units), size: 12 };
1013
- if (units <= 100)
1014
- return { cols: 10, size: 10 };
1015
- return { cols: 20, size: 6 };
1016
- }
1017
- /** Above this many marks, a node shows a bar of its counts and lists its failed marks. */
1018
- export const BAR_MARKS = 200;
1019
- /** A node shows its marks as a bar of counts. */
1020
- export const asBar = (n) => n.units.filter((m) => m !== null).length + n.single.length > BAR_MARKS;
1021
- /** A node's bar: each state's share of its marks, in `order`, the states it has only. */
1022
- export function barParts(c, order) {
1023
- return order.filter((s) => c[s] > 0).map((s) => ({ state: s, n: c[s], pct: (100 * c[s]) / Math.max(1, c.total) }));
1024
- }
1025
- /**
1026
- * The node joined to `from` along a drawn edge: a later one (`later`, that waits for
1027
- * it or was created after it) or an earlier one; of several, the one nearest across
1028
- * the flow (`across`), then the first in drawing order. Null when there is none.
1029
- */
1030
- export function alongEdge(g, from, dir, across) {
1031
- const rank = new Map(g.nodes.map((n, i) => [n.id, i]));
1032
- const ids = [...new Set(g.edges.filter((e) => !e.back && (dir === 'later' ? e.from === from : e.to === from)).map((e) => (dir === 'later' ? e.to : e.from)))];
1033
- const at = across(from);
1034
- ids.sort((a, b) => Math.abs(across(a) - at) - Math.abs(across(b) - at) || (rank.get(a) ?? 0) - (rank.get(b) ?? 0));
1035
- return ids[0] ?? null;
1036
- }
1037
- /** The band past a loop box's content that its return to the next pass runs in, and the return's corner radius. */
1038
- export const RETURN_BAND = 24;
1039
- const RETURN_R = 6;
1040
- export const SPACE = {
1041
- across: { gap: 56, boxGap: 40, sep: 16, pointSep: 12, pad: 12, padEnd: RETURN_BAND, margin: 16 },
1042
- down: { gap: 40, boxGap: 32, sep: 16, pointSep: 12, pad: 10, padEnd: RETURN_BAND, margin: 16 },
1043
- };
1044
- /** Tops (across the flow) nearest to `want` that keep the order and each gap: weighted isotonic regression (pool adjacent violators). */
1045
- function placeRow(want, gaps, weight) {
1046
- const off = [0];
1047
- for (let i = 1; i < want.length; i++)
1048
- off.push(off[i - 1] + gaps[i - 1]);
1049
- const blocks = [];
1050
- want.forEach((v, i) => {
1051
- const w = weight[i] ?? 1;
1052
- blocks.push({ sum: w * (v - off[i]), w, n: 1 });
1053
- while (blocks.length > 1) {
1054
- const b = blocks[blocks.length - 1];
1055
- const a = blocks[blocks.length - 2];
1056
- if (a.sum / a.w <= b.sum / b.w)
1057
- break;
1058
- blocks.splice(-2, 2, { sum: a.sum + b.sum, w: a.w + b.w, n: a.n + b.n });
1059
- }
1060
- });
1061
- const out = [];
1062
- for (const b of blocks)
1063
- for (let k = 0; k < b.n; k++)
1064
- out.push(b.sum / b.w + off[out.length]);
1065
- return out;
1066
- }
1067
- /**
1068
- * Where everything of a layer's graph is drawn, from the sizes of its nodes, its stubs
1069
- * and its loop boxes' labels (`lbl:<box id>`): left to right, or top to bottom when
1070
- * `down`. Each container is laid out inside out, a box being one item of its parent:
1071
- * its layers side by side, an item at the start of its layer; across the flow, each
1072
- * item as near as its order allows to the items it is joined to (sweeps of weighted
1073
- * isotonic regression), so chains run straight. An edge runs along its points and
1074
- * bends only in the gap between two layers; it enters and leaves a box where its
1075
- * point inside is, and never passes a node or a box's label.
1076
- */
1077
- export function layoutGraph(g, sizes, down = false) {
1078
- const S = SPACE[down ? 'down' : 'across'];
1079
- const len = (s) => (down ? s.h : s.w);
1080
- const brd = (s) => (down ? s.w : s.h);
1081
- const boxIds = new Set(g.boxes.map((b) => b.id));
1082
- const label = (box) => sizes.get(`lbl:${box}`) ?? { w: 0, h: 0 };
1083
- // A box's padding: along the flow, and across it. Its label is in a band at its top: across
1084
- // the flow left to right; along it top to bottom, where a gap under the label lets edges in.
1085
- const padOf = (box) => down
1086
- ? { u0: label(box).h + S.pad + S.gap / 2, u1: S.pad, v0: S.pad, v1: S.padEnd, minU: 0, minV: label(box).w + 2 * S.pad }
1087
- : { u0: S.pad, u1: S.pad, v0: label(box).h + S.pad, v1: S.padEnd, minU: label(box).w + 2 * S.pad, minV: 0 };
1088
- const extent = new Map(); // a box's size, along and across
1089
- const slotSize = (id) => {
1090
- if (boxIds.has(id))
1091
- return extent.get(id) ?? { u: 0, v: 0 };
1092
- if (isPoint(id))
1093
- return { u: 0, v: 0 };
1094
- const s = sizes.get(id) ?? { w: 0, h: 0 };
1095
- return { u: len(s), v: brd(s) };
1096
- };
1097
- const local = new Map();
1098
- const layerIn = new Map();
1099
- const depth = (c) => (c === '' ? 0 : c.split('.').length);
1100
- const containers = ['', ...g.boxes.map((b) => b.id)].sort((a, b) => depth(b) - depth(a));
1101
- // Where an edge meets a slot, across the flow from the slot's top: a node's middle,
1102
- // a point, or (a box) where the edge's step inside it is.
1103
- const attach = (ri, si) => {
1104
- const steps = g.routes[ri]?.steps ?? [];
1105
- const s = steps[si];
1106
- if (isPoint(s.id))
1107
- return 0;
1108
- if (!boxIds.has(s.id))
1109
- return slotSize(s.id).v / 2;
1110
- const inner = steps[si - 1]?.in === s.id ? si - 1 : si + 1;
1111
- const step = steps[inner];
1112
- return padOf(s.id).v0 + (local.get(s.id)?.top.get(step.id) ?? 0) + attach(ri, inner);
1113
- };
1114
- for (const c of containers) {
1115
- const rows = (g.slots[c] ?? []).map((r) => [...r]);
1116
- layerIn.set(c, new Map(rows.flatMap((r, i) => r.map((id) => [id, i]))));
1117
- const width = rows.map((r) => Math.max(0, ...r.map((id) => slotSize(id).u)));
1118
- const gap = c === '' ? S.gap : S.boxGap;
1119
- const start = [];
1120
- width.forEach((_, i) => start.push(i ? start[i - 1] + width[i - 1] + gap : 0));
1121
- // The joins inside this container: slot a of layer l to slot b of layer l + 1.
1122
- const links = [];
1123
- g.routes.forEach((r, ri) => r.steps.forEach((s, si) => {
1124
- const p = r.steps[si - 1];
1125
- if (si && p && p.in === c && s.in === c && layerIn.get(c)?.get(p.id) !== layerIn.get(c)?.get(s.id))
1126
- links.push({ a: p.id, b: s.id, oa: attach(ri, si - 1), ob: attach(ri, si) });
1127
- }));
1128
- // Points sit close to each other and to a node, further from a box (its next-pass mark is on its edge).
1129
- const sepOf = (a, b) => b === undefined ? 0 : boxIds.has(a) || boxIds.has(b) || !(isPoint(a) || isPoint(b)) ? S.sep : S.pointSep;
1130
- const top = new Map();
1131
- for (const r of rows) {
1132
- let v = 0;
1133
- r.forEach((id, i) => {
1134
- top.set(id, v);
1135
- v += slotSize(id).v + sepOf(id, r[i + 1]);
1136
- });
1137
- }
1138
- const place = (r, want) => {
1139
- const desired = r.map((id) => {
1140
- const xs = want(id);
1141
- return xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : (top.get(id) ?? 0);
1142
- });
1143
- const gaps = r.map((id, i) => slotSize(id).v + sepOf(id, r[i + 1]));
1144
- // The nodes first, as if the points between them were fixed in size; then the points, around the nodes.
1145
- const real = r.map((id, i) => (isPoint(id) ? -1 : i)).filter((i) => i >= 0);
1146
- const span = (i, j) => gaps.slice(i, j).reduce((a, b) => a + b, 0);
1147
- const at = placeRow(real.map((i) => desired[i]), real.map((i, k) => (k + 1 < real.length ? span(i, real[k + 1]) : 0)), real.map(() => 1));
1148
- real.forEach((i, k) => (desired[i] = at[k]));
1149
- placeRow(desired, gaps, r.map((id) => (isPoint(id) ? 1 : 1e6))).forEach((v, i) => top.set(r[i], Math.round(v * 100) / 100));
1150
- };
1151
- // A node lines up with the nodes it is joined to, when it has any on that side, else with the points;
1152
- // a point lines up with whatever it is joined to, so a chain runs straight and a long edge goes round it.
1153
- const near = (id, ls) => {
1154
- const real = isPoint(id) ? ls : ls.filter((x) => !isPoint(x.other));
1155
- return (real.length ? real : ls).map((x) => x.at);
1156
- };
1157
- const fromPrev = (id) => near(id, links.filter((l) => l.b === id).map((l) => ({ other: l.a, at: (top.get(l.a) ?? 0) + l.oa - l.ob })));
1158
- const fromNext = (id) => near(id, links.filter((l) => l.a === id).map((l) => ({ other: l.b, at: (top.get(l.b) ?? 0) + l.ob - l.oa })));
1159
- for (let sweep = 0; sweep < 8; sweep++) {
1160
- const order = rows.map((_, i) => i);
1161
- if (sweep % 2)
1162
- order.reverse();
1163
- for (const i of order)
1164
- place(rows[i], sweep % 2 ? fromNext : fromPrev);
1165
- }
1166
- for (const r of rows)
1167
- place(r, (id) => [...fromPrev(id), ...fromNext(id)]);
1168
- const lo = top.size ? Math.min(...top.values()) : 0;
1169
- for (const [id, v] of top)
1170
- top.set(id, v - lo);
1171
- const u = (start[start.length - 1] ?? 0) + (width[width.length - 1] ?? 0);
1172
- const v = Math.max(0, ...rows.flat().map((id) => (top.get(id) ?? 0) + slotSize(id).v));
1173
- local.set(c, { rows, start, width, top, u, v });
1174
- if (c !== '') {
1175
- const p = padOf(c);
1176
- extent.set(c, { u: Math.max(p.u0 + u + p.u1, p.minU), v: Math.max(p.v0 + v + p.v1, p.minV) });
1177
- }
1178
- }
1179
- // Inside out, then outside in: each container's place in the drawing.
1180
- const at = {};
1181
- const origin = new Map();
1182
- const rect = (u, v, s) => (down ? { x: v, y: u, w: s.v, h: s.u } : { x: u, y: v, w: s.u, h: s.v });
1183
- const put = (c, ou, ov) => {
1184
- origin.set(c, { u: ou, v: ov });
1185
- const l = local.get(c);
1186
- l.rows.forEach((r, i) => r.forEach((id) => {
1187
- if (isPoint(id))
1188
- return;
1189
- const u = ou + l.start[i];
1190
- const v = ov + (l.top.get(id) ?? 0);
1191
- at[id] = rect(u, v, slotSize(id));
1192
- if (boxIds.has(id)) {
1193
- const p = padOf(id);
1194
- put(id, u + p.u0, v + p.v0);
1195
- }
1196
- }));
1197
- };
1198
- put('', S.margin, S.margin);
1199
- const whole = local.get('');
1200
- const U = whole.u + 2 * S.margin;
1201
- const V = whole.v + 2 * S.margin;
1202
- // A container's layer l: where it starts and ends along the flow, in the drawing.
1203
- const col = (c, l) => {
1204
- const lc = local.get(c);
1205
- const o = origin.get(c) ?? { u: 0, v: 0 };
1206
- const c0 = o.u + (lc.start[l] ?? 0);
1207
- return [c0, c0 + (lc.width[l] ?? 0)];
1208
- };
1209
- const layer = (c, id) => layerIn.get(c)?.get(id) ?? 0;
1210
- const byOf = (s) => (s && isPoint(s.id) ? (/\|by:([^|]*)\|/.exec(s.id)?.[1] ?? '') : '');
1211
- const edges = g.routes.map((r, ri) => {
1212
- const vOf = (si) => {
1213
- const s = r.steps[si];
1214
- const o = origin.get(s.in) ?? { u: 0, v: 0 };
1215
- return o.v + (local.get(s.in)?.top.get(s.id) ?? 0) + attach(ri, si);
1216
- };
1217
- const runs = r.steps.map((s, si) => {
1218
- const prev = r.steps[si - 1];
1219
- const next = r.steps[si + 1];
1220
- const [c0, c1] = col(s.in, layer(s.in, s.id));
1221
- const v = vOf(si);
1222
- if (si === 0)
1223
- return { a: c0 + slotSize(s.id).u, b: c1, at: v };
1224
- if (si === r.steps.length - 1)
1225
- return { a: c0, b: c0, at: v };
1226
- if (isPoint(s.id)) {
1227
- const box = byOf(s);
1228
- // Beside a box: from where it left the box's side, or up to where it goes in.
1229
- if (box && prev?.id === box)
1230
- return { a: col(box, layer(box, r.steps[si - 2]?.id ?? '') + 1)[0], b: c1, at: v };
1231
- if (box && next?.id === box) {
1232
- const ln = layer(box, r.steps[si + 2]?.id ?? '');
1233
- const gap = ln > 0 ? col(box, ln - 1)[1] : (at[box] ? (down ? at[box].y : at[box].x) : c0) + label(box).h + S.pad;
1234
- return { a: c0, b: gap, at: v };
1235
- }
1236
- return { a: c0, b: c1, at: v };
1237
- }
1238
- const p = padOf(s.id);
1239
- const inner = local.get(s.id);
1240
- // Out of a box: at its far side, or through its side after the layer it leaves.
1241
- if (prev?.in === s.id) {
1242
- if (byOf(next) === s.id) {
1243
- const u = col(s.id, layer(s.id, prev.id) + 1)[0];
1244
- return { a: u, b: u, at: vOf(si + 1) };
1245
- }
1246
- return { a: c0 + p.u0 + inner.u, b: c1, at: v };
1247
- }
1248
- // Into a box: at its near side, or through its side before the layer it goes to.
1249
- if (byOf(prev) === s.id) {
1250
- const u = col(s.id, layer(s.id, next?.id ?? ''))[0];
1251
- return { a: u, b: u, at: vOf(si + 1) };
1252
- }
1253
- return { a: c0, b: c0 + p.u0, at: v };
1254
- });
1255
- return { from: r.from, to: r.to, type: r.type, runs };
1256
- });
1257
- // Each box's return to its next pass: from its last layer's item that reaches furthest
1258
- // across the flow, along the band past the content, to its first layer's.
1259
- const returns = g.boxes.flatMap((b) => {
1260
- const l = local.get(b.id);
1261
- const o = origin.get(b.id);
1262
- if (!l || !o)
1263
- return [];
1264
- const outer = (row) => {
1265
- const ids = (row ?? []).filter((id) => !isPoint(id));
1266
- let best = null;
1267
- for (const id of ids) {
1268
- const s = slotSize(id);
1269
- const v = (l.top.get(id) ?? 0) + s.v;
1270
- if (!best || v > best.v)
1271
- best = { u: s.u, v };
1272
- }
1273
- return best;
1274
- };
1275
- const real = l.rows.map((r, i) => (r.some((id) => !isPoint(id)) ? i : -1)).filter((i) => i >= 0);
1276
- const fi = real[0] ?? 0;
1277
- const li = real[real.length - 1] ?? 0;
1278
- const last = outer(l.rows[li]);
1279
- const first = outer(l.rows[fi]);
1280
- if (!last || !first)
1281
- return [];
1282
- // An item's middle along the flow, as wide as its layer; one layer: either side of its middle.
1283
- let from = o.u + (l.start[li] ?? 0) + (l.width[li] ?? 0) / 2;
1284
- let to = o.u + (l.start[fi] ?? 0) + (l.width[fi] ?? 0) / 2;
1285
- if (from - to < 4 * RETURN_R) {
1286
- const d = Math.max(2 * RETURN_R, Math.min(28, Math.min(last.u, first.u) / 4));
1287
- from = (from + to) / 2 + d;
1288
- to = from - 2 * d;
1289
- }
1290
- return [{ box: b.id, from, v0: o.v + last.v, to, v1: o.v + first.v, band: o.v + l.v + RETURN_BAND / 2 }];
1291
- });
1292
- return { w: down ? V : U, h: down ? U : V, at, edges, returns, down };
1293
- }
1294
- /**
1295
- * A loop box's return as an SVG path: out from its last layer to the band, back along
1296
- * it with rounded corners, and in to its first layer, stopping `gap` short of each item.
1297
- */
1298
- export function returnPath(r, down = false, gap = 3) {
1299
- const r2 = (n) => Math.round(n * 2) / 2;
1300
- const pt = (u, v) => (down ? `${r2(v)},${r2(u)}` : `${r2(u)},${r2(v)}`);
1301
- const k = Math.max(0, Math.min(RETURN_R, (r.from - r.to) / 2, r.band - r.v0 - gap, r.band - r.v1 - gap));
1302
- return [
1303
- `M${pt(r.from, r.v0 + gap)}`,
1304
- `L${pt(r.from, r.band - k)}`,
1305
- `Q${pt(r.from, r.band)} ${pt(r.from - k, r.band)}`,
1306
- `L${pt(r.to + k, r.band)}`,
1307
- `Q${pt(r.to, r.band)} ${pt(r.to, r.band - k)}`,
1308
- `L${pt(r.to, r.v1 + gap)}`,
1309
- ].join(' ');
1310
- }
1311
- /** An edge's line as an SVG path: its runs, joined by a curve in each gap where it bends. */
1312
- export function edgePath(runs, down = false) {
1313
- const r2 = (n) => Math.round(n * 2) / 2;
1314
- const pt = (u, v) => (down ? `${r2(v)},${r2(u)}` : `${r2(u)},${r2(v)}`);
1315
- const first = runs[0];
1316
- if (!first)
1317
- return '';
1318
- let d = `M${pt(first.a, first.at)}`;
1319
- runs.forEach((r, i) => {
1320
- if (r.b !== r.a)
1321
- d += ` L${pt(r.b, r.at)}`;
1322
- const next = runs[i + 1];
1323
- if (!next)
1324
- return;
1325
- if (Math.abs(next.at - r.at) < 0.5) {
1326
- if (next.a !== r.b)
1327
- d += ` L${pt(next.a, next.at)}`;
1328
- return;
1329
- }
1330
- const m = (r.b + next.a) / 2;
1331
- d += ` C${pt(m, r.at)} ${pt(m, next.at)} ${pt(next.a, next.at)}`;
1332
- });
1333
- return d;
1334
- }
1335
- /**
1336
- * The frame's height and the view that shows the whole graph: scaled down to fit the
1337
- * frame's width and at most `maxH` of height, never scaled up, and centred.
1338
- */
1339
- export function fitView(graphW, graphH, frameW, maxH) {
1340
- if (graphW <= 0 || graphH <= 0 || frameW <= 0)
1341
- return { k: 1, x: 0, y: 0, h: Math.max(0, graphH) };
1342
- const byWidth = Math.min(1, frameW / graphW);
1343
- const h = Math.min(graphH * byWidth, maxH);
1344
- const k = Math.min(byWidth, h / graphH);
1345
- return { k, x: (frameW - graphW * k) / 2, y: (h - graphH * k) / 2, h };
1346
- }
1347
- /** `v` scaled by `factor` about the point (`cx`, `cy`) of the frame, within `min` and `max`. */
1348
- export function zoomView(v, factor, cx, cy, min, max) {
1349
- const k = Math.min(max, Math.max(min, v.k * factor));
1350
- const r = k / v.k;
1351
- return { k, x: cx - (cx - v.x) * r, y: cy - (cy - v.y) * r };
1352
- }
1353
- export function kpis(journal) {
1354
- const ran = journal.filter(isLaunched);
1355
- const py = ran.filter(isPython);
1356
- const llm = ran.filter((e) => !isPython(e));
1357
- const wall = (xs) => xs.reduce((a, e) => a + (e.wall_s || 0), 0);
1358
- const keys = (xs) => [...new Set(xs.map((e) => e.key))];
1359
- const tasks = (xs) => keys(xs).length;
1360
- const retried = ran.filter((e) => (e.attempt ?? 0) > 0);
1361
- const fixed = ran.filter((e) => (e.rounds || 1) > 1);
1362
- const switched = ran.filter((e) => (e.hops || []).length > 1);
1363
- return {
1364
- attempts: ran.length,
1365
- python: py.length,
1366
- launches: llm.length,
1367
- bad: ran.filter((e) => !e.ok).length,
1368
- launchWall: wall(llm),
1369
- pythonWall: wall(py),
1370
- retries: retried.length,
1371
- retriedTasks: tasks(retried),
1372
- retriedKeys: keys(retried),
1373
- fixupKeys: keys(fixed),
1374
- fixups: fixed.reduce((a, e) => a + (e.rounds || 1) - 1, 0),
1375
- fixupTasks: tasks(fixed),
1376
- switches: switched.reduce((a, e) => a + (e.hops || []).length - 1, 0),
1377
- switchTasks: tasks(switched),
1378
- };
1379
- }
1380
- // --- timeline -------------------------------------------------------------------
1381
- /** Finished tasks older than this before the newest are left out. */
1382
- export const TIMELINE_WINDOW_MS = 6 * 3600e3;
1383
- const STEP_MS = 60e3;
1384
- /** Every finished task as a bar on its kind's lane, running ones growing to `now`. Null when there is nothing to draw. */
1385
- export function timeline(journal, live, now) {
1386
- const bars = [];
1387
- for (const e of journal) {
1388
- if (!e.ts)
1389
- continue;
1390
- const end = Date.parse(e.ts);
1391
- if (Number.isNaN(end))
1392
- continue;
1393
- const cls = !isLaunched(e) ? 'idle' : !e.ok ? 'bad' : isPython(e) ? 'py' : 'llm';
1394
- bars.push({ lane: e.kind || '?', cls, start: end - (e.wall_s || 0) * 1000, end, live: false, row: e });
1395
- }
1396
- if (!bars.length && !live.length)
1397
- return null;
1398
- for (const a of live)
1399
- bars.push({ lane: a.kind, cls: 'run', start: a.t0, end: now, live: true, liveCls: a.cls });
1400
- const newest = Math.max(...bars.filter((x) => !x.live).map((x) => x.end), ...live.map((a) => a.t0));
1401
- const vis = bars.filter((x) => x.end >= newest - TIMELINE_WINDOW_MS);
1402
- const t0 = Math.min(...vis.map((x) => x.start));
1403
- const t1 = live.length ? t0 + Math.ceil((now - t0 + 20e3) / STEP_MS) * STEP_MS : newest;
1404
- const names = [];
1405
- for (const x of [...vis].sort((a, b) => a.start - b.start))
1406
- if (!names.includes(x.lane))
1407
- names.push(x.lane);
1408
- const lanes = names.map((name) => {
1409
- const mine = vis.filter((x) => x.lane === name).sort((a, b) => a.start - b.start);
1410
- // Each bar on the first track whose last bar has ended: overlapping bars do not hide each other.
1411
- const ends = [];
1412
- const bars = mine.map(({ cls, start, end, live: isLive, row }) => {
1413
- let track = ends.findIndex((e) => e <= start);
1414
- if (track < 0)
1415
- track = ends.push(end) - 1;
1416
- else
1417
- ends[track] = end;
1418
- return row ? { cls, start, end, live: isLive, track, row } : { cls, start, end, live: isLive, track };
1419
- });
1420
- return { name, llm: mine.some((x) => x.cls === 'llm' || x.liveCls === 'llm'), bars, tracks: Math.max(1, ends.length) };
1421
- });
1422
- return { t0, span: Math.max(1, t1 - t0), lanes, hidden: bars.length - vis.length, live: live.length > 0 };
1423
- }
1424
- /** A bar's left edge and width, in percent of the span; a live bar grows to `now`. */
1425
- export function barBox(b, tl, now) {
1426
- const left = ((b.start - tl.t0) / tl.span) * 100;
1427
- const end = b.live ? now : b.end;
1428
- const w = ((end - b.start) / tl.span) * 100;
1429
- return { left, width: Math.max(0.4, b.live ? Math.min(100 - left, w) : w) };
1430
- }
1431
- //# sourceMappingURL=logic.js.map