@sleep2agi/agent-network-dashboard 0.4.6-preview.15 → 0.4.6-preview.151

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 (355) 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/01lmbsd37fybu.js +1 -0
  181. package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
  182. package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
  183. package/.next/static/chunks/05b69-7w4fvbn.css +2 -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/0ax6wj36e_knr.js +7 -0
  187. package/.next/static/chunks/0d45f.svln5hm.js +1 -0
  188. package/.next/static/chunks/0f6m4wjhlakrw.js +1 -0
  189. package/.next/static/chunks/0g1ap~wkozy~6.js +4 -0
  190. package/.next/static/chunks/0glmrdj9112tc.js +7 -0
  191. package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
  192. package/.next/static/chunks/0x8x.29hkju5a.js +1 -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 +115 -0
  202. package/app/components/ServersDrawer.tsx +64 -27
  203. package/app/components/TopoGraph.tsx +2866 -190
  204. package/app/components/utils.ts +8 -7
  205. package/app/globals.css +27 -1
  206. package/app/lib/time.ts +36 -0
  207. package/package.json +1 -1
  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-test.mjs +73 -0
  214. package/scripts/topo-active-links-tooltip-test.mjs +79 -0
  215. package/scripts/topo-arrival-ping-test.mjs +87 -0
  216. package/scripts/topo-arrow-tier-test.mjs +61 -0
  217. package/scripts/topo-auto-fit-test.mjs +73 -0
  218. package/scripts/topo-chat-focus-ring-test.mjs +77 -0
  219. package/scripts/topo-chat-target-breath-test.mjs +84 -0
  220. package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
  221. package/scripts/topo-click-ripple-test.mjs +66 -0
  222. package/scripts/topo-cmdk-edge-clear-test.mjs +109 -0
  223. package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
  224. package/scripts/topo-cmdk-pin-test.mjs +123 -0
  225. package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
  226. package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
  227. package/scripts/topo-cwd-tooltip-test.mjs +64 -0
  228. package/scripts/topo-dblclick-reset-test.mjs +74 -0
  229. package/scripts/topo-dispatch-pulse-test.mjs +83 -0
  230. package/scripts/topo-edge-badge-click-pin-test.mjs +94 -0
  231. package/scripts/topo-edge-badge-hot-test.mjs +115 -0
  232. package/scripts/topo-edge-badge-hover-test.mjs +85 -0
  233. package/scripts/topo-edge-count-badge-test.mjs +86 -0
  234. package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
  235. package/scripts/topo-edge-focus-test.mjs +113 -0
  236. package/scripts/topo-edge-hover-test.mjs +93 -0
  237. package/scripts/topo-edge-pill-test.mjs +105 -0
  238. package/scripts/topo-edge-tooltip-test.mjs +70 -0
  239. package/scripts/topo-endpoint-ring-test.mjs +93 -0
  240. package/scripts/topo-esc-clears-pin-test.mjs +98 -0
  241. package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
  242. package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
  243. package/scripts/topo-filter-pill-count-test.mjs +110 -0
  244. package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
  245. package/scripts/topo-fit-key-test.mjs +106 -0
  246. package/scripts/topo-freshness-chip-test.mjs +74 -0
  247. package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
  248. package/scripts/topo-group-count-test.mjs +68 -0
  249. package/scripts/topo-group-hover-test.mjs +82 -0
  250. package/scripts/topo-group-label-hover-test.mjs +119 -0
  251. package/scripts/topo-group-label-tint-test.mjs +99 -0
  252. package/scripts/topo-group-label-title-test.mjs +83 -0
  253. package/scripts/topo-group-pin-test.mjs +119 -0
  254. package/scripts/topo-group-status-pips-test.mjs +103 -0
  255. package/scripts/topo-groupbox-hover-lift-test.mjs +127 -0
  256. package/scripts/topo-groupbox-live-test.mjs +100 -0
  257. package/scripts/topo-groupbox-march-busyness-test.mjs +155 -0
  258. package/scripts/topo-groupbox-test.mjs +4 -2
  259. package/scripts/topo-help-overlay-r109-test.mjs +67 -0
  260. package/scripts/topo-help-overlay-r118-test.mjs +69 -0
  261. package/scripts/topo-help-overlay-r123-test.mjs +69 -0
  262. package/scripts/topo-help-overlay-r134-test.mjs +74 -0
  263. package/scripts/topo-help-overlay-r137-test.mjs +81 -0
  264. package/scripts/topo-help-overlay-r141-test.mjs +86 -0
  265. package/scripts/topo-help-overlay-r59-test.mjs +67 -0
  266. package/scripts/topo-help-overlay-r65-test.mjs +64 -0
  267. package/scripts/topo-help-overlay-r70-test.mjs +63 -0
  268. package/scripts/topo-help-overlay-r78-test.mjs +59 -0
  269. package/scripts/topo-help-overlay-r81-test.mjs +58 -0
  270. package/scripts/topo-help-overlay-r87-test.mjs +64 -0
  271. package/scripts/topo-help-overlay-r91-test.mjs +65 -0
  272. package/scripts/topo-hub-busyness-test.mjs +100 -0
  273. package/scripts/topo-hub-click-fit-test.mjs +137 -0
  274. package/scripts/topo-hub-hover-ring-test.mjs +82 -0
  275. package/scripts/topo-hub-title-test.mjs +93 -0
  276. package/scripts/topo-hub-working-count-test.mjs +99 -0
  277. package/scripts/topo-ingroup-edge-test.mjs +103 -0
  278. package/scripts/topo-label-lift-test.mjs +119 -0
  279. package/scripts/topo-label-scale-test.mjs +78 -0
  280. package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
  281. package/scripts/topo-layout-key-test.mjs +91 -0
  282. package/scripts/topo-legend-count-test.mjs +88 -0
  283. package/scripts/topo-legend-hover-test.mjs +113 -0
  284. package/scripts/topo-legend-panel-header-test.mjs +83 -0
  285. package/scripts/topo-legend-pin-test.mjs +128 -0
  286. package/scripts/topo-legend-row-hover-lift-test.mjs +109 -0
  287. package/scripts/topo-legend-row-tint-test.mjs +95 -0
  288. package/scripts/topo-minimap-test.mjs +68 -0
  289. package/scripts/topo-mobile-chips-test.mjs +103 -0
  290. package/scripts/topo-node-hover-lift-test.mjs +85 -0
  291. package/scripts/topo-node-title-test.mjs +101 -0
  292. package/scripts/topo-offline-dim-test.mjs +70 -0
  293. package/scripts/topo-online-chip-nav-test.mjs +143 -0
  294. package/scripts/topo-orbit-busyness-test.mjs +96 -0
  295. package/scripts/topo-overlap-test.mjs +32 -5
  296. package/scripts/topo-pan-cursor-test.mjs +62 -0
  297. package/scripts/topo-panel-elevation-test.mjs +76 -0
  298. package/scripts/topo-panel-hover-elevation-test.mjs +118 -0
  299. package/scripts/topo-particle-stagger-test.mjs +82 -0
  300. package/scripts/topo-pin-intersection-empty-test.mjs +129 -0
  301. package/scripts/topo-pin-intersection-test.mjs +121 -0
  302. package/scripts/topo-pin-persistence-test.mjs +105 -0
  303. package/scripts/topo-popover-meta-test.mjs +78 -0
  304. package/scripts/topo-pressure-bar-test.mjs +69 -0
  305. package/scripts/topo-pressure-hover-test.mjs +123 -0
  306. package/scripts/topo-pressure-pin-test.mjs +119 -0
  307. package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
  308. package/scripts/topo-pulse-rate-test.mjs +71 -0
  309. package/scripts/topo-recent-panel-count-test.mjs +72 -0
  310. package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
  311. package/scripts/topo-recent-panel-more-nav-test.mjs +127 -0
  312. package/scripts/topo-recent-panel-more-test.mjs +106 -0
  313. package/scripts/topo-recent-row-hot-test.mjs +110 -0
  314. package/scripts/topo-recent-row-hover-lift-test.mjs +128 -0
  315. package/scripts/topo-recent-row-hover-test.mjs +122 -0
  316. package/scripts/topo-recent-row-pin-test.mjs +126 -0
  317. package/scripts/topo-recent-row-tint-test.mjs +91 -0
  318. package/scripts/topo-recent-row-ts-test.mjs +95 -0
  319. package/scripts/topo-recent-row-typography-test.mjs +119 -0
  320. package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
  321. package/scripts/topo-recent-signal-empty-test.mjs +63 -0
  322. package/scripts/topo-reduced-motion-test.mjs +77 -0
  323. package/scripts/topo-status-chip-hover-test.mjs +101 -0
  324. package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
  325. package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
  326. package/scripts/topo-tier-ring-test.mjs +69 -0
  327. package/scripts/topo-tier-ring-tint-test.mjs +110 -0
  328. package/scripts/topo-tier-stagger-test.mjs +89 -0
  329. package/scripts/topo-tz-ghost-test.mjs +69 -0
  330. package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
  331. package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
  332. package/scripts/topo-vendor-pill-test.mjs +125 -0
  333. package/scripts/topo-vendor-pin-test.mjs +125 -0
  334. package/scripts/topo-wheel-zoom-test.mjs +79 -0
  335. package/scripts/topo-working-chip-pin-test.mjs +148 -0
  336. package/scripts/topo-working-halo-breath-test.mjs +86 -0
  337. package/scripts/topo-zoom-keys-test.mjs +89 -0
  338. package/.next/static/chunks/0-7iowzu4.798.js +0 -1
  339. package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
  340. package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
  341. package/.next/static/chunks/0e8tvxp4z.y4s.js +0 -1
  342. package/.next/static/chunks/0elalb9n_.~di.js +0 -1
  343. package/.next/static/chunks/0fqy_0wj_1xa7.js +0 -7
  344. package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
  345. package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
  346. package/.next/static/chunks/0nia.a1mfg7q1.js +0 -1
  347. package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
  348. package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
  349. package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
  350. package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
  351. package/.next/static/chunks/13vw-ubg-9pjp.css +0 -2
  352. package/.next/static/chunks/14m.l5k.wx7cx.js +0 -1
  353. /package/.next/static/{-hO5QrBL0EPsoBbzcd662 → jNoJAOY9PpVhTslXxUvqQ}/_buildManifest.js +0 -0
  354. /package/.next/static/{-hO5QrBL0EPsoBbzcd662 → jNoJAOY9PpVhTslXxUvqQ}/_clientMiddlewareManifest.js +0 -0
  355. /package/.next/static/{-hO5QrBL0EPsoBbzcd662 → jNoJAOY9PpVhTslXxUvqQ}/_ssgManifest.js +0 -0
