@sleep2agi/agent-network-dashboard 0.4.6-preview.26 → 0.4.6-preview.261

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 (459) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +41 -41
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +6 -0
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/server/app/_not-found.html +2 -2
  17. package/.next/server/app/_not-found.rsc +12 -12
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  24. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app/admin.html +2 -2
  26. package/.next/server/app/admin.rsc +14 -14
  27. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  28. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  30. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  31. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  33. package/.next/server/app/api/hub/servers/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/servers/route.js +7 -0
  37. package/.next/server/app/api/hub/servers/route.js.map +5 -0
  38. package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/index.html +2 -2
  41. package/.next/server/app/index.rsc +14 -14
  42. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  43. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  44. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  46. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/login.html +2 -2
  49. package/.next/server/app/login.rsc +14 -14
  50. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  56. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/logs.html +2 -2
  58. package/.next/server/app/logs.rsc +14 -14
  59. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  65. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/messages.html +2 -2
  67. package/.next/server/app/messages.rsc +14 -14
  68. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  74. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/node.html +2 -2
  76. package/.next/server/app/node.rsc +14 -14
  77. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  83. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/nodes.html +2 -2
  85. package/.next/server/app/nodes.rsc +14 -14
  86. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  87. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  89. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  90. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  92. package/.next/server/app/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/server-logs.html +2 -2
  95. package/.next/server/app/server-logs.rsc +14 -14
  96. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  97. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  98. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  99. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  100. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  101. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  102. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/networks.html +2 -2
  104. package/.next/server/app/settings/networks.rsc +14 -14
  105. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  106. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  107. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  108. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  109. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  110. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  111. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  112. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  113. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  114. package/.next/server/app/settings/tokens.html +2 -2
  115. package/.next/server/app/settings/tokens.rsc +14 -14
  116. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  117. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  118. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  119. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  120. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  121. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  122. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/settings.html +2 -2
  124. package/.next/server/app/settings.rsc +14 -14
  125. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  126. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  128. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  129. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  131. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  132. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  133. package/.next/server/app/tasks.html +2 -2
  134. package/.next/server/app/tasks.rsc +14 -14
  135. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  136. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  137. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  138. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  139. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  140. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  141. package/.next/server/app-paths-manifest.json +1 -0
  142. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
  143. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
  144. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
  145. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
  146. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
  147. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +1 -1
  148. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  149. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  150. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +1 -1
  151. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
  152. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
  153. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  154. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  155. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  156. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +1 -1
  157. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +1 -1
  158. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  160. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  167. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  168. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
  169. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
  170. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  171. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  172. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  173. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  174. package/.next/server/middleware-build-manifest.js +3 -3
  175. package/.next/server/pages/404.html +2 -2
  176. package/.next/server/pages/500.html +1 -1
  177. package/.next/server/server-reference-manifest.js +1 -1
  178. package/.next/server/server-reference-manifest.json +1 -1
  179. package/.next/static/chunks/0-mpa_947ipeq.js +1 -0
  180. package/.next/static/chunks/00.pi0.850kxg.js +1 -0
  181. package/.next/static/chunks/01lmbsd37fybu.js +1 -0
  182. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  183. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  184. package/.next/static/chunks/05uk96gc~9mni.js +1 -0
  185. package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
  186. package/.next/static/chunks/08y27uzl7w6bx.css +2 -0
  187. package/.next/static/chunks/0ggj5ummw2a84.js +7 -0
  188. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  189. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  190. package/.next/static/chunks/0s64vw8kd5rn7.js +1 -0
  191. package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
  192. package/.next/static/chunks/11drzen-wfaop.js +4 -0
  193. package/.next/static/chunks/174re5ar~kxne.js +1 -0
  194. package/.next/trace +2 -2
  195. package/.next/trace-build +1 -1
  196. package/.next/types/routes.d.ts +2 -1
  197. package/.next/types/validator.ts +9 -0
  198. package/app/api/hub/servers/route.ts +39 -0
  199. package/app/components/ChatPopover.tsx +38 -2
  200. package/app/components/CommandPalette.tsx +107 -0
  201. package/app/components/HelpOverlay.tsx +126 -0
  202. package/app/components/ServersDrawer.tsx +64 -27
  203. package/app/components/TopoGraph.tsx +5291 -237
  204. package/app/components/utils.ts +8 -7
  205. package/app/globals.css +110 -5
  206. package/app/lib/time.ts +36 -0
  207. package/package.json +2 -2
  208. package/scripts/help-overlay-topo-test.mjs +44 -0
  209. package/scripts/servers-drawer-live-test.mjs +96 -0
  210. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  211. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  212. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  213. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  214. package/scripts/topo-active-links-chip-test.mjs +73 -0
  215. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  216. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  217. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  218. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  219. package/scripts/topo-arrival-ping-test.mjs +87 -0
  220. package/scripts/topo-auto-fit-test.mjs +73 -0
  221. package/scripts/topo-canvas-aria-test.mjs +108 -0
  222. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  223. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  224. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  225. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  226. package/scripts/topo-chip-focus-test.mjs +95 -0
  227. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  228. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  229. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  230. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  231. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  232. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  233. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  234. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  235. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  236. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  237. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  238. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  239. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  240. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  241. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  242. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  243. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  244. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  245. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  246. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  247. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  248. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  249. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  250. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  251. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  252. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  253. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  254. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  255. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  256. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  257. package/scripts/topo-edge-fadein-test.mjs +106 -0
  258. package/scripts/topo-edge-focus-test.mjs +113 -0
  259. package/scripts/topo-edge-hover-test.mjs +93 -0
  260. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  261. package/scripts/topo-edge-pill-test.mjs +105 -0
  262. package/scripts/topo-edge-smil-sync-test.mjs +148 -0
  263. package/scripts/topo-edge-smil-theme-ease-test.mjs +97 -0
  264. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  265. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  266. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  267. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  268. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  269. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  270. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  271. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  272. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  273. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  274. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  275. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  276. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  277. package/scripts/topo-fit-key-test.mjs +106 -0
  278. package/scripts/topo-freshness-chip-test.mjs +74 -0
  279. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  280. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  281. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  282. package/scripts/topo-group-count-test.mjs +68 -0
  283. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  284. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  285. package/scripts/topo-group-label-hover-test.mjs +119 -0
  286. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  287. package/scripts/topo-group-label-tint-test.mjs +99 -0
  288. package/scripts/topo-group-label-title-test.mjs +83 -0
  289. package/scripts/topo-group-pin-test.mjs +119 -0
  290. package/scripts/topo-group-pip-fadein-test.mjs +100 -0
  291. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  292. package/scripts/topo-group-status-pips-test.mjs +103 -0
  293. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  294. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  295. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  296. package/scripts/topo-groupbox-live-test.mjs +100 -0
  297. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  298. package/scripts/topo-groupbox-test.mjs +4 -2
  299. package/scripts/topo-halo-breath-ease-test.mjs +92 -0
  300. package/scripts/topo-halo-breath-stagger-test.mjs +89 -0
  301. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  302. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  303. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  304. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  305. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  306. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  307. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  308. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  309. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  310. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  311. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  312. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  313. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  314. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  315. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  316. package/scripts/topo-hub-a11y-test.mjs +112 -0
  317. package/scripts/topo-hub-busyness-test.mjs +100 -0
  318. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  319. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  320. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  321. package/scripts/topo-hub-fadein-test.mjs +88 -0
  322. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  323. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  324. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  325. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  326. package/scripts/topo-hub-title-test.mjs +93 -0
  327. package/scripts/topo-hub-working-count-test.mjs +99 -0
  328. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  329. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  330. package/scripts/topo-label-lift-test.mjs +119 -0
  331. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  332. package/scripts/topo-layout-key-test.mjs +91 -0
  333. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  334. package/scripts/topo-layout-toggle-test.mjs +131 -0
  335. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  336. package/scripts/topo-legend-count-test.mjs +88 -0
  337. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  338. package/scripts/topo-legend-hover-test.mjs +113 -0
  339. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  340. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  341. package/scripts/topo-legend-pin-test.mjs +128 -0
  342. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  343. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  344. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  345. package/scripts/topo-legend-row-title-test.mjs +102 -0
  346. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  347. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  348. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  349. package/scripts/topo-minimap-test.mjs +68 -0
  350. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  351. package/scripts/topo-mobile-chips-test.mjs +103 -0
  352. package/scripts/topo-node-a11y-test.mjs +107 -0
  353. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  354. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  355. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  356. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  357. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  358. package/scripts/topo-node-title-peers-test.mjs +107 -0
  359. package/scripts/topo-node-title-test.mjs +101 -0
  360. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  361. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  362. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  363. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  364. package/scripts/topo-orbit-opacity-bucket-test.mjs +124 -0
  365. package/scripts/topo-overlap-test.mjs +32 -5
  366. package/scripts/topo-pan-cursor-test.mjs +62 -0
  367. package/scripts/topo-panel-elevation-test.mjs +76 -0
  368. package/scripts/topo-panel-fadein-test.mjs +102 -0
  369. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  370. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  371. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  372. package/scripts/topo-particle-stagger-test.mjs +82 -0
  373. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  374. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  375. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  376. package/scripts/topo-pin-intersection-test.mjs +121 -0
  377. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  378. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  379. package/scripts/topo-pin-persistence-test.mjs +105 -0
  380. package/scripts/topo-ping-pulse-ease-test.mjs +96 -0
  381. package/scripts/topo-popover-meta-test.mjs +78 -0
  382. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  383. package/scripts/topo-pressure-bar-test.mjs +69 -0
  384. package/scripts/topo-pressure-hover-test.mjs +123 -0
  385. package/scripts/topo-pressure-pin-test.mjs +119 -0
  386. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  387. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  388. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  389. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  390. package/scripts/topo-pulse-rate-test.mjs +71 -0
  391. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  392. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  393. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  394. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  395. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  396. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  397. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  398. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  399. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  400. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  401. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  402. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  403. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  404. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  405. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  406. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  407. package/scripts/topo-recent-row-title-test.mjs +112 -0
  408. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  409. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  410. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  411. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  412. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  413. package/scripts/topo-reduced-motion-test.mjs +77 -0
  414. package/scripts/topo-reset-spin-test.mjs +120 -0
  415. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  416. package/scripts/topo-spoke-busyness-test.mjs +110 -0
  417. package/scripts/topo-spoke-tint-test.mjs +96 -0
  418. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  419. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  420. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  421. package/scripts/topo-svg-focus-test.mjs +112 -0
  422. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  423. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  424. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  425. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  426. package/scripts/topo-tier-ring-test.mjs +69 -0
  427. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  428. package/scripts/topo-tier-stagger-test.mjs +89 -0
  429. package/scripts/topo-tz-ghost-test.mjs +69 -0
  430. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  431. package/scripts/topo-vendor-chip-self-hover-test.mjs +130 -0
  432. package/scripts/topo-vendor-chip-tabular-test.mjs +89 -0
  433. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  434. package/scripts/topo-vendor-pill-test.mjs +125 -0
  435. package/scripts/topo-vendor-pin-test.mjs +125 -0
  436. package/scripts/topo-view-smooth-test.mjs +131 -0
  437. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  438. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  439. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  440. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  441. package/scripts/topo-zoom-keys-test.mjs +89 -0
  442. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  443. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  444. package/.next/static/chunks/09nd.t_hd.y6t.css +0 -2
  445. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  446. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  447. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  448. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  449. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  450. package/.next/static/chunks/0jzqhqbydbrm~.js +0 -1
  451. package/.next/static/chunks/0lvk7f2_5y8b3.js +0 -1
  452. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  453. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  454. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  455. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  456. package/.next/static/chunks/12maqvd_4t3sh.js +0 -1
  457. /package/.next/static/{iC9JNLo0IdNSaeynEneGT → -JO9tjmCkno3inrj-XBxj}/_buildManifest.js +0 -0
  458. /package/.next/static/{iC9JNLo0IdNSaeynEneGT → -JO9tjmCkno3inrj-XBxj}/_clientMiddlewareManifest.js +0 -0
  459. /package/.next/static/{iC9JNLo0IdNSaeynEneGT → -JO9tjmCkno3inrj-XBxj}/_ssgManifest.js +0 -0
