@sleep2agi/agent-network-dashboard 0.4.6 → 0.5.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 (493) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +31 -31
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +16 -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/server/[hostname]/agents/route/app-paths-manifest.json +3 -0
  34. package/.next/server/app/api/hub/server/[hostname]/agents/route/build-manifest.json +9 -0
  35. package/.next/server/app/api/hub/server/[hostname]/agents/route/server-reference-manifest.json +4 -0
  36. package/.next/server/app/api/hub/server/[hostname]/agents/route.js +7 -0
  37. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.map +5 -0
  38. package/.next/server/app/api/hub/server/[hostname]/agents/route.js.nft.json +1 -0
  39. package/.next/server/app/api/hub/server/[hostname]/agents/route_client-reference-manifest.js +3 -0
  40. package/.next/server/app/api/hub/server/[hostname]/health/route/app-paths-manifest.json +3 -0
  41. package/.next/server/app/api/hub/server/[hostname]/health/route/build-manifest.json +9 -0
  42. package/.next/server/app/api/hub/server/[hostname]/health/route/server-reference-manifest.json +4 -0
  43. package/.next/server/app/api/hub/server/[hostname]/health/route.js +7 -0
  44. package/.next/server/app/api/hub/server/[hostname]/health/route.js.map +5 -0
  45. package/.next/server/app/api/hub/server/[hostname]/health/route.js.nft.json +1 -0
  46. package/.next/server/app/api/hub/server/[hostname]/health/route_client-reference-manifest.js +3 -0
  47. package/.next/server/app/index.html +2 -2
  48. package/.next/server/app/index.rsc +14 -14
  49. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  50. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  55. package/.next/server/app/login.html +2 -2
  56. package/.next/server/app/login.rsc +14 -14
  57. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  58. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  59. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  60. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  61. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  62. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  63. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app/logs.html +2 -2
  65. package/.next/server/app/logs.rsc +14 -14
  66. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  67. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  68. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  69. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  70. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  71. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  72. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  73. package/.next/server/app/messages.html +2 -2
  74. package/.next/server/app/messages.rsc +14 -14
  75. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  76. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  77. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  78. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  79. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  80. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  81. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  82. package/.next/server/app/node.html +2 -2
  83. package/.next/server/app/node.rsc +14 -14
  84. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  85. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  86. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  87. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  88. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  89. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  90. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  91. package/.next/server/app/nodes.html +2 -2
  92. package/.next/server/app/nodes.rsc +14 -14
  93. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  94. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  95. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  96. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  97. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  98. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  99. package/.next/server/app/page_client-reference-manifest.js +1 -1
  100. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  101. package/.next/server/app/server-logs.html +2 -2
  102. package/.next/server/app/server-logs.rsc +14 -14
  103. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  104. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  105. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  106. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  107. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  108. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  109. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  110. package/.next/server/app/settings/networks.html +2 -2
  111. package/.next/server/app/settings/networks.rsc +14 -14
  112. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  113. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  114. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  115. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  116. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  117. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  118. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  119. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  120. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  121. package/.next/server/app/settings/tokens.html +2 -2
  122. package/.next/server/app/settings/tokens.rsc +14 -14
  123. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  124. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  125. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  126. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  127. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  128. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  129. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  130. package/.next/server/app/settings.html +2 -2
  131. package/.next/server/app/settings.rsc +14 -14
  132. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  133. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  134. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  135. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  136. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  137. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  138. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  139. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  140. package/.next/server/app/tasks.html +2 -2
  141. package/.next/server/app/tasks.rsc +14 -14
  142. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  143. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  144. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  145. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  146. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  147. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  148. package/.next/server/app-paths-manifest.json +2 -0
  149. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js +3 -0
  150. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_agents_route_actions_0vr92s4.js.map +1 -0
  151. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js +3 -0
  152. package/.next/server/chunks/0dk5_next-internal_server_app_api_hub_server_[hostname]_health_route_actions_0vr-bv5.js.map +1 -0
  153. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js +3 -0
  154. package/.next/server/chunks/[root-of-the-server]__05nyivw._.js.map +1 -0
  155. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js +3 -0
  156. package/.next/server/chunks/[root-of-the-server]__0floe3h._.js.map +1 -0
  157. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +1 -1
  158. package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -1
  159. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -2
  160. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  161. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  162. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  163. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  164. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  165. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  166. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  167. package/.next/server/middleware-build-manifest.js +3 -3
  168. package/.next/server/pages/404.html +2 -2
  169. package/.next/server/pages/500.html +1 -1
  170. package/.next/server/server-reference-manifest.js +1 -1
  171. package/.next/server/server-reference-manifest.json +1 -1
  172. package/.next/static/chunks/0aauz~36q5n2a.css +2 -0
  173. package/.next/static/chunks/0f~0kgy2pycyc.js +1 -0
  174. package/.next/static/chunks/0l_~q07bhpkcx.js +7 -0
  175. package/.next/static/chunks/1175-dp18a1e_.js +1 -0
  176. package/.next/static/chunks/1552j0415oeal.js +4 -0
  177. package/.next/trace +2 -2
  178. package/.next/trace-build +1 -1
  179. package/.next/types/routes.d.ts +3 -1
  180. package/.next/types/validator.ts +18 -0
  181. package/app/api/hub/server/[hostname]/agents/route.ts +62 -0
  182. package/app/api/hub/server/[hostname]/health/route.ts +58 -0
  183. package/app/api/hub/servers/route.ts +88 -1
  184. package/app/components/CommandPalette.tsx +107 -0
  185. package/app/components/HelpOverlay.tsx +109 -1
  186. package/app/components/ServersDrawer.tsx +181 -5
  187. package/app/components/TopoGraph.tsx +6409 -286
  188. package/app/globals.css +110 -5
  189. package/app/lib/vendorIdentity.ts +14 -3
  190. package/docs/rfc/v0.10.0-hero3-dashboard-upgrade.md +230 -0
  191. package/package.json +2 -2
  192. package/public/sleep2agi-logo.svg +11 -0
  193. package/public/vendors/claude.svg +11 -0
  194. package/public/vendors/minimax.svg +11 -0
  195. package/public/vendors/openai.svg +12 -0
  196. package/screenshots/r293-p0/after-full.png +0 -0
  197. package/screenshots/r293-p0/after-topo.png +0 -0
  198. package/screenshots/r293-p0/baseline-full.png +0 -0
  199. package/screenshots/r293-p0/baseline-topo.png +0 -0
  200. package/screenshots/r293-p0/logo-integrated-full.png +0 -0
  201. package/screenshots/r293-p0/logo-integrated-topo.png +0 -0
  202. package/screenshots/r293-p0/servers-drawer-v0.10.0-full.png +0 -0
  203. package/screenshots/r293-p0/servers-drawer-v0.10.0.png +0 -0
  204. package/screenshots/r293-p0/wave1-after-full.png +0 -0
  205. package/screenshots/r293-p0/wave1-after-topo.png +0 -0
  206. package/scripts/p0-servers-drawer-screenshot.mjs +99 -0
  207. package/scripts/p0-topo-screenshot.mjs +82 -0
  208. package/scripts/topo-active-filter-pill-test.mjs +101 -0
  209. package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
  210. package/scripts/topo-active-links-chip-nav-test.mjs +117 -0
  211. package/scripts/topo-active-links-chip-self-hover-test.mjs +171 -0
  212. package/scripts/topo-active-links-empty-recede-test.mjs +125 -0
  213. package/scripts/topo-active-links-freshness-test.mjs +113 -0
  214. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  215. package/scripts/topo-active-pulse-ease-test.mjs +120 -0
  216. package/scripts/topo-brand-logo-transition-test.mjs +94 -0
  217. package/scripts/topo-brand-watermark-test.mjs +97 -0
  218. package/scripts/topo-canvas-aria-test.mjs +108 -0
  219. package/scripts/topo-canvas-brand-fade-test.mjs +87 -0
  220. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  221. package/scripts/topo-chat-target-ring-ease-test.mjs +82 -0
  222. package/scripts/topo-chat-target-ring-fade-test.mjs +124 -0
  223. package/scripts/topo-chip-empty-recede-test.mjs +116 -0
  224. package/scripts/topo-chip-focus-test.mjs +95 -0
  225. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  226. package/scripts/topo-chip-row-gap-test.mjs +90 -0
  227. package/scripts/topo-chip-row-semantic-gap-test.mjs +97 -0
  228. package/scripts/topo-chip-row-tabular-test.mjs +111 -0
  229. package/scripts/topo-chip-unit-demotion-test.mjs +93 -0
  230. package/scripts/topo-chiprow-data-weight-test.mjs +94 -0
  231. package/scripts/topo-chrome-a11y-test.mjs +107 -0
  232. package/scripts/topo-chrome-active-weight-test.mjs +104 -0
  233. package/scripts/topo-chrome-focus-ring-unify-test.mjs +100 -0
  234. package/scripts/topo-chrome-gap-test.mjs +73 -0
  235. package/scripts/topo-chrome-icon-stroke-test.mjs +95 -0
  236. package/scripts/topo-chrome-inactive-color-test.mjs +90 -0
  237. package/scripts/topo-chrome-pop-extension-test.mjs +98 -0
  238. package/scripts/topo-chrome-pop-nodesize-test.mjs +88 -0
  239. package/scripts/topo-chrome-press-state-test.mjs +135 -0
  240. package/scripts/topo-chrome-strip-semantic-gap-test.mjs +99 -0
  241. package/scripts/topo-chrome-strip-theme-ease-test.mjs +99 -0
  242. package/scripts/topo-chrome-zoom-level-pop-test.mjs +122 -0
  243. package/scripts/topo-chrome-zoom-pop-test.mjs +130 -0
  244. package/scripts/topo-chrome-zoom-readout-weight-test.mjs +81 -0
  245. package/scripts/topo-click-ripple-ease-test.mjs +94 -0
  246. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  247. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  248. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  249. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  250. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  251. package/scripts/topo-corner-radius-unify-test.mjs +103 -0
  252. package/scripts/topo-dense-alias-fill-transition-test.mjs +133 -0
  253. package/scripts/topo-discrete-zoom-smooth-test.mjs +125 -0
  254. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  255. package/scripts/topo-edge-badge-crossfade-test.mjs +138 -0
  256. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  257. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  258. package/scripts/topo-edge-badge-lift-test.mjs +150 -0
  259. package/scripts/topo-edge-badge-ripple-test.mjs +111 -0
  260. package/scripts/topo-edge-badge-stroke-transition-test.mjs +118 -0
  261. package/scripts/topo-edge-badge-text-pin-test.mjs +121 -0
  262. package/scripts/topo-edge-badge-theme-ease-test.mjs +89 -0
  263. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  264. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  265. package/scripts/topo-edge-fadein-test.mjs +106 -0
  266. package/scripts/topo-edge-focus-test.mjs +113 -0
  267. package/scripts/topo-edge-hover-test.mjs +13 -5
  268. package/scripts/topo-edge-pill-hot-test.mjs +129 -0
  269. package/scripts/topo-edge-pill-test.mjs +105 -0
  270. package/scripts/topo-edge-ping-pulse-retired-test.mjs +119 -0
  271. package/scripts/topo-edge-stroke-ease-test.mjs +100 -0
  272. package/scripts/topo-edge-stroke-transition-test.mjs +132 -0
  273. package/scripts/topo-empty-hint-tracking-test.mjs +84 -0
  274. package/scripts/topo-empty-state-breath-test.mjs +150 -0
  275. package/scripts/topo-empty-state-crossfade-test.mjs +130 -0
  276. package/scripts/topo-empty-state-recenter-test.mjs +107 -0
  277. package/scripts/topo-empty-subhint-tracking-test.mjs +87 -0
  278. package/scripts/topo-endpoint-ring-fade-test.mjs +132 -0
  279. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  280. package/scripts/topo-endpoint-ring-thicken-test.mjs +112 -0
  281. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  282. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  283. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  284. package/scripts/topo-filter-pill-count-tabular-test.mjs +135 -0
  285. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  286. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  287. package/scripts/topo-freshness-chip-transition-test.mjs +80 -0
  288. package/scripts/topo-freshness-chip-weight-test.mjs +90 -0
  289. package/scripts/topo-freshness-conditional-render-test.mjs +92 -0
  290. package/scripts/topo-fullscreen-active-test.mjs +115 -0
  291. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  292. package/scripts/topo-group-count-test.mjs +6 -1
  293. package/scripts/topo-group-label-a11y-test.mjs +113 -0
  294. package/scripts/topo-group-label-count-inherit-test.mjs +99 -0
  295. package/scripts/topo-group-label-hover-test.mjs +119 -0
  296. package/scripts/topo-group-label-pin-spacing-test.mjs +136 -0
  297. package/scripts/topo-group-label-tint-test.mjs +99 -0
  298. package/scripts/topo-group-label-title-test.mjs +83 -0
  299. package/scripts/topo-group-pin-test.mjs +119 -0
  300. package/scripts/topo-group-pip-fadein-test.mjs +104 -0
  301. package/scripts/topo-group-pip-singletier-drop-test.mjs +131 -0
  302. package/scripts/topo-group-pip-tabular-test.mjs +95 -0
  303. package/scripts/topo-group-status-pips-test.mjs +103 -0
  304. package/scripts/topo-groupbox-fadein-test.mjs +95 -0
  305. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  306. package/scripts/topo-groupbox-hubcore-ease-test.mjs +106 -0
  307. package/scripts/topo-groupbox-live-test.mjs +100 -0
  308. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  309. package/scripts/topo-groupbox-test.mjs +4 -2
  310. package/scripts/topo-header-gap-test.mjs +94 -0
  311. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  312. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  313. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  314. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  315. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  316. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  317. package/scripts/topo-help-overlay-r153-test.mjs +85 -0
  318. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  319. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  320. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  321. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  322. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  323. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  324. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  325. package/scripts/topo-hot-tail-crossfade-test.mjs +117 -0
  326. package/scripts/topo-html-overlay-padding-unify-test.mjs +120 -0
  327. package/scripts/topo-hub-a11y-test.mjs +112 -0
  328. package/scripts/topo-hub-busyness-test.mjs +100 -0
  329. package/scripts/topo-hub-center-crossfade-test.mjs +124 -0
  330. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  331. package/scripts/topo-hub-digit-scale-test.mjs +129 -0
  332. package/scripts/topo-hub-fadein-test.mjs +88 -0
  333. package/scripts/topo-hub-hover-lift-test.mjs +101 -0
  334. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  335. package/scripts/topo-hub-pip-theme-ease-test.mjs +116 -0
  336. package/scripts/topo-hub-spoke-ease-test.mjs +120 -0
  337. package/scripts/topo-hub-title-test.mjs +7 -2
  338. package/scripts/topo-hub-working-count-test.mjs +99 -0
  339. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  340. package/scripts/topo-inner-radar-ring-retired-test.mjs +89 -0
  341. package/scripts/topo-kicker-color-test.mjs +93 -0
  342. package/scripts/topo-kicker-tracking-test.mjs +101 -0
  343. package/scripts/topo-kicker-weight-test.mjs +98 -0
  344. package/scripts/topo-label-card-theme-ease-test.mjs +85 -0
  345. package/scripts/topo-label-lift-test.mjs +5 -1
  346. package/scripts/topo-layout-hover-text-preview-test.mjs +101 -0
  347. package/scripts/topo-layout-switch-crossfade-test.mjs +126 -0
  348. package/scripts/topo-layout-toggle-border-unify-test.mjs +104 -0
  349. package/scripts/topo-layout-toggle-test.mjs +131 -0
  350. package/scripts/topo-layout-trailer-mr-test.mjs +82 -0
  351. package/scripts/topo-legend-compress-test.mjs +107 -0
  352. package/scripts/topo-legend-count-empty-test.mjs +126 -0
  353. package/scripts/topo-legend-count-tabular-parity-test.mjs +90 -0
  354. package/scripts/topo-legend-count-tabular-test.mjs +87 -0
  355. package/scripts/topo-legend-count-test.mjs +88 -0
  356. package/scripts/topo-legend-count-tier-fill-test.mjs +121 -0
  357. package/scripts/topo-legend-count-weight-test.mjs +86 -0
  358. package/scripts/topo-legend-header-symmetric-inset-test.mjs +112 -0
  359. package/scripts/topo-legend-hitbox-swatch-center-test.mjs +93 -0
  360. package/scripts/topo-legend-hover-test.mjs +113 -0
  361. package/scripts/topo-legend-offline-delimiter-test.mjs +84 -0
  362. package/scripts/topo-legend-offline-label-simplify-test.mjs +86 -0
  363. package/scripts/topo-legend-panel-count-weight-test.mjs +89 -0
  364. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  365. package/scripts/topo-legend-pin-ring-fade-test.mjs +112 -0
  366. package/scripts/topo-legend-pin-test.mjs +128 -0
  367. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  368. package/scripts/topo-legend-row-labels-simplify-test.mjs +80 -0
  369. package/scripts/topo-legend-row-pin-spacing-test.mjs +124 -0
  370. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  371. package/scripts/topo-legend-row-title-test.mjs +102 -0
  372. package/scripts/topo-legend-swatch-grow-test.mjs +116 -0
  373. package/scripts/topo-legend-swatch-radius-test.mjs +78 -0
  374. package/scripts/topo-minimap-a11y-test.mjs +101 -0
  375. package/scripts/topo-minimap-dot-transition-test.mjs +113 -0
  376. package/scripts/topo-minimap-radius-test.mjs +88 -0
  377. package/scripts/topo-minimap-rect-stroke-test.mjs +94 -0
  378. package/scripts/topo-minimap-viewport-smooth-test.mjs +124 -0
  379. package/scripts/topo-monogram-font-test.mjs +112 -0
  380. package/scripts/topo-monogram-stroke-test.mjs +101 -0
  381. package/scripts/topo-more-flows-letterspacing-test.mjs +111 -0
  382. package/scripts/topo-node-a11y-test.mjs +107 -0
  383. package/scripts/topo-node-alias-chat-pin-test.mjs +95 -0
  384. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  385. package/scripts/topo-node-label-card-elevation-test.mjs +157 -0
  386. package/scripts/topo-node-label-card-stroke-test.mjs +135 -0
  387. package/scripts/topo-node-stroke-transition-test.mjs +103 -0
  388. package/scripts/topo-node-text-fill-transition-test.mjs +119 -0
  389. package/scripts/topo-node-title-peers-test.mjs +107 -0
  390. package/scripts/topo-node-title-test.mjs +101 -0
  391. package/scripts/topo-nodesize-active-hover-test.mjs +109 -0
  392. package/scripts/topo-nodesize-crossfade-test.mjs +141 -0
  393. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  394. package/scripts/topo-orbit-particles-retired-test.mjs +91 -0
  395. package/scripts/topo-panel-count-unit-test.mjs +112 -0
  396. package/scripts/topo-panel-elevation-test.mjs +76 -0
  397. package/scripts/topo-panel-fadein-test.mjs +102 -0
  398. package/scripts/topo-panel-hot-tail-fade-test.mjs +132 -0
  399. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  400. package/scripts/topo-panel-rx-test.mjs +90 -0
  401. package/scripts/topo-panel-theme-ease-test.mjs +85 -0
  402. package/scripts/topo-panel-title-fill-ease-test.mjs +91 -0
  403. package/scripts/topo-panel-title-tracking-test.mjs +78 -0
  404. package/scripts/topo-particle-stagger-test.mjs +82 -0
  405. package/scripts/topo-pin-intersection-color-ease-test.mjs +111 -0
  406. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  407. package/scripts/topo-pin-intersection-tabular-test.mjs +96 -0
  408. package/scripts/topo-pin-intersection-test.mjs +121 -0
  409. package/scripts/topo-pin-intersection-warning-crossfade-test.mjs +107 -0
  410. package/scripts/topo-pin-mirror-transition-test.mjs +101 -0
  411. package/scripts/topo-pin-persistence-test.mjs +105 -0
  412. package/scripts/topo-prefix-opacity-test.mjs +107 -0
  413. package/scripts/topo-prepublish-guard-test.mjs +95 -0
  414. package/scripts/topo-pressure-hover-test.mjs +123 -0
  415. package/scripts/topo-pressure-pin-test.mjs +119 -0
  416. package/scripts/topo-pressure-seg-self-hover-test.mjs +126 -0
  417. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  418. package/scripts/topo-pressure-seg-transition-test.mjs +107 -0
  419. package/scripts/topo-pulse-dot-crossfade-test.mjs +122 -0
  420. package/scripts/topo-pulse-dot-retired-test.mjs +86 -0
  421. package/scripts/topo-recent-panel-count-freshness-test.mjs +109 -0
  422. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  423. package/scripts/topo-recent-panel-count-weight-test.mjs +91 -0
  424. package/scripts/topo-recent-panel-footer-breath-test.mjs +142 -0
  425. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  426. package/scripts/topo-recent-panel-hot-tabular-test.mjs +113 -0
  427. package/scripts/topo-recent-panel-more-crossfade-test.mjs +124 -0
  428. package/scripts/topo-recent-panel-more-cyan-hover-test.mjs +151 -0
  429. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  430. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  431. package/scripts/topo-recent-panel-readability-bump-test.mjs +151 -0
  432. package/scripts/topo-recent-row-count-fill-test.mjs +129 -0
  433. package/scripts/topo-recent-row-count-weight-test.mjs +112 -0
  434. package/scripts/topo-recent-row-fadein-test.mjs +107 -0
  435. package/scripts/topo-recent-row-freshness-test.mjs +102 -0
  436. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  437. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  438. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  439. package/scripts/topo-recent-row-pin-spacing-test.mjs +137 -0
  440. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  441. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  442. package/scripts/topo-recent-row-title-test.mjs +112 -0
  443. package/scripts/topo-recent-row-ts-alpha-test.mjs +100 -0
  444. package/scripts/topo-recent-row-ts-tabular-test.mjs +108 -0
  445. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  446. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  447. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  448. package/scripts/topo-reset-spin-test.mjs +120 -0
  449. package/scripts/topo-runtime-badge-grow-test.mjs +114 -0
  450. package/scripts/topo-spokes-retired-test.mjs +93 -0
  451. package/scripts/topo-starfield-density-test.mjs +89 -0
  452. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  453. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  454. package/scripts/topo-statusbar-chips-self-hover-test.mjs +177 -0
  455. package/scripts/topo-svg-focus-test.mjs +112 -0
  456. package/scripts/topo-sweep-busyness-test.mjs +94 -0
  457. package/scripts/topo-tabular-nums-sweep-test.mjs +93 -0
  458. package/scripts/topo-tier-guide-dashes-test.mjs +86 -0
  459. package/scripts/topo-tier-ring-fadein-test.mjs +107 -0
  460. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  461. package/scripts/topo-tier-ring-test.mjs +69 -0
  462. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  463. package/scripts/topo-tier-stagger-test.mjs +89 -0
  464. package/scripts/topo-title-block-tight-leading-test.mjs +95 -0
  465. package/scripts/topo-title-tracking-test.mjs +98 -0
  466. package/scripts/topo-titleblock-gap-test.mjs +86 -0
  467. package/scripts/topo-titleblock-margin-test.mjs +83 -0
  468. package/scripts/topo-toggle-inactive-cyan-hover-test.mjs +110 -0
  469. package/scripts/topo-top-rail-theme-ease-test.mjs +89 -0
  470. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  471. package/scripts/topo-vendor-chip-self-hover-test.mjs +131 -0
  472. package/scripts/topo-vendor-chip-tabular-test.mjs +92 -0
  473. package/scripts/topo-vendor-chip-threshold-test.mjs +78 -0
  474. package/scripts/topo-vendor-chip-weight-test.mjs +87 -0
  475. package/scripts/topo-vendor-count-suffix-test.mjs +95 -0
  476. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  477. package/scripts/topo-vendor-pill-test.mjs +125 -0
  478. package/scripts/topo-vendor-pin-test.mjs +125 -0
  479. package/scripts/topo-view-smooth-test.mjs +131 -0
  480. package/scripts/topo-watermark-tracking-test.mjs +99 -0
  481. package/scripts/topo-working-chip-pin-test.mjs +154 -0
  482. package/scripts/topo-working-halo-breath-retired-test.mjs +99 -0
  483. package/scripts/topo-wrapper-flow-arrow-theme-test.mjs +97 -0
  484. package/scripts/topo-wrapper-radius-test.mjs +73 -0
  485. package/scripts/topo-wrapper-shadow-theme-ease-test.mjs +89 -0
  486. package/.next/static/chunks/088ouqy7tekty.js +0 -2
  487. package/.next/static/chunks/0hyl7mdapsz38.js +0 -1
  488. package/.next/static/chunks/0mqwn-l7wtcdo.js +0 -7
  489. package/.next/static/chunks/127kw7~wsnyu7.css +0 -2
  490. package/.next/static/chunks/16yfxp9ak2gdm.js +0 -1
  491. /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_buildManifest.js +0 -0
  492. /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_clientMiddlewareManifest.js +0 -0
  493. /package/.next/static/{23MxP0RBVA4BaOJ_HMYmM → 424zX3i80AJ_wHF_otKuP}/_ssgManifest.js +0 -0