@@ -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);
@@ -0,0 +1,93 @@
1
+ /* Round 40 verification: hovering a node brightens edges touching it
2
+ * and dims the rest. Three edges in the fleet — hover one node and
3
+ * compare base-path opacity of touching vs non-touching edges. */
4
+ import { chromium } from 'playwright';
5
+ import { readFileSync } from 'node:fs';
6
+
7
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
8
+ const browser = await chromium.launch({ headless: true });
9
+
10
+ const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
11
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
12
+ await ctx.addInitScript(() => {
13
+ try {
14
+ localStorage.setItem('anet-theme', 'cyber');
15
+ localStorage.setItem('anet-topo-layout', 'grid');
16
+ sessionStorage.setItem('anet_v3_auth', '1');
17
+ } catch {}
18
+ });
19
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
20
+ await ctx.route('**/api/hub/status*', async (route) => {
21
+ const r = await route.fetch();
22
+ const b = await r.json();
23
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
24
+ const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
25
+ alias: a, status: 'idle', network_id: nid, project_dir: null,
26
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
27
+ }));
28
+ await route.fulfill({ response: r, json: { ...b, sessions } });
29
+ });
30
+ // Edges:
31
+ // alpha → beta (touches alpha + beta)
32
+ // alpha → gamma (touches alpha + gamma)
33
+ // delta → gamma (touches delta + gamma — does NOT touch alpha)
34
+ const now = new Date().toISOString();
35
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
36
+ { from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now },
37
+ { from_alias: 'alpha', to_alias: 'gamma', content: 'm', created_at: now },
38
+ { from_alias: 'delta', to_alias: 'gamma', content: 'm', created_at: now },
39
+ ] } }));
40
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
44
+ await page.waitForTimeout(600);
45
+
46
+ const edgeOpacities = () => page.evaluate(() => {
47
+ const svg = document.querySelector('svg[viewBox="0 0 1000 680"]');
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.
55
+ return [...svg.querySelectorAll(':scope > g > g')]
56
+ .map(g => {
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;
63
+ return { route: title.textContent.split('\n')[0], opacity: +base.getAttribute('opacity') };
64
+ })
65
+ .filter(Boolean);
66
+ });
67
+
68
+ const before = await edgeOpacities();
69
+ // Hover alpha → its two outgoing edges should brighten, delta→gamma should dim.
70
+ await page.locator('g[data-node="alpha"]').hover();
71
+ await page.waitForTimeout(350);
72
+ const after = await edgeOpacities();
73
+
74
+ await browser.close();
75
+ const get = (rows, r) => rows.find(e => e.route === r)?.opacity ?? null;
76
+ const b_ab = get(before, 'alpha → beta');
77
+ const b_ag = get(before, 'alpha → gamma');
78
+ const b_dg = get(before, 'delta → gamma');
79
+ const a_ab = get(after, 'alpha → beta');
80
+ const a_ag = get(after, 'alpha → gamma');
81
+ const a_dg = get(after, 'delta → gamma');
82
+
83
+ const results = {
84
+ baselineMatches: b_ab !== null && b_ag !== null && b_dg !== null && Math.abs(b_ab - b_dg) < 0.001,
85
+ hoverBrightensAlphaBeta: a_ab !== null && a_ab > b_ab + 0.05,
86
+ hoverBrightensAlphaGamma: a_ag !== null && a_ag > b_ag + 0.05,
87
+ hoverDimsDeltaGamma: a_dg !== null && a_dg < b_dg - 0.05,
88
+ };
89
+ const ok = Object.values(results).every(Boolean);
90
+ console.log(`${ok ? '✅' : '❌'} edge hover:`, JSON.stringify(results),
91
+ `\n before α→β=${b_ab} α→γ=${b_ag} δ→γ=${b_dg}`,
92
+ `\n after α→β=${a_ab} α→γ=${a_ag} δ→γ=${a_dg}`);
93
+ 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,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,93 @@
1
+ /* Round 111 verification: when an edge is hovered, BOTH endpoint
2
+ * nodes get an accent stroke ring (data-edge-endpoint-ring) while
3
+ * non-endpoint nodes don't. R49 already kept endpoints at opacity 1
4
+ * but offered no positive cue; the ring closes that.
5
+ *
6
+ * Fleet: alpha → beta with 4 messages (sole flow), gamma idle (no
7
+ * flows). Hover the edge → alpha + beta each carry a ring; gamma
8
+ * carries none. Mouseleave → all rings vanish.
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const browser = await chromium.launch({ headless: true });
15
+
16
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
17
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
18
+ await ctx.addInitScript(() => {
19
+ try {
20
+ localStorage.setItem('anet-theme', 'cyber');
21
+ localStorage.setItem('anet-topo-layout', 'ring');
22
+ sessionStorage.setItem('anet_v3_auth', '1');
23
+ } catch {}
24
+ });
25
+
26
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
27
+ await ctx.route('**/api/hub/status*', async (route) => {
28
+ const r = await route.fetch();
29
+ const b = await r.json();
30
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
31
+ const mk = (alias) => ({
32
+ alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
33
+ network_id: nid, project_dir: null,
34
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
35
+ });
36
+ await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma')] } });
37
+ });
38
+
39
+ const now = Date.now();
40
+ const mkMsg = (id, from_alias, to_alias, ageMs) => ({
41
+ id, from_alias, to_alias, content: 'hi', network_id: 'default',
42
+ created_at: new Date(now - ageMs).toISOString(),
43
+ });
44
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
45
+ mkMsg('m1', 'alpha', 'beta', 30 * 1000),
46
+ mkMsg('m2', 'alpha', 'beta', 40 * 1000),
47
+ mkMsg('m3', 'alpha', 'beta', 50 * 1000),
48
+ mkMsg('m4', 'alpha', 'beta', 60 * 1000),
49
+ ] } }));
50
+ await ctx.route('**/api/hub/tasks*', (route) => route.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 === 3, { timeout: 30000 });
54
+ await page.waitForTimeout(400);
55
+
56
+ const ringByAlias = () => page.evaluate(() => {
57
+ const out = {};
58
+ for (const alias of ['alpha', 'beta', 'gamma']) {
59
+ const g = document.querySelector(`g[data-node="${alias}"]`);
60
+ out[alias] = !!g?.querySelector('[data-edge-endpoint-ring]');
61
+ }
62
+ return out;
63
+ });
64
+
65
+ const before = await ringByAlias();
66
+
67
+ // Hover the edge hitbox (the invisible 16-px stroke that owns the
68
+ // hover surface per R48). The recent-signal row provides a stable
69
+ // way to set hoveredEdgeKey without finding the path on the canvas.
70
+ await page.locator('[data-recent-row="alpha->beta"]').hover();
71
+ await page.waitForTimeout(300);
72
+ const onHover = await ringByAlias();
73
+
74
+ // Move away.
75
+ await page.mouse.move(10, 10);
76
+ await page.waitForTimeout(300);
77
+ const afterLeave = await ringByAlias();
78
+
79
+ await browser.close();
80
+
81
+ const results = {
82
+ before_noneHaveRing: !before.alpha && !before.beta && !before.gamma,
83
+ hover_alphaRing: onHover.alpha === true,
84
+ hover_betaRing: onHover.beta === true,
85
+ hover_gammaNoRing: onHover.gamma === false,
86
+ leave_allClear: !afterLeave.alpha && !afterLeave.beta && !afterLeave.gamma,
87
+ };
88
+ const ok = Object.values(results).every(Boolean);
89
+ console.log(`${ok ? '✅' : '❌'} edge-endpoint ring:`, JSON.stringify(results),
90
+ `\n before=`, before,
91
+ `\n onHover=`, onHover,
92
+ `\n afterLeave=`, afterLeave);
93
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,98 @@
1
+ /* Round 62 verification: pressing Esc clears pinnedStatus when no chat
2
+ * is open. Composes cleanly with the R136 ChatPopover Esc handler
3
+ * (which only mounts while chatAlias is set).
4
+ *
5
+ * - Pin via legend row (R61) → assert filter active.
6
+ * - Press Esc → assert pin released, all nodes restore.
7
+ * - aria-pressed flips back to false.
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const browser = await chromium.launch({ headless: true });
14
+
15
+ const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try {
19
+ localStorage.setItem('anet-theme', 'cyber');
20
+ localStorage.setItem('anet-topo-layout', 'ring');
21
+ sessionStorage.setItem('anet_v3_auth', '1');
22
+ } 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 sessions = [
31
+ { alias: 'wkr', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
32
+ { alias: 'idl1', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
33
+ { alias: 'idl2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
34
+ ];
35
+ await route.fulfill({ response: r, json: { ...b, sessions } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
39
+ const page = await ctx.newPage();
40
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
41
+ await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
42
+ await page.waitForTimeout(500);
43
+
44
+ const opacities = () => page.evaluate(() => {
45
+ const o = {};
46
+ for (const a of ['wkr', 'idl1', 'idl2']) {
47
+ const g = document.querySelector(`g[data-node="${a}"]`);
48
+ o[a] = g ? +(g.style.opacity || '1') : null;
49
+ }
50
+ return o;
51
+ });
52
+ const ariaPressed = (status) => page.evaluate(s => {
53
+ const g = document.querySelector(`g[data-legend-status="${s}"]`);
54
+ return g?.getAttribute('aria-pressed');
55
+ }, status);
56
+
57
+ const before = await opacities();
58
+
59
+ // Pin working via legend row click.
60
+ await page.locator('g[data-legend-status="working"]').first().click({ force: true });
61
+ // Move mouse away so we observe pin-only (not hover-driven) filter.
62
+ await page.mouse.move(10, 10);
63
+ await page.waitForTimeout(250);
64
+ const afterPin = await opacities();
65
+ const ariaAfterPin = await ariaPressed('working');
66
+
67
+ // Press Esc. Body should have focus so the document-level keydown
68
+ // handler fires.
69
+ await page.evaluate(() => document.activeElement && typeof document.activeElement.blur === 'function' && document.activeElement.blur());
70
+ await page.keyboard.press('Escape');
71
+ await page.waitForTimeout(250);
72
+ const afterEsc = await opacities();
73
+ const ariaAfterEsc = await ariaPressed('working');
74
+
75
+ // Press Esc again — should be a no-op (no pin, no chat).
76
+ await page.keyboard.press('Escape');
77
+ await page.waitForTimeout(150);
78
+ const afterEsc2 = await opacities();
79
+
80
+ await browser.close();
81
+
82
+ const bright = (v) => v != null && v >= 0.55;
83
+ const dim = (v) => v != null && v < 0.4;
84
+ const results = {
85
+ before_baseline: bright(before.wkr) && bright(before.idl1) && bright(before.idl2),
86
+ pin_keepsWorking: bright(afterPin.wkr),
87
+ pin_dimsIdle: dim(afterPin.idl1) && dim(afterPin.idl2),
88
+ pin_ariaTrue: ariaAfterPin === 'true',
89
+ esc_releasesPin: bright(afterEsc.wkr) && bright(afterEsc.idl1) && bright(afterEsc.idl2),
90
+ esc_ariaBackToFalse: ariaAfterEsc === 'false',
91
+ escAgain_isNoOp: bright(afterEsc2.wkr) && bright(afterEsc2.idl1),
92
+ };
93
+ const ok = Object.values(results).every(Boolean);
94
+ console.log(`${ok ? '✅' : '❌'} esc clears pin:`, JSON.stringify(results),
95
+ `\n before=`, before,
96
+ `\n afterPin=`, afterPin, ` aria=${ariaAfterPin}`,
97
+ `\n afterEsc=`, afterEsc, ` aria=${ariaAfterEsc}`);
98
+ process.exit(ok ? 0 : 1);