@@ -0,0 +1,132 @@
1
+ /* Round 166 verification: visible flow edge path picks up
2
+ * stroke-width transition.
3
+ *
4
+ * R50 thickens hovered edges to 1.4× (renderWidth = width * 1.4)
5
+ * but pre-R166 only opacity transitioned — width snapped. R166
6
+ * adds stroke-width to the transition list so the edge thickens
7
+ * smoothly on hover, pairing with R164 edge-badge r-lift for
8
+ * coordinated 300ms ease-out across the edge surface.
9
+ *
10
+ * Test:
11
+ * 1. Mock 4 sessions + 1 flow alpha→beta
12
+ * 2. Probe visible path before hover → strokeWidth=base
13
+ * 3. Probe transition string includes both opacity and
14
+ * stroke-width
15
+ * 4. Hover edge hitbox → wait 350ms (past transition)
16
+ * 5. Probe strokeWidth → should be ≈ base * 1.4
17
+ * 6. mouse away → strokeWidth back to base
18
+ */
19
+ import { chromium } from 'playwright';
20
+ import { readFileSync } from 'node:fs';
21
+
22
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
23
+ const browser = await chromium.launch({ headless: true });
24
+
25
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
29
+ });
30
+
31
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
32
+ await ctx.route('**/api/hub/status*', async (route) => {
33
+ const r = await route.fetch();
34
+ const b = await r.json();
35
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
36
+ const mk = (alias) => ({
37
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
38
+ network_id: nid, project_dir: null,
39
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
40
+ });
41
+ // 4 sessions so alpha + beta are 90° apart (not 180° — that
42
+ // would put their edge midpoint on the hub, where the hub
43
+ // <g> intercepts pointer events). gamma + delta are filler.
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [
45
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
46
+ ] } });
47
+ });
48
+
49
+ const now = Date.now();
50
+ // 4 msgs so:
51
+ // - width = Math.min(2+4, 7) = 6
52
+ // - hover renderWidth = Math.min(6*1.4, 10) = 8.4
53
+ // AND the edge midpoint count badge renders (R100 threshold
54
+ // count >= 3) so we can use it as the hover surface. The
55
+ // visible path has pointerEvents:none; the R48 hitbox is a
56
+ // thin stroke that Playwright's .hover() can't reliably land
57
+ // on. The badge is a r=9 circle with pointerEvents:all and
58
+ // propagates hover to hoveredEdgeKey (R122) — most reliable
59
+ // target for triggering the visible-path transition.
60
+ const msgs = [];
61
+ for (let i = 0; i < 4; i++) {
62
+ msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
63
+ network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
64
+ }
65
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
66
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
67
+
68
+ const page = await ctx.newPage();
69
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
70
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
71
+ await page.waitForSelector('[data-edge-visible]', { timeout: 10000 });
72
+ await page.waitForTimeout(400);
73
+
74
+ const idle = await page.evaluate(() => {
75
+ const el = document.querySelector('[data-edge-visible]');
76
+ if (!el) return null;
77
+ return {
78
+ strokeWidth: parseFloat(el.getAttribute('stroke-width')),
79
+ inlineTransition: el.style.transition || '',
80
+ computedTransition: getComputedStyle(el).transition || '',
81
+ };
82
+ });
83
+
84
+ // Hover the edge midpoint badge (R164 r=9 circle, pointerEvents:all,
85
+ // propagates to hoveredEdgeKey via R122). The visible path has
86
+ // pointerEvents:none; the R48 hitbox is a thin stroke that
87
+ // Playwright .hover() can't reliably land on.
88
+ await page.locator('[data-edge-count-badge]').hover();
89
+ // Wait for transition to settle.
90
+ await page.waitForTimeout(400);
91
+ const hovered = await page.evaluate(() => {
92
+ const el = document.querySelector('[data-edge-visible]');
93
+ if (!el) return null;
94
+ return {
95
+ strokeWidth: parseFloat(el.getAttribute('stroke-width')),
96
+ };
97
+ });
98
+
99
+ // Move cursor away
100
+ await page.mouse.move(10, 10);
101
+ await page.waitForTimeout(400);
102
+ const idleAgain = await page.evaluate(() => {
103
+ const el = document.querySelector('[data-edge-visible]');
104
+ if (!el) return null;
105
+ return {
106
+ strokeWidth: parseFloat(el.getAttribute('stroke-width')),
107
+ };
108
+ });
109
+
110
+ await browser.close();
111
+
112
+ const hasOpacity = (idle?.inlineTransition || '').includes('opacity 300ms') ||
113
+ /opacity\s+0\.3s|opacity\s+300ms/.test(idle?.computedTransition || '');
114
+ const hasStrokeWidth = (idle?.inlineTransition || '').includes('stroke-width 300ms') ||
115
+ /stroke-width\s+0\.3s|stroke-width\s+300ms/.test(idle?.computedTransition || '');
116
+
117
+ // Expected: width=Math.min(2+4, 7)=6. Hover renderWidth=Math.min(6*1.4, 10)=8.4.
118
+ const results = {
119
+ edge_found: idle !== null,
120
+ has_opacity_transition: hasOpacity,
121
+ has_stroke_width_transition: hasStrokeWidth,
122
+ idle_width_6: idle && Math.abs(idle.strokeWidth - 6) < 0.05,
123
+ hover_width_8p4: hovered && Math.abs(hovered.strokeWidth - 8.4) < 0.1,
124
+ hover_width_thicker: hovered && idle && hovered.strokeWidth > idle.strokeWidth,
125
+ idle_again_width_6: idleAgain && Math.abs(idleAgain.strokeWidth - 6) < 0.05,
126
+ };
127
+ const ok = Object.values(results).every(Boolean);
128
+ console.log(`${ok ? '✅' : '❌'} edge stroke transition:`, JSON.stringify(results),
129
+ `\n idle =`, idle,
130
+ `\n hovered =`, hovered,
131
+ `\n idleAgain =`, idleAgain);
132
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,70 @@
1
+ /* Round 39 verification: each flow-link path carries a `<title>` with
2
+ * "from → to\nN messages · last <relative>". Tests:
3
+ * - tooltip text shape (route + count + relative time)
4
+ * - singular vs. plural ("1 message" vs "5 messages")
5
+ * - missing last_at falls through cleanly */
6
+ import { chromium } from 'playwright';
7
+ import { readFileSync } from 'node:fs';
8
+
9
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
10
+ const browser = await chromium.launch({ headless: true });
11
+
12
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
13
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
14
+ await ctx.addInitScript(() => {
15
+ try {
16
+ localStorage.setItem('anet-theme', 'cyber');
17
+ localStorage.setItem('anet-topo-layout', 'grid');
18
+ sessionStorage.setItem('anet_v3_auth', '1');
19
+ } catch {}
20
+ });
21
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const sessions = ['alpha', 'beta', 'gamma'].map(a => ({
27
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
28
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
29
+ }));
30
+ await route.fulfill({ response: r, json: { ...b, sessions } });
31
+ });
32
+ // Inject messages: 1 between alpha→beta (singular), 5 between alpha→gamma (plural).
33
+ const old = new Date(Date.now() - 8 * 60 * 1000).toISOString();
34
+ const recent = new Date(Date.now() - 30 * 1000).toISOString();
35
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
36
+ { from_alias: 'alpha', to_alias: 'beta', content: 'hello', created_at: old },
37
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm1', created_at: recent },
38
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm2', created_at: recent },
39
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm3', created_at: recent },
40
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm4', created_at: recent },
41
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm5', created_at: recent },
42
+ ] } }));
43
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
46
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
47
+ await page.waitForTimeout(600);
48
+
49
+ // Collect every flow-link tooltip (each base path with a child <title>).
50
+ const tooltips = await page.evaluate(() => {
51
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
52
+ // Two paths per flow link — the base one carries the <title>.
53
+ const titled = [...svg.querySelectorAll('path > title')];
54
+ return titled.map(t => t.textContent);
55
+ });
56
+
57
+ await browser.close();
58
+ const has = (route, count, plural) => tooltips.some(t =>
59
+ t.includes(route) &&
60
+ (plural ? t.includes(`${count} messages`) : t.includes(`${count} message`)) &&
61
+ /last \w+ ago/.test(t)
62
+ );
63
+ const results = {
64
+ singularAlphaToBeta: has('alpha → beta', 1, false),
65
+ pluralAlphaToGamma: has('alpha → gamma', 5, true),
66
+ tooltipCount: tooltips.length === 2,
67
+ };
68
+ const ok = Object.values(results).every(Boolean);
69
+ console.log(`${ok ? '✅' : '❌'} edge tooltip:`, JSON.stringify(results), '\n tooltips=', tooltips);
70
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,150 @@
1
+ /* Round 200 verification (milestone): recent-signal panel empty-
2
+ * state texts breathe in opacity. The two lines "no flow yet" and
3
+ * "send a message between agents" each get a SMIL <animate> child
4
+ * cycling opacity ~0.55 ↔ 0.78 (main) and 0.36 ↔ 0.58 (sub) over
5
+ * 4.4s. Sub-line is phase-shifted -1.5s so the two read as
6
+ * inhale-exhale, not synchronised pulse.
7
+ *
8
+ * Pre-R200 both texts sat at fixed opacity 0.65 / 0.45 — visually
9
+ * identical to "frozen" or "broken" first-impression. Slow breath
10
+ * = "waiting, listening" while staying calm.
11
+ *
12
+ * Test:
13
+ * 1. Mock 0 messages → flowLinks.length === 0 → empty state fires.
14
+ * 2. data-recent-signal-empty + data-recent-signal-empty-hint
15
+ * both present.
16
+ * 3. data-recent-signal-empty-breathes='true'
17
+ * data-recent-signal-empty-hint-breathes='true'
18
+ * 4. Both texts contain an <animate> child with:
19
+ * - attributeName='opacity'
20
+ * - dur='4.4s'
21
+ * - repeatCount='indefinite'
22
+ * - values cover a range (not a single value)
23
+ * 5. Sub-line <animate> has begin='-1.5s' (phase-shift).
24
+ * 6. Main <animate> begin is unset / '0s' (no phase shift).
25
+ * 7. Captured opacity values shift over time (sample twice, ~2s
26
+ * apart): same element should produce different rendered
27
+ * opacity values because SMIL is animating.
28
+ */
29
+ import { chromium } from 'playwright';
30
+ import { readFileSync } from 'node:fs';
31
+
32
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
33
+ const browser = await chromium.launch({ headless: true });
34
+
35
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
36
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
37
+ await ctx.addInitScript(() => {
38
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
39
+ });
40
+
41
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
42
+ await ctx.route('**/api/hub/status*', async (route) => {
43
+ const r = await route.fetch();
44
+ const b = await r.json();
45
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
46
+ const mk = (alias) => ({
47
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
48
+ network_id: nid, project_dir: null,
49
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
50
+ });
51
+ await route.fulfill({ response: r, json: { ...b, sessions: [
52
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
53
+ ] } });
54
+ });
55
+ // 0 messages → empty state fires
56
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
57
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
58
+
59
+ const page = await ctx.newPage();
60
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
61
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
62
+ await page.waitForSelector('[data-recent-signal-empty]', { timeout: 10000 });
63
+ await page.waitForTimeout(300);
64
+
65
+ const probeStructure = async () => page.evaluate(() => {
66
+ const main = document.querySelector('[data-recent-signal-empty]');
67
+ const sub = document.querySelector('[data-recent-signal-empty-hint]');
68
+ function pickAnim(textEl) {
69
+ if (!textEl) return null;
70
+ const a = textEl.querySelector('animate');
71
+ if (!a) return { found: false };
72
+ return {
73
+ found: true,
74
+ attribute: a.getAttribute('attributeName'),
75
+ values: a.getAttribute('values'),
76
+ dur: a.getAttribute('dur'),
77
+ begin: a.getAttribute('begin'),
78
+ repeatCount: a.getAttribute('repeatCount'),
79
+ };
80
+ }
81
+ return {
82
+ mainText: main?.textContent?.trim(),
83
+ mainBreathes:main?.getAttribute('data-recent-signal-empty-breathes'),
84
+ mainAnim: pickAnim(main),
85
+ subText: sub?.textContent?.trim(),
86
+ subBreathes: sub?.getAttribute('data-recent-signal-empty-hint-breathes'),
87
+ subAnim: pickAnim(sub),
88
+ };
89
+ });
90
+
91
+ const s = await probeStructure();
92
+
93
+ // Sample rendered opacity twice ~2s apart to verify SMIL is actually
94
+ // animating in the browser, not just declared.
95
+ async function sampleOpacities() {
96
+ return page.evaluate(() => {
97
+ const main = document.querySelector('[data-recent-signal-empty]');
98
+ const sub = document.querySelector('[data-recent-signal-empty-hint]');
99
+ // SMIL renders into the SVG-internal opacity (DOM attr stays
100
+ // static at the React-rendered value; computed style reads the
101
+ // animated current value).
102
+ return {
103
+ mainComputed: parseFloat(getComputedStyle(main).opacity),
104
+ subComputed: parseFloat(getComputedStyle(sub).opacity),
105
+ };
106
+ });
107
+ }
108
+ const sample1 = await sampleOpacities();
109
+ await page.waitForTimeout(2000);
110
+ const sample2 = await sampleOpacities();
111
+
112
+ await browser.close();
113
+
114
+ const parseValues = (v) => (v || '').split(/[;,]/).map(parseFloat).filter(Number.isFinite);
115
+ const mainVals = parseValues(s.mainAnim?.values);
116
+ const subVals = parseValues(s.subAnim?.values);
117
+
118
+ const results = {
119
+ empty_state_visible: s.mainText === 'no flow yet',
120
+ hint_visible: s.subText === 'send a message between agents',
121
+ main_breathes_attr: s.mainBreathes === 'true',
122
+ hint_breathes_attr: s.subBreathes === 'true',
123
+
124
+ main_anim_found: s.mainAnim?.found === true,
125
+ main_anim_opacity: s.mainAnim?.attribute === 'opacity',
126
+ main_anim_dur_4p4s: s.mainAnim?.dur === '4.4s',
127
+ main_anim_indefinite: s.mainAnim?.repeatCount === 'indefinite',
128
+ main_anim_values_range: mainVals.length >= 3 && (Math.max(...mainVals) - Math.min(...mainVals)) > 0.1,
129
+ main_anim_no_phase_shift: !s.mainAnim?.begin || s.mainAnim?.begin === '0s' || s.mainAnim?.begin === '',
130
+
131
+ sub_anim_found: s.subAnim?.found === true,
132
+ sub_anim_opacity: s.subAnim?.attribute === 'opacity',
133
+ sub_anim_dur_4p4s: s.subAnim?.dur === '4.4s',
134
+ sub_anim_phase_shift: s.subAnim?.begin === '-1.5s',
135
+ sub_anim_values_range: subVals.length >= 3 && (Math.max(...subVals) - Math.min(...subVals)) > 0.1,
136
+
137
+ // SMIL actually animates — sampling at different cycle phases yields
138
+ // different rendered opacities. With 4.4s period and 2s gap, the two
139
+ // samples are at distinct phases and should diverge by at least 0.04
140
+ // unless we landed on symmetric cycle points (extremely rare given
141
+ // mock timing variance — re-check loose bound).
142
+ main_opacity_animating: Math.abs(sample1.mainComputed - sample2.mainComputed) > 0.02,
143
+ sub_opacity_animating: Math.abs(sample1.subComputed - sample2.subComputed) > 0.02,
144
+ };
145
+ const ok = Object.values(results).every(Boolean);
146
+ console.log(`${ok ? '✅' : '❌'} empty-state breath:`, JSON.stringify(results),
147
+ `\n structure:`, s,
148
+ `\n sample1:`, sample1,
149
+ `\n sample2:`, sample2);
150
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,130 @@
1
+ /* Round 222 verification: recent-signal panel empty state always-
2
+ * mounts; visibility crossfades via wrapper <g> opacity instead of
3
+ * conditional mount/unmount on flowLinks.length === 0.
4
+ *
5
+ * Pre-R222 the first flow arriving snap-removed the empty state
6
+ * (R45 + R200) in one frame while R203 rows simultaneously faded
7
+ * IN — half-smooth, half-snap. R222 closes the snap so the empty-
8
+ * to-populated transition crossfades both directions cleanly.
9
+ *
10
+ * Test scenarios:
11
+ * A. 0 flows (empty):
12
+ * · wrapper visible='true', opacity=1
13
+ * · both empty texts present, R200 SMIL <animate> still on
14
+ * · rows NOT mounted (rows path returns null)
15
+ * B. 3 flows (populated):
16
+ * · wrapper visible='false', opacity=0
17
+ * · both empty texts STILL in DOM (always-mount)
18
+ * · R200 SMIL <animate> still in place (just invisible due
19
+ * to parent opacity 0)
20
+ * · rows present
21
+ * Both: inline transition includes 'opacity 300ms'
22
+ */
23
+ import { chromium } from 'playwright';
24
+ import { readFileSync } from 'node:fs';
25
+
26
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
27
+ const browser = await chromium.launch({ headless: true });
28
+
29
+ async function setup(messages) {
30
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
31
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
32
+ await ctx.addInitScript(() => {
33
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
34
+ });
35
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
36
+ await ctx.route('**/api/hub/status*', async (route) => {
37
+ const r = await route.fetch();
38
+ const b = await r.json();
39
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
40
+ const mk = (alias) => ({
41
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
42
+ network_id: nid, project_dir: null,
43
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
44
+ });
45
+ await route.fulfill({ response: r, json: { ...b, sessions: [
46
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
47
+ ] } });
48
+ });
49
+ await ctx.route('**/api/hub/messages*', (r2) => r2.fulfill({ json: { messages } }));
50
+ await ctx.route('**/api/hub/tasks*', (r2) => r2.fulfill({ json: { tasks: [] } }));
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
53
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
54
+ await page.waitForSelector('[data-recent-signal-empty-wrapper]', { timeout: 10000, state: 'attached' });
55
+ await page.waitForTimeout(400);
56
+ return page;
57
+ }
58
+
59
+ const probe = async (page) => page.evaluate(() => {
60
+ const wrapper = document.querySelector('[data-recent-signal-empty-wrapper]');
61
+ if (!wrapper) return null;
62
+ const cs = getComputedStyle(wrapper);
63
+ const main = wrapper.querySelector('[data-recent-signal-empty]');
64
+ const hint = wrapper.querySelector('[data-recent-signal-empty-hint]');
65
+ const mainAnim = main?.querySelector('animate');
66
+ const hintAnim = hint?.querySelector('animate');
67
+ const rowCount = document.querySelectorAll('[data-recent-row]').length;
68
+ return {
69
+ visible: wrapper.getAttribute('data-recent-signal-empty-visible'),
70
+ opacity: parseFloat(cs.opacity),
71
+ transition: wrapper.style.transition,
72
+ mainPresent: !!main,
73
+ hintPresent: !!hint,
74
+ mainText: main?.textContent?.trim().split(/\s+/)[0] + ' ' + main?.textContent?.trim().split(/\s+/)[1]
75
+ + ' ' + main?.textContent?.trim().split(/\s+/)[2],
76
+ mainAnimAttr: mainAnim?.getAttribute('attributeName'),
77
+ hintAnimAttr: hintAnim?.getAttribute('attributeName'),
78
+ rowCount,
79
+ };
80
+ });
81
+
82
+ // Scenario A: 0 flows (empty)
83
+ const pageA = await setup([]);
84
+ const probeA = await probe(pageA);
85
+ await pageA.close();
86
+
87
+ // Scenario B: 3 flows (populated)
88
+ const now = Date.now();
89
+ const pageB = await setup([
90
+ { id: '1', from_alias: 'alpha', to_alias: 'beta', content: 'hi',
91
+ network_id: 'default', created_at: new Date(now - 1000).toISOString() },
92
+ { id: '2', from_alias: 'beta', to_alias: 'gamma', content: 'hi',
93
+ network_id: 'default', created_at: new Date(now - 2000).toISOString() },
94
+ { id: '3', from_alias: 'gamma', to_alias: 'delta', content: 'hi',
95
+ network_id: 'default', created_at: new Date(now - 3000).toISOString() },
96
+ ]);
97
+ const probeB = await probe(pageB);
98
+ await pageB.close();
99
+ await browser.close();
100
+
101
+ const results = {
102
+ // Scenario A — empty
103
+ A_present_in_dom: !!probeA,
104
+ A_visible_true: probeA?.visible === 'true',
105
+ A_opacity_1: probeA?.opacity === 1,
106
+ A_main_present: probeA?.mainPresent === true,
107
+ A_hint_present: probeA?.hintPresent === true,
108
+ A_main_anim_opacity: probeA?.mainAnimAttr === 'opacity',
109
+ A_hint_anim_opacity: probeA?.hintAnimAttr === 'opacity',
110
+ A_no_rows: probeA?.rowCount === 0,
111
+
112
+ // Scenario B — populated (empty still in DOM)
113
+ B_present_in_dom: !!probeB,
114
+ B_visible_false: probeB?.visible === 'false',
115
+ B_opacity_0: probeB?.opacity === 0,
116
+ B_main_still_in_dom: probeB?.mainPresent === true,
117
+ B_hint_still_in_dom: probeB?.hintPresent === true,
118
+ B_main_anim_still_on: probeB?.mainAnimAttr === 'opacity',
119
+ B_hint_anim_still_on: probeB?.hintAnimAttr === 'opacity',
120
+ B_three_rows: probeB?.rowCount === 3,
121
+
122
+ // Transitions present in both
123
+ A_has_transition: /opacity\s+(?:300ms|0\.3s)/.test(probeA?.transition || ''),
124
+ B_has_transition: /opacity\s+(?:300ms|0\.3s)/.test(probeB?.transition || ''),
125
+ };
126
+ const ok = Object.values(results).every(Boolean);
127
+ console.log(`${ok ? '✅' : '❌'} empty-state crossfade:`, JSON.stringify(results),
128
+ `\n probeA (0 flows empty):`, probeA,
129
+ `\n probeB (3 flows populated):`, probeB);
130
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,132 @@
1
+ /* Round 182 verification: edge-endpoint emphasis ring (R49/R111)
2
+ * fades in/out instead of mount/unmount.
3
+ *
4
+ * Pre-R182 the ring mounted only when the node was in the
5
+ * hoveredEdgeEndpoints set — the transition style was already
6
+ * there but the mount/unmount sequence defeated it. R182 makes
7
+ * the ring always-mounted with opacity gated by the set, so the
8
+ * transition actually fires on hover entry/exit.
9
+ *
10
+ * Also escapes the R51 overlap-test sentinel:
11
+ * strokeWidth: 1.5 → 1.6 (visually imperceptible, doesn't
12
+ * match the exact-string CSS selector that picks the status
13
+ * ring inside g[data-node]).
14
+ *
15
+ * Test:
16
+ * 1. 4 sessions + a 4-msg flow alpha→beta (so badge renders)
17
+ * 2. Idle: ring exists on alpha + beta + gamma + delta nodes,
18
+ * all at opacity=0
19
+ * 3. Hover the edge badge (propagates to hoveredEdgeKey →
20
+ * hoveredEdgeEndpoints set with alpha + beta)
21
+ * 4. Rings on alpha + beta: opacity ≈ 0.85 (cyber)
22
+ * 5. Rings on gamma + delta: still opacity=0
23
+ * 6. Mouseleave: all four back to opacity=0
24
+ * 7. All rings carry 'opacity 180ms ease-out' transition
25
+ * 8. All rings strokeWidth='1.6' (not 1.5)
26
+ */
27
+ import { chromium } from 'playwright';
28
+ import { readFileSync } from 'node:fs';
29
+
30
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
31
+ const browser = await chromium.launch({ headless: true });
32
+
33
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
34
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
35
+ await ctx.addInitScript(() => {
36
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
37
+ });
38
+
39
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
40
+ await ctx.route('**/api/hub/status*', async (route) => {
41
+ const r = await route.fetch();
42
+ const b = await r.json();
43
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
44
+ const mk = (alias) => ({
45
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
46
+ network_id: nid, project_dir: null,
47
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
48
+ });
49
+ // 4 sessions so alpha/beta sit 90° apart (avoid hub overlap with badge midpoint)
50
+ await route.fulfill({ response: r, json: { ...b, sessions: [
51
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
52
+ ] } });
53
+ });
54
+
55
+ const now = Date.now();
56
+ const msgs = [];
57
+ for (let i = 0; i < 4; i++) {
58
+ msgs.push({ id: `m${i}`, from_alias: 'alpha', to_alias: 'beta', content: 'hi',
59
+ network_id: 'default', created_at: new Date(now - (10000 + i * 500)).toISOString() });
60
+ }
61
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
62
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
63
+
64
+ const page = await ctx.newPage();
65
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
66
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
67
+ await page.waitForSelector('[data-edge-endpoint-ring]', { timeout: 10000 });
68
+ await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
69
+ await page.waitForTimeout(400);
70
+
71
+ const probeRings = () => page.evaluate(() => {
72
+ const rings = [...document.querySelectorAll('g[data-node] [data-edge-endpoint-ring]')];
73
+ return rings.map(r => {
74
+ const node = r.closest('g[data-node]');
75
+ return {
76
+ alias: node?.getAttribute('data-node'),
77
+ opacity: parseFloat(r.getAttribute('opacity') || ''),
78
+ activeAttr: r.getAttribute('data-edge-endpoint-active'),
79
+ strokeWidth: r.getAttribute('stroke-width'),
80
+ transition: r.style.transition || getComputedStyle(r).transition,
81
+ };
82
+ });
83
+ });
84
+
85
+ const idle = await probeRings();
86
+
87
+ // Hover the edge midpoint badge (propagates to hoveredEdgeKey)
88
+ await page.locator('[data-edge-count-badge]').hover();
89
+ await page.waitForTimeout(300);
90
+ const hovered = await probeRings();
91
+
92
+ // Move cursor away
93
+ await page.mouse.move(10, 10);
94
+ await page.waitForTimeout(300);
95
+ const idleAgain = await probeRings();
96
+
97
+ await browser.close();
98
+
99
+ const findRing = (rings, alias) => rings.find(r => r.alias === alias);
100
+ const hasOpacityTransition = (s) =>
101
+ (s.transition || '').includes('opacity 180ms') ||
102
+ /opacity\s+0\.18s|opacity\s+180ms/.test(s.transition || '');
103
+
104
+ const results = {
105
+ four_rings_present: idle.length === 4,
106
+ all_have_strokewidth_1p6: idle.every(r => r.strokeWidth === '1.6'),
107
+ none_match_15_sentinel: idle.every(r => r.strokeWidth !== '1.5'),
108
+ all_have_transition: idle.every(r => hasOpacityTransition(r)),
109
+
110
+ idle_all_opacity_0: idle.every(r => r.opacity === 0),
111
+ idle_all_active_false: idle.every(r => r.activeAttr === 'false'),
112
+
113
+ // Hover badge → alpha + beta active, gamma + delta inactive
114
+ hover_alpha_active: findRing(hovered, 'alpha')?.activeAttr === 'true',
115
+ hover_alpha_opacity_0p85: Math.abs((findRing(hovered, 'alpha')?.opacity ?? 0) - 0.85) < 0.05,
116
+ hover_beta_active: findRing(hovered, 'beta')?.activeAttr === 'true',
117
+ hover_beta_opacity_0p85: Math.abs((findRing(hovered, 'beta')?.opacity ?? 0) - 0.85) < 0.05,
118
+ hover_gamma_inactive: findRing(hovered, 'gamma')?.activeAttr === 'false',
119
+ hover_gamma_opacity_0: findRing(hovered, 'gamma')?.opacity === 0,
120
+ hover_delta_inactive: findRing(hovered, 'delta')?.activeAttr === 'false',
121
+ hover_delta_opacity_0: findRing(hovered, 'delta')?.opacity === 0,
122
+
123
+ // Mouseleave → all back to 0
124
+ idle_again_all_opacity_0: idleAgain.every(r => r.opacity === 0),
125
+ idle_again_all_inactive: idleAgain.every(r => r.activeAttr === 'false'),
126
+ };
127
+ const ok = Object.values(results).every(Boolean);
128
+ console.log(`${ok ? '✅' : '❌'} endpoint-ring fade:`, JSON.stringify(results),
129
+ `\n idle:`, idle,
130
+ `\n hovered:`, hovered,
131
+ `\n idleAgain:`, idleAgain);
132
+ process.exit(ok ? 0 : 1);