@@ -0,0 +1,106 @@
1
+ /* Round 172 verification: edges fade-in alongside R9 staggered
2
+ * node reveal.
3
+ *
4
+ * Pre-R172 nodes used `anet-fade-in` with R9 stagger
5
+ * (animationDelay = tier×180 + nodeIdx×25 / nodeIdx×25 capped
6
+ * at 24) but edges appeared instantly. First-paint sequence:
7
+ * nodes ease in over ~600ms while edges already at full
8
+ * visibility — reveal looks inconsistent.
9
+ *
10
+ * R172 wraps each edge <g> in:
11
+ * className="anet-fade-in"
12
+ * style={{ animationDelay: '280ms + index*35ms (cap 20)' }}
13
+ * data-edge-group={link.key}
14
+ *
15
+ * Edges start fading in 280ms after first paint (after node
16
+ * stagger lands), staggered by 35ms intra-edge offset.
17
+ *
18
+ * Test:
19
+ * 1. Mock 4 sessions + 3 flows
20
+ * 2. Probe each edge <g[data-edge-group]>
21
+ * 3. Verify .anet-fade-in className present
22
+ * 4. Verify animation-delay attribute increments by 35ms
23
+ * starting from 280ms (clamped at idx=20)
24
+ */
25
+ import { chromium } from 'playwright';
26
+ import { readFileSync } from 'node:fs';
27
+
28
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
29
+ const browser = await chromium.launch({ headless: true });
30
+
31
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
32
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
33
+ await ctx.addInitScript(() => {
34
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
35
+ });
36
+
37
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
38
+ await ctx.route('**/api/hub/status*', async (route) => {
39
+ const r = await route.fetch();
40
+ const b = await r.json();
41
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
42
+ const mk = (alias) => ({
43
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
44
+ network_id: nid, project_dir: null,
45
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
46
+ });
47
+ await route.fulfill({ response: r, json: { ...b, sessions: [
48
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
49
+ ] } });
50
+ });
51
+
52
+ const now = Date.now();
53
+ // 3 distinct flow links so we can probe stagger across multiple indices
54
+ const msgs = [
55
+ { id: 'a', from_alias: 'alpha', to_alias: 'beta', content: 'hi', network_id: 'default', created_at: new Date(now - 5000).toISOString() },
56
+ { id: 'b', from_alias: 'beta', to_alias: 'gamma', content: 'hi', network_id: 'default', created_at: new Date(now - 10000).toISOString() },
57
+ { id: 'c', from_alias: 'gamma', to_alias: 'delta', content: 'hi', network_id: 'default', created_at: new Date(now - 15000).toISOString() },
58
+ ];
59
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
60
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
61
+
62
+ const page = await ctx.newPage();
63
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
64
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
65
+ await page.waitForSelector('[data-edge-group]', { timeout: 10000 });
66
+ await page.waitForTimeout(400);
67
+
68
+ const probe = await page.evaluate(() => {
69
+ const edges = [...document.querySelectorAll('[data-edge-group]')];
70
+ return edges.map(e => ({
71
+ key: e.getAttribute('data-edge-group'),
72
+ hasFadeClass: (e.getAttribute('class') || '').includes('anet-fade-in'),
73
+ animationDelay: e.style.animationDelay || getComputedStyle(e).animationDelay,
74
+ }));
75
+ });
76
+
77
+ await browser.close();
78
+
79
+ if (probe.length < 3) {
80
+ console.log(`❌ wrong edge count: expected 3+, got ${probe.length}`, probe);
81
+ process.exit(1);
82
+ }
83
+
84
+ // Parse animationDelay (e.g. "280ms" or "0.28s") → number in ms
85
+ const parseDelay = (s) => {
86
+ if (!s) return null;
87
+ if (s.endsWith('ms')) return parseFloat(s);
88
+ if (s.endsWith('s')) return parseFloat(s) * 1000;
89
+ return parseFloat(s);
90
+ };
91
+ const delays = probe.map(p => parseDelay(p.animationDelay));
92
+
93
+ const results = {
94
+ three_plus_edges: probe.length >= 3,
95
+ all_have_fadeClass: probe.every(p => p.hasFadeClass),
96
+ all_have_delay: delays.every(d => d !== null && d >= 280),
97
+ first_edge_280ms: Math.abs(delays[0] - 280) < 0.5,
98
+ second_edge_315ms: Math.abs(delays[1] - 315) < 0.5,
99
+ third_edge_350ms: Math.abs(delays[2] - 350) < 0.5,
100
+ delays_strictly_increase: delays[0] < delays[1] && delays[1] < delays[2],
101
+ };
102
+ const ok = Object.values(results).every(Boolean);
103
+ console.log(`${ok ? '✅' : '❌'} edge fade-in:`, JSON.stringify(results),
104
+ `\n edges =`, probe,
105
+ `\n delays (ms) =`, delays);
106
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,113 @@
1
+ /* Round 50 verification: hovering a flow edge boosts that edge's own
2
+ * opacity 2× and thickens its stroke 1.4×, while every other edge dims
3
+ * to 0.35×. Complements R49 (which highlights the endpoint nodes) —
4
+ * together they make "this is the edge you're pointing at" unmistakable.
5
+ *
6
+ * Sessions: alpha, beta, gamma, delta. Messages: alpha→beta + gamma→delta.
7
+ * Hover the alpha→beta hitbox and read:
8
+ * - alpha→beta visible-path opacity boosts (~2× base)
9
+ * - alpha→beta visible-path strokeWidth = 1.4 × base (rounded by browser)
10
+ * - gamma→delta visible-path opacity dims (~0.35× base)
11
+ * - gamma→delta visible-path strokeWidth unchanged
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const browser = await chromium.launch({ headless: true });
18
+
19
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 1500 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try {
23
+ localStorage.setItem('anet-theme', 'cyber');
24
+ localStorage.setItem('anet-topo-layout', 'ring');
25
+ sessionStorage.setItem('anet_v3_auth', '1');
26
+ } catch {}
27
+ });
28
+
29
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
30
+ await ctx.route('**/api/hub/status*', async (route) => {
31
+ const r = await route.fetch();
32
+ const b = await r.json();
33
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
34
+ const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
35
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
36
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
37
+ }));
38
+ await route.fulfill({ response: r, json: { ...b, sessions } });
39
+ });
40
+ const now = new Date().toISOString();
41
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
42
+ { from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
43
+ { from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
44
+ ] } }));
45
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
46
+ const page = await ctx.newPage();
47
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
48
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
49
+ await page.waitForTimeout(600);
50
+
51
+ // Read each flow edge's visible base path (the one with marker-end).
52
+ const readEdges = () => page.evaluate(() => {
53
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
54
+ const rows = [];
55
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
56
+ const t = g.querySelector('path[data-edge-hitbox] title');
57
+ if (!t) continue;
58
+ const route = (t.textContent || '').split('\n')[0];
59
+ const base = [...g.querySelectorAll(':scope > path')].find(
60
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
61
+ );
62
+ if (!base) continue;
63
+ rows.push({
64
+ route,
65
+ opacity: +base.getAttribute('opacity'),
66
+ strokeWidth: +base.getAttribute('stroke-width'),
67
+ });
68
+ }
69
+ return rows;
70
+ });
71
+
72
+ const before = await readEdges();
73
+
74
+ // Move cursor to a point ON the alpha→beta hitbox curve.
75
+ const target = await page.evaluate(() => {
76
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
77
+ for (const g of svg.querySelectorAll(':scope > g > g')) {
78
+ const t = g.querySelector('path[data-edge-hitbox] title');
79
+ if (t && /^alpha → beta/.test(t.textContent || '')) {
80
+ const hb = g.querySelector('path[data-edge-hitbox]');
81
+ const pt = hb.getPointAtLength(hb.getTotalLength() / 2);
82
+ const ctm = hb.getScreenCTM();
83
+ return { x: pt.x * ctm.a + pt.y * ctm.c + ctm.e, y: pt.x * ctm.b + pt.y * ctm.d + ctm.f };
84
+ }
85
+ }
86
+ return null;
87
+ });
88
+ if (!target) { console.log('❌ no alpha→beta hitbox found'); process.exit(1); }
89
+ await page.mouse.move(10, 10);
90
+ await page.mouse.move(target.x, target.y);
91
+ await page.waitForTimeout(350);
92
+ const during = await readEdges();
93
+
94
+ await browser.close();
95
+
96
+ const find = (rows, r) => rows.find(e => e.route === r);
97
+ const b_ab = find(before, 'alpha → beta');
98
+ const b_gd = find(before, 'gamma → delta');
99
+ const a_ab = find(during, 'alpha → beta');
100
+ const a_gd = find(during, 'gamma → delta');
101
+
102
+ const results = {
103
+ baselineMatches: b_ab && b_gd && Math.abs(b_ab.opacity - b_gd.opacity) < 0.001 && b_ab.strokeWidth === b_gd.strokeWidth,
104
+ hoveredEdgeBrightens: a_ab && a_ab.opacity > b_ab.opacity * 1.5,
105
+ hoveredEdgeThickens: a_ab && a_ab.strokeWidth > b_ab.strokeWidth * 1.2,
106
+ otherEdgeDims: a_gd && a_gd.opacity < b_gd.opacity * 0.6,
107
+ otherEdgeStaysThin: a_gd && a_gd.strokeWidth === b_gd.strokeWidth,
108
+ };
109
+ const ok = Object.values(results).every(Boolean);
110
+ console.log(`${ok ? '✅' : '❌'} edge focus:`, JSON.stringify(results),
111
+ `\n before α→β=${JSON.stringify(b_ab)} γ→δ=${JSON.stringify(b_gd)}`,
112
+ `\n during α→β=${JSON.stringify(a_ab)} γ→δ=${JSON.stringify(a_gd)}`);
113
+ process.exit(ok ? 0 : 1);
@@ -45,13 +45,21 @@ await page.waitForTimeout(600);
45
45
 
46
46
  const edgeOpacities = () => page.evaluate(() => {
47
47
  const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
48
- // Each flow-link <g> has two paths; the FIRST one is the base path
49
- // (carries the <title> with route) — read its opacity.
48
+ // R48 reordered the children of each flow-link <g>:
49
+ // path[data-edge-hitbox] <-- transparent hover surface, carries <title>
50
+ // path[marker-end] <-- the visible base edge with opacity
51
+ // path[id^=flow-path-] <-- the dashed runway
52
+ // Read the route from the hitbox's <title>, but the opacity from the
53
+ // visible base path. Picking by marker-end keeps the selector resilient
54
+ // to future siblings.
50
55
  return [...svg.querySelectorAll(':scope > g > g')]
51
56
  .map(g => {
52
- const base = g.querySelector('path');
53
- const title = base?.querySelector('title');
54
- if (!title) return null;
57
+ const hitbox = g.querySelector('path[data-edge-hitbox]');
58
+ const title = hitbox?.querySelector('title');
59
+ const base = [...g.querySelectorAll(':scope > path')].find(
60
+ p => !p.hasAttribute('data-edge-hitbox') && p.hasAttribute('marker-end')
61
+ );
62
+ if (!title || !base) return null;
55
63
  return { route: title.textContent.split('\n')[0], opacity: +base.getAttribute('opacity') };
56
64
  })
57
65
  .filter(Boolean);
@@ -0,0 +1,129 @@
1
+ /* Round 150 verification: R119 edge filter pill extends the hot-
2
+ * lane convention. When the pinned edge has count >= 10, the
3
+ * count tspan inside the pill flips to amber + 700 weight, and
4
+ * the tooltip grows a "hot lane · ≥ 10" marker.
5
+ *
6
+ * 4th hot-lane surface now active:
7
+ * R126 canvas badge → amber stroke when count >= 10
8
+ * R127 recent-row count → amber + bold tspan
9
+ * R129 panel header tail → "· N hot" amber suffix
10
+ * R150 edge filter pill → amber count tspan + tooltip marker
11
+ *
12
+ * All four use the same hotStroke palette (#d97706 light / #fbbf24
13
+ * dark) so the eye instantly recognises hot signal across
14
+ * surfaces.
15
+ *
16
+ * Two test states:
17
+ * - Pin a hot edge (count >= 10):
18
+ * - data-active-filter-edge-hot="true"
19
+ * - data-active-filter-edge-count-hot tspan present
20
+ * - tspan color = amber, weight = 700
21
+ * - title contains "hot lane · ≥ 10"
22
+ * - Pin a warm edge (count < 10):
23
+ * - data-active-filter-edge-hot="false"
24
+ * - data-active-filter-edge-count tspan present (non-hot)
25
+ * - title does NOT contain "hot lane"
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: 1400, 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, status) => ({
45
+ alias, status, 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
+ await route.fulfill({ response: r, json: { ...b, sessions: [
50
+ mk('alpha', 'working'), mk('beta', 'working'), mk('gamma', 'idle'),
51
+ ] } });
52
+ });
53
+
54
+ const now = Date.now();
55
+ const mkMsg = (id, from, to, ageMs) => ({
56
+ id, from_alias: from, to_alias: to, content: 'hi',
57
+ network_id: 'default', created_at: new Date(now - ageMs).toISOString(),
58
+ });
59
+ const msgs = [];
60
+ // 12 alpha→beta = HOT (count >= 10)
61
+ for (let i = 0; i < 12; i++) msgs.push(mkMsg(`a${i}`, 'alpha', 'beta', 20000 + i * 500));
62
+ // 4 beta→gamma = warm
63
+ for (let i = 0; i < 4; i++) msgs.push(mkMsg(`b${i}`, 'beta', 'gamma', 30000 + i * 500));
64
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
65
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
66
+
67
+ const page = await ctx.newPage();
68
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
69
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
70
+ await page.waitForSelector('[data-recent-row="alpha->beta"]', { timeout: 10000 });
71
+ await page.waitForTimeout(400);
72
+
73
+ const inspectPill = () => page.evaluate(() => {
74
+ const pill = document.querySelector('[data-active-filter="edge"]');
75
+ if (!pill) return null;
76
+ const hotTspan = pill.querySelector('[data-active-filter-edge-count-hot]');
77
+ const warmTspan = pill.querySelector('[data-active-filter-edge-count]');
78
+ return {
79
+ hot: pill.getAttribute('data-active-filter-edge-hot'),
80
+ title: pill.getAttribute('title'),
81
+ hotTspanText: hotTspan?.textContent?.trim(),
82
+ hotTspanStyle: hotTspan?.getAttribute('style'),
83
+ warmTspanText: warmTspan?.textContent?.trim(),
84
+ matchCount: pill.getAttribute('data-filter-match-count'),
85
+ };
86
+ });
87
+
88
+ // Pin hot edge alpha→beta (12 msgs)
89
+ await page.locator('[data-recent-row="alpha->beta"]').click();
90
+ await page.waitForTimeout(250);
91
+ const hot = await inspectPill();
92
+
93
+ // Unpin → pin warm edge beta→gamma (4 msgs)
94
+ await page.keyboard.press('Escape');
95
+ await page.waitForTimeout(200);
96
+ await page.locator('[data-recent-row="beta->gamma"]').click();
97
+ await page.waitForTimeout(250);
98
+ const warm = await inspectPill();
99
+
100
+ await browser.close();
101
+
102
+ // cyber theme amber #fbbf24 = rgb(251, 191, 36)
103
+ const amberInStyle = (s) =>
104
+ !!s && (/rgba?\(251,\s*191,\s*36/i.test(s) || /#fbbf24/i.test(s));
105
+
106
+ const results = {
107
+ // Hot edge pinned (alpha→beta, 12 msgs)
108
+ hot_pillPresent: hot !== null,
109
+ hot_attrTrue: hot?.hot === 'true',
110
+ hot_matchCount12: hot?.matchCount === '12',
111
+ hot_tspanHasCount: hot?.hotTspanText?.includes('12') === true,
112
+ hot_tspanAmberColor: amberInStyle(hot?.hotTspanStyle || ''),
113
+ hot_tspanWeight700: (hot?.hotTspanStyle || '').includes('font-weight: 700'),
114
+ hot_titleHotMarker: (hot?.title || '').includes('hot lane'),
115
+ hot_noWarmTspan: hot?.warmTspanText === undefined,
116
+
117
+ // Warm edge pinned (beta→gamma, 4 msgs)
118
+ warm_pillPresent: warm !== null,
119
+ warm_attrFalse: warm?.hot === 'false',
120
+ warm_matchCount4: warm?.matchCount === '4',
121
+ warm_tspanHasCount: warm?.warmTspanText?.includes('4') === true,
122
+ warm_noHotTspan: warm?.hotTspanText === undefined,
123
+ warm_titleNoHotMarker: !(warm?.title || '').includes('hot lane'),
124
+ };
125
+ const ok = Object.values(results).every(Boolean);
126
+ console.log(`${ok ? '✅' : '❌'} edge pill hot:`, JSON.stringify(results),
127
+ `\n hot=`, hot,
128
+ `\n warm=`, warm);
129
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,105 @@
1
+ /* Round 119 verification: when a recent-signal row is pinned (R116),
2
+ * the chip row renders a 4th "filter: from→to · N" pill matching
3
+ * the R64 / R89 pattern for status / group / vendor pins. Closes the
4
+ * pill-pattern gap left after R116.
5
+ *
6
+ * Verifies:
7
+ * - Before pin: no edge pill
8
+ * - Click row → pill shows correct from→to and count
9
+ * - Click pill body → clears
10
+ * - × button also clears
11
+ * - data-filter-match-aliases lists the two endpoints
12
+ */
13
+ import { chromium } from 'playwright';
14
+ import { readFileSync } from 'node:fs';
15
+
16
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
17
+ const browser = await chromium.launch({ headless: true });
18
+
19
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
20
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
21
+ await ctx.addInitScript(() => {
22
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
23
+ });
24
+
25
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
26
+ await ctx.route('**/api/hub/status*', async (route) => {
27
+ const r = await route.fetch();
28
+ const b = await r.json();
29
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
30
+ const mk = (alias) => ({
31
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
32
+ network_id: nid, project_dir: null,
33
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
34
+ });
35
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
36
+ });
37
+
38
+ const now = Date.now();
39
+ const mkMsg = (id, from_alias, to_alias, ageMs) => ({
40
+ id, from_alias, to_alias, content: 'hi', network_id: 'default',
41
+ created_at: new Date(now - ageMs).toISOString(),
42
+ });
43
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
44
+ mkMsg('m1', 'alpha', 'beta', 30 * 1000),
45
+ mkMsg('m2', 'alpha', 'beta', 40 * 1000),
46
+ mkMsg('m3', 'alpha', 'beta', 50 * 1000),
47
+ ] } }));
48
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
49
+ const page = await ctx.newPage();
50
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
51
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
52
+ await page.waitForSelector('[data-recent-row]', { timeout: 10000 });
53
+ await page.waitForTimeout(400);
54
+
55
+ const readPill = () => page.evaluate(() => {
56
+ const p = document.querySelector('[data-active-filter="edge"]');
57
+ if (!p) return null;
58
+ return {
59
+ text: (p.textContent || '').trim(),
60
+ count: p.getAttribute('data-filter-match-count'),
61
+ aliases: p.getAttribute('data-filter-match-aliases') || '',
62
+ title: p.getAttribute('title') || '',
63
+ };
64
+ });
65
+
66
+ const before = await readPill();
67
+
68
+ // Click row to pin.
69
+ await page.locator('[data-recent-row="alpha->beta"]').click();
70
+ await page.mouse.move(10, 10);
71
+ await page.waitForTimeout(300);
72
+ const afterPin = await readPill();
73
+
74
+ // Click pill body to clear.
75
+ await page.locator('[data-active-filter="edge"]').click();
76
+ await page.mouse.move(10, 10);
77
+ await page.waitForTimeout(300);
78
+ const afterBodyClear = await readPill();
79
+
80
+ // Pin again, then click × button.
81
+ await page.locator('[data-recent-row="alpha->beta"]').click();
82
+ await page.mouse.move(10, 10);
83
+ await page.waitForTimeout(300);
84
+ await page.locator('[data-active-filter="edge"] button').click();
85
+ await page.waitForTimeout(300);
86
+ const afterXClear = await readPill();
87
+
88
+ await browser.close();
89
+
90
+ const results = {
91
+ before_noPill: before === null,
92
+ pin_pillRendered: afterPin !== null,
93
+ pin_textContainsArrow: /alpha→beta/.test(afterPin?.text || ''),
94
+ pin_count3: afterPin?.count === '3',
95
+ pin_aliasesMatch: afterPin?.aliases === 'alpha,beta',
96
+ pin_titleHasFromTo: /alpha → beta \(3 msgs\)/.test(afterPin?.title || ''),
97
+ bodyClear_pillGone: afterBodyClear === null,
98
+ xClear_pillGone: afterXClear === null,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} edge pill:`, JSON.stringify(results),
102
+ `\n afterPin=`, afterPin,
103
+ `\n afterBodyClear=`, afterBodyClear,
104
+ `\n afterXClear=`, afterXClear);
105
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,119 @@
1
+ /* Round 279 verification: arrival ping + dispatch pulse SMIL retired
2
+ * (R75 + R76 + R228 + R231 + R252 family) — 减法 cut #5.
3
+ *
4
+ * Per active flow edge the pre-R279 SMIL family was:
5
+ * particle (R50 animateMotion along curve path) ← KEPT
6
+ * arrival ping (R75 circle + r/opacity animate) ← RETIRED
7
+ * dispatch pulse (R76 circle + r/opacity animate) ← RETIRED
8
+ *
9
+ * For a 5-edge fleet that's 5×3 = 15 simultaneous SMIL on edges
10
+ * alone, pre-R279. The particle (a moving dot along the path) is the
11
+ * primary "data flowing from A → B" visual signal; the ping + pulse
12
+ * are secondary "arrival/dispatch confirmation" that the moving
13
+ * particle already conveys. R279 culls ping + pulse, keeps particle.
14
+ *
15
+ * Net: -10 SMIL animations for a 5-edge fleet. Combined with R276
16
+ * (-4 orbit) and R278 (-4 working halo), the canvas motion budget
17
+ * is now: 1 hub breath + N particles per active edge, that's it.
18
+ *
19
+ * Test scope:
20
+ * 1. No element matches `[data-arrival-ping]` (R75 retired).
21
+ * 2. No element matches `[data-dispatch-pulse]` (R76 retired).
22
+ * 3. Particles still present (`[data-edge-particle]` or equivalent).
23
+ * 4. R275 freshness chip absent (regression).
24
+ * 5. R276 orbit absent (regression).
25
+ * 6. R277 legend height 88 (regression).
26
+ * 7. R278 working halo <animate> count 0 (regression).
27
+ */
28
+ import { chromium } from 'playwright';
29
+ import { readFileSync } from 'node:fs';
30
+
31
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
32
+ const browser = await chromium.launch({ headless: true });
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
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
39
+ await ctx.route('**/api/hub/status*', async (route) => {
40
+ const r = await route.fetch();
41
+ const b = await r.json();
42
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
43
+ const mk = (alias) => ({
44
+ alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
45
+ network_id: nid, project_dir: null,
46
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
47
+ });
48
+ await route.fulfill({ response: r, json: { ...b, sessions: [
49
+ mk('alpha'), mk('beta'), mk('gamma'), mk('delta'),
50
+ ] } });
51
+ });
52
+ // 5 distinct flow pairs so particles + (formerly) ping/pulse mount
53
+ // on multiple edges with link.count >= 3 (the dispatch-pulse gate
54
+ // pre-R279).
55
+ const now = Date.now();
56
+ const msgs = [];
57
+ for (let pair = 0; pair < 5; pair++) {
58
+ for (let i = 0; i < 5; i++) {
59
+ const from = ['alpha','beta','gamma','delta'][pair % 4];
60
+ const to = ['beta','gamma','delta','alpha'][pair % 4];
61
+ msgs.push({
62
+ id: `m${pair}-${i}`, from_alias: from, to_alias: to, content: 'hi',
63
+ network_id: 'default',
64
+ created_at: new Date(now - (1000 + pair * 200 + i * 50)).toISOString(),
65
+ });
66
+ }
67
+ }
68
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: msgs } }));
69
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
70
+
71
+ const page = await ctx.newPage();
72
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
73
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
74
+ await page.waitForSelector('[data-topo-panel="legend"]', { timeout: 10000 });
75
+ await page.waitForSelector('[data-edge-particle]', { timeout: 10000, state: 'attached' });
76
+ await page.waitForTimeout(300);
77
+
78
+ const probe = await page.evaluate(() => {
79
+ const ping = document.querySelectorAll('[data-arrival-ping]');
80
+ const pulse = document.querySelectorAll('[data-dispatch-pulse]');
81
+ const particle = document.querySelectorAll('[data-edge-particle]');
82
+ const freshnessChip = document.querySelector('[data-freshness-chip]');
83
+ const orbits = document.querySelectorAll('[data-topo-orbit-bucket]');
84
+ const legendG = document.querySelector('[data-topo-panel="legend"]');
85
+ const legendH = legendG?.querySelector('rect')?.getAttribute('height') ?? null;
86
+ const halos = document.querySelectorAll('[data-node-halo-breath="on"]');
87
+ let haloAnimateCount = 0;
88
+ halos.forEach((h) => { haloAnimateCount += h.querySelectorAll('animate').length; });
89
+ return {
90
+ pingCount: ping.length,
91
+ pulseCount: pulse.length,
92
+ particleCount: particle.length,
93
+ freshnessPresent: freshnessChip !== null,
94
+ orbitCount: orbits.length,
95
+ legendHeight: legendH,
96
+ haloAnimateCount,
97
+ };
98
+ });
99
+ await browser.close();
100
+
101
+ const results = {
102
+ arrival_ping_absent: probe.pingCount === 0,
103
+ dispatch_pulse_absent: probe.pulseCount === 0,
104
+ particles_still_present: probe.particleCount > 0,
105
+ r275_freshness_absent: probe.freshnessPresent === false,
106
+ r276_orbit_absent: probe.orbitCount === 0,
107
+ r277_legend_height_88: probe.legendHeight === '88',
108
+ r278_halo_animate_count_zero: probe.haloAnimateCount === 0,
109
+ };
110
+ const ok = Object.values(results).every(Boolean);
111
+ console.log(`${ok ? '✅' : '❌'} edge ping/pulse retired:`, JSON.stringify(results),
112
+ '\n arrival ping count (expect 0):', probe.pingCount,
113
+ '\n dispatch pulse count (expect 0):', probe.pulseCount,
114
+ '\n particle count (expect > 0):', probe.particleCount,
115
+ '\n freshness absent:', !probe.freshnessPresent,
116
+ '\n orbit count (expect 0):', probe.orbitCount,
117
+ '\n legend height:', probe.legendHeight,
118
+ '\n halo animate count (expect 0):', probe.haloAnimateCount);
119
+ process.exit(ok ? 0 : 